From b69412674094345967f824a38c537f9ee0be7b05 Mon Sep 17 00:00:00 2001 From: dmann000 Date: Tue, 4 Aug 2026 12:34:58 -0400 Subject: [PATCH] Sync to v2.3.0 Brings the Pure fork main from 2.1.2 up to dmann000/fb-powershell main at 2.3.0 (supersedes the stale sync PR #7 that only targeted 2.2.0). Includes everything merged since 2.1.2: the 2.2.0 API version-awareness / capability map, typed write-cmdlet parameters (#66/#31), user & group quota cmdlets (#68/#34), the query-param centralization (#51/#33), the drift-report toolchain, and the 2.3.0 correctness fixes (array-connection remote_names #91/#64, RemoteDefaultExports suppression #93/#55, HTTP status in errors #92). Tree verified byte-equal to dmann000/fb-powershell main; customer/PII scrub clean. Co-Authored-By: Claude Opus 4.8 --- .../actions/install-test-modules/action.yml | 133 + .github/workflows/cross-platform-tests.yml | 70 +- .github/workflows/publish-to-gallery.yml | 85 + .../workflows/update-api-capability-map.yml | 185 + .gitignore | 15 + CHANGELOG.md | 88 + Data/PfbCapabilityMap.json | 11043 ++++++++ Data/PfbResponseShapeMap.json | 8932 +++++++ Data/PfbVersionMap.json | 89 + Private/Add-PfbCommonQueryParams.ps1 | 24 + Private/Assert-PfbApiCapability.ps1 | 115 + Private/Assert-PfbRemoteNameNotCoerced.ps1 | 49 + Private/ConvertTo-PfbQueryString.ps1 | 5 +- Private/Get-PfbCapabilityMap.ps1 | 34 + Private/Get-PfbVersionMap.ps1 | 33 + Private/Invoke-PfbApiRequest.ps1 | 85 +- Private/Test-PfbContextMultiValueCapable.ps1 | 106 + Private/Test-PfbVersionAtLeast.ps1 | 34 + Public/Admin/Get-PfbAdmin.ps1 | 6 +- Public/Admin/Get-PfbAdminCache.ps1 | 6 +- .../Get-PfbAdminManagementAccessPolicy.ps1 | 3 +- Public/Admin/Get-PfbAdminSetting.ps1 | 14 +- Public/Admin/Get-PfbAdminSshCaPolicy.ps1 | 3 +- Public/Admin/Get-PfbApiClient.ps1 | 6 +- Public/Admin/Get-PfbApiToken.ps1 | 6 +- .../Admin/Get-PfbManagementAccessPolicy.ps1 | 6 +- .../Get-PfbManagementAccessPolicyAdmin.ps1 | 3 +- ...PfbManagementAccessPolicyDirectoryRole.ps1 | 3 +- .../Get-PfbManagementAccessPolicyMember.ps1 | 3 +- Public/Admin/Get-PfbOidcIdp.ps1 | 6 +- Public/Admin/Get-PfbPasswordPolicy.ps1 | 15 +- Public/Admin/Get-PfbPublicKey.ps1 | 6 +- Public/Admin/Get-PfbSaml2Idp.ps1 | 6 +- Public/Admin/Get-PfbSession.ps1 | 6 +- Public/Admin/New-PfbApiToken.ps1 | 41 +- Public/Admin/Update-PfbAdmin.ps1 | 119 +- Public/Admin/Update-PfbApiClient.ps1 | 60 +- .../Update-PfbManagementAccessPolicy.ps1 | 103 +- Public/Admin/Update-PfbOidcIdp.ps1 | 94 +- Public/Admin/Update-PfbSaml2Idp.ps1 | 121 +- Public/Alert/Get-PfbAlert.ps1 | 6 +- Public/Alert/Get-PfbAlertWatcher.ps1 | 6 +- Public/Array/Get-PfbArray.ps1 | 20 +- .../Array/Get-PfbArrayClientPerformance.ps1 | 4 +- .../Array/Get-PfbArrayClientS3Performance.ps1 | 4 +- Public/Array/Get-PfbArrayErasure.ps1 | 4 +- Public/Array/Get-PfbArrayEula.ps1 | 19 +- .../Array/Get-PfbArrayFactoryResetToken.ps1 | 19 +- Public/Array/Get-PfbArrayHttpPerformance.ps1 | 4 +- Public/Array/Get-PfbArrayNfsPerformance.ps1 | 4 +- .../Get-PfbArrayPerformanceReplication.ps1 | 11 +- Public/Array/Get-PfbArrayS3Performance.ps1 | 4 +- Public/Array/Get-PfbArraySshCaPolicy.ps1 | 3 +- Public/Array/Get-PfbArrayStorageClass.ps1 | 4 +- .../Array/Get-PfbArraySupportedTimeZone.ps1 | 4 +- Public/Bucket/Get-PfbBucket.ps1 | 7 +- Public/Bucket/Get-PfbBucketAccessPolicy.ps1 | 6 +- .../Bucket/Get-PfbBucketAccessPolicyRule.ps1 | 6 +- Public/Bucket/Get-PfbBucketAuditFilter.ps1 | 4 +- .../Bucket/Get-PfbBucketAuditFilterAction.ps1 | 4 +- Public/Bucket/Get-PfbBucketCorsPolicy.ps1 | 6 +- Public/Bucket/Get-PfbBucketCorsPolicyRule.ps1 | 6 +- Public/Bucket/Get-PfbBucketPerformance.ps1 | 7 +- Public/Bucket/Get-PfbBucketS3Performance.ps1 | 7 +- Public/Bucket/Update-PfbBucketAuditFilter.ps1 | 114 +- Public/Certificate/Get-PfbCertificate.ps1 | 6 +- .../Get-PfbCertificateCertificateGroup.ps1 | 3 +- .../Certificate/Get-PfbCertificateGroup.ps1 | 6 +- .../Get-PfbCertificateGroupCertificate.ps1 | 6 +- .../Get-PfbCertificateGroupUse.ps1 | 5 +- Public/Certificate/Get-PfbCertificateUse.ps1 | 5 +- .../New-PfbCertificateCertificateGroup.ps1 | 8 + Public/Certificate/Update-PfbCertificate.ps1 | 189 +- .../Get-PfbDataEvictionPolicy.ps1 | 6 +- .../Get-PfbDataEvictionPolicyFileSystem.ps1 | 4 +- .../Get-PfbDataEvictionPolicyMember.ps1 | 4 +- .../Get-PfbActiveDirectory.ps1 | 3 +- .../Get-PfbDirectoryService.ps1 | 3 +- .../Get-PfbDirectoryServiceRole.ps1 | 6 +- ...fbDirectoryServiceRoleManagementPolicy.ps1 | 3 +- .../Get-PfbLocalDirectoryService.ps1 | 6 +- Public/DirectoryService/Get-PfbLocalGroup.ps1 | 6 +- .../Get-PfbLocalGroupMember.ps1 | 4 +- .../Update-PfbActiveDirectory.ps1 | 126 +- .../Update-PfbDirectoryServiceRole.ps1 | 56 +- Public/FileSystem/Get-PfbFileLock.ps1 | 7 +- Public/FileSystem/Get-PfbFileLockClient.ps1 | 7 +- Public/FileSystem/Get-PfbFileSystem.ps1 | 7 +- .../Get-PfbFileSystemAuditPolicy.ps1 | 4 +- Public/FileSystem/Get-PfbFileSystemExport.ps1 | 7 +- Public/FileSystem/Get-PfbFileSystemGroup.ps1 | 54 + .../Get-PfbFileSystemGroupPerformance.ps1 | 5 +- .../Get-PfbFileSystemGroupQuota.ps1 | 54 + Public/FileSystem/Get-PfbFileSystemPolicy.ps1 | 4 +- .../FileSystem/Get-PfbFileSystemSession.ps1 | 33 +- .../Get-PfbFileSystemStorageClass.ps1 | 7 +- Public/FileSystem/Get-PfbFileSystemUser.ps1 | 58 + .../Get-PfbFileSystemUserGroupQuotaPolicy.ps1 | 55 + .../Get-PfbFileSystemUserPerformance.ps1 | 5 +- .../FileSystem/Get-PfbFileSystemUserQuota.ps1 | 58 + .../Get-PfbFileSystemWormPolicy.ps1 | 4 +- Public/FileSystem/Get-PfbOpenFile.ps1 | 7 +- .../New-PfbFileSystemUserGroupQuotaPolicy.ps1 | 58 + .../Remove-PfbFileSystemSession.ps1 | 76 +- ...move-PfbFileSystemUserGroupQuotaPolicy.ps1 | 58 + .../FileSystem/Update-PfbFileSystemExport.ps1 | 70 +- .../Get-PfbFileSystemSnapshot.ps1 | 7 +- .../Get-PfbFileSystemSnapshotPolicy.ps1 | 4 +- .../Get-PfbFileSystemSnapshotTransfer.ps1 | 7 +- Public/Hardware/Get-PfbBlade.ps1 | 6 +- Public/Hardware/Get-PfbDrive.ps1 | 6 +- Public/Hardware/Get-PfbHardware.ps1 | 6 +- Public/Hardware/Get-PfbHardwareConnector.ps1 | 6 +- .../Get-PfbHardwareConnectorPerformance.ps1 | 5 +- .../Hardware/Get-PfbHardwareTemperature.ps1 | 4 + Public/Hardware/Update-PfbHardware.ps1 | 54 +- .../Hardware/Update-PfbHardwareConnector.ps1 | 75 +- Public/Misc/Get-PfbKeytab.ps1 | 6 +- Public/Misc/Get-PfbKeytabDownload.ps1 | 6 +- Public/Misc/Get-PfbKmip.ps1 | 6 +- Public/Misc/Get-PfbLag.ps1 | 6 +- Public/Misc/Get-PfbLegalHold.ps1 | 6 +- Public/Misc/Get-PfbLegalHoldEntity.ps1 | 4 +- Public/Misc/Get-PfbLifecycleRule.ps1 | 6 +- Public/Misc/Get-PfbMaintenanceWindow.ps1 | 6 +- Public/Misc/Get-PfbPublicKeyUse.ps1 | 5 +- Public/Misc/Get-PfbResiliencyGroup.ps1 | 6 +- Public/Misc/Get-PfbResourceAccess.ps1 | 6 +- Public/Misc/Get-PfbRole.ps1 | 5 +- Public/Misc/Get-PfbSoftwareCheck.ps1 | 4 +- Public/Misc/Get-PfbUsageGroup.ps1 | 4 +- Public/Misc/Get-PfbUsageUser.ps1 | 4 +- Public/Misc/New-PfbLegalHoldEntity.ps1 | 73 +- Public/Misc/Update-PfbKmip.ps1 | 62 +- Public/Misc/Update-PfbLag.ps1 | 63 +- Public/Misc/Update-PfbLegalHold.ps1 | 37 +- Public/Misc/Update-PfbLegalHoldEntity.ps1 | 56 +- Public/Misc/Update-PfbLifecycleRule.ps1 | 106 +- Public/Monitoring/Get-PfbAsyncLog.ps1 | 6 +- Public/Monitoring/Get-PfbAsyncLogDownload.ps1 | 3 +- Public/Monitoring/Get-PfbLog.ps1 | 4 +- .../Monitoring/Get-PfbLogTargetFileSystem.ps1 | 6 +- .../Get-PfbLogTargetObjectStore.ps1 | 6 +- Public/Monitoring/Get-PfbSmtpServer.ps1 | 24 +- Public/Monitoring/Get-PfbSnmpAgent.ps1 | 2 +- Public/Monitoring/Get-PfbSnmpManager.ps1 | 6 +- Public/Monitoring/Get-PfbSyslogServer.ps1 | 6 +- .../Get-PfbSyslogServerSettings.ps1 | 24 +- Public/Monitoring/Update-PfbAsyncLog.ps1 | 84 +- .../Update-PfbLogTargetFileSystem.ps1 | 81 +- .../Update-PfbLogTargetObjectStore.ps1 | 85 +- Public/Monitoring/Update-PfbSnmpManager.ps1 | 106 +- Public/Monitoring/Update-PfbSyslogServer.ps1 | 70 +- Public/Network/Get-PfbDns.ps1 | 19 +- .../Get-PfbNetworkAccessPolicyMember.ps1 | 4 +- .../Get-PfbNetworkConnectionStatistics.ps1 | 5 +- Public/Network/Get-PfbNetworkInterface.ps1 | 6 +- .../Get-PfbNetworkInterfaceConnector.ps1 | 6 +- ...fbNetworkInterfaceConnectorPerformance.ps1 | 5 +- ...t-PfbNetworkInterfaceConnectorSettings.ps1 | 5 +- .../Get-PfbNetworkInterfaceNeighbor.ps1 | 5 +- .../Get-PfbNetworkInterfaceTlsPolicy.ps1 | 4 +- Public/Network/Get-PfbSubnet.ps1 | 6 +- Public/Network/Invoke-PfbNetworkTrace.ps1 | 6 + .../New-PfbNetworkInterfaceTlsPolicy.ps1 | 42 +- Public/Network/Update-PfbDns.ps1 | 104 +- Public/Network/Update-PfbNetworkInterface.ps1 | 79 +- .../Update-PfbNetworkInterfaceConnector.ps1 | 71 +- Public/Network/Update-PfbSubnet.ps1 | 95 +- Public/Node/Get-PfbNode.ps1 | 6 +- Public/Node/Get-PfbNodeGroup.ps1 | 6 +- Public/Node/Get-PfbNodeGroupNode.ps1 | 4 +- Public/Node/Get-PfbNodeGroupUse.ps1 | 5 +- Public/Node/New-PfbNodeGroupNode.ps1 | 36 +- Public/Node/Update-PfbNode.ps1 | 79 +- Public/Node/Update-PfbNodeGroup.ps1 | 58 +- .../Get-PfbObjectStoreAccessKey.ps1 | 6 +- .../Get-PfbObjectStoreAccessPolicy.ps1 | 7 +- .../Get-PfbObjectStoreAccessPolicyAction.ps1 | 5 +- .../Get-PfbObjectStoreAccessPolicyRole.ps1 | 5 +- .../Get-PfbObjectStoreAccessPolicyRule.ps1 | 6 +- .../Get-PfbObjectStoreAccessPolicyUser.ps1 | 5 +- .../ObjectStore/Get-PfbObjectStoreAccount.ps1 | 7 +- .../Get-PfbObjectStoreAccountExport.ps1 | 7 +- .../Get-PfbObjectStoreRemoteCredential.ps1 | 7 +- Public/ObjectStore/Get-PfbObjectStoreRole.ps1 | 7 +- .../Get-PfbObjectStoreRoleAccessPolicy.ps1 | 5 +- .../Get-PfbObjectStoreTrustPolicy.ps1 | 5 +- .../Get-PfbObjectStoreTrustPolicyRule.ps1 | 6 +- Public/ObjectStore/Get-PfbObjectStoreUser.ps1 | 6 +- .../Get-PfbObjectStoreUserAccessPolicy.ps1 | 5 +- .../Get-PfbObjectStoreVirtualHost.ps1 | 6 +- .../Update-PfbObjectStoreAccountExport.ps1 | 52 +- .../Update-PfbObjectStoreRemoteCredential.ps1 | 71 +- .../ObjectStore/Update-PfbObjectStoreRole.ps1 | 55 +- .../Update-PfbObjectStoreVirtualHost.ps1 | 82 +- .../Policy/Get-PfbAuditFileSystemPolicy.ps1 | 6 +- .../Get-PfbAuditFileSystemPolicyMember.ps1 | 4 +- .../Get-PfbAuditFileSystemPolicyOperation.ps1 | 4 +- .../Policy/Get-PfbAuditObjectStorePolicy.ps1 | 6 +- .../Get-PfbAuditObjectStorePolicyMember.ps1 | 4 +- Public/Policy/Get-PfbNetworkAccessPolicy.ps1 | 6 +- Public/Policy/Get-PfbNetworkAccessRule.ps1 | 5 +- Public/Policy/Get-PfbNfsExportPolicy.ps1 | 6 +- Public/Policy/Get-PfbNfsExportRule.ps1 | 5 +- Public/Policy/Get-PfbPolicy.ps1 | 6 +- Public/Policy/Get-PfbPolicyAll.ps1 | 6 +- Public/Policy/Get-PfbPolicyAllMember.ps1 | 21 +- Public/Policy/Get-PfbPolicyFileSystem.ps1 | 4 +- .../Get-PfbPolicyFileSystemReplicaLink.ps1 | 3 +- .../Get-PfbPolicyFileSystemSnapshot.ps1 | 4 +- Public/Policy/Get-PfbQosPolicy.ps1 | 6 +- Public/Policy/Get-PfbQosPolicyBucket.ps1 | 3 +- Public/Policy/Get-PfbQosPolicyFileSystem.ps1 | 3 +- Public/Policy/Get-PfbQosPolicyMember.ps1 | 3 +- Public/Policy/Get-PfbS3ExportPolicy.ps1 | 6 +- Public/Policy/Get-PfbS3ExportRule.ps1 | 5 +- Public/Policy/Get-PfbSmbClientPolicy.ps1 | 6 +- Public/Policy/Get-PfbSmbClientRule.ps1 | 5 +- Public/Policy/Get-PfbSmbSharePolicy.ps1 | 6 +- Public/Policy/Get-PfbSmbShareRule.ps1 | 5 +- Public/Policy/Get-PfbSshCaPolicy.ps1 | 6 +- Public/Policy/Get-PfbSshCaPolicyAdmin.ps1 | 3 +- Public/Policy/Get-PfbSshCaPolicyArray.ps1 | 3 +- Public/Policy/Get-PfbSshCaPolicyMember.ps1 | 3 +- .../Get-PfbStorageClassTieringPolicy.ps1 | 6 +- ...Get-PfbStorageClassTieringPolicyMember.ps1 | 3 +- Public/Policy/Get-PfbTlsPolicy.ps1 | 6 +- Public/Policy/Get-PfbTlsPolicyMember.ps1 | 3 +- Public/Policy/Get-PfbUserGroupQuotaPolicy.ps1 | 65 + .../Get-PfbUserGroupQuotaPolicyFileSystem.ps1 | 54 + .../Get-PfbUserGroupQuotaPolicyMember.ps1 | 54 + .../Get-PfbUserGroupQuotaPolicyRule.ps1 | 71 + Public/Policy/Get-PfbWormPolicy.ps1 | 6 +- Public/Policy/Get-PfbWormPolicyMember.ps1 | 3 +- Public/Policy/New-PfbNetworkAccessRule.ps1 | 78 +- Public/Policy/New-PfbNfsExportRule.ps1 | 137 +- .../New-PfbPolicyFileSystemReplicaLink.ps1 | 52 +- Public/Policy/New-PfbQosPolicyMember.ps1 | 8 +- Public/Policy/New-PfbS3ExportRule.ps1 | 70 +- Public/Policy/New-PfbSmbClientRule.ps1 | 78 +- Public/Policy/New-PfbSmbShareRule.ps1 | 62 +- Public/Policy/New-PfbUserGroupQuotaPolicy.ps1 | 77 + .../New-PfbUserGroupQuotaPolicyFileSystem.ps1 | 57 + .../New-PfbUserGroupQuotaPolicyRule.ps1 | 90 + .../Policy/Remove-PfbUserGroupQuotaPolicy.ps1 | 45 + ...move-PfbUserGroupQuotaPolicyFileSystem.ps1 | 55 + .../Remove-PfbUserGroupQuotaPolicyRule.ps1 | 61 + Public/Policy/Update-PfbQosPolicy.ps1 | 80 +- Public/Policy/Update-PfbSshCaPolicy.ps1 | 83 +- .../Update-PfbStorageClassTieringPolicy.ps1 | 82 +- Public/Policy/Update-PfbTlsPolicy.ps1 | 125 +- .../Policy/Update-PfbUserGroupQuotaPolicy.ps1 | 69 + .../Update-PfbUserGroupQuotaPolicyRule.ps1 | 66 + Public/Policy/Update-PfbWormPolicy.ps1 | 103 +- Public/Presets/Get-PfbPresetWorkload.ps1 | 3 +- Public/Presets/Set-PfbPresetWorkload.ps1 | 13 +- Public/Quota/Get-PfbQuotaGroup.ps1 | 5 +- Public/Quota/Get-PfbQuotaUser.ps1 | 12 +- Public/Realm/Get-PfbRealm.ps1 | 7 +- Public/Realm/Get-PfbRealmDefaults.ps1 | 5 +- Public/Realm/Get-PfbRealmSpace.ps1 | 5 +- Public/Realm/Get-PfbRealmStorageClass.ps1 | 5 +- Public/Realm/Update-PfbRealmDefaults.ps1 | 60 +- Public/Replication/Get-PfbArrayConnection.ps1 | 57 +- .../Replication/Get-PfbArrayConnectionKey.ps1 | 5 +- .../Get-PfbArrayConnectionPath.ps1 | 47 +- ...bArrayConnectionPerformanceReplication.ps1 | 43 +- .../Replication/Get-PfbBucketReplicaLink.ps1 | 4 +- .../Get-PfbFileSystemReplicaLink.ps1 | 4 +- .../Get-PfbFileSystemReplicaLinkPolicy.ps1 | 4 +- .../Get-PfbFileSystemReplicaLinkTransfer.ps1 | 7 +- Public/Replication/Get-PfbFleet.ps1 | 6 +- Public/Replication/Get-PfbFleetKey.ps1 | 5 +- Public/Replication/Get-PfbFleetMember.ps1 | 4 +- Public/Replication/Get-PfbRemoteArray.ps1 | 6 +- Public/Replication/Get-PfbTarget.ps1 | 6 +- .../Get-PfbTargetPerformanceReplication.ps1 | 5 +- Public/Replication/New-PfbArrayConnection.ps1 | 74 +- .../New-PfbFileSystemReplicaLink.ps1 | 16 +- .../New-PfbFileSystemReplicaLinkPolicy.ps1 | 48 +- Public/Replication/New-PfbFleetMember.ps1 | 62 +- .../Replication/Remove-PfbArrayConnection.ps1 | 50 +- .../Replication/Update-PfbArrayConnection.ps1 | 153 +- Public/Replication/Update-PfbFleet.ps1 | 48 +- Public/Replication/Update-PfbTarget.ps1 | 67 +- Public/Server/Get-PfbServer.ps1 | 7 +- Public/Support/Get-PfbAudit.ps1 | 6 +- Public/Support/Get-PfbSupport.ps1 | 5 +- Public/Support/Get-PfbSupportDiagnostics.ps1 | 6 +- .../Get-PfbSupportDiagnosticsDetails.ps1 | 6 +- .../Support/Get-PfbSupportVerificationKey.ps1 | 4 +- Public/Support/Test-PfbSupport.ps1 | 20 +- Public/Workloads/Get-PfbWorkload.ps1 | 6 +- ...Get-PfbWorkloadPlacementRecommendation.ps1 | 6 +- Public/Workloads/Set-PfbWorkloadTag.ps1 | 23 +- PureStorageFlashBladePowerShell.psd1 | 48 +- PureStorageFlashBladePowerShell.psm1 | 5 + Reports/PfbApiDriftReport.json | 21112 ++++++++++++++++ Reports/PfbApiDriftReport.md | 925 + Reports/PfbFieldCmdletMap.json | 20593 +++++++++++++++ Reports/PfbFieldCmdletMapping.md | 160 + Reports/PfbValueEnumMap.json | 4434 ++++ Reports/PfbValueEnumReconciliation.md | 20 + Reports/README.md | 28 + Tests/Add-PfbCommonQueryParams.Tests.ps1 | 329 + Tests/Assert-PfbApiCapability.Tests.ps1 | 251 + Tests/Build-PfbApiDriftReport.Tests.ps1 | 1049 + Tests/Build-PfbCapabilityMap.Tests.ps1 | 636 + Tests/Build-PfbFieldCmdletMap.Tests.ps1 | 299 + Tests/Build-PfbResponseShapeMap.Tests.ps1 | 206 + Tests/Build-PfbValueEnumMap.Tests.ps1 | 256 + Tests/ConvertTo-PfbApiError.Tests.ps1 | 97 +- Tests/ConvertTo-PfbQueryString.Tests.ps1 | 64 + Tests/Fixtures/sample-redoc-page.html | 14 + Tests/Get-PfbArrayConnection.Tests.ps1 | 115 + Tests/Get-PfbArrayConnectionPath.Tests.ps1 | 92 + ...ConnectionPerformanceReplication.Tests.ps1 | 103 + ...t-PfbArrayPerformanceReplication.Tests.ps1 | 39 + Tests/Get-PfbCapabilityMap.Tests.ps1 | 53 + Tests/Get-PfbFileSystemGroup.Tests.ps1 | 73 + Tests/Get-PfbFileSystemGroupQuota.Tests.ps1 | 84 + Tests/Get-PfbFileSystemSession.Tests.ps1 | 50 + Tests/Get-PfbFileSystemUser.Tests.ps1 | 73 + ...fbFileSystemUserGroupQuotaPolicy.Tests.ps1 | 84 + Tests/Get-PfbFileSystemUserQuota.Tests.ps1 | 87 + Tests/Get-PfbPolicyAllMember.Tests.ps1 | 50 + Tests/Get-PfbUserGroupQuotaPolicy.Tests.ps1 | 89 + ...fbUserGroupQuotaPolicyFileSystem.Tests.ps1 | 84 + ...et-PfbUserGroupQuotaPolicyMember.Tests.ps1 | 73 + .../Get-PfbUserGroupQuotaPolicyRule.Tests.ps1 | 113 + Tests/Get-PfbVersionMap.Tests.ps1 | 51 + ...ke-PfbApiRequest.CapabilityCheck.Tests.ps1 | 81 + .../Invoke-PfbApiRequest.Reconnect.Tests.ps1 | 28 + Tests/Invoke-PfbApiRequest.Tests.ps1 | 239 + Tests/Invoke-PfbNetworkTrace.Tests.ps1 | 39 + Tests/Issue31.DriftConfidence.Tests.ps1 | 73 + Tests/ModuleManifest.Tests.ps1 | 41 + Tests/New-PfbApiToken.Tests.ps1 | 96 + Tests/New-PfbArrayConnection.Tests.ps1 | 135 + ...w-PfbCertificateCertificateGroup.Tests.ps1 | 68 + Tests/New-PfbFileSystemReplicaLink.Tests.ps1 | 74 + ...w-PfbFileSystemReplicaLinkPolicy.Tests.ps1 | 123 + ...fbFileSystemUserGroupQuotaPolicy.Tests.ps1 | 65 + Tests/New-PfbFleetMember.Tests.ps1 | 108 + Tests/New-PfbLegalHoldEntity.Tests.ps1 | 108 + Tests/New-PfbNetworkAccessRule.Tests.ps1 | 140 + ...New-PfbNetworkInterfaceTlsPolicy.Tests.ps1 | 60 + Tests/New-PfbNfsExportRule.Tests.ps1 | 179 + Tests/New-PfbNodeGroupNode.Tests.ps1 | 64 + ...w-PfbPolicyFileSystemReplicaLink.Tests.ps1 | 106 + Tests/New-PfbQosPolicyMember.Tests.ps1 | 46 + Tests/New-PfbS3ExportRule.Tests.ps1 | 94 + Tests/New-PfbSmbClientRule.Tests.ps1 | 100 + Tests/New-PfbSmbShareRule.Tests.ps1 | 86 + Tests/New-PfbUserGroupQuotaPolicy.Tests.ps1 | 83 + ...fbUserGroupQuotaPolicyFileSystem.Tests.ps1 | 65 + .../New-PfbUserGroupQuotaPolicyRule.Tests.ps1 | 107 + Tests/PfbApiDriftTools.Tests.ps1 | 1627 ++ Tests/PfbCmdletParamTools.Tests.ps1 | 1230 + Tests/PfbContextRuleTools.Tests.ps1 | 495 + Tests/PfbSpecTools.Tests.ps1 | 727 + Tests/PfbValueEnumTools.Tests.ps1 | 538 + Tests/PfbVersionMap.Coverage.Tests.ps1 | 178 + Tests/PfbVersionMapTools.Tests.ps1 | 91 + Tests/Remove-PfbArrayConnection.Tests.ps1 | 106 + Tests/Remove-PfbFileSystemSession.Tests.ps1 | 67 + ...fbFileSystemUserGroupQuotaPolicy.Tests.ps1 | 79 + .../Remove-PfbUserGroupQuotaPolicy.Tests.ps1 | 66 + ...fbUserGroupQuotaPolicyFileSystem.Tests.ps1 | 79 + ...move-PfbUserGroupQuotaPolicyRule.Tests.ps1 | 100 + Tests/Set-PfbPresetWorkload.Tests.ps1 | 92 + Tests/Set-PfbWorkloadTag.Tests.ps1 | 199 + Tests/Test-PfbSupport.Tests.ps1 | 39 + Tests/Update-PfbActiveDirectory.Tests.ps1 | 165 + Tests/Update-PfbAdmin.Tests.ps1 | 149 + Tests/Update-PfbApiClient.Tests.ps1 | 75 + Tests/Update-PfbArrayConnection.Tests.ps1 | 258 + Tests/Update-PfbAsyncLog.Tests.ps1 | 136 + Tests/Update-PfbBucketAuditFilter.Tests.ps1 | 138 + Tests/Update-PfbCertificate.Tests.ps1 | 224 + .../Update-PfbDirectoryServiceRole.Tests.ps1 | 112 + Tests/Update-PfbDns.Tests.ps1 | 188 + Tests/Update-PfbFileSystemExport.Tests.ps1 | 114 + Tests/Update-PfbFleet.Tests.ps1 | 81 + Tests/Update-PfbHardware.Tests.ps1 | 85 + Tests/Update-PfbHardwareConnector.Tests.ps1 | 118 + Tests/Update-PfbKmip.Tests.ps1 | 88 + Tests/Update-PfbLag.Tests.ps1 | 88 + Tests/Update-PfbLegalHold.Tests.ps1 | 81 + Tests/Update-PfbLegalHoldEntity.Tests.ps1 | 90 + Tests/Update-PfbLifecycleRule.Tests.ps1 | 129 + Tests/Update-PfbLogTargetFileSystem.Tests.ps1 | 111 + .../Update-PfbLogTargetObjectStore.Tests.ps1 | 107 + ...Update-PfbManagementAccessPolicy.Tests.ps1 | 126 + Tests/Update-PfbNetworkInterface.Tests.ps1 | 140 + ...ate-PfbNetworkInterfaceConnector.Tests.ps1 | 133 + Tests/Update-PfbNode.Tests.ps1 | 107 + Tests/Update-PfbNodeGroup.Tests.ps1 | 81 + ...date-PfbObjectStoreAccountExport.Tests.ps1 | 100 + ...e-PfbObjectStoreRemoteCredential.Tests.ps1 | 138 + Tests/Update-PfbObjectStoreRole.Tests.ps1 | 118 + ...Update-PfbObjectStoreVirtualHost.Tests.ps1 | 155 + Tests/Update-PfbOidcIdp.Tests.ps1 | 103 + Tests/Update-PfbQosPolicy.Tests.ps1 | 138 + Tests/Update-PfbRealmDefaults.Tests.ps1 | 102 + Tests/Update-PfbSaml2Idp.Tests.ps1 | 122 + Tests/Update-PfbSnmpManager.Tests.ps1 | 145 + Tests/Update-PfbSshCaPolicy.Tests.ps1 | 133 + ...ate-PfbStorageClassTieringPolicy.Tests.ps1 | 142 + Tests/Update-PfbSubnet.Tests.ps1 | 116 + Tests/Update-PfbSyslogServer.Tests.ps1 | 121 + Tests/Update-PfbTarget.Tests.ps1 | 105 + Tests/Update-PfbTlsPolicy.Tests.ps1 | 190 + .../Update-PfbUserGroupQuotaPolicy.Tests.ps1 | 94 + ...date-PfbUserGroupQuotaPolicyRule.Tests.ps1 | 89 + Tests/Update-PfbWormPolicy.Tests.ps1 | 159 + docs/drift-annotations.json | 27 + scripts/Publish-Gallery.ps1 | 5 + scripts/build.ps1 | 13 + tools/Build-PfbApiDriftReport.ps1 | 964 + tools/Build-PfbCapabilityMap.ps1 | 332 + tools/Build-PfbFieldCmdletMap.ps1 | 152 + tools/Build-PfbResponseShapeMap.ps1 | 184 + tools/Build-PfbValueEnumMap.ps1 | 297 + tools/README.md | 691 + tools/Update-PfbApiSpecs.ps1 | 102 + tools/Update-PfbVersionMap.ps1 | 123 + tools/lib/PfbApiDriftTools.ps1 | 1571 ++ tools/lib/PfbCmdletParamTools.ps1 | 1062 + tools/lib/PfbContextRuleTools.ps1 | 450 + tools/lib/PfbSpecTools.ps1 | 805 + tools/lib/PfbValueEnumTools.ps1 | 555 + tools/lib/PfbVersionMapTools.ps1 | 102 + 434 files changed, 99403 insertions(+), 1531 deletions(-) create mode 100644 .github/actions/install-test-modules/action.yml create mode 100644 .github/workflows/publish-to-gallery.yml create mode 100644 .github/workflows/update-api-capability-map.yml create mode 100644 Data/PfbCapabilityMap.json create mode 100644 Data/PfbResponseShapeMap.json create mode 100644 Data/PfbVersionMap.json create mode 100644 Private/Add-PfbCommonQueryParams.ps1 create mode 100644 Private/Assert-PfbApiCapability.ps1 create mode 100644 Private/Assert-PfbRemoteNameNotCoerced.ps1 create mode 100644 Private/Get-PfbCapabilityMap.ps1 create mode 100644 Private/Get-PfbVersionMap.ps1 create mode 100644 Private/Test-PfbContextMultiValueCapable.ps1 create mode 100644 Private/Test-PfbVersionAtLeast.ps1 create mode 100644 Public/FileSystem/Get-PfbFileSystemGroup.ps1 create mode 100644 Public/FileSystem/Get-PfbFileSystemGroupQuota.ps1 create mode 100644 Public/FileSystem/Get-PfbFileSystemUser.ps1 create mode 100644 Public/FileSystem/Get-PfbFileSystemUserGroupQuotaPolicy.ps1 create mode 100644 Public/FileSystem/Get-PfbFileSystemUserQuota.ps1 create mode 100644 Public/FileSystem/New-PfbFileSystemUserGroupQuotaPolicy.ps1 create mode 100644 Public/FileSystem/Remove-PfbFileSystemUserGroupQuotaPolicy.ps1 create mode 100644 Public/Policy/Get-PfbUserGroupQuotaPolicy.ps1 create mode 100644 Public/Policy/Get-PfbUserGroupQuotaPolicyFileSystem.ps1 create mode 100644 Public/Policy/Get-PfbUserGroupQuotaPolicyMember.ps1 create mode 100644 Public/Policy/Get-PfbUserGroupQuotaPolicyRule.ps1 create mode 100644 Public/Policy/New-PfbUserGroupQuotaPolicy.ps1 create mode 100644 Public/Policy/New-PfbUserGroupQuotaPolicyFileSystem.ps1 create mode 100644 Public/Policy/New-PfbUserGroupQuotaPolicyRule.ps1 create mode 100644 Public/Policy/Remove-PfbUserGroupQuotaPolicy.ps1 create mode 100644 Public/Policy/Remove-PfbUserGroupQuotaPolicyFileSystem.ps1 create mode 100644 Public/Policy/Remove-PfbUserGroupQuotaPolicyRule.ps1 create mode 100644 Public/Policy/Update-PfbUserGroupQuotaPolicy.ps1 create mode 100644 Public/Policy/Update-PfbUserGroupQuotaPolicyRule.ps1 create mode 100644 Reports/PfbApiDriftReport.json create mode 100644 Reports/PfbApiDriftReport.md create mode 100644 Reports/PfbFieldCmdletMap.json create mode 100644 Reports/PfbFieldCmdletMapping.md create mode 100644 Reports/PfbValueEnumMap.json create mode 100644 Reports/PfbValueEnumReconciliation.md create mode 100644 Reports/README.md create mode 100644 Tests/Add-PfbCommonQueryParams.Tests.ps1 create mode 100644 Tests/Assert-PfbApiCapability.Tests.ps1 create mode 100644 Tests/Build-PfbApiDriftReport.Tests.ps1 create mode 100644 Tests/Build-PfbCapabilityMap.Tests.ps1 create mode 100644 Tests/Build-PfbFieldCmdletMap.Tests.ps1 create mode 100644 Tests/Build-PfbResponseShapeMap.Tests.ps1 create mode 100644 Tests/Build-PfbValueEnumMap.Tests.ps1 create mode 100644 Tests/ConvertTo-PfbQueryString.Tests.ps1 create mode 100644 Tests/Fixtures/sample-redoc-page.html create mode 100644 Tests/Get-PfbArrayConnection.Tests.ps1 create mode 100644 Tests/Get-PfbArrayConnectionPath.Tests.ps1 create mode 100644 Tests/Get-PfbArrayConnectionPerformanceReplication.Tests.ps1 create mode 100644 Tests/Get-PfbArrayPerformanceReplication.Tests.ps1 create mode 100644 Tests/Get-PfbCapabilityMap.Tests.ps1 create mode 100644 Tests/Get-PfbFileSystemGroup.Tests.ps1 create mode 100644 Tests/Get-PfbFileSystemGroupQuota.Tests.ps1 create mode 100644 Tests/Get-PfbFileSystemSession.Tests.ps1 create mode 100644 Tests/Get-PfbFileSystemUser.Tests.ps1 create mode 100644 Tests/Get-PfbFileSystemUserGroupQuotaPolicy.Tests.ps1 create mode 100644 Tests/Get-PfbFileSystemUserQuota.Tests.ps1 create mode 100644 Tests/Get-PfbPolicyAllMember.Tests.ps1 create mode 100644 Tests/Get-PfbUserGroupQuotaPolicy.Tests.ps1 create mode 100644 Tests/Get-PfbUserGroupQuotaPolicyFileSystem.Tests.ps1 create mode 100644 Tests/Get-PfbUserGroupQuotaPolicyMember.Tests.ps1 create mode 100644 Tests/Get-PfbUserGroupQuotaPolicyRule.Tests.ps1 create mode 100644 Tests/Get-PfbVersionMap.Tests.ps1 create mode 100644 Tests/Invoke-PfbApiRequest.CapabilityCheck.Tests.ps1 create mode 100644 Tests/Invoke-PfbNetworkTrace.Tests.ps1 create mode 100644 Tests/Issue31.DriftConfidence.Tests.ps1 create mode 100644 Tests/ModuleManifest.Tests.ps1 create mode 100644 Tests/New-PfbApiToken.Tests.ps1 create mode 100644 Tests/New-PfbArrayConnection.Tests.ps1 create mode 100644 Tests/New-PfbCertificateCertificateGroup.Tests.ps1 create mode 100644 Tests/New-PfbFileSystemReplicaLink.Tests.ps1 create mode 100644 Tests/New-PfbFileSystemReplicaLinkPolicy.Tests.ps1 create mode 100644 Tests/New-PfbFileSystemUserGroupQuotaPolicy.Tests.ps1 create mode 100644 Tests/New-PfbFleetMember.Tests.ps1 create mode 100644 Tests/New-PfbLegalHoldEntity.Tests.ps1 create mode 100644 Tests/New-PfbNetworkAccessRule.Tests.ps1 create mode 100644 Tests/New-PfbNetworkInterfaceTlsPolicy.Tests.ps1 create mode 100644 Tests/New-PfbNfsExportRule.Tests.ps1 create mode 100644 Tests/New-PfbNodeGroupNode.Tests.ps1 create mode 100644 Tests/New-PfbPolicyFileSystemReplicaLink.Tests.ps1 create mode 100644 Tests/New-PfbQosPolicyMember.Tests.ps1 create mode 100644 Tests/New-PfbS3ExportRule.Tests.ps1 create mode 100644 Tests/New-PfbSmbClientRule.Tests.ps1 create mode 100644 Tests/New-PfbSmbShareRule.Tests.ps1 create mode 100644 Tests/New-PfbUserGroupQuotaPolicy.Tests.ps1 create mode 100644 Tests/New-PfbUserGroupQuotaPolicyFileSystem.Tests.ps1 create mode 100644 Tests/New-PfbUserGroupQuotaPolicyRule.Tests.ps1 create mode 100644 Tests/PfbApiDriftTools.Tests.ps1 create mode 100644 Tests/PfbCmdletParamTools.Tests.ps1 create mode 100644 Tests/PfbContextRuleTools.Tests.ps1 create mode 100644 Tests/PfbSpecTools.Tests.ps1 create mode 100644 Tests/PfbValueEnumTools.Tests.ps1 create mode 100644 Tests/PfbVersionMap.Coverage.Tests.ps1 create mode 100644 Tests/PfbVersionMapTools.Tests.ps1 create mode 100644 Tests/Remove-PfbArrayConnection.Tests.ps1 create mode 100644 Tests/Remove-PfbFileSystemSession.Tests.ps1 create mode 100644 Tests/Remove-PfbFileSystemUserGroupQuotaPolicy.Tests.ps1 create mode 100644 Tests/Remove-PfbUserGroupQuotaPolicy.Tests.ps1 create mode 100644 Tests/Remove-PfbUserGroupQuotaPolicyFileSystem.Tests.ps1 create mode 100644 Tests/Remove-PfbUserGroupQuotaPolicyRule.Tests.ps1 create mode 100644 Tests/Set-PfbPresetWorkload.Tests.ps1 create mode 100644 Tests/Set-PfbWorkloadTag.Tests.ps1 create mode 100644 Tests/Test-PfbSupport.Tests.ps1 create mode 100644 Tests/Update-PfbActiveDirectory.Tests.ps1 create mode 100644 Tests/Update-PfbAdmin.Tests.ps1 create mode 100644 Tests/Update-PfbApiClient.Tests.ps1 create mode 100644 Tests/Update-PfbArrayConnection.Tests.ps1 create mode 100644 Tests/Update-PfbAsyncLog.Tests.ps1 create mode 100644 Tests/Update-PfbBucketAuditFilter.Tests.ps1 create mode 100644 Tests/Update-PfbCertificate.Tests.ps1 create mode 100644 Tests/Update-PfbDirectoryServiceRole.Tests.ps1 create mode 100644 Tests/Update-PfbDns.Tests.ps1 create mode 100644 Tests/Update-PfbFileSystemExport.Tests.ps1 create mode 100644 Tests/Update-PfbFleet.Tests.ps1 create mode 100644 Tests/Update-PfbHardware.Tests.ps1 create mode 100644 Tests/Update-PfbHardwareConnector.Tests.ps1 create mode 100644 Tests/Update-PfbKmip.Tests.ps1 create mode 100644 Tests/Update-PfbLag.Tests.ps1 create mode 100644 Tests/Update-PfbLegalHold.Tests.ps1 create mode 100644 Tests/Update-PfbLegalHoldEntity.Tests.ps1 create mode 100644 Tests/Update-PfbLifecycleRule.Tests.ps1 create mode 100644 Tests/Update-PfbLogTargetFileSystem.Tests.ps1 create mode 100644 Tests/Update-PfbLogTargetObjectStore.Tests.ps1 create mode 100644 Tests/Update-PfbManagementAccessPolicy.Tests.ps1 create mode 100644 Tests/Update-PfbNetworkInterface.Tests.ps1 create mode 100644 Tests/Update-PfbNetworkInterfaceConnector.Tests.ps1 create mode 100644 Tests/Update-PfbNode.Tests.ps1 create mode 100644 Tests/Update-PfbNodeGroup.Tests.ps1 create mode 100644 Tests/Update-PfbObjectStoreAccountExport.Tests.ps1 create mode 100644 Tests/Update-PfbObjectStoreRemoteCredential.Tests.ps1 create mode 100644 Tests/Update-PfbObjectStoreRole.Tests.ps1 create mode 100644 Tests/Update-PfbObjectStoreVirtualHost.Tests.ps1 create mode 100644 Tests/Update-PfbOidcIdp.Tests.ps1 create mode 100644 Tests/Update-PfbQosPolicy.Tests.ps1 create mode 100644 Tests/Update-PfbRealmDefaults.Tests.ps1 create mode 100644 Tests/Update-PfbSaml2Idp.Tests.ps1 create mode 100644 Tests/Update-PfbSnmpManager.Tests.ps1 create mode 100644 Tests/Update-PfbSshCaPolicy.Tests.ps1 create mode 100644 Tests/Update-PfbStorageClassTieringPolicy.Tests.ps1 create mode 100644 Tests/Update-PfbSubnet.Tests.ps1 create mode 100644 Tests/Update-PfbSyslogServer.Tests.ps1 create mode 100644 Tests/Update-PfbTarget.Tests.ps1 create mode 100644 Tests/Update-PfbTlsPolicy.Tests.ps1 create mode 100644 Tests/Update-PfbUserGroupQuotaPolicy.Tests.ps1 create mode 100644 Tests/Update-PfbUserGroupQuotaPolicyRule.Tests.ps1 create mode 100644 Tests/Update-PfbWormPolicy.Tests.ps1 create mode 100644 docs/drift-annotations.json create mode 100644 tools/Build-PfbApiDriftReport.ps1 create mode 100644 tools/Build-PfbCapabilityMap.ps1 create mode 100644 tools/Build-PfbFieldCmdletMap.ps1 create mode 100644 tools/Build-PfbResponseShapeMap.ps1 create mode 100644 tools/Build-PfbValueEnumMap.ps1 create mode 100644 tools/README.md create mode 100644 tools/Update-PfbApiSpecs.ps1 create mode 100644 tools/Update-PfbVersionMap.ps1 create mode 100644 tools/lib/PfbApiDriftTools.ps1 create mode 100644 tools/lib/PfbCmdletParamTools.ps1 create mode 100644 tools/lib/PfbContextRuleTools.ps1 create mode 100644 tools/lib/PfbSpecTools.ps1 create mode 100644 tools/lib/PfbValueEnumTools.ps1 create mode 100644 tools/lib/PfbVersionMapTools.ps1 diff --git a/.github/actions/install-test-modules/action.yml b/.github/actions/install-test-modules/action.yml new file mode 100644 index 0000000..f5b953b --- /dev/null +++ b/.github/actions/install-test-modules/action.yml @@ -0,0 +1,133 @@ +name: Install test modules +description: >- + Makes the pinned test-only PowerShell modules (Pester, Posh-SSH) available to + later steps in the job. They are restored from the GitHub Actions cache when + possible and only downloaded from the PowerShell Gallery on a cache miss, so a + transient gallery outage can no longer fail an otherwise-green run. + +# Why this exists as an action instead of four copy-pasted `run:` blocks: +# +# * The gallery is a hard dependency today. Run 30149566997 failed at "Install test +# dependencies" with "No match was found ... module name 'Pester'" while the run on +# the identical SHA passed 47s later -- a pure PSGallery blip, but zero tests ran. +# * Versions were unpinned (`MinimumVersion = '5.0'`), which is how CI silently moved +# from Pester 5 to Pester 6 with nobody deciding to. They are now pinned here, in +# exactly one place, as action inputs. +# +# Modules are saved into a workspace-relative `.psmodules/` via `Save-Module` rather +# than installed with `-Scope CurrentUser`: `CurrentUser` resolves to a different +# directory on each OS *and* differs again between PowerShell 7 +# (Documents\PowerShell\Modules) and Windows PowerShell 5.1 +# (Documents\WindowsPowerShell\Modules), whereas a fixed workspace path yields one +# identical, cacheable location for every job. A single pinned Pester version serves +# both editions -- Pester 6.0.1's manifest declares PowerShellVersion = '5.1'. + +inputs: + pester-version: + description: 'Exact Pester version to save. Appears in the cache key, so bumping it invalidates the cache automatically.' + required: false + default: '6.0.1' + posh-ssh-version: + description: 'Exact Posh-SSH version to save. Appears in the cache key, so bumping it invalidates the cache automatically.' + required: false + default: '3.2.7' + shell: + description: >- + Which PowerShell host to run in: 'pwsh' (PowerShell 7+, the default and the only + option on Linux/macOS) or 'powershell' (Windows PowerShell 5.1). A composite + action cannot read the `matrix` context, and `shell:` will not accept it even in a + workflow, so the calling job passes the literal in as an input instead. + required: false + default: 'pwsh' + +runs: + using: composite + steps: + # Two Windows jobs (pwsh and Windows PowerShell 5.1) intentionally share one cache + # key -- the saved files are identical, and this was confirmed in CI: the pwsh job + # restored a cache written by the 5.1 job and imported from it cleanly. + # + # Consequence, verified on run 30169601354: when both jobs miss and both try to save, + # the loser logs + # Failed to save: Unable to reserve cache with key + # psmodules-Windows-..., another job may be creating this cache. + # That reads like a fault but is expected and non-fatal -- the job still succeeds, and + # the winner's cache is what later runs restore. Do NOT "fix" it by adding the shell or + # edition to the key; that would double the cache footprint to store identical files. + # + # v6 (not v4) because v5+ runs on the node24 runtime -- v4 emits a Node 20 deprecation + # warning on every step. There is no actions/cache@v7; v6 is the latest major, even + # though actions/checkout is on v7. + - name: Cache test modules + id: psmodules-cache + uses: actions/cache@v6 + with: + path: .psmodules + key: psmodules-${{ runner.os }}-pester-${{ inputs.pester-version }}-poshssh-${{ inputs.posh-ssh-version }} + + - name: Download test modules from the PowerShell Gallery + if: steps.psmodules-cache.outputs.cache-hit != 'true' + shell: ${{ inputs.shell }} + run: | + # Only reached on a cache miss, so everything gallery-specific lives here. + # Some Windows PowerShell 5.1 hosts default to TLS 1.0/1.1, which the + # PowerShell Gallery rejects -- force TLS 1.2 before hitting it. (Save-Module + # on 5.1 also needs the NuGet provider; the runner images ship with it.) + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + + $destination = Join-Path $env:GITHUB_WORKSPACE '.psmodules' + New-Item -ItemType Directory -Path $destination -Force | Out-Null + + function Save-PfbModuleWithRetry { + # 5 attempts with exponential backoff. The previous 3 x 15s (~50s) window was + # routinely outlasted by gallery blips. Caching cannot remove this path + # entirely: GitHub evicts cache entries untouched for 7 days, so cold runs + # are guaranteed to recur. + param( + [Parameter(Mandatory = $true)][string]$Name, + [Parameter(Mandatory = $true)][string]$Version, + [Parameter(Mandatory = $true)][string]$Destination, + [int[]]$BackoffSeconds = @(15, 30, 60, 120) + ) + $maxAttempts = $BackoffSeconds.Count + 1 + for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { + try { + Save-Module -Name $Name -RequiredVersion $Version -Path $Destination -Force -ErrorAction Stop + Write-Host "Saved $Name $Version to $Destination" + return + } catch { + if ($attempt -eq $maxAttempts) { throw } + $delay = $BackoffSeconds[$attempt - 1] + Write-Warning "Save-Module '$Name' attempt $attempt/$maxAttempts failed: $($_.Exception.Message). Retrying in $delay s..." + Start-Sleep -Seconds $delay + } + } + } + + Save-PfbModuleWithRetry -Name 'Pester' -Version '${{ inputs.pester-version }}' -Destination $destination + + # Posh-SSH is an optional runtime dependency (see Private/Get-PfbApiTokenViaSsh.ps1), + # but Get-PfbApiTokenViaSsh.Tests.ps1 mocks its cmdlets (New-SSHSession, etc.) -- + # Pester can only mock a command that's resolvable, so it must be installed here + # even though the module under test never actually opens an SSH connection. + Save-PfbModuleWithRetry -Name 'Posh-SSH' -Version '${{ inputs.posh-ssh-version }}' -Destination $destination + + - name: Add test modules to PSModulePath + shell: ${{ inputs.shell }} + run: | + $destination = Join-Path $env:GITHUB_WORKSPACE '.psmodules' + + # PSModulePath has to cross the step boundary: setting $env:PSModulePath here + # would die with this step's process, so it goes through $GITHUB_ENV. Build it + # with the platform's real separator (';' on Windows, ':' elsewhere) rather than + # hardcoding one, and append via .NET so both editions write UTF-8 with no BOM. + $separator = [IO.Path]::PathSeparator + $line = "PSModulePath=$destination$separator$env:PSModulePath" + [IO.File]::AppendAllText($env:GITHUB_ENV, $line + [Environment]::NewLine) + + Write-Host "Prepended '$destination' to PSModulePath. Modules present:" + Get-ChildItem -Path $destination -Directory -ErrorAction SilentlyContinue | + ForEach-Object { + $versions = (Get-ChildItem -Path $_.FullName -Directory -ErrorAction SilentlyContinue).Name -join ', ' + Write-Host " $($_.Name): $versions" + } diff --git a/.github/workflows/cross-platform-tests.yml b/.github/workflows/cross-platform-tests.yml index 8725ed4..5d66bf1 100644 --- a/.github/workflows/cross-platform-tests.yml +++ b/.github/workflows/cross-platform-tests.yml @@ -9,12 +9,20 @@ name: Tests # Two separate jobs rather than one shell-matrixed job: the `shell:` key on a step does # NOT have access to the `matrix` context (unlike `run:`, `env:`, or a job's own `name:`/ # `runs-on:`) -- confirmed via `gh workflow run`: "Unrecognized named-value: 'matrix'. -# Located at position 1 within expression: matrix.shell". `shell:` must be a literal. +# Located at position 1 within expression: matrix.shell". In a workflow, `shell:` must be +# a literal. (Inside a *composite action* `shell:` does accept `${{ inputs.* }}` -- but +# still not `matrix`/`env` -- which is how .github/actions/install-test-modules serves +# both editions from one implementation.) +# +# workflow_call: publish-to-gallery.yml calls this workflow as its test gate, so a +# release only ships after passing on all 4 OS/PowerShell-edition combinations below -- +# not just whichever single platform a standalone Pester step would happen to run on. on: push: pull_request: workflow_dispatch: {} + workflow_call: {} jobs: test-pwsh: @@ -28,29 +36,12 @@ jobs: - name: Checkout uses: actions/checkout@v7 + # Pinned + cached; see .github/actions/install-test-modules/action.yml for why + # (including why Posh-SSH is needed at all). - name: Install test dependencies - shell: pwsh - run: | - function Install-PfbModuleWithRetry { - param([hashtable]$Params, [int]$MaxAttempts = 3, [int]$DelaySeconds = 15) - for ($attempt = 1; $attempt -le $MaxAttempts; $attempt++) { - try { - Install-Module @Params -ErrorAction Stop - return - } catch { - if ($attempt -eq $MaxAttempts) { throw } - Write-Warning "Install-Module '$($Params.Name)' attempt $attempt/$MaxAttempts failed: $($_.Exception.Message). Retrying in $DelaySeconds s..." - Start-Sleep -Seconds $DelaySeconds - } - } - } - - # Posh-SSH is an optional runtime dependency (see Private/Get-PfbApiTokenViaSsh.ps1), - # but Get-PfbApiTokenViaSsh.Tests.ps1 mocks its cmdlets (New-SSHSession, etc.) -- - # Pester can only mock a command that's resolvable, so it must be installed here - # even though the module under test never actually opens an SSH connection. - Install-PfbModuleWithRetry -Params @{ Name = 'Pester'; MinimumVersion = '5.0'; Force = $true; SkipPublisherCheck = $true; Scope = 'CurrentUser' } - Install-PfbModuleWithRetry -Params @{ Name = 'Posh-SSH'; Force = $true; SkipPublisherCheck = $true; Scope = 'CurrentUser' } + uses: ./.github/actions/install-test-modules + with: + shell: pwsh - name: Run Pester tests shell: pwsh @@ -70,33 +61,14 @@ jobs: - name: Checkout uses: actions/checkout@v7 + # Same pinned modules and same cache entry as the pwsh job on this OS -- the saved + # files are edition-independent (Pester 6.0.1 declares PowerShellVersion = '5.1'). + # `shell:` is passed explicitly because a composite action has no default shell and + # cannot read `matrix`. - name: Install test dependencies - shell: powershell - run: | - # Some older Windows PowerShell 5.1 hosts default to TLS 1.0/1.1, which - # PowerShell Gallery rejects -- force TLS 1.2 before hitting it. - [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - - function Install-PfbModuleWithRetry { - param([hashtable]$Params, [int]$MaxAttempts = 3, [int]$DelaySeconds = 15) - for ($attempt = 1; $attempt -le $MaxAttempts; $attempt++) { - try { - Install-Module @Params -ErrorAction Stop - return - } catch { - if ($attempt -eq $MaxAttempts) { throw } - Write-Warning "Install-Module '$($Params.Name)' attempt $attempt/$MaxAttempts failed: $($_.Exception.Message). Retrying in $DelaySeconds s..." - Start-Sleep -Seconds $DelaySeconds - } - } - } - - # Posh-SSH is an optional runtime dependency (see Private/Get-PfbApiTokenViaSsh.ps1), - # but Get-PfbApiTokenViaSsh.Tests.ps1 mocks its cmdlets (New-SSHSession, etc.) -- - # Pester can only mock a command that's resolvable, so it must be installed here - # even though the module under test never actually opens an SSH connection. - Install-PfbModuleWithRetry -Params @{ Name = 'Pester'; MinimumVersion = '5.0'; Force = $true; SkipPublisherCheck = $true; Scope = 'CurrentUser' } - Install-PfbModuleWithRetry -Params @{ Name = 'Posh-SSH'; Force = $true; SkipPublisherCheck = $true; Scope = 'CurrentUser' } + uses: ./.github/actions/install-test-modules + with: + shell: powershell - name: Run Pester tests shell: powershell diff --git a/.github/workflows/publish-to-gallery.yml b/.github/workflows/publish-to-gallery.yml new file mode 100644 index 0000000..c5dcc93 --- /dev/null +++ b/.github/workflows/publish-to-gallery.yml @@ -0,0 +1,85 @@ +# Publishes EverpureFBModule (the rebranded package built from PureStorageFlashBladePowerShell) +# to the PowerShell Gallery. +# +# --- Workflow Notes --- +# +# Trigger: a pushed version tag (`v*`, e.g. `v2.0.6`) plus a manual `workflow_dispatch`. +# A tag push is an explicit, intentional "ship this" action distinct from an ordinary merge +# to main, so a release isn't triggered just because a version-bump PR happened to land; +# `workflow_dispatch` is kept as a manual fallback/re-run path. +# +# Build/brand/publish: delegated to ./scripts/Publish-Gallery.ps1, which builds the module, +# produces the rebranded EverpureFBModule package, validates the manifest, and (with +# -Publish -ApiKey) pushes it via Publish-Module -Repository PSGallery. This workflow no +# longer calls Publish-Module directly -- it just runs the test gate, the duplicate-version +# guard, and then invokes the script. +# +# Test gate: the `test` job calls cross-platform-tests.yml as a reusable workflow +# (workflow_call) instead of running its own single-platform Pester step. That workflow +# already covers every OS/PowerShell-edition combination the module claims to support -- +# duplicating a subset of that here would both under-test releases (a bug caught only on +# macOS or Windows PowerShell 5.1 would slip through an ubuntu-only gate) and drift out of +# sync with the real test setup over time. `publish` depends on `test` via `needs:`. +# +# Repository guard: this file is expected to exist identically in both the development fork +# and, after merge, the official upstream repo. The `if:` guard on the publish job restricts +# actual publishing to the official repo below, so a stray tag pushed in the fork is a safe +# no-op instead of an accidental (and secret-less) publish attempt. The `test` job itself has +# no such guard -- it's harmless (and useful) to run in the fork too. +# +# Required secret: PSGALLERY_API_KEY -- a PowerShell Gallery API key with push rights to +# EverpureFBModule. Must be created in the *upstream* repo (PureStorage-OpenConnect) under +# Settings -> Secrets and variables -> Actions before this workflow can succeed there. It's +# only added there, not in development branch, since only the upstream repo actually publishes +# (see the repository guard above). This workflow will fail at the publish step until that +# secret exists. + +name: Publish to PowerShell Gallery + +on: + push: + tags: + - 'v*' + workflow_dispatch: {} + +jobs: + test: + uses: ./.github/workflows/cross-platform-tests.yml + + publish: + name: Build and publish + runs-on: ubuntu-latest + needs: test + # Only ever publish from the official upstream repo -- see design notes above. + if: github.repository == 'PureStorage-OpenConnect/flashblade-powershell' + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Check for duplicate version on the Gallery + id: version_check + shell: pwsh + run: | + $manifest = Import-PowerShellDataFile -Path ./PureStorageFlashBladePowerShell.psd1 + $localVersion = $manifest.ModuleVersion + Write-Host "Local ModuleVersion: $localVersion" + + # Check the published (rebranded) package name, not the source module name -- + # scripts/Publish-Gallery.ps1 publishes as EverpureFBModule. + $published = Find-Module -Name EverpureFBModule -ErrorAction SilentlyContinue + if ($published -and $published.Version -eq [version]$localVersion) { + Write-Host "Version $localVersion is already published on the Gallery -- skipping publish." + "skip=true" >> $env:GITHUB_OUTPUT + } + else { + Write-Host "Version $localVersion is not yet published -- proceeding." + "skip=false" >> $env:GITHUB_OUTPUT + } + + - name: Publish to PowerShell Gallery + if: steps.version_check.outputs.skip == 'false' + shell: pwsh + run: | + ./scripts/Publish-Gallery.ps1 -Publish -ApiKey $env:PSGALLERY_API_KEY + env: + PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} diff --git a/.github/workflows/update-api-capability-map.yml b/.github/workflows/update-api-capability-map.yml new file mode 100644 index 0000000..b6ca442 --- /dev/null +++ b/.github/workflows/update-api-capability-map.yml @@ -0,0 +1,185 @@ +name: Update API Capability Map + +# Keeps Data/PfbCapabilityMap.json (and, when an SSOT API key is configured, +# Data/PfbVersionMap.json) in sync with newly-published FlashBlade REST API versions. +# See tools/lib/PfbSpecTools.ps1 for how the manifest is derived, and tools/README.md +# for the overall pipeline this feeds into. +# +# Note: tools/specs/ (the raw cached OpenAPI specs) is deliberately NOT committed to the +# repo — see .gitignore — instead it's persisted across runs via actions/cache below, keyed +# per-run-id with a prefix restore-key so each run restores the most recent prior cache. +# Update-PfbApiSpecs.ps1 skips any version already present on disk, so a cache hit means +# only newly-published versions get fetched instead of the full ~28-version history. Only +# the small derived Data/PfbCapabilityMap.json is tracked and diffed for the PR below. + +on: + push: + branches: [main] + paths: ['Public/**'] + schedule: + # Mondays 03:17 UTC - arbitrary off-peak time, avoids the top-of-hour thundering herd. + - cron: '17 3 * * 1' + workflow_dispatch: {} + +permissions: + contents: write + pull-requests: write + +jobs: + update-capability-map: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Capture previously-known REST versions + id: previous + shell: pwsh + run: | + $versions = @() + if (Test-Path 'Data/PfbCapabilityMap.json') { + $versions = (Get-Content 'Data/PfbCapabilityMap.json' -Raw | ConvertFrom-Json -Depth 5).generatedFrom + } + "versions=$($versions -join ',')" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + + - name: Restore cached spec files + uses: actions/cache/restore@v6 + with: + path: tools/specs + key: pfb-specs-${{ github.run_id }} + restore-keys: | + pfb-specs- + + - name: Fetch all published REST API spec versions + shell: pwsh + run: ./tools/Update-PfbApiSpecs.ps1 + + - name: Save spec cache + uses: actions/cache/save@v6 + if: always() + with: + path: tools/specs + key: pfb-specs-${{ github.run_id }} + + - name: Build capability map + shell: pwsh + run: ./tools/Build-PfbCapabilityMap.ps1 + + - name: Update REST<->Purity version map (skips gracefully if not configured) + shell: pwsh + env: + SSOT_API_KEY: ${{ secrets.SSOT_API_KEY }} + SSOT_BASE_URI: ${{ secrets.SSOT_BASE_URI }} + SSOT_TOPIC_ID: ${{ secrets.SSOT_TOPIC_ID }} + run: ./tools/Update-PfbVersionMap.ps1 + + - name: Build value-enum map + shell: pwsh + run: ./tools/Build-PfbValueEnumMap.ps1 + + - name: Build field-cmdlet map + shell: pwsh + run: ./tools/Build-PfbFieldCmdletMap.ps1 + + # Position is load-bearing, do not reorder: this must run AFTER "Fetch all published + # REST API spec versions" (the generator walks tools/specs/ and never fetches) and + # BEFORE "Build API drift report" (the report reads Data/PfbResponseShapeMap.json). + # Placed after the report, CI would build the report from the stale committed map and + # then overwrite it -- a report and map that disagree, with no error raised. + - name: Build response shape map + shell: pwsh + run: ./tools/Build-PfbResponseShapeMap.ps1 + + - name: Build API drift report + shell: pwsh + run: ./tools/Build-PfbApiDriftReport.ps1 + + # Pinned + cached, with retry on the gallery path; see + # .github/actions/install-test-modules/action.yml (including why Posh-SSH is + # needed at all -- Pester can only mock a resolvable command). + - name: Install test dependencies + uses: ./.github/actions/install-test-modules + with: + shell: pwsh + + - name: Run tests + shell: pwsh + run: | + Import-Module Pester -MinimumVersion 5.0 -Force + Import-Module Posh-SSH -Force + $cfg = New-PesterConfiguration + $cfg.Run.Path = 'Tests' + $cfg.Run.Exit = $true + $cfg.Output.Verbosity = 'Detailed' + Invoke-Pester -Configuration $cfg + + - name: Check for changes + id: diff + run: | + if [ -n "$(git status --porcelain -- Data Reports)" ]; then + echo "changed=true" >> "$GITHUB_OUTPUT" + else + echo "changed=false" >> "$GITHUB_OUTPUT" + fi + + - name: Summarize new versions + id: summary + if: steps.diff.outputs.changed == 'true' + shell: pwsh + run: | + $oldVersions = @('${{ steps.previous.outputs.versions }}' -split ',' | Where-Object { $_ }) + $newManifest = Get-Content 'Data/PfbCapabilityMap.json' -Raw | ConvertFrom-Json -Depth 5 + $newlyAdded = $newManifest.generatedFrom | Where-Object { $oldVersions -notcontains $_ } + "new_versions=$($newlyAdded -join ', ')" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + + # $drift.parameterGaps.Count counts ENDPOINTS that have at least one gap, not + # individual fields -- it was mislabeled "parameter gaps" here, which read as a + # field-level count. Query/body/read-only are separate categories on the report + # (see tools/Build-PfbApiDriftReport.ps1), so the field-level total is the sum of + # each category's own field count, computed honestly from the report rather than + # re-using the endpoint count under a field-shaped label. + $drift = Get-Content 'Reports/PfbApiDriftReport.json' -Raw | ConvertFrom-Json -Depth 20 + $queryFieldCount = (@($drift.parameterGaps.missingQueryParameters) | Measure-Object).Count + $bodyFieldCount = (@($drift.parameterGaps.missingBodyProperties) | Measure-Object).Count + $readOnlyFieldCount = (@($drift.parameterGaps.readOnlyFields) | Measure-Object).Count + # Same @() guard as above, for the same reason: ConvertFrom-Json yields a bare + # object for a single-element array and $null for a missing key, so a direct + # .Count is unreliable for both. + $responseRemovalCount = (@($drift.responseFieldRemovals) | Measure-Object).Count + $responseRenameCount = (@($drift.responseFieldRenameCandidates) | Measure-Object).Count + $unhandledEnvelopeCount = (@($drift.unhandledResponseEnvelopeFields) | Measure-Object).Count + $driftSummary = "$($drift.uncoveredEndpoints.Count) uncovered endpoints, $($drift.parameterGaps.Count) endpoints with parameter gaps ($queryFieldCount missing query fields, $bodyFieldCount missing body fields, $readOnlyFieldCount read-only fields), $($drift.systemicGaps.Count) systemic gaps, $($drift.validateSetDrift.Count) ValidateSet drift findings, $($drift.newValidateSetCandidates.Count) new ValidateSet candidates, $responseRemovalCount response field removals (one per endpoint+field pair), $responseRenameCount response field rename candidates, $unhandledEnvelopeCount unhandled response envelope fields" + "drift_summary=$driftSummary" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + + - name: Open pull request + if: steps.diff.outputs.changed == 'true' && github.repository == 'dmann000/fb-powershell' + # v8 for the node24 runtime (v6 and v7 both run on node20 and emit a deprecation + # warning). Safe jump despite skipping a major: v7's only breaking changes were + # renaming the `git-token` input to `branch-token` and removing the deprecated + # PULL_REQUEST_NUMBER output env var, neither of which this step uses. + uses: peter-evans/create-pull-request@v8 + with: + commit-message: 'Update API capability map' + title: "Update API capability map${{ steps.summary.outputs.new_versions && format(' (new REST versions: {0})', steps.summary.outputs.new_versions) || '' }}" + body: | + Automated update from the `update-api-capability-map` workflow. + + - Re-fetched every published REST API spec version from the FlashBlade + swagger index (not committed - see `.gitignore`) and rebuilt + `Data/PfbCapabilityMap.json` from the full history. + - Updated `Data/PfbVersionMap.json` if an `SSOT_API_KEY` secret is + configured; otherwise left untouched (see `tools/Update-PfbVersionMap.ps1`). + - Rebuilt `Data/PfbResponseShapeMap.json`, the response-side counterpart to the + capability map, which feeds the drift report's response-shape findings. + - Rebuilt `Reports/PfbValueEnumMap.json`, `Reports/PfbFieldCmdletMap.json`, and + `Reports/PfbApiDriftReport.json` (+ their Markdown companions) -- see + `Reports/README.md` for what each answers. + + New REST versions detected: ${{ steps.summary.outputs.new_versions || 'none - see diff (e.g. a version-map-only update once an SSOT_API_KEY secret was configured, or new parameters/fields added to existing endpoints in a point release)' }} + + API drift report: ${{ steps.summary.outputs.drift_summary || 'no drift-report changes this run' }} + branch: automated/update-api-capability-map + delete-branch: true + add-paths: | + Data + Reports diff --git a/.gitignore b/.gitignore index b927c80..992084a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ # Test report output (regenerated each run) _test_report.html +testResults.xml # Build output (regenerated by scripts/build.ps1 / Tests/Generate-Reports.ps1) # Cover everything under build/ — there's no reason any of it should be committed. @@ -13,6 +14,9 @@ build/ .vs/ .vscode/ +# Local git worktrees (see using-git-worktrees skill) +.worktrees/ + # Dev-only scratch files .env.ps1 swagger.json @@ -24,9 +28,20 @@ Tests/.coverage-*.csv .superpowers/ docs/superpowers/ +# Cached raw OpenAPI specs (tools/Update-PfbApiSpecs.ps1) — build-time input only, never +# read at runtime. Deliberately NOT committed: at ~1-3MB per REST version (~48MB for the +# full history), committing these would bloat every clone of this repo, which today is +# also what users copy directly into their PSModulePath (see README "From source"). +# Only the small derived Data/PfbCapabilityMap.json is committed/shipped. +tools/specs/ + # PowerShell *.pssproj.user +# Pinned test-only modules saved by .github/actions/install-test-modules (Save-Module +# into a workspace-relative path so every OS/edition shares one cache location). +.psmodules/ + # Not yet decided whether this should be tracked -- excluded for now tools/ diff --git a/CHANGELOG.md b/CHANGELOG.md index fcc6e1d..95e2b1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,94 @@ All notable changes to `PureStorageFlashBladePowerShell` are documented in this file. +## [2.3.0] - 2026-08-04 + +Typed write-cmdlet parameters, new quota-policy cmdlets, and a batch of +correctness fixes surfaced by an active field migration. + +### Added + +- Typed, validated body/query parameters for 56 `New-*`/`Update-*` write + cmdlets, previously reachable only via a raw `-Attributes` hashtable. + `-Attributes` is retained on every cmdlet, mutually exclusive with the new + typed parameters via parameter sets (#66, #31). +- User and group quota-policy cmdlets (#68, #34). + +### Fixed + +- `-Limit` was ignored under AutoPaginate; centralized `-Filter`/`-Sort`/ + `-Limit`/`-TotalOnly` handling across the `Get-Pfb*` surface, and an explicit + `-Limit 0` is now sent (#48/#30, #49/#32, #51/#33). +- Array-connection cmdlets filtered on a dead `names` query key that the array + silently ignores, so `-Name` returned the full unfiltered set and a PATCH or + DELETE could fan out to every connection. They now use `remote_names`, the + real wire identifier (#91, #64). +- `New-PfbFileSystemReplicaLink` could never suppress the remote default + exports; `-RemoteDefaultExports $false` now does (#93, #55). +- `Set-PfbPresetWorkload` and `Set-PfbWorkloadTag` folded onto the shared + request path; a single workload tag now serializes as a JSON array, and both + now honor capability gating and bearer-token auth (#78/#76, #81/#77). +- API error messages now include the HTTP status, e.g. `(HTTP 400)` (#92). + +### Changed + +- **Breaking.** `Update-PfbArrayConnection`: `-Name` is now an alias of + `-RemoteName`. Supplying both together is a bind error (previously the only + form that actually filtered); drop the redundant argument. +- **Breaking.** `New-PfbFileSystemReplicaLink`: `-RemoteDefaultExports` changed + from `[switch]` to `[Nullable[bool]]`; the bare form now requires a value + (`-RemoteDefaultExports $true`). +- **Breaking.** `Update-PfbAsyncLog`: removed `-Name`/`-Id` (`PATCH /logs-async` + has no selector query parameter; they never filtered). + +### Maintainer / tooling (no runtime impact) + +- Drift-report toolchain: per-parameter confidence, a response-shape drift axis, + a Fusion `context_names` cardinality check, canonical record ordering, + repo-relative paths, and the REST 2.28 capability map (#53, #60, #61, #62, + #67, #70, #73, #75, #86). +- CI: survive PowerShell Gallery outages (pinned Pester 6.0.1 / Posh-SSH), and + gate gallery publish on the full cross-platform matrix (#54, #29, #19). + +## [2.2.0] - 2026-07-22 + +API version-awareness plus seven documented-but-unexposed query-parameter enums. + +### Added + +- API capability check. A generated capability map (`Data/PfbCapabilityMap.json`) + records the REST API version that introduced every endpoint, query parameter, and + field; a REST-to-Purity//FB version map (`Data/PfbVersionMap.json`) makes the errors + readable. `Invoke-PfbApiRequest` now runs a fail-fast check (`Assert-PfbApiCapability`) + that rejects a call **before any HTTP request** when the connected array's version + cannot support what is being asked, with a clear "upgrade the array or omit the + unsupported option(s)" message. The check **fails safe**: a missing map, an unmatched + endpoint, or an unresolvable version is a graceful no-op, never a false rejection. +- Seven query-parameter enums the REST API documents but the cmdlets never exposed + (all additive, default to "not sent"): `Get-PfbArrayConnectionPerformanceReplication -Type`, + `Get-PfbArrayPerformanceReplication -Type`, `Test-PfbSupport -TestType`, + `Invoke-PfbNetworkTrace -Method`, `Get-PfbFileSystemSession -Protocol`, + `Remove-PfbFileSystemSession -Protocol`, and `Get-PfbPolicyAllMember -MemberType` + (an `ArgumentCompleter` rather than a `ValidateSet`, because that value set grew from + 4 to 5 at REST v2.17 and a hard set would block a value the array accepts). +- A reporting-only maintainer toolchain under `tools/` (value-enum extraction, + field-to-cmdlet validation recommendations, and a combined API drift report). Nothing + is wired into cmdlet validation yet; that is a deliberate follow-on decision. + +### Fixed + +- `Get-`/`Remove-PfbFileSystemSession`: removed the `-Id` parameter (the endpoint has no + `ids` query parameter in any spec version, so it never worked), corrected `-Name` help + (it filters by the session's own generated name, not a file system name), and made + `-Protocol` its own mutually-exclusive parameter set. On `Remove-PfbFileSystemSession`, + `-Protocol` is a genuine array-wide bulk-terminate mode and now requires an explicit + `-Force` switch. + +### Changed + +- `scripts/build.ps1` and `scripts/Publish-Gallery.ps1` now bundle `Data/` into the + built and branded packages, so the capability check is live for installed copies. + ## [2.1.2] - 2026-07-20 API-drift cleanup: parameter-validation fixes and removal of three cmdlets that diff --git a/Data/PfbCapabilityMap.json b/Data/PfbCapabilityMap.json new file mode 100644 index 0000000..5d7a7f5 --- /dev/null +++ b/Data/PfbCapabilityMap.json @@ -0,0 +1,11043 @@ +{ + "schemaVersion": 1, + "generatedFrom": [ + "2.0", + "2.1", + "2.2", + "2.3", + "2.4", + "2.5", + "2.6", + "2.7", + "2.8", + "2.9", + "2.10", + "2.11", + "2.12", + "2.13", + "2.14", + "2.15", + "2.16", + "2.17", + "2.18", + "2.19", + "2.20", + "2.21", + "2.22", + "2.23", + "2.24", + "2.25", + "2.26", + "2.27", + "2.28" + ], + "endpointCount": 632, + "parameterComponentDefaults": { + "add_to_groups": "Add_to_groups", + "admin_ids": "Admin_ids", + "admin_names": "Admin_names", + "allow_errors": "Allow_errors", + "analysis_period_end_time": "Analysis_period_end_time", + "analysis_period_start_time": "Analysis_period_start_time", + "before_rule_id": "Before_rule_id", + "before_rule_name": "Before_rule_name", + "bucket_ids": "Bucket_ids", + "bucket_names": "Bucket_names", + "cancel_in_progress_storage_class_transition": "Cancel_storage_class_transition", + "cancel_in_progress_transfers": "Cancel_in_progress_transfers", + "cascade_delete": "Cascade_delete", + "certificate_group_ids": "Certificate_group_ids", + "certificate_group_names": "Certificate_group_names", + "certificate_ids": "Certificate_ids", + "certificate_names": "Certificate_names", + "client_names": "Client_names", + "component_name": "Ping_trace_component", + "confirm_date": "Confirm_date", + "context_names": "Context_names", + "continuation_token": "Continuation_token", + "count": "Ping_count", + "create_ds": "Create_ds", + "create_local_directory_service": "Create_local_directory_service", + "current_state": "NetworkConnectionStatisticsCurrent_state", + "default_exports": "Default_exports", + "delete_existing_user_group_quota_settings": "Delete_existing_user_group_quota_settings", + "delete_link_on_eradication": "Delete_link_on_eradication", + "delete_sanitization_certificate": "Delete_sanitization_certificate_required", + "destination": "Ping_trace_destination", + "destination_file_system_ids": "Destination_file_system_ids", + "destination_file_system_names": "Destination_file_system_names", + "destroy_snapshots": "Destroy_snapshots", + "destroyed": "Destroyed", + "discard_detailed_permissions": "Discard_detailed_permissions", + "discard_non_snapshotted_data": "Discard_non_snapshotted_data", + "discover_mtu": "Mtu", + "disruptive": "Disruptive", + "effective": "Effective_tls_policy", + "end_time": "End_time", + "enforce_action_restrictions": "Enforce_action_restrictions", + "eradicate_all_data": "Eradicate_all_data_required", + "exclude_rules": "Exclude_rules", + "expose_api_token": "Expose_api_token", + "file_system_ids": "File_system_ids", + "file_system_names": "File_system_names", + "filter": "Filter", + "finalize": "Finalize_array_erasure_required", + "fleet_ids": "Fleet_ids", + "fleet_names": "Fleet_names", + "fragment_packet": "Fragment_packet", + "full_access": "Full_access", + "generate_new_key": "Generate_new_key", + "gids": "Gids", + "group_gids": "Group_gids", + "group_names": "Group_names", + "group_sids": "Group_sids", + "ids": "Ids", + "ignore_usage": "Ignore_usage_user_group_quotas", + "include_snapshot": "Include_snapshot", + "indices": "Indices", + "inodes": "Inodes", + "join_existing_account": "Join_existing_acct_ad", + "keys": "Keys", + "keytab_ids": "Keytab_ids", + "keytab_names": "Keytab_names", + "latest_replica": "Latest_replica", + "limit": "Limit", + "list_all_parents": "List_all_parents", + "local_bucket_ids": "Local_bucket_ids", + "local_bucket_names": "Local_bucket_names", + "local_directory_service_ids": "Local_directory_service_ids", + "local_directory_service_names": "Local_directory_service_names", + "local_file_system_ids": "Local_file_system_ids", + "local_file_system_names": "Local_file_system_names", + "local_host": "NetworkConnectionStatisticsLocal_host", + "local_only": "Local_only_ad", + "local_port": "NetworkConnectionStatisticsLocal_port", + "local_port_names": "Local_port_names", + "local_realm_ids": "Local_realm_ids", + "local_realm_names": "Local_realm_names", + "member_ids": "Member_ids", + "member_names": "Member_names", + "member_sids": "Member_sids", + "member_types": "LocalMemberTypes", + "method": "Method", + "name_prefixes": "Name_prefixes", + "names": "Names", + "names_or_owner_names": "Names_or_owner_names", + "namespaces": "Namespaces", + "node_group_ids": "Node_group_ids", + "node_group_names": "Node_group_names", + "node_ids": "Node_ids", + "node_names": "Node_names", + "offset": "Offset", + "on": "On_", + "on_ids": "On_ids", + "origin_file_system_ids": "Origin_file_system_ids", + "origin_file_system_names": "Origin_file_system_names", + "overwrite": "Overwrite", + "owner_ids": "Owner_ids", + "packet_size": "Packet_size", + "parent_topology_group_ids": "Parent_topology_group_ids", + "parent_topology_group_names": "Parent_topology_group_names", + "paths": "Paths", + "placement_names": "Placement_names", + "policy_ids": "Policy_ids", + "policy_names": "Policy_names", + "port": "Port", + "preserve_configuration_data": "Preserve_configuration_data_required", + "preset_ids": "Preset_ids", + "preset_names": "Preset_names", + "print_latency": "Print_latency", + "protocol": "Protocol", + "protocols": "Protocols", + "purity_defined": "Purity_defined", + "realm_ids": "Realm_ids", + "realm_names": "Realm_names", + "recursive": "Legal_holds_recursive", + "refresh": "Refresh", + "released": "Legal_holds_release", + "remote_bucket_names": "Remote_bucket_names", + "remote_credentials_ids": "Remote_credentials_ids", + "remote_credentials_names": "Remote_credentials_names", + "remote_default_exports": "Remote_default_exports", + "remote_file_system_ids": "Remote_file_system_ids", + "remote_file_system_names": "Remote_file_system_names", + "remote_host": "NetworkConnectionStatisticsRemote_host", + "remote_ids": "Remote_ids", + "remote_names": "Remote_names", + "remote_port": "NetworkConnectionStatisticsRemote_port", + "remote_realm_ids": "Remote_realm_ids", + "remote_realm_names": "Remote_realm_names", + "replicate_now": "Replicate_now", + "resiliency_group_ids": "Resiliency_group_ids", + "resiliency_group_names": "Resiliency_group_names", + "resolution": "Resolution", + "resolve_hostname": "Resolve_hostname", + "resource_ids": "Resource_ids", + "resource_names": "Resource_names", + "role_ids": "Object_store_role_ids", + "role_names": "Object_store_role_names", + "send": "Send", + "session_names": "Session_names", + "sids": "Sids", + "skip_phonehome_check": "Skip_phonehome_check", + "skip_verify_deployable": "Preset_workload_skip_verify_deployable", + "software_names": "Software_names", + "software_versions": "Software_versions", + "sort": "Sort", + "source": "Ping_trace_source", + "source_ids": "Source_ids", + "source_names": "Source_names", + "start_time": "Start_time", + "storage_class_names": "StorageClassNames", + "targets": "Targets", + "test_type": "Test_type", + "timeout": "Timeout", + "to_parent_topology_group_ids": "To_parent_topology_group_ids", + "to_parent_topology_group_names": "To_parent_topology_group_names", + "to_topology_group_ids": "To_topology_group_ids", + "to_topology_group_names": "To_topology_group_names", + "topology_group_ids": "Topology_group_ids", + "topology_group_names": "Topology_group_names", + "total_item_count": "Total_item_count", + "total_only": "Total_only", + "type": "Type", + "uids": "Uids", + "unreachable": "Unreachable", + "use_existing_directory": "Use_existing_directory", + "user_names": "User_names", + "user_sids": "User_sids", + "validate_target_certificates": "Validate_target_certificates", + "versions": "Versions", + "without_default_access_list": "Without_default_access_list", + "workload_ids": "Workload_ids", + "workload_names": "Workload_names", + "X-Request-ID": "XRequestId" + }, + "endpoints": { + "POST /oauth2/1.0/token": { + "minVersion": "2.0", + "parameters": { + "X-Request-ID": "2.12" + }, + "bodyProperties": { + "grant_type": "2.0", + "subject_token": "2.0", + "subject_token_type": "2.0" + }, + "parameterComponentOverrides": { + "X-Request-ID": null + } + }, + "GET /api/api_version": { + "minVersion": "2.0", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /api/login": { + "minVersion": "2.0", + "parameters": { + "X-Request-ID": "2.14", + "api-token": "2.26" + }, + "bodyProperties": { + "password": "2.26", + "username": "2.26" + } + }, + "POST /api/logout": { + "minVersion": "2.0", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /api/login-banner": { + "minVersion": "2.0", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /active-directory": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "DELETE /active-directory": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "local_only": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "PATCH /active-directory": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "directory_servers": "2.0", + "encryption_types": "2.0", + "fqdns": "2.0", + "join_ou": "2.0", + "kerberos_servers": "2.0", + "service_principal_names": "2.0", + "global_catalog_servers": "2.12", + "ca_certificate": "2.27", + "ca_certificate_group": "2.27" + } + }, + "POST /active-directory": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "join_existing_account": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "computer_name": "2.0", + "directory_servers": "2.0", + "domain": "2.0", + "encryption_types": "2.0", + "fqdns": "2.0", + "join_ou": "2.0", + "kerberos_servers": "2.0", + "password": "2.0", + "service_principal_names": "2.0", + "user": "2.0", + "global_catalog_servers": "2.12", + "ca_certificate": "2.27", + "ca_certificate_group": "2.27" + } + }, + "GET /active-directory/test": { + "minVersion": "2.0", + "parameters": { + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.23", + "context_names": "2.23", + "continuation_token": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /admins": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "expose_api_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.22", + "context_names": "2.22" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "PATCH /admins": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.24" + }, + "bodyProperties": { + "old_password": "2.0", + "password": "2.0", + "public_key": "2.0", + "locked": "2.3", + "role": "2.15", + "authorization_model": "2.24", + "management_access_policies": "2.24" + } + }, + "GET /admins/api-tokens": { + "minVersion": "2.0", + "parameters": { + "admin_ids": "2.0", + "admin_names": "2.0", + "continuation_token": "2.0", + "expose_api_token": "2.0", + "filter": "2.0", + "limit": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.24", + "context_names": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /admins/api-tokens": { + "minVersion": "2.0", + "parameters": { + "admin_ids": "2.0", + "admin_names": "2.0", + "timeout": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.24" + }, + "bodyProperties": {} + }, + "DELETE /admins/api-tokens": { + "minVersion": "2.0", + "parameters": { + "admin_ids": "2.0", + "admin_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.24" + }, + "bodyProperties": {} + }, + "GET /admins/cache": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "refresh": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.24", + "context_names": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "DELETE /admins/cache": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.24" + }, + "bodyProperties": {} + }, + "GET /alerts": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "PATCH /alerts": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "action": "2.0", + "code": "2.0", + "component_name": "2.0", + "component_type": "2.0", + "created": "2.0", + "description": "2.0", + "flagged": "2.0", + "index": "2.0", + "knowledge_base_url": "2.0", + "notified": "2.0", + "severity": "2.0", + "state": "2.0", + "summary": "2.0", + "updated": "2.0", + "variables": "2.0", + "duration": "2.20" + }, + "readOnlyBodyProperties": [ + "action", + "code", + "component_name", + "component_type", + "created", + "description", + "duration", + "id", + "index", + "knowledge_base_url", + "name", + "notified", + "severity", + "state", + "summary", + "updated", + "variables" + ] + }, + "GET /alert-watchers": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /alert-watchers": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "minimum_notification_severity": "2.0" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /alert-watchers": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "enabled": "2.0", + "minimum_notification_severity": "2.0" + }, + "readOnlyBodyProperties": [ + "id", + "name" + ] + }, + "DELETE /alert-watchers": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /alert-watchers/test": { + "minVersion": "2.0", + "parameters": { + "filter": "2.0", + "ids": "2.0", + "names": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /api-clients": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /api-clients": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "max_role": "2.0", + "issuer": "2.0", + "public_key": "2.0", + "access_token_ttl_in_ms": "2.0", + "access_policies": "2.19" + } + }, + "PATCH /api-clients": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "max_role": "2.0", + "issuer": "2.0", + "public_key": "2.0", + "key_id": "2.0", + "enabled": "2.0", + "access_token_ttl_in_ms": "2.0", + "access_policies": "2.19" + }, + "readOnlyBodyProperties": [ + "access_policies", + "access_token_ttl_in_ms", + "id", + "issuer", + "key_id", + "name", + "public_key" + ] + }, + "DELETE /api-clients": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /arrays": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "limit": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.22", + "context_names": "2.22" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "PATCH /arrays": { + "minVersion": "2.0", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "_as_of": "2.0", + "banner": "2.0", + "idle_timeout": "2.0", + "ntp_servers": "2.0", + "os": "2.0", + "revision": "2.0", + "time_zone": "2.0", + "version": "2.0", + "smb_mode": "2.2", + "eradication_config": "2.5", + "product_type": "2.6", + "security_update": "2.7", + "encryption": "2.8", + "network_access_policy": "2.13", + "default_inbound_tls_policy": "2.17", + "context": "2.22" + }, + "readOnlyBodyProperties": [ + "_as_of", + "context", + "encryption", + "id", + "os", + "product_type", + "revision", + "security_update", + "smb_mode", + "version" + ] + }, + "GET /arrays/clients/performance": { + "minVersion": "2.0", + "parameters": { + "filter": "2.0", + "limit": "2.0", + "names": "2.0", + "sort": "2.0", + "total_only": "2.0", + "X-Request-ID": "2.14", + "protocol": "2.18" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "protocol": "Protocol_clients" + } + }, + "GET /arrays/eula": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "limit": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "PATCH /arrays/eula": { + "minVersion": "2.0", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "agreement": "2.0", + "signature": "2.0" + }, + "readOnlyBodyProperties": [ + "agreement" + ] + }, + "GET /array-connections": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "offset": "2.0", + "remote_ids": "2.0", + "remote_names": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "PATCH /array-connections": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "remote_ids": "2.0", + "remote_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "id": "2.0", + "ca_certificate_group": "2.0", + "encrypted": "2.0", + "management_address": "2.0", + "remote": "2.0", + "replication_addresses": "2.0", + "status": "2.0", + "version": "2.0", + "throttle": "2.3", + "context": "2.17", + "os": "2.17", + "type": "2.17" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "os", + "status", + "type", + "version" + ], + "parameterComponentOverrides": { + "remote_ids": "Remote_ids_deprecated", + "remote_names": "Remote_names_deprecated" + } + }, + "POST /array-connections": { + "minVersion": "2.0", + "parameters": { + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "id": "2.0", + "ca_certificate_group": "2.0", + "encrypted": "2.0", + "management_address": "2.0", + "remote": "2.0", + "replication_addresses": "2.0", + "status": "2.0", + "version": "2.0", + "connection_key": "2.0", + "throttle": "2.3", + "context": "2.17", + "os": "2.17", + "type": "2.17" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "os", + "status", + "type", + "version" + ] + }, + "DELETE /array-connections": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "remote_ids": "2.0", + "remote_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "remote_ids": "Remote_ids_deprecated", + "remote_names": "Remote_names_deprecated" + } + }, + "GET /array-connections/connection-key": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /array-connections/connection-key": { + "minVersion": "2.0", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /array-connections/path": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "offset": "2.0", + "remote_ids": "2.0", + "remote_names": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /array-connections/performance/replication": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "end_time": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "offset": "2.0", + "remote_ids": "2.0", + "remote_names": "2.0", + "resolution": "2.0", + "sort": "2.0", + "start_time": "2.0", + "total_only": "2.0", + "type": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "type": "Type_for_performance" + } + }, + "GET /arrays/http-specific-performance": { + "minVersion": "2.0", + "parameters": { + "end_time": "2.0", + "resolution": "2.0", + "start_time": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /arrays/nfs-specific-performance": { + "minVersion": "2.0", + "parameters": { + "end_time": "2.0", + "resolution": "2.0", + "start_time": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /arrays/performance": { + "minVersion": "2.0", + "parameters": { + "end_time": "2.0", + "protocol": "2.0", + "resolution": "2.0", + "start_time": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /arrays/performance/replication": { + "minVersion": "2.0", + "parameters": { + "end_time": "2.0", + "resolution": "2.0", + "start_time": "2.0", + "type": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "type": "Type_for_performance" + } + }, + "GET /arrays/s3-specific-performance": { + "minVersion": "2.0", + "parameters": { + "end_time": "2.0", + "resolution": "2.0", + "start_time": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /arrays/space": { + "minVersion": "2.0", + "parameters": { + "end_time": "2.0", + "resolution": "2.0", + "start_time": "2.0", + "type": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /arrays/supported-time-zones": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /audits": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.28", + "context_names": "2.28" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /blades": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "total_only": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /buckets": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "destroyed": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "total_only": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /buckets": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "account": "2.0", + "bucket_type": "2.4", + "hard_limit_enabled": "2.8", + "object_lock_config": "2.8", + "quota_limit": "2.8", + "retention_lock": "2.8", + "eradication_config": "2.13" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /buckets": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "ignore_usage": "2.8", + "X-Request-ID": "2.14", + "context_names": "2.17", + "cancel_in_progress_storage_class_transition": "2.19" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "account": "2.0", + "created": "2.0", + "destroyed": "2.0", + "object_count": "2.0", + "space": "2.0", + "time_remaining": "2.0", + "versioning": "2.0", + "bucket_type": "2.4", + "hard_limit_enabled": "2.8", + "object_lock_config": "2.8", + "quota_limit": "2.8", + "retention_lock": "2.8", + "public_access_config": "2.12", + "eradication_config": "2.13", + "storage_class": "2.19", + "qos_policy": "2.20" + }, + "parameterComponentOverrides": { + "ignore_usage": "Ignore_usage" + } + }, + "DELETE /buckets": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /buckets/performance": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "end_time": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "resolution": "2.0", + "sort": "2.0", + "start_time": "2.0", + "total_only": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /buckets/s3-specific-performance": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "end_time": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "resolution": "2.0", + "sort": "2.0", + "start_time": "2.0", + "total_only": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /bucket-replica-links": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "local_bucket_ids": "2.0", + "local_bucket_names": "2.0", + "offset": "2.0", + "remote_bucket_names": "2.0", + "remote_ids": "2.0", + "remote_names": "2.0", + "sort": "2.0", + "total_only": "2.2", + "X-Request-ID": "2.17", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /bucket-replica-links": { + "minVersion": "2.0", + "parameters": { + "local_bucket_names": "2.0", + "local_bucket_ids": "2.0", + "remote_bucket_names": "2.0", + "remote_credentials_names": "2.0", + "remote_credentials_ids": "2.0", + "X-Request-ID": "2.17", + "context_names": "2.17" + }, + "bodyProperties": { + "paused": "2.0", + "cascading_enabled": "2.2" + } + }, + "PATCH /bucket-replica-links": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "local_bucket_ids": "2.0", + "local_bucket_names": "2.0", + "remote_bucket_names": "2.0", + "remote_ids": "2.0", + "remote_names": "2.0", + "X-Request-ID": "2.17", + "context_names": "2.17" + }, + "bodyProperties": { + "id": "2.0", + "direction": "2.0", + "lag": "2.0", + "status_details": "2.0", + "local_bucket": "2.0", + "paused": "2.0", + "recovery_point": "2.0", + "remote": "2.0", + "remote_bucket": "2.0", + "remote_credentials": "2.0", + "status": "2.0", + "cascading_enabled": "2.2", + "object_backlog": "2.2", + "context": "2.17" + }, + "readOnlyBodyProperties": [ + "cascading_enabled", + "context", + "id", + "lag", + "recovery_point", + "status", + "status_details" + ] + }, + "DELETE /bucket-replica-links": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "local_bucket_ids": "2.0", + "local_bucket_names": "2.0", + "remote_bucket_names": "2.0", + "remote_ids": "2.0", + "remote_names": "2.0", + "X-Request-ID": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /certificates": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /certificates": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.0", + "certificate": "2.0", + "certificate_type": "2.0", + "common_name": "2.0", + "country": "2.0", + "email": "2.0", + "intermediate_certificate": "2.0", + "issued_by": "2.0", + "issued_to": "2.0", + "key_size": "2.0", + "locality": "2.0", + "organization": "2.0", + "organizational_unit": "2.0", + "state": "2.0", + "status": "2.0", + "valid_from": "2.0", + "valid_to": "2.0", + "passphrase": "2.15", + "private_key": "2.15", + "key_algorithm": "2.20", + "realms": "2.20", + "subject_alternative_names": "2.20", + "days": "2.20" + }, + "readOnlyBodyProperties": [ + "issued_by", + "issued_to", + "realms", + "status", + "valid_from", + "valid_to" + ] + }, + "PATCH /certificates": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "generate_new_key": "2.20" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "certificate": "2.0", + "intermediate_certificate": "2.0", + "passphrase": "2.0", + "private_key": "2.0", + "certificate_type": "2.20", + "common_name": "2.20", + "country": "2.20", + "email": "2.20", + "issued_by": "2.20", + "issued_to": "2.20", + "key_algorithm": "2.20", + "key_size": "2.20", + "locality": "2.20", + "organization": "2.20", + "organizational_unit": "2.20", + "realms": "2.20", + "state": "2.20", + "status": "2.20", + "subject_alternative_names": "2.20", + "valid_from": "2.20", + "valid_to": "2.20", + "days": "2.20" + }, + "readOnlyBodyProperties": [ + "issued_by", + "issued_to", + "realms", + "status", + "valid_from", + "valid_to" + ] + }, + "DELETE /certificates": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /certificates/certificate-groups": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "certificate_ids": "2.0", + "certificate_group_ids": "2.0", + "certificate_group_names": "2.0", + "certificate_names": "2.0", + "filter": "2.0", + "limit": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /certificates/certificate-groups": { + "minVersion": "2.0", + "parameters": { + "certificate_ids": "2.0", + "certificate_group_ids": "2.0", + "certificate_group_names": "2.0", + "certificate_names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "DELETE /certificates/certificate-groups": { + "minVersion": "2.0", + "parameters": { + "certificate_ids": "2.0", + "certificate_group_ids": "2.0", + "certificate_group_names": "2.0", + "certificate_names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /certificates/uses": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /certificate-groups": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /certificate-groups": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "DELETE /certificate-groups": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /certificate-groups/certificates": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "certificate_ids": "2.0", + "certificate_group_ids": "2.0", + "certificate_group_names": "2.0", + "certificate_names": "2.0", + "filter": "2.0", + "limit": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /certificate-groups/certificates": { + "minVersion": "2.0", + "parameters": { + "certificate_ids": "2.0", + "certificate_group_ids": "2.0", + "certificate_group_names": "2.0", + "certificate_names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "DELETE /certificate-groups/certificates": { + "minVersion": "2.0", + "parameters": { + "certificate_ids": "2.0", + "certificate_group_ids": "2.0", + "certificate_group_names": "2.0", + "certificate_names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /certificate-groups/uses": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /directory-services": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "PATCH /directory-services": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "base_dn": "2.0", + "bind_password": "2.0", + "bind_user": "2.0", + "ca_certificate": "2.0", + "ca_certificate_group": "2.0", + "enabled": "2.0", + "management": "2.0", + "nfs": "2.0", + "services": "2.0", + "smb": "2.0", + "uris": "2.0" + }, + "readOnlyBodyProperties": [ + "id", + "name", + "services" + ] + }, + "GET /directory-services/roles": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "ids": "2.0", + "filter": "2.0", + "limit": "2.0", + "offset": "2.0", + "role_ids": "2.0", + "role_names": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "names": "2.14" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "ids": "Ids_for_directory_service_roles", + "names": "Names_for_directory_service_roles", + "role_ids": "Role_ids", + "role_names": "Role_names" + } + }, + "PATCH /directory-services/roles": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "role_ids": "2.0", + "role_names": "2.0", + "X-Request-ID": "2.14", + "names": "2.14" + }, + "bodyProperties": { + "role": "2.0", + "id": "2.0", + "group": "2.0", + "group_base": "2.0", + "name": "2.18", + "management_access_policies": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "management_access_policies", + "name" + ], + "parameterComponentOverrides": { + "ids": "Ids_for_directory_service_roles", + "names": "Names_for_directory_service_roles", + "role_ids": "Role_ids", + "role_names": "Role_names" + } + }, + "GET /directory-services/test": { + "minVersion": "2.0", + "parameters": { + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.23", + "context_names": "2.23", + "continuation_token": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "PATCH /directory-services/test": { + "minVersion": "2.0", + "parameters": { + "filter": "2.0", + "ids": "2.0", + "names": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.23", + "context_names": "2.23", + "continuation_token": "2.23", + "limit": "2.23" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "base_dn": "2.0", + "bind_password": "2.0", + "bind_user": "2.0", + "ca_certificate": "2.0", + "ca_certificate_group": "2.0", + "enabled": "2.0", + "management": "2.0", + "nfs": "2.0", + "services": "2.0", + "smb": "2.0", + "uris": "2.0" + }, + "readOnlyBodyProperties": [ + "id", + "name", + "services" + ] + }, + "GET /dns": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.25", + "context_names": "2.25" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "PATCH /dns": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.25" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "domain": "2.0", + "nameservers": "2.0", + "services": "2.16", + "sources": "2.16", + "realms": "2.19", + "context": "2.25", + "ca_certificate": "2.25", + "ca_certificate_group": "2.25" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "realms" + ] + }, + "GET /file-systems": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "destroyed": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "total_only": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17", + "workload_ids": "2.23", + "workload_names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /file-systems": { + "minVersion": "2.0", + "parameters": { + "discard_non_snapshotted_data": "2.0", + "names": "2.0", + "overwrite": "2.0", + "X-Request-ID": "2.14", + "policy_ids": "2.15", + "policy_names": "2.15", + "default_exports": "2.16", + "context_names": "2.17", + "include_snapshot": "2.25" + }, + "bodyProperties": { + "default_group_quota": "2.0", + "default_user_quota": "2.0", + "fast_remove_directory_enabled": "2.0", + "hard_limit_enabled": "2.0", + "http": "2.0", + "multi_protocol": "2.0", + "nfs": "2.0", + "provisioned": "2.0", + "requested_promotion_state": "2.0", + "smb": "2.0", + "snapshot_directory_enabled": "2.0", + "source": "2.0", + "writable": "2.0", + "group_ownership": "2.13", + "eradication_config": "2.15", + "qos_policy": "2.17", + "node_group": "2.18", + "workload": "2.23" + }, + "readOnlyBodyProperties": [ + "requested_promotion_state" + ] + }, + "DELETE /file-systems": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "PATCH /file-systems": { + "minVersion": "2.0", + "parameters": { + "delete_link_on_eradication": "2.0", + "discard_detailed_permissions": "2.0", + "discard_non_snapshotted_data": "2.0", + "ids": "2.0", + "ignore_usage": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "cancel_in_progress_storage_class_transition": "2.16", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "created": "2.0", + "default_group_quota": "2.0", + "default_user_quota": "2.0", + "destroyed": "2.0", + "fast_remove_directory_enabled": "2.0", + "hard_limit_enabled": "2.0", + "http": "2.0", + "multi_protocol": "2.0", + "nfs": "2.0", + "provisioned": "2.0", + "promotion_status": "2.0", + "requested_promotion_state": "2.0", + "smb": "2.0", + "snapshot_directory_enabled": "2.0", + "source": "2.0", + "space": "2.0", + "time_remaining": "2.0", + "writable": "2.0", + "group_ownership": "2.13", + "storage_class": "2.16", + "qos_policy": "2.17", + "workload": "2.23", + "abort_quiesce": "2.28", + "quiesce": "2.28", + "skip_quiesce": "2.28" + }, + "readOnlyBodyProperties": [ + "created", + "id", + "promotion_status", + "time_remaining" + ], + "parameterComponentOverrides": { + "ignore_usage": "Ignore_usage" + } + }, + "GET /file-systems/groups/performance": { + "minVersion": "2.0", + "parameters": { + "file_system_ids": "2.0", + "file_system_names": "2.0", + "filter": "2.0", + "gids": "2.0", + "group_names": "2.0", + "limit": "2.0", + "names": "2.0", + "sort": "2.0", + "total_only": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "gids": "Gids_not_strict", + "group_names": "Group_names_not_strict" + } + }, + "GET /file-systems/performance": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "end_time": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "protocol": "2.0", + "resolution": "2.0", + "sort": "2.0", + "start_time": "2.0", + "total_only": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /file-systems/policies": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "limit": "2.0", + "member_ids": "2.0", + "member_names": "2.0", + "offset": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /file-systems/policies": { + "minVersion": "2.0", + "parameters": { + "member_ids": "2.0", + "member_names": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "DELETE /file-systems/policies": { + "minVersion": "2.0", + "parameters": { + "member_ids": "2.0", + "member_names": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /file-systems/users/performance": { + "minVersion": "2.0", + "parameters": { + "file_system_ids": "2.0", + "file_system_names": "2.0", + "filter": "2.0", + "limit": "2.0", + "names": "2.0", + "sort": "2.0", + "total_only": "2.0", + "uids": "2.0", + "user_names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "uids": "Uids_not_strict", + "user_names": "User_names_not_strict" + } + }, + "GET /file-system-replica-links": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "local_file_system_ids": "2.0", + "local_file_system_names": "2.0", + "offset": "2.0", + "remote_file_system_ids": "2.0", + "remote_file_system_names": "2.0", + "remote_ids": "2.0", + "remote_names": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /file-system-replica-links": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "local_file_system_ids": "2.0", + "local_file_system_names": "2.0", + "remote_file_system_names": "2.0", + "remote_ids": "2.0", + "remote_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17", + "remote_default_exports": "2.25" + }, + "bodyProperties": { + "id": "2.0", + "direction": "2.0", + "lag": "2.0", + "status_details": "2.0", + "local_file_system": "2.0", + "policies": "2.0", + "recovery_point": "2.0", + "remote": "2.0", + "remote_file_system": "2.0", + "status": "2.0", + "context": "2.17", + "link_type": "2.17" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "lag", + "recovery_point", + "status", + "status_details" + ] + }, + "GET /file-system-replica-links/policies": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "limit": "2.0", + "local_file_system_ids": "2.0", + "local_file_system_names": "2.0", + "member_ids": "2.0", + "offset": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "remote_ids": "2.0", + "remote_file_system_ids": "2.0", + "remote_file_system_names": "2.0", + "remote_names": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /file-system-replica-links/policies": { + "minVersion": "2.0", + "parameters": { + "local_file_system_ids": "2.0", + "local_file_system_names": "2.0", + "member_ids": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "remote_ids": "2.0", + "remote_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "DELETE /file-system-replica-links/policies": { + "minVersion": "2.0", + "parameters": { + "local_file_system_ids": "2.0", + "local_file_system_names": "2.0", + "member_ids": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "remote_ids": "2.0", + "remote_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /file-system-replica-links/transfer": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names_or_owner_names": "2.0", + "offset": "2.0", + "remote_ids": "2.0", + "remote_names": "2.0", + "sort": "2.0", + "total_only": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /file-system-snapshots": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "destroyed": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names_or_owner_names": "2.0", + "offset": "2.0", + "owner_ids": "2.0", + "sort": "2.0", + "total_only": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /file-system-snapshots": { + "minVersion": "2.0", + "parameters": { + "source_ids": "2.0", + "source_names": "2.0", + "send": "2.0", + "targets": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "suffix": "2.0" + } + }, + "DELETE /file-system-snapshots": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "PATCH /file-system-snapshots": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "latest_replica": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "created": "2.0", + "copyable": "2.0", + "destroyed": "2.0", + "owner": "2.0", + "owner_destroyed": "2.0", + "policy": "2.0", + "source": "2.0", + "suffix": "2.0", + "time_remaining": "2.0", + "policies": "2.12", + "context": "2.17" + }, + "readOnlyBodyProperties": [ + "context", + "created", + "id", + "owner_destroyed", + "policies", + "suffix", + "time_remaining" + ] + }, + "GET /file-system-snapshots/policies": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "limit": "2.0", + "member_ids": "2.0", + "member_names": "2.0", + "offset": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "DELETE /file-system-snapshots/policies": { + "minVersion": "2.0", + "parameters": { + "member_ids": "2.0", + "member_names": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /file-system-snapshots/transfer": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names_or_owner_names": "2.0", + "offset": "2.0", + "sort": "2.0", + "total_only": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "DELETE /file-system-snapshots/transfer": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "remote_names": "2.0", + "remote_ids": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /hardware": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "limit": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.16", + "ids": "2.17", + "names": "2.17" + }, + "bodyProperties": {} + }, + "PATCH /hardware": { + "minVersion": "2.0", + "parameters": { + "X-Request-ID": "2.14", + "ids": "2.17", + "names": "2.17" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "details": "2.0", + "identify_enabled": "2.0", + "index": "2.0", + "model": "2.0", + "serial": "2.0", + "slot": "2.0", + "speed": "2.0", + "status": "2.0", + "temperature": "2.0", + "type": "2.0", + "part_number": "2.8", + "management_mac": "2.16", + "data_mac": "2.16", + "sensor_readings": "2.18" + }, + "readOnlyBodyProperties": [ + "data_mac", + "details", + "id", + "index", + "management_mac", + "model", + "name", + "part_number", + "sensor_readings", + "serial", + "slot", + "speed", + "status", + "temperature", + "type" + ] + }, + "GET /hardware-connectors": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "limit": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "ids": "2.17", + "names": "2.17" + }, + "bodyProperties": {} + }, + "PATCH /hardware-connectors": { + "minVersion": "2.0", + "parameters": { + "X-Request-ID": "2.14", + "ids": "2.17", + "names": "2.17" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "connector_type": "2.0", + "lane_speed": "2.0", + "port_count": "2.0", + "transceiver_type": "2.0", + "port_speed": "2.15", + "lanes_per_port": "2.20" + }, + "readOnlyBodyProperties": [ + "connector_type", + "id", + "name", + "transceiver_type" + ] + }, + "GET /keytabs": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "DELETE /keytabs": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /keytabs": { + "minVersion": "2.0", + "parameters": { + "name_prefixes": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "source": "2.0" + } + }, + "GET /keytabs/download": { + "minVersion": "2.0", + "parameters": { + "keytab_ids": "2.0", + "keytab_names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /keytabs/upload": { + "minVersion": "2.0", + "parameters": { + "name_prefixes": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "keytab_file": "2.16" + } + }, + "GET /lifecycle-rules": { + "minVersion": "2.0", + "parameters": { + "bucket_ids": "2.0", + "bucket_names": "2.0", + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /lifecycle-rules": { + "minVersion": "2.0", + "parameters": { + "confirm_date": "2.1", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "bucket": "2.0", + "rule_id": "2.0", + "keep_previous_version_for": "2.0", + "prefix": "2.0", + "abort_incomplete_multipart_uploads_after": "2.1", + "keep_current_version_for": "2.1", + "keep_current_version_until": "2.1" + } + }, + "PATCH /lifecycle-rules": { + "minVersion": "2.0", + "parameters": { + "bucket_ids": "2.0", + "bucket_names": "2.0", + "ids": "2.0", + "names": "2.0", + "confirm_date": "2.1", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "enabled": "2.0", + "keep_previous_version_for": "2.0", + "prefix": "2.0", + "abort_incomplete_multipart_uploads_after": "2.1", + "keep_current_version_for": "2.1", + "keep_current_version_until": "2.1" + } + }, + "DELETE /lifecycle-rules": { + "minVersion": "2.0", + "parameters": { + "bucket_ids": "2.0", + "bucket_names": "2.0", + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /link-aggregation-groups": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /link-aggregation-groups": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "lag_speed": "2.0", + "mac_address": "2.0", + "ports": "2.0", + "port_speed": "2.0", + "status": "2.0" + }, + "readOnlyBodyProperties": [ + "id", + "lag_speed", + "mac_address", + "name", + "port_speed", + "status" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /link-aggregation-groups": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "ports": "2.0", + "add_ports": "2.0", + "remove_ports": "2.0" + } + }, + "DELETE /link-aggregation-groups": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /logs": { + "minVersion": "2.0", + "parameters": { + "end_time": "2.0", + "start_time": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /network-interfaces": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /network-interfaces": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "address": "2.0", + "enabled": "2.0", + "gateway": "2.0", + "mtu": "2.0", + "netmask": "2.0", + "services": "2.0", + "subnet": "2.0", + "type": "2.0", + "vlan": "2.0", + "server": "2.16", + "realms": "2.19", + "attached_servers": "2.20", + "rdma_enabled": "2.28" + }, + "readOnlyBodyProperties": [ + "id", + "name", + "realms" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /network-interfaces": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "address": "2.0", + "services": "2.0", + "server": "2.16", + "attached_servers": "2.20", + "rdma_enabled": "2.28" + } + }, + "DELETE /network-interfaces": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /object-store-access-keys": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /object-store-access-keys": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "user": "2.0", + "secret_access_key": "2.0" + }, + "parameterComponentOverrides": { + "names": "Access_key_names" + } + }, + "PATCH /object-store-access-keys": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.0", + "created": "2.0", + "enabled": "2.0", + "secret_access_key": "2.0", + "user": "2.0", + "context": "2.17", + "access_key_id": "2.20" + }, + "readOnlyBodyProperties": [ + "access_key_id", + "context", + "created", + "name", + "secret_access_key", + "user" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /object-store-access-keys": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "GET /object-store-access-policies": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "exclude_rules": "2.4", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /object-store-access-policies/object-store-users": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "limit": "2.0", + "member_ids": "2.0", + "member_names": "2.0", + "offset": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /object-store-access-policies/object-store-users": { + "minVersion": "2.0", + "parameters": { + "member_ids": "2.0", + "member_names": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "DELETE /object-store-access-policies/object-store-users": { + "minVersion": "2.0", + "parameters": { + "member_ids": "2.0", + "member_names": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /object-store-accounts": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "total_only": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /object-store-accounts": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "bucket_defaults": "2.8", + "hard_limit_enabled": "2.8", + "quota_limit": "2.8", + "account_exports": "2.20" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /object-store-accounts": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /object-store-remote-credentials": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /object-store-remote-credentials": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "access_key_id": "2.0", + "secret_access_key": "2.0" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /object-store-remote-credentials": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "access_key_id": "2.0", + "secret_access_key": "2.0", + "remote": "2.0", + "context": "2.17", + "realms": "2.20" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "realms" + ] + }, + "DELETE /object-store-remote-credentials": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /object-store-users": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /object-store-users": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "full_access": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /object-store-users": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /object-store-users/object-store-access-policies": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "limit": "2.0", + "member_ids": "2.0", + "member_names": "2.0", + "offset": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /object-store-users/object-store-access-policies": { + "minVersion": "2.0", + "parameters": { + "member_ids": "2.0", + "member_names": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "DELETE /object-store-users/object-store-access-policies": { + "minVersion": "2.0", + "parameters": { + "member_ids": "2.0", + "member_names": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /object-store-virtual-hosts": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /object-store-virtual-hosts": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "id": "2.20", + "name": "2.20", + "context": "2.20", + "attached_servers": "2.20", + "hostname": "2.20", + "realms": "2.20" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "name", + "realms" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /object-store-virtual-hosts": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /policies": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17", + "workload_ids": "2.23", + "workload_names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /policies": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "enabled": "2.0", + "is_local": "2.0", + "location": "2.0", + "rules": "2.0", + "policy_type": "2.2", + "retention_lock": "2.5", + "context": "2.17", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms", + "retention_lock" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /policies": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "PATCH /policies": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "destroy_snapshots": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "enabled": "2.0", + "is_local": "2.0", + "location": "2.0", + "rules": "2.0", + "add_rules": "2.0", + "remove_rules": "2.0", + "policy_type": "2.2", + "retention_lock": "2.5", + "context": "2.17", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "name", + "policy_type", + "realms", + "retention_lock" + ] + }, + "GET /policies/file-systems": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "limit": "2.0", + "member_ids": "2.0", + "member_names": "2.0", + "offset": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /policies/file-systems": { + "minVersion": "2.0", + "parameters": { + "member_ids": "2.0", + "member_names": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "DELETE /policies/file-systems": { + "minVersion": "2.0", + "parameters": { + "member_ids": "2.0", + "member_names": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /policies/file-system-snapshots": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "limit": "2.0", + "member_ids": "2.0", + "member_names": "2.0", + "offset": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "DELETE /policies/file-system-snapshots": { + "minVersion": "2.0", + "parameters": { + "member_ids": "2.0", + "member_names": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /policies/members": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "limit": "2.0", + "local_file_system_ids": "2.0", + "local_file_system_names": "2.0", + "member_ids": "2.0", + "member_names": "2.0", + "member_types": "2.0", + "offset": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "remote_ids": "2.0", + "remote_file_system_ids": "2.0", + "remote_file_system_names": "2.0", + "remote_names": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "member_types": "Member_types" + } + }, + "GET /policies/file-system-replica-links": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "limit": "2.0", + "local_file_system_ids": "2.0", + "local_file_system_names": "2.0", + "member_ids": "2.0", + "offset": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "remote_ids": "2.0", + "remote_file_system_ids": "2.0", + "remote_file_system_names": "2.0", + "remote_names": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /policies/file-system-replica-links": { + "minVersion": "2.0", + "parameters": { + "local_file_system_ids": "2.0", + "local_file_system_names": "2.0", + "member_ids": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "remote_ids": "2.0", + "remote_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "DELETE /policies/file-system-replica-links": { + "minVersion": "2.0", + "parameters": { + "local_file_system_ids": "2.0", + "local_file_system_names": "2.0", + "member_ids": "2.0", + "policy_ids": "2.0", + "policy_names": "2.0", + "remote_ids": "2.0", + "remote_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /quotas/groups": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "file_system_ids": "2.0", + "file_system_names": "2.0", + "filter": "2.0", + "gids": "2.0", + "group_names": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "gids": "Group_quota_gids" + } + }, + "POST /quotas/groups": { + "minVersion": "2.0", + "parameters": { + "file_system_ids": "2.0", + "file_system_names": "2.0", + "gids": "2.0", + "group_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.0", + "quota": "2.0" + }, + "readOnlyBodyProperties": [ + "name" + ], + "parameterComponentOverrides": { + "gids": "Group_quota_gids" + } + }, + "DELETE /quotas/groups": { + "minVersion": "2.0", + "parameters": { + "file_system_names": "2.0", + "file_system_ids": "2.0", + "gids": "2.0", + "group_names": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "gids": "Group_quota_gids" + } + }, + "PATCH /quotas/groups": { + "minVersion": "2.0", + "parameters": { + "file_system_names": "2.0", + "file_system_ids": "2.0", + "gids": "2.0", + "group_names": "2.0", + "names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.0", + "quota": "2.0" + }, + "readOnlyBodyProperties": [ + "name" + ], + "parameterComponentOverrides": { + "gids": "Group_quota_gids" + } + }, + "GET /quotas/settings": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "PATCH /quotas/settings": { + "minVersion": "2.0", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "contact": "2.0", + "direct_notifications_enabled": "2.0" + }, + "readOnlyBodyProperties": [ + "id", + "name" + ] + }, + "GET /quotas/users": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "file_system_ids": "2.0", + "file_system_names": "2.0", + "filter": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "uids": "2.0", + "user_names": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "uids": "User_quota_uids" + } + }, + "POST /quotas/users": { + "minVersion": "2.0", + "parameters": { + "file_system_ids": "2.0", + "file_system_names": "2.0", + "uids": "2.0", + "user_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.0", + "quota": "2.0" + }, + "readOnlyBodyProperties": [ + "name" + ], + "parameterComponentOverrides": { + "uids": "User_quota_uids" + } + }, + "DELETE /quotas/users": { + "minVersion": "2.0", + "parameters": { + "file_system_names": "2.0", + "file_system_ids": "2.0", + "names": "2.0", + "uids": "2.0", + "user_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "uids": "User_quota_uids" + } + }, + "PATCH /quotas/users": { + "minVersion": "2.0", + "parameters": { + "file_system_names": "2.0", + "file_system_ids": "2.0", + "names": "2.0", + "uids": "2.0", + "user_names": "2.0", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.0", + "quota": "2.0" + }, + "readOnlyBodyProperties": [ + "name" + ], + "parameterComponentOverrides": { + "uids": "User_quota_uids" + } + }, + "GET /roles": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "ids": "2.0", + "filter": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /smtp-servers": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "PATCH /smtp-servers": { + "minVersion": "2.0", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "relay_host": "2.0", + "sender_domain": "2.0", + "encryption_mode": "2.15" + }, + "readOnlyBodyProperties": [ + "id", + "name" + ] + }, + "GET /snmp-agents": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "PATCH /snmp-agents": { + "minVersion": "2.0", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "engine_id": "2.0", + "version": "2.0", + "v2c": "2.0", + "v3": "2.0" + }, + "readOnlyBodyProperties": [ + "engine_id", + "id", + "name" + ] + }, + "GET /snmp-agents/mib": { + "minVersion": "2.0", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /snmp-managers": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "ids": "2.0", + "filter": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /snmp-managers": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "host": "2.0", + "notification": "2.0", + "version": "2.0", + "v2c": "2.0", + "v3": "2.0" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /snmp-managers": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "host": "2.0", + "notification": "2.0", + "version": "2.0", + "v2c": "2.0", + "v3": "2.0" + }, + "readOnlyBodyProperties": [ + "id" + ] + }, + "DELETE /snmp-managers": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /snmp-managers/test": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "ids": "2.0", + "filter": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /subnets": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /subnets": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "id": "2.18", + "name": "2.18", + "enabled": "2.18", + "gateway": "2.18", + "interfaces": "2.18", + "link_aggregation_group": "2.18", + "mtu": "2.18", + "prefix": "2.18", + "services": "2.18", + "vlan": "2.18" + }, + "readOnlyBodyProperties": [ + "enabled", + "id", + "interfaces", + "name", + "services" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /subnets": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "id": "2.18", + "name": "2.18", + "enabled": "2.18", + "gateway": "2.18", + "interfaces": "2.18", + "link_aggregation_group": "2.18", + "mtu": "2.18", + "prefix": "2.18", + "services": "2.18", + "vlan": "2.18" + }, + "readOnlyBodyProperties": [ + "enabled", + "id", + "interfaces", + "name", + "services" + ] + }, + "DELETE /subnets": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /support": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "PATCH /support": { + "minVersion": "2.0", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "phonehome_enabled": "2.0", + "proxy": "2.0", + "remote_assist_active": "2.0", + "remote_assist_opened": "2.0", + "remote_assist_expires": "2.0", + "remote_assist_status": "2.0", + "remote_assist_paths": "2.0", + "remote_assist_duration": "2.14", + "edge_agent_update_enabled": "2.18", + "edge_management_enabled": "2.18" + }, + "readOnlyBodyProperties": [ + "id", + "name", + "remote_assist_expires", + "remote_assist_opened", + "remote_assist_paths", + "remote_assist_status" + ] + }, + "GET /support/test": { + "minVersion": "2.0", + "parameters": { + "filter": "2.0", + "sort": "2.0", + "test_type": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /syslog-servers": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "names": "Names_for_syslog" + } + }, + "POST /syslog-servers": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "uri": "2.14", + "services": "2.14", + "sources": "2.20" + }, + "parameterComponentOverrides": { + "names": "Names_for_syslog" + } + }, + "PATCH /syslog-servers": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "uri": "2.14", + "services": "2.14", + "sources": "2.20" + }, + "parameterComponentOverrides": { + "names": "Names_for_syslog" + } + }, + "DELETE /syslog-servers": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "names": "Names_for_syslog" + } + }, + "GET /syslog-servers/settings": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "PATCH /syslog-servers/settings": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "ca_certificate": "2.0", + "ca_certificate_group": "2.0" + }, + "readOnlyBodyProperties": [ + "id", + "name" + ] + }, + "GET /syslog-servers/test": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /targets": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /targets": { + "minVersion": "2.0", + "parameters": { + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "address": "2.0" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /targets": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.0", + "id": "2.0", + "address": "2.0", + "ca_certificate_group": "2.0", + "status": "2.0", + "status_details": "2.0" + }, + "readOnlyBodyProperties": [ + "id", + "status", + "status_details" + ] + }, + "DELETE /targets": { + "minVersion": "2.0", + "parameters": { + "ids": "2.0", + "names": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /targets/performance/replication": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "end_time": "2.0", + "filter": "2.0", + "ids": "2.0", + "limit": "2.0", + "names": "2.0", + "offset": "2.0", + "resolution": "2.0", + "sort": "2.0", + "start_time": "2.0", + "total_only": "2.0", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /usage/groups": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "file_system_ids": "2.0", + "file_system_names": "2.0", + "filter": "2.0", + "gids": "2.0", + "group_names": "2.0", + "limit": "2.0", + "offset": "2.0", + "sort": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "gids": "Group_quota_gids" + } + }, + "GET /usage/users": { + "minVersion": "2.0", + "parameters": { + "continuation_token": "2.0", + "file_system_ids": "2.0", + "file_system_names": "2.0", + "filter": "2.0", + "limit": "2.0", + "offset": "2.0", + "sort": "2.0", + "uids": "2.0", + "user_names": "2.0", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "uids": "User_quota_uids" + } + }, + "GET /arrays/factory-reset-token": { + "minVersion": "2.1", + "parameters": { + "continuation_token": "2.1", + "filter": "2.1", + "limit": "2.1", + "offset": "2.1", + "sort": "2.1", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "DELETE /arrays/factory-reset-token": { + "minVersion": "2.1", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /arrays/factory-reset-token": { + "minVersion": "2.1", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /kmip": { + "minVersion": "2.1", + "parameters": { + "names": "2.1", + "ids": "2.1", + "filter": "2.1", + "limit": "2.1", + "offset": "2.1", + "sort": "2.1", + "continuation_token": "2.1", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /kmip": { + "minVersion": "2.1", + "parameters": { + "names": "2.1", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "id": "2.18", + "name": "2.18", + "ca_certificate": "2.18", + "ca_certificate_group": "2.18", + "uris": "2.18" + }, + "readOnlyBodyProperties": [ + "id", + "name" + ] + }, + "PATCH /kmip": { + "minVersion": "2.1", + "parameters": { + "names": "2.1", + "ids": "2.1", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "id": "2.18", + "name": "2.18", + "ca_certificate": "2.18", + "ca_certificate_group": "2.18", + "uris": "2.18" + }, + "readOnlyBodyProperties": [ + "id", + "name" + ] + }, + "DELETE /kmip": { + "minVersion": "2.1", + "parameters": { + "names": "2.1", + "ids": "2.1", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /kmip/test": { + "minVersion": "2.1", + "parameters": { + "names": "2.1", + "ids": "2.1", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /rapid-data-locking": { + "minVersion": "2.1", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "PATCH /rapid-data-locking": { + "minVersion": "2.1", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "enabled": "2.1", + "kmip_server": "2.1" + } + }, + "GET /rapid-data-locking/test": { + "minVersion": "2.1", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /rapid-data-locking/rotate": { + "minVersion": "2.1", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /object-store-access-policies": { + "minVersion": "2.2", + "parameters": { + "enforce_action_restrictions": "2.2", + "names": "2.2", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "rules": "2.2", + "description": "2.2" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /object-store-access-policies": { + "minVersion": "2.2", + "parameters": { + "enforce_action_restrictions": "2.2", + "ids": "2.2", + "names": "2.2", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "rules": "2.2" + } + }, + "DELETE /object-store-access-policies": { + "minVersion": "2.2", + "parameters": { + "ids": "2.2", + "names": "2.2", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /object-store-access-policies/rules": { + "minVersion": "2.2", + "parameters": { + "continuation_token": "2.2", + "filter": "2.2", + "limit": "2.2", + "names": "2.2", + "offset": "2.2", + "policy_ids": "2.2", + "policy_names": "2.2", + "sort": "2.2", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /object-store-access-policies/rules": { + "minVersion": "2.2", + "parameters": { + "enforce_action_restrictions": "2.2", + "names": "2.2", + "policy_ids": "2.2", + "policy_names": "2.2", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "actions": "2.2", + "conditions": "2.2", + "resources": "2.2", + "effect": "2.18" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /object-store-access-policies/rules": { + "minVersion": "2.2", + "parameters": { + "enforce_action_restrictions": "2.2", + "names": "2.2", + "policy_ids": "2.2", + "policy_names": "2.2", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.2", + "actions": "2.2", + "conditions": "2.2", + "effect": "2.2", + "policy": "2.2", + "resources": "2.2" + } + }, + "DELETE /object-store-access-policies/rules": { + "minVersion": "2.2", + "parameters": { + "names": "2.2", + "policy_ids": "2.2", + "policy_names": "2.2", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /object-store-access-policy-actions": { + "minVersion": "2.2", + "parameters": { + "continuation_token": "2.2", + "filter": "2.2", + "limit": "2.2", + "names": "2.2", + "offset": "2.2", + "sort": "2.2", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /policies-all": { + "minVersion": "2.2", + "parameters": { + "continuation_token": "2.2", + "filter": "2.2", + "ids": "2.2", + "limit": "2.2", + "names": "2.2", + "offset": "2.2", + "sort": "2.2", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /policies-all/members": { + "minVersion": "2.2", + "parameters": { + "continuation_token": "2.2", + "filter": "2.2", + "limit": "2.2", + "local_file_system_ids": "2.2", + "local_file_system_names": "2.2", + "member_ids": "2.2", + "member_names": "2.2", + "member_types": "2.2", + "offset": "2.2", + "policy_ids": "2.2", + "policy_names": "2.2", + "remote_ids": "2.2", + "remote_file_system_ids": "2.2", + "remote_file_system_names": "2.2", + "remote_names": "2.2", + "sort": "2.2", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "member_types": "Member_types" + } + }, + "GET /admins/settings": { + "minVersion": "2.3", + "parameters": { + "continuation_token": "2.3", + "filter": "2.3", + "limit": "2.3", + "offset": "2.3", + "sort": "2.3", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "PATCH /admins/settings": { + "minVersion": "2.3", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "lockout_duration": "2.3", + "max_login_attempts": "2.3", + "min_password_length": "2.3" + } + }, + "GET /file-systems/policies-all": { + "minVersion": "2.3", + "parameters": { + "continuation_token": "2.3", + "filter": "2.3", + "limit": "2.3", + "member_ids": "2.3", + "member_names": "2.3", + "offset": "2.3", + "policy_ids": "2.3", + "policy_names": "2.3", + "sort": "2.3", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /hardware-connectors/performance": { + "minVersion": "2.3", + "parameters": { + "end_time": "2.3", + "filter": "2.3", + "ids": "2.3", + "limit": "2.3", + "names": "2.3", + "offset": "2.3", + "resolution": "2.3", + "sort": "2.3", + "start_time": "2.3", + "total_only": "2.3", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /nfs-export-policies": { + "minVersion": "2.3", + "parameters": { + "continuation_token": "2.3", + "filter": "2.3", + "ids": "2.3", + "limit": "2.3", + "names": "2.3", + "offset": "2.3", + "sort": "2.3", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17", + "workload_ids": "2.23", + "workload_names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /nfs-export-policies": { + "minVersion": "2.3", + "parameters": { + "names": "2.3", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.3", + "id": "2.3", + "enabled": "2.3", + "is_local": "2.3", + "location": "2.3", + "policy_type": "2.3", + "rules": "2.3", + "version": "2.3", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /nfs-export-policies": { + "minVersion": "2.3", + "parameters": { + "ids": "2.3", + "names": "2.3", + "versions": "2.3", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.3", + "id": "2.3", + "enabled": "2.3", + "is_local": "2.3", + "location": "2.3", + "policy_type": "2.3", + "rules": "2.3", + "version": "2.3", + "context": "2.17", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms", + "version" + ] + }, + "DELETE /nfs-export-policies": { + "minVersion": "2.3", + "parameters": { + "ids": "2.3", + "names": "2.3", + "versions": "2.3", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /nfs-export-policies/rules": { + "minVersion": "2.3", + "parameters": { + "continuation_token": "2.3", + "filter": "2.3", + "ids": "2.3", + "limit": "2.3", + "names": "2.3", + "offset": "2.3", + "policy_ids": "2.3", + "policy_names": "2.3", + "sort": "2.3", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /nfs-export-policies/rules": { + "minVersion": "2.3", + "parameters": { + "before_rule_id": "2.3", + "before_rule_name": "2.3", + "policy_ids": "2.3", + "policy_names": "2.3", + "versions": "2.3", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "id": "2.18", + "name": "2.18", + "access": "2.18", + "anongid": "2.18", + "anonuid": "2.18", + "atime": "2.18", + "client": "2.18", + "fileid_32bit": "2.18", + "permission": "2.18", + "policy": "2.18", + "policy_version": "2.18", + "required_transport_security": "2.18", + "secure": "2.18", + "security": "2.18", + "index": "2.18", + "context": "2.18" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "name", + "policy_version" + ] + }, + "DELETE /nfs-export-policies/rules": { + "minVersion": "2.3", + "parameters": { + "ids": "2.3", + "names": "2.3", + "versions": "2.3", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "PATCH /nfs-export-policies/rules": { + "minVersion": "2.3", + "parameters": { + "before_rule_id": "2.3", + "before_rule_name": "2.3", + "ids": "2.3", + "names": "2.3", + "versions": "2.3", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "id": "2.18", + "name": "2.18", + "access": "2.18", + "anongid": "2.18", + "anonuid": "2.18", + "atime": "2.18", + "client": "2.18", + "fileid_32bit": "2.18", + "permission": "2.18", + "policy": "2.18", + "policy_version": "2.18", + "required_transport_security": "2.18", + "secure": "2.18", + "security": "2.18", + "index": "2.18", + "context": "2.18" + }, + "readOnlyBodyProperties": [ + "id", + "name" + ] + }, + "GET /sessions": { + "minVersion": "2.3", + "parameters": { + "continuation_token": "2.3", + "filter": "2.3", + "ids": "2.3", + "limit": "2.3", + "names": "2.3", + "offset": "2.3", + "sort": "2.3", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /drives": { + "minVersion": "2.4", + "parameters": { + "continuation_token": "2.4", + "filter": "2.4", + "ids": "2.4", + "limit": "2.4", + "names": "2.4", + "offset": "2.4", + "sort": "2.4", + "total_only": "2.4", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /logs-async": { + "minVersion": "2.4", + "parameters": { + "continuation_token": "2.4", + "filter": "2.4", + "ids": "2.4", + "limit": "2.4", + "names": "2.4", + "offset": "2.4", + "sort": "2.4", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "PATCH /logs-async": { + "minVersion": "2.4", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.4", + "id": "2.4", + "start_time": "2.4", + "end_time": "2.4", + "last_request_time": "2.4", + "processing": "2.4", + "progress": "2.4", + "hardware_components": "2.4", + "available_files": "2.4" + }, + "readOnlyBodyProperties": [ + "available_files", + "id", + "last_request_time", + "name", + "processing", + "progress" + ] + }, + "GET /logs-async/download": { + "minVersion": "2.4", + "parameters": { + "names": "2.4", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /network-interfaces/ping": { + "minVersion": "2.6", + "parameters": { + "packet_size": "2.6", + "count": "2.6", + "component_name": "2.6", + "destination": "2.6", + "source": "2.6", + "print_latency": "2.6", + "resolve_hostname": "2.6", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /network-interfaces/trace": { + "minVersion": "2.6", + "parameters": { + "fragment_packet": "2.6", + "method": "2.6", + "discover_mtu": "2.6", + "component_name": "2.6", + "destination": "2.6", + "source": "2.6", + "port": "2.6", + "resolve_hostname": "2.6", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /support/verification-keys": { + "minVersion": "2.7", + "parameters": { + "continuation_token": "2.7", + "filter": "2.7", + "limit": "2.7", + "offset": "2.7", + "sort": "2.7", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "PATCH /support/verification-keys": { + "minVersion": "2.7", + "parameters": { + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "signed_verification_key": "2.7" + } + }, + "GET /file-systems/locks": { + "minVersion": "2.8", + "parameters": { + "continuation_token": "2.8", + "client_names": "2.8", + "file_system_ids": "2.8", + "file_system_names": "2.8", + "filter": "2.8", + "inodes": "2.8", + "limit": "2.8", + "names": "2.8", + "paths": "2.8", + "X-Request-ID": "2.14", + "allow_errors": "2.22", + "context_names": "2.22" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "DELETE /file-systems/locks": { + "minVersion": "2.8", + "parameters": { + "client_names": "2.8", + "names": "2.8", + "file_system_ids": "2.10", + "file_system_names": "2.10", + "inodes": "2.10", + "paths": "2.10", + "recursive": "2.10", + "X-Request-ID": "2.14", + "context_names": "2.22" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "recursive": "Recursive" + } + }, + "GET /file-systems/locks/clients": { + "minVersion": "2.8", + "parameters": { + "continuation_token": "2.8", + "filter": "2.8", + "limit": "2.8", + "X-Request-ID": "2.14", + "allow_errors": "2.22", + "context_names": "2.22" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /file-systems/locks/nlm-reclamations": { + "minVersion": "2.8", + "parameters": { + "X-Request-ID": "2.14", + "context_names": "2.22" + }, + "bodyProperties": {} + }, + "PATCH /object-store-accounts": { + "minVersion": "2.8", + "parameters": { + "ids": "2.8", + "ignore_usage": "2.8", + "names": "2.8", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "bucket_defaults": "2.8", + "hard_limit_enabled": "2.8", + "quota_limit": "2.8", + "public_access_config": "2.12" + }, + "parameterComponentOverrides": { + "ignore_usage": "Ignore_usage" + } + }, + "DELETE /file-system-replica-links": { + "minVersion": "2.10", + "parameters": { + "ids": "2.10", + "local_file_system_ids": "2.10", + "local_file_system_names": "2.10", + "remote_file_system_ids": "2.10", + "remote_file_system_names": "2.10", + "remote_ids": "2.10", + "remote_names": "2.10", + "cancel_in_progress_transfers": "2.10", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /file-systems/sessions": { + "minVersion": "2.10", + "parameters": { + "continuation_token": "2.10", + "client_names": "2.10", + "limit": "2.10", + "names": "2.10", + "protocols": "2.10", + "user_names": "2.10", + "X-Request-ID": "2.14", + "allow_errors": "2.22", + "context_names": "2.22" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "user_names": "Sessions_user_names" + } + }, + "DELETE /file-systems/sessions": { + "minVersion": "2.10", + "parameters": { + "client_names": "2.10", + "disruptive": "2.10", + "names": "2.10", + "protocols": "2.10", + "user_names": "2.10", + "X-Request-ID": "2.14", + "context_names": "2.22" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "user_names": "Sessions_user_names" + } + }, + "GET /smb-client-policies": { + "minVersion": "2.10", + "parameters": { + "continuation_token": "2.10", + "filter": "2.10", + "ids": "2.10", + "limit": "2.10", + "names": "2.10", + "offset": "2.10", + "sort": "2.10", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17", + "workload_ids": "2.23", + "workload_names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /smb-client-policies": { + "minVersion": "2.10", + "parameters": { + "names": "2.10", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.10", + "id": "2.10", + "enabled": "2.10", + "is_local": "2.10", + "location": "2.10", + "policy_type": "2.10", + "rules": "2.10", + "access_based_enumeration_enabled": "2.17", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /smb-client-policies": { + "minVersion": "2.10", + "parameters": { + "ids": "2.10", + "names": "2.10", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.10", + "id": "2.10", + "enabled": "2.10", + "is_local": "2.10", + "location": "2.10", + "policy_type": "2.10", + "version": "2.10", + "rules": "2.10", + "context": "2.17", + "access_based_enumeration_enabled": "2.17", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms", + "version" + ] + }, + "DELETE /smb-client-policies": { + "minVersion": "2.10", + "parameters": { + "ids": "2.10", + "names": "2.10", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /smb-client-policies/rules": { + "minVersion": "2.10", + "parameters": { + "continuation_token": "2.10", + "filter": "2.10", + "ids": "2.10", + "limit": "2.10", + "names": "2.10", + "offset": "2.10", + "policy_ids": "2.10", + "policy_names": "2.10", + "sort": "2.10", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /smb-client-policies/rules": { + "minVersion": "2.10", + "parameters": { + "before_rule_id": "2.10", + "before_rule_name": "2.10", + "policy_ids": "2.10", + "policy_names": "2.10", + "versions": "2.10", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.10", + "id": "2.10", + "client": "2.10", + "permission": "2.10", + "index": "2.10", + "encryption": "2.11" + }, + "readOnlyBodyProperties": [ + "id", + "name" + ] + }, + "PATCH /smb-client-policies/rules": { + "minVersion": "2.10", + "parameters": { + "before_rule_id": "2.10", + "before_rule_name": "2.10", + "ids": "2.10", + "names": "2.10", + "versions": "2.10", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.10", + "id": "2.10", + "client": "2.10", + "permission": "2.10", + "policy": "2.10", + "policy_version": "2.10", + "index": "2.10", + "encryption": "2.11", + "context": "2.17" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "name", + "policy_version" + ] + }, + "DELETE /smb-client-policies/rules": { + "minVersion": "2.10", + "parameters": { + "ids": "2.10", + "names": "2.10", + "versions": "2.10", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /smb-share-policies": { + "minVersion": "2.10", + "parameters": { + "continuation_token": "2.10", + "filter": "2.10", + "ids": "2.10", + "limit": "2.10", + "names": "2.10", + "offset": "2.10", + "sort": "2.10", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17", + "workload_ids": "2.23", + "workload_names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /smb-share-policies": { + "minVersion": "2.10", + "parameters": { + "names": "2.10", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.10", + "id": "2.10", + "enabled": "2.10", + "is_local": "2.10", + "location": "2.10", + "policy_type": "2.10", + "rules": "2.10", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /smb-share-policies": { + "minVersion": "2.10", + "parameters": { + "ids": "2.10", + "names": "2.10", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.10", + "id": "2.10", + "enabled": "2.10", + "is_local": "2.10", + "location": "2.10", + "policy_type": "2.10", + "rules": "2.10", + "context": "2.17", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ] + }, + "DELETE /smb-share-policies": { + "minVersion": "2.10", + "parameters": { + "ids": "2.10", + "names": "2.10", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /smb-share-policies/rules": { + "minVersion": "2.10", + "parameters": { + "continuation_token": "2.10", + "filter": "2.10", + "ids": "2.10", + "limit": "2.10", + "names": "2.10", + "offset": "2.10", + "policy_ids": "2.10", + "policy_names": "2.10", + "sort": "2.10", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /smb-share-policies/rules": { + "minVersion": "2.10", + "parameters": { + "policy_ids": "2.10", + "policy_names": "2.10", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.10", + "id": "2.10", + "change": "2.10", + "full_control": "2.10", + "principal": "2.10", + "read": "2.10" + }, + "readOnlyBodyProperties": [ + "id", + "name" + ] + }, + "PATCH /smb-share-policies/rules": { + "minVersion": "2.10", + "parameters": { + "ids": "2.10", + "names": "2.10", + "policy_ids": "2.10", + "policy_names": "2.10", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.10", + "id": "2.10", + "change": "2.10", + "full_control": "2.10", + "policy": "2.10", + "principal": "2.10", + "read": "2.10" + }, + "readOnlyBodyProperties": [ + "id", + "name" + ] + }, + "DELETE /smb-share-policies/rules": { + "minVersion": "2.10", + "parameters": { + "ids": "2.10", + "names": "2.10", + "policy_ids": "2.10", + "policy_names": "2.10", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /buckets/cross-origin-resource-sharing-policies": { + "minVersion": "2.12", + "parameters": { + "bucket_ids": "2.12", + "bucket_names": "2.12", + "continuation_token": "2.12", + "filter": "2.12", + "limit": "2.12", + "names": "2.12", + "offset": "2.12", + "sort": "2.12", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /buckets/cross-origin-resource-sharing-policies": { + "minVersion": "2.12", + "parameters": { + "bucket_ids": "2.12", + "bucket_names": "2.12", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "rules": "2.12" + } + }, + "DELETE /buckets/cross-origin-resource-sharing-policies": { + "minVersion": "2.12", + "parameters": { + "bucket_ids": "2.12", + "bucket_names": "2.12", + "names": "2.12", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /buckets/cross-origin-resource-sharing-policies/rules": { + "minVersion": "2.12", + "parameters": { + "bucket_ids": "2.12", + "bucket_names": "2.12", + "continuation_token": "2.12", + "filter": "2.12", + "limit": "2.12", + "names": "2.12", + "offset": "2.12", + "policy_names": "2.12", + "sort": "2.12", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /buckets/cross-origin-resource-sharing-policies/rules": { + "minVersion": "2.12", + "parameters": { + "bucket_ids": "2.12", + "bucket_names": "2.12", + "names": "2.12", + "policy_names": "2.12", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "allowed_headers": "2.12", + "allowed_methods": "2.12", + "allowed_origins": "2.12" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /buckets/cross-origin-resource-sharing-policies/rules": { + "minVersion": "2.12", + "parameters": { + "bucket_ids": "2.12", + "bucket_names": "2.12", + "names": "2.12", + "policy_names": "2.12", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /buckets/bucket-access-policies": { + "minVersion": "2.12", + "parameters": { + "bucket_ids": "2.12", + "bucket_names": "2.12", + "continuation_token": "2.12", + "filter": "2.12", + "limit": "2.12", + "names": "2.12", + "offset": "2.12", + "sort": "2.12", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /buckets/bucket-access-policies": { + "minVersion": "2.12", + "parameters": { + "bucket_ids": "2.12", + "bucket_names": "2.12", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "rules": "2.12" + } + }, + "DELETE /buckets/bucket-access-policies": { + "minVersion": "2.12", + "parameters": { + "bucket_ids": "2.12", + "bucket_names": "2.12", + "names": "2.12", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /buckets/bucket-access-policies/rules": { + "minVersion": "2.12", + "parameters": { + "bucket_ids": "2.12", + "bucket_names": "2.12", + "continuation_token": "2.12", + "filter": "2.12", + "limit": "2.12", + "names": "2.12", + "offset": "2.12", + "policy_names": "2.12", + "sort": "2.12", + "X-Request-ID": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /buckets/bucket-access-policies/rules": { + "minVersion": "2.12", + "parameters": { + "bucket_ids": "2.12", + "bucket_names": "2.12", + "names": "2.12", + "policy_names": "2.12", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "actions": "2.12", + "effect": "2.12", + "principals": "2.12", + "resources": "2.12" + }, + "readOnlyBodyProperties": [ + "effect" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /buckets/bucket-access-policies/rules": { + "minVersion": "2.12", + "parameters": { + "bucket_ids": "2.12", + "bucket_names": "2.12", + "names": "2.12", + "policy_names": "2.12", + "X-Request-ID": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /network-access-policies": { + "minVersion": "2.13", + "parameters": { + "continuation_token": "2.13", + "filter": "2.13", + "ids": "2.13", + "limit": "2.13", + "names": "2.13", + "offset": "2.13", + "sort": "2.13", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "PATCH /network-access-policies": { + "minVersion": "2.13", + "parameters": { + "ids": "2.13", + "names": "2.13", + "versions": "2.13", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.13", + "id": "2.13", + "enabled": "2.13", + "is_local": "2.13", + "location": "2.13", + "policy_type": "2.13", + "version": "2.13", + "rules": "2.13", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms", + "version" + ] + }, + "GET /network-access-policies/members": { + "minVersion": "2.13", + "parameters": { + "continuation_token": "2.13", + "filter": "2.13", + "limit": "2.13", + "member_ids": "2.13", + "member_names": "2.13", + "offset": "2.13", + "policy_ids": "2.13", + "policy_names": "2.13", + "sort": "2.13", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /network-access-policies/rules": { + "minVersion": "2.13", + "parameters": { + "continuation_token": "2.13", + "filter": "2.13", + "ids": "2.13", + "limit": "2.13", + "names": "2.13", + "offset": "2.13", + "policy_ids": "2.13", + "policy_names": "2.13", + "sort": "2.13", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "POST /network-access-policies/rules": { + "minVersion": "2.13", + "parameters": { + "before_rule_id": "2.13", + "before_rule_name": "2.13", + "policy_ids": "2.13", + "policy_names": "2.13", + "versions": "2.13", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.13", + "id": "2.13", + "effect": "2.13", + "client": "2.13", + "interfaces": "2.13", + "index": "2.13" + }, + "readOnlyBodyProperties": [ + "id", + "name" + ] + }, + "PATCH /network-access-policies/rules": { + "minVersion": "2.13", + "parameters": { + "before_rule_id": "2.13", + "before_rule_name": "2.13", + "ids": "2.13", + "names": "2.13", + "versions": "2.13", + "X-Request-ID": "2.14" + }, + "bodyProperties": { + "name": "2.13", + "id": "2.13", + "effect": "2.13", + "client": "2.13", + "interfaces": "2.13", + "policy": "2.13", + "policy_version": "2.13", + "index": "2.13" + }, + "readOnlyBodyProperties": [ + "id", + "name", + "policy_version" + ] + }, + "DELETE /network-access-policies/rules": { + "minVersion": "2.13", + "parameters": { + "ids": "2.13", + "names": "2.13", + "versions": "2.13", + "X-Request-ID": "2.14" + }, + "bodyProperties": {} + }, + "GET /admins/ssh-certificate-authority-policies": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "continuation_token": "2.14", + "filter": "2.14", + "limit": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "offset": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "sort": "2.14", + "allow_errors": "2.24", + "context_names": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /admins/ssh-certificate-authority-policies": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "context_names": "2.24" + }, + "bodyProperties": {} + }, + "DELETE /admins/ssh-certificate-authority-policies": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "context_names": "2.24" + }, + "bodyProperties": {} + }, + "GET /arrays/ssh-certificate-authority-policies": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "continuation_token": "2.14", + "filter": "2.14", + "limit": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "offset": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "sort": "2.14", + "allow_errors": "2.24", + "context_names": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /arrays/ssh-certificate-authority-policies": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "context_names": "2.24" + }, + "bodyProperties": {} + }, + "DELETE /arrays/ssh-certificate-authority-policies": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "context_names": "2.24" + }, + "bodyProperties": {} + }, + "GET /audit-file-systems-policies": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "continuation_token": "2.14", + "filter": "2.14", + "ids": "2.14", + "limit": "2.14", + "names": "2.14", + "offset": "2.14", + "sort": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /audit-file-systems-policies": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "names": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.14", + "id": "2.14", + "enabled": "2.14", + "is_local": "2.14", + "location": "2.14", + "policy_type": "2.14", + "log_targets": "2.14", + "control_type": "2.18", + "rules": "2.18", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /audit-file-systems-policies": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "ids": "2.14", + "names": "2.14", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.14", + "id": "2.14", + "enabled": "2.14", + "is_local": "2.14", + "location": "2.14", + "policy_type": "2.14", + "log_targets": "2.14", + "add_log_targets": "2.14", + "remove_log_targets": "2.14", + "rules": "2.18", + "realms": "2.19", + "control_type": "2.25" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ] + }, + "DELETE /audit-file-systems-policies": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "ids": "2.14", + "names": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /audit-file-systems-policies/members": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "continuation_token": "2.14", + "filter": "2.14", + "limit": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "offset": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "sort": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /audit-file-systems-policies/members": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "DELETE /audit-file-systems-policies/members": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "POST /directory-services/roles": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "names": "2.14" + }, + "bodyProperties": { + "group": "2.18", + "group_base": "2.18", + "id": "2.18", + "name": "2.18", + "role": "2.18", + "management_access_policies": "2.19" + } + }, + "DELETE /directory-services/roles": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "names": "2.14", + "ids": "2.14" + }, + "bodyProperties": {} + }, + "GET /file-systems/audit-policies": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "continuation_token": "2.14", + "filter": "2.14", + "limit": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "offset": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "sort": "2.14", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /file-systems/audit-policies": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "DELETE /file-systems/audit-policies": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /public-keys": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "continuation_token": "2.14", + "filter": "2.14", + "ids": "2.14", + "names": "2.14", + "limit": "2.14", + "offset": "2.14", + "sort": "2.14" + }, + "bodyProperties": {} + }, + "POST /public-keys": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "names": "2.14" + }, + "bodyProperties": { + "public_key": "2.14" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /public-keys": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "ids": "2.14", + "names": "2.14" + }, + "bodyProperties": {} + }, + "GET /public-keys/uses": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "continuation_token": "2.14", + "filter": "2.14", + "ids": "2.14", + "limit": "2.14", + "names": "2.14", + "offset": "2.14", + "sort": "2.14" + }, + "bodyProperties": {} + }, + "GET /ssh-certificate-authority-policies": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "continuation_token": "2.14", + "filter": "2.14", + "ids": "2.14", + "names": "2.14", + "limit": "2.14", + "offset": "2.14", + "sort": "2.14", + "allow_errors": "2.24", + "context_names": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /ssh-certificate-authority-policies": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "names": "2.14" + }, + "bodyProperties": { + "name": "2.14", + "id": "2.14", + "enabled": "2.14", + "is_local": "2.14", + "location": "2.14", + "policy_type": "2.14", + "signing_authority": "2.14", + "static_authorized_principals": "2.14", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /ssh-certificate-authority-policies": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "ids": "2.14", + "names": "2.14" + }, + "bodyProperties": {} + }, + "PATCH /ssh-certificate-authority-policies": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "ids": "2.14", + "names": "2.14" + }, + "bodyProperties": { + "policy_type": "2.14", + "name": "2.14", + "id": "2.14", + "signing_authority": "2.14", + "static_authorized_principals": "2.14", + "enabled": "2.14", + "is_local": "2.14", + "location": "2.14", + "realms": "2.19", + "context": "2.24" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "is_local", + "policy_type", + "realms" + ] + }, + "GET /ssh-certificate-authority-policies/admins": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "continuation_token": "2.14", + "filter": "2.14", + "limit": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "offset": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "sort": "2.14", + "allow_errors": "2.24", + "context_names": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /ssh-certificate-authority-policies/admins": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "context_names": "2.24" + }, + "bodyProperties": {} + }, + "DELETE /ssh-certificate-authority-policies/admins": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "context_names": "2.24" + }, + "bodyProperties": {} + }, + "GET /ssh-certificate-authority-policies/arrays": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "continuation_token": "2.14", + "filter": "2.14", + "limit": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "offset": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "sort": "2.14", + "allow_errors": "2.24", + "context_names": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /ssh-certificate-authority-policies/arrays": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "context_names": "2.24" + }, + "bodyProperties": {} + }, + "DELETE /ssh-certificate-authority-policies/arrays": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "context_names": "2.24" + }, + "bodyProperties": {} + }, + "GET /ssh-certificate-authority-policies/members": { + "minVersion": "2.14", + "parameters": { + "X-Request-ID": "2.14", + "continuation_token": "2.14", + "filter": "2.14", + "limit": "2.14", + "member_ids": "2.14", + "member_names": "2.14", + "offset": "2.14", + "policy_ids": "2.14", + "policy_names": "2.14", + "sort": "2.14", + "allow_errors": "2.24", + "context_names": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /admins": { + "minVersion": "2.15", + "parameters": { + "X-Request-ID": "2.15", + "names": "2.15", + "context_names": "2.24" + }, + "bodyProperties": { + "password": "2.15", + "public_key": "2.15", + "role": "2.15", + "management_access_policies": "2.19", + "admin_type": "2.26" + } + }, + "DELETE /admins": { + "minVersion": "2.15", + "parameters": { + "X-Request-ID": "2.15", + "ids": "2.15", + "names": "2.15", + "context_names": "2.24" + }, + "bodyProperties": {} + }, + "GET /file-systems/worm-data-policies": { + "minVersion": "2.15", + "parameters": { + "X-Request-ID": "2.15", + "continuation_token": "2.15", + "filter": "2.15", + "limit": "2.15", + "member_ids": "2.15", + "member_names": "2.15", + "offset": "2.15", + "policy_ids": "2.15", + "policy_names": "2.15", + "sort": "2.15", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /sso/saml2/idps": { + "minVersion": "2.15", + "parameters": { + "X-Request-ID": "2.15", + "continuation_token": "2.15", + "filter": "2.15", + "ids": "2.15", + "names": "2.15", + "limit": "2.15", + "offset": "2.15", + "sort": "2.15" + }, + "bodyProperties": {} + }, + "POST /sso/saml2/idps": { + "minVersion": "2.15", + "parameters": { + "X-Request-ID": "2.15", + "names": "2.15" + }, + "bodyProperties": { + "name": "2.15", + "id": "2.15", + "enabled": "2.15", + "array_url": "2.15", + "sp": "2.15", + "idp": "2.15", + "binding": "2.17", + "services": "2.17", + "prn": "2.17", + "management": "2.24" + }, + "readOnlyBodyProperties": [ + "prn" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /sso/saml2/idps": { + "minVersion": "2.15", + "parameters": { + "X-Request-ID": "2.15", + "ids": "2.15", + "names": "2.15" + }, + "bodyProperties": {} + }, + "PATCH /sso/saml2/idps": { + "minVersion": "2.15", + "parameters": { + "X-Request-ID": "2.15", + "ids": "2.15", + "names": "2.15" + }, + "bodyProperties": { + "name": "2.15", + "id": "2.15", + "enabled": "2.15", + "array_url": "2.15", + "sp": "2.15", + "idp": "2.15", + "binding": "2.18", + "prn": "2.18", + "services": "2.18", + "management": "2.24" + }, + "readOnlyBodyProperties": [ + "id", + "prn" + ] + }, + "GET /worm-data-policies": { + "minVersion": "2.15", + "parameters": { + "X-Request-ID": "2.15", + "continuation_token": "2.15", + "filter": "2.15", + "limit": "2.15", + "offset": "2.15", + "ids": "2.15", + "names": "2.15", + "sort": "2.15", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /worm-data-policies": { + "minVersion": "2.15", + "parameters": { + "X-Request-ID": "2.15", + "names": "2.15", + "context_names": "2.17" + }, + "bodyProperties": { + "id": "2.18", + "name": "2.18", + "enabled": "2.18", + "is_local": "2.18", + "location": "2.18", + "policy_type": "2.18", + "default_retention": "2.18", + "max_retention": "2.18", + "min_retention": "2.18", + "mode": "2.18", + "retention_lock": "2.18", + "context": "2.18", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "is_local", + "name", + "policy_type", + "realms" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /worm-data-policies": { + "minVersion": "2.15", + "parameters": { + "X-Request-ID": "2.15", + "ids": "2.15", + "names": "2.15", + "context_names": "2.17" + }, + "bodyProperties": { + "id": "2.18", + "name": "2.18", + "enabled": "2.18", + "is_local": "2.18", + "location": "2.18", + "policy_type": "2.18", + "default_retention": "2.18", + "max_retention": "2.18", + "min_retention": "2.18", + "mode": "2.18", + "retention_lock": "2.18", + "context": "2.18", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "is_local", + "name", + "policy_type", + "realms" + ] + }, + "DELETE /worm-data-policies": { + "minVersion": "2.15", + "parameters": { + "X-Request-ID": "2.15", + "ids": "2.15", + "names": "2.15", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /worm-data-policies/members": { + "minVersion": "2.15", + "parameters": { + "X-Request-ID": "2.15", + "continuation_token": "2.15", + "filter": "2.15", + "limit": "2.15", + "member_ids": "2.15", + "member_names": "2.15", + "offset": "2.15", + "policy_ids": "2.15", + "policy_names": "2.15", + "sort": "2.15", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /arrays/space/storage-classes": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "continuation_token": "2.16", + "end_time": "2.16", + "filter": "2.16", + "limit": "2.16", + "offset": "2.16", + "resolution": "2.16", + "sort": "2.16", + "start_time": "2.16", + "storage_class_names": "2.16", + "total_only": "2.16" + }, + "bodyProperties": {} + }, + "POST /dns": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "names": "2.16", + "context_names": "2.25" + }, + "bodyProperties": { + "name": "2.16", + "id": "2.16", + "domain": "2.16", + "nameservers": "2.16", + "services": "2.16", + "sources": "2.16", + "ca_certificate": "2.25", + "ca_certificate_group": "2.25" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /dns": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "ids": "2.16", + "names": "2.16", + "context_names": "2.25" + }, + "bodyProperties": {} + }, + "GET /file-system-exports": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "continuation_token": "2.16", + "filter": "2.16", + "ids": "2.16", + "limit": "2.16", + "names": "2.16", + "offset": "2.16", + "sort": "2.16", + "allow_errors": "2.17", + "context_names": "2.17", + "workload_ids": "2.23", + "workload_names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /file-system-exports": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "member_ids": "2.16", + "member_names": "2.16", + "policy_ids": "2.16", + "policy_names": "2.16", + "context_names": "2.17" + }, + "bodyProperties": { + "export_name": "2.16", + "server": "2.16", + "share_policy": "2.16" + } + }, + "PATCH /file-system-exports": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "ids": "2.16", + "names": "2.16", + "context_names": "2.17" + }, + "bodyProperties": { + "name": "2.16", + "id": "2.16", + "enabled": "2.16", + "export_name": "2.16", + "member": "2.16", + "policy": "2.16", + "server": "2.16", + "share_policy": "2.16", + "status": "2.16", + "policy_type": "2.16", + "context": "2.17" + }, + "readOnlyBodyProperties": [ + "context", + "enabled", + "id", + "name", + "policy_type", + "status" + ] + }, + "DELETE /file-system-exports": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "ids": "2.16", + "names": "2.16", + "context_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /maintenance-windows": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "continuation_token": "2.16", + "filter": "2.16", + "ids": "2.16", + "limit": "2.16", + "names": "2.16", + "offset": "2.16", + "sort": "2.16" + }, + "bodyProperties": {} + }, + "POST /maintenance-windows": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "names": "2.16" + }, + "bodyProperties": { + "timeout": "2.16" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /maintenance-windows": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "ids": "2.16", + "names": "2.16" + }, + "bodyProperties": {} + }, + "GET /password-policies": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "continuation_token": "2.16", + "filter": "2.16", + "ids": "2.16", + "limit": "2.16", + "names": "2.16", + "offset": "2.16", + "sort": "2.16" + }, + "bodyProperties": {} + }, + "PATCH /password-policies": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "ids": "2.16", + "names": "2.16" + }, + "bodyProperties": { + "policy_type": "2.16", + "lockout_duration": "2.16", + "max_login_attempts": "2.16", + "min_password_length": "2.16", + "password_history": "2.16", + "min_password_age": "2.16", + "min_character_groups": "2.16", + "min_characters_per_group": "2.16", + "enforce_username_check": "2.16", + "enforce_dictionary_check": "2.16", + "name": "2.17", + "id": "2.17", + "location": "2.17", + "is_local": "2.17", + "enabled": "2.17", + "max_password_age": "2.18", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ] + }, + "GET /sso/saml2/idps/test": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "filter": "2.16", + "ids": "2.16", + "limit": "2.16", + "names": "2.16", + "sort": "2.16" + }, + "bodyProperties": {} + }, + "PATCH /sso/saml2/idps/test": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "ids": "2.16", + "names": "2.16" + }, + "bodyProperties": { + "id": "2.18", + "name": "2.18", + "array_url": "2.18", + "binding": "2.18", + "enabled": "2.18", + "idp": "2.18", + "prn": "2.18", + "services": "2.18", + "sp": "2.18", + "management": "2.24" + }, + "readOnlyBodyProperties": [ + "id", + "prn" + ] + }, + "GET /servers": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "continuation_token": "2.16", + "filter": "2.16", + "ids": "2.16", + "limit": "2.16", + "names": "2.16", + "offset": "2.16", + "sort": "2.16", + "allow_errors": "2.17", + "context_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /servers": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "create_ds": "2.16", + "names": "2.16", + "create_local_directory_service": "2.24" + }, + "bodyProperties": { + "directory_services": "2.18", + "dns": "2.18", + "local_directory_service": "2.24" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /servers": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "ids": "2.16", + "names": "2.16" + }, + "bodyProperties": { + "directory_services": "2.18", + "dns": "2.18", + "local_directory_service": "2.24" + } + }, + "DELETE /servers": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "cascade_delete": "2.16", + "ids": "2.16", + "names": "2.16" + }, + "bodyProperties": {} + }, + "GET /support-diagnostics": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "continuation_token": "2.16", + "filter": "2.16", + "ids": "2.16", + "limit": "2.16", + "names": "2.16", + "offset": "2.16", + "sort": "2.16" + }, + "bodyProperties": {} + }, + "POST /support-diagnostics": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "analysis_period_start_time": "2.16", + "analysis_period_end_time": "2.16" + }, + "bodyProperties": {} + }, + "GET /support-diagnostics/details": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "continuation_token": "2.16", + "filter": "2.16", + "ids": "2.16", + "limit": "2.16", + "names": "2.16", + "offset": "2.16", + "sort": "2.16" + }, + "bodyProperties": {} + }, + "GET /software-check": { + "minVersion": "2.16", + "parameters": { + "filter": "2.16", + "ids": "2.16", + "limit": "2.16", + "names": "2.16", + "offset": "2.16", + "software_versions": "2.16", + "software_names": "2.16", + "sort": "2.16", + "total_item_count": "2.16" + }, + "bodyProperties": {} + }, + "POST /software-check": { + "minVersion": "2.16", + "parameters": { + "X-Request-ID": "2.16", + "software_versions": "2.16", + "software_names": "2.16" + }, + "bodyProperties": {} + }, + "GET /qos-policies/file-systems": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "limit": "2.17", + "member_ids": "2.17", + "member_names": "2.17", + "offset": "2.17", + "policy_ids": "2.17", + "policy_names": "2.17", + "sort": "2.17", + "allow_errors": "2.23", + "context_names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /tls-policies/network-interfaces": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "limit": "2.17", + "member_ids": "2.17", + "member_names": "2.17", + "offset": "2.17", + "policy_ids": "2.17", + "policy_names": "2.17", + "sort": "2.17" + }, + "bodyProperties": {} + }, + "POST /tls-policies/network-interfaces": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "member_ids": "2.17", + "member_names": "2.17", + "policy_ids": "2.17", + "policy_names": "2.17" + }, + "bodyProperties": {} + }, + "DELETE /tls-policies/network-interfaces": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "member_ids": "2.17", + "member_names": "2.17", + "policy_ids": "2.17", + "policy_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /legal-holds": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "ids": "2.17", + "limit": "2.17", + "names": "2.17", + "offset": "2.17", + "sort": "2.17" + }, + "bodyProperties": {} + }, + "POST /legal-holds": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "names": "2.17" + }, + "bodyProperties": { + "id": "2.18", + "name": "2.18", + "description": "2.18", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "name", + "realms" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /legal-holds": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "ids": "2.17", + "names": "2.17" + }, + "bodyProperties": {} + }, + "PATCH /legal-holds": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "ids": "2.17", + "names": "2.17" + }, + "bodyProperties": { + "id": "2.18", + "name": "2.18", + "description": "2.18", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "name", + "realms" + ] + }, + "GET /fleets/members": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "fleet_ids": "2.17", + "fleet_names": "2.17", + "limit": "2.17", + "member_ids": "2.17", + "member_names": "2.17", + "offset": "2.17", + "sort": "2.17", + "total_only": "2.17" + }, + "bodyProperties": {} + }, + "POST /fleets/members": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "fleet_ids": "2.17", + "fleet_names": "2.17" + }, + "bodyProperties": { + "members": "2.17" + } + }, + "DELETE /fleets/members": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "member_ids": "2.17", + "member_names": "2.17", + "unreachable": "2.17" + }, + "bodyProperties": {} + }, + "GET /tls-policies": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "continuation_token": "2.17", + "effective": "2.17", + "filter": "2.17", + "ids": "2.17", + "limit": "2.17", + "names": "2.17", + "offset": "2.17", + "purity_defined": "2.17", + "sort": "2.17" + }, + "bodyProperties": {} + }, + "POST /tls-policies": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "names": "2.17" + }, + "bodyProperties": { + "name": "2.17", + "id": "2.17", + "policy_type": "2.17", + "location": "2.17", + "is_local": "2.17", + "enabled": "2.17", + "enabled_tls_ciphers": "2.17", + "appliance_certificate": "2.17", + "min_tls_version": "2.17", + "disabled_tls_ciphers": "2.17", + "client_certificates_required": "2.18", + "trusted_client_certificate_authority": "2.18", + "verify_client_certificate_trust": "2.18", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /tls-policies": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "ids": "2.17", + "names": "2.17" + }, + "bodyProperties": {} + }, + "PATCH /tls-policies": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "ids": "2.17", + "names": "2.17" + }, + "bodyProperties": { + "name": "2.17", + "id": "2.17", + "policy_type": "2.17", + "location": "2.17", + "is_local": "2.17", + "enabled": "2.17", + "enabled_tls_ciphers": "2.17", + "appliance_certificate": "2.17", + "min_tls_version": "2.17", + "disabled_tls_ciphers": "2.17", + "client_certificates_required": "2.18", + "trusted_client_certificate_authority": "2.18", + "verify_client_certificate_trust": "2.18", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ] + }, + "GET /qos-policies": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "ids": "2.17", + "limit": "2.17", + "names": "2.17", + "offset": "2.17", + "sort": "2.17", + "allow_errors": "2.23", + "context_names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /qos-policies": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "names": "2.17", + "context_names": "2.23" + }, + "bodyProperties": { + "name": "2.17", + "id": "2.17", + "policy_type": "2.17", + "location": "2.17", + "is_local": "2.17", + "enabled": "2.17", + "max_total_bytes_per_sec": "2.17", + "max_total_ops_per_sec": "2.17", + "realms": "2.19", + "context": "2.23" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "is_local", + "policy_type", + "realms" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /qos-policies": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "ids": "2.17", + "names": "2.17", + "context_names": "2.23" + }, + "bodyProperties": {} + }, + "PATCH /qos-policies": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "ids": "2.17", + "names": "2.17", + "context_names": "2.23" + }, + "bodyProperties": { + "name": "2.17", + "id": "2.17", + "policy_type": "2.17", + "location": "2.17", + "is_local": "2.17", + "enabled": "2.17", + "max_total_bytes_per_sec": "2.17", + "max_total_ops_per_sec": "2.17", + "realms": "2.19", + "context": "2.23" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "is_local", + "policy_type", + "realms" + ] + }, + "GET /fleets/fleet-key": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "limit": "2.17", + "offset": "2.17", + "sort": "2.17", + "total_only": "2.17" + }, + "bodyProperties": {} + }, + "POST /fleets/fleet-key": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17" + }, + "bodyProperties": {} + }, + "GET /tls-policies/members": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "limit": "2.17", + "member_ids": "2.17", + "member_names": "2.17", + "offset": "2.17", + "policy_ids": "2.17", + "policy_names": "2.17", + "sort": "2.17" + }, + "bodyProperties": {} + }, + "GET /object-store-roles": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "allow_errors": "2.17", + "context_names": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "ids": "2.17", + "limit": "2.17", + "names": "2.17", + "offset": "2.17", + "sort": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /object-store-roles": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "context_names": "2.17", + "names": "2.17" + }, + "bodyProperties": { + "max_session_duration": "2.17" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /object-store-roles": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "context_names": "2.17", + "ids": "2.17", + "names": "2.17" + }, + "bodyProperties": {} + }, + "PATCH /object-store-roles": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "context_names": "2.17", + "ids": "2.17", + "names": "2.17" + }, + "bodyProperties": { + "name": "2.17", + "id": "2.17", + "context": "2.17", + "trusted_entities": "2.17", + "created": "2.17", + "prn": "2.17", + "account": "2.17", + "max_session_duration": "2.17" + }, + "readOnlyBodyProperties": [ + "context", + "created", + "id", + "name", + "prn", + "trusted_entities" + ] + }, + "GET /object-store-access-policies/object-store-roles": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "allow_errors": "2.17", + "context_names": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "limit": "2.17", + "member_ids": "2.17", + "member_names": "2.17", + "offset": "2.17", + "policy_ids": "2.17", + "policy_names": "2.17", + "sort": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /object-store-access-policies/object-store-roles": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "context_names": "2.17", + "member_ids": "2.17", + "member_names": "2.17", + "policy_ids": "2.17", + "policy_names": "2.17" + }, + "bodyProperties": {} + }, + "DELETE /object-store-access-policies/object-store-roles": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "context_names": "2.17", + "member_ids": "2.17", + "member_names": "2.17", + "policy_ids": "2.17", + "policy_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /file-systems/open-files": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "client_names": "2.17", + "continuation_token": "2.17", + "file_system_ids": "2.17", + "file_system_names": "2.17", + "ids": "2.17", + "limit": "2.17", + "paths": "2.17", + "protocols": "2.17", + "session_names": "2.17", + "user_names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "paths": "Open_files_paths", + "protocols": "Protocols_required", + "user_names": "Open_files_user_names" + } + }, + "DELETE /file-systems/open-files": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "ids": "2.17" + }, + "bodyProperties": {} + }, + "GET /remote-arrays": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "continuation_token": "2.17", + "current_fleet_only": "2.17", + "filter": "2.17", + "ids": "2.17", + "limit": "2.17", + "names": "2.17", + "offset": "2.17", + "sort": "2.17", + "total_only": "2.17" + }, + "bodyProperties": {} + }, + "PATCH /file-system-replica-links": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "context_names": "2.17", + "ids": "2.17", + "local_file_system_ids": "2.17", + "local_file_system_names": "2.17", + "remote_ids": "2.17", + "remote_names": "2.17", + "replicate_now": "2.17" + }, + "bodyProperties": {} + }, + "GET /network-interfaces/connectors/performance": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "end_time": "2.17", + "filter": "2.17", + "ids": "2.17", + "limit": "2.17", + "names": "2.17", + "offset": "2.17", + "resolution": "2.17", + "sort": "2.17", + "start_time": "2.17", + "total_only": "2.17" + }, + "bodyProperties": {} + }, + "GET /fleets": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "ids": "2.17", + "limit": "2.17", + "names": "2.17", + "offset": "2.17", + "sort": "2.17", + "total_only": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "ids": "Ids_single", + "names": "Names_single" + } + }, + "POST /fleets": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "names": "Names_single" + } + }, + "DELETE /fleets": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "ids": "2.17", + "names": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "ids": "Ids_single", + "names": "Names_single" + } + }, + "PATCH /fleets": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "ids": "2.17", + "names": "2.17" + }, + "bodyProperties": { + "name": "2.17" + }, + "parameterComponentOverrides": { + "ids": "Ids_single", + "names": "Names_single" + } + }, + "GET /qos-policies/members": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "limit": "2.17", + "member_ids": "2.17", + "member_names": "2.17", + "offset": "2.17", + "policy_ids": "2.17", + "policy_names": "2.17", + "sort": "2.17", + "member_types": "2.20", + "allow_errors": "2.23", + "context_names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "member_types": "Member_types_qos" + } + }, + "GET /network-interfaces/connectors": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "ids": "2.17", + "limit": "2.17", + "names": "2.17", + "offset": "2.17", + "sort": "2.17" + }, + "bodyProperties": {} + }, + "PATCH /network-interfaces/connectors": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "ids": "2.17", + "names": "2.17" + }, + "bodyProperties": { + "id": "2.18", + "name": "2.18", + "connector_type": "2.18", + "lane_speed": "2.18", + "port_count": "2.18", + "port_speed": "2.18", + "transceiver_type": "2.18", + "lanes_per_port": "2.20" + }, + "readOnlyBodyProperties": [ + "connector_type", + "id", + "name", + "transceiver_type" + ] + }, + "GET /sso/oidc/idps": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "ids": "2.17", + "limit": "2.17", + "names": "2.17", + "offset": "2.17", + "sort": "2.17" + }, + "bodyProperties": {} + }, + "POST /sso/oidc/idps": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "names": "2.17" + }, + "bodyProperties": { + "idp": "2.17", + "services": "2.17", + "prn": "2.17", + "enabled": "2.17" + }, + "readOnlyBodyProperties": [ + "prn" + ] + }, + "DELETE /sso/oidc/idps": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "ids": "2.17", + "names": "2.17" + }, + "bodyProperties": {} + }, + "PATCH /sso/oidc/idps": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "ids": "2.17", + "names": "2.17" + }, + "bodyProperties": { + "idp": "2.17", + "services": "2.17", + "prn": "2.17", + "enabled": "2.17", + "name": "2.17" + }, + "readOnlyBodyProperties": [ + "prn" + ] + }, + "GET /object-store-roles/object-store-access-policies": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "allow_errors": "2.17", + "context_names": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "limit": "2.17", + "member_ids": "2.17", + "member_names": "2.17", + "offset": "2.17", + "policy_ids": "2.17", + "policy_names": "2.17", + "sort": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /object-store-roles/object-store-access-policies": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "context_names": "2.17", + "member_ids": "2.17", + "member_names": "2.17", + "policy_ids": "2.17", + "policy_names": "2.17" + }, + "bodyProperties": {} + }, + "DELETE /object-store-roles/object-store-access-policies": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "context_names": "2.17", + "member_ids": "2.17", + "member_names": "2.17", + "policy_ids": "2.17", + "policy_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /object-store-roles/object-store-trust-policies/rules": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "allow_errors": "2.17", + "context_names": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "indices": "2.17", + "limit": "2.17", + "names": "2.17", + "offset": "2.17", + "policy_names": "2.17", + "role_ids": "2.17", + "role_names": "2.17", + "sort": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /object-store-roles/object-store-trust-policies/rules": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "context_names": "2.17", + "names": "2.17", + "policy_names": "2.17", + "role_ids": "2.17", + "role_names": "2.17" + }, + "bodyProperties": { + "actions": "2.18", + "conditions": "2.18", + "effect": "2.18", + "policy": "2.18", + "principals": "2.18" + }, + "readOnlyBodyProperties": [ + "effect" + ] + }, + "DELETE /object-store-roles/object-store-trust-policies/rules": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "context_names": "2.17", + "indices": "2.17", + "names": "2.17", + "policy_names": "2.17", + "role_ids": "2.17", + "role_names": "2.17" + }, + "bodyProperties": {} + }, + "PATCH /object-store-roles/object-store-trust-policies/rules": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "context_names": "2.17", + "indices": "2.17", + "names": "2.17", + "policy_names": "2.17", + "role_ids": "2.17", + "role_names": "2.17" + }, + "bodyProperties": { + "actions": "2.18", + "conditions": "2.18", + "effect": "2.18", + "policy": "2.18", + "principals": "2.18" + }, + "readOnlyBodyProperties": [ + "effect" + ] + }, + "PATCH /object-store-roles/object-store-trust-policies/upload": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "context_names": "2.17", + "names": "2.17", + "role_ids": "2.17", + "role_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /object-store-roles/object-store-trust-policies": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "allow_errors": "2.17", + "context_names": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "limit": "2.17", + "names": "2.17", + "offset": "2.17", + "role_ids": "2.17", + "role_names": "2.17", + "sort": "2.17" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /network-interfaces/tls-policies": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "limit": "2.17", + "member_ids": "2.17", + "member_names": "2.17", + "offset": "2.17", + "policy_ids": "2.17", + "policy_names": "2.17", + "sort": "2.17" + }, + "bodyProperties": {} + }, + "POST /network-interfaces/tls-policies": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "member_ids": "2.17", + "member_names": "2.17", + "policy_ids": "2.17", + "policy_names": "2.17" + }, + "bodyProperties": {} + }, + "DELETE /network-interfaces/tls-policies": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "member_ids": "2.17", + "member_names": "2.17", + "policy_ids": "2.17", + "policy_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /object-store-roles/object-store-trust-policies/download": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "names": "2.17", + "role_ids": "2.17", + "role_names": "2.17" + }, + "bodyProperties": {} + }, + "GET /network-interfaces/connectors/settings": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "continuation_token": "2.17", + "filter": "2.17", + "ids": "2.17", + "limit": "2.17", + "names": "2.17", + "offset": "2.17", + "sort": "2.17" + }, + "bodyProperties": {} + }, + "GET /legal-holds/held-entities": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "continuation_token": "2.17", + "file_system_ids": "2.17", + "file_system_names": "2.17", + "ids": "2.17", + "limit": "2.17", + "names": "2.17", + "paths": "2.17" + }, + "bodyProperties": {} + }, + "POST /legal-holds/held-entities": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "file_system_ids": "2.17", + "file_system_names": "2.17", + "ids": "2.17", + "names": "2.17", + "paths": "2.17", + "recursive": "2.17" + }, + "bodyProperties": {} + }, + "PATCH /legal-holds/held-entities": { + "minVersion": "2.17", + "parameters": { + "X-Request-ID": "2.17", + "file_system_ids": "2.17", + "file_system_names": "2.17", + "ids": "2.17", + "names": "2.17", + "paths": "2.17", + "recursive": "2.17", + "released": "2.17" + }, + "bodyProperties": {} + }, + "GET /arrays/clients/s3-specific-performance": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "filter": "2.18", + "limit": "2.18", + "names": "2.18", + "sort": "2.18", + "total_only": "2.18" + }, + "bodyProperties": {} + }, + "POST /arrays/erasures": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "eradicate_all_data": "2.18", + "preserve_configuration_data": "2.18", + "skip_phonehome_check": "2.18" + }, + "bodyProperties": {} + }, + "PATCH /arrays/erasures": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "delete_sanitization_certificate": "2.18", + "eradicate_all_data": "2.18", + "finalize": "2.18" + }, + "bodyProperties": {} + }, + "GET /arrays/erasures": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18" + }, + "bodyProperties": {} + }, + "DELETE /arrays/erasures": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18" + }, + "bodyProperties": {} + }, + "GET /audit-file-systems-policy-operations": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "allow_errors": "2.18", + "context_names": "2.18", + "continuation_token": "2.18", + "filter": "2.18", + "limit": "2.18", + "names": "2.18", + "offset": "2.18", + "sort": "2.18" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /file-systems/space/storage-classes": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "continuation_token": "2.18", + "filter": "2.18", + "ids": "2.18", + "limit": "2.18", + "names": "2.18", + "offset": "2.18", + "sort": "2.18", + "storage_class_names": "2.18", + "total_only": "2.18" + }, + "bodyProperties": {} + }, + "GET /network-interfaces/network-connection-statistics": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "current_state": "2.18", + "filter": "2.18", + "limit": "2.18", + "local_host": "2.18", + "local_port": "2.18", + "offset": "2.18", + "remote_host": "2.18", + "remote_port": "2.18", + "sort": "2.18" + }, + "bodyProperties": {} + }, + "GET /nodes": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "continuation_token": "2.18", + "filter": "2.18", + "ids": "2.18", + "limit": "2.18", + "names": "2.18", + "offset": "2.18", + "sort": "2.18", + "total_only": "2.18" + }, + "bodyProperties": {} + }, + "PATCH /nodes": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "ids": "2.18", + "names": "2.18" + }, + "bodyProperties": { + "id": "2.18", + "name": "2.18", + "capacity": "2.18", + "data_addresses": "2.18", + "details": "2.18", + "management_address": "2.18", + "raw_capacity": "2.18", + "serial_number": "2.18", + "status": "2.18", + "unique": "2.18", + "chassis_serial_number": "2.23", + "node_key": "2.27" + }, + "readOnlyBodyProperties": [ + "capacity", + "chassis_serial_number", + "data_addresses", + "details", + "id", + "raw_capacity", + "status", + "unique" + ] + }, + "POST /nodes/batch": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "add_to_groups": "2.23" + }, + "bodyProperties": {} + }, + "GET /node-groups": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "continuation_token": "2.18", + "filter": "2.18", + "ids": "2.18", + "limit": "2.18", + "names": "2.18", + "offset": "2.18", + "sort": "2.18" + }, + "bodyProperties": {} + }, + "POST /node-groups": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "names": "2.18" + }, + "bodyProperties": {} + }, + "PATCH /node-groups": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "ids": "2.18", + "names": "2.18" + }, + "bodyProperties": { + "name": "2.18" + } + }, + "DELETE /node-groups": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "ids": "2.18", + "names": "2.18" + }, + "bodyProperties": {} + }, + "GET /node-groups/nodes": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "continuation_token": "2.18", + "filter": "2.18", + "limit": "2.18", + "node_group_ids": "2.18", + "node_group_names": "2.18", + "node_ids": "2.18", + "node_names": "2.18", + "offset": "2.18", + "sort": "2.18" + }, + "bodyProperties": {} + }, + "POST /node-groups/nodes": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "node_group_ids": "2.18", + "node_group_names": "2.18", + "node_ids": "2.18", + "node_names": "2.18" + }, + "bodyProperties": {} + }, + "DELETE /node-groups/nodes": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "node_group_ids": "2.18", + "node_group_names": "2.18", + "node_ids": "2.18", + "node_names": "2.18" + }, + "bodyProperties": {} + }, + "GET /node-groups/uses": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "continuation_token": "2.18", + "filter": "2.18", + "ids": "2.18", + "limit": "2.18", + "names": "2.18", + "offset": "2.18", + "sort": "2.18" + }, + "bodyProperties": {} + }, + "GET /storage-class-tiering-policies": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "continuation_token": "2.18", + "filter": "2.18", + "ids": "2.18", + "limit": "2.18", + "names": "2.18", + "offset": "2.18", + "sort": "2.18" + }, + "bodyProperties": {} + }, + "POST /storage-class-tiering-policies": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "names": "2.18" + }, + "bodyProperties": { + "id": "2.18", + "name": "2.18", + "enabled": "2.18", + "is_local": "2.18", + "location": "2.18", + "policy_type": "2.18", + "archival_rules": "2.18", + "retrieval_rules": "2.18", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /storage-class-tiering-policies": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "ids": "2.18", + "names": "2.18" + }, + "bodyProperties": {} + }, + "PATCH /storage-class-tiering-policies": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "ids": "2.18", + "names": "2.18" + }, + "bodyProperties": { + "id": "2.18", + "name": "2.18", + "enabled": "2.18", + "is_local": "2.18", + "location": "2.18", + "policy_type": "2.18", + "archival_rules": "2.18", + "retrieval_rules": "2.18", + "realms": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ] + }, + "GET /storage-class-tiering-policies/members": { + "minVersion": "2.18", + "parameters": { + "X-Request-ID": "2.18", + "allow_errors": "2.18", + "context_names": "2.18", + "continuation_token": "2.18", + "filter": "2.18", + "limit": "2.18", + "member_ids": "2.18", + "member_names": "2.18", + "offset": "2.18", + "policy_ids": "2.18", + "policy_names": "2.18", + "sort": "2.18" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /admins/management-access-policies": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "continuation_token": "2.19", + "filter": "2.19", + "limit": "2.19", + "member_ids": "2.19", + "member_names": "2.19", + "offset": "2.19", + "policy_ids": "2.19", + "policy_names": "2.19", + "sort": "2.19", + "allow_errors": "2.24", + "context_names": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /admins/management-access-policies": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "member_ids": "2.19", + "member_names": "2.19", + "policy_ids": "2.19", + "policy_names": "2.19", + "context_names": "2.24" + }, + "bodyProperties": {} + }, + "DELETE /admins/management-access-policies": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "member_ids": "2.19", + "member_names": "2.19", + "policy_ids": "2.19", + "policy_names": "2.19", + "context_names": "2.24" + }, + "bodyProperties": {} + }, + "GET /directory-services/roles/management-access-policies": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "continuation_token": "2.19", + "filter": "2.19", + "limit": "2.19", + "member_ids": "2.19", + "member_names": "2.19", + "offset": "2.19", + "policy_ids": "2.19", + "policy_names": "2.19", + "sort": "2.19" + }, + "bodyProperties": {} + }, + "POST /directory-services/roles/management-access-policies": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "member_ids": "2.19", + "member_names": "2.19", + "policy_ids": "2.19", + "policy_names": "2.19" + }, + "bodyProperties": {} + }, + "DELETE /directory-services/roles/management-access-policies": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "member_ids": "2.19", + "member_names": "2.19", + "policy_ids": "2.19", + "policy_names": "2.19" + }, + "bodyProperties": {} + }, + "GET /management-access-policies": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "continuation_token": "2.19", + "filter": "2.19", + "ids": "2.19", + "limit": "2.19", + "names": "2.19", + "offset": "2.19", + "sort": "2.19", + "allow_errors": "2.24", + "context_names": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /management-access-policies": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "names": "2.19", + "context_names": "2.26" + }, + "bodyProperties": { + "id": "2.19", + "name": "2.19", + "enabled": "2.19", + "is_local": "2.19", + "location": "2.19", + "policy_type": "2.19", + "realms": "2.19", + "aggregation_strategy": "2.19", + "rules": "2.19" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /management-access-policies": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "ids": "2.19", + "names": "2.19", + "context_names": "2.26" + }, + "bodyProperties": {} + }, + "PATCH /management-access-policies": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "ids": "2.19", + "names": "2.19", + "context_names": "2.26" + }, + "bodyProperties": { + "id": "2.19", + "name": "2.19", + "enabled": "2.19", + "is_local": "2.19", + "location": "2.19", + "policy_type": "2.19", + "realms": "2.19", + "version": "2.19", + "aggregation_strategy": "2.19", + "rules": "2.19", + "context": "2.24" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "is_local", + "policy_type", + "realms", + "version" + ] + }, + "GET /management-access-policies/admins": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "continuation_token": "2.19", + "filter": "2.19", + "limit": "2.19", + "member_ids": "2.19", + "member_names": "2.19", + "offset": "2.19", + "policy_ids": "2.19", + "policy_names": "2.19", + "sort": "2.19", + "allow_errors": "2.24", + "context_names": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /management-access-policies/admins": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "member_ids": "2.19", + "member_names": "2.19", + "policy_ids": "2.19", + "policy_names": "2.19", + "context_names": "2.24" + }, + "bodyProperties": {} + }, + "DELETE /management-access-policies/admins": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "member_ids": "2.19", + "member_names": "2.19", + "policy_ids": "2.19", + "policy_names": "2.19", + "context_names": "2.24" + }, + "bodyProperties": {} + }, + "GET /management-access-policies/directory-services/roles": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "continuation_token": "2.19", + "filter": "2.19", + "limit": "2.19", + "member_ids": "2.19", + "member_names": "2.19", + "offset": "2.19", + "policy_ids": "2.19", + "policy_names": "2.19", + "sort": "2.19" + }, + "bodyProperties": {} + }, + "POST /management-access-policies/directory-services/roles": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "member_ids": "2.19", + "member_names": "2.19", + "policy_ids": "2.19", + "policy_names": "2.19" + }, + "bodyProperties": {} + }, + "DELETE /management-access-policies/directory-services/roles": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "member_ids": "2.19", + "member_names": "2.19", + "policy_ids": "2.19", + "policy_names": "2.19" + }, + "bodyProperties": {} + }, + "GET /management-access-policies/members": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "continuation_token": "2.19", + "filter": "2.19", + "limit": "2.19", + "member_ids": "2.19", + "member_names": "2.19", + "offset": "2.19", + "policy_ids": "2.19", + "policy_names": "2.19", + "sort": "2.19", + "allow_errors": "2.24", + "context_names": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /qos-policies/buckets": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "continuation_token": "2.19", + "filter": "2.19", + "limit": "2.19", + "member_ids": "2.19", + "member_names": "2.19", + "offset": "2.19", + "policy_ids": "2.19", + "policy_names": "2.19", + "sort": "2.19", + "allow_errors": "2.23", + "context_names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /realms": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "continuation_token": "2.19", + "destroyed": "2.19", + "filter": "2.19", + "ids": "2.19", + "limit": "2.19", + "names": "2.19", + "offset": "2.19", + "sort": "2.19", + "total_only": "2.19", + "allow_errors": "2.24", + "context_names": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /realms": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "names": "2.19", + "without_default_access_list": "2.19" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /realms": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "ids": "2.19", + "names": "2.19" + }, + "bodyProperties": {} + }, + "PATCH /realms": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "ids": "2.19", + "names": "2.19" + }, + "bodyProperties": { + "id": "2.19", + "name": "2.19", + "default_inbound_tls_policy": "2.19", + "destroyed": "2.19" + }, + "readOnlyBodyProperties": [ + "id" + ] + }, + "GET /realms/space": { + "minVersion": "2.19", + "parameters": { + "continuation_token": "2.19", + "end_time": "2.19", + "filter": "2.19", + "ids": "2.19", + "limit": "2.19", + "names": "2.19", + "offset": "2.19", + "resolution": "2.19", + "sort": "2.19", + "start_time": "2.19", + "total_only": "2.19", + "type": "2.19" + }, + "bodyProperties": {} + }, + "GET /realms/space/storage-classes": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "continuation_token": "2.19", + "end_time": "2.19", + "filter": "2.19", + "ids": "2.19", + "limit": "2.19", + "names": "2.19", + "offset": "2.19", + "resolution": "2.19", + "sort": "2.19", + "start_time": "2.19", + "storage_class_names": "2.19", + "total_only": "2.19" + }, + "bodyProperties": {} + }, + "GET /resource-accesses": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "continuation_token": "2.19", + "filter": "2.19", + "ids": "2.19", + "limit": "2.19", + "offset": "2.19", + "sort": "2.19" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "limit": "Limit_scale" + } + }, + "DELETE /resource-accesses": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19", + "ids": "2.19" + }, + "bodyProperties": {} + }, + "POST /resource-accesses/batch": { + "minVersion": "2.19", + "parameters": { + "X-Request-ID": "2.19" + }, + "bodyProperties": {} + }, + "GET /audit-object-store-policies": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "allow_errors": "2.20", + "context_names": "2.20", + "continuation_token": "2.20", + "filter": "2.20", + "ids": "2.20", + "limit": "2.20", + "names": "2.20", + "offset": "2.20", + "sort": "2.20" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /audit-object-store-policies": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "names": "2.20" + }, + "bodyProperties": { + "id": "2.20", + "name": "2.20", + "enabled": "2.20", + "is_local": "2.20", + "location": "2.20", + "policy_type": "2.20", + "realms": "2.20", + "log_targets": "2.20" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /audit-object-store-policies": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "ids": "2.20", + "names": "2.20" + }, + "bodyProperties": {} + }, + "PATCH /audit-object-store-policies": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "ids": "2.20", + "names": "2.20" + }, + "bodyProperties": { + "id": "2.20", + "name": "2.20", + "enabled": "2.20", + "is_local": "2.20", + "location": "2.20", + "policy_type": "2.20", + "realms": "2.20", + "log_targets": "2.20", + "add_log_targets": "2.20", + "remove_log_targets": "2.20" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ] + }, + "GET /audit-object-store-policies/members": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "allow_errors": "2.20", + "context_names": "2.20", + "continuation_token": "2.20", + "filter": "2.20", + "limit": "2.20", + "member_ids": "2.20", + "member_names": "2.20", + "offset": "2.20", + "policy_ids": "2.20", + "policy_names": "2.20", + "sort": "2.20" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /audit-object-store-policies/members": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "member_ids": "2.20", + "member_names": "2.20", + "policy_ids": "2.20", + "policy_names": "2.20" + }, + "bodyProperties": {} + }, + "DELETE /audit-object-store-policies/members": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "member_ids": "2.20", + "member_names": "2.20", + "policy_ids": "2.20", + "policy_names": "2.20" + }, + "bodyProperties": {} + }, + "GET /bucket-audit-filter-actions": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "allow_errors": "2.20", + "context_names": "2.20", + "continuation_token": "2.20", + "filter": "2.20", + "limit": "2.20", + "names": "2.20", + "offset": "2.20", + "sort": "2.20" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /buckets/audit-filters": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "allow_errors": "2.20", + "bucket_ids": "2.20", + "bucket_names": "2.20", + "context_names": "2.20", + "continuation_token": "2.20", + "filter": "2.20", + "limit": "2.20", + "names": "2.20", + "offset": "2.20", + "sort": "2.20" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /buckets/audit-filters": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "bucket_ids": "2.20", + "bucket_names": "2.20", + "context_names": "2.20", + "names": "2.20" + }, + "bodyProperties": { + "actions": "2.20", + "s3_prefixes": "2.20" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /buckets/audit-filters": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "bucket_ids": "2.20", + "bucket_names": "2.20", + "context_names": "2.20", + "names": "2.20" + }, + "bodyProperties": {} + }, + "PATCH /buckets/audit-filters": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "bucket_ids": "2.20", + "bucket_names": "2.20", + "context_names": "2.20", + "names": "2.20" + }, + "bodyProperties": { + "actions": "2.20", + "s3_prefixes": "2.20" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "POST /certificates/certificate-signing-requests": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20" + }, + "bodyProperties": { + "certificate": "2.20", + "common_name": "2.20", + "country": "2.20", + "email": "2.20", + "locality": "2.20", + "organization": "2.20", + "organizational_unit": "2.20", + "state": "2.20", + "subject_alternative_names": "2.20" + } + }, + "GET /log-targets/file-systems": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "allow_errors": "2.20", + "context_names": "2.20", + "continuation_token": "2.20", + "filter": "2.20", + "ids": "2.20", + "limit": "2.20", + "names": "2.20", + "offset": "2.20", + "sort": "2.20" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /log-targets/file-systems": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "names": "2.20" + }, + "bodyProperties": { + "id": "2.20", + "name": "2.20", + "file_system": "2.20", + "keep_for": "2.20", + "keep_size": "2.20" + }, + "readOnlyBodyProperties": [ + "id" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /log-targets/file-systems": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "ids": "2.20", + "names": "2.20" + }, + "bodyProperties": {} + }, + "PATCH /log-targets/file-systems": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "ids": "2.20", + "names": "2.20" + }, + "bodyProperties": { + "id": "2.20", + "name": "2.20", + "file_system": "2.20", + "keep_for": "2.20", + "keep_size": "2.20" + }, + "readOnlyBodyProperties": [ + "id" + ] + }, + "GET /log-targets/object-store": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "allow_errors": "2.20", + "context_names": "2.20", + "continuation_token": "2.20", + "filter": "2.20", + "ids": "2.20", + "limit": "2.20", + "names": "2.20", + "offset": "2.20", + "sort": "2.20" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /log-targets/object-store": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "names": "2.20" + }, + "bodyProperties": { + "id": "2.20", + "name": "2.20", + "bucket": "2.20", + "log_name_prefix": "2.20", + "log_rotate": "2.20" + }, + "readOnlyBodyProperties": [ + "id" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /log-targets/object-store": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "ids": "2.20", + "names": "2.20" + }, + "bodyProperties": {} + }, + "PATCH /log-targets/object-store": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "ids": "2.20", + "names": "2.20" + }, + "bodyProperties": { + "id": "2.20", + "name": "2.20", + "bucket": "2.20", + "log_name_prefix": "2.20", + "log_rotate": "2.20" + }, + "readOnlyBodyProperties": [ + "id" + ] + }, + "GET /network-interfaces/neighbors": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "continuation_token": "2.20", + "filter": "2.20", + "limit": "2.20", + "local_port_names": "2.20", + "offset": "2.20", + "sort": "2.20", + "total_item_count": "2.20" + }, + "bodyProperties": {} + }, + "GET /object-store-account-exports": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "allow_errors": "2.20", + "context_names": "2.20", + "continuation_token": "2.20", + "filter": "2.20", + "ids": "2.20", + "limit": "2.20", + "names": "2.20", + "offset": "2.20", + "sort": "2.20" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /object-store-account-exports": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "member_ids": "2.20", + "member_names": "2.20", + "policy_ids": "2.20", + "policy_names": "2.20" + }, + "bodyProperties": { + "export_enabled": "2.20", + "server": "2.20" + } + }, + "DELETE /object-store-account-exports": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "ids": "2.20", + "names": "2.20" + }, + "bodyProperties": {} + }, + "PATCH /object-store-account-exports": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "ids": "2.20", + "names": "2.20" + }, + "bodyProperties": { + "export_enabled": "2.20", + "policy": "2.20" + } + }, + "PATCH /object-store-virtual-hosts": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "ids": "2.20", + "names": "2.20" + }, + "bodyProperties": { + "id": "2.20", + "name": "2.20", + "add_attached_servers": "2.20", + "attached_servers": "2.20", + "hostname": "2.20", + "remove_attached_servers": "2.20" + }, + "readOnlyBodyProperties": [ + "id" + ] + }, + "POST /qos-policies/members": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "member_ids": "2.20", + "member_names": "2.20", + "member_types": "2.20", + "policy_ids": "2.20", + "policy_names": "2.20", + "context_names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "member_types": "Member_types_qos_no_buckets" + } + }, + "DELETE /qos-policies/members": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "member_ids": "2.20", + "member_names": "2.20", + "member_types": "2.20", + "policy_ids": "2.20", + "policy_names": "2.20", + "context_names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "member_types": "Member_types_qos_no_buckets" + } + }, + "GET /realms/defaults": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "allow_errors": "2.20", + "context_names": "2.20", + "continuation_token": "2.20", + "filter": "2.20", + "limit": "2.20", + "offset": "2.20", + "realm_ids": "2.20", + "realm_names": "2.20", + "sort": "2.20" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "PATCH /realms/defaults": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "realm_ids": "2.20", + "realm_names": "2.20" + }, + "bodyProperties": { + "context": "2.20", + "object_store": "2.20", + "realm": "2.20" + }, + "readOnlyBodyProperties": [ + "context", + "realm" + ] + }, + "GET /s3-export-policies": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "allow_errors": "2.20", + "context_names": "2.20", + "continuation_token": "2.20", + "filter": "2.20", + "ids": "2.20", + "limit": "2.20", + "names": "2.20", + "offset": "2.20", + "sort": "2.20" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /s3-export-policies": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "names": "2.20" + }, + "bodyProperties": { + "enabled": "2.20", + "rules": "2.20" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /s3-export-policies": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "ids": "2.20", + "names": "2.20" + }, + "bodyProperties": {} + }, + "PATCH /s3-export-policies": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "ids": "2.20", + "names": "2.20" + }, + "bodyProperties": { + "enabled": "2.20", + "name": "2.20", + "rules": "2.20" + } + }, + "GET /s3-export-policies/rules": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "allow_errors": "2.20", + "context_names": "2.20", + "continuation_token": "2.20", + "filter": "2.20", + "limit": "2.20", + "names": "2.20", + "offset": "2.20", + "policy_ids": "2.20", + "policy_names": "2.20", + "sort": "2.20" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /s3-export-policies/rules": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "names": "2.20", + "policy_ids": "2.20", + "policy_names": "2.20" + }, + "bodyProperties": { + "actions": "2.20", + "effect": "2.20", + "resources": "2.20" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /s3-export-policies/rules": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "names": "2.20", + "policy_ids": "2.20", + "policy_names": "2.20" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /s3-export-policies/rules": { + "minVersion": "2.20", + "parameters": { + "X-Request-ID": "2.20", + "context_names": "2.20", + "names": "2.20", + "policy_ids": "2.20", + "policy_names": "2.20" + }, + "bodyProperties": { + "actions": "2.20", + "effect": "2.20", + "resources": "2.20" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "GET /data-eviction-policies": { + "minVersion": "2.21", + "parameters": { + "X-Request-ID": "2.21", + "allow_errors": "2.21", + "context_names": "2.21", + "continuation_token": "2.21", + "filter": "2.21", + "ids": "2.21", + "limit": "2.21", + "names": "2.21", + "offset": "2.21", + "sort": "2.21" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /data-eviction-policies": { + "minVersion": "2.21", + "parameters": { + "X-Request-ID": "2.21", + "context_names": "2.21", + "names": "2.21" + }, + "bodyProperties": { + "id": "2.21", + "name": "2.21", + "enabled": "2.21", + "is_local": "2.21", + "location": "2.21", + "policy_type": "2.21", + "realms": "2.21", + "keep_size": "2.21" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /data-eviction-policies": { + "minVersion": "2.21", + "parameters": { + "X-Request-ID": "2.21", + "context_names": "2.21", + "ids": "2.21", + "names": "2.21" + }, + "bodyProperties": {} + }, + "PATCH /data-eviction-policies": { + "minVersion": "2.21", + "parameters": { + "X-Request-ID": "2.21", + "context_names": "2.21", + "ids": "2.21", + "names": "2.21" + }, + "bodyProperties": { + "id": "2.21", + "name": "2.21", + "enabled": "2.21", + "is_local": "2.21", + "location": "2.21", + "policy_type": "2.21", + "realms": "2.21", + "context": "2.21", + "keep_size": "2.21" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "is_local", + "policy_type", + "realms" + ] + }, + "GET /data-eviction-policies/file-systems": { + "minVersion": "2.21", + "parameters": { + "X-Request-ID": "2.21", + "allow_errors": "2.21", + "context_names": "2.21", + "continuation_token": "2.21", + "filter": "2.21", + "limit": "2.21", + "member_ids": "2.21", + "member_names": "2.21", + "offset": "2.21", + "policy_ids": "2.21", + "policy_names": "2.21", + "sort": "2.21" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /data-eviction-policies/file-systems": { + "minVersion": "2.21", + "parameters": { + "X-Request-ID": "2.21", + "context_names": "2.21", + "member_ids": "2.21", + "member_names": "2.21", + "policy_ids": "2.21", + "policy_names": "2.21" + }, + "bodyProperties": {} + }, + "DELETE /data-eviction-policies/file-systems": { + "minVersion": "2.21", + "parameters": { + "X-Request-ID": "2.21", + "context_names": "2.21", + "member_ids": "2.21", + "member_names": "2.21", + "policy_ids": "2.21", + "policy_names": "2.21" + }, + "bodyProperties": {} + }, + "GET /data-eviction-policies/members": { + "minVersion": "2.21", + "parameters": { + "X-Request-ID": "2.21", + "allow_errors": "2.21", + "context_names": "2.21", + "continuation_token": "2.21", + "filter": "2.21", + "limit": "2.21", + "member_ids": "2.21", + "member_names": "2.21", + "offset": "2.21", + "policy_ids": "2.21", + "policy_names": "2.21", + "sort": "2.21" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /file-systems/data-eviction-policies": { + "minVersion": "2.21", + "parameters": { + "X-Request-ID": "2.21", + "allow_errors": "2.21", + "context_names": "2.21", + "continuation_token": "2.21", + "filter": "2.21", + "limit": "2.21", + "member_ids": "2.21", + "member_names": "2.21", + "offset": "2.21", + "policy_ids": "2.21", + "policy_names": "2.21", + "sort": "2.21" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /file-systems/data-eviction-policies": { + "minVersion": "2.21", + "parameters": { + "X-Request-ID": "2.21", + "context_names": "2.21", + "member_ids": "2.21", + "member_names": "2.21", + "policy_ids": "2.21", + "policy_names": "2.21" + }, + "bodyProperties": {} + }, + "DELETE /file-systems/data-eviction-policies": { + "minVersion": "2.21", + "parameters": { + "X-Request-ID": "2.21", + "context_names": "2.21", + "member_ids": "2.21", + "member_names": "2.21", + "policy_ids": "2.21", + "policy_names": "2.21" + }, + "bodyProperties": {} + }, + "GET /admins/management-authentication-policies": { + "minVersion": "2.22", + "parameters": { + "X-Request-ID": "2.22", + "allow_errors": "2.22", + "context_names": "2.22", + "continuation_token": "2.22", + "filter": "2.22", + "limit": "2.22", + "member_ids": "2.22", + "member_names": "2.22", + "offset": "2.22", + "policy_ids": "2.22", + "policy_names": "2.22", + "sort": "2.22" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /admins/management-authentication-policies": { + "minVersion": "2.22", + "parameters": { + "X-Request-ID": "2.22", + "context_names": "2.22", + "member_ids": "2.22", + "member_names": "2.22", + "policy_ids": "2.22", + "policy_names": "2.22" + }, + "bodyProperties": {} + }, + "DELETE /admins/management-authentication-policies": { + "minVersion": "2.22", + "parameters": { + "X-Request-ID": "2.22", + "context_names": "2.22", + "member_ids": "2.22", + "member_names": "2.22", + "policy_ids": "2.22", + "policy_names": "2.22" + }, + "bodyProperties": {} + }, + "GET /arrays/management-authentication-policies": { + "minVersion": "2.22", + "parameters": { + "X-Request-ID": "2.22", + "allow_errors": "2.22", + "context_names": "2.22", + "continuation_token": "2.22", + "filter": "2.22", + "limit": "2.22", + "member_ids": "2.22", + "member_names": "2.22", + "offset": "2.22", + "policy_ids": "2.22", + "policy_names": "2.22", + "sort": "2.22" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /arrays/management-authentication-policies": { + "minVersion": "2.22", + "parameters": { + "X-Request-ID": "2.22", + "context_names": "2.22", + "member_ids": "2.22", + "member_names": "2.22", + "policy_ids": "2.22", + "policy_names": "2.22" + }, + "bodyProperties": {} + }, + "DELETE /arrays/management-authentication-policies": { + "minVersion": "2.22", + "parameters": { + "X-Request-ID": "2.22", + "context_names": "2.22", + "member_ids": "2.22", + "member_names": "2.22", + "policy_ids": "2.22", + "policy_names": "2.22" + }, + "bodyProperties": {} + }, + "GET /management-authentication-policies": { + "minVersion": "2.22", + "parameters": { + "X-Request-ID": "2.22", + "allow_errors": "2.22", + "context_names": "2.22", + "continuation_token": "2.22", + "filter": "2.22", + "ids": "2.22", + "limit": "2.22", + "names": "2.22", + "offset": "2.22", + "sort": "2.22" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /management-authentication-policies": { + "minVersion": "2.22", + "parameters": { + "X-Request-ID": "2.22", + "context_names": "2.22", + "names": "2.22" + }, + "bodyProperties": { + "id": "2.22", + "name": "2.22", + "enabled": "2.22", + "is_local": "2.22", + "location": "2.22", + "policy_type": "2.22", + "realms": "2.22", + "ssh": "2.22" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /management-authentication-policies": { + "minVersion": "2.22", + "parameters": { + "X-Request-ID": "2.22", + "context_names": "2.22", + "ids": "2.22", + "names": "2.22" + }, + "bodyProperties": {} + }, + "PATCH /management-authentication-policies": { + "minVersion": "2.22", + "parameters": { + "X-Request-ID": "2.22", + "context_names": "2.22", + "ids": "2.22", + "names": "2.22" + }, + "bodyProperties": { + "id": "2.22", + "name": "2.22", + "enabled": "2.22", + "is_local": "2.22", + "location": "2.22", + "policy_type": "2.22", + "realms": "2.22", + "ssh": "2.22", + "context": "2.22" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "is_local", + "policy_type", + "realms" + ] + }, + "GET /management-authentication-policies/members": { + "minVersion": "2.22", + "parameters": { + "X-Request-ID": "2.22", + "allow_errors": "2.22", + "context_names": "2.22", + "continuation_token": "2.22", + "filter": "2.22", + "limit": "2.22", + "member_ids": "2.22", + "member_names": "2.22", + "member_types": "2.22", + "offset": "2.22", + "policy_ids": "2.22", + "policy_names": "2.22", + "sort": "2.22" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "member_types": "Management_access_policies_member_types" + } + }, + "POST /management-authentication-policies/members": { + "minVersion": "2.22", + "parameters": { + "X-Request-ID": "2.22", + "context_names": "2.22", + "member_ids": "2.22", + "member_names": "2.22", + "member_types": "2.22", + "policy_ids": "2.22", + "policy_names": "2.22" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "member_types": "Management_access_policies_member_types" + } + }, + "DELETE /management-authentication-policies/members": { + "minVersion": "2.22", + "parameters": { + "X-Request-ID": "2.22", + "context_names": "2.22", + "member_ids": "2.22", + "member_names": "2.22", + "member_types": "2.22", + "policy_ids": "2.22", + "policy_names": "2.22" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "member_types": "Management_access_policies_member_types" + } + }, + "GET /presets/workload": { + "minVersion": "2.23", + "parameters": { + "X-Request-ID": "2.23", + "context_names": "2.23", + "ids": "2.23", + "names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "PUT /presets/workload": { + "minVersion": "2.23", + "parameters": { + "X-Request-ID": "2.23", + "context_names": "2.23", + "ids": "2.23", + "names": "2.23", + "skip_verify_deployable": "2.23" + }, + "bodyProperties": { + "id": "2.23", + "name": "2.23", + "context": "2.23", + "description": "2.23", + "directory_configurations": "2.23", + "export_configurations": "2.23", + "parameters": "2.23", + "periodic_replication_configurations": "2.23", + "placement_configurations": "2.23", + "platform_features": "2.23", + "qos_configurations": "2.23", + "quota_configurations": "2.23", + "revision": "2.23", + "snapshot_configurations": "2.23", + "volume_configurations": "2.23", + "workload_tags": "2.23", + "workload_type": "2.23" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "revision" + ], + "parameterComponentOverrides": { + "ids": "Ids_single", + "names": "Names_single" + } + }, + "POST /presets/workload": { + "minVersion": "2.23", + "parameters": { + "X-Request-ID": "2.23", + "context_names": "2.23", + "names": "2.23", + "skip_verify_deployable": "2.23" + }, + "bodyProperties": { + "description": "2.23", + "directory_configurations": "2.23", + "export_configurations": "2.23", + "parameters": "2.23", + "periodic_replication_configurations": "2.23", + "placement_configurations": "2.23", + "platform_features": "2.23", + "qos_configurations": "2.23", + "quota_configurations": "2.23", + "revision": "2.23", + "snapshot_configurations": "2.23", + "volume_configurations": "2.23", + "workload_tags": "2.23", + "workload_type": "2.23" + }, + "readOnlyBodyProperties": [ + "revision" + ], + "parameterComponentOverrides": { + "names": "Names_single_required" + } + }, + "DELETE /presets/workload": { + "minVersion": "2.23", + "parameters": { + "X-Request-ID": "2.23", + "context_names": "2.23", + "ids": "2.23", + "names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "ids": "Ids_single", + "names": "Names_single" + } + }, + "PATCH /presets/workload": { + "minVersion": "2.23", + "parameters": { + "X-Request-ID": "2.23", + "context_names": "2.23", + "ids": "2.23", + "names": "2.23" + }, + "bodyProperties": { + "name": "2.23" + }, + "parameterComponentOverrides": { + "ids": "Ids_single", + "names": "Names_single" + } + }, + "GET /resiliency-groups": { + "minVersion": "2.23", + "parameters": { + "X-Request-ID": "2.23", + "continuation_token": "2.23", + "filter": "2.23", + "ids": "2.23", + "limit": "2.23", + "names": "2.23", + "offset": "2.23", + "sort": "2.23" + }, + "bodyProperties": {} + }, + "GET /resiliency-groups/members": { + "minVersion": "2.23", + "parameters": { + "X-Request-ID": "2.23", + "continuation_token": "2.23", + "filter": "2.23", + "limit": "2.23", + "member_ids": "2.23", + "member_names": "2.23", + "offset": "2.23", + "resiliency_group_ids": "2.23", + "resiliency_group_names": "2.23", + "sort": "2.23" + }, + "bodyProperties": {} + }, + "GET /workloads": { + "minVersion": "2.23", + "parameters": { + "X-Request-ID": "2.23", + "allow_errors": "2.23", + "context_names": "2.23", + "continuation_token": "2.23", + "destroyed": "2.23", + "ids": "2.23", + "limit": "2.23", + "names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /workloads": { + "minVersion": "2.23", + "parameters": { + "X-Request-ID": "2.23", + "context_names": "2.23", + "names": "2.23", + "preset_ids": "2.23", + "preset_names": "2.23" + }, + "bodyProperties": { + "parameters": "2.23" + }, + "parameterComponentOverrides": { + "names": "Names_single_required" + } + }, + "DELETE /workloads": { + "minVersion": "2.23", + "parameters": { + "X-Request-ID": "2.23", + "context_names": "2.23", + "ids": "2.23", + "names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "ids": "Ids_single", + "names": "Names_single" + } + }, + "PATCH /workloads": { + "minVersion": "2.23", + "parameters": { + "X-Request-ID": "2.23", + "context_names": "2.23", + "ids": "2.23", + "names": "2.23" + }, + "bodyProperties": { + "destroyed": "2.23", + "name": "2.23" + }, + "parameterComponentOverrides": { + "ids": "Ids_single", + "names": "Names_single" + } + }, + "GET /workloads/placement-recommendations": { + "minVersion": "2.23", + "parameters": { + "X-Request-ID": "2.23", + "allow_errors": "2.23", + "context_names": "2.23", + "continuation_token": "2.23", + "ids": "2.23", + "limit": "2.23", + "names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /workloads/placement-recommendations": { + "minVersion": "2.23", + "parameters": { + "X-Request-ID": "2.23", + "context_names": "2.23", + "placement_names": "2.23", + "preset_ids": "2.23", + "preset_names": "2.23" + }, + "bodyProperties": { + "context": "2.23", + "additional_constraints": "2.23", + "created": "2.23", + "expires": "2.23", + "id": "2.23", + "more_results_available": "2.23", + "name": "2.23", + "parameters": "2.23", + "placement_names": "2.23", + "preset": "2.23", + "progress": "2.23", + "projection_months": "2.23", + "recommendation_engine": "2.23", + "results": "2.23", + "results_limit": "2.23", + "status": "2.23" + }, + "readOnlyBodyProperties": [ + "context", + "created", + "expires", + "id", + "more_results_available", + "name", + "placement_names", + "progress", + "results", + "status" + ], + "parameterComponentOverrides": { + "preset_ids": null, + "preset_names": null + } + }, + "GET /workloads/tags": { + "minVersion": "2.23", + "parameters": { + "X-Request-ID": "2.23", + "allow_errors": "2.23", + "context_names": "2.23", + "namespaces": "2.23", + "resource_ids": "2.23", + "resource_names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "DELETE /workloads/tags": { + "minVersion": "2.23", + "parameters": { + "X-Request-ID": "2.23", + "context_names": "2.23", + "keys": "2.23", + "namespaces": "2.23", + "resource_ids": "2.23", + "resource_names": "2.23" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "namespaces": "Namespaces_delete" + } + }, + "PUT /workloads/tags/batch": { + "minVersion": "2.23", + "parameters": { + "X-Request-ID": "2.23", + "context_names": "2.23", + "resource_ids": "2.23", + "resource_names": "2.23" + }, + "bodyProperties": {} + }, + "GET /directory-services/local/directory-services": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "allow_errors": "2.24", + "context_names": "2.24", + "continuation_token": "2.24", + "destroyed": "2.24", + "filter": "2.24", + "ids": "2.24", + "limit": "2.24", + "names": "2.24", + "offset": "2.24", + "sort": "2.24", + "total_item_count": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "ids": "IdsSemiRequired", + "names": "NamesSemiRequired" + } + }, + "POST /directory-services/local/directory-services": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "context_names": "2.24", + "names": "2.24" + }, + "bodyProperties": { + "domain": "2.24" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /directory-services/local/directory-services": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "context_names": "2.24", + "ids": "2.24", + "names": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "ids": "IdsSemiRequired", + "names": "NamesSemiRequired" + } + }, + "PATCH /directory-services/local/directory-services": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "context_names": "2.24", + "ids": "2.24", + "names": "2.24" + }, + "bodyProperties": { + "context": "2.24", + "destroyed": "2.24", + "time_remaining": "2.24", + "id": "2.24", + "name": "2.24", + "realms": "2.24", + "server": "2.24", + "domain": "2.24" + }, + "readOnlyBodyProperties": [ + "context", + "destroyed", + "id", + "realms", + "server", + "time_remaining" + ], + "parameterComponentOverrides": { + "ids": "IdsSemiRequired", + "names": "NamesSemiRequired" + } + }, + "GET /directory-services/local/groups": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "allow_errors": "2.24", + "context_names": "2.24", + "continuation_token": "2.24", + "filter": "2.24", + "gids": "2.24", + "ids": "2.24", + "limit": "2.24", + "names": "2.24", + "offset": "2.24", + "sids": "2.24", + "sort": "2.24", + "total_item_count": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "ids": "IdsSemiRequired", + "names": "NamesSemiRequired" + } + }, + "POST /directory-services/local/groups": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "context_names": "2.24", + "local_directory_service_ids": "2.24", + "local_directory_service_names": "2.24", + "names": "2.24" + }, + "bodyProperties": { + "email": "2.24", + "gid": "2.24" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /directory-services/local/groups": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "context_names": "2.24", + "gids": "2.24", + "local_directory_service_ids": "2.24", + "local_directory_service_names": "2.24", + "names": "2.24", + "sids": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "names": "NamesGeneral" + } + }, + "PATCH /directory-services/local/groups": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "context_names": "2.24", + "gids": "2.24", + "local_directory_service_ids": "2.24", + "local_directory_service_names": "2.24", + "names": "2.24", + "sids": "2.24" + }, + "bodyProperties": { + "context": "2.24", + "built_in": "2.24", + "email": "2.24", + "gid": "2.24", + "name": "2.24", + "sid": "2.24" + }, + "readOnlyBodyProperties": [ + "built_in", + "context", + "sid" + ], + "parameterComponentOverrides": { + "names": "NamesGeneral" + } + }, + "GET /directory-services/local/groups/members": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "allow_errors": "2.24", + "context_names": "2.24", + "continuation_token": "2.24", + "filter": "2.24", + "group_gids": "2.24", + "group_names": "2.24", + "group_sids": "2.24", + "limit": "2.24", + "member_ids": "2.24", + "member_names": "2.24", + "member_sids": "2.24", + "member_types": "2.24", + "offset": "2.24", + "sort": "2.24", + "total_item_count": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "group_names": "Local_group_names", + "member_ids": "Local_member_ids" + } + }, + "POST /directory-services/local/groups/members": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "context_names": "2.24", + "group_gids": "2.24", + "group_names": "2.24", + "group_sids": "2.24", + "local_directory_service_ids": "2.24", + "local_directory_service_names": "2.24" + }, + "bodyProperties": { + "members": "2.24" + }, + "parameterComponentOverrides": { + "group_names": "Local_group_names" + } + }, + "DELETE /directory-services/local/groups/members": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "context_names": "2.24", + "group_gids": "2.24", + "group_names": "2.24", + "group_sids": "2.24", + "local_directory_service_ids": "2.24", + "local_directory_service_names": "2.24", + "member_ids": "2.24", + "member_names": "2.24", + "member_sids": "2.24", + "member_types": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "group_names": "Local_group_names", + "member_ids": "Local_member_ids" + } + }, + "GET /directory-services/local/users": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "allow_errors": "2.24", + "context_names": "2.24", + "continuation_token": "2.24", + "filter": "2.24", + "ids": "2.24", + "limit": "2.24", + "names": "2.24", + "offset": "2.24", + "sids": "2.24", + "sort": "2.24", + "total_item_count": "2.24", + "uids": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "ids": "IdsSemiRequired", + "names": "NamesSemiRequired" + } + }, + "POST /directory-services/local/users": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "context_names": "2.24", + "local_directory_service_ids": "2.24", + "local_directory_service_names": "2.24", + "names": "2.24" + }, + "bodyProperties": { + "email": "2.24", + "enabled": "2.24", + "password": "2.24", + "primary_group": "2.24", + "uid": "2.24" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /directory-services/local/users": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "context_names": "2.24", + "local_directory_service_ids": "2.24", + "local_directory_service_names": "2.24", + "names": "2.24", + "sids": "2.24", + "uids": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "names": "NamesGeneral" + } + }, + "PATCH /directory-services/local/users": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "context_names": "2.24", + "local_directory_service_ids": "2.24", + "local_directory_service_names": "2.24", + "names": "2.24", + "sids": "2.24", + "uids": "2.24" + }, + "bodyProperties": { + "email": "2.24", + "enabled": "2.24", + "name": "2.24", + "password": "2.24", + "primary_group": "2.24", + "uid": "2.24" + }, + "parameterComponentOverrides": { + "names": "NamesGeneral" + } + }, + "GET /directory-services/local/users/members": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "allow_errors": "2.24", + "context_names": "2.24", + "continuation_token": "2.24", + "filter": "2.24", + "group_gids": "2.24", + "group_names": "2.24", + "group_sids": "2.24", + "limit": "2.24", + "member_ids": "2.24", + "member_names": "2.24", + "member_sids": "2.24", + "offset": "2.24", + "sort": "2.24", + "total_item_count": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "group_names": "Local_group_names", + "member_ids": "Local_member_ids" + } + }, + "POST /directory-services/local/users/members": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "context_names": "2.24", + "local_directory_service_ids": "2.24", + "local_directory_service_names": "2.24", + "member_ids": "2.24", + "member_names": "2.24", + "member_sids": "2.24" + }, + "bodyProperties": { + "groups": "2.24", + "is_primary": "2.24" + }, + "parameterComponentOverrides": { + "member_ids": "Local_member_ids" + } + }, + "DELETE /directory-services/local/users/members": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "context_names": "2.24", + "group_gids": "2.24", + "group_names": "2.24", + "group_sids": "2.24", + "local_directory_service_ids": "2.24", + "local_directory_service_names": "2.24", + "member_ids": "2.24", + "member_names": "2.24", + "member_sids": "2.24", + "member_types": "2.24" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "group_names": "Local_group_names", + "member_ids": "Local_member_ids" + } + }, + "GET /support-diagnostics/settings": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "continuation_token": "2.24", + "filter": "2.24", + "limit": "2.24", + "offset": "2.24", + "sort": "2.24", + "total_item_count": "2.24" + }, + "bodyProperties": {} + }, + "PATCH /support-diagnostics/settings": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24" + }, + "bodyProperties": {} + }, + "GET /software-bundle": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "continuation_token": "2.24", + "filter": "2.24", + "ids": "2.24", + "limit": "2.24", + "offset": "2.24", + "sort": "2.24", + "total_item_count": "2.24" + }, + "bodyProperties": {} + }, + "POST /software-bundle": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24" + }, + "bodyProperties": { + "source": "2.24" + } + }, + "GET /software-patches": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "continuation_token": "2.24", + "filter": "2.24", + "ids": "2.24", + "limit": "2.24", + "names": "2.24", + "offset": "2.24", + "sort": "2.24", + "total_item_count": "2.24" + }, + "bodyProperties": {} + }, + "POST /software-patches": { + "minVersion": "2.24", + "parameters": { + "X-Request-ID": "2.24", + "allow_ha_reduction": "2.24", + "name": "2.24" + }, + "bodyProperties": {} + }, + "GET /file-systems/user-group-quota-policies": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "allow_errors": "2.25", + "context_names": "2.25", + "continuation_token": "2.25", + "filter": "2.25", + "limit": "2.25", + "member_ids": "2.25", + "member_names": "2.25", + "offset": "2.25", + "policy_ids": "2.25", + "policy_names": "2.25", + "sort": "2.25" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /file-systems/user-group-quota-policies": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "context_names": "2.25", + "delete_existing_user_group_quota_settings": "2.25", + "ignore_usage": "2.25", + "member_ids": "2.25", + "member_names": "2.25", + "policy_ids": "2.25", + "policy_names": "2.25" + }, + "bodyProperties": {} + }, + "DELETE /file-systems/user-group-quota-policies": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "context_names": "2.25", + "member_ids": "2.25", + "member_names": "2.25", + "policy_ids": "2.25", + "policy_names": "2.25" + }, + "bodyProperties": {} + }, + "GET /file-system-junctions": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "allow_errors": "2.25", + "context_names": "2.25", + "continuation_token": "2.25", + "destination_file_system_ids": "2.25", + "destination_file_system_names": "2.25", + "filter": "2.25", + "ids": "2.25", + "limit": "2.25", + "names": "2.25", + "offset": "2.25", + "origin_file_system_ids": "2.25", + "origin_file_system_names": "2.25", + "sort": "2.25" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /file-system-junctions": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "context_names": "2.25", + "names": "2.25", + "origin_file_system_ids": "2.25", + "origin_file_system_names": "2.25", + "use_existing_directory": "2.25" + }, + "bodyProperties": { + "context": "2.25", + "id": "2.25", + "name": "2.25", + "destination_file_system": "2.25", + "junction_origin_path": "2.25", + "origin_file_system": "2.25", + "status": "2.25" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "name", + "origin_file_system", + "status" + ], + "parameterComponentOverrides": { + "names": "Names_single_required", + "origin_file_system_ids": "Origin_file_system_ids_single", + "origin_file_system_names": "Origin_file_system_names_single" + } + }, + "DELETE /file-system-junctions": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "context_names": "2.25", + "ids": "2.25", + "names": "2.25" + }, + "bodyProperties": {} + }, + "GET /file-system-group-quotas": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "allow_errors": "2.25", + "context_names": "2.25", + "continuation_token": "2.25", + "file_system_ids": "2.25", + "file_system_names": "2.25", + "filter": "2.25", + "gids": "2.25", + "group_names": "2.25", + "limit": "2.25", + "offset": "2.25", + "sort": "2.25" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /file-systems/groups": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "allow_errors": "2.25", + "context_names": "2.25", + "continuation_token": "2.25", + "file_system_ids": "2.25", + "file_system_names": "2.25", + "filter": "2.25", + "gids": "2.25", + "group_names": "2.25", + "limit": "2.25", + "offset": "2.25", + "sort": "2.25" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /file-system-user-quotas": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "allow_errors": "2.25", + "context_names": "2.25", + "continuation_token": "2.25", + "file_system_ids": "2.25", + "file_system_names": "2.25", + "filter": "2.25", + "limit": "2.25", + "offset": "2.25", + "sort": "2.25", + "uids": "2.25", + "user_names": "2.25", + "user_sids": "2.25" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /file-systems/users": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "allow_errors": "2.25", + "context_names": "2.25", + "continuation_token": "2.25", + "file_system_ids": "2.25", + "file_system_names": "2.25", + "filter": "2.25", + "limit": "2.25", + "offset": "2.25", + "sort": "2.25", + "uids": "2.25", + "user_names": "2.25", + "user_sids": "2.25" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /realm-connections": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "continuation_token": "2.25", + "filter": "2.25", + "ids": "2.25", + "limit": "2.25", + "local_realm_ids": "2.25", + "local_realm_names": "2.25", + "offset": "2.25", + "remote_realm_ids": "2.25", + "remote_realm_names": "2.25", + "sort": "2.25" + }, + "bodyProperties": {} + }, + "POST /realm-connections": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "local_realm_ids": "2.25", + "local_realm_names": "2.25", + "remote_realm_ids": "2.25", + "remote_realm_names": "2.25" + }, + "bodyProperties": { + "id": "2.25", + "local_realm": "2.25", + "remote_realm": "2.25", + "status": "2.25", + "connection_key": "2.25" + }, + "readOnlyBodyProperties": [ + "id", + "local_realm", + "remote_realm", + "status" + ] + }, + "DELETE /realm-connections": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "local_realm_ids": "2.25", + "local_realm_names": "2.25", + "remote_realm_ids": "2.25", + "remote_realm_names": "2.25" + }, + "bodyProperties": {} + }, + "GET /realm-connections/connection-key": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "continuation_token": "2.25", + "filter": "2.25", + "limit": "2.25", + "offset": "2.25", + "realm_ids": "2.25", + "realm_names": "2.25", + "sort": "2.25" + }, + "bodyProperties": {} + }, + "POST /realm-connections/connection-key": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "realm_ids": "2.25", + "realm_names": "2.25" + }, + "bodyProperties": {} + }, + "DELETE /realm-connections/connection-key": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "realm_ids": "2.25", + "realm_names": "2.25" + }, + "bodyProperties": {} + }, + "GET /remote-realms": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "continuation_token": "2.25", + "filter": "2.25", + "ids": "2.25", + "limit": "2.25", + "names": "2.25", + "offset": "2.25", + "on": "2.25", + "on_ids": "2.25", + "sort": "2.25", + "total_only": "2.25" + }, + "bodyProperties": {} + }, + "GET /user-group-quota-policies": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "allow_errors": "2.25", + "context_names": "2.25", + "continuation_token": "2.25", + "filter": "2.25", + "ids": "2.25", + "limit": "2.25", + "names": "2.25", + "offset": "2.25", + "sort": "2.25" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /user-group-quota-policies": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "context_names": "2.25", + "file_system_ids": "2.25", + "file_system_names": "2.25", + "names": "2.25" + }, + "bodyProperties": { + "id": "2.25", + "name": "2.25", + "realms": "2.25", + "enabled": "2.25", + "is_local": "2.25", + "location": "2.25", + "policy_type": "2.25", + "rules": "2.25" + }, + "readOnlyBodyProperties": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "PATCH /user-group-quota-policies": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "context_names": "2.25", + "ids": "2.25", + "ignore_usage": "2.25", + "names": "2.25", + "versions": "2.25" + }, + "bodyProperties": { + "id": "2.25", + "name": "2.25", + "realms": "2.25", + "enabled": "2.25", + "is_local": "2.25", + "location": "2.25", + "policy_type": "2.25", + "version": "2.25", + "context": "2.25", + "rules": "2.25" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "is_local", + "policy_type", + "realms", + "version" + ] + }, + "DELETE /user-group-quota-policies": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "context_names": "2.25", + "ids": "2.25", + "names": "2.25", + "versions": "2.25" + }, + "bodyProperties": {} + }, + "GET /user-group-quota-policies/rules": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "allow_errors": "2.25", + "context_names": "2.25", + "continuation_token": "2.25", + "filter": "2.25", + "ids": "2.25", + "limit": "2.25", + "names": "2.25", + "offset": "2.25", + "sort": "2.25" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /user-group-quota-policies/rules": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "context_names": "2.25", + "ignore_usage": "2.25", + "policy_ids": "2.25", + "policy_names": "2.25" + }, + "bodyProperties": { + "enforced": "2.25", + "notifications": "2.25", + "quota_limit": "2.25", + "quota_type": "2.25", + "subject": "2.25" + } + }, + "DELETE /user-group-quota-policies/rules": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "context_names": "2.25", + "ids": "2.25", + "names": "2.25", + "policy_ids": "2.25", + "policy_names": "2.25" + }, + "bodyProperties": {} + }, + "PATCH /user-group-quota-policies/rules": { + "minVersion": "2.25", + "parameters": { + "context_names": "2.25", + "ids": "2.25", + "ignore_usage": "2.25", + "names": "2.25" + }, + "bodyProperties": { + "id": "2.25", + "name": "2.25", + "notifications": "2.25", + "quota_limit": "2.25" + }, + "readOnlyBodyProperties": [ + "id", + "name" + ] + }, + "GET /user-group-quota-policies/file-systems": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "allow_errors": "2.25", + "context_names": "2.25", + "continuation_token": "2.25", + "filter": "2.25", + "limit": "2.25", + "member_ids": "2.25", + "member_names": "2.25", + "offset": "2.25", + "policy_ids": "2.25", + "policy_names": "2.25", + "sort": "2.25" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /user-group-quota-policies/file-systems": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "context_names": "2.25", + "delete_existing_user_group_quota_settings": "2.25", + "ignore_usage": "2.25", + "member_ids": "2.25", + "member_names": "2.25", + "policy_ids": "2.25", + "policy_names": "2.25" + }, + "bodyProperties": {} + }, + "DELETE /user-group-quota-policies/file-systems": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "context_names": "2.25", + "member_ids": "2.25", + "member_names": "2.25", + "policy_ids": "2.25", + "policy_names": "2.25" + }, + "bodyProperties": {} + }, + "GET /user-group-quota-policies/members": { + "minVersion": "2.25", + "parameters": { + "X-Request-ID": "2.25", + "allow_errors": "2.25", + "context_names": "2.25", + "continuation_token": "2.25", + "filter": "2.25", + "limit": "2.25", + "member_ids": "2.25", + "member_names": "2.25", + "offset": "2.25", + "policy_ids": "2.25", + "policy_names": "2.25", + "sort": "2.25" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /management-access-policies/roles": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "allow_errors": "2.26", + "context_names": "2.26", + "continuation_token": "2.26", + "filter": "2.26", + "ids": "2.26", + "limit": "2.26", + "names": "2.26", + "offset": "2.26", + "sort": "2.26" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /management-access-policies/roles": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "context_names": "2.26", + "names": "2.26" + }, + "bodyProperties": { + "description": "2.26" + }, + "parameterComponentOverrides": { + "names": "Names_required" + } + }, + "DELETE /management-access-policies/roles": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "context_names": "2.26", + "ids": "2.26", + "names": "2.26" + }, + "bodyProperties": {} + }, + "GET /management-access-policies/roles/permissions": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "allow_errors": "2.26", + "context_names": "2.26", + "continuation_token": "2.26", + "filter": "2.26", + "ids": "2.26", + "limit": "2.26", + "names": "2.26", + "offset": "2.26", + "role_ids": "2.26", + "role_names": "2.26", + "sort": "2.26" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "role_ids": "Management_access_role_ids", + "role_names": "Management_access_role_names" + } + }, + "POST /management-access-policies/roles/permissions": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "context_names": "2.26", + "role_ids": "2.26", + "role_names": "2.26" + }, + "bodyProperties": { + "actions": "2.26", + "resource": "2.26" + }, + "parameterComponentOverrides": { + "role_ids": "Management_access_role_ids", + "role_names": "Management_access_role_names" + } + }, + "DELETE /management-access-policies/roles/permissions": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "context_names": "2.26", + "ids": "2.26", + "names": "2.26" + }, + "bodyProperties": {} + }, + "PATCH /management-access-policies/roles/permissions": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "context_names": "2.26", + "ids": "2.26", + "names": "2.26" + }, + "bodyProperties": { + "actions": "2.26" + } + }, + "GET /management-access-policies/roles/permissions/supported-resources": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "allow_errors": "2.26", + "context_names": "2.26", + "continuation_token": "2.26", + "filter": "2.26", + "limit": "2.26", + "names": "2.26", + "offset": "2.26", + "sort": "2.26" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /management-access-policies/rules": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "allow_errors": "2.26", + "context_names": "2.26", + "continuation_token": "2.26", + "filter": "2.26", + "ids": "2.26", + "limit": "2.26", + "names": "2.26", + "offset": "2.26", + "policy_ids": "2.26", + "policy_names": "2.26", + "sort": "2.26" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /management-access-policies/rules": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "before_rule_id": "2.26", + "before_rule_name": "2.26", + "context_names": "2.26", + "policy_ids": "2.26", + "policy_names": "2.26", + "versions": "2.26" + }, + "bodyProperties": { + "context": "2.26", + "id": "2.26", + "name": "2.26", + "role": "2.26", + "scope": "2.26", + "index": "2.26", + "policy": "2.26", + "policy_version": "2.26" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "name", + "policy", + "policy_version" + ], + "parameterComponentOverrides": { + "versions": "Concurrency_versions" + } + }, + "DELETE /management-access-policies/rules": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "context_names": "2.26", + "ids": "2.26", + "names": "2.26", + "versions": "2.26" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "versions": "Concurrency_versions" + } + }, + "PATCH /management-access-policies/rules": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "before_rule_id": "2.26", + "before_rule_name": "2.26", + "context_names": "2.26", + "ids": "2.26", + "names": "2.26", + "versions": "2.26" + }, + "bodyProperties": { + "context": "2.26", + "id": "2.26", + "name": "2.26", + "role": "2.26", + "scope": "2.26", + "index": "2.26", + "policy": "2.26", + "policy_version": "2.26" + }, + "readOnlyBodyProperties": [ + "context", + "id", + "name", + "policy", + "policy_version" + ], + "parameterComponentOverrides": { + "versions": "Concurrency_versions" + } + }, + "PATCH /remote-arrays": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "ids": "2.26", + "names": "2.26", + "to_topology_group_ids": "2.26", + "to_topology_group_names": "2.26" + }, + "bodyProperties": {} + }, + "GET /support/system-manifest": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "allow_errors": "2.26", + "context_names": "2.26", + "continuation_token": "2.26", + "filter": "2.26", + "limit": "2.26", + "offset": "2.26", + "sort": "2.26", + "total_only": "2.26" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /topology-groups": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "context_names": "2.26", + "continuation_token": "2.26", + "filter": "2.26", + "ids": "2.26", + "limit": "2.26", + "list_all_parents": "2.26", + "names": "2.26", + "offset": "2.26", + "sort": "2.26", + "total_item_count": "2.26" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "POST /topology-groups": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "context_names": "2.26", + "names": "2.26", + "parent_topology_group_ids": "2.26", + "parent_topology_group_names": "2.26" + }, + "bodyProperties": {} + }, + "PATCH /topology-groups": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "context_names": "2.26", + "ids": "2.26", + "names": "2.26", + "to_parent_topology_group_ids": "2.26", + "to_parent_topology_group_names": "2.26" + }, + "bodyProperties": { + "topology_group": "2.26" + } + }, + "DELETE /topology-groups": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "context_names": "2.26", + "ids": "2.26", + "names": "2.26" + }, + "bodyProperties": {} + }, + "GET /topology-groups/arrays": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "context_names": "2.26", + "continuation_token": "2.26", + "filter": "2.26", + "limit": "2.26", + "member_ids": "2.26", + "member_names": "2.26", + "offset": "2.26", + "sort": "2.26", + "topology_group_ids": "2.26", + "topology_group_names": "2.26", + "total_item_count": "2.26" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get" + } + }, + "GET /topology-groups/members": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "context_names": "2.26", + "continuation_token": "2.26", + "filter": "2.26", + "limit": "2.26", + "member_ids": "2.26", + "member_names": "2.26", + "offset": "2.26", + "recursive": "2.26", + "sort": "2.26", + "topology_group_ids": "2.26", + "topology_group_names": "2.26", + "total_item_count": "2.26" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "context_names": "Context_names_get", + "recursive": "Topology_group_members_recursive" + } + }, + "POST /topology-groups/members": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "context_names": "2.26", + "topology_group_ids": "2.26", + "topology_group_names": "2.26" + }, + "bodyProperties": { + "members": "2.26" + } + }, + "DELETE /topology-groups/members": { + "minVersion": "2.26", + "parameters": { + "X-Request-ID": "2.26", + "context_names": "2.26", + "member_ids": "2.26", + "member_names": "2.26", + "topology_group_ids": "2.26", + "topology_group_names": "2.26" + }, + "bodyProperties": {} + }, + "PATCH /active-directory/test": { + "minVersion": "2.27", + "parameters": { + "X-Request-ID": "2.27", + "context_names": "2.27", + "ids": "2.27", + "names": "2.27" + }, + "bodyProperties": { + "ca_certificate": "2.27", + "ca_certificate_group": "2.27" + } + }, + "POST /fleets/members/batch": { + "minVersion": "2.27", + "parameters": { + "X-Request-ID": "2.27", + "fleet_ids": "2.27", + "fleet_names": "2.27", + "validate_target_certificates": "2.27" + }, + "bodyProperties": {} + }, + "GET /storage-classes/members": { + "minVersion": "2.27", + "parameters": { + "X-Request-ID": "2.27", + "continuation_token": "2.27", + "filter": "2.27", + "limit": "2.27", + "member_ids": "2.27", + "member_names": "2.27", + "offset": "2.27", + "sort": "2.27", + "storage_class_names": "2.27" + }, + "bodyProperties": {}, + "parameterComponentOverrides": { + "storage_class_names": "Storage_class_names" + } + } + } +} diff --git a/Data/PfbResponseShapeMap.json b/Data/PfbResponseShapeMap.json new file mode 100644 index 0000000..5d91e19 --- /dev/null +++ b/Data/PfbResponseShapeMap.json @@ -0,0 +1,8932 @@ +{ + "schemaVersion": 1, + "generatedFrom": [ + "2.0", + "2.1", + "2.2", + "2.3", + "2.4", + "2.5", + "2.6", + "2.7", + "2.8", + "2.9", + "2.10", + "2.11", + "2.12", + "2.13", + "2.14", + "2.15", + "2.16", + "2.17", + "2.18", + "2.19", + "2.20", + "2.21", + "2.22", + "2.23", + "2.24", + "2.25", + "2.26", + "2.27", + "2.28" + ], + "endpoints": { + "GET /active-directory": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "ca_certificate": "2.27", + "ca_certificate_group": "2.27", + "computer_name": "2.0", + "directory_servers": "2.0", + "domain": "2.0", + "encryption_types": "2.0", + "global_catalog_servers": "2.12", + "id": "2.0", + "join_ou": "2.0", + "kerberos_servers": "2.0", + "name": "2.0", + "realms": "2.19", + "server": "2.16", + "service_principal_names": "2.0" + } + }, + "GET /active-directory/test": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.23", + "errors": "2.23", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "component_address": "2.0", + "component_name": "2.0", + "context": "2.23", + "description": "2.0", + "destination": "2.0", + "enabled": "2.0", + "resource": "2.0", + "result_details": "2.0", + "success": "2.0", + "test_type": "2.0" + } + }, + "GET /admins": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.22", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "admin_type": "2.24", + "api_token": "2.0", + "authorization_model": "2.24", + "context": "2.22", + "id": "2.0", + "is_local": "2.0", + "locked": "2.3", + "lockout_remaining": "2.3", + "management_access_policies": "2.19", + "name": "2.0", + "public_key": "2.0", + "role": "2.0" + } + }, + "GET /admins/api-tokens": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.24", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "admin": "2.0", + "api_token": "2.0", + "context": "2.24" + } + }, + "GET /admins/cache": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.24", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "authorization_model": "2.24", + "context": "2.24", + "id": "2.0", + "management_access_policies": "2.19", + "name": "2.0", + "role": "2.0", + "time": "2.0" + } + }, + "GET /admins/management-access-policies": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.19", + "errors": "2.24", + "items": "2.19", + "total_item_count": "2.19" + }, + "responseItemProperties": { + "context": "2.24", + "member": "2.19", + "policy": "2.19" + } + }, + "GET /admins/management-authentication-policies": { + "minVersion": "2.22", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.22", + "errors": "2.22", + "items": "2.22", + "total_item_count": "2.22" + }, + "responseItemProperties": { + "context": "2.22", + "member": "2.22", + "policy": "2.22" + } + }, + "GET /admins/settings": { + "minVersion": "2.3", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.3", + "items": "2.3", + "total_item_count": "2.3" + }, + "responseItemProperties": { + "lockout_duration": "2.3", + "max_login_attempts": "2.3", + "min_password_length": "2.3" + } + }, + "GET /admins/ssh-certificate-authority-policies": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.14", + "errors": "2.24", + "items": "2.14", + "total_item_count": "2.14" + }, + "responseItemProperties": { + "context": "2.24", + "member": "2.14", + "policy": "2.14" + } + }, + "GET /alert-watchers": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "enabled": "2.0", + "id": "2.0", + "minimum_notification_severity": "2.0", + "name": "2.0" + } + }, + "GET /alert-watchers/test": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "component_address": "2.0", + "component_name": "2.0", + "description": "2.0", + "destination": "2.0", + "enabled": "2.0", + "resource": "2.0", + "result_details": "2.0", + "success": "2.0", + "test_type": "2.0" + } + }, + "GET /alerts": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "action": "2.0", + "code": "2.0", + "component_name": "2.0", + "component_type": "2.0", + "created": "2.0", + "description": "2.0", + "duration": "2.20", + "flagged": "2.0", + "id": "2.0", + "index": "2.0", + "knowledge_base_url": "2.0", + "name": "2.0", + "notified": "2.0", + "severity": "2.0", + "state": "2.0", + "summary": "2.0", + "updated": "2.0", + "variables": "2.0" + } + }, + "GET /api-clients": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "access_policies": "2.19", + "access_token_ttl_in_ms": "2.0", + "enabled": "2.0", + "id": "2.0", + "issuer": "2.0", + "key_id": "2.0", + "max_role": "2.0", + "name": "2.0", + "public_key": "2.0" + } + }, + "GET /api/api_version": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "versions": "2.0" + }, + "responseItemProperties": {} + }, + "GET /api/login-banner": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "login_banner": "2.0" + }, + "responseItemProperties": {} + }, + "GET /array-connections": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "ca_certificate_group": "2.0", + "context": "2.17", + "encrypted": "2.0", + "id": "2.0", + "management_address": "2.0", + "os": "2.17", + "remote": "2.0", + "replication_addresses": "2.0", + "status": "2.0", + "throttle": "2.3", + "type": "2.17", + "version": "2.0" + } + }, + "GET /array-connections/connection-key": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "connection_key": "2.0", + "created": "2.0", + "expires": "2.0" + } + }, + "GET /array-connections/path": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "destination": "2.0", + "id": "2.0", + "remote": "2.0", + "source": "2.0", + "status": "2.0", + "status_details": "2.0", + "type": "2.17" + } + }, + "GET /array-connections/performance/replication": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "aggregate": "2.1", + "continuous": "2.1", + "id": "2.0", + "periodic": "2.0", + "remote": "2.0", + "time": "2.0" + } + }, + "GET /arrays": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.22", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "_as_of": "2.0", + "banner": "2.0", + "context": "2.22", + "default_inbound_tls_policy": "2.17", + "encryption": "2.8", + "eradication_config": "2.5", + "id": "2.0", + "idle_timeout": "2.0", + "name": "2.0", + "network_access_policy": "2.13", + "ntp_servers": "2.0", + "os": "2.0", + "product_type": "2.6", + "revision": "2.0", + "security_update": "2.7", + "smb_mode": "2.2", + "time_zone": "2.0", + "version": "2.0" + } + }, + "GET /arrays/clients/performance": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "bytes_per_op": "2.0", + "bytes_per_read": "2.0", + "bytes_per_write": "2.0", + "name": "2.0", + "others_per_sec": "2.0", + "read_bytes_per_sec": "2.0", + "reads_per_sec": "2.0", + "time": "2.0", + "usec_per_other_op": "2.0", + "usec_per_read_op": "2.0", + "usec_per_write_op": "2.0", + "write_bytes_per_sec": "2.0", + "writes_per_sec": "2.0" + } + }, + "GET /arrays/clients/s3-specific-performance": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.18", + "items": "2.18", + "total": "2.18", + "total_item_count": "2.18" + }, + "responseItemProperties": { + "name": "2.18", + "others_per_sec": "2.18", + "read_buckets_per_sec": "2.18", + "read_objects_per_sec": "2.18", + "time": "2.18", + "usec_per_other_op": "2.18", + "usec_per_read_bucket_op": "2.18", + "usec_per_read_object_op": "2.18", + "usec_per_write_bucket_op": "2.18", + "usec_per_write_object_op": "2.18", + "write_buckets_per_sec": "2.18", + "write_objects_per_sec": "2.18" + } + }, + "GET /arrays/erasures": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.18", + "items": "2.18", + "total_item_count": "2.18" + }, + "responseItemProperties": { + "details": "2.18", + "name": "2.18", + "sanitization_certificate": "2.18", + "status": "2.18" + } + }, + "GET /arrays/eula": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "agreement": "2.0", + "signature": "2.0" + } + }, + "GET /arrays/factory-reset-token": { + "minVersion": "2.1", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.1", + "items": "2.1", + "total_item_count": "2.1" + }, + "responseItemProperties": { + "name": "2.1", + "token": "2.1" + } + }, + "GET /arrays/http-specific-performance": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "id": "2.0", + "name": "2.0", + "others_per_sec": "2.0", + "read_dirs_per_sec": "2.0", + "read_files_per_sec": "2.0", + "time": "2.0", + "usec_per_other_op": "2.0", + "usec_per_read_dir_op": "2.0", + "usec_per_read_file_op": "2.0", + "usec_per_write_dir_op": "2.0", + "usec_per_write_file_op": "2.0", + "write_dirs_per_sec": "2.0", + "write_files_per_sec": "2.0" + } + }, + "GET /arrays/management-authentication-policies": { + "minVersion": "2.22", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.22", + "errors": "2.22", + "items": "2.22", + "total_item_count": "2.22" + }, + "responseItemProperties": { + "context": "2.22", + "member": "2.22", + "policy": "2.22" + } + }, + "GET /arrays/nfs-specific-performance": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "accesses_per_sec": "2.0", + "aggregate_file_metadata_creates_per_sec": "2.0", + "aggregate_file_metadata_modifies_per_sec": "2.0", + "aggregate_file_metadata_reads_per_sec": "2.0", + "aggregate_other_per_sec": "2.0", + "aggregate_share_metadata_reads_per_sec": "2.0", + "aggregate_usec_per_file_metadata_create_op": "2.0", + "aggregate_usec_per_file_metadata_modify_op": "2.0", + "aggregate_usec_per_file_metadata_read_op": "2.0", + "aggregate_usec_per_other_op": "2.0", + "aggregate_usec_per_share_metadata_read_op": "2.0", + "context": "2.17", + "creates_per_sec": "2.0", + "fsinfos_per_sec": "2.0", + "fsstats_per_sec": "2.0", + "getattrs_per_sec": "2.0", + "id": "2.0", + "links_per_sec": "2.0", + "lookups_per_sec": "2.0", + "mkdirs_per_sec": "2.0", + "name": "2.0", + "pathconfs_per_sec": "2.0", + "readdirpluses_per_sec": "2.0", + "readdirs_per_sec": "2.0", + "readlinks_per_sec": "2.0", + "reads_per_sec": "2.0", + "removes_per_sec": "2.0", + "renames_per_sec": "2.0", + "rmdirs_per_sec": "2.0", + "setattrs_per_sec": "2.0", + "symlinks_per_sec": "2.0", + "time": "2.0", + "usec_per_access_op": "2.0", + "usec_per_create_op": "2.0", + "usec_per_fsinfo_op": "2.0", + "usec_per_fsstat_op": "2.0", + "usec_per_getattr_op": "2.0", + "usec_per_link_op": "2.0", + "usec_per_lookup_op": "2.0", + "usec_per_mkdir_op": "2.0", + "usec_per_pathconf_op": "2.0", + "usec_per_read_op": "2.0", + "usec_per_readdir_op": "2.0", + "usec_per_readdirplus_op": "2.0", + "usec_per_readlink_op": "2.0", + "usec_per_remove_op": "2.0", + "usec_per_rename_op": "2.0", + "usec_per_rmdir_op": "2.0", + "usec_per_setattr_op": "2.0", + "usec_per_symlink_op": "2.0", + "usec_per_write_op": "2.0", + "writes_per_sec": "2.0" + } + }, + "GET /arrays/performance": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "bytes_per_op": "2.0", + "bytes_per_read": "2.0", + "bytes_per_write": "2.0", + "context": "2.17", + "id": "2.0", + "name": "2.0", + "others_per_sec": "2.0", + "read_bytes_per_sec": "2.0", + "reads_per_sec": "2.0", + "time": "2.0", + "usec_per_other_op": "2.0", + "usec_per_read_op": "2.0", + "usec_per_write_op": "2.0", + "write_bytes_per_sec": "2.0", + "writes_per_sec": "2.0" + } + }, + "GET /arrays/performance/replication": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "aggregate": "2.1", + "context": "2.17", + "continuous": "2.1", + "id": "2.0", + "periodic": "2.0", + "time": "2.0" + }, + "removedResponseFields": [ + { + "field": "remote", + "location": "items", + "introducedVersion": "2.0", + "lastSeenVersion": "2.10" + } + ] + }, + "GET /arrays/s3-specific-performance": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "id": "2.0", + "name": "2.0", + "others_per_sec": "2.0", + "read_buckets_per_sec": "2.0", + "read_objects_per_sec": "2.0", + "time": "2.0", + "usec_per_other_op": "2.0", + "usec_per_read_bucket_op": "2.0", + "usec_per_read_object_op": "2.0", + "usec_per_write_bucket_op": "2.0", + "usec_per_write_object_op": "2.0", + "write_buckets_per_sec": "2.0", + "write_objects_per_sec": "2.0" + } + }, + "GET /arrays/space": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "capacity": "2.0", + "context": "2.17", + "id": "2.0", + "name": "2.0", + "parity": "2.0", + "space": "2.0", + "time": "2.0" + } + }, + "GET /arrays/space/storage-classes": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.16", + "items": "2.16", + "total": "2.16", + "total_item_count": "2.16" + }, + "responseItemProperties": { + "capacity": "2.16", + "name": "2.16", + "space": "2.16", + "time": "2.16" + } + }, + "GET /arrays/ssh-certificate-authority-policies": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.14", + "errors": "2.24", + "items": "2.14", + "total_item_count": "2.14" + }, + "responseItemProperties": { + "context": "2.24", + "member": "2.14", + "policy": "2.14" + } + }, + "GET /arrays/supported-time-zones": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "name": "2.0" + } + }, + "GET /audit-file-systems-policies": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.14", + "errors": "2.17", + "items": "2.14", + "total_item_count": "2.14" + }, + "responseItemProperties": { + "context": "2.17", + "control_type": "2.18", + "enabled": "2.14", + "id": "2.14", + "is_local": "2.14", + "location": "2.14", + "log_targets": "2.14", + "name": "2.14", + "policy_type": "2.14", + "realms": "2.19", + "rules": "2.18" + } + }, + "GET /audit-file-systems-policies/members": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.14", + "errors": "2.17", + "items": "2.14", + "total_item_count": "2.14" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.14", + "policy": "2.14" + } + }, + "GET /audit-file-systems-policy-operations": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.18", + "items": "2.18", + "total_item_count": "2.18" + }, + "responseItemProperties": { + "description": "2.18", + "name": "2.18" + } + }, + "GET /audit-object-store-policies": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.20", + "errors": "2.20", + "items": "2.20", + "total_item_count": "2.20" + }, + "responseItemProperties": { + "context": "2.20", + "enabled": "2.20", + "id": "2.20", + "is_local": "2.20", + "location": "2.20", + "log_targets": "2.20", + "name": "2.20", + "policy_type": "2.20", + "realms": "2.20" + } + }, + "GET /audit-object-store-policies/members": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.20", + "errors": "2.20", + "items": "2.20", + "total_item_count": "2.20" + }, + "responseItemProperties": { + "context": "2.20", + "member": "2.20", + "policy": "2.20" + } + }, + "GET /audits": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.28", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "arguments": "2.0", + "command": "2.0", + "context": "2.28", + "correlation_id": "2.28", + "id": "2.0", + "ip_address": "2.0", + "name": "2.0", + "origin": "2.17", + "scopes": "2.20", + "subcommand": "2.0", + "system_event": "2.28", + "time": "2.0", + "user": "2.0", + "user_agent": "2.0", + "user_interface": "2.0" + } + }, + "GET /blades": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "details": "2.0", + "id": "2.0", + "name": "2.0", + "progress": "2.0", + "raw_capacity": "2.0", + "status": "2.0", + "target": "2.0" + } + }, + "GET /bucket-audit-filter-actions": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.20", + "errors": "2.20", + "items": "2.20", + "total_item_count": "2.20" + }, + "responseItemProperties": { + "context": "2.20", + "description": "2.20", + "name": "2.20" + } + }, + "GET /bucket-replica-links": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total": "2.2", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "cascading_enabled": "2.2", + "context": "2.17", + "direction": "2.0", + "id": "2.0", + "lag": "2.0", + "local_bucket": "2.0", + "object_backlog": "2.2", + "paused": "2.0", + "recovery_point": "2.0", + "remote": "2.0", + "remote_bucket": "2.0", + "remote_credentials": "2.0", + "status": "2.0", + "status_details": "2.0" + } + }, + "GET /buckets": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "account": "2.0", + "bucket_type": "2.4", + "context": "2.17", + "created": "2.0", + "destroyed": "2.0", + "eradication_config": "2.8", + "hard_limit_enabled": "2.8", + "id": "2.0", + "name": "2.0", + "object_count": "2.0", + "object_lock_config": "2.8", + "public_access_config": "2.12", + "public_status": "2.12", + "qos_policy": "2.20", + "quota_limit": "2.8", + "retention_lock": "2.8", + "space": "2.0", + "storage_class": "2.19", + "time_remaining": "2.0", + "time_remaining_status": "2.14", + "versioning": "2.0" + } + }, + "GET /buckets/audit-filters": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.20", + "errors": "2.20", + "items": "2.20", + "total_item_count": "2.20" + }, + "responseItemProperties": { + "actions": "2.20", + "bucket": "2.20", + "context": "2.20", + "name": "2.20", + "s3_prefixes": "2.20" + } + }, + "GET /buckets/bucket-access-policies": { + "minVersion": "2.12", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.12", + "errors": "2.17", + "items": "2.12", + "total_item_count": "2.12" + }, + "responseItemProperties": { + "bucket": "2.12", + "context": "2.17", + "enabled": "2.12", + "id": "2.12", + "is_local": "2.12", + "location": "2.12", + "name": "2.12", + "policy_type": "2.12", + "realms": "2.19", + "rules": "2.12" + } + }, + "GET /buckets/bucket-access-policies/rules": { + "minVersion": "2.12", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.12", + "errors": "2.17", + "items": "2.12", + "total_item_count": "2.12" + }, + "responseItemProperties": { + "actions": "2.12", + "context": "2.17", + "effect": "2.12", + "name": "2.12", + "policy": "2.12", + "principals": "2.12", + "resources": "2.12" + } + }, + "GET /buckets/cross-origin-resource-sharing-policies": { + "minVersion": "2.12", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.12", + "errors": "2.17", + "items": "2.12", + "total_item_count": "2.12" + }, + "responseItemProperties": { + "bucket": "2.12", + "context": "2.17", + "enabled": "2.12", + "id": "2.12", + "is_local": "2.12", + "location": "2.12", + "name": "2.12", + "policy_type": "2.12", + "realms": "2.19", + "rules": "2.12" + } + }, + "GET /buckets/cross-origin-resource-sharing-policies/rules": { + "minVersion": "2.12", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.12", + "errors": "2.17", + "items": "2.12", + "total_item_count": "2.12" + }, + "responseItemProperties": { + "allowed_headers": "2.12", + "allowed_methods": "2.12", + "allowed_origins": "2.12", + "context": "2.17", + "name": "2.12", + "policy": "2.12" + } + }, + "GET /buckets/performance": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "bytes_per_op": "2.0", + "bytes_per_read": "2.0", + "bytes_per_write": "2.0", + "id": "2.0", + "max_total_bytes_per_sec": "2.19", + "max_total_ops_per_sec": "2.19", + "name": "2.0", + "others_per_sec": "2.0", + "read_bytes_per_sec": "2.0", + "reads_per_sec": "2.0", + "time": "2.0", + "usec_per_other_op": "2.0", + "usec_per_read_op": "2.0", + "usec_per_write_op": "2.0", + "write_bytes_per_sec": "2.0", + "writes_per_sec": "2.0" + } + }, + "GET /buckets/s3-specific-performance": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "id": "2.0", + "name": "2.0", + "others_per_sec": "2.0", + "read_buckets_per_sec": "2.0", + "read_objects_per_sec": "2.0", + "time": "2.0", + "usec_per_other_op": "2.0", + "usec_per_read_bucket_op": "2.0", + "usec_per_read_object_op": "2.0", + "usec_per_write_bucket_op": "2.0", + "usec_per_write_object_op": "2.0", + "write_buckets_per_sec": "2.0", + "write_objects_per_sec": "2.0" + } + }, + "GET /certificate-groups": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "id": "2.0", + "name": "2.0", + "realms": "2.19" + } + }, + "GET /certificate-groups/certificates": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "group": "2.0", + "member": "2.0" + } + }, + "GET /certificate-groups/uses": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "id": "2.0", + "name": "2.0", + "use": "2.0" + } + }, + "GET /certificates": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "certificate": "2.0", + "certificate_type": "2.0", + "common_name": "2.0", + "country": "2.0", + "email": "2.0", + "id": "2.0", + "intermediate_certificate": "2.0", + "issued_by": "2.0", + "issued_to": "2.0", + "key_algorithm": "2.20", + "key_size": "2.0", + "locality": "2.0", + "name": "2.0", + "organization": "2.0", + "organizational_unit": "2.0", + "realms": "2.19", + "state": "2.0", + "status": "2.0", + "subject_alternative_names": "2.0", + "valid_from": "2.0", + "valid_to": "2.0" + } + }, + "GET /certificates/certificate-groups": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "group": "2.0", + "member": "2.0" + } + }, + "GET /certificates/uses": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "group": "2.0", + "id": "2.0", + "name": "2.0", + "use": "2.0" + } + }, + "GET /data-eviction-policies": { + "minVersion": "2.21", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.21", + "errors": "2.21", + "items": "2.21", + "total_item_count": "2.21" + }, + "responseItemProperties": { + "context": "2.21", + "enabled": "2.21", + "id": "2.21", + "is_local": "2.21", + "keep_size": "2.21", + "location": "2.21", + "name": "2.21", + "policy_type": "2.21", + "realms": "2.21" + } + }, + "GET /data-eviction-policies/file-systems": { + "minVersion": "2.21", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.21", + "errors": "2.21", + "items": "2.21", + "total_item_count": "2.21" + }, + "responseItemProperties": { + "context": "2.21", + "member": "2.21", + "policy": "2.21" + } + }, + "GET /data-eviction-policies/members": { + "minVersion": "2.21", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.21", + "errors": "2.21", + "items": "2.21", + "total_item_count": "2.21" + }, + "responseItemProperties": { + "context": "2.21", + "member": "2.21", + "policy": "2.21" + } + }, + "GET /directory-services": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "base_dn": "2.0", + "bind_password": "2.0", + "bind_user": "2.0", + "ca_certificate": "2.0", + "ca_certificate_group": "2.0", + "enabled": "2.0", + "id": "2.0", + "management": "2.0", + "name": "2.0", + "nfs": "2.0", + "services": "2.0", + "smb": "2.0", + "uris": "2.0" + } + }, + "GET /directory-services/local/directory-services": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.24", + "errors": "2.24", + "items": "2.24", + "total_item_count": "2.24" + }, + "responseItemProperties": { + "context": "2.24", + "destroyed": "2.24", + "domain": "2.24", + "id": "2.24", + "name": "2.24", + "realms": "2.24", + "server": "2.24", + "time_remaining": "2.24" + } + }, + "GET /directory-services/local/groups": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.24", + "errors": "2.24", + "items": "2.24", + "total_item_count": "2.24" + }, + "responseItemProperties": { + "built_in": "2.24", + "context": "2.24", + "email": "2.24", + "gid": "2.24", + "local_directory_service": "2.24", + "name": "2.24", + "realms": "2.24", + "server": "2.24", + "sid": "2.24" + } + }, + "GET /directory-services/local/groups/members": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.24", + "errors": "2.24", + "items": "2.24", + "total_item_count": "2.24" + }, + "responseItemProperties": { + "context": "2.24", + "group": "2.24", + "group_gid": "2.24", + "is_primary_group": "2.24", + "local_directory_service": "2.24", + "member": "2.24", + "member_id": "2.24", + "realms": "2.24", + "server": "2.24" + } + }, + "GET /directory-services/local/users": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.24", + "errors": "2.24", + "items": "2.24", + "total_item_count": "2.24" + }, + "responseItemProperties": { + "built_in": "2.24", + "context": "2.24", + "email": "2.24", + "enabled": "2.24", + "local_directory_service": "2.24", + "name": "2.24", + "primary_group": "2.24", + "realms": "2.24", + "server": "2.24", + "sid": "2.24", + "uid": "2.24" + } + }, + "GET /directory-services/local/users/members": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.24", + "errors": "2.24", + "items": "2.24", + "total_item_count": "2.24" + }, + "responseItemProperties": { + "context": "2.24", + "group": "2.24", + "group_gid": "2.24", + "is_primary_group": "2.24", + "local_directory_service": "2.24", + "member": "2.24", + "member_id": "2.24", + "realms": "2.24", + "server": "2.24" + } + }, + "GET /directory-services/roles": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "group": "2.0", + "group_base": "2.0", + "id": "2.0", + "management_access_policies": "2.19", + "name": "2.14", + "role": "2.0" + } + }, + "GET /directory-services/roles/management-access-policies": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.19", + "items": "2.19", + "total_item_count": "2.19" + }, + "responseItemProperties": { + "member": "2.19", + "policy": "2.19" + } + }, + "GET /directory-services/test": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.23", + "errors": "2.23", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "component_address": "2.0", + "component_name": "2.0", + "context": "2.23", + "description": "2.0", + "destination": "2.0", + "enabled": "2.0", + "resource": "2.0", + "result_details": "2.0", + "success": "2.0", + "test_type": "2.0" + } + }, + "GET /dns": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.25", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "ca_certificate": "2.25", + "ca_certificate_group": "2.25", + "context": "2.25", + "domain": "2.0", + "id": "2.0", + "name": "2.0", + "nameservers": "2.0", + "realms": "2.19", + "services": "2.16", + "sources": "2.16" + } + }, + "GET /drives": { + "minVersion": "2.4", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.4", + "items": "2.4", + "total": "2.4", + "total_item_count": "2.4" + }, + "responseItemProperties": { + "details": "2.4", + "id": "2.4", + "name": "2.4", + "progress": "2.4", + "raw_capacity": "2.4", + "status": "2.4", + "type": "2.9" + } + }, + "GET /file-system-exports": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.16", + "errors": "2.17", + "items": "2.16", + "total_item_count": "2.16" + }, + "responseItemProperties": { + "context": "2.17", + "enabled": "2.16", + "export_name": "2.16", + "id": "2.16", + "member": "2.16", + "name": "2.16", + "policy": "2.16", + "policy_type": "2.16", + "server": "2.16", + "share_policy": "2.16", + "status": "2.16", + "workload": "2.23" + } + }, + "GET /file-system-group-quotas": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.25", + "errors": "2.25", + "items": "2.25", + "total_item_count": "2.25" + }, + "responseItemProperties": { + "context": "2.25", + "file_system": "2.25", + "group": "2.25", + "percentage_used": "2.25", + "policy_rule": "2.25", + "usage": "2.25" + } + }, + "GET /file-system-junctions": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.25", + "errors": "2.25", + "items": "2.25", + "total_item_count": "2.25" + }, + "responseItemProperties": { + "context": "2.25", + "destination_file_system": "2.25", + "id": "2.25", + "junction_origin_path": "2.25", + "name": "2.25", + "origin_file_system": "2.25", + "status": "2.25" + } + }, + "GET /file-system-replica-links": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "direction": "2.0", + "id": "2.0", + "lag": "2.0", + "link_type": "2.17", + "local_file_system": "2.0", + "policies": "2.0", + "recovery_point": "2.0", + "remote": "2.0", + "remote_file_system": "2.0", + "status": "2.0", + "status_details": "2.0" + } + }, + "GET /file-system-replica-links/policies": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "context": "2.22", + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "link": "2.0", + "member": "2.0", + "policy": "2.0" + } + }, + "GET /file-system-replica-links/transfer": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "completed": "2.0", + "context": "2.17", + "data_transferred": "2.0", + "direction": "2.0", + "id": "2.0", + "local_snapshot": "2.10", + "name": "2.0", + "progress": "2.0", + "remote": "2.0", + "remote_snapshot": "2.0", + "started": "2.0", + "status": "2.0" + } + }, + "GET /file-system-snapshots": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "created": "2.0", + "destroyed": "2.0", + "id": "2.0", + "name": "2.0", + "owner": "2.0", + "owner_destroyed": "2.0", + "policies": "2.12", + "policy": "2.0", + "source": "2.0", + "suffix": "2.0", + "time_remaining": "2.0" + }, + "removedResponseFields": [ + { + "field": "copyable", + "location": "items", + "introducedVersion": "2.0", + "lastSeenVersion": "2.10" + } + ] + }, + "GET /file-system-snapshots/policies": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.0", + "policy": "2.0" + } + }, + "GET /file-system-snapshots/transfer": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "completed": "2.0", + "context": "2.17", + "data_transferred": "2.0", + "direction": "2.0", + "id": "2.0", + "local_snapshot": "2.10", + "name": "2.0", + "progress": "2.0", + "remote": "2.0", + "remote_snapshot": "2.0", + "started": "2.0", + "status": "2.0" + } + }, + "GET /file-system-user-quotas": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.25", + "errors": "2.25", + "items": "2.25", + "total_item_count": "2.25" + }, + "responseItemProperties": { + "context": "2.25", + "file_system": "2.25", + "percentage_used": "2.25", + "policy_rule": "2.25", + "usage": "2.25", + "user": "2.25" + } + }, + "GET /file-systems": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "created": "2.0", + "default_group_quota": "2.0", + "default_user_quota": "2.0", + "destroyed": "2.0", + "eradication_config": "2.15", + "fast_remove_directory_enabled": "2.0", + "group_ownership": "2.13", + "hard_limit_enabled": "2.0", + "http": "2.0", + "id": "2.0", + "multi_protocol": "2.0", + "name": "2.0", + "nfs": "2.0", + "node_group": "2.18", + "promotion_status": "2.0", + "provisioned": "2.0", + "qos_policy": "2.17", + "realms": "2.19", + "requested_promotion_state": "2.0", + "smb": "2.0", + "snapshot_directory_enabled": "2.0", + "source": "2.0", + "space": "2.0", + "storage_class": "2.16", + "time_remaining": "2.0", + "workload": "2.23", + "writable": "2.0" + } + }, + "GET /file-systems/audit-policies": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.14", + "errors": "2.17", + "items": "2.14", + "total_item_count": "2.14" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.14", + "policy": "2.14" + } + }, + "GET /file-systems/data-eviction-policies": { + "minVersion": "2.21", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.21", + "errors": "2.21", + "items": "2.21", + "total_item_count": "2.21" + }, + "responseItemProperties": { + "context": "2.21", + "member": "2.21", + "policy": "2.21" + } + }, + "GET /file-systems/groups": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.25", + "errors": "2.25", + "items": "2.25", + "total_item_count": "2.25" + }, + "responseItemProperties": { + "context": "2.25", + "file_system": "2.25", + "group": "2.25", + "limited_by": "2.25", + "percentage_used": "2.25", + "quota_limit": "2.25", + "usage": "2.25" + } + }, + "GET /file-systems/groups/performance": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0", + "total": "2.0" + }, + "responseItemProperties": { + "bytes_per_op": "2.0", + "bytes_per_read": "2.0", + "bytes_per_write": "2.0", + "file_system": "2.0", + "group": "2.0", + "name": "2.0", + "others_per_sec": "2.0", + "read_bytes_per_sec": "2.0", + "reads_per_sec": "2.0", + "time": "2.0", + "usec_per_other_op": "2.0", + "usec_per_read_op": "2.0", + "usec_per_write_op": "2.0", + "write_bytes_per_sec": "2.0", + "writes_per_sec": "2.0" + } + }, + "GET /file-systems/locks": { + "minVersion": "2.8", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.8", + "errors": "2.22", + "items": "2.8", + "total_item_count": "2.8" + }, + "responseItemProperties": { + "access_type": "2.8", + "client": "2.8", + "context": "2.22", + "created_at": "2.8", + "inode": "2.8", + "name": "2.8", + "path": "2.8", + "protocol": "2.8", + "range": "2.8", + "source": "2.8" + } + }, + "GET /file-systems/locks/clients": { + "minVersion": "2.8", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.8", + "errors": "2.22", + "items": "2.8", + "total_item_count": "2.8" + }, + "responseItemProperties": { + "context": "2.22", + "name": "2.8" + } + }, + "GET /file-systems/open-files": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "client": "2.17", + "id": "2.17", + "lock_count": "2.17", + "mode": "2.17", + "path": "2.17", + "session": "2.17", + "source": "2.17", + "user": "2.17" + } + }, + "GET /file-systems/performance": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "bytes_per_op": "2.0", + "bytes_per_read": "2.0", + "bytes_per_write": "2.0", + "id": "2.0", + "name": "2.0", + "others_per_sec": "2.0", + "read_bytes_per_sec": "2.0", + "reads_per_sec": "2.0", + "time": "2.0", + "usec_per_other_op": "2.0", + "usec_per_read_op": "2.0", + "usec_per_write_op": "2.0", + "write_bytes_per_sec": "2.0", + "writes_per_sec": "2.0" + } + }, + "GET /file-systems/policies": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.0", + "policy": "2.0" + } + }, + "GET /file-systems/policies-all": { + "minVersion": "2.3", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.3", + "errors": "2.17", + "items": "2.3", + "total_item_count": "2.3" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.3", + "policy": "2.3" + } + }, + "GET /file-systems/sessions": { + "minVersion": "2.10", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.10", + "errors": "2.22", + "items": "2.10", + "total_item_count": "2.10" + }, + "responseItemProperties": { + "authentication": "2.10", + "client": "2.10", + "connection_time": "2.10", + "context": "2.22", + "idle_time": "2.10", + "name": "2.10", + "opens": "2.10", + "port": "2.10", + "protocol": "2.10", + "time": "2.10", + "user": "2.10" + } + }, + "GET /file-systems/space/storage-classes": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.18", + "items": "2.18", + "total": "2.18", + "total_item_count": "2.18" + }, + "responseItemProperties": { + "id": "2.18", + "name": "2.18", + "space": "2.18", + "storage_class": "2.18", + "time": "2.18" + } + }, + "GET /file-systems/user-group-quota-policies": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.25", + "errors": "2.25", + "items": "2.25", + "total_item_count": "2.25" + }, + "responseItemProperties": { + "context": "2.25", + "member": "2.25", + "policy": "2.25" + } + }, + "GET /file-systems/users": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.25", + "errors": "2.25", + "items": "2.25", + "total_item_count": "2.25" + }, + "responseItemProperties": { + "context": "2.25", + "file_system": "2.25", + "limited_by": "2.25", + "percentage_used": "2.25", + "quota_limit": "2.25", + "usage": "2.25", + "user": "2.25" + } + }, + "GET /file-systems/users/performance": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0", + "total": "2.0" + }, + "responseItemProperties": { + "bytes_per_op": "2.0", + "bytes_per_read": "2.0", + "bytes_per_write": "2.0", + "file_system": "2.0", + "name": "2.0", + "others_per_sec": "2.0", + "read_bytes_per_sec": "2.0", + "reads_per_sec": "2.0", + "time": "2.0", + "usec_per_other_op": "2.0", + "usec_per_read_op": "2.0", + "usec_per_write_op": "2.0", + "user": "2.0", + "write_bytes_per_sec": "2.0", + "writes_per_sec": "2.0" + } + }, + "GET /file-systems/worm-data-policies": { + "minVersion": "2.15", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.15", + "errors": "2.17", + "items": "2.15", + "total_item_count": "2.15" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.15", + "policy": "2.15" + } + }, + "GET /fleets": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "id": "2.17", + "is_local": "2.17", + "name": "2.17" + } + }, + "GET /fleets/fleet-key": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "created": "2.17", + "expires": "2.17", + "fleet_key": "2.17" + } + }, + "GET /fleets/members": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "coordinator_of": "2.20", + "fleet": "2.17", + "member": "2.17", + "status": "2.17", + "status_details": "2.17" + } + }, + "GET /hardware": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "data_mac": "2.16", + "details": "2.0", + "id": "2.0", + "identify_enabled": "2.0", + "index": "2.0", + "management_mac": "2.16", + "model": "2.0", + "name": "2.0", + "part_number": "2.8", + "sensor_readings": "2.18", + "serial": "2.0", + "slot": "2.0", + "speed": "2.0", + "status": "2.0", + "temperature": "2.0", + "type": "2.0" + } + }, + "GET /hardware-connectors": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "connector_type": "2.0", + "id": "2.0", + "lane_speed": "2.0", + "lanes_per_port": "2.20", + "name": "2.0", + "port_count": "2.0", + "port_speed": "2.15", + "transceiver_type": "2.0" + } + }, + "GET /hardware-connectors/performance": { + "minVersion": "2.3", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.3", + "items": "2.3", + "total": "2.3", + "total_item_count": "2.3" + }, + "responseItemProperties": { + "id": "2.3", + "link_aggregation_group": "2.3", + "name": "2.3", + "other_errors_per_sec": "2.3", + "received_bytes_per_sec": "2.3", + "received_crc_errors_per_sec": "2.3", + "received_frame_errors_per_sec": "2.3", + "received_packets_per_sec": "2.3", + "time": "2.3", + "total_errors_per_sec": "2.3", + "transmitted_bytes_per_sec": "2.3", + "transmitted_carrier_errors_per_sec": "2.3", + "transmitted_dropped_errors_per_sec": "2.3", + "transmitted_packets_per_sec": "2.3" + } + }, + "GET /keytabs": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "encryption_type": "2.0", + "fqdn": "2.0", + "id": "2.0", + "kvno": "2.0", + "name": "2.0", + "prefix": "2.0", + "principal": "2.0", + "realm": "2.0", + "server": "2.16", + "source": "2.0", + "suffix": "2.0" + } + }, + "GET /keytabs/download": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": {}, + "responseItemProperties": {} + }, + "GET /kmip": { + "minVersion": "2.1", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.1" + }, + "responseItemProperties": { + "ca_certificate": "2.1", + "ca_certificate_group": "2.1", + "id": "2.1", + "name": "2.1", + "uris": "2.1" + } + }, + "GET /kmip/test": { + "minVersion": "2.1", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.1" + }, + "responseItemProperties": { + "component_address": "2.1", + "component_name": "2.1", + "description": "2.1", + "destination": "2.1", + "enabled": "2.1", + "resource": "2.1", + "result_details": "2.1", + "success": "2.1", + "test_type": "2.1" + } + }, + "GET /legal-holds": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "description": "2.17", + "id": "2.17", + "name": "2.17", + "realms": "2.19" + } + }, + "GET /legal-holds/held-entities": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "file_system": "2.17", + "legal_hold": "2.17", + "path": "2.17", + "status": "2.17" + } + }, + "GET /lifecycle-rules": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "abort_incomplete_multipart_uploads_after": "2.1", + "bucket": "2.0", + "cleanup_expired_object_delete_marker": "2.1", + "context": "2.17", + "enabled": "2.0", + "id": "2.0", + "keep_current_version_for": "2.1", + "keep_current_version_until": "2.1", + "keep_previous_version_for": "2.0", + "name": "2.0", + "prefix": "2.0", + "rule_id": "2.0" + } + }, + "GET /link-aggregation-groups": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "id": "2.0", + "lag_speed": "2.0", + "mac_address": "2.0", + "name": "2.0", + "port_speed": "2.0", + "ports": "2.0", + "status": "2.0" + } + }, + "GET /log-targets/file-systems": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.20", + "items": "2.20", + "total_item_count": "2.20" + }, + "responseItemProperties": { + "file_system": "2.20", + "id": "2.20", + "keep_for": "2.20", + "keep_size": "2.20", + "name": "2.20" + } + }, + "GET /log-targets/object-store": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.20", + "errors": "2.20", + "items": "2.20", + "total_item_count": "2.20" + }, + "responseItemProperties": { + "bucket": "2.20", + "context": "2.20", + "id": "2.20", + "log_name_prefix": "2.20", + "log_rotate": "2.20", + "name": "2.20" + } + }, + "GET /logs": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": {}, + "responseItemProperties": {} + }, + "GET /logs-async": { + "minVersion": "2.4", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.4", + "items": "2.4", + "total_item_count": "2.4" + }, + "responseItemProperties": { + "available_files": "2.4", + "end_time": "2.4", + "hardware_components": "2.4", + "id": "2.4", + "last_request_time": "2.4", + "name": "2.4", + "processing": "2.4", + "progress": "2.4", + "start_time": "2.4" + } + }, + "GET /logs-async/download": { + "minVersion": "2.4", + "lastSeenVersion": "2.28", + "responseEnvelope": {}, + "responseItemProperties": {} + }, + "GET /maintenance-windows": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.16", + "items": "2.16", + "total_item_count": "2.16" + }, + "responseItemProperties": { + "created": "2.16", + "expires": "2.16", + "id": "2.16", + "name": "2.16" + } + }, + "GET /management-access-policies": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.19", + "errors": "2.24", + "items": "2.19", + "total_item_count": "2.19" + }, + "responseItemProperties": { + "aggregation_strategy": "2.19", + "context": "2.24", + "enabled": "2.19", + "id": "2.19", + "is_local": "2.19", + "location": "2.19", + "name": "2.19", + "policy_type": "2.19", + "realms": "2.19", + "rules": "2.19", + "version": "2.19" + } + }, + "GET /management-access-policies/admins": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.19", + "errors": "2.24", + "items": "2.19", + "total_item_count": "2.19" + }, + "responseItemProperties": { + "context": "2.24", + "member": "2.19", + "policy": "2.19" + } + }, + "GET /management-access-policies/directory-services/roles": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.19", + "items": "2.19", + "total_item_count": "2.19" + }, + "responseItemProperties": { + "member": "2.19", + "policy": "2.19" + } + }, + "GET /management-access-policies/members": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.19", + "errors": "2.24", + "items": "2.19", + "total_item_count": "2.19" + }, + "responseItemProperties": { + "context": "2.24", + "member": "2.19", + "policy": "2.19" + } + }, + "GET /management-access-policies/roles": { + "minVersion": "2.26", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.26", + "errors": "2.26", + "items": "2.26", + "total_item_count": "2.26" + }, + "responseItemProperties": { + "context": "2.26", + "description": "2.26", + "id": "2.26", + "name": "2.26", + "pure_defined": "2.26" + } + }, + "GET /management-access-policies/roles/permissions": { + "minVersion": "2.26", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.26", + "errors": "2.26", + "items": "2.26", + "total_item_count": "2.26" + }, + "responseItemProperties": { + "actions": "2.26", + "context": "2.26", + "id": "2.26", + "name": "2.26", + "resource": "2.26", + "role": "2.26" + } + }, + "GET /management-access-policies/roles/permissions/supported-resources": { + "minVersion": "2.26", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.26", + "errors": "2.26", + "items": "2.26", + "total_item_count": "2.26" + }, + "responseItemProperties": { + "context": "2.26", + "description": "2.26", + "name": "2.26", + "supported_actions": "2.26" + } + }, + "GET /management-access-policies/rules": { + "minVersion": "2.26", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.26", + "errors": "2.26", + "items": "2.26", + "total_item_count": "2.26" + }, + "responseItemProperties": { + "context": "2.26", + "id": "2.26", + "index": "2.26", + "name": "2.26", + "policy": "2.26", + "policy_version": "2.26", + "role": "2.26", + "scope": "2.26" + } + }, + "GET /management-authentication-policies": { + "minVersion": "2.22", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.22", + "errors": "2.22", + "items": "2.22", + "total_item_count": "2.22" + }, + "responseItemProperties": { + "context": "2.22", + "enabled": "2.22", + "id": "2.22", + "is_local": "2.22", + "location": "2.22", + "name": "2.22", + "policy_type": "2.22", + "realms": "2.22", + "ssh": "2.22" + } + }, + "GET /management-authentication-policies/members": { + "minVersion": "2.22", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.22", + "errors": "2.22", + "items": "2.22", + "total_item_count": "2.22" + }, + "responseItemProperties": { + "context": "2.22", + "member": "2.22", + "policy": "2.22" + } + }, + "GET /network-access-policies": { + "minVersion": "2.13", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.13", + "items": "2.13", + "total_item_count": "2.13" + }, + "responseItemProperties": { + "enabled": "2.13", + "id": "2.13", + "is_local": "2.13", + "location": "2.13", + "name": "2.13", + "policy_type": "2.13", + "realms": "2.19", + "rules": "2.13", + "version": "2.13" + } + }, + "GET /network-access-policies/members": { + "minVersion": "2.13", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.13", + "items": "2.13", + "total_item_count": "2.13" + }, + "responseItemProperties": { + "member": "2.13", + "policy": "2.13" + } + }, + "GET /network-access-policies/rules": { + "minVersion": "2.13", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.13", + "items": "2.13", + "total_item_count": "2.13" + }, + "responseItemProperties": { + "client": "2.13", + "effect": "2.13", + "id": "2.13", + "index": "2.13", + "interfaces": "2.13", + "name": "2.13", + "policy": "2.13", + "policy_version": "2.13" + } + }, + "GET /network-interfaces": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "address": "2.0", + "attached_servers": "2.20", + "enabled": "2.0", + "gateway": "2.0", + "id": "2.0", + "mtu": "2.0", + "name": "2.0", + "netmask": "2.0", + "rdma_enabled": "2.28", + "realms": "2.19", + "services": "2.0", + "subnet": "2.0", + "type": "2.0", + "vlan": "2.0" + }, + "removedResponseFields": [ + { + "field": "server", + "location": "items", + "introducedVersion": "2.16", + "lastSeenVersion": "2.19" + } + ] + }, + "GET /network-interfaces/connectors": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "connector_type": "2.17", + "id": "2.17", + "lane_speed": "2.17", + "lanes_per_port": "2.20", + "name": "2.17", + "port_count": "2.17", + "port_speed": "2.17", + "transceiver_type": "2.17" + } + }, + "GET /network-interfaces/connectors/performance": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "items": "2.17", + "total": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "flow_control_received_congestion_packets_per_sec": "2.17", + "flow_control_received_discarded_packets_per_sec": "2.17", + "flow_control_received_lossless_bytes_per_sec": "2.17", + "flow_control_received_pause_frames_per_sec": "2.17", + "flow_control_transmitted_congestion_packets_per_sec": "2.17", + "flow_control_transmitted_discarded_packets_per_sec": "2.17", + "flow_control_transmitted_lossless_bytes_per_sec": "2.17", + "flow_control_transmitted_pause_frames_per_sec": "2.17", + "id": "2.17", + "link_aggregation_group": "2.17", + "name": "2.17", + "other_errors_per_sec": "2.17", + "rdma_received_req_cqe_errors_per_sec": "2.17", + "rdma_received_sequence_errors_per_sec": "2.17", + "rdma_transmitted_local_ack_timeout_errors_per_sec": "2.17", + "received_bytes_per_sec": "2.17", + "received_crc_errors_per_sec": "2.17", + "received_frame_errors_per_sec": "2.17", + "received_packets_per_sec": "2.17", + "time": "2.17", + "total_errors_per_sec": "2.17", + "transmitted_bytes_per_sec": "2.17", + "transmitted_carrier_errors_per_sec": "2.17", + "transmitted_dropped_errors_per_sec": "2.17", + "transmitted_packets_per_sec": "2.17" + } + }, + "GET /network-interfaces/connectors/settings": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "id": "2.17", + "name": "2.17", + "roce": "2.17" + } + }, + "GET /network-interfaces/neighbors": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.20", + "items": "2.20", + "total_item_count": "2.20" + }, + "responseItemProperties": { + "initial_ttl_in_sec": "2.20", + "local_port": "2.20", + "neighbor_chassis": "2.20", + "neighbor_port": "2.20" + } + }, + "GET /network-interfaces/network-connection-statistics": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.18" + }, + "responseItemProperties": { + "current_state": "2.18", + "local": "2.18", + "remote": "2.18", + "time": "2.18" + } + }, + "GET /network-interfaces/ping": { + "minVersion": "2.6", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.6", + "items": "2.6", + "total_item_count": "2.6" + }, + "responseItemProperties": { + "component_name": "2.6", + "destination": "2.6", + "details": "2.6", + "source": "2.6" + } + }, + "GET /network-interfaces/tls-policies": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "member": "2.17", + "policy": "2.17" + } + }, + "GET /network-interfaces/trace": { + "minVersion": "2.6", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.6", + "items": "2.6", + "total_item_count": "2.6" + }, + "responseItemProperties": { + "component_name": "2.6", + "destination": "2.6", + "details": "2.6", + "source": "2.6" + } + }, + "GET /nfs-export-policies": { + "minVersion": "2.3", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.3", + "errors": "2.17", + "items": "2.3", + "total_item_count": "2.3" + }, + "responseItemProperties": { + "context": "2.17", + "enabled": "2.3", + "id": "2.3", + "is_local": "2.3", + "location": "2.3", + "name": "2.3", + "policy_type": "2.3", + "realms": "2.19", + "rules": "2.3", + "version": "2.3", + "workload": "2.23" + } + }, + "GET /nfs-export-policies/rules": { + "minVersion": "2.3", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.3", + "errors": "2.17", + "items": "2.3", + "total_item_count": "2.3" + }, + "responseItemProperties": { + "access": "2.3", + "anongid": "2.3", + "anonuid": "2.3", + "atime": "2.3", + "client": "2.3", + "context": "2.17", + "fileid_32bit": "2.3", + "id": "2.3", + "index": "2.3", + "name": "2.3", + "permission": "2.3", + "policy": "2.3", + "policy_version": "2.3", + "required_transport_security": "2.18", + "secure": "2.3", + "security": "2.3" + } + }, + "GET /node-groups": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.18", + "items": "2.18", + "total_item_count": "2.18" + }, + "responseItemProperties": { + "id": "2.18", + "name": "2.18" + } + }, + "GET /node-groups/nodes": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.18", + "items": "2.18", + "total_item_count": "2.18" + }, + "responseItemProperties": { + "group": "2.18", + "member": "2.18" + } + }, + "GET /node-groups/uses": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.18", + "items": "2.18", + "total_item_count": "2.18" + }, + "responseItemProperties": { + "id": "2.18", + "name": "2.18", + "use": "2.18" + } + }, + "GET /nodes": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.18", + "items": "2.18", + "total": "2.18", + "total_item_count": "2.18" + }, + "responseItemProperties": { + "capacity": "2.18", + "chassis_serial_number": "2.23", + "data_addresses": "2.18", + "details": "2.18", + "id": "2.18", + "management_address": "2.18", + "name": "2.18", + "raw_capacity": "2.18", + "serial_number": "2.18", + "status": "2.18", + "unique": "2.18" + } + }, + "GET /object-store-access-keys": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "access_key_id": "2.20", + "context": "2.17", + "created": "2.0", + "enabled": "2.0", + "name": "2.0", + "secret_access_key": "2.0", + "user": "2.0" + } + }, + "GET /object-store-access-policies": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "account": "2.2", + "arn": "2.2", + "context": "2.17", + "created": "2.2", + "description": "2.2", + "enabled": "2.2", + "id": "2.0", + "is_local": "2.2", + "location": "2.2", + "name": "2.0", + "policy_type": "2.2", + "realms": "2.19", + "rules": "2.2", + "updated": "2.2" + } + }, + "GET /object-store-access-policies/object-store-roles": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "errors": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.17", + "policy": "2.17" + } + }, + "GET /object-store-access-policies/object-store-users": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.0", + "policy": "2.0" + } + }, + "GET /object-store-access-policies/rules": { + "minVersion": "2.2", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.2", + "errors": "2.17", + "items": "2.2", + "total_item_count": "2.2" + }, + "responseItemProperties": { + "actions": "2.2", + "conditions": "2.2", + "context": "2.17", + "effect": "2.2", + "name": "2.2", + "policy": "2.2", + "resources": "2.2" + } + }, + "GET /object-store-access-policy-actions": { + "minVersion": "2.2", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.2", + "errors": "2.17", + "items": "2.2", + "total_item_count": "2.2" + }, + "responseItemProperties": { + "context": "2.17", + "description": "2.2", + "name": "2.2" + } + }, + "GET /object-store-account-exports": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.20", + "errors": "2.20", + "items": "2.20", + "total_item_count": "2.20" + }, + "responseItemProperties": { + "context": "2.20", + "enabled": "2.20", + "id": "2.20", + "member": "2.20", + "name": "2.20", + "policy": "2.20", + "realms": "2.20", + "server": "2.20" + } + }, + "GET /object-store-accounts": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "bucket_defaults": "2.8", + "context": "2.17", + "created": "2.0", + "hard_limit_enabled": "2.8", + "id": "2.0", + "name": "2.0", + "object_count": "2.0", + "public_access_config": "2.12", + "quota_limit": "2.8", + "realms": "2.20", + "space": "2.0" + } + }, + "GET /object-store-remote-credentials": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "access_key_id": "2.0", + "context": "2.17", + "id": "2.0", + "name": "2.0", + "realms": "2.20", + "remote": "2.0", + "secret_access_key": "2.0" + } + }, + "GET /object-store-roles": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "errors": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "account": "2.17", + "context": "2.17", + "created": "2.17", + "id": "2.17", + "max_session_duration": "2.17", + "name": "2.17", + "prn": "2.17", + "trusted_entities": "2.17" + } + }, + "GET /object-store-roles/object-store-access-policies": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "errors": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.17", + "policy": "2.17" + } + }, + "GET /object-store-roles/object-store-trust-policies": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "errors": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "context": "2.17", + "enabled": "2.17", + "id": "2.17", + "is_local": "2.17", + "location": "2.17", + "name": "2.17", + "policy_type": "2.17", + "realms": "2.19", + "role": "2.17", + "rules": "2.17" + } + }, + "GET /object-store-roles/object-store-trust-policies/download": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": {}, + "responseItemProperties": {} + }, + "GET /object-store-roles/object-store-trust-policies/rules": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "errors": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "actions": "2.17", + "conditions": "2.17", + "context": "2.17", + "effect": "2.17", + "index": "2.17", + "name": "2.17", + "policy": "2.17", + "principals": "2.17" + } + }, + "GET /object-store-users": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "access_keys": "2.0", + "account": "2.0", + "context": "2.17", + "created": "2.0", + "id": "2.0", + "name": "2.0" + } + }, + "GET /object-store-users/object-store-access-policies": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.0", + "policy": "2.0" + } + }, + "GET /object-store-virtual-hosts": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "attached_servers": "2.20", + "context": "2.17", + "hostname": "2.20", + "id": "2.0", + "name": "2.0", + "realms": "2.20" + } + }, + "GET /password-policies": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.16", + "items": "2.16", + "total_item_count": "2.16" + }, + "responseItemProperties": { + "enabled": "2.16", + "enforce_dictionary_check": "2.16", + "enforce_username_check": "2.16", + "id": "2.16", + "is_local": "2.16", + "location": "2.16", + "lockout_duration": "2.16", + "max_login_attempts": "2.16", + "max_password_age": "2.18", + "min_character_groups": "2.16", + "min_characters_per_group": "2.16", + "min_password_age": "2.16", + "min_password_length": "2.16", + "name": "2.16", + "password_history": "2.16", + "policy_type": "2.16", + "realms": "2.19" + } + }, + "GET /policies": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "enabled": "2.0", + "id": "2.0", + "is_local": "2.0", + "location": "2.0", + "name": "2.0", + "policy_type": "2.2", + "realms": "2.19", + "retention_lock": "2.5", + "rules": "2.0", + "workload": "2.23" + } + }, + "GET /policies-all": { + "minVersion": "2.2", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.2", + "errors": "2.17", + "items": "2.2", + "total_item_count": "2.2" + }, + "responseItemProperties": { + "context": "2.17", + "enabled": "2.2", + "id": "2.2", + "is_local": "2.2", + "location": "2.2", + "name": "2.2", + "policy_type": "2.2", + "realms": "2.19" + } + }, + "GET /policies-all/members": { + "minVersion": "2.2", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "context": "2.22", + "continuation_token": "2.2", + "errors": "2.17", + "items": "2.2", + "total_item_count": "2.2" + }, + "responseItemProperties": { + "context": "2.17", + "link": "2.2", + "member": "2.2", + "policy": "2.2" + } + }, + "GET /policies/file-system-replica-links": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "context": "2.22", + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "link": "2.0", + "member": "2.0", + "policy": "2.0" + } + }, + "GET /policies/file-system-snapshots": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.0", + "policy": "2.0" + } + }, + "GET /policies/file-systems": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.0", + "policy": "2.0" + } + }, + "GET /policies/members": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "context": "2.22", + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "link": "2.0", + "member": "2.0", + "policy": "2.0" + } + }, + "GET /presets/workload": { + "minVersion": "2.23", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.23", + "errors": "2.23", + "items": "2.23", + "total_item_count": "2.23" + }, + "responseItemProperties": { + "context": "2.23", + "description": "2.23", + "directory_configurations": "2.23", + "export_configurations": "2.23", + "id": "2.23", + "name": "2.23", + "parameters": "2.23", + "periodic_replication_configurations": "2.23", + "placement_configurations": "2.23", + "platform_features": "2.23", + "qos_configurations": "2.23", + "quota_configurations": "2.23", + "revision": "2.23", + "snapshot_configurations": "2.23", + "volume_configurations": "2.23", + "workload_tags": "2.23", + "workload_type": "2.23" + } + }, + "GET /public-keys": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.14", + "items": "2.14", + "total_item_count": "2.14" + }, + "responseItemProperties": { + "algorithm": "2.14", + "id": "2.14", + "key_size": "2.14", + "name": "2.14", + "public_key": "2.14" + } + }, + "GET /public-keys/uses": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.14", + "items": "2.14", + "total_item_count": "2.14" + }, + "responseItemProperties": { + "id": "2.14", + "name": "2.14", + "use": "2.14" + } + }, + "GET /qos-policies": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "errors": "2.23", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "context": "2.23", + "enabled": "2.17", + "id": "2.17", + "is_local": "2.17", + "location": "2.17", + "max_total_bytes_per_sec": "2.17", + "max_total_ops_per_sec": "2.17", + "name": "2.17", + "policy_type": "2.17", + "realms": "2.19" + } + }, + "GET /qos-policies/buckets": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.19", + "errors": "2.23", + "items": "2.19", + "total_item_count": "2.19" + }, + "responseItemProperties": { + "context": "2.23", + "member": "2.19", + "policy": "2.19" + } + }, + "GET /qos-policies/file-systems": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "errors": "2.23", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "context": "2.23", + "member": "2.17", + "policy": "2.17" + } + }, + "GET /qos-policies/members": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "errors": "2.23", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "context": "2.23", + "member": "2.17", + "policy": "2.17" + } + }, + "GET /quotas/groups": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "file_system": "2.0", + "file_system_default_quota": "2.0", + "group": "2.0", + "name": "2.0", + "quota": "2.0", + "usage": "2.0" + } + }, + "GET /quotas/settings": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "contact": "2.0", + "direct_notifications_enabled": "2.0", + "id": "2.0", + "name": "2.0" + } + }, + "GET /quotas/users": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "file_system": "2.0", + "file_system_default_quota": "2.0", + "name": "2.0", + "quota": "2.0", + "usage": "2.0", + "user": "2.0" + } + }, + "GET /rapid-data-locking": { + "minVersion": "2.1", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.1" + }, + "responseItemProperties": { + "enabled": "2.1", + "kmip_server": "2.1" + } + }, + "GET /rapid-data-locking/test": { + "minVersion": "2.1", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.1" + }, + "responseItemProperties": { + "component_address": "2.1", + "component_name": "2.1", + "description": "2.1", + "destination": "2.1", + "enabled": "2.1", + "resource": "2.1", + "result_details": "2.1", + "success": "2.1", + "test_type": "2.1" + } + }, + "GET /realm-connections": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.25", + "items": "2.25", + "total_item_count": "2.25" + }, + "responseItemProperties": { + "id": "2.25", + "local_realm": "2.25", + "remote_realm": "2.25", + "status": "2.25" + } + }, + "GET /realm-connections/connection-key": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.25", + "items": "2.25", + "total_item_count": "2.25" + }, + "responseItemProperties": { + "connection_key": "2.25", + "created": "2.25", + "expires": "2.25", + "realm": "2.25" + } + }, + "GET /realms": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "context": "2.24", + "continuation_token": "2.19", + "errors": "2.24", + "items": "2.19", + "total": "2.19", + "total_item_count": "2.19" + }, + "responseItemProperties": { + "created": "2.19", + "default_inbound_tls_policy": "2.19", + "destroyed": "2.19", + "id": "2.19", + "name": "2.19", + "space": "2.19", + "time_remaining": "2.19" + } + }, + "GET /realms/defaults": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.20", + "errors": "2.20", + "items": "2.20", + "total_item_count": "2.20" + }, + "responseItemProperties": { + "context": "2.20", + "object_store": "2.20", + "realm": "2.20" + } + }, + "GET /realms/space": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.19", + "items": "2.19", + "total": "2.19", + "total_item_count": "2.19" + }, + "responseItemProperties": { + "id": "2.19", + "name": "2.19", + "space": "2.19", + "time": "2.19" + } + }, + "GET /realms/space/storage-classes": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.19", + "items": "2.19", + "total": "2.19", + "total_item_count": "2.19" + }, + "responseItemProperties": { + "id": "2.19", + "name": "2.19", + "space": "2.19", + "storage_class": "2.19", + "time": "2.19" + } + }, + "GET /remote-arrays": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "api_versions": "2.22", + "fleet": "2.17", + "id": "2.17", + "is_local": "2.17", + "model": "2.17", + "name": "2.17", + "os": "2.17", + "topology_group": "2.26", + "version": "2.17" + } + }, + "GET /remote-realms": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.25", + "items": "2.25", + "total_item_count": "2.25" + }, + "responseItemProperties": { + "arrays": "2.25", + "id": "2.25", + "name": "2.25" + } + }, + "GET /resiliency-groups": { + "minVersion": "2.23", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.23", + "items": "2.23", + "total_item_count": "2.23" + }, + "responseItemProperties": { + "id": "2.23", + "name": "2.23", + "status": "2.23", + "status_details": "2.23" + } + }, + "GET /resiliency-groups/members": { + "minVersion": "2.23", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.23", + "items": "2.23", + "total_item_count": "2.23" + }, + "responseItemProperties": { + "group": "2.23", + "member": "2.23" + } + }, + "GET /resource-accesses": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.19", + "items": "2.19", + "more_items_remaining": "2.19", + "total_item_count": "2.19" + }, + "responseItemProperties": { + "id": "2.19", + "resource": "2.19", + "scope": "2.19" + } + }, + "GET /roles": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "id": "2.0", + "name": "2.0", + "permissions": "2.0" + } + }, + "GET /s3-export-policies": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.20", + "errors": "2.20", + "items": "2.20", + "total_item_count": "2.20" + }, + "responseItemProperties": { + "context": "2.20", + "enabled": "2.20", + "id": "2.20", + "is_local": "2.20", + "location": "2.20", + "name": "2.20", + "policy_type": "2.20", + "realms": "2.20", + "rules": "2.20" + } + }, + "GET /s3-export-policies/rules": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.20", + "errors": "2.20", + "items": "2.20", + "total_item_count": "2.20" + }, + "responseItemProperties": { + "actions": "2.20", + "context": "2.20", + "effect": "2.20", + "name": "2.20", + "policy": "2.20", + "resources": "2.20" + } + }, + "GET /servers": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.16", + "errors": "2.17", + "items": "2.16", + "total_item_count": "2.16" + }, + "responseItemProperties": { + "context": "2.17", + "created": "2.16", + "directory_services": "2.16", + "dns": "2.16", + "id": "2.16", + "local_directory_service": "2.24", + "name": "2.16", + "realms": "2.19" + } + }, + "GET /sessions": { + "minVersion": "2.3", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.3", + "items": "2.3", + "total_item_count": "2.3" + }, + "responseItemProperties": { + "end_time": "2.3", + "event": "2.3", + "event_count": "2.3", + "id": "2.3", + "identifying_details": "2.14", + "location": "2.3", + "method": "2.3", + "name": "2.3", + "start_time": "2.3", + "user": "2.3", + "user_interface": "2.3" + } + }, + "GET /smb-client-policies": { + "minVersion": "2.10", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.10", + "errors": "2.17", + "items": "2.10", + "total_item_count": "2.10" + }, + "responseItemProperties": { + "access_based_enumeration_enabled": "2.17", + "context": "2.17", + "enabled": "2.10", + "id": "2.10", + "is_local": "2.10", + "location": "2.10", + "name": "2.10", + "policy_type": "2.10", + "realms": "2.19", + "rules": "2.10", + "version": "2.10", + "workload": "2.23" + } + }, + "GET /smb-client-policies/rules": { + "minVersion": "2.10", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.10", + "errors": "2.17", + "items": "2.10", + "total_item_count": "2.10" + }, + "responseItemProperties": { + "client": "2.10", + "context": "2.17", + "encryption": "2.11", + "id": "2.10", + "index": "2.10", + "name": "2.10", + "permission": "2.10", + "policy": "2.10", + "policy_version": "2.10" + } + }, + "GET /smb-share-policies": { + "minVersion": "2.10", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.10", + "errors": "2.17", + "items": "2.10", + "total_item_count": "2.10" + }, + "responseItemProperties": { + "context": "2.17", + "enabled": "2.10", + "id": "2.10", + "is_local": "2.10", + "location": "2.10", + "name": "2.10", + "policy_type": "2.10", + "realms": "2.19", + "rules": "2.10", + "workload": "2.23" + } + }, + "GET /smb-share-policies/rules": { + "minVersion": "2.10", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.10", + "errors": "2.17", + "items": "2.10", + "total_item_count": "2.10" + }, + "responseItemProperties": { + "change": "2.10", + "context": "2.17", + "full_control": "2.10", + "id": "2.10", + "name": "2.10", + "policy": "2.10", + "principal": "2.10", + "read": "2.10" + } + }, + "GET /smtp-servers": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "encryption_mode": "2.15", + "id": "2.0", + "name": "2.0", + "relay_host": "2.0", + "sender_domain": "2.0" + } + }, + "GET /snmp-agents": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "engine_id": "2.0", + "id": "2.0", + "name": "2.0", + "v2c": "2.0", + "v3": "2.0", + "version": "2.0" + } + }, + "GET /snmp-agents/mib": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "mib": "2.0" + } + }, + "GET /snmp-managers": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "host": "2.0", + "id": "2.0", + "name": "2.0", + "notification": "2.0", + "v2c": "2.0", + "v3": "2.0", + "version": "2.0" + } + }, + "GET /snmp-managers/test": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "component_address": "2.0", + "component_name": "2.0", + "description": "2.0", + "destination": "2.0", + "enabled": "2.0", + "resource": "2.0", + "result_details": "2.0", + "success": "2.0", + "test_type": "2.0" + } + }, + "GET /software-bundle": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.24", + "items": "2.24", + "total_item_count": "2.24" + }, + "responseItemProperties": { + "created": "2.24", + "details": "2.24", + "download_progress": "2.24", + "id": "2.24", + "source": "2.24", + "status": "2.24" + } + }, + "GET /software-check": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.16", + "items": "2.16", + "total_item_count": "2.16" + }, + "responseItemProperties": { + "checks": "2.16", + "details": "2.16", + "end_time": "2.16", + "id": "2.16", + "name": "2.16", + "software_name": "2.16", + "software_upgrade_hops": "2.16", + "software_version": "2.16", + "start_time": "2.16", + "status": "2.16" + } + }, + "GET /software-patches": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.24", + "items": "2.24", + "total_item_count": "2.24" + }, + "responseItemProperties": { + "alert_code": "2.24", + "description": "2.24", + "details": "2.24", + "ha_reduction_required": "2.24", + "id": "2.24", + "name": "2.24", + "progress": "2.24", + "status": "2.24" + } + }, + "GET /ssh-certificate-authority-policies": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.14", + "errors": "2.24", + "items": "2.14", + "total_item_count": "2.14" + }, + "responseItemProperties": { + "context": "2.24", + "enabled": "2.14", + "id": "2.14", + "is_local": "2.14", + "location": "2.14", + "name": "2.14", + "policy_type": "2.14", + "realms": "2.19", + "signing_authority": "2.14", + "static_authorized_principals": "2.14" + } + }, + "GET /ssh-certificate-authority-policies/admins": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.14", + "errors": "2.24", + "items": "2.14", + "total_item_count": "2.14" + }, + "responseItemProperties": { + "context": "2.24", + "member": "2.14", + "policy": "2.14" + } + }, + "GET /ssh-certificate-authority-policies/arrays": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.14", + "errors": "2.24", + "items": "2.14", + "total_item_count": "2.14" + }, + "responseItemProperties": { + "context": "2.24", + "member": "2.14", + "policy": "2.14" + } + }, + "GET /ssh-certificate-authority-policies/members": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.14", + "errors": "2.24", + "items": "2.14", + "total_item_count": "2.14" + }, + "responseItemProperties": { + "context": "2.24", + "member": "2.14", + "policy": "2.14" + } + }, + "GET /sso/oidc/idps": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "enabled": "2.17", + "id": "2.17", + "idp": "2.17", + "name": "2.17", + "prn": "2.17", + "services": "2.17" + } + }, + "GET /sso/saml2/idps": { + "minVersion": "2.15", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.15", + "items": "2.15", + "total_item_count": "2.15" + }, + "responseItemProperties": { + "array_url": "2.15", + "binding": "2.17", + "enabled": "2.15", + "id": "2.15", + "idp": "2.15", + "management": "2.24", + "name": "2.15", + "prn": "2.17", + "services": "2.17", + "sp": "2.15" + } + }, + "GET /sso/saml2/idps/test": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.16" + }, + "responseItemProperties": { + "component_address": "2.16", + "component_name": "2.16", + "description": "2.16", + "destination": "2.16", + "enabled": "2.16", + "resource": "2.16", + "result_details": "2.16", + "success": "2.16", + "test_type": "2.16" + } + }, + "GET /storage-class-tiering-policies": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.18", + "items": "2.18", + "total_item_count": "2.18" + }, + "responseItemProperties": { + "archival_rules": "2.18", + "enabled": "2.18", + "id": "2.18", + "is_local": "2.18", + "location": "2.18", + "name": "2.18", + "policy_type": "2.18", + "realms": "2.19", + "retrieval_rules": "2.18" + } + }, + "GET /storage-class-tiering-policies/members": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.18", + "errors": "2.18", + "items": "2.18", + "total_item_count": "2.18" + }, + "responseItemProperties": { + "member": "2.18", + "policy": "2.18" + } + }, + "GET /storage-classes/members": { + "minVersion": "2.27", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.27", + "items": "2.27", + "total_item_count": "2.27" + }, + "responseItemProperties": { + "member": "2.27", + "storage_class": "2.27" + } + }, + "GET /subnets": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "enabled": "2.0", + "gateway": "2.0", + "id": "2.0", + "interfaces": "2.0", + "link_aggregation_group": "2.0", + "mtu": "2.0", + "name": "2.0", + "prefix": "2.0", + "services": "2.0", + "vlan": "2.0" + } + }, + "GET /support": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "edge_agent_update_enabled": "2.18", + "edge_management_enabled": "2.18", + "id": "2.0", + "name": "2.0", + "phonehome_enabled": "2.0", + "proxy": "2.0", + "remote_assist_active": "2.0", + "remote_assist_duration": "2.14", + "remote_assist_expires": "2.0", + "remote_assist_opened": "2.0", + "remote_assist_paths": "2.0", + "remote_assist_status": "2.0" + } + }, + "GET /support-diagnostics": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.16", + "items": "2.16", + "total_item_count": "2.16" + }, + "responseItemProperties": { + "analysis_period": "2.16", + "id": "2.16", + "index": "2.16", + "name": "2.16", + "severity_count": "2.16", + "start_time": "2.16", + "status": "2.16", + "task_id": "2.16", + "version": "2.16" + } + }, + "GET /support-diagnostics/details": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.16", + "items": "2.16", + "total_item_count": "2.16" + }, + "responseItemProperties": { + "id": "2.16", + "index": "2.16", + "name": "2.16", + "result_details": "2.16", + "severity": "2.16", + "task_id": "2.16", + "test_name": "2.16", + "test_type": "2.16" + } + }, + "GET /support-diagnostics/settings": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.24", + "items": "2.24", + "total_item_count": "2.24" + }, + "responseItemProperties": { + "last_updated": "2.24", + "version": "2.24" + } + }, + "GET /support/system-manifest": { + "minVersion": "2.26", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.26", + "errors": "2.26", + "items": "2.26", + "total_item_count": "2.26" + }, + "responseItemProperties": { + "context": "2.26", + "system-manifest": "2.26" + } + }, + "GET /support/test": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "component_address": "2.0", + "component_name": "2.0", + "description": "2.0", + "destination": "2.0", + "enabled": "2.0", + "resource": "2.0", + "result_details": "2.0", + "success": "2.0", + "test_type": "2.0" + } + }, + "GET /support/verification-keys": { + "minVersion": "2.7", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.7", + "items": "2.7", + "total_item_count": "2.7" + }, + "responseItemProperties": { + "key_id": "2.7", + "name": "2.7", + "verification_key": "2.7" + } + }, + "GET /syslog-servers": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "id": "2.0", + "name": "2.0", + "services": "2.14", + "sources": "2.20", + "uri": "2.0" + } + }, + "GET /syslog-servers/settings": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "ca_certificate": "2.0", + "ca_certificate_group": "2.0", + "id": "2.0", + "name": "2.0" + } + }, + "GET /syslog-servers/test": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "component_address": "2.0", + "component_name": "2.0", + "description": "2.0", + "destination": "2.0", + "enabled": "2.0", + "resource": "2.0", + "result_details": "2.0", + "success": "2.0", + "test_type": "2.0" + } + }, + "GET /targets": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "address": "2.0", + "ca_certificate_group": "2.0", + "context": "2.17", + "id": "2.0", + "name": "2.0", + "status": "2.0", + "status_details": "2.0" + } + }, + "GET /targets/performance/replication": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "items": "2.0", + "total": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "aggregate": "2.1", + "continuous": "2.1", + "id": "2.0", + "name": "2.0", + "periodic": "2.0", + "time": "2.0" + } + }, + "GET /tls-policies": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "appliance_certificate": "2.17", + "client_certificates_required": "2.18", + "disabled_tls_ciphers": "2.17", + "enabled": "2.17", + "enabled_tls_ciphers": "2.17", + "id": "2.17", + "is_local": "2.17", + "location": "2.17", + "min_tls_version": "2.17", + "name": "2.17", + "policy_type": "2.17", + "realms": "2.19", + "trusted_client_certificate_authority": "2.18", + "verify_client_certificate_trust": "2.18" + } + }, + "GET /tls-policies/members": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "member": "2.17", + "policy": "2.17" + } + }, + "GET /tls-policies/network-interfaces": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.17", + "items": "2.17", + "total_item_count": "2.17" + }, + "responseItemProperties": { + "member": "2.17", + "policy": "2.17" + } + }, + "GET /topology-groups": { + "minVersion": "2.26", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.26", + "errors": "2.26", + "items": "2.26", + "total_item_count": "2.26" + }, + "responseItemProperties": { + "context": "2.26", + "id": "2.26", + "name": "2.26", + "parent_topology_group": "2.26" + } + }, + "GET /topology-groups/arrays": { + "minVersion": "2.26", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.26", + "errors": "2.26", + "items": "2.26", + "total_item_count": "2.26" + }, + "responseItemProperties": { + "context": "2.26", + "member": "2.26", + "status": "2.26", + "status_details": "2.26", + "topology_group": "2.26" + } + }, + "GET /topology-groups/members": { + "minVersion": "2.26", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.26", + "errors": "2.26", + "items": "2.26", + "total_item_count": "2.26" + }, + "responseItemProperties": { + "context": "2.26", + "member": "2.26", + "status": "2.26", + "status_details": "2.26", + "topology_group": "2.26" + } + }, + "GET /usage/groups": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "file_system": "2.0", + "file_system_default_quota": "2.0", + "group": "2.0", + "name": "2.0", + "quota": "2.0", + "usage": "2.0" + } + }, + "GET /usage/users": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.0", + "errors": "2.17", + "items": "2.0", + "total_item_count": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "file_system": "2.0", + "file_system_default_quota": "2.0", + "name": "2.0", + "quota": "2.0", + "usage": "2.0", + "user": "2.0" + } + }, + "GET /user-group-quota-policies": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.25", + "errors": "2.25", + "items": "2.25", + "total_item_count": "2.25" + }, + "responseItemProperties": { + "context": "2.25", + "enabled": "2.25", + "id": "2.25", + "is_local": "2.25", + "location": "2.25", + "name": "2.25", + "policy_type": "2.25", + "realms": "2.25", + "rules": "2.25", + "version": "2.25" + } + }, + "GET /user-group-quota-policies/file-systems": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.25", + "errors": "2.25", + "items": "2.25", + "total_item_count": "2.25" + }, + "responseItemProperties": { + "context": "2.25", + "member": "2.25", + "policy": "2.25" + } + }, + "GET /user-group-quota-policies/members": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.25", + "errors": "2.25", + "items": "2.25", + "total_item_count": "2.25" + }, + "responseItemProperties": { + "context": "2.25", + "member": "2.25", + "policy": "2.25" + } + }, + "GET /user-group-quota-policies/rules": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.25", + "errors": "2.25", + "items": "2.25", + "total_item_count": "2.25" + }, + "responseItemProperties": { + "context": "2.25", + "enforced": "2.25", + "id": "2.25", + "index": "2.25", + "name": "2.25", + "notifications": "2.25", + "policy": "2.25", + "policy_version": "2.25", + "quota_limit": "2.25", + "quota_type": "2.25", + "subject": "2.25" + } + }, + "GET /workloads": { + "minVersion": "2.23", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.23", + "errors": "2.23", + "items": "2.23", + "total_item_count": "2.23" + }, + "responseItemProperties": { + "context": "2.23", + "created": "2.23", + "destroyed": "2.23", + "id": "2.23", + "name": "2.23", + "preset": "2.23", + "status": "2.23", + "status_details": "2.23", + "time_remaining": "2.23" + } + }, + "GET /workloads/placement-recommendations": { + "minVersion": "2.23", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.23", + "errors": "2.23", + "items": "2.23", + "total_item_count": "2.23" + }, + "responseItemProperties": { + "additional_constraints": "2.23", + "context": "2.23", + "created": "2.23", + "expires": "2.23", + "id": "2.23", + "more_results_available": "2.23", + "name": "2.23", + "parameters": "2.23", + "placement_names": "2.23", + "preset": "2.23", + "progress": "2.23", + "projection_months": "2.23", + "recommendation_engine": "2.23", + "results": "2.23", + "results_limit": "2.23", + "status": "2.23" + } + }, + "GET /workloads/tags": { + "minVersion": "2.23", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.23", + "errors": "2.23", + "items": "2.23", + "total_item_count": "2.23" + }, + "responseItemProperties": { + "context": "2.23", + "copyable": "2.23", + "key": "2.23", + "namespace": "2.23", + "resource": "2.23", + "value": "2.23" + } + }, + "GET /worm-data-policies": { + "minVersion": "2.15", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.15", + "errors": "2.17", + "items": "2.15", + "total_item_count": "2.15" + }, + "responseItemProperties": { + "context": "2.17", + "default_retention": "2.15", + "enabled": "2.15", + "id": "2.15", + "is_local": "2.15", + "location": "2.15", + "max_retention": "2.15", + "min_retention": "2.15", + "mode": "2.15", + "name": "2.15", + "policy_type": "2.15", + "realms": "2.19", + "retention_lock": "2.15" + } + }, + "GET /worm-data-policies/members": { + "minVersion": "2.15", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.15", + "errors": "2.17", + "items": "2.15", + "total_item_count": "2.15" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.15", + "policy": "2.15" + } + }, + "PATCH /active-directory": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "ca_certificate": "2.27", + "ca_certificate_group": "2.27", + "computer_name": "2.0", + "directory_servers": "2.0", + "domain": "2.0", + "encryption_types": "2.0", + "global_catalog_servers": "2.12", + "id": "2.0", + "join_ou": "2.0", + "kerberos_servers": "2.0", + "name": "2.0", + "realms": "2.19", + "server": "2.16", + "service_principal_names": "2.0" + } + }, + "PATCH /active-directory/test": { + "minVersion": "2.27", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.27", + "errors": "2.27", + "items": "2.27", + "total_item_count": "2.27" + }, + "responseItemProperties": { + "component_address": "2.27", + "component_name": "2.27", + "context": "2.27", + "description": "2.27", + "destination": "2.27", + "enabled": "2.27", + "resource": "2.27", + "result_details": "2.27", + "success": "2.27", + "test_type": "2.27" + } + }, + "PATCH /admins": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "admin_type": "2.24", + "api_token": "2.0", + "authorization_model": "2.24", + "context": "2.22", + "id": "2.0", + "is_local": "2.0", + "locked": "2.3", + "lockout_remaining": "2.3", + "management_access_policies": "2.19", + "name": "2.0", + "public_key": "2.0", + "role": "2.0" + } + }, + "PATCH /admins/settings": { + "minVersion": "2.3", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.3" + }, + "responseItemProperties": { + "lockout_duration": "2.3", + "max_login_attempts": "2.3", + "min_password_length": "2.3" + } + }, + "PATCH /alert-watchers": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "enabled": "2.0", + "id": "2.0", + "minimum_notification_severity": "2.0", + "name": "2.0" + } + }, + "PATCH /alerts": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "action": "2.0", + "code": "2.0", + "component_name": "2.0", + "component_type": "2.0", + "created": "2.0", + "description": "2.0", + "duration": "2.20", + "flagged": "2.0", + "id": "2.0", + "index": "2.0", + "knowledge_base_url": "2.0", + "name": "2.0", + "notified": "2.0", + "severity": "2.0", + "state": "2.0", + "summary": "2.0", + "updated": "2.0", + "variables": "2.0" + } + }, + "PATCH /api-clients": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "access_policies": "2.19", + "access_token_ttl_in_ms": "2.0", + "enabled": "2.0", + "id": "2.0", + "issuer": "2.0", + "key_id": "2.0", + "max_role": "2.0", + "name": "2.0", + "public_key": "2.0" + } + }, + "PATCH /array-connections": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "ca_certificate_group": "2.0", + "context": "2.17", + "encrypted": "2.0", + "id": "2.0", + "management_address": "2.0", + "os": "2.17", + "remote": "2.0", + "replication_addresses": "2.0", + "status": "2.0", + "throttle": "2.3", + "type": "2.17", + "version": "2.0" + } + }, + "PATCH /arrays": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "_as_of": "2.0", + "banner": "2.0", + "context": "2.22", + "default_inbound_tls_policy": "2.17", + "encryption": "2.8", + "eradication_config": "2.5", + "id": "2.0", + "idle_timeout": "2.0", + "name": "2.0", + "network_access_policy": "2.13", + "ntp_servers": "2.0", + "os": "2.0", + "product_type": "2.6", + "revision": "2.0", + "security_update": "2.7", + "smb_mode": "2.2", + "time_zone": "2.0", + "version": "2.0" + } + }, + "PATCH /arrays/erasures": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.18" + }, + "responseItemProperties": { + "details": "2.18", + "name": "2.18", + "sanitization_certificate": "2.18", + "status": "2.18" + } + }, + "PATCH /arrays/eula": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "agreement": "2.0", + "signature": "2.0" + } + }, + "PATCH /audit-file-systems-policies": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.14" + }, + "responseItemProperties": { + "context": "2.17", + "control_type": "2.18", + "enabled": "2.14", + "id": "2.14", + "is_local": "2.14", + "location": "2.14", + "log_targets": "2.14", + "name": "2.14", + "policy_type": "2.14", + "realms": "2.19", + "rules": "2.18" + } + }, + "PATCH /audit-object-store-policies": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "context": "2.20", + "enabled": "2.20", + "id": "2.20", + "is_local": "2.20", + "location": "2.20", + "log_targets": "2.20", + "name": "2.20", + "policy_type": "2.20", + "realms": "2.20" + } + }, + "PATCH /bucket-replica-links": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0", + "total": "2.2" + }, + "responseItemProperties": { + "cascading_enabled": "2.2", + "context": "2.17", + "direction": "2.0", + "id": "2.0", + "lag": "2.0", + "local_bucket": "2.0", + "object_backlog": "2.2", + "paused": "2.0", + "recovery_point": "2.0", + "remote": "2.0", + "remote_bucket": "2.0", + "remote_credentials": "2.0", + "status": "2.0", + "status_details": "2.0" + } + }, + "PATCH /buckets": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "account": "2.0", + "bucket_type": "2.4", + "context": "2.17", + "created": "2.0", + "destroyed": "2.0", + "eradication_config": "2.8", + "hard_limit_enabled": "2.8", + "id": "2.0", + "name": "2.0", + "object_count": "2.0", + "object_lock_config": "2.8", + "public_access_config": "2.12", + "public_status": "2.12", + "qos_policy": "2.20", + "quota_limit": "2.8", + "retention_lock": "2.8", + "space": "2.0", + "storage_class": "2.19", + "time_remaining": "2.0", + "time_remaining_status": "2.14", + "versioning": "2.0" + } + }, + "PATCH /buckets/audit-filters": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "actions": "2.20", + "bucket": "2.20", + "context": "2.20", + "name": "2.20", + "s3_prefixes": "2.20" + } + }, + "PATCH /certificates": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "certificate": "2.0", + "certificate_type": "2.0", + "common_name": "2.0", + "country": "2.0", + "email": "2.0", + "id": "2.0", + "intermediate_certificate": "2.0", + "issued_by": "2.0", + "issued_to": "2.0", + "key_algorithm": "2.20", + "key_size": "2.0", + "locality": "2.0", + "name": "2.0", + "organization": "2.0", + "organizational_unit": "2.0", + "realms": "2.19", + "state": "2.0", + "status": "2.0", + "subject_alternative_names": "2.0", + "valid_from": "2.0", + "valid_to": "2.0" + } + }, + "PATCH /data-eviction-policies": { + "minVersion": "2.21", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.21" + }, + "responseItemProperties": { + "context": "2.21", + "enabled": "2.21", + "id": "2.21", + "is_local": "2.21", + "keep_size": "2.21", + "location": "2.21", + "name": "2.21", + "policy_type": "2.21", + "realms": "2.21" + } + }, + "PATCH /directory-services": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "base_dn": "2.0", + "bind_password": "2.0", + "bind_user": "2.0", + "ca_certificate": "2.0", + "ca_certificate_group": "2.0", + "enabled": "2.0", + "id": "2.0", + "management": "2.0", + "name": "2.0", + "nfs": "2.0", + "services": "2.0", + "smb": "2.0", + "uris": "2.0" + } + }, + "PATCH /directory-services/local/directory-services": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.24" + }, + "responseItemProperties": { + "context": "2.24", + "destroyed": "2.24", + "domain": "2.24", + "id": "2.24", + "name": "2.24", + "realms": "2.24", + "server": "2.24", + "time_remaining": "2.24" + } + }, + "PATCH /directory-services/local/groups": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.24" + }, + "responseItemProperties": { + "built_in": "2.24", + "context": "2.24", + "email": "2.24", + "gid": "2.24", + "local_directory_service": "2.24", + "name": "2.24", + "realms": "2.24", + "server": "2.24", + "sid": "2.24" + } + }, + "PATCH /directory-services/local/users": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.24" + }, + "responseItemProperties": { + "built_in": "2.24", + "context": "2.24", + "email": "2.24", + "enabled": "2.24", + "local_directory_service": "2.24", + "name": "2.24", + "primary_group": "2.24", + "realms": "2.24", + "server": "2.24", + "sid": "2.24", + "uid": "2.24" + } + }, + "PATCH /directory-services/roles": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "group": "2.0", + "group_base": "2.0", + "id": "2.0", + "management_access_policies": "2.19", + "name": "2.14", + "role": "2.0" + } + }, + "PATCH /directory-services/test": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.23", + "errors": "2.23", + "items": "2.0", + "total_item_count": "2.23" + }, + "responseItemProperties": { + "component_address": "2.0", + "component_name": "2.0", + "context": "2.23", + "description": "2.0", + "destination": "2.0", + "enabled": "2.0", + "resource": "2.0", + "result_details": "2.0", + "success": "2.0", + "test_type": "2.0" + } + }, + "PATCH /dns": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "ca_certificate": "2.25", + "ca_certificate_group": "2.25", + "context": "2.25", + "domain": "2.0", + "id": "2.0", + "name": "2.0", + "nameservers": "2.0", + "realms": "2.19", + "services": "2.16", + "sources": "2.16" + } + }, + "PATCH /file-system-exports": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.16" + }, + "responseItemProperties": { + "context": "2.17", + "enabled": "2.16", + "export_name": "2.16", + "id": "2.16", + "member": "2.16", + "name": "2.16", + "policy": "2.16", + "policy_type": "2.16", + "server": "2.16", + "share_policy": "2.16", + "status": "2.16", + "workload": "2.23" + } + }, + "PATCH /file-system-replica-links": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "context": "2.17", + "direction": "2.17", + "id": "2.17", + "lag": "2.17", + "link_type": "2.17", + "local_file_system": "2.17", + "policies": "2.17", + "recovery_point": "2.17", + "remote": "2.17", + "remote_file_system": "2.17", + "status": "2.17", + "status_details": "2.17" + } + }, + "PATCH /file-system-snapshots": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "created": "2.0", + "destroyed": "2.0", + "id": "2.0", + "name": "2.0", + "owner": "2.0", + "owner_destroyed": "2.0", + "policies": "2.12", + "policy": "2.0", + "source": "2.0", + "suffix": "2.0", + "time_remaining": "2.0" + }, + "removedResponseFields": [ + { + "field": "copyable", + "location": "items", + "introducedVersion": "2.0", + "lastSeenVersion": "2.10" + } + ] + }, + "PATCH /file-systems": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "created": "2.0", + "default_group_quota": "2.0", + "default_user_quota": "2.0", + "destroyed": "2.0", + "eradication_config": "2.15", + "fast_remove_directory_enabled": "2.0", + "group_ownership": "2.13", + "hard_limit_enabled": "2.0", + "http": "2.0", + "id": "2.0", + "multi_protocol": "2.0", + "name": "2.0", + "nfs": "2.0", + "node_group": "2.18", + "promotion_status": "2.0", + "provisioned": "2.0", + "qos_policy": "2.17", + "realms": "2.19", + "requested_promotion_state": "2.0", + "smb": "2.0", + "snapshot_directory_enabled": "2.0", + "source": "2.0", + "space": "2.0", + "storage_class": "2.16", + "time_remaining": "2.0", + "workload": "2.23", + "writable": "2.0" + } + }, + "PATCH /fleets": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "id": "2.17", + "is_local": "2.17", + "name": "2.17" + } + }, + "PATCH /hardware": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "data_mac": "2.16", + "details": "2.0", + "id": "2.0", + "identify_enabled": "2.0", + "index": "2.0", + "management_mac": "2.16", + "model": "2.0", + "name": "2.0", + "part_number": "2.8", + "sensor_readings": "2.18", + "serial": "2.0", + "slot": "2.0", + "speed": "2.0", + "status": "2.0", + "temperature": "2.0", + "type": "2.0" + } + }, + "PATCH /hardware-connectors": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "connector_type": "2.0", + "id": "2.0", + "lane_speed": "2.0", + "lanes_per_port": "2.20", + "name": "2.0", + "port_count": "2.0", + "port_speed": "2.15", + "transceiver_type": "2.0" + } + }, + "PATCH /kmip": { + "minVersion": "2.1", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.1" + }, + "responseItemProperties": { + "ca_certificate": "2.1", + "ca_certificate_group": "2.1", + "id": "2.1", + "name": "2.1", + "uris": "2.1" + } + }, + "PATCH /legal-holds": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "description": "2.17", + "id": "2.17", + "name": "2.17", + "realms": "2.19" + } + }, + "PATCH /legal-holds/held-entities": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "file_system": "2.17", + "legal_hold": "2.17", + "path": "2.17", + "status": "2.17" + } + }, + "PATCH /lifecycle-rules": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "abort_incomplete_multipart_uploads_after": "2.1", + "bucket": "2.0", + "cleanup_expired_object_delete_marker": "2.1", + "context": "2.17", + "enabled": "2.0", + "id": "2.0", + "keep_current_version_for": "2.1", + "keep_current_version_until": "2.1", + "keep_previous_version_for": "2.0", + "name": "2.0", + "prefix": "2.0", + "rule_id": "2.0" + } + }, + "PATCH /link-aggregation-groups": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "id": "2.0", + "lag_speed": "2.0", + "mac_address": "2.0", + "name": "2.0", + "port_speed": "2.0", + "ports": "2.0", + "status": "2.0" + } + }, + "PATCH /log-targets/file-systems": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "file_system": "2.20", + "id": "2.20", + "keep_for": "2.20", + "keep_size": "2.20", + "name": "2.20" + } + }, + "PATCH /log-targets/object-store": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "bucket": "2.20", + "context": "2.20", + "id": "2.20", + "log_name_prefix": "2.20", + "log_rotate": "2.20", + "name": "2.20" + } + }, + "PATCH /logs-async": { + "minVersion": "2.4", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.4" + }, + "responseItemProperties": { + "available_files": "2.4", + "end_time": "2.4", + "hardware_components": "2.4", + "id": "2.4", + "last_request_time": "2.4", + "name": "2.4", + "processing": "2.4", + "progress": "2.4", + "start_time": "2.4" + } + }, + "PATCH /management-access-policies": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.19" + }, + "responseItemProperties": { + "aggregation_strategy": "2.19", + "context": "2.24", + "enabled": "2.19", + "id": "2.19", + "is_local": "2.19", + "location": "2.19", + "name": "2.19", + "policy_type": "2.19", + "realms": "2.19", + "rules": "2.19", + "version": "2.19" + } + }, + "PATCH /management-access-policies/roles/permissions": { + "minVersion": "2.26", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.26" + }, + "responseItemProperties": { + "actions": "2.26", + "context": "2.26", + "id": "2.26", + "name": "2.26", + "resource": "2.26", + "role": "2.26" + } + }, + "PATCH /management-access-policies/rules": { + "minVersion": "2.26", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.26" + }, + "responseItemProperties": { + "context": "2.26", + "id": "2.26", + "index": "2.26", + "name": "2.26", + "policy": "2.26", + "policy_version": "2.26", + "role": "2.26", + "scope": "2.26" + } + }, + "PATCH /management-authentication-policies": { + "minVersion": "2.22", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.22" + }, + "responseItemProperties": { + "context": "2.22", + "enabled": "2.22", + "id": "2.22", + "is_local": "2.22", + "location": "2.22", + "name": "2.22", + "policy_type": "2.22", + "realms": "2.22", + "ssh": "2.22" + } + }, + "PATCH /network-access-policies": { + "minVersion": "2.13", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.13" + }, + "responseItemProperties": { + "enabled": "2.13", + "id": "2.13", + "is_local": "2.13", + "location": "2.13", + "name": "2.13", + "policy_type": "2.13", + "realms": "2.19", + "rules": "2.13", + "version": "2.13" + } + }, + "PATCH /network-access-policies/rules": { + "minVersion": "2.13", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.13" + }, + "responseItemProperties": { + "client": "2.13", + "effect": "2.13", + "id": "2.13", + "index": "2.13", + "interfaces": "2.13", + "name": "2.13", + "policy": "2.13", + "policy_version": "2.13" + } + }, + "PATCH /network-interfaces": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "address": "2.0", + "attached_servers": "2.20", + "enabled": "2.0", + "gateway": "2.0", + "id": "2.0", + "mtu": "2.0", + "name": "2.0", + "netmask": "2.0", + "rdma_enabled": "2.28", + "realms": "2.19", + "services": "2.0", + "subnet": "2.0", + "type": "2.0", + "vlan": "2.0" + }, + "removedResponseFields": [ + { + "field": "server", + "location": "items", + "introducedVersion": "2.16", + "lastSeenVersion": "2.19" + } + ] + }, + "PATCH /network-interfaces/connectors": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "connector_type": "2.17", + "id": "2.17", + "lane_speed": "2.17", + "lanes_per_port": "2.20", + "name": "2.17", + "port_count": "2.17", + "port_speed": "2.17", + "transceiver_type": "2.17" + } + }, + "PATCH /nfs-export-policies": { + "minVersion": "2.3", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.3" + }, + "responseItemProperties": { + "context": "2.17", + "enabled": "2.3", + "id": "2.3", + "is_local": "2.3", + "location": "2.3", + "name": "2.3", + "policy_type": "2.3", + "realms": "2.19", + "rules": "2.3", + "version": "2.3", + "workload": "2.23" + } + }, + "PATCH /nfs-export-policies/rules": { + "minVersion": "2.3", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.3" + }, + "responseItemProperties": { + "access": "2.3", + "anongid": "2.3", + "anonuid": "2.3", + "atime": "2.3", + "client": "2.3", + "context": "2.17", + "fileid_32bit": "2.3", + "id": "2.3", + "index": "2.3", + "name": "2.3", + "permission": "2.3", + "policy": "2.3", + "policy_version": "2.3", + "required_transport_security": "2.18", + "secure": "2.3", + "security": "2.3" + } + }, + "PATCH /node-groups": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.18" + }, + "responseItemProperties": { + "id": "2.18", + "name": "2.18" + } + }, + "PATCH /nodes": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.18" + }, + "responseItemProperties": { + "capacity": "2.18", + "chassis_serial_number": "2.23", + "data_addresses": "2.18", + "details": "2.18", + "id": "2.18", + "management_address": "2.18", + "name": "2.18", + "raw_capacity": "2.18", + "serial_number": "2.18", + "status": "2.18", + "unique": "2.18" + } + }, + "PATCH /object-store-access-keys": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "access_key_id": "2.20", + "context": "2.17", + "created": "2.0", + "enabled": "2.0", + "name": "2.0", + "secret_access_key": "2.0", + "user": "2.0" + } + }, + "PATCH /object-store-access-policies": { + "minVersion": "2.2", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.2" + }, + "responseItemProperties": { + "account": "2.2", + "arn": "2.2", + "context": "2.17", + "created": "2.2", + "description": "2.2", + "enabled": "2.2", + "id": "2.2", + "is_local": "2.2", + "location": "2.2", + "name": "2.2", + "policy_type": "2.2", + "realms": "2.19", + "rules": "2.2", + "updated": "2.2" + } + }, + "PATCH /object-store-access-policies/rules": { + "minVersion": "2.2", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.2" + }, + "responseItemProperties": { + "actions": "2.2", + "conditions": "2.2", + "context": "2.17", + "effect": "2.2", + "name": "2.2", + "policy": "2.2", + "resources": "2.2" + } + }, + "PATCH /object-store-account-exports": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "context": "2.20", + "enabled": "2.20", + "id": "2.20", + "member": "2.20", + "name": "2.20", + "policy": "2.20", + "realms": "2.20", + "server": "2.20" + } + }, + "PATCH /object-store-accounts": { + "minVersion": "2.8", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.8" + }, + "responseItemProperties": { + "bucket_defaults": "2.8", + "context": "2.17", + "created": "2.8", + "hard_limit_enabled": "2.8", + "id": "2.8", + "name": "2.8", + "object_count": "2.8", + "public_access_config": "2.12", + "quota_limit": "2.8", + "realms": "2.20", + "space": "2.8" + } + }, + "PATCH /object-store-remote-credentials": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "access_key_id": "2.0", + "context": "2.17", + "id": "2.0", + "name": "2.0", + "realms": "2.20", + "remote": "2.0", + "secret_access_key": "2.0" + } + }, + "PATCH /object-store-roles": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "account": "2.17", + "context": "2.17", + "created": "2.17", + "id": "2.17", + "max_session_duration": "2.17", + "name": "2.17", + "prn": "2.17", + "trusted_entities": "2.17" + } + }, + "PATCH /object-store-roles/object-store-trust-policies/rules": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "actions": "2.17", + "conditions": "2.17", + "context": "2.17", + "effect": "2.17", + "index": "2.17", + "name": "2.17", + "policy": "2.17", + "principals": "2.17" + } + }, + "PATCH /object-store-roles/object-store-trust-policies/upload": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "context": "2.17", + "enabled": "2.17", + "id": "2.17", + "is_local": "2.17", + "location": "2.17", + "name": "2.17", + "policy_type": "2.17", + "realms": "2.19", + "role": "2.17", + "rules": "2.17" + } + }, + "PATCH /object-store-virtual-hosts": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "attached_servers": "2.20", + "context": "2.20", + "hostname": "2.20", + "id": "2.20", + "name": "2.20", + "realms": "2.20" + } + }, + "PATCH /password-policies": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.16" + }, + "responseItemProperties": { + "enabled": "2.16", + "enforce_dictionary_check": "2.16", + "enforce_username_check": "2.16", + "id": "2.16", + "is_local": "2.16", + "location": "2.16", + "lockout_duration": "2.16", + "max_login_attempts": "2.16", + "max_password_age": "2.18", + "min_character_groups": "2.16", + "min_characters_per_group": "2.16", + "min_password_age": "2.16", + "min_password_length": "2.16", + "name": "2.16", + "password_history": "2.16", + "policy_type": "2.16", + "realms": "2.19" + } + }, + "PATCH /policies": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "enabled": "2.0", + "id": "2.0", + "is_local": "2.0", + "location": "2.0", + "name": "2.0", + "policy_type": "2.2", + "realms": "2.19", + "retention_lock": "2.5", + "rules": "2.0", + "workload": "2.23" + } + }, + "PATCH /presets/workload": { + "minVersion": "2.23", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.23" + }, + "responseItemProperties": { + "context": "2.23", + "description": "2.23", + "directory_configurations": "2.23", + "export_configurations": "2.23", + "id": "2.23", + "name": "2.23", + "parameters": "2.23", + "periodic_replication_configurations": "2.23", + "placement_configurations": "2.23", + "platform_features": "2.23", + "qos_configurations": "2.23", + "quota_configurations": "2.23", + "revision": "2.23", + "snapshot_configurations": "2.23", + "volume_configurations": "2.23", + "workload_tags": "2.23", + "workload_type": "2.23" + } + }, + "PATCH /qos-policies": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "context": "2.23", + "enabled": "2.17", + "id": "2.17", + "is_local": "2.17", + "location": "2.17", + "max_total_bytes_per_sec": "2.17", + "max_total_ops_per_sec": "2.17", + "name": "2.17", + "policy_type": "2.17", + "realms": "2.19" + } + }, + "PATCH /quotas/groups": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "file_system": "2.0", + "file_system_default_quota": "2.0", + "group": "2.0", + "name": "2.0", + "quota": "2.0", + "usage": "2.0" + } + }, + "PATCH /quotas/settings": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "contact": "2.0", + "direct_notifications_enabled": "2.0", + "id": "2.0", + "name": "2.0" + } + }, + "PATCH /quotas/users": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "file_system": "2.0", + "file_system_default_quota": "2.0", + "name": "2.0", + "quota": "2.0", + "usage": "2.0", + "user": "2.0" + } + }, + "PATCH /rapid-data-locking": { + "minVersion": "2.1", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.1" + }, + "responseItemProperties": { + "enabled": "2.1", + "kmip_server": "2.1" + } + }, + "PATCH /realms": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.19" + }, + "responseItemProperties": { + "created": "2.19", + "default_inbound_tls_policy": "2.19", + "destroyed": "2.19", + "id": "2.19", + "name": "2.19", + "space": "2.19", + "time_remaining": "2.19" + } + }, + "PATCH /realms/defaults": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "context": "2.20", + "object_store": "2.20", + "realm": "2.20" + } + }, + "PATCH /remote-arrays": { + "minVersion": "2.26", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.26" + }, + "responseItemProperties": { + "api_versions": "2.26", + "fleet": "2.26", + "id": "2.26", + "is_local": "2.26", + "model": "2.26", + "name": "2.26", + "os": "2.26", + "topology_group": "2.26", + "version": "2.26" + } + }, + "PATCH /s3-export-policies": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "context": "2.20", + "enabled": "2.20", + "id": "2.20", + "is_local": "2.20", + "location": "2.20", + "name": "2.20", + "policy_type": "2.20", + "realms": "2.20", + "rules": "2.20" + } + }, + "PATCH /s3-export-policies/rules": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "actions": "2.20", + "context": "2.20", + "effect": "2.20", + "name": "2.20", + "policy": "2.20", + "resources": "2.20" + } + }, + "PATCH /servers": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.16" + }, + "responseItemProperties": { + "created": "2.16", + "directory_services": "2.16", + "dns": "2.16", + "id": "2.16", + "local_directory_service": "2.24", + "name": "2.16", + "realms": "2.19" + } + }, + "PATCH /smb-client-policies": { + "minVersion": "2.10", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.10" + }, + "responseItemProperties": { + "access_based_enumeration_enabled": "2.17", + "context": "2.17", + "enabled": "2.10", + "id": "2.10", + "is_local": "2.10", + "location": "2.10", + "name": "2.10", + "policy_type": "2.10", + "realms": "2.19", + "rules": "2.10", + "version": "2.10", + "workload": "2.23" + } + }, + "PATCH /smb-client-policies/rules": { + "minVersion": "2.10", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.10" + }, + "responseItemProperties": { + "client": "2.10", + "context": "2.17", + "encryption": "2.11", + "id": "2.10", + "index": "2.10", + "name": "2.10", + "permission": "2.10", + "policy": "2.10", + "policy_version": "2.10" + } + }, + "PATCH /smb-share-policies": { + "minVersion": "2.10", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.10" + }, + "responseItemProperties": { + "context": "2.17", + "enabled": "2.10", + "id": "2.10", + "is_local": "2.10", + "location": "2.10", + "name": "2.10", + "policy_type": "2.10", + "realms": "2.19", + "rules": "2.10", + "workload": "2.23" + } + }, + "PATCH /smb-share-policies/rules": { + "minVersion": "2.10", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.10" + }, + "responseItemProperties": { + "change": "2.10", + "context": "2.17", + "full_control": "2.10", + "id": "2.10", + "name": "2.10", + "policy": "2.10", + "principal": "2.10", + "read": "2.10" + } + }, + "PATCH /smtp-servers": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "encryption_mode": "2.15", + "id": "2.0", + "name": "2.0", + "relay_host": "2.0", + "sender_domain": "2.0" + } + }, + "PATCH /snmp-agents": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "engine_id": "2.0", + "id": "2.0", + "name": "2.0", + "v2c": "2.0", + "v3": "2.0", + "version": "2.0" + } + }, + "PATCH /snmp-managers": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "host": "2.0", + "id": "2.0", + "name": "2.0", + "notification": "2.0", + "v2c": "2.0", + "v3": "2.0", + "version": "2.0" + } + }, + "PATCH /ssh-certificate-authority-policies": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.14" + }, + "responseItemProperties": { + "context": "2.24", + "enabled": "2.14", + "id": "2.14", + "is_local": "2.14", + "location": "2.14", + "name": "2.14", + "policy_type": "2.14", + "realms": "2.19", + "signing_authority": "2.14", + "static_authorized_principals": "2.14" + } + }, + "PATCH /sso/oidc/idps": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "enabled": "2.17", + "id": "2.17", + "idp": "2.17", + "name": "2.17", + "prn": "2.17", + "services": "2.17" + } + }, + "PATCH /sso/saml2/idps": { + "minVersion": "2.15", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.15" + }, + "responseItemProperties": { + "array_url": "2.15", + "binding": "2.17", + "enabled": "2.15", + "id": "2.15", + "idp": "2.15", + "management": "2.24", + "name": "2.15", + "prn": "2.17", + "services": "2.17", + "sp": "2.15" + } + }, + "PATCH /sso/saml2/idps/test": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.16" + }, + "responseItemProperties": { + "component_address": "2.16", + "component_name": "2.16", + "description": "2.16", + "destination": "2.16", + "enabled": "2.16", + "resource": "2.16", + "result_details": "2.16", + "success": "2.16", + "test_type": "2.16" + } + }, + "PATCH /storage-class-tiering-policies": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.18" + }, + "responseItemProperties": { + "archival_rules": "2.18", + "enabled": "2.18", + "id": "2.18", + "is_local": "2.18", + "location": "2.18", + "name": "2.18", + "policy_type": "2.18", + "realms": "2.19", + "retrieval_rules": "2.18" + } + }, + "PATCH /subnets": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "enabled": "2.0", + "gateway": "2.0", + "id": "2.0", + "interfaces": "2.0", + "link_aggregation_group": "2.0", + "mtu": "2.0", + "name": "2.0", + "prefix": "2.0", + "services": "2.0", + "vlan": "2.0" + } + }, + "PATCH /support": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "edge_agent_update_enabled": "2.18", + "edge_management_enabled": "2.18", + "id": "2.0", + "name": "2.0", + "phonehome_enabled": "2.0", + "proxy": "2.0", + "remote_assist_active": "2.0", + "remote_assist_duration": "2.14", + "remote_assist_expires": "2.0", + "remote_assist_opened": "2.0", + "remote_assist_paths": "2.0", + "remote_assist_status": "2.0" + } + }, + "PATCH /support-diagnostics/settings": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.24" + }, + "responseItemProperties": { + "last_updated": "2.24", + "version": "2.24" + } + }, + "PATCH /support/verification-keys": { + "minVersion": "2.7", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.7" + }, + "responseItemProperties": { + "key_id": "2.7", + "name": "2.7", + "verification_key": "2.7" + } + }, + "PATCH /syslog-servers": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "id": "2.0", + "name": "2.0", + "services": "2.14", + "sources": "2.20", + "uri": "2.0" + } + }, + "PATCH /syslog-servers/settings": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "ca_certificate": "2.0", + "ca_certificate_group": "2.0", + "id": "2.0", + "name": "2.0" + } + }, + "PATCH /targets": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "address": "2.0", + "ca_certificate_group": "2.0", + "id": "2.0", + "name": "2.0", + "status": "2.0", + "status_details": "2.0" + } + }, + "PATCH /tls-policies": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "appliance_certificate": "2.17", + "client_certificates_required": "2.18", + "disabled_tls_ciphers": "2.17", + "enabled": "2.17", + "enabled_tls_ciphers": "2.17", + "id": "2.17", + "is_local": "2.17", + "location": "2.17", + "min_tls_version": "2.17", + "name": "2.17", + "policy_type": "2.17", + "realms": "2.19", + "trusted_client_certificate_authority": "2.18", + "verify_client_certificate_trust": "2.18" + } + }, + "PATCH /topology-groups": { + "minVersion": "2.26", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.26" + }, + "responseItemProperties": { + "context": "2.26", + "id": "2.26", + "name": "2.26", + "parent_topology_group": "2.26" + } + }, + "PATCH /user-group-quota-policies": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.25" + }, + "responseItemProperties": { + "context": "2.25", + "enabled": "2.25", + "id": "2.25", + "is_local": "2.25", + "location": "2.25", + "name": "2.25", + "policy_type": "2.25", + "realms": "2.25", + "rules": "2.25", + "version": "2.25" + } + }, + "PATCH /user-group-quota-policies/rules": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.25" + }, + "responseItemProperties": { + "context": "2.25", + "enforced": "2.25", + "id": "2.25", + "index": "2.25", + "name": "2.25", + "notifications": "2.25", + "policy": "2.25", + "policy_version": "2.25", + "quota_limit": "2.25", + "quota_type": "2.25", + "subject": "2.25" + } + }, + "PATCH /workloads": { + "minVersion": "2.23", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.23" + }, + "responseItemProperties": { + "context": "2.23", + "created": "2.23", + "destroyed": "2.23", + "id": "2.23", + "name": "2.23", + "preset": "2.23", + "status": "2.23", + "status_details": "2.23", + "time_remaining": "2.23" + } + }, + "PATCH /worm-data-policies": { + "minVersion": "2.15", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.15" + }, + "responseItemProperties": { + "context": "2.17", + "default_retention": "2.15", + "enabled": "2.15", + "id": "2.15", + "is_local": "2.15", + "location": "2.15", + "max_retention": "2.15", + "min_retention": "2.15", + "mode": "2.15", + "name": "2.15", + "policy_type": "2.15", + "realms": "2.19", + "retention_lock": "2.15" + } + }, + "POST /active-directory": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "ca_certificate": "2.27", + "ca_certificate_group": "2.27", + "computer_name": "2.0", + "directory_servers": "2.0", + "domain": "2.0", + "encryption_types": "2.0", + "global_catalog_servers": "2.12", + "id": "2.0", + "join_ou": "2.0", + "kerberos_servers": "2.0", + "name": "2.0", + "realms": "2.19", + "server": "2.16", + "service_principal_names": "2.0" + } + }, + "POST /admins": { + "minVersion": "2.15", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.15" + }, + "responseItemProperties": { + "admin_type": "2.24", + "api_token": "2.15", + "authorization_model": "2.24", + "context": "2.22", + "id": "2.15", + "is_local": "2.15", + "locked": "2.15", + "lockout_remaining": "2.15", + "management_access_policies": "2.19", + "name": "2.15", + "public_key": "2.15", + "role": "2.15" + } + }, + "POST /admins/api-tokens": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "admin": "2.0", + "api_token": "2.0", + "context": "2.24" + } + }, + "POST /admins/management-access-policies": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.19" + }, + "responseItemProperties": { + "context": "2.24", + "member": "2.19", + "policy": "2.19" + } + }, + "POST /admins/management-authentication-policies": { + "minVersion": "2.22", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.22" + }, + "responseItemProperties": { + "context": "2.22", + "member": "2.22", + "policy": "2.22" + } + }, + "POST /admins/ssh-certificate-authority-policies": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.14" + }, + "responseItemProperties": { + "context": "2.24", + "member": "2.14", + "policy": "2.14" + } + }, + "POST /alert-watchers": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "enabled": "2.0", + "id": "2.0", + "minimum_notification_severity": "2.0", + "name": "2.0" + } + }, + "POST /api-clients": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "access_policies": "2.19", + "access_token_ttl_in_ms": "2.0", + "enabled": "2.0", + "id": "2.0", + "issuer": "2.0", + "key_id": "2.0", + "max_role": "2.0", + "name": "2.0", + "public_key": "2.0" + } + }, + "POST /api/login": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "username": "2.0" + }, + "responseItemProperties": {} + }, + "POST /array-connections": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "ca_certificate_group": "2.0", + "context": "2.17", + "encrypted": "2.0", + "id": "2.0", + "management_address": "2.0", + "os": "2.17", + "remote": "2.0", + "replication_addresses": "2.0", + "status": "2.0", + "throttle": "2.3", + "type": "2.17", + "version": "2.0" + } + }, + "POST /array-connections/connection-key": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "connection_key": "2.0", + "created": "2.0", + "expires": "2.0" + } + }, + "POST /arrays/erasures": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.18" + }, + "responseItemProperties": { + "details": "2.18", + "name": "2.18", + "sanitization_certificate": "2.18", + "status": "2.18" + } + }, + "POST /arrays/factory-reset-token": { + "minVersion": "2.1", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.1" + }, + "responseItemProperties": { + "name": "2.1", + "token": "2.1" + } + }, + "POST /arrays/management-authentication-policies": { + "minVersion": "2.22", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.22" + }, + "responseItemProperties": { + "context": "2.22", + "member": "2.22", + "policy": "2.22" + } + }, + "POST /arrays/ssh-certificate-authority-policies": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.14" + }, + "responseItemProperties": { + "context": "2.24", + "member": "2.14", + "policy": "2.14" + } + }, + "POST /audit-file-systems-policies": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.14" + }, + "responseItemProperties": { + "context": "2.17", + "control_type": "2.18", + "enabled": "2.14", + "id": "2.14", + "is_local": "2.14", + "location": "2.14", + "log_targets": "2.14", + "name": "2.14", + "policy_type": "2.14", + "realms": "2.19", + "rules": "2.18" + } + }, + "POST /audit-file-systems-policies/members": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.14" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.14", + "policy": "2.14" + } + }, + "POST /audit-object-store-policies": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "context": "2.20", + "enabled": "2.20", + "id": "2.20", + "is_local": "2.20", + "location": "2.20", + "log_targets": "2.20", + "name": "2.20", + "policy_type": "2.20", + "realms": "2.20" + } + }, + "POST /audit-object-store-policies/members": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "context": "2.20", + "member": "2.20", + "policy": "2.20" + } + }, + "POST /bucket-replica-links": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0", + "total": "2.2" + }, + "responseItemProperties": { + "cascading_enabled": "2.2", + "context": "2.17", + "direction": "2.0", + "id": "2.0", + "lag": "2.0", + "local_bucket": "2.0", + "object_backlog": "2.2", + "paused": "2.0", + "recovery_point": "2.0", + "remote": "2.0", + "remote_bucket": "2.0", + "remote_credentials": "2.0", + "status": "2.0", + "status_details": "2.0" + } + }, + "POST /buckets": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "account": "2.0", + "bucket_type": "2.4", + "context": "2.17", + "created": "2.0", + "destroyed": "2.0", + "eradication_config": "2.8", + "hard_limit_enabled": "2.8", + "id": "2.0", + "name": "2.0", + "object_count": "2.0", + "object_lock_config": "2.8", + "public_access_config": "2.12", + "public_status": "2.12", + "qos_policy": "2.20", + "quota_limit": "2.8", + "retention_lock": "2.8", + "space": "2.0", + "storage_class": "2.19", + "time_remaining": "2.0", + "time_remaining_status": "2.14", + "versioning": "2.0" + } + }, + "POST /buckets/audit-filters": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "actions": "2.20", + "bucket": "2.20", + "context": "2.20", + "name": "2.20", + "s3_prefixes": "2.20" + } + }, + "POST /buckets/bucket-access-policies": { + "minVersion": "2.12", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.12" + }, + "responseItemProperties": { + "bucket": "2.12", + "context": "2.17", + "enabled": "2.12", + "id": "2.12", + "is_local": "2.12", + "location": "2.12", + "name": "2.12", + "policy_type": "2.12", + "realms": "2.19", + "rules": "2.12" + } + }, + "POST /buckets/bucket-access-policies/rules": { + "minVersion": "2.12", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.12" + }, + "responseItemProperties": { + "actions": "2.12", + "context": "2.17", + "effect": "2.12", + "name": "2.12", + "policy": "2.12", + "principals": "2.12", + "resources": "2.12" + } + }, + "POST /buckets/cross-origin-resource-sharing-policies": { + "minVersion": "2.12", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.12" + }, + "responseItemProperties": { + "bucket": "2.12", + "context": "2.17", + "enabled": "2.12", + "id": "2.12", + "is_local": "2.12", + "location": "2.12", + "name": "2.12", + "policy_type": "2.12", + "realms": "2.19", + "rules": "2.12" + } + }, + "POST /buckets/cross-origin-resource-sharing-policies/rules": { + "minVersion": "2.12", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.12" + }, + "responseItemProperties": { + "allowed_headers": "2.12", + "allowed_methods": "2.12", + "allowed_origins": "2.12", + "context": "2.17", + "name": "2.12", + "policy": "2.12" + } + }, + "POST /certificate-groups": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "id": "2.0", + "name": "2.0", + "realms": "2.19" + } + }, + "POST /certificate-groups/certificates": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "group": "2.0", + "member": "2.0" + } + }, + "POST /certificates": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "certificate": "2.0", + "certificate_type": "2.0", + "common_name": "2.0", + "country": "2.0", + "email": "2.0", + "id": "2.0", + "intermediate_certificate": "2.0", + "issued_by": "2.0", + "issued_to": "2.0", + "key_algorithm": "2.20", + "key_size": "2.0", + "locality": "2.0", + "name": "2.0", + "organization": "2.0", + "organizational_unit": "2.0", + "realms": "2.19", + "state": "2.0", + "status": "2.0", + "subject_alternative_names": "2.0", + "valid_from": "2.0", + "valid_to": "2.0" + } + }, + "POST /certificates/certificate-groups": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "group": "2.0", + "member": "2.0" + } + }, + "POST /certificates/certificate-signing-requests": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "certificate_signing_request": "2.20" + } + }, + "POST /data-eviction-policies": { + "minVersion": "2.21", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.21" + }, + "responseItemProperties": { + "context": "2.21", + "enabled": "2.21", + "id": "2.21", + "is_local": "2.21", + "keep_size": "2.21", + "location": "2.21", + "name": "2.21", + "policy_type": "2.21", + "realms": "2.21" + } + }, + "POST /data-eviction-policies/file-systems": { + "minVersion": "2.21", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.21" + }, + "responseItemProperties": { + "context": "2.21", + "member": "2.21", + "policy": "2.21" + } + }, + "POST /directory-services/local/directory-services": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.24" + }, + "responseItemProperties": { + "context": "2.24", + "destroyed": "2.24", + "domain": "2.24", + "id": "2.24", + "name": "2.24", + "realms": "2.24", + "server": "2.24", + "time_remaining": "2.24" + } + }, + "POST /directory-services/local/groups": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.24" + }, + "responseItemProperties": { + "built_in": "2.24", + "context": "2.24", + "email": "2.24", + "gid": "2.24", + "local_directory_service": "2.24", + "name": "2.24", + "realms": "2.24", + "server": "2.24", + "sid": "2.24" + } + }, + "POST /directory-services/local/groups/members": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.24" + }, + "responseItemProperties": { + "context": "2.24", + "group": "2.24", + "group_gid": "2.24", + "is_primary_group": "2.24", + "local_directory_service": "2.24", + "member": "2.24", + "member_id": "2.24", + "realms": "2.24", + "server": "2.24" + } + }, + "POST /directory-services/local/users": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.24" + }, + "responseItemProperties": { + "built_in": "2.24", + "context": "2.24", + "email": "2.24", + "enabled": "2.24", + "local_directory_service": "2.24", + "name": "2.24", + "primary_group": "2.24", + "realms": "2.24", + "server": "2.24", + "sid": "2.24", + "uid": "2.24" + } + }, + "POST /directory-services/local/users/members": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.24" + }, + "responseItemProperties": { + "context": "2.24", + "group": "2.24", + "group_gid": "2.24", + "is_primary_group": "2.24", + "local_directory_service": "2.24", + "member": "2.24", + "member_id": "2.24", + "realms": "2.24", + "server": "2.24" + } + }, + "POST /directory-services/roles": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.14" + }, + "responseItemProperties": { + "group": "2.14", + "group_base": "2.14", + "id": "2.14", + "management_access_policies": "2.19", + "name": "2.14", + "role": "2.14" + } + }, + "POST /directory-services/roles/management-access-policies": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.19" + }, + "responseItemProperties": { + "member": "2.19", + "policy": "2.19" + } + }, + "POST /dns": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.16" + }, + "responseItemProperties": { + "ca_certificate": "2.25", + "ca_certificate_group": "2.25", + "context": "2.25", + "domain": "2.16", + "id": "2.16", + "name": "2.16", + "nameservers": "2.16", + "realms": "2.19", + "services": "2.16", + "sources": "2.16" + } + }, + "POST /file-system-exports": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.16" + }, + "responseItemProperties": { + "context": "2.17", + "enabled": "2.16", + "export_name": "2.16", + "id": "2.16", + "member": "2.16", + "name": "2.16", + "policy": "2.16", + "policy_type": "2.16", + "server": "2.16", + "share_policy": "2.16", + "status": "2.16", + "workload": "2.23" + } + }, + "POST /file-system-junctions": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.25" + }, + "responseItemProperties": { + "context": "2.25", + "destination_file_system": "2.25", + "id": "2.25", + "junction_origin_path": "2.25", + "name": "2.25", + "origin_file_system": "2.25", + "status": "2.25" + } + }, + "POST /file-system-replica-links": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "direction": "2.0", + "id": "2.0", + "lag": "2.0", + "link_type": "2.17", + "local_file_system": "2.0", + "policies": "2.0", + "recovery_point": "2.0", + "remote": "2.0", + "remote_file_system": "2.0", + "status": "2.0", + "status_details": "2.0" + } + }, + "POST /file-system-replica-links/policies": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "context": "2.22", + "items": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "link": "2.0", + "member": "2.0", + "policy": "2.0" + } + }, + "POST /file-system-snapshots": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "created": "2.0", + "destroyed": "2.0", + "id": "2.0", + "name": "2.0", + "owner": "2.0", + "owner_destroyed": "2.0", + "policies": "2.12", + "policy": "2.0", + "source": "2.0", + "suffix": "2.0", + "time_remaining": "2.0" + }, + "removedResponseFields": [ + { + "field": "copyable", + "location": "items", + "introducedVersion": "2.0", + "lastSeenVersion": "2.10" + } + ] + }, + "POST /file-systems": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "created": "2.0", + "default_group_quota": "2.0", + "default_user_quota": "2.0", + "destroyed": "2.0", + "eradication_config": "2.15", + "fast_remove_directory_enabled": "2.0", + "group_ownership": "2.13", + "hard_limit_enabled": "2.0", + "http": "2.0", + "id": "2.0", + "multi_protocol": "2.0", + "name": "2.0", + "nfs": "2.0", + "node_group": "2.18", + "promotion_status": "2.0", + "provisioned": "2.0", + "qos_policy": "2.17", + "realms": "2.19", + "requested_promotion_state": "2.0", + "smb": "2.0", + "snapshot_directory_enabled": "2.0", + "source": "2.0", + "space": "2.0", + "storage_class": "2.16", + "time_remaining": "2.0", + "workload": "2.23", + "writable": "2.0" + } + }, + "POST /file-systems/audit-policies": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.14" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.14", + "policy": "2.14" + } + }, + "POST /file-systems/data-eviction-policies": { + "minVersion": "2.21", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.21" + }, + "responseItemProperties": { + "context": "2.21", + "member": "2.21", + "policy": "2.21" + } + }, + "POST /file-systems/locks/nlm-reclamations": { + "minVersion": "2.8", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.8" + }, + "responseItemProperties": { + "context": "2.22", + "end": "2.8", + "start": "2.8" + } + }, + "POST /file-systems/policies": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.0", + "policy": "2.0" + } + }, + "POST /file-systems/user-group-quota-policies": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.25" + }, + "responseItemProperties": { + "context": "2.25", + "member": "2.25", + "policy": "2.25" + } + }, + "POST /fleets": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "id": "2.17", + "is_local": "2.17", + "name": "2.17" + } + }, + "POST /fleets/fleet-key": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "created": "2.17", + "expires": "2.17", + "fleet_key": "2.17" + } + }, + "POST /fleets/members": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "coordinator_of": "2.20", + "fleet": "2.17", + "member": "2.17", + "status": "2.17", + "status_details": "2.17" + } + }, + "POST /fleets/members/batch": { + "minVersion": "2.27", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.27" + }, + "responseItemProperties": { + "certificate_details": "2.27", + "management_address": "2.27", + "member": "2.27", + "status": "2.27", + "status_details": "2.27" + } + }, + "POST /keytabs": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "encryption_type": "2.0", + "fqdn": "2.0", + "id": "2.0", + "kvno": "2.0", + "name": "2.0", + "prefix": "2.0", + "principal": "2.0", + "realm": "2.0", + "server": "2.16", + "source": "2.0", + "suffix": "2.0" + } + }, + "POST /keytabs/upload": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": {}, + "responseItemProperties": {} + }, + "POST /kmip": { + "minVersion": "2.1", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.1" + }, + "responseItemProperties": { + "ca_certificate": "2.1", + "ca_certificate_group": "2.1", + "id": "2.1", + "name": "2.1", + "uris": "2.1" + } + }, + "POST /legal-holds": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "description": "2.17", + "id": "2.17", + "name": "2.17", + "realms": "2.19" + } + }, + "POST /legal-holds/held-entities": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "file_system": "2.17", + "legal_hold": "2.17", + "path": "2.17", + "status": "2.17" + } + }, + "POST /lifecycle-rules": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "abort_incomplete_multipart_uploads_after": "2.1", + "bucket": "2.0", + "cleanup_expired_object_delete_marker": "2.1", + "context": "2.17", + "enabled": "2.0", + "id": "2.0", + "keep_current_version_for": "2.1", + "keep_current_version_until": "2.1", + "keep_previous_version_for": "2.0", + "name": "2.0", + "prefix": "2.0", + "rule_id": "2.0" + } + }, + "POST /link-aggregation-groups": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "id": "2.0", + "lag_speed": "2.0", + "mac_address": "2.0", + "name": "2.0", + "port_speed": "2.0", + "ports": "2.0", + "status": "2.0" + } + }, + "POST /log-targets/file-systems": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "file_system": "2.20", + "id": "2.20", + "keep_for": "2.20", + "keep_size": "2.20", + "name": "2.20" + } + }, + "POST /log-targets/object-store": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "bucket": "2.20", + "context": "2.20", + "id": "2.20", + "log_name_prefix": "2.20", + "log_rotate": "2.20", + "name": "2.20" + } + }, + "POST /maintenance-windows": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.16" + }, + "responseItemProperties": { + "created": "2.16", + "expires": "2.16", + "id": "2.16", + "name": "2.16" + } + }, + "POST /management-access-policies": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.19" + }, + "responseItemProperties": { + "aggregation_strategy": "2.19", + "context": "2.24", + "enabled": "2.19", + "id": "2.19", + "is_local": "2.19", + "location": "2.19", + "name": "2.19", + "policy_type": "2.19", + "realms": "2.19", + "rules": "2.19", + "version": "2.19" + } + }, + "POST /management-access-policies/admins": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.19" + }, + "responseItemProperties": { + "context": "2.24", + "member": "2.19", + "policy": "2.19" + } + }, + "POST /management-access-policies/directory-services/roles": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.19" + }, + "responseItemProperties": { + "member": "2.19", + "policy": "2.19" + } + }, + "POST /management-access-policies/roles": { + "minVersion": "2.26", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.26" + }, + "responseItemProperties": { + "context": "2.26", + "description": "2.26", + "id": "2.26", + "name": "2.26", + "pure_defined": "2.26" + } + }, + "POST /management-access-policies/roles/permissions": { + "minVersion": "2.26", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.26" + }, + "responseItemProperties": { + "actions": "2.26", + "context": "2.26", + "id": "2.26", + "name": "2.26", + "resource": "2.26", + "role": "2.26" + } + }, + "POST /management-access-policies/rules": { + "minVersion": "2.26", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.26" + }, + "responseItemProperties": { + "context": "2.26", + "id": "2.26", + "index": "2.26", + "name": "2.26", + "policy": "2.26", + "policy_version": "2.26", + "role": "2.26", + "scope": "2.26" + } + }, + "POST /management-authentication-policies": { + "minVersion": "2.22", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.22" + }, + "responseItemProperties": { + "context": "2.22", + "enabled": "2.22", + "id": "2.22", + "is_local": "2.22", + "location": "2.22", + "name": "2.22", + "policy_type": "2.22", + "realms": "2.22", + "ssh": "2.22" + } + }, + "POST /management-authentication-policies/members": { + "minVersion": "2.22", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.22" + }, + "responseItemProperties": { + "context": "2.22", + "member": "2.22", + "policy": "2.22" + } + }, + "POST /network-access-policies/rules": { + "minVersion": "2.13", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.13" + }, + "responseItemProperties": { + "client": "2.13", + "effect": "2.13", + "id": "2.13", + "index": "2.13", + "interfaces": "2.13", + "name": "2.13", + "policy": "2.13", + "policy_version": "2.13" + } + }, + "POST /network-interfaces": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "address": "2.0", + "attached_servers": "2.20", + "enabled": "2.0", + "gateway": "2.0", + "id": "2.0", + "mtu": "2.0", + "name": "2.0", + "netmask": "2.0", + "rdma_enabled": "2.28", + "realms": "2.19", + "services": "2.0", + "subnet": "2.0", + "type": "2.0", + "vlan": "2.0" + }, + "removedResponseFields": [ + { + "field": "server", + "location": "items", + "introducedVersion": "2.16", + "lastSeenVersion": "2.19" + } + ] + }, + "POST /network-interfaces/tls-policies": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "member": "2.17", + "policy": "2.17" + } + }, + "POST /nfs-export-policies": { + "minVersion": "2.3", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.3" + }, + "responseItemProperties": { + "context": "2.17", + "enabled": "2.3", + "id": "2.3", + "is_local": "2.3", + "location": "2.3", + "name": "2.3", + "policy_type": "2.3", + "realms": "2.19", + "rules": "2.3", + "version": "2.3", + "workload": "2.23" + } + }, + "POST /nfs-export-policies/rules": { + "minVersion": "2.3", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.3" + }, + "responseItemProperties": { + "access": "2.3", + "anongid": "2.3", + "anonuid": "2.3", + "atime": "2.3", + "client": "2.3", + "context": "2.17", + "fileid_32bit": "2.3", + "id": "2.3", + "index": "2.3", + "name": "2.3", + "permission": "2.3", + "policy": "2.3", + "policy_version": "2.3", + "required_transport_security": "2.18", + "secure": "2.3", + "security": "2.3" + } + }, + "POST /node-groups": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.18" + }, + "responseItemProperties": { + "id": "2.18", + "name": "2.18" + } + }, + "POST /node-groups/nodes": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.18" + }, + "responseItemProperties": { + "group": "2.18", + "member": "2.18" + } + }, + "POST /nodes/batch": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.18" + }, + "responseItemProperties": { + "capacity": "2.18", + "chassis_serial_number": "2.23", + "data_addresses": "2.18", + "details": "2.18", + "id": "2.18", + "management_address": "2.18", + "name": "2.18", + "raw_capacity": "2.18", + "serial_number": "2.18", + "status": "2.18", + "unique": "2.18" + } + }, + "POST /oauth2/1.0/token": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "access_token": "2.0", + "expires_in": "2.0", + "issued_token_type": "2.0", + "token_type": "2.0" + }, + "responseItemProperties": {} + }, + "POST /object-store-access-keys": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "access_key_id": "2.20", + "context": "2.17", + "created": "2.0", + "enabled": "2.0", + "name": "2.0", + "secret_access_key": "2.0", + "user": "2.0" + } + }, + "POST /object-store-access-policies": { + "minVersion": "2.2", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.2" + }, + "responseItemProperties": { + "account": "2.2", + "arn": "2.2", + "context": "2.17", + "created": "2.2", + "description": "2.2", + "enabled": "2.2", + "id": "2.2", + "is_local": "2.2", + "location": "2.2", + "name": "2.2", + "policy_type": "2.2", + "realms": "2.19", + "rules": "2.2", + "updated": "2.2" + } + }, + "POST /object-store-access-policies/object-store-roles": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.17", + "policy": "2.17" + } + }, + "POST /object-store-access-policies/object-store-users": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.0", + "policy": "2.0" + } + }, + "POST /object-store-access-policies/rules": { + "minVersion": "2.2", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.2" + }, + "responseItemProperties": { + "actions": "2.2", + "conditions": "2.2", + "context": "2.17", + "effect": "2.2", + "name": "2.2", + "policy": "2.2", + "resources": "2.2" + } + }, + "POST /object-store-account-exports": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "context": "2.20", + "enabled": "2.20", + "id": "2.20", + "member": "2.20", + "name": "2.20", + "policy": "2.20", + "realms": "2.20", + "server": "2.20" + } + }, + "POST /object-store-accounts": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "bucket_defaults": "2.8", + "context": "2.17", + "created": "2.0", + "hard_limit_enabled": "2.8", + "id": "2.0", + "name": "2.0", + "object_count": "2.0", + "public_access_config": "2.12", + "quota_limit": "2.8", + "realms": "2.20", + "space": "2.0" + } + }, + "POST /object-store-remote-credentials": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "access_key_id": "2.0", + "context": "2.17", + "id": "2.0", + "name": "2.0", + "realms": "2.20", + "remote": "2.0", + "secret_access_key": "2.0" + } + }, + "POST /object-store-roles": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "account": "2.17", + "context": "2.17", + "created": "2.17", + "id": "2.17", + "max_session_duration": "2.17", + "name": "2.17", + "prn": "2.17", + "trusted_entities": "2.17" + } + }, + "POST /object-store-roles/object-store-access-policies": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.17", + "policy": "2.17" + } + }, + "POST /object-store-roles/object-store-trust-policies/rules": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "actions": "2.17", + "conditions": "2.17", + "context": "2.17", + "effect": "2.17", + "index": "2.17", + "name": "2.17", + "policy": "2.17", + "principals": "2.17" + } + }, + "POST /object-store-users": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "access_keys": "2.0", + "account": "2.0", + "context": "2.17", + "created": "2.0", + "id": "2.0", + "name": "2.0" + } + }, + "POST /object-store-users/object-store-access-policies": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.0", + "policy": "2.0" + } + }, + "POST /object-store-virtual-hosts": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "attached_servers": "2.20", + "context": "2.17", + "hostname": "2.20", + "id": "2.0", + "name": "2.0", + "realms": "2.20" + } + }, + "POST /policies": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "enabled": "2.0", + "id": "2.0", + "is_local": "2.0", + "location": "2.0", + "name": "2.0", + "policy_type": "2.2", + "realms": "2.19", + "retention_lock": "2.5", + "rules": "2.0", + "workload": "2.23" + } + }, + "POST /policies/file-system-replica-links": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "context": "2.22", + "items": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "link": "2.0", + "member": "2.0", + "policy": "2.0" + } + }, + "POST /policies/file-systems": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "member": "2.0", + "policy": "2.0" + } + }, + "POST /presets/workload": { + "minVersion": "2.23", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.23" + }, + "responseItemProperties": { + "context": "2.23", + "description": "2.23", + "directory_configurations": "2.23", + "export_configurations": "2.23", + "id": "2.23", + "name": "2.23", + "parameters": "2.23", + "periodic_replication_configurations": "2.23", + "placement_configurations": "2.23", + "platform_features": "2.23", + "qos_configurations": "2.23", + "quota_configurations": "2.23", + "revision": "2.23", + "snapshot_configurations": "2.23", + "volume_configurations": "2.23", + "workload_tags": "2.23", + "workload_type": "2.23" + } + }, + "POST /public-keys": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.14" + }, + "responseItemProperties": { + "algorithm": "2.14", + "id": "2.14", + "key_size": "2.14", + "name": "2.14", + "public_key": "2.14" + } + }, + "POST /qos-policies": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "context": "2.23", + "enabled": "2.17", + "id": "2.17", + "is_local": "2.17", + "location": "2.17", + "max_total_bytes_per_sec": "2.17", + "max_total_ops_per_sec": "2.17", + "name": "2.17", + "policy_type": "2.17", + "realms": "2.19" + } + }, + "POST /qos-policies/members": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "context": "2.23", + "member": "2.20", + "policy": "2.20" + } + }, + "POST /quotas/groups": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "file_system": "2.0", + "file_system_default_quota": "2.0", + "group": "2.0", + "name": "2.0", + "quota": "2.0", + "usage": "2.0" + } + }, + "POST /quotas/users": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "context": "2.17", + "file_system": "2.0", + "file_system_default_quota": "2.0", + "name": "2.0", + "quota": "2.0", + "usage": "2.0", + "user": "2.0" + } + }, + "POST /realm-connections": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.25" + }, + "responseItemProperties": { + "id": "2.25", + "local_realm": "2.25", + "remote_realm": "2.25", + "status": "2.25" + } + }, + "POST /realm-connections/connection-key": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.25" + }, + "responseItemProperties": { + "connection_key": "2.25", + "created": "2.25", + "expires": "2.25", + "realm": "2.25" + } + }, + "POST /realms": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.19" + }, + "responseItemProperties": { + "created": "2.19", + "default_inbound_tls_policy": "2.19", + "destroyed": "2.19", + "id": "2.19", + "name": "2.19", + "space": "2.19", + "time_remaining": "2.19" + } + }, + "POST /resource-accesses/batch": { + "minVersion": "2.19", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.19" + }, + "responseItemProperties": { + "id": "2.19", + "resource": "2.19", + "scope": "2.19" + } + }, + "POST /s3-export-policies": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "context": "2.20", + "enabled": "2.20", + "id": "2.20", + "is_local": "2.20", + "location": "2.20", + "name": "2.20", + "policy_type": "2.20", + "realms": "2.20", + "rules": "2.20" + } + }, + "POST /s3-export-policies/rules": { + "minVersion": "2.20", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.20" + }, + "responseItemProperties": { + "actions": "2.20", + "context": "2.20", + "effect": "2.20", + "name": "2.20", + "policy": "2.20", + "resources": "2.20" + } + }, + "POST /servers": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.16" + }, + "responseItemProperties": { + "created": "2.16", + "directory_services": "2.16", + "dns": "2.16", + "id": "2.16", + "local_directory_service": "2.24", + "name": "2.16", + "realms": "2.19" + } + }, + "POST /smb-client-policies": { + "minVersion": "2.10", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.10" + }, + "responseItemProperties": { + "access_based_enumeration_enabled": "2.17", + "context": "2.17", + "enabled": "2.10", + "id": "2.10", + "is_local": "2.10", + "location": "2.10", + "name": "2.10", + "policy_type": "2.10", + "realms": "2.19", + "rules": "2.10", + "version": "2.10", + "workload": "2.23" + } + }, + "POST /smb-client-policies/rules": { + "minVersion": "2.10", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.10" + }, + "responseItemProperties": { + "client": "2.10", + "context": "2.17", + "encryption": "2.11", + "id": "2.10", + "index": "2.10", + "name": "2.10", + "permission": "2.10", + "policy": "2.10", + "policy_version": "2.10" + } + }, + "POST /smb-share-policies": { + "minVersion": "2.10", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.10" + }, + "responseItemProperties": { + "context": "2.17", + "enabled": "2.10", + "id": "2.10", + "is_local": "2.10", + "location": "2.10", + "name": "2.10", + "policy_type": "2.10", + "realms": "2.19", + "rules": "2.10", + "workload": "2.23" + } + }, + "POST /smb-share-policies/rules": { + "minVersion": "2.10", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.10" + }, + "responseItemProperties": { + "change": "2.10", + "context": "2.17", + "full_control": "2.10", + "id": "2.10", + "name": "2.10", + "policy": "2.10", + "principal": "2.10", + "read": "2.10" + } + }, + "POST /snmp-managers": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "host": "2.0", + "id": "2.0", + "name": "2.0", + "notification": "2.0", + "v2c": "2.0", + "v3": "2.0", + "version": "2.0" + } + }, + "POST /software-bundle": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.24" + }, + "responseItemProperties": { + "created": "2.24", + "details": "2.24", + "download_progress": "2.24", + "id": "2.24", + "source": "2.24", + "status": "2.24" + } + }, + "POST /software-check": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.16" + }, + "responseItemProperties": { + "checks": "2.16", + "details": "2.16", + "end_time": "2.16", + "id": "2.16", + "name": "2.16", + "software_name": "2.16", + "software_upgrade_hops": "2.16", + "software_version": "2.16", + "start_time": "2.16", + "status": "2.16" + } + }, + "POST /software-patches": { + "minVersion": "2.24", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.24" + }, + "responseItemProperties": { + "alert_code": "2.24", + "description": "2.24", + "details": "2.24", + "ha_reduction_required": "2.24", + "id": "2.24", + "name": "2.24", + "progress": "2.24", + "status": "2.24" + } + }, + "POST /ssh-certificate-authority-policies": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.14" + }, + "responseItemProperties": { + "context": "2.24", + "enabled": "2.14", + "id": "2.14", + "is_local": "2.14", + "location": "2.14", + "name": "2.14", + "policy_type": "2.14", + "realms": "2.19", + "signing_authority": "2.14", + "static_authorized_principals": "2.14" + } + }, + "POST /ssh-certificate-authority-policies/admins": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.14" + }, + "responseItemProperties": { + "context": "2.24", + "member": "2.14", + "policy": "2.14" + } + }, + "POST /ssh-certificate-authority-policies/arrays": { + "minVersion": "2.14", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.14" + }, + "responseItemProperties": { + "context": "2.24", + "member": "2.14", + "policy": "2.14" + } + }, + "POST /sso/oidc/idps": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "enabled": "2.17", + "id": "2.17", + "idp": "2.17", + "name": "2.17", + "prn": "2.17", + "services": "2.17" + } + }, + "POST /sso/saml2/idps": { + "minVersion": "2.15", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.15" + }, + "responseItemProperties": { + "array_url": "2.15", + "binding": "2.17", + "enabled": "2.15", + "id": "2.15", + "idp": "2.15", + "management": "2.24", + "name": "2.15", + "prn": "2.17", + "services": "2.17", + "sp": "2.15" + } + }, + "POST /storage-class-tiering-policies": { + "minVersion": "2.18", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.18" + }, + "responseItemProperties": { + "archival_rules": "2.18", + "enabled": "2.18", + "id": "2.18", + "is_local": "2.18", + "location": "2.18", + "name": "2.18", + "policy_type": "2.18", + "realms": "2.19", + "retrieval_rules": "2.18" + } + }, + "POST /subnets": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "enabled": "2.0", + "gateway": "2.0", + "id": "2.0", + "interfaces": "2.0", + "link_aggregation_group": "2.0", + "mtu": "2.0", + "name": "2.0", + "prefix": "2.0", + "services": "2.0", + "vlan": "2.0" + } + }, + "POST /support-diagnostics": { + "minVersion": "2.16", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "continuation_token": "2.16", + "items": "2.16", + "total_item_count": "2.16" + }, + "responseItemProperties": { + "analysis_period": "2.16", + "id": "2.16", + "index": "2.16", + "name": "2.16", + "severity_count": "2.16", + "start_time": "2.16", + "status": "2.16", + "task_id": "2.16", + "version": "2.16" + } + }, + "POST /syslog-servers": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "id": "2.0", + "name": "2.0", + "services": "2.14", + "sources": "2.20", + "uri": "2.0" + } + }, + "POST /targets": { + "minVersion": "2.0", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.0" + }, + "responseItemProperties": { + "address": "2.0", + "ca_certificate_group": "2.0", + "id": "2.0", + "name": "2.0", + "status": "2.0", + "status_details": "2.0" + } + }, + "POST /tls-policies": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "appliance_certificate": "2.17", + "client_certificates_required": "2.18", + "disabled_tls_ciphers": "2.17", + "enabled": "2.17", + "enabled_tls_ciphers": "2.17", + "id": "2.17", + "is_local": "2.17", + "location": "2.17", + "min_tls_version": "2.17", + "name": "2.17", + "policy_type": "2.17", + "realms": "2.19", + "trusted_client_certificate_authority": "2.18", + "verify_client_certificate_trust": "2.18" + } + }, + "POST /tls-policies/network-interfaces": { + "minVersion": "2.17", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.17" + }, + "responseItemProperties": { + "member": "2.17", + "policy": "2.17" + } + }, + "POST /topology-groups": { + "minVersion": "2.26", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.26" + }, + "responseItemProperties": { + "context": "2.26", + "id": "2.26", + "name": "2.26", + "parent_topology_group": "2.26" + } + }, + "POST /topology-groups/members": { + "minVersion": "2.26", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.26" + }, + "responseItemProperties": { + "context": "2.26", + "member": "2.26", + "status": "2.26", + "status_details": "2.26", + "topology_group": "2.26" + } + }, + "POST /user-group-quota-policies": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.25" + }, + "responseItemProperties": { + "context": "2.25", + "enabled": "2.25", + "id": "2.25", + "is_local": "2.25", + "location": "2.25", + "name": "2.25", + "policy_type": "2.25", + "realms": "2.25", + "rules": "2.25", + "version": "2.25" + } + }, + "POST /user-group-quota-policies/file-systems": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.25" + }, + "responseItemProperties": { + "context": "2.25", + "member": "2.25", + "policy": "2.25" + } + }, + "POST /user-group-quota-policies/rules": { + "minVersion": "2.25", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.25" + }, + "responseItemProperties": { + "context": "2.25", + "enforced": "2.25", + "id": "2.25", + "index": "2.25", + "name": "2.25", + "notifications": "2.25", + "policy": "2.25", + "policy_version": "2.25", + "quota_limit": "2.25", + "quota_type": "2.25", + "subject": "2.25" + } + }, + "POST /workloads": { + "minVersion": "2.23", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.23" + }, + "responseItemProperties": { + "context": "2.23", + "created": "2.23", + "destroyed": "2.23", + "id": "2.23", + "name": "2.23", + "preset": "2.23", + "status": "2.23", + "status_details": "2.23", + "time_remaining": "2.23" + } + }, + "POST /workloads/placement-recommendations": { + "minVersion": "2.23", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.23" + }, + "responseItemProperties": { + "additional_constraints": "2.23", + "context": "2.23", + "created": "2.23", + "expires": "2.23", + "id": "2.23", + "more_results_available": "2.23", + "name": "2.23", + "parameters": "2.23", + "placement_names": "2.23", + "preset": "2.23", + "progress": "2.23", + "projection_months": "2.23", + "recommendation_engine": "2.23", + "results": "2.23", + "results_limit": "2.23", + "status": "2.23" + } + }, + "POST /worm-data-policies": { + "minVersion": "2.15", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.15" + }, + "responseItemProperties": { + "context": "2.17", + "default_retention": "2.15", + "enabled": "2.15", + "id": "2.15", + "is_local": "2.15", + "location": "2.15", + "max_retention": "2.15", + "min_retention": "2.15", + "mode": "2.15", + "name": "2.15", + "policy_type": "2.15", + "realms": "2.19", + "retention_lock": "2.15" + } + }, + "PUT /presets/workload": { + "minVersion": "2.23", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.23" + }, + "responseItemProperties": { + "context": "2.23", + "description": "2.23", + "directory_configurations": "2.23", + "export_configurations": "2.23", + "id": "2.23", + "name": "2.23", + "parameters": "2.23", + "periodic_replication_configurations": "2.23", + "placement_configurations": "2.23", + "platform_features": "2.23", + "qos_configurations": "2.23", + "quota_configurations": "2.23", + "revision": "2.23", + "snapshot_configurations": "2.23", + "volume_configurations": "2.23", + "workload_tags": "2.23", + "workload_type": "2.23" + } + }, + "PUT /workloads/tags/batch": { + "minVersion": "2.23", + "lastSeenVersion": "2.28", + "responseEnvelope": { + "items": "2.23" + }, + "responseItemProperties": { + "context": "2.23", + "copyable": "2.23", + "key": "2.23", + "namespace": "2.23", + "resource": "2.23", + "value": "2.23" + } + } + } +} diff --git a/Data/PfbVersionMap.json b/Data/PfbVersionMap.json new file mode 100644 index 0000000..68d0840 --- /dev/null +++ b/Data/PfbVersionMap.json @@ -0,0 +1,89 @@ +{ + "2.0": { + "purity": "3.2.0" + }, + "2.1": { + "purity": "3.2.3" + }, + "2.2": { + "purity": "3.2.4" + }, + "2.3": { + "purity": "3.3.0" + }, + "2.4": { + "purity": "3.3.3/4.0.0" + }, + "2.5": { + "purity": "4.0.2" + }, + "2.6": { + "purity": "4.0.2" + }, + "2.7": { + "purity": "4.0.4" + }, + "2.8": { + "purity": "4.1.0" + }, + "2.9": { + "purity": "4.2.0" + }, + "2.10": { + "purity": "4.1.5" + }, + "2.11": { + "purity": "4.3.0" + }, + "2.12": { + "purity": "4.3.4" + }, + "2.13": { + "purity": "4.4.0" + }, + "2.14": { + "purity": "4.4.2" + }, + "2.15": { + "purity": "4.5.0" + }, + "2.16": { + "purity": "4.5.2" + }, + "2.17": { + "purity": "4.5.6" + }, + "2.18": { + "purity": "4.6.0" + }, + "2.19": { + "purity": "4.6.1" + }, + "2.20": { + "purity": "4.6.3" + }, + "2.21": { + "purity": "4.6.4" + }, + "2.22": { + "purity": "4.6.5" + }, + "2.23": { + "purity": "4.6.7" + }, + "2.24": { + "purity": "4.6.8" + }, + "2.25": { + "purity": "4.8.0" + }, + "2.26": { + "purity": "4.8.1" + }, + "2.27": { + "purity": "4.8.3" + }, + "2.28": { + "purity": "4.8.4" + } +} diff --git a/Private/Add-PfbCommonQueryParams.ps1 b/Private/Add-PfbCommonQueryParams.ps1 new file mode 100644 index 0000000..7741128 --- /dev/null +++ b/Private/Add-PfbCommonQueryParams.ps1 @@ -0,0 +1,24 @@ +function Add-PfbCommonQueryParams { + <# + .SYNOPSIS + Adds common query parameters to a query-parameter hashtable. + .DESCRIPTION + Populates a hashtable with the standard -Filter, -Sort, -Limit, -TotalOnly, + -Names, and -Ids query parameters used across all Get-Pfb* cmdlets. Uses + ContainsKey semantics to detect bound parameters, allowing explicit passes + of falsy values (e.g. -Limit 0 or empty-string -Filter) to be included. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)][hashtable]$Into, + [Parameter(Mandatory)][System.Collections.IDictionary]$BoundParameters, + [string[]]$Names, + [string[]]$Ids + ) + if ($BoundParameters.ContainsKey('Filter')) { $Into['filter'] = $BoundParameters['Filter'] } + if ($BoundParameters.ContainsKey('Sort')) { $Into['sort'] = $BoundParameters['Sort'] } + if ($BoundParameters.ContainsKey('Limit')) { $Into['limit'] = $BoundParameters['Limit'] } + if ($BoundParameters.ContainsKey('TotalOnly')) { $Into['total_only'] = 'true' } + if ($Names) { $Into['names'] = $Names -join ',' } + if ($Ids) { $Into['ids'] = $Ids -join ',' } +} diff --git a/Private/Assert-PfbApiCapability.ps1 b/Private/Assert-PfbApiCapability.ps1 new file mode 100644 index 0000000..5045d85 --- /dev/null +++ b/Private/Assert-PfbApiCapability.ps1 @@ -0,0 +1,115 @@ +function Assert-PfbApiCapability { + <# + .SYNOPSIS + Throws before an API call is sent if the connected array's REST version does not + support the requested endpoint, query parameter, or request-body field. + .DESCRIPTION + Looks up Data/PfbCapabilityMap.json (built by tools/Build-PfbCapabilityMap.ps1) for + the "$Method $Endpoint" being called. If the map is unavailable, or the endpoint is + not present in it, this is a deliberate no-op: the map may be stale, or the endpoint + may take path parameters not captured by the map's flat key format. A capability + check must never be the reason a call that would otherwise succeed gets blocked. + .PARAMETER ApiVersion + Overrides the version to check against instead of $Array.ApiVersion (mirrors + Invoke-PfbApiRequest's -ApiVersionOverride). + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [PSCustomObject]$Array, + + [Parameter(Mandatory)] + [string]$Method, + + [Parameter(Mandatory)] + [string]$Endpoint, + + # Hashtable OR array. Invoke-PfbApiRequest forwards its own -Body straight into this + # parameter, and a few endpoints declare a top-level JSON array as their request body, + # so this must admit both or the forwarding call fails at bind time. + # + # ICollection, not IEnumerable and not a bare [object]. IEnumerable looks narrower but + # is worse: System.String implements it, so PowerShell happily converts -Body 42 into + # the string "42" instead of rejecting it. ICollection admits every dictionary and + # list shape (Hashtable, Hashtable[], Object[], OrderedDictionary, List) while + # still rejecting strings, numbers, booleans and PSCustomObjects exactly as + # [hashtable] did, and -- unlike a ValidateScript, which rejects an explicit $null + # even under [AllowNull()] -- it still accepts the $null this function is handed on + # every bodyless call. Verified under both PowerShell 5.1 and 7. + # See the array-body note on the body-field loop below for how each shape is treated. + [Parameter()] + [System.Collections.ICollection]$Body, + + [Parameter()] + [hashtable]$QueryParams, + + [Parameter()] + [string]$ApiVersion + ) + + $map = Get-PfbCapabilityMap + if (-not $map) { return } + + $normalizedEndpoint = '/' + $Endpoint.TrimStart('/') + $key = "$Method $normalizedEndpoint" + $entry = $map.endpoints.$key + if (-not $entry) { return } + + $effectiveVersion = if ($ApiVersion) { $ApiVersion } else { $Array.ApiVersion } + if (-not $effectiveVersion) { return } + + $versionMap = Get-PfbVersionMap + + function Format-PfbVersionDescription { + param([string]$RestVersion) + $purity = $versionMap.$RestVersion.purity + if ($purity) { return "REST $RestVersion (Purity//FB $purity)" } + return "REST $RestVersion" + } + + $violations = [System.Collections.Generic.List[string]]::new() + + if (-not (Test-PfbVersionAtLeast -Have $effectiveVersion -Need $entry.minVersion)) { + $violations.Add("$key requires $(Format-PfbVersionDescription $entry.minVersion)") + } + + if ($QueryParams) { + foreach ($paramName in $QueryParams.Keys) { + $value = $QueryParams[$paramName] + if ($null -eq $value -or ($value -is [string] -and [string]::IsNullOrEmpty($value))) { continue } + + $introducedIn = $entry.parameters.$paramName + if ($introducedIn -and -not (Test-PfbVersionAtLeast -Have $effectiveVersion -Need $introducedIn)) { + $violations.Add("parameter '$paramName' on $key requires $(Format-PfbVersionDescription $introducedIn)") + } + } + } + + # Dictionary bodies only, and deliberately so. An array body's per-element fields are NOT + # checked, because the capability map records nothing to check them against: + # tools/Build-PfbCapabilityMap.ps1 fills bodyProperties from Get-PfbSchemaPropertyNames, + # whose schema walk resolves $ref and allOf but never descends through an array schema's + # "items". Every array-bodied endpoint in the spec -- PUT /workloads/tags/batch, + # POST /nodes/batch, POST /resource-accesses/batch -- therefore carries + # "bodyProperties": {} in Data/PfbCapabilityMap.json, so a union-of-element-fields check + # would compare every field against an empty map and could never fire. Skipping is an + # honest no-op; the alternative is a gate that only looks like one. Teaching the map to + # record per-element fields is its own change, and this loop picks it up for free if a + # future map representation ever lands. + # + # Endpoint minVersion and query-parameter checks above still run for array-bodied calls, + # which is gating those endpoints previously had none of. + if ($Body -is [System.Collections.IDictionary]) { + foreach ($propName in $Body.Keys) { + $introducedIn = $entry.bodyProperties.$propName + if ($introducedIn -and -not (Test-PfbVersionAtLeast -Have $effectiveVersion -Need $introducedIn)) { + $violations.Add("request-body field '$propName' on $key requires $(Format-PfbVersionDescription $introducedIn)") + } + } + } + + if ($violations.Count -eq 0) { return } + + $haveDescription = Format-PfbVersionDescription $effectiveVersion + throw "$($violations -join '; '), but the connected array is running $haveDescription. Upgrade the array or omit the unsupported option(s)." +} diff --git a/Private/Assert-PfbRemoteNameNotCoerced.ps1 b/Private/Assert-PfbRemoteNameNotCoerced.ps1 new file mode 100644 index 0000000..79614e6 --- /dev/null +++ b/Private/Assert-PfbRemoteNameNotCoerced.ps1 @@ -0,0 +1,49 @@ +function Assert-PfbRemoteNameNotCoerced { + <# + .SYNOPSIS + Reject a -RemoteName value that is the ToString() of a whole piped object. + .DESCRIPTION + PowerShell resolves pipeline binding in three passes: ByValue-without-coercion, + then ByPropertyName-without-coercion, then ByValue-WITH-coercion. + + Get-PfbArrayConnectionPath and Get-PfbArrayConnectionPerformanceReplication have no -Id + parameter at all, so a piped connection object falls through to pass 3 and the entire + PSCustomObject is ToString()-ed into [string[]]$RemoteName. Get-/Remove-PfbArrayConnection + do expose -Id and usually bind at pass 2 on it, but that is a property of the piped + OBJECT, not of the cmdlet: pipe anything lacking id/name/remoteName and pass 3 fires + there too. (Update-PfbArrayConnection is genuinely immune -- neither of its parameters + declares ValueFromPipeline, so pass 3 is unreachable and the guard would be dead code.) + + Either way the result is a garbage filter such as + + remote_names=@{id=10314f42-aaaa; status=connected; remote=} + + Before issue #64 the key was the unrecognised (and therefore silently ignored) names=, so + this returned every record unfiltered. Now that remote_names= is live, the array rejects + it with "Array connection does not exist." This helper turns that into an actionable, + module-level failure instead of a confusing server-side one. + + -RemoteName by value with plain strings ('FB-B','FB-C' | Get-PfbArrayConnectionPath) is + unaffected, as is binding by property name. + + Deliberately called imperatively from each cmdlet's process block rather than wired as + [ValidateScript({ Assert-PfbRemoteNameNotCoerced $_ })] like Assert-PfbSafeName. That was + tried and reverted: a ValidateScript failure on a PIPELINE-bound argument is a + NON-terminating per-item binding error, so the cmdlet's end block still runs and still + issues the request -- with no remote_names key at all, i.e. the silently-unfiltered + result that issue #64 exists to eliminate. Verified on both editions. The imperative + throw terminates the pipeline, which is the required behaviour. + #> + param([Parameter(Mandatory)] [object]$Value) + + foreach ($v in @($Value)) { + if ($v -is [string] -and $v -like '*@{*') { + throw ("-RemoteName received a stringified object ('{0}') instead of a remote array name. " -f $v) + + 'A piped object that binds to neither -Id nor a name property is coerced whole into ' + + '-RemoteName. Pipe the remote name instead, e.g. ' + + 'Get-PfbArrayConnection | Select-Object -ExpandProperty remote | ' + + 'Select-Object -ExpandProperty name | Get-PfbArrayConnectionPath, or pass -RemoteName explicitly.' + } + } + return $true +} diff --git a/Private/ConvertTo-PfbQueryString.ps1 b/Private/ConvertTo-PfbQueryString.ps1 index 13f72c4..0358b3d 100644 --- a/Private/ConvertTo-PfbQueryString.ps1 +++ b/Private/ConvertTo-PfbQueryString.ps1 @@ -24,7 +24,10 @@ function ConvertTo-PfbQueryString { continue } - if ($value -is [array]) { + if ($value -is [bool]) { + $value = $value.ToString().ToLowerInvariant() + } + elseif ($value -is [array]) { $value = $value -join ',' } diff --git a/Private/Get-PfbCapabilityMap.ps1 b/Private/Get-PfbCapabilityMap.ps1 new file mode 100644 index 0000000..1b2a5c6 --- /dev/null +++ b/Private/Get-PfbCapabilityMap.ps1 @@ -0,0 +1,34 @@ +function Get-PfbCapabilityMap { + <# + .SYNOPSIS + Loads and caches Data/PfbCapabilityMap.json, the generated manifest mapping each + FlashBlade REST endpoint (and its parameters/request-body fields) to the REST + version it was introduced in. + .DESCRIPTION + Returns $null if the manifest file is missing rather than throwing -- callers + (Assert-PfbApiCapability) treat a missing/unloadable map as "nothing to check + against" rather than a hard failure. + #> + [CmdletBinding()] + param() + + if ($null -ne $script:PfbCapabilityMap) { + return $script:PfbCapabilityMap + } + + $path = Join-Path $script:PfbModuleRoot 'Data/PfbCapabilityMap.json' + if (-not (Test-Path $path)) { + return $null + } + + $json = Get-Content -Path $path -Raw + # ConvertFrom-Json has no -Depth parameter on Windows PowerShell 5.1 (added in PS6) -- + # 5.1's own recursion limit (100) is already far deeper than this manifest's shape. + $script:PfbCapabilityMap = if ($PSVersionTable.PSVersion.Major -ge 6) { + $json | ConvertFrom-Json -Depth 20 + } + else { + $json | ConvertFrom-Json + } + return $script:PfbCapabilityMap +} diff --git a/Private/Get-PfbVersionMap.ps1 b/Private/Get-PfbVersionMap.ps1 new file mode 100644 index 0000000..30c77bf --- /dev/null +++ b/Private/Get-PfbVersionMap.ps1 @@ -0,0 +1,33 @@ +function Get-PfbVersionMap { + <# + .SYNOPSIS + Loads and caches Data/PfbVersionMap.json, the REST-version to Purity//FB-version + pairing. + .DESCRIPTION + Returns $null if the file is missing. This map is purely cosmetic -- it only + enriches capability-check error messages with a Purity//FB version alongside the + REST version; a missing map never blocks a call. + #> + [CmdletBinding()] + param() + + if ($null -ne $script:PfbVersionMap) { + return $script:PfbVersionMap + } + + $path = Join-Path $script:PfbModuleRoot 'Data/PfbVersionMap.json' + if (-not (Test-Path $path)) { + return $null + } + + $json = Get-Content -Path $path -Raw + # ConvertFrom-Json has no -Depth parameter on Windows PowerShell 5.1 (added in PS6) -- + # 5.1's own recursion limit (100) is already far deeper than this manifest's shape. + $script:PfbVersionMap = if ($PSVersionTable.PSVersion.Major -ge 6) { + $json | ConvertFrom-Json -Depth 5 + } + else { + $json | ConvertFrom-Json + } + return $script:PfbVersionMap +} diff --git a/Private/Invoke-PfbApiRequest.ps1 b/Private/Invoke-PfbApiRequest.ps1 index 2cdaea2..26e87df 100644 --- a/Private/Invoke-PfbApiRequest.ps1 +++ b/Private/Invoke-PfbApiRequest.ps1 @@ -13,14 +13,19 @@ function Invoke-PfbApiRequest { [PSCustomObject]$Array, [Parameter(Mandatory)] - [ValidateSet('GET', 'POST', 'PATCH', 'DELETE')] + [ValidateSet('GET', 'POST', 'PATCH', 'PUT', 'DELETE')] [string]$Method, [Parameter(Mandatory)] [string]$Endpoint, + # Hashtable OR array: a few endpoints (PUT /workloads/tags/batch, POST /nodes/batch, + # POST /resource-accesses/batch) declare a top-level JSON array as their request body. + # Matches Assert-PfbApiCapability's -Body, which this is forwarded into unmodified -- + # see the type note there for why ICollection rather than IEnumerable or [object]. + # Every existing caller passes a [hashtable] (or $null), all of which bind unchanged. [Parameter()] - [hashtable]$Body, + [System.Collections.ICollection]$Body, [Parameter()] [hashtable]$QueryParams, @@ -35,6 +40,11 @@ function Invoke-PfbApiRequest { [string]$ApiVersionOverride ) + # Fail fast if the connected array's REST version doesn't support this endpoint/param/ + # field, before any network call is made. Never sent if incompatible: see + # Assert-PfbApiCapability's header for why an unrecognized endpoint is a silent no-op. + Assert-PfbApiCapability -Array $Array -Method $Method -Endpoint $Endpoint -Body $Body -QueryParams $QueryParams -ApiVersion $ApiVersionOverride + # Certificate/OAuth2 sessions: proactively refresh the access token before it expires, # rather than waiting for a 401. A proactive refresh generates no failed-authentication # entry in the array's session log, unlike a reactive 401-triggered refresh. @@ -92,8 +102,15 @@ function Invoke-PfbApiRequest { Headers = $headers } - if ($Body -and ($Method -eq 'POST' -or $Method -eq 'PATCH')) { - $restParams['Body'] = ($Body | ConvertTo-Json -Depth 10 -Compress) + # GET/DELETE never carry a body: the guard is a whitelist, not a "not GET" test, so a new + # verb has to be added here deliberately rather than inheriting body serialisation. + # + # -InputObject, NOT the pipeline: the pipeline unrolls a collection, so a one-element + # array body would arrive at ConvertTo-Json as its bare element and serialise to a JSON + # object instead of a one-element array. A hashtable is never unrolled by the pipeline, + # so the hashtable path's output is byte-identical either way. + if ($Body -and ($Method -eq 'POST' -or $Method -eq 'PATCH' -or $Method -eq 'PUT')) { + $restParams['Body'] = (ConvertTo-Json -InputObject $Body -Depth 10 -Compress) } # SSL handling @@ -104,6 +121,15 @@ function Invoke-PfbApiRequest { # HTTP timeout handling — default to 30s if the connection object predates this field $restParams['TimeoutSec'] = if ($Array.HttpTimeoutMs) { [int][Math]::Ceiling($Array.HttpTimeoutMs / 1000.0) } else { 30 } + # If the caller set a page-size/limit query param (every Get-Pfb* cmdlet's -Limit maps to + # this), treat it as a hard cap on the running total across pages -- Purity//FB REST treats + # `limit` as page size only and keeps returning a continuation_token even once the caller's + # desired item count has been reached, so AutoPaginate must stop itself. + $requestedLimit = $null + if ($QueryParams -and $QueryParams.ContainsKey('limit') -and [int]$QueryParams['limit'] -gt 0) { + $requestedLimit = [int]$QueryParams['limit'] + } + $allItems = [System.Collections.Generic.List[object]]::new() $totalItemCount = $null $hasMore = $true @@ -206,7 +232,8 @@ function Invoke-PfbApiRequest { } # Handle pagination - if ($AutoPaginate -and $response.continuation_token) { + $limitReached = ($null -ne $requestedLimit -and $allItems.Count -ge $requestedLimit) + if ($AutoPaginate -and $response.continuation_token -and -not $limitReached) { # Update the URI with continuation token if (-not $QueryParams) { $QueryParams = @{} } $QueryParams['continuation_token'] = $response.continuation_token @@ -219,6 +246,12 @@ function Invoke-PfbApiRequest { } } + # The last page fetched may have overshot the requested limit (server-side page size is + # independent of it) -- trim so callers get exactly what they asked for. + if ($null -ne $requestedLimit -and $allItems.Count -gt $requestedLimit) { + $allItems = $allItems.GetRange(0, $requestedLimit) + } + # If TotalOnly was requested and we got a count but no items, return the count if ($allItems.Count -eq 0 -and $null -ne $totalItemCount) { return [PSCustomObject]@{ total_item_count = $totalItemCount } @@ -280,6 +313,19 @@ function ConvertTo-PfbApiError { <# .SYNOPSIS Formats a FlashBlade API error into a readable message, preserving the original exception. + .DESCRIPTION + Includes the HTTP status as "(HTTP nnn)" whenever the failure actually reached the array + and came back with one. + + This matters for anything that consumes these failures programmatically -- automated + tests, log analysis, retry logic -- because the status is what separates "the request was + malformed" (400) from "the credential lacks permission" (403) from "the endpoint does not + exist at this REST version" (404) from "transient, retry" (503). Those need different + responses, and the human-readable message alone frequently cannot distinguish them: a + FlashBlade 400 and 403 can both come back as nothing more specific than "Bad Request". + + The status is only omitted when there genuinely is none to report -- a DNS failure, + connection timeout or rejected certificate never produced an HTTP response at all. #> [CmdletBinding()] param( @@ -288,7 +334,28 @@ function ConvertTo-PfbApiError { [System.Management.Automation.ErrorRecord]$ErrorRecord ) - $errorMessage = "FlashBlade API error on ${Method} ${Endpoint}: $($ErrorRecord.Exception.Message)" + # Read from the TRANSPORT layer, never from the error body's own "code" field. The two are + # not the same number: "code" is an application error code that sometimes coincides with the + # HTTP status (401, 403) and sometimes does not -- a write rejected for operating on the + # wrong array in a fleet returns code 13 inside an HTTP 400. Trusting "code" would therefore + # report a plausible but wrong status part of the time, which is worse than reporting none: + # a missing status reads as "unknown", a wrong one gets believed. + # + # Same access pattern as the reconnect gate above, which reads the status off the same + # exceptions in this same catch block. + $statusPart = '' + if ($ErrorRecord.Exception.Response) { + # HttpResponseException (PS 6+) and WebException (5.1) hold different response types but + # both expose .StatusCode as a System.Net.HttpStatusCode, which casts to int. + # Range-checked so a response object carrying no usable status contributes nothing + # rather than "(HTTP 0)". + $statusCode = [int]$ErrorRecord.Exception.Response.StatusCode + if ($statusCode -ge 100 -and $statusCode -le 599) { + $statusPart = " (HTTP $statusCode)" + } + } + + $errorMessage = "FlashBlade API error on ${Method} ${Endpoint}${statusPart}: $($ErrorRecord.Exception.Message)" if ($ErrorRecord.ErrorDetails.Message) { try { $apiError = $ErrorRecord.ErrorDetails.Message | ConvertFrom-Json @@ -298,7 +365,11 @@ function ConvertTo-PfbApiError { # if both are somehow present. $errorList = if ($apiError.errors) { $apiError.errors } else { $apiError.error } if ($errorList) { - $errorMessage = "FlashBlade API error: $($errorList[0].message)" + # $statusPart is repeated because this branch REPLACES the message above rather + # than extending it. Without it the status would be dropped from precisely the + # failures that returned a parseable body -- i.e. every deliberate API rejection, + # which is the case where knowing the status is most useful. + $errorMessage = "FlashBlade API error${statusPart}: $($errorList[0].message)" } } catch { } diff --git a/Private/Test-PfbContextMultiValueCapable.ps1 b/Private/Test-PfbContextMultiValueCapable.ps1 new file mode 100644 index 0000000..467eabc --- /dev/null +++ b/Private/Test-PfbContextMultiValueCapable.ps1 @@ -0,0 +1,106 @@ +function Test-PfbContextMultiValueCapable { + <# + .SYNOPSIS + THE single declared home of the Fusion `context_names` cardinality rule: can this + endpoint accept MANY context names, or exactly one? + .DESCRIPTION + The rule, ratified in rev 3 of docs/design/fusion-context-injection.md (section 8): + + An endpoint is multi-context-capable if and only if its `context_names` + parameter resolves to component `Context_names_get` AND the endpoint also + declares `allow_errors`. + + Both facts are recorded per endpoint in Data/PfbCapabilityMap.json, so at runtime + this is a lookup, not a derivation. Against the committed map (generatedFrom + 2.0-2.28) the rule yields 135 capable endpoints of the 139 referencing the + multi-value component; against fb2.27, 134 of 139. + + This function is deliberately PURE -- no map loading, no file I/O. Each caller + supplies facts from its own source: the maintainer drift check from the capability + map or from a single spec version, the context-injection path from the map entry it + has already resolved. That is what lets both execute the same rule instead of + re-deriving it. + + WHY NOT THE HTTP VERB. Rev 2 proposed a clean verb split -- GET multi-value, + mutations size-1 -- and this file previously described it as ground truth. It is + not, and that claim has been withdrawn. Live testing on 2026-08-01 against FB-A + (Purity//FB 4.8.2, REST 2.26, coordinator of fleet `cc-test-fleet` with FB-C as a + remote member) disproved it: four fleet-scoped GETs reject any two-name context + with `400 code 15 "Multiple location contexts are not allowed."` -- + `GET /presets/workload`, `GET /topology-groups`, `GET /topology-groups/arrays`, + `GET /topology-groups/members`. `code 15` fires BEFORE the cross-array + authorization gate that yields `code 20` on control endpoints, so it is structural + to the endpoint, not a permission artifact. A fleet-scoped endpoint has exactly one + meaningful context -- the fleet -- so multi-value is not merely restricted there, + it is meaningless. Their spec entries reference `Context_names_get` in error + (upstream defect, in review; still present at 2.28). See Appendix A of the design + doc for the full evidence and Appendix B for the defect registry. + + WHY `allow_errors` RATHER THAN THE COMPONENT NAME ALONE. The component reference is + wrong for five endpoints (those four plus `DELETE /management-access-policies` in + 2.26/2.27, since fixed in 2.28), while `allow_errors` is correct on every case for + which evidence exists. A genuinely multi-context endpoint needs a partial-failure + story, so its absence is strong evidence the endpoint cannot fan out -- which is + how the upstream bug report characterizes the defect ("missing e.g. `allow_errors` + / HTTP 207 / `_context`"). + + WHY NOT HTTP 207. It is the strictest and most accurate signal, and the one + upstream tooling uses, but the capability map records the request surface only and + holds no response data at all, so it is unavailable at runtime. Gating on it would + also treat 11 endpoints of unknown status as size-1 on no evidence. It serves as a + corroborating signal for the maintainer drift check, sourced from tools/specs, and + never as a runtime gate. + + The verb survives ONLY as a fallback for an endpoint with no component signal at + all -- i.e. absent from the capability map entirely. It is not the rule. + .PARAMETER Method + The endpoint's HTTP method. Case-insensitive. Used only in the no-signal fallback. + .PARAMETER ContextComponent + The component the endpoint's `context_names` parameter resolves to, e.g. + 'Context_names_get' or 'Context_names'. Pass $null or omit when no component is + known -- that, and only that, selects the verb fallback. + .PARAMETER DeclaresAllowErrors + Whether the endpoint declares an `allow_errors` parameter. + .OUTPUTS + [bool] -- $true when the endpoint may carry multiple context names, $false when it + accepts exactly one. + .NOTES + The fallback throws on a method it has no verdict for. Returning $false there would + quietly assume "size-1" for a verb nobody has reasoned about; failing loudly forces + a human decision. + #> + [CmdletBinding()] + [OutputType([bool])] + param( + [Parameter(Mandatory)] + [string]$Method, + + [AllowNull()] + [AllowEmptyString()] + [string]$ContextComponent, + + [Parameter(Mandatory)] + [bool]$DeclaresAllowErrors + ) + + if (-not [string]::IsNullOrWhiteSpace($ContextComponent)) { + return ($ContextComponent -eq 'Context_names_get') -and $DeclaresAllowErrors + } + + # No component signal at all -- the endpoint is not in the capability map. Fall back to + # the verb, which is a guess, not the rule. + switch ($Method.ToUpperInvariant()) { + 'GET' { return $true } + 'POST' { return $false } + 'PATCH' { return $false } + 'PUT' { return $false } + 'DELETE' { return $false } + default { + throw ("Test-PfbContextMultiValueCapable: no context-cardinality verdict is " + + "defined for HTTP method '$Method', and no component signal was supplied " + + 'to decide it on evidence. The verb fallback covers GET/POST/PUT/PATCH/' + + 'DELETE only -- extend this function deliberately rather than assuming a ' + + 'default.') + } + } +} diff --git a/Private/Test-PfbVersionAtLeast.ps1 b/Private/Test-PfbVersionAtLeast.ps1 new file mode 100644 index 0000000..e2e5f81 --- /dev/null +++ b/Private/Test-PfbVersionAtLeast.ps1 @@ -0,0 +1,34 @@ +function Test-PfbVersionAtLeast { + <# + .SYNOPSIS + Tests whether one REST API version string is numerically >= another. + .DESCRIPTION + Naive string comparison ranks '2.9' above '2.26'. Parses each into numeric + Major/Minor components (same idiom as ConvertTo-PfbVersionObject) and compares + those instead. + .PARAMETER Have + The version to test, e.g. the connected array's negotiated REST version. + .PARAMETER Need + The minimum version required. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string]$Have, + + [Parameter(Mandatory)] + [string]$Need + ) + + $haveParts = $Have -split '\.' + $needParts = $Need -split '\.' + $haveMajor = [int]$haveParts[0] + $haveMinor = [int]$haveParts[1] + $needMajor = [int]$needParts[0] + $needMinor = [int]$needParts[1] + + if ($haveMajor -ne $needMajor) { + return $haveMajor -gt $needMajor + } + return $haveMinor -ge $needMinor +} diff --git a/Public/Admin/Get-PfbAdmin.ps1 b/Public/Admin/Get-PfbAdmin.ps1 index 26511b3..c4a4572 100644 --- a/Public/Admin/Get-PfbAdmin.ps1 +++ b/Public/Admin/Get-PfbAdmin.ps1 @@ -53,11 +53,7 @@ function Get-PfbAdmin { } 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'admins' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Admin/Get-PfbAdminCache.ps1 b/Public/Admin/Get-PfbAdminCache.ps1 index 2d98fa0..32f3feb 100644 --- a/Public/Admin/Get-PfbAdminCache.ps1 +++ b/Public/Admin/Get-PfbAdminCache.ps1 @@ -55,11 +55,7 @@ function Get-PfbAdminCache { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'admins/cache' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Admin/Get-PfbAdminManagementAccessPolicy.ps1 b/Public/Admin/Get-PfbAdminManagementAccessPolicy.ps1 index 453c53d..b259a51 100644 --- a/Public/Admin/Get-PfbAdminManagementAccessPolicy.ps1 +++ b/Public/Admin/Get-PfbAdminManagementAccessPolicy.ps1 @@ -48,12 +48,11 @@ function Get-PfbAdminManagementAccessPolicy { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'admins/management-access-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Admin/Get-PfbAdminSetting.ps1 b/Public/Admin/Get-PfbAdminSetting.ps1 index 196c639..7143fd0 100644 --- a/Public/Admin/Get-PfbAdminSetting.ps1 +++ b/Public/Admin/Get-PfbAdminSetting.ps1 @@ -6,6 +6,12 @@ function Get-PfbAdminSetting { The Get-PfbAdminSetting cmdlet returns global administrator settings from the connected Pure Storage FlashBlade. These settings include lockout policy, single sign-on configuration, and other administrative-level options. + .PARAMETER Filter + A server-side filter expression to narrow results. + .PARAMETER Sort + Sort field and direction. + .PARAMETER Limit + Maximum number of items to return. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -23,6 +29,9 @@ function Get-PfbAdminSetting { #> [CmdletBinding()] param( + [Parameter()] [string]$Filter, + [Parameter()] [string]$Sort, + [Parameter()] [int]$Limit, [Parameter()] [PSCustomObject]$Array ) @@ -31,6 +40,9 @@ function Get-PfbAdminSetting { } process { - Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'admins/settings' + $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'admins/settings' -QueryParams $queryParams } } diff --git a/Public/Admin/Get-PfbAdminSshCaPolicy.ps1 b/Public/Admin/Get-PfbAdminSshCaPolicy.ps1 index ee344e0..08a5db5 100644 --- a/Public/Admin/Get-PfbAdminSshCaPolicy.ps1 +++ b/Public/Admin/Get-PfbAdminSshCaPolicy.ps1 @@ -48,12 +48,11 @@ function Get-PfbAdminSshCaPolicy { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'admins/ssh-certificate-authority-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Admin/Get-PfbApiClient.ps1 b/Public/Admin/Get-PfbApiClient.ps1 index 6da8e0d..be6f710 100644 --- a/Public/Admin/Get-PfbApiClient.ps1 +++ b/Public/Admin/Get-PfbApiClient.ps1 @@ -55,11 +55,7 @@ function Get-PfbApiClient { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'api-clients' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Admin/Get-PfbApiToken.ps1 b/Public/Admin/Get-PfbApiToken.ps1 index c63d17e..3216127 100644 --- a/Public/Admin/Get-PfbApiToken.ps1 +++ b/Public/Admin/Get-PfbApiToken.ps1 @@ -53,11 +53,7 @@ function Get-PfbApiToken { } 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'admins/api-tokens' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Admin/Get-PfbManagementAccessPolicy.ps1 b/Public/Admin/Get-PfbManagementAccessPolicy.ps1 index 9afb312..e1c6313 100644 --- a/Public/Admin/Get-PfbManagementAccessPolicy.ps1 +++ b/Public/Admin/Get-PfbManagementAccessPolicy.ps1 @@ -54,11 +54,7 @@ function Get-PfbManagementAccessPolicy { } 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'management-access-policies' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Admin/Get-PfbManagementAccessPolicyAdmin.ps1 b/Public/Admin/Get-PfbManagementAccessPolicyAdmin.ps1 index fd6e83e..ae68848 100644 --- a/Public/Admin/Get-PfbManagementAccessPolicyAdmin.ps1 +++ b/Public/Admin/Get-PfbManagementAccessPolicyAdmin.ps1 @@ -47,12 +47,11 @@ function Get-PfbManagementAccessPolicyAdmin { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'management-access-policies/admins' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Admin/Get-PfbManagementAccessPolicyDirectoryRole.ps1 b/Public/Admin/Get-PfbManagementAccessPolicyDirectoryRole.ps1 index 5b0e024..791e2b9 100644 --- a/Public/Admin/Get-PfbManagementAccessPolicyDirectoryRole.ps1 +++ b/Public/Admin/Get-PfbManagementAccessPolicyDirectoryRole.ps1 @@ -47,12 +47,11 @@ function Get-PfbManagementAccessPolicyDirectoryRole { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'management-access-policies/directory-services/roles' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Admin/Get-PfbManagementAccessPolicyMember.ps1 b/Public/Admin/Get-PfbManagementAccessPolicyMember.ps1 index 2833ab4..f646f90 100644 --- a/Public/Admin/Get-PfbManagementAccessPolicyMember.ps1 +++ b/Public/Admin/Get-PfbManagementAccessPolicyMember.ps1 @@ -47,12 +47,11 @@ function Get-PfbManagementAccessPolicyMember { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'management-access-policies/members' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Admin/Get-PfbOidcIdp.ps1 b/Public/Admin/Get-PfbOidcIdp.ps1 index 8f596ad..64701c7 100644 --- a/Public/Admin/Get-PfbOidcIdp.ps1 +++ b/Public/Admin/Get-PfbOidcIdp.ps1 @@ -54,11 +54,7 @@ function Get-PfbOidcIdp { } 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'sso/oidc/idps' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Admin/Get-PfbPasswordPolicy.ps1 b/Public/Admin/Get-PfbPasswordPolicy.ps1 index 7cb4c4d..ed67bb3 100644 --- a/Public/Admin/Get-PfbPasswordPolicy.ps1 +++ b/Public/Admin/Get-PfbPasswordPolicy.ps1 @@ -6,6 +6,12 @@ function Get-PfbPasswordPolicy { The Get-PfbPasswordPolicy cmdlet returns the password policy settings from the connected Pure Storage FlashBlade. This is a singleton resource that returns the current password complexity and expiration requirements configured on the array. + .PARAMETER Filter + A server-side filter expression to narrow results. + .PARAMETER Sort + Sort field and direction. + .PARAMETER Limit + Maximum number of items to return. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -23,9 +29,16 @@ function Get-PfbPasswordPolicy { #> [CmdletBinding()] param( + [Parameter()] [string]$Filter, + [Parameter()] [string]$Sort, + [Parameter()] [int]$Limit, [Parameter()] [PSCustomObject]$Array ) Assert-PfbConnection -Array ([ref]$Array) - Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'password-policies' -AutoPaginate + + $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'password-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Admin/Get-PfbPublicKey.ps1 b/Public/Admin/Get-PfbPublicKey.ps1 index 4c86335..75ad98b 100644 --- a/Public/Admin/Get-PfbPublicKey.ps1 +++ b/Public/Admin/Get-PfbPublicKey.ps1 @@ -55,11 +55,7 @@ function Get-PfbPublicKey { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'public-keys' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Admin/Get-PfbSaml2Idp.ps1 b/Public/Admin/Get-PfbSaml2Idp.ps1 index 214cfce..87845ec 100644 --- a/Public/Admin/Get-PfbSaml2Idp.ps1 +++ b/Public/Admin/Get-PfbSaml2Idp.ps1 @@ -54,11 +54,7 @@ function Get-PfbSaml2Idp { } 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'sso/saml2/idps' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Admin/Get-PfbSession.ps1 b/Public/Admin/Get-PfbSession.ps1 index 7f77395..9e33743 100644 --- a/Public/Admin/Get-PfbSession.ps1 +++ b/Public/Admin/Get-PfbSession.ps1 @@ -55,11 +55,7 @@ function Get-PfbSession { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'sessions' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Admin/New-PfbApiToken.ps1 b/Public/Admin/New-PfbApiToken.ps1 index 7490a25..da8acdf 100644 --- a/Public/Admin/New-PfbApiToken.ps1 +++ b/Public/Admin/New-PfbApiToken.ps1 @@ -4,15 +4,23 @@ function New-PfbApiToken { Creates a new API token for a FlashBlade administrator. .DESCRIPTION The New-PfbApiToken cmdlet generates a new API token for an administrator account on the - connected Pure Storage FlashBlade. The administrator is identified by name or ID. Optional - attributes can be provided to configure token properties. Supports ShouldProcess for - confirmation prompts. + connected Everpure FlashBlade. The administrator is identified by name or ID. Supports + ShouldProcess for confirmation prompts. + + Note that `POST /admins/api-tokens` takes no request body at all -- every field this + endpoint accepts is a query parameter. .PARAMETER Name - The name of the administrator account for which to create an API token. + The name of the administrator account for which to create an API token. Sent as the + `admin_names` query parameter. Also accepts the alias -AdminNames. .PARAMETER Id - The ID of the administrator account for which to create an API token. + The ID of the administrator account for which to create an API token. Sent as the + `admin_ids` query parameter. Also accepts the alias -AdminIds. + .PARAMETER Timeout + The duration of API token validity, in milliseconds. .PARAMETER Attributes - A hashtable of token attributes to set (e.g., timeout duration). + Retained for backward compatibility only. `POST /admins/api-tokens` accepts no request + body, so nothing supplied here is sent to the array. Use -Timeout to set the token's + validity period. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -20,9 +28,9 @@ function New-PfbApiToken { Creates a new API token for the administrator named "pureuser". .EXAMPLE - New-PfbApiToken -Name "ops-admin" -Attributes @{ timeout = 86400000 } + New-PfbApiToken -Name "ops-admin" -Timeout 86400000 - Creates a new API token with a custom timeout for the specified administrator. + Creates a new API token for "ops-admin" that is valid for 24 hours. .EXAMPLE New-PfbApiToken -Id "10314f42-020d-7080-8013-000ddt400012" @@ -31,8 +39,14 @@ function New-PfbApiToken { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param( [Parameter(ParameterSetName = 'ByName', ValueFromPipelineByPropertyName)] + [Alias('AdminNames')] [string]$Name, - [Parameter(ParameterSetName = 'ById')] [string]$Id, + + [Parameter(ParameterSetName = 'ById')] + [Alias('AdminIds')] + [string]$Id, + + [Parameter()] [long]$Timeout, [Parameter()] [hashtable]$Attributes, [Parameter()] [PSCustomObject]$Array ) @@ -42,9 +56,14 @@ function New-PfbApiToken { } process { + # POST /admins/api-tokens accepts admin_names/admin_ids, NOT names/ids. The cmdlet + # previously sent names/ids, which the array silently ignored, so -Name and -Id had + # no effect at all. $queryParams = @{} - if ($Name) { $queryParams['names'] = $Name } - if ($Id) { $queryParams['ids'] = $Id } + if ($Name) { $queryParams['admin_names'] = $Name } + if ($Id) { $queryParams['admin_ids'] = $Id } + if ($PSBoundParameters.ContainsKey('Timeout')) { $queryParams['timeout'] = $Timeout } + $target = if ($Name) { $Name } else { $Id } $body = if ($Attributes) { $Attributes } else { @{} } diff --git a/Public/Admin/Update-PfbAdmin.ps1 b/Public/Admin/Update-PfbAdmin.ps1 index 66b5258..db7c2bd 100644 --- a/Public/Admin/Update-PfbAdmin.ps1 +++ b/Public/Admin/Update-PfbAdmin.ps1 @@ -4,36 +4,92 @@ function Update-PfbAdmin { Updates a FlashBlade administrator account. .DESCRIPTION The Update-PfbAdmin cmdlet modifies attributes of an existing administrator account - on the connected Pure Storage FlashBlade. The target administrator can be identified - by name or ID. Common updates include role changes and password resets. Supports - pipeline input and ShouldProcess for confirmation prompts. + on the connected Everpure FlashBlade. The target administrator can be identified + by name or ID. Common updates include password resets and access-policy changes. + Supports pipeline input and ShouldProcess for confirmation prompts. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the administrator account to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the administrator account to update. + .PARAMETER AuthorizationModel + The location for access policies mapping. + .PARAMETER Locked + If set to $false, the specified user is unlocked. + .PARAMETER ManagementAccessPolicies + Names of the management access policies associated with the statically-authorized + administrator. + .PARAMETER OldPassword + Old user password. + .PARAMETER Password + New user password. + .PARAMETER PublicKey + Public key for SSH access. .PARAMETER Attributes - A hashtable of administrator attributes to modify (e.g., role, password). + A hashtable of administrator attributes to modify. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbAdmin -Name "pureuser" -Attributes @{ role = @{ name = "array_admin" } } + Update-PfbAdmin -Name "ops-admin" -Password "N3wP@ssw0rd!" -Locked $false + + Resets the password for "ops-admin" and unlocks the account using typed parameters. + .EXAMPLE + Update-PfbAdmin -Name "ops-admin" -ManagementAccessPolicies "array-admin","readonly" - Assigns the array_admin role to the administrator account named "pureuser". + Assigns two management access policies to the administrator named "ops-admin". .EXAMPLE Update-PfbAdmin -Name "ops-admin" -Attributes @{ password = "N3wP@ssw0rd!" } Updates the password for the administrator account named "ops-admin". .EXAMPLE - Update-PfbAdmin -Id "10314f42-020d-7080-8013-000ddt400012" -Attributes @{ role = @{ name = "readonly" } } + Update-PfbAdmin -Id "10314f42-020d-7080-8013-000ddt400012" -Attributes @{ locked = $false } - Changes the role of the administrator identified by ID to readonly. + Unlocks the administrator identified by ID. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$AuthorizationModel, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [Nullable[bool]]$Locked, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$ManagementAccessPolicies, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$OldPassword, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Password, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$PublicKey, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -44,9 +100,46 @@ function Update-PfbAdmin { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # EVERY value-carrying parameter -- body OR query -- is guarded by + # $PSBoundParameters.ContainsKey, never by truthiness. `if ($X)` silently discards + # any legitimate falsy value the caller explicitly supplied: $false, 0, '' and an + # empty array all fail a truthiness test. So `-Locked $false` would never unlock, + # an integer field could never be set to a meaningful 0, and + # `-ManagementAccessPolicies @()` could never clear a list. ContainsKey asks the + # only correct question -- did the caller supply this parameter at all? + # + # The one exemption is a MANDATORY [string] selector like -Name/-Id above: the + # binder rejects '' for those before any guard runs, so their falsy branch is + # unreachable and `if ($Name)` is safe. That exemption is about mandatory-ness, + # not about being a query parameter -- an OPTIONAL query parameter still needs + # ContainsKey (see -Timeout in New-PfbApiToken.ps1). + # + # No [bool] cast on $Locked either: constraint 7 forbids it (it breaks the + # wire-name trace) and [Nullable[bool]] + ContainsKey already sends an explicit + # $false correctly. + $body = @{} + if ($PSBoundParameters.ContainsKey('AuthorizationModel')) { $body['authorization_model'] = $AuthorizationModel } + if ($PSBoundParameters.ContainsKey('Locked')) { $body['locked'] = $Locked } + if ($PSBoundParameters.ContainsKey('OldPassword')) { $body['old_password'] = $OldPassword } + if ($PSBoundParameters.ContainsKey('Password')) { $body['password'] = $Password } + if ($PSBoundParameters.ContainsKey('PublicKey')) { $body['public_key'] = $PublicKey } + + # Constraint 8(b): management_access_policies is an ARRAY OF REFERENCES (item + # schema is {id, name, resource_type}), so the parameter is [string[]] and the + # projection is assigned INLINE -- constraint 7 forbids a $policyRefs local. + if ($PSBoundParameters.ContainsKey('ManagementAccessPolicies')) { + $body['management_access_policies'] = @($ManagementAccessPolicies | ForEach-Object { @{ name = $_ } }) + } + } + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update admin')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'admins' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'admins' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Admin/Update-PfbApiClient.ps1 b/Public/Admin/Update-PfbApiClient.ps1 index 37428eb..ea7157e 100644 --- a/Public/Admin/Update-PfbApiClient.ps1 +++ b/Public/Admin/Update-PfbApiClient.ps1 @@ -4,38 +4,58 @@ function Update-PfbApiClient { Updates an API client on the FlashBlade. .DESCRIPTION The Update-PfbApiClient cmdlet modifies an existing API client on the connected - Pure Storage FlashBlade. Properties that can be updated include the public key, - maximum role, and enabled state. + Everpure FlashBlade. + + Note that `PATCH /api-clients` reuses the full ApiClient resource schema. Of its nine + properties, seven are read-only and `max_role` is deprecated rather than read-only, + which leaves `enabled` as the only settable field this cmdlet exposes as a typed + parameter. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the API client to update. .PARAMETER Id The ID of the API client to update. + .PARAMETER Enabled + If $true, the API client is permitted to exchange ID Tokens for access tokens. .PARAMETER Attributes - A hashtable of properties to update on the API client. + A hashtable of properties to update on the API client. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbApiClient -Name 'automation-client' -Attributes @{ enabled = $true } + Update-PfbApiClient -Name 'automation-client' -Enabled $true - Enables the API client named 'automation-client'. + Enables the API client named 'automation-client' using a typed parameter. .EXAMPLE - Update-PfbApiClient -Name 'automation-client' -Attributes @{ max_role = @{ name = 'readonly' } } + Update-PfbApiClient -Name 'automation-client' -Attributes @{ enabled = $true } - Changes the maximum role of the API client to read-only. + Enables the API client named 'automation-client'. .EXAMPLE - Update-PfbApiClient -Id '12345678-abcd-efgh-ijkl-123456789012' -Attributes @{ public_key = $newKey } + Update-PfbApiClient -Id '12345678-abcd-efgh-ijkl-123456789012' -Enabled $false - Updates the public key of an API client by ID. + Disables an API client by ID. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [string]$Id, - [Parameter(Mandatory)] + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [Nullable[bool]]$Enabled, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [hashtable]$Attributes, [Parameter()] [PSCustomObject]$Array @@ -46,13 +66,23 @@ function Update-PfbApiClient { } process { - $target = if ($Name) { $Name } else { $Id } $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Every body parameter is guarded by ContainsKey, never by truthiness -- see the + # canonical explanation in Update-PfbAdmin.ps1. + $body = @{} + if ($PSBoundParameters.ContainsKey('Enabled')) { $body['enabled'] = $Enabled } + } + + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update API client')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'api-clients' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'api-clients' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Admin/Update-PfbManagementAccessPolicy.ps1 b/Public/Admin/Update-PfbManagementAccessPolicy.ps1 index cd459e2..33b7d06 100644 --- a/Public/Admin/Update-PfbManagementAccessPolicy.ps1 +++ b/Public/Admin/Update-PfbManagementAccessPolicy.ps1 @@ -4,20 +4,50 @@ function Update-PfbManagementAccessPolicy { Updates an existing management access policy on the FlashBlade. .DESCRIPTION The Update-PfbManagementAccessPolicy cmdlet modifies attributes of an existing management - access policy on the connected Pure Storage FlashBlade. The target policy can be identified + access policy on the connected Everpure FlashBlade. The target policy can be identified by name or ID. Supports ShouldProcess for confirmation prompts. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the management access policy to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the management access policy to update. + .PARAMETER AggregationStrategy + When this is set to `least-common-permissions`, any users to whom this policy applies + can receive no access rights exceeding those defined in this policy's capability and + resource. When this is set to `all-permissions`, any users to whom this policy applies + are capable of receiving additional access rights from other policies that apply to + them. + .PARAMETER Enabled + If $true, the policy is enabled. + .PARAMETER Location + Name of the array where the policy is defined. + .PARAMETER NewName + A new user-specified name for the policy. Named -NewName rather than -Name because + -Name already identifies which policy to update. + .PARAMETER Rules + All of the rules that are part of this policy, in evaluation order. Each rule is a + hashtable with `role` and `scope` sub-objects, for example + @{ role = @{ name = 'viewer' }; scope = @{ name = 'array-1'; resource_type = 'arrays' } }. .PARAMETER Attributes - A hashtable of policy attributes to modify. + A hashtable of policy attributes to modify. Mutually exclusive with the individual + typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbManagementAccessPolicy -Name "ops-policy" -Attributes @{ allowed_operations = @("read","write") } + Update-PfbManagementAccessPolicy -Name "ops-policy" -Enabled $true -AggregationStrategy "least-common-permissions" + + Enables the "ops-policy" management access policy and restricts its aggregation + strategy, using typed parameters. + .EXAMPLE + Update-PfbManagementAccessPolicy -Name "ops-policy" -Rules @( + @{ role = @{ name = "viewer" }; scope = @{ name = "array-1"; resource_type = "arrays" } } + ) - Updates the operations allowed by the "ops-policy" management access policy. + Replaces the policy's rule list with a single viewer rule scoped to "array-1". .EXAMPLE Update-PfbManagementAccessPolicy -Id "abc12345-6789-0abc-def0-123456789abc" -Attributes @{ enabled = $true } @@ -27,12 +57,41 @@ function Update-PfbManagementAccessPolicy { Shows what would happen if the policy were updated without making changes. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$AggregationStrategy, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [Nullable[bool]]$Enabled, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Location, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$NewName, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [hashtable[]]$Rules, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) @@ -44,10 +103,34 @@ function Update-PfbManagementAccessPolicy { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } - $target = if ($Name) { $Name } else { $Id } + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Every body parameter is guarded by ContainsKey, never by truthiness -- see the + # canonical explanation in Update-PfbAdmin.ps1. + $body = @{} + if ($PSBoundParameters.ContainsKey('AggregationStrategy')) { $body['aggregation_strategy'] = $AggregationStrategy } + if ($PSBoundParameters.ContainsKey('Enabled')) { $body['enabled'] = $Enabled } + if ($PSBoundParameters.ContainsKey('NewName')) { $body['name'] = $NewName } + + # Constraint 8(a): location is a SCALAR REFERENCE (_fixedReference, properties are + # exactly {id, name, resource_type}), so the parameter is [string] and the + # reference object is built INLINE -- constraint 7 forbids a $locationRef local. + if ($PSBoundParameters.ContainsKey('Location')) { $body['location'] = @{ name = $Location } } + + # Constraint 8(c): rules is an array of COMPOSITE objects, not references -- the + # item schema (ManagementAccessPolicyRuleInPolicy) carries `role`, `scope` and + # `index` beyond {id, name, resource_type}, and its own `name` is readOnly. So it + # is passed straight through rather than projected into @{ name = ... }, which + # would write a field the schema does not accept. + if ($PSBoundParameters.ContainsKey('Rules')) { $body['rules'] = @($Rules) } + } + + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update management access policy')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'management-access-policies' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'management-access-policies' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Admin/Update-PfbOidcIdp.ps1 b/Public/Admin/Update-PfbOidcIdp.ps1 index ebebc69..537ffc0 100644 --- a/Public/Admin/Update-PfbOidcIdp.ps1 +++ b/Public/Admin/Update-PfbOidcIdp.ps1 @@ -4,35 +4,83 @@ function Update-PfbOidcIdp { Updates an existing OIDC identity provider configuration on the FlashBlade. .DESCRIPTION The Update-PfbOidcIdp cmdlet modifies attributes of an existing OIDC (OpenID Connect) - identity provider configuration on the connected Pure Storage FlashBlade. The target + identity provider configuration on the connected Everpure FlashBlade. The target IdP can be identified by name or ID. Supports ShouldProcess for confirmation prompts. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the OIDC identity provider to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the OIDC identity provider to update. + .PARAMETER Enabled + If set to $true, the OIDC SSO configuration is enabled. + .PARAMETER Idp + Properties specific to the identity provider, as a hashtable -- for example + @{ provider_url = 'https://idp.example.com' }. The schema models this as a multi-field + configuration sub-object (`provider_url`, `provider_url_ca_certificate`, + `provider_url_ca_certificate_group`), not as a reference to another resource, so it is + passed through rather than given a name-only parameter. + .PARAMETER NewName + A new name for the provider. Named -NewName rather than -Name because -Name + already identifies which IdP to update. + .PARAMETER Services + Services that the OIDC SSO authentication is used for. .PARAMETER Attributes - A hashtable of OIDC IdP attributes to modify (e.g., issuer, client_id, enabled). + A hashtable of OIDC IdP attributes to modify. Mutually exclusive with the individual + typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbOidcIdp -Name "okta-prod" -Attributes @{ enabled = $false } + Update-PfbOidcIdp -Name "okta-prod" -Enabled $false + + Disables the OIDC identity provider named "okta-prod" using a typed parameter. + .EXAMPLE + Update-PfbOidcIdp -Name "okta-prod" -Idp @{ provider_url = "https://new-idp.example.com" } - Disables the OIDC identity provider named "okta-prod". + Repoints the OIDC identity provider at a new provider URL. .EXAMPLE - Update-PfbOidcIdp -Id "abc12345-6789-0abc-def0-123456789abc" -Attributes @{ issuer = "https://new-issuer.example.com" } + Update-PfbOidcIdp -Id "abc12345-6789-0abc-def0-123456789abc" -Attributes @{ enabled = $false } - Updates the issuer URL for the OIDC IdP identified by ID. + Disables the OIDC IdP identified by ID. .EXAMPLE - Update-PfbOidcIdp -Name "azure-ad" -Attributes @{ client_id = "new-client-id" } -WhatIf + Update-PfbOidcIdp -Name "azure-ad" -NewName "azure-ad-renamed" -WhatIf - Shows what would happen if the OIDC IdP were updated without making changes. + Shows what would happen if the OIDC IdP were renamed without making changes. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [Nullable[bool]]$Enabled, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [hashtable]$Idp, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$NewName, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$Services, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) @@ -44,10 +92,28 @@ function Update-PfbOidcIdp { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } - $target = if ($Name) { $Name } else { $Id } + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Every body parameter is guarded by ContainsKey, never by truthiness -- see the + # canonical explanation in Update-PfbAdmin.ps1. + $body = @{} + if ($PSBoundParameters.ContainsKey('Enabled')) { $body['enabled'] = $Enabled } + if ($PSBoundParameters.ContainsKey('NewName')) { $body['name'] = $NewName } + if ($PSBoundParameters.ContainsKey('Services')) { $body['services'] = @($Services) } + + # Constraint 8(c): idp is a COMPOSITE sub-object (_oidcSsoPostIdp carries + # provider_url and its CA-certificate references), not a reference -- it has no + # `name` property at all, so projecting it into @{ name = ... } would write a + # field the schema does not have. Pass it straight through. + if ($PSBoundParameters.ContainsKey('Idp')) { $body['idp'] = $Idp } + } + + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update OIDC identity provider')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'sso/oidc/idps' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'sso/oidc/idps' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Admin/Update-PfbSaml2Idp.ps1 b/Public/Admin/Update-PfbSaml2Idp.ps1 index 026933c..d95ec84 100644 --- a/Public/Admin/Update-PfbSaml2Idp.ps1 +++ b/Public/Admin/Update-PfbSaml2Idp.ps1 @@ -4,35 +4,108 @@ function Update-PfbSaml2Idp { Updates an existing SAML2 identity provider configuration on the FlashBlade. .DESCRIPTION The Update-PfbSaml2Idp cmdlet modifies attributes of an existing SAML2 identity provider - configuration on the connected Pure Storage FlashBlade. The target IdP can be identified + configuration on the connected Everpure FlashBlade. The target IdP can be identified by name or ID. Supports ShouldProcess for confirmation prompts. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the SAML2 identity provider to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the SAML2 identity provider to update. + .PARAMETER ArrayUrl + The URL of the array. + .PARAMETER Binding + SAML2 binding to use for the request from the FlashBlade to the Identity Provider. + .PARAMETER Enabled + If set to $true, the SAML2 SSO configuration is enabled. + .PARAMETER Idp + Properties specific to the identity provider, as a hashtable -- for example + @{ entity_id = 'urn:idp'; metadata_url = 'https://idp.example.com/metadata' }. The + schema models this as a multi-field configuration sub-object, not as a reference to + another resource, so it is passed through rather than given a name-only parameter. + .PARAMETER Management + Properties specific to the management service, as a hashtable -- for example + @{ trust_other_saml_sps_in_fleet = $false }. + .PARAMETER NewName + A new user-specified name for the provider. Named -NewName rather than -Name + because -Name already identifies which IdP to update. + .PARAMETER Services + Services that the SAML2 SSO authentication is used for. + .PARAMETER Sp + Properties specific to the service provider, as a hashtable -- for example + @{ entity_id = 'urn:sp'; signing_credential = @{ name = 'cert1' } }. .PARAMETER Attributes - A hashtable of SAML2 IdP attributes to modify (e.g., metadata_url, enabled). + A hashtable of SAML2 IdP attributes to modify. Mutually exclusive with the individual + typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbSaml2Idp -Name "adfs-prod" -Attributes @{ enabled = $false } + Update-PfbSaml2Idp -Name "adfs-prod" -Enabled $false + + Disables the SAML2 identity provider named "adfs-prod" using a typed parameter. + .EXAMPLE + Update-PfbSaml2Idp -Name "adfs-prod" -Idp @{ metadata_url = "https://new-adfs.corp.example.com/metadata" } - Disables the SAML2 identity provider named "adfs-prod". + Updates the metadata URL for the SAML2 identity provider named "adfs-prod". .EXAMPLE - Update-PfbSaml2Idp -Id "abc12345-6789-0abc-def0-123456789abc" -Attributes @{ metadata_url = "https://new-adfs.corp.example.com/metadata" } + Update-PfbSaml2Idp -Id "abc12345-6789-0abc-def0-123456789abc" -Attributes @{ enabled = $false } - Updates the metadata URL for the SAML2 IdP identified by ID. + Disables the SAML2 IdP identified by ID. .EXAMPLE - Update-PfbSaml2Idp -Name "custom-saml" -Attributes @{ sign_in_url = "https://new-idp.example.com/sso" } -WhatIf + Update-PfbSaml2Idp -Name "custom-saml" -ArrayUrl "https://fb.corp.example.com" -WhatIf Shows what would happen if the SAML2 IdP were updated without making changes. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$ArrayUrl, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Binding, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [Nullable[bool]]$Enabled, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [hashtable]$Idp, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [hashtable]$Management, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$NewName, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$Services, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [hashtable]$Sp, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) @@ -44,10 +117,32 @@ function Update-PfbSaml2Idp { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } - $target = if ($Name) { $Name } else { $Id } + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Every body parameter is guarded by ContainsKey, never by truthiness -- see the + # canonical explanation in Update-PfbAdmin.ps1. + $body = @{} + if ($PSBoundParameters.ContainsKey('ArrayUrl')) { $body['array_url'] = $ArrayUrl } + if ($PSBoundParameters.ContainsKey('Binding')) { $body['binding'] = $Binding } + if ($PSBoundParameters.ContainsKey('Enabled')) { $body['enabled'] = $Enabled } + if ($PSBoundParameters.ContainsKey('NewName')) { $body['name'] = $NewName } + if ($PSBoundParameters.ContainsKey('Services')) { $body['services'] = @($Services) } + + # Constraint 8(c): idp, management and sp are all COMPOSITE sub-objects + # (_saml2SsoIdp, _saml2SsoManagement, _saml2SsoSp), not references -- none of them + # has a `name` property, so projecting them into @{ name = ... } would write a + # field the schema does not have. Pass them straight through. + if ($PSBoundParameters.ContainsKey('Idp')) { $body['idp'] = $Idp } + if ($PSBoundParameters.ContainsKey('Management')) { $body['management'] = $Management } + if ($PSBoundParameters.ContainsKey('Sp')) { $body['sp'] = $Sp } + } + + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update SAML2 identity provider')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'sso/saml2/idps' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'sso/saml2/idps' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Alert/Get-PfbAlert.ps1 b/Public/Alert/Get-PfbAlert.ps1 index 0c0b253..9105217 100644 --- a/Public/Alert/Get-PfbAlert.ps1 +++ b/Public/Alert/Get-PfbAlert.ps1 @@ -49,11 +49,7 @@ function Get-PfbAlert { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds if ($PSBoundParameters.ContainsKey('Flagged')) { $queryParams['flagged'] = ([bool]$Flagged).ToString().ToLower() } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'alerts' -QueryParams $queryParams -AutoPaginate diff --git a/Public/Alert/Get-PfbAlertWatcher.ps1 b/Public/Alert/Get-PfbAlertWatcher.ps1 index 38757b3..e903f6e 100644 --- a/Public/Alert/Get-PfbAlertWatcher.ps1 +++ b/Public/Alert/Get-PfbAlertWatcher.ps1 @@ -44,11 +44,7 @@ function Get-PfbAlertWatcher { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'alert-watchers' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Array/Get-PfbArray.ps1 b/Public/Array/Get-PfbArray.ps1 index 99ac2bd..2461e79 100644 --- a/Public/Array/Get-PfbArray.ps1 +++ b/Public/Array/Get-PfbArray.ps1 @@ -4,6 +4,12 @@ function Get-PfbArray { Retrieves FlashBlade array information. .DESCRIPTION Returns array attributes including name, OS version, model, and configuration. + .PARAMETER Filter + A server-side filter expression to narrow results. + .PARAMETER Sort + Sort field and direction. + .PARAMETER Limit + Maximum number of items to return. .PARAMETER Array The FlashBlade connection object. If not specified, uses the default connection. .EXAMPLE @@ -13,11 +19,23 @@ function Get-PfbArray { #> [CmdletBinding()] param( + [Parameter()] + [string]$Filter, + + [Parameter()] + [string]$Sort, + + [Parameter()] + [int]$Limit, + [Parameter()] [PSCustomObject]$Array ) Assert-PfbConnection -Array ([ref]$Array) - Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'arrays' -AutoPaginate + $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'arrays' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Array/Get-PfbArrayClientPerformance.ps1 b/Public/Array/Get-PfbArrayClientPerformance.ps1 index cab8a6c..0c50ded 100644 --- a/Public/Array/Get-PfbArrayClientPerformance.ps1 +++ b/Public/Array/Get-PfbArrayClientPerformance.ps1 @@ -46,9 +46,7 @@ function Get-PfbArrayClientPerformance { process { $queryParams = @{} - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($StartTime) { $queryParams['start_time'] = $StartTime } if ($EndTime) { $queryParams['end_time'] = $EndTime } if ($Resolution) { $queryParams['resolution'] = $Resolution } diff --git a/Public/Array/Get-PfbArrayClientS3Performance.ps1 b/Public/Array/Get-PfbArrayClientS3Performance.ps1 index a5fca40..3c631ec 100644 --- a/Public/Array/Get-PfbArrayClientS3Performance.ps1 +++ b/Public/Array/Get-PfbArrayClientS3Performance.ps1 @@ -43,9 +43,7 @@ function Get-PfbArrayClientS3Performance { ) Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($StartTime) { $queryParams['start_time'] = $StartTime } if ($EndTime) { $queryParams['end_time'] = $EndTime } if ($Resolution) { $queryParams['resolution'] = $Resolution } diff --git a/Public/Array/Get-PfbArrayErasure.ps1 b/Public/Array/Get-PfbArrayErasure.ps1 index c2234e3..20c1369 100644 --- a/Public/Array/Get-PfbArrayErasure.ps1 +++ b/Public/Array/Get-PfbArrayErasure.ps1 @@ -33,8 +33,6 @@ function Get-PfbArrayErasure { ) Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'arrays/erasures' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Array/Get-PfbArrayEula.ps1 b/Public/Array/Get-PfbArrayEula.ps1 index 4666c5a..e11194b 100644 --- a/Public/Array/Get-PfbArrayEula.ps1 +++ b/Public/Array/Get-PfbArrayEula.ps1 @@ -5,6 +5,12 @@ function Get-PfbArrayEula { .DESCRIPTION The Get-PfbArrayEula cmdlet returns the current End User License Agreement acceptance status from the connected Pure Storage FlashBlade. This is a singleton endpoint. + .PARAMETER Filter + A server-side filter expression to narrow results. + .PARAMETER Sort + Sort field and direction. + .PARAMETER Limit + Maximum number of items to return. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -21,7 +27,16 @@ function Get-PfbArrayEula { Retrieves the EULA status and displays whether it has been accepted. #> [CmdletBinding()] - param([Parameter()] [PSCustomObject]$Array) + param( + [Parameter()] [string]$Filter, + [Parameter()] [string]$Sort, + [Parameter()] [int]$Limit, + [Parameter()] [PSCustomObject]$Array + ) Assert-PfbConnection -Array ([ref]$Array) - Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'arrays/eula' + + $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'arrays/eula' -QueryParams $queryParams } diff --git a/Public/Array/Get-PfbArrayFactoryResetToken.ps1 b/Public/Array/Get-PfbArrayFactoryResetToken.ps1 index d13a415..e615f1a 100644 --- a/Public/Array/Get-PfbArrayFactoryResetToken.ps1 +++ b/Public/Array/Get-PfbArrayFactoryResetToken.ps1 @@ -5,6 +5,12 @@ function Get-PfbArrayFactoryResetToken { .DESCRIPTION The Get-PfbArrayFactoryResetToken cmdlet returns the current factory reset token status from the connected Pure Storage FlashBlade. This is a singleton endpoint. + .PARAMETER Filter + A server-side filter expression to narrow results. + .PARAMETER Sort + Sort field and direction. + .PARAMETER Limit + Maximum number of items to return. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -21,7 +27,16 @@ function Get-PfbArrayFactoryResetToken { Retrieves the factory reset token value. #> [CmdletBinding()] - param([Parameter()] [PSCustomObject]$Array) + param( + [Parameter()] [string]$Filter, + [Parameter()] [string]$Sort, + [Parameter()] [int]$Limit, + [Parameter()] [PSCustomObject]$Array + ) Assert-PfbConnection -Array ([ref]$Array) - Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'arrays/factory-reset-token' + + $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'arrays/factory-reset-token' -QueryParams $queryParams } diff --git a/Public/Array/Get-PfbArrayHttpPerformance.ps1 b/Public/Array/Get-PfbArrayHttpPerformance.ps1 index 24ceec8..179115d 100644 --- a/Public/Array/Get-PfbArrayHttpPerformance.ps1 +++ b/Public/Array/Get-PfbArrayHttpPerformance.ps1 @@ -46,9 +46,7 @@ function Get-PfbArrayHttpPerformance { process { $queryParams = @{} - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($StartTime) { $queryParams['start_time'] = $StartTime } if ($EndTime) { $queryParams['end_time'] = $EndTime } if ($Resolution) { $queryParams['resolution'] = $Resolution } diff --git a/Public/Array/Get-PfbArrayNfsPerformance.ps1 b/Public/Array/Get-PfbArrayNfsPerformance.ps1 index 2a69d14..5e8e576 100644 --- a/Public/Array/Get-PfbArrayNfsPerformance.ps1 +++ b/Public/Array/Get-PfbArrayNfsPerformance.ps1 @@ -46,9 +46,7 @@ function Get-PfbArrayNfsPerformance { process { $queryParams = @{} - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($StartTime) { $queryParams['start_time'] = $StartTime } if ($EndTime) { $queryParams['end_time'] = $EndTime } if ($Resolution) { $queryParams['resolution'] = $Resolution } diff --git a/Public/Array/Get-PfbArrayPerformanceReplication.ps1 b/Public/Array/Get-PfbArrayPerformanceReplication.ps1 index 0153eec..3c4ae1c 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 ) @@ -46,12 +52,11 @@ function Get-PfbArrayPerformanceReplication { process { $queryParams = @{} - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters 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/Public/Array/Get-PfbArrayS3Performance.ps1 b/Public/Array/Get-PfbArrayS3Performance.ps1 index ed69b4e..ad9a754 100644 --- a/Public/Array/Get-PfbArrayS3Performance.ps1 +++ b/Public/Array/Get-PfbArrayS3Performance.ps1 @@ -46,9 +46,7 @@ function Get-PfbArrayS3Performance { process { $queryParams = @{} - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($StartTime) { $queryParams['start_time'] = $StartTime } if ($EndTime) { $queryParams['end_time'] = $EndTime } if ($Resolution) { $queryParams['resolution'] = $Resolution } diff --git a/Public/Array/Get-PfbArraySshCaPolicy.ps1 b/Public/Array/Get-PfbArraySshCaPolicy.ps1 index 7bda0f4..0286150 100644 --- a/Public/Array/Get-PfbArraySshCaPolicy.ps1 +++ b/Public/Array/Get-PfbArraySshCaPolicy.ps1 @@ -45,12 +45,11 @@ function Get-PfbArraySshCaPolicy { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'arrays/ssh-certificate-authority-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Array/Get-PfbArrayStorageClass.ps1 b/Public/Array/Get-PfbArrayStorageClass.ps1 index 2e188fe..e60fe77 100644 --- a/Public/Array/Get-PfbArrayStorageClass.ps1 +++ b/Public/Array/Get-PfbArrayStorageClass.ps1 @@ -39,9 +39,7 @@ function Get-PfbArrayStorageClass { ) Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters try { Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'arrays/space/storage-classes' -QueryParams $queryParams -AutoPaginate diff --git a/Public/Array/Get-PfbArraySupportedTimeZone.ps1 b/Public/Array/Get-PfbArraySupportedTimeZone.ps1 index 4ca771f..80b9633 100644 --- a/Public/Array/Get-PfbArraySupportedTimeZone.ps1 +++ b/Public/Array/Get-PfbArraySupportedTimeZone.ps1 @@ -34,8 +34,6 @@ function Get-PfbArraySupportedTimeZone { ) Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'arrays/supported-time-zones' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Bucket/Get-PfbBucket.ps1 b/Public/Bucket/Get-PfbBucket.ps1 index 5ae1eb6..1d17945 100644 --- a/Public/Bucket/Get-PfbBucket.ps1 +++ b/Public/Bucket/Get-PfbBucket.ps1 @@ -65,12 +65,7 @@ function Get-PfbBucket { 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 } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds if ($Destroyed) { $queryParams['destroyed'] = 'true' } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'buckets' -QueryParams $queryParams -AutoPaginate diff --git a/Public/Bucket/Get-PfbBucketAccessPolicy.ps1 b/Public/Bucket/Get-PfbBucketAccessPolicy.ps1 index 59a73da..7f48690 100644 --- a/Public/Bucket/Get-PfbBucketAccessPolicy.ps1 +++ b/Public/Bucket/Get-PfbBucketAccessPolicy.ps1 @@ -87,15 +87,11 @@ function Get-PfbBucketAccessPolicy { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($allIds.Count -gt 0) { $queryParams['ids'] = $allIds -join ',' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds if ($allMemberNames.Count -gt 0) { $queryParams['member_names'] = $allMemberNames -join ',' } if ($allMemberIds.Count -gt 0) { $queryParams['member_ids'] = $allMemberIds -join ',' } if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } try { Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'buckets/bucket-access-policies' -QueryParams $queryParams -AutoPaginate diff --git a/Public/Bucket/Get-PfbBucketAccessPolicyRule.ps1 b/Public/Bucket/Get-PfbBucketAccessPolicyRule.ps1 index 7b0d079..00d04d3 100644 --- a/Public/Bucket/Get-PfbBucketAccessPolicyRule.ps1 +++ b/Public/Bucket/Get-PfbBucketAccessPolicyRule.ps1 @@ -74,13 +74,9 @@ function Get-PfbBucketAccessPolicyRule { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($allIds.Count -gt 0) { $queryParams['ids'] = $allIds -join ',' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds if ($allMemberNames.Count -gt 0) { $queryParams['member_names'] = $allMemberNames -join ',' } if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } try { Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'buckets/bucket-access-policies/rules' -QueryParams $queryParams -AutoPaginate diff --git a/Public/Bucket/Get-PfbBucketAuditFilter.ps1 b/Public/Bucket/Get-PfbBucketAuditFilter.ps1 index 7a7c8f3..e5fc182 100644 --- a/Public/Bucket/Get-PfbBucketAuditFilter.ps1 +++ b/Public/Bucket/Get-PfbBucketAuditFilter.ps1 @@ -59,11 +59,9 @@ function Get-PfbBucketAuditFilter { end { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($allMemberNames.Count -gt 0) { $queryParams['member_names'] = $allMemberNames -join ',' } if ($allMemberIds.Count -gt 0) { $queryParams['member_ids'] = $allMemberIds -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'buckets/audit-filters' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Bucket/Get-PfbBucketAuditFilterAction.ps1 b/Public/Bucket/Get-PfbBucketAuditFilterAction.ps1 index 2916c9d..3521381 100644 --- a/Public/Bucket/Get-PfbBucketAuditFilterAction.ps1 +++ b/Public/Bucket/Get-PfbBucketAuditFilterAction.ps1 @@ -38,9 +38,7 @@ function Get-PfbBucketAuditFilterAction { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'bucket-audit-filter-actions' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Bucket/Get-PfbBucketCorsPolicy.ps1 b/Public/Bucket/Get-PfbBucketCorsPolicy.ps1 index 088acbd..43ec6bf 100644 --- a/Public/Bucket/Get-PfbBucketCorsPolicy.ps1 +++ b/Public/Bucket/Get-PfbBucketCorsPolicy.ps1 @@ -81,14 +81,10 @@ function Get-PfbBucketCorsPolicy { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($allIds.Count -gt 0) { $queryParams['ids'] = $allIds -join ',' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds if ($allMemberNames.Count -gt 0) { $queryParams['member_names'] = $allMemberNames -join ',' } if ($allMemberIds.Count -gt 0) { $queryParams['member_ids'] = $allMemberIds -join ',' } if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } try { Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'buckets/cross-origin-resource-sharing-policies' -QueryParams $queryParams -AutoPaginate diff --git a/Public/Bucket/Get-PfbBucketCorsPolicyRule.ps1 b/Public/Bucket/Get-PfbBucketCorsPolicyRule.ps1 index c8f4d2e..9a04b21 100644 --- a/Public/Bucket/Get-PfbBucketCorsPolicyRule.ps1 +++ b/Public/Bucket/Get-PfbBucketCorsPolicyRule.ps1 @@ -74,13 +74,9 @@ function Get-PfbBucketCorsPolicyRule { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($allIds.Count -gt 0) { $queryParams['ids'] = $allIds -join ',' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds if ($allMemberNames.Count -gt 0) { $queryParams['member_names'] = $allMemberNames -join ',' } if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } try { Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'buckets/cross-origin-resource-sharing-policies/rules' -QueryParams $queryParams -AutoPaginate diff --git a/Public/Bucket/Get-PfbBucketPerformance.ps1 b/Public/Bucket/Get-PfbBucketPerformance.ps1 index 5f13740..27f1b1b 100644 --- a/Public/Bucket/Get-PfbBucketPerformance.ps1 +++ b/Public/Bucket/Get-PfbBucketPerformance.ps1 @@ -66,15 +66,10 @@ function Get-PfbBucketPerformance { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds if ($StartTime) { $queryParams['start_time'] = $StartTime } if ($EndTime) { $queryParams['end_time'] = $EndTime } if ($Resolution) { $queryParams['resolution'] = $Resolution } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'buckets/performance' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Bucket/Get-PfbBucketS3Performance.ps1 b/Public/Bucket/Get-PfbBucketS3Performance.ps1 index cb7f874..a551f9a 100644 --- a/Public/Bucket/Get-PfbBucketS3Performance.ps1 +++ b/Public/Bucket/Get-PfbBucketS3Performance.ps1 @@ -67,15 +67,10 @@ function Get-PfbBucketS3Performance { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds if ($StartTime) { $queryParams['start_time'] = $StartTime } if ($EndTime) { $queryParams['end_time'] = $EndTime } if ($Resolution) { $queryParams['resolution'] = $Resolution } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'buckets/s3-specific-performance' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Bucket/Update-PfbBucketAuditFilter.ps1 b/Public/Bucket/Update-PfbBucketAuditFilter.ps1 index a8bab2d..1d742b1 100644 --- a/Public/Bucket/Update-PfbBucketAuditFilter.ps1 +++ b/Public/Bucket/Update-PfbBucketAuditFilter.ps1 @@ -4,39 +4,89 @@ function Update-PfbBucketAuditFilter { Updates an existing bucket audit filter on the FlashBlade. .DESCRIPTION Modifies an existing audit filter for a bucket identified by member name - or member ID. Use the Attributes parameter to supply the properties to - update as a hashtable. + or member ID. Use the individual typed parameters for a specific audit + filter field, or the Attributes parameter to supply several properties + to update at once as a hashtable. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. -FilterNames is a query parameter and is orthogonal + to the body, so it can be combined freely with either. .PARAMETER MemberName - The name of the bucket whose audit filter should be updated. + The name of the bucket whose audit filter should be updated. Sent as the + 'bucket_names' query parameter. .PARAMETER MemberId - The ID of the bucket whose audit filter should be updated. + The ID of the bucket whose audit filter should be updated. Sent as the + 'bucket_ids' query parameter. + .PARAMETER FilterNames + The audit filter's own name(s), sent as the required 'names' query parameter. + When not explicitly supplied, this defaults to -MemberName (audit filters are + named after their owning bucket), so existing -MemberName-only callers keep + working. If you only have -MemberId and the filter's name differs from the + bucket's name, supply -FilterNames explicitly. + .PARAMETER Actions + The list of ops to be audited by this filter (e.g. 's3:GetObject'). No fixed + value set is documented in the spec, so this is not validated against a closed + list. + .PARAMETER S3Prefixes + The list of object name prefixes; ops in -Actions are audited only for objects + matching these prefixes. .PARAMETER Attributes - A hashtable of audit filter properties to update. - Only specified properties are changed. + A hashtable of audit filter properties to update. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbBucketAuditFilter -MemberName "mybucket" -Attributes @{ actions = @("s3.GetObject","s3.PutObject") } + Update-PfbBucketAuditFilter -MemberName "mybucket" -Actions "s3:GetObject","s3:PutObject" - Updates the audit filter for 'mybucket' to log GetObject and PutObject operations. + Updates the audit filter for 'mybucket' to log GetObject and PutObject operations + using typed parameters. .EXAMPLE - Update-PfbBucketAuditFilter -MemberId "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -Attributes @{ actions = @("s3.DeleteObject") } + Update-PfbBucketAuditFilter -MemberId "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -FilterNames "mybucket" -Actions "s3:DeleteObject" - Updates the audit filter by bucket ID. + Updates the audit filter by bucket ID, explicitly supplying the filter's own name. .EXAMPLE - Update-PfbBucketAuditFilter -MemberName "mybucket" -Attributes @{ enabled = $true } + Update-PfbBucketAuditFilter -MemberName "mybucket" -Attributes @{ actions = @("s3:GetObject","s3:PutObject") } - Enables the audit filter for the specified bucket. + Updates the audit filter for 'mybucket' using a raw attribute hashtable. + .NOTES + Wire-correctness fix: the real `PATCH /buckets/audit-filters` endpoint's query + parameters are `bucket_ids`/`bucket_names` (confirmed directly against the real + OpenAPI spec) -- not `member_ids`/`member_names`, which this cmdlet previously sent + and which do not exist on this endpoint at all. -MemberName/-MemberId now map to the + correct wire keys. Separately, the endpoint also requires a `names` query parameter + on every PATCH (identifying the audit-filter resource itself) -- new -FilterNames + parameter, defaulted from -MemberName for backward compatibility. `actions` and + `s3_prefixes` are the only two real body properties on `BucketAuditFilterPost`; + `bucket_ids`/`bucket_names`/`names` are query parameters only, never body fields. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByMemberNameIndividual')] param( - [Parameter(ParameterSetName = 'ByMemberName', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByMemberNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByMemberNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] [string]$MemberName, - [Parameter(ParameterSetName = 'ByMemberId', Mandatory)] + [Parameter(ParameterSetName = 'ByMemberIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByMemberIdAttributes', Mandatory)] [string]$MemberId, - [Parameter(Mandatory)] + # Constraint 17: `names` is a QUERY parameter, orthogonal to the request body, so + # -FilterNames is declared bare rather than scoped to the *Individual sets. + [Parameter()] + [string[]]$FilterNames, + + [Parameter(ParameterSetName = 'ByMemberNameIndividual')] + [Parameter(ParameterSetName = 'ByMemberIdIndividual')] + [string[]]$Actions, + + [Parameter(ParameterSetName = 'ByMemberNameIndividual')] + [Parameter(ParameterSetName = 'ByMemberIdIndividual')] + [string[]]$S3Prefixes, + + [Parameter(ParameterSetName = 'ByMemberNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByMemberIdAttributes', Mandatory)] [hashtable]$Attributes, [Parameter()] [PSCustomObject]$Array @@ -48,13 +98,39 @@ function Update-PfbBucketAuditFilter { process { $queryParams = @{} - if ($MemberName) { $queryParams['member_names'] = $MemberName } - if ($MemberId) { $queryParams['member_ids'] = $MemberId } + if ($MemberName) { $queryParams['bucket_names'] = $MemberName } + if ($MemberId) { $queryParams['bucket_ids'] = $MemberId } + + if ($PSBoundParameters.ContainsKey('FilterNames')) { + $queryParams['names'] = @($FilterNames) + } + elseif ($MemberName) { + # The real endpoint requires 'names' on every PATCH. Default to -MemberName + # (audit filters are named after their owning bucket) so existing + # -MemberName-only callers keep working without a breaking change. + $queryParams['names'] = $MemberName + } + else { + # -MemberId alone gives no value to infer -FilterNames from (the audit filter's + # own name cannot be derived from the bucket's ID). Fail fast client-side rather + # than send a request the array will reject anyway for missing the required + # 'names' query parameter. + throw "Update-PfbBucketAuditFilter: -FilterNames is required when -MemberId is used without -MemberName -- the audit filter's own name cannot be inferred from an ID alone. Supply -FilterNames explicitly." + } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('Actions')) { $body['actions'] = @($Actions) } + if ($PSBoundParameters.ContainsKey('S3Prefixes')) { $body['s3_prefixes'] = @($S3Prefixes) } + } $target = if ($MemberName) { $MemberName } else { $MemberId } if ($PSCmdlet.ShouldProcess($target, 'Update bucket audit filter')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'buckets/audit-filters' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'buckets/audit-filters' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Certificate/Get-PfbCertificate.ps1 b/Public/Certificate/Get-PfbCertificate.ps1 index e430f73..c3d5595 100644 --- a/Public/Certificate/Get-PfbCertificate.ps1 +++ b/Public/Certificate/Get-PfbCertificate.ps1 @@ -51,11 +51,7 @@ function Get-PfbCertificate { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'certificates' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Certificate/Get-PfbCertificateCertificateGroup.ps1 b/Public/Certificate/Get-PfbCertificateCertificateGroup.ps1 index 4b800f1..39a70fb 100644 --- a/Public/Certificate/Get-PfbCertificateCertificateGroup.ps1 +++ b/Public/Certificate/Get-PfbCertificateCertificateGroup.ps1 @@ -39,10 +39,9 @@ function Get-PfbCertificateCertificateGroup { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($CertificateName) { $queryParams['certificate_names'] = $CertificateName -join ',' } if ($CertificateGroupName) { $queryParams['certificate_group_names'] = $CertificateGroupName -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'certificates/certificate-groups' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Certificate/Get-PfbCertificateGroup.ps1 b/Public/Certificate/Get-PfbCertificateGroup.ps1 index 7716cce..6d5c859 100644 --- a/Public/Certificate/Get-PfbCertificateGroup.ps1 +++ b/Public/Certificate/Get-PfbCertificateGroup.ps1 @@ -55,11 +55,7 @@ function Get-PfbCertificateGroup { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'certificate-groups' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Certificate/Get-PfbCertificateGroupCertificate.ps1 b/Public/Certificate/Get-PfbCertificateGroupCertificate.ps1 index a5a6d1d..a775c26 100644 --- a/Public/Certificate/Get-PfbCertificateGroupCertificate.ps1 +++ b/Public/Certificate/Get-PfbCertificateGroupCertificate.ps1 @@ -55,11 +55,7 @@ function Get-PfbCertificateGroupCertificate { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'certificate-groups/certificates' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Certificate/Get-PfbCertificateGroupUse.ps1 b/Public/Certificate/Get-PfbCertificateGroupUse.ps1 index 38a4146..35e3b50 100644 --- a/Public/Certificate/Get-PfbCertificateGroupUse.ps1 +++ b/Public/Certificate/Get-PfbCertificateGroupUse.ps1 @@ -45,10 +45,7 @@ function Get-PfbCertificateGroupUse { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'certificate-groups/uses' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Certificate/Get-PfbCertificateUse.ps1 b/Public/Certificate/Get-PfbCertificateUse.ps1 index 453c350..9d619d1 100644 --- a/Public/Certificate/Get-PfbCertificateUse.ps1 +++ b/Public/Certificate/Get-PfbCertificateUse.ps1 @@ -45,10 +45,7 @@ function Get-PfbCertificateUse { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'certificates/uses' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Certificate/New-PfbCertificateCertificateGroup.ps1 b/Public/Certificate/New-PfbCertificateCertificateGroup.ps1 index 7480bb3..09d6cc9 100644 --- a/Public/Certificate/New-PfbCertificateCertificateGroup.ps1 +++ b/Public/Certificate/New-PfbCertificateCertificateGroup.ps1 @@ -7,8 +7,12 @@ function New-PfbCertificateCertificateGroup { certificate and a certificate group on the connected Pure Storage FlashBlade. .PARAMETER CertificateName The certificate name. + .PARAMETER CertificateId + The certificate ID. Sent as the 'certificate_ids' query parameter. .PARAMETER CertificateGroupName The certificate group name. + .PARAMETER CertificateGroupId + The certificate group ID. Sent as the 'certificate_group_ids' query parameter. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -28,6 +32,8 @@ function New-PfbCertificateCertificateGroup { param( [Parameter()] [string]$CertificateName, [Parameter()] [string]$CertificateGroupName, + [Parameter()] [string]$CertificateId, + [Parameter()] [string]$CertificateGroupId, [Parameter()] [PSCustomObject]$Array ) @@ -35,7 +41,9 @@ function New-PfbCertificateCertificateGroup { $queryParams = @{} if ($CertificateName) { $queryParams['certificate_names'] = $CertificateName } + if ($PSBoundParameters.ContainsKey('CertificateId')) { $queryParams['certificate_ids'] = $CertificateId } if ($CertificateGroupName) { $queryParams['certificate_group_names'] = $CertificateGroupName } + if ($PSBoundParameters.ContainsKey('CertificateGroupId')) { $queryParams['certificate_group_ids'] = $CertificateGroupId } $target = "${CertificateName}:${CertificateGroupName}" diff --git a/Public/Certificate/Update-PfbCertificate.ps1 b/Public/Certificate/Update-PfbCertificate.ps1 index ede8cd0..17ba7ca 100644 --- a/Public/Certificate/Update-PfbCertificate.ps1 +++ b/Public/Certificate/Update-PfbCertificate.ps1 @@ -1,25 +1,75 @@ function Update-PfbCertificate { <# .SYNOPSIS - Updates an existing SSL/TLS certificate on a Pure Storage FlashBlade. + Updates an existing SSL/TLS certificate on an Everpure FlashBlade. .DESCRIPTION The Update-PfbCertificate cmdlet modifies an existing certificate on the FlashBlade. - The certificate can be identified by name or by ID. The Attributes hashtable contains - the updated certificate data such as a renewed certificate body or a new private key. - This cmdlet supports pipeline input by property name and the ShouldProcess pattern. + The certificate can be identified by name or by ID. Use the individual typed + parameters for a specific certificate field, or the Attributes hashtable to supply + several updated certificate properties at once, such as a renewed certificate body + or a new private key. This cmdlet supports pipeline input by property name and the + ShouldProcess pattern. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. -GenerateNewKey is a query parameter and is orthogonal + to the body, so it can be combined freely with either. .PARAMETER Name The name of the certificate to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the certificate to update. + .PARAMETER GenerateNewKey + If set to $true, a new private key is generated when generating a new certificate + with the specified attributes. This may not be set to $true when importing a + certificate and private key, and may not be set to $false when generating a new + self-signed certificate to replace a certificate that was imported. Default is + $false. Sent as the 'generate_new_key' query parameter. + .PARAMETER Certificate + The text of the certificate. + .PARAMETER CertificateType + The type of certificate. Certificates of type 'appliance' are used by the array to + verify its identity to clients. Certificates of type 'external' are used by the + array to identify external servers to which it is configured to communicate. This + field may only be specified at certificate creation time. + .PARAMETER CommonName + The common name field listed in the certificate. + .PARAMETER Country + The country field listed in the certificate. + .PARAMETER Days + The number of days that the self-signed certificate is valid. + .PARAMETER Email + The email field listed in the certificate. + .PARAMETER IntermediateCertificate + Intermediate certificate chains. + .PARAMETER KeyAlgorithm + The key algorithm used to generate the certificate. + .PARAMETER KeySize + The size (in bits) of the private key for the certificate. + .PARAMETER Locality + The locality field listed in the certificate. + .PARAMETER Organization + The organization field listed in the certificate. + .PARAMETER OrganizationalUnit + The organizational unit field listed in the certificate. + .PARAMETER Passphrase + The passphrase used to encrypt -PrivateKey. + .PARAMETER PrivateKey + The text of the private key. + .PARAMETER State + The state/province field listed in the certificate. + .PARAMETER SubjectAlternativeNames + The alternative names that are secured by this certificate. .PARAMETER Attributes - A hashtable containing the updated certificate data. Supported keys include 'certificate', - 'key', and 'intermediate_certificate'. + A hashtable containing the updated certificate data. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbCertificate -Name 'web-cert' -Attributes @{ certificate = $newCertPem; key = $newKeyPem } + Update-PfbCertificate -Name 'web-cert' -Certificate $newCertPem -PrivateKey $newKeyPem - Updates the certificate named 'web-cert' with a renewed certificate and key. + Updates the certificate named 'web-cert' with a renewed certificate and private key + using typed parameters. .EXAMPLE Update-PfbCertificate -Id '10314f42-020d-7080-8013-000ddt400090' -Attributes @{ certificate = $certPem } @@ -29,11 +79,93 @@ function Update-PfbCertificate { Pipes a certificate object to update its intermediate certificate chain. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + # Constraint 17: generate_new_key is a QUERY parameter, orthogonal to the request + # body, so it is declared bare rather than scoped to the *Individual sets. Placing + # it there would make `-Attributes ... -GenerateNewKey` fail with + # AmbiguousParameterSet for no reason -- it has nothing to do with the body. + [Parameter()] + [Nullable[bool]]$GenerateNewKey, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Certificate, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [ValidateSet('appliance', 'external')] + [string]$CertificateType, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$CommonName, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Country, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [int]$Days, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Email, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$IntermediateCertificate, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$KeyAlgorithm, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [int]$KeySize, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Locality, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Organization, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$OrganizationalUnit, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Passphrase, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$PrivateKey, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$State, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$SubjectAlternativeNames, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -43,10 +175,39 @@ function Update-PfbCertificate { process { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } - if ($Id) { $queryParams['ids'] = $Id } + if ($Id) { $queryParams['ids'] = $Id } + if ($PSBoundParameters.ContainsKey('GenerateNewKey')) { $queryParams['generate_new_key'] = $GenerateNewKey } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # EVERY value-carrying parameter is guarded with $PSBoundParameters.ContainsKey, + # never truthiness -- an integer field (Days/KeySize) must be able to reach the + # wire as an explicit 0, and -SubjectAlternativeNames @() must be able to clear + # the list. + $body = @{} + if ($PSBoundParameters.ContainsKey('Certificate')) { $body['certificate'] = $Certificate } + if ($PSBoundParameters.ContainsKey('CertificateType')) { $body['certificate_type'] = $CertificateType } + if ($PSBoundParameters.ContainsKey('CommonName')) { $body['common_name'] = $CommonName } + if ($PSBoundParameters.ContainsKey('Country')) { $body['country'] = $Country } + if ($PSBoundParameters.ContainsKey('Days')) { $body['days'] = $Days } + if ($PSBoundParameters.ContainsKey('Email')) { $body['email'] = $Email } + if ($PSBoundParameters.ContainsKey('IntermediateCertificate')) { $body['intermediate_certificate'] = $IntermediateCertificate } + if ($PSBoundParameters.ContainsKey('KeyAlgorithm')) { $body['key_algorithm'] = $KeyAlgorithm } + if ($PSBoundParameters.ContainsKey('KeySize')) { $body['key_size'] = $KeySize } + if ($PSBoundParameters.ContainsKey('Locality')) { $body['locality'] = $Locality } + if ($PSBoundParameters.ContainsKey('Organization')) { $body['organization'] = $Organization } + if ($PSBoundParameters.ContainsKey('OrganizationalUnit')) { $body['organizational_unit'] = $OrganizationalUnit } + if ($PSBoundParameters.ContainsKey('Passphrase')) { $body['passphrase'] = $Passphrase } + if ($PSBoundParameters.ContainsKey('PrivateKey')) { $body['private_key'] = $PrivateKey } + if ($PSBoundParameters.ContainsKey('State')) { $body['state'] = $State } + if ($PSBoundParameters.ContainsKey('SubjectAlternativeNames')) { $body['subject_alternative_names'] = @($SubjectAlternativeNames) } + } + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update certificate')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'certificates' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'certificates' -Body $body -QueryParams $queryParams } } } diff --git a/Public/DataEviction/Get-PfbDataEvictionPolicy.ps1 b/Public/DataEviction/Get-PfbDataEvictionPolicy.ps1 index f101bc1..2150c8d 100644 --- a/Public/DataEviction/Get-PfbDataEvictionPolicy.ps1 +++ b/Public/DataEviction/Get-PfbDataEvictionPolicy.ps1 @@ -39,11 +39,7 @@ function Get-PfbDataEvictionPolicy { process { $queryParams = @{} - if ($Name) { $queryParams['names'] = $Name -join ',' } - if ($Id) { $queryParams['ids'] = $Id -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $Name -Ids $Id Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'data-eviction-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/DataEviction/Get-PfbDataEvictionPolicyFileSystem.ps1 b/Public/DataEviction/Get-PfbDataEvictionPolicyFileSystem.ps1 index 505841e..2900e96 100644 --- a/Public/DataEviction/Get-PfbDataEvictionPolicyFileSystem.ps1 +++ b/Public/DataEviction/Get-PfbDataEvictionPolicyFileSystem.ps1 @@ -43,9 +43,7 @@ function Get-PfbDataEvictionPolicyFileSystem { if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'data-eviction-policies/file-systems' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/DataEviction/Get-PfbDataEvictionPolicyMember.ps1 b/Public/DataEviction/Get-PfbDataEvictionPolicyMember.ps1 index 3848721..eb34fd8 100644 --- a/Public/DataEviction/Get-PfbDataEvictionPolicyMember.ps1 +++ b/Public/DataEviction/Get-PfbDataEvictionPolicyMember.ps1 @@ -45,9 +45,7 @@ function Get-PfbDataEvictionPolicyMember { if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'data-eviction-policies/members' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/DirectoryService/Get-PfbActiveDirectory.ps1 b/Public/DirectoryService/Get-PfbActiveDirectory.ps1 index 763e0e3..9e1597f 100644 --- a/Public/DirectoryService/Get-PfbActiveDirectory.ps1 +++ b/Public/DirectoryService/Get-PfbActiveDirectory.ps1 @@ -42,8 +42,7 @@ function Get-PfbActiveDirectory { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'active-directory' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/DirectoryService/Get-PfbDirectoryService.ps1 b/Public/DirectoryService/Get-PfbDirectoryService.ps1 index a03c164..e5f72e9 100644 --- a/Public/DirectoryService/Get-PfbDirectoryService.ps1 +++ b/Public/DirectoryService/Get-PfbDirectoryService.ps1 @@ -44,8 +44,7 @@ function Get-PfbDirectoryService { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'directory-services' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/DirectoryService/Get-PfbDirectoryServiceRole.ps1 b/Public/DirectoryService/Get-PfbDirectoryServiceRole.ps1 index 3debbf9..cb52dd5 100644 --- a/Public/DirectoryService/Get-PfbDirectoryServiceRole.ps1 +++ b/Public/DirectoryService/Get-PfbDirectoryServiceRole.ps1 @@ -54,11 +54,7 @@ function Get-PfbDirectoryServiceRole { } 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'directory-services/roles' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/DirectoryService/Get-PfbDirectoryServiceRoleManagementPolicy.ps1 b/Public/DirectoryService/Get-PfbDirectoryServiceRoleManagementPolicy.ps1 index c503015..9aa1658 100644 --- a/Public/DirectoryService/Get-PfbDirectoryServiceRoleManagementPolicy.ps1 +++ b/Public/DirectoryService/Get-PfbDirectoryServiceRoleManagementPolicy.ps1 @@ -51,8 +51,7 @@ function Get-PfbDirectoryServiceRoleManagementPolicy { if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'directory-services/roles/management-access-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/DirectoryService/Get-PfbLocalDirectoryService.ps1 b/Public/DirectoryService/Get-PfbLocalDirectoryService.ps1 index 39a02b0..c42a0a5 100644 --- a/Public/DirectoryService/Get-PfbLocalDirectoryService.ps1 +++ b/Public/DirectoryService/Get-PfbLocalDirectoryService.ps1 @@ -46,11 +46,7 @@ function Get-PfbLocalDirectoryService { } 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'directory-services/local/directory-services' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/DirectoryService/Get-PfbLocalGroup.ps1 b/Public/DirectoryService/Get-PfbLocalGroup.ps1 index 59efc3d..0b6e59c 100644 --- a/Public/DirectoryService/Get-PfbLocalGroup.ps1 +++ b/Public/DirectoryService/Get-PfbLocalGroup.ps1 @@ -45,11 +45,7 @@ function Get-PfbLocalGroup { } 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'directory-services/local/groups' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/DirectoryService/Get-PfbLocalGroupMember.ps1 b/Public/DirectoryService/Get-PfbLocalGroupMember.ps1 index 086f7f2..0f987e0 100644 --- a/Public/DirectoryService/Get-PfbLocalGroupMember.ps1 +++ b/Public/DirectoryService/Get-PfbLocalGroupMember.ps1 @@ -43,9 +43,7 @@ function Get-PfbLocalGroupMember { $queryParams = @{} if ($allGroups.Count -gt 0) { $queryParams['group_names'] = $allGroups -join ',' } if ($Member) { $queryParams['member_names'] = $Member -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'directory-services/local/groups/members' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/DirectoryService/Update-PfbActiveDirectory.ps1 b/Public/DirectoryService/Update-PfbActiveDirectory.ps1 index 4b87c17..13059d2 100644 --- a/Public/DirectoryService/Update-PfbActiveDirectory.ps1 +++ b/Public/DirectoryService/Update-PfbActiveDirectory.ps1 @@ -4,35 +4,117 @@ function Update-PfbActiveDirectory { Updates an existing Active Directory configuration on a FlashBlade array. .DESCRIPTION The Update-PfbActiveDirectory cmdlet modifies attributes of an existing Active Directory - configuration on the connected Pure Storage FlashBlade. The target configuration can be + configuration on the connected Everpure FlashBlade. The target configuration can be identified by name or ID. Common updates include changing directory servers, encryption types, and service principal names. Supports pipeline input and ShouldProcess. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the Active Directory configuration to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the Active Directory configuration to update. + .PARAMETER CaCertificate + Name of the Certificate Authority (CA) certificate that signed the certificates of the + configured servers, which is used to validate the authenticity of those servers. + .PARAMETER CaCertificateGroup + Name of a certificate group containing CA certificates that can be used to validate the + authenticity of the configured servers. + .PARAMETER DirectoryServers + A list of directory servers that will be used for lookups related to user authorization. + .PARAMETER EncryptionTypes + The encryption types that will be supported for use by clients for Kerberos authentication. + .PARAMETER Fqdns + A list of fully qualified domain names to use to register service principal names for the + machine account. + .PARAMETER GlobalCatalogServers + A list of global catalog servers that will be used for lookups related to user authorization. + .PARAMETER JoinOu + The relative distinguished name of the organizational unit in which the computer account + should be created when joining the domain. + .PARAMETER KerberosServers + A list of key distribution servers to use for Kerberos protocol. + .PARAMETER ServicePrincipalNames + A list of service principal names to register for the machine account, which can be used + for the creation of keys for Kerberos authentication. .PARAMETER Attributes - A hashtable of Active Directory attributes to modify. + A hashtable of Active Directory attributes to modify. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbActiveDirectory -Name "ad1" -Attributes @{ directory_servers = @("dc02.corp.example.com") } + Update-PfbActiveDirectory -Name "ad1" -DirectoryServers "dc02.corp.example.com" + + Updates the directory servers for the Active Directory configuration named "ad1" using + typed parameters. + .EXAMPLE + Update-PfbActiveDirectory -Id "10314f42-020d-7080-8013-000ddt400055" -EncryptionTypes "aes256-cts-hmac-sha1-96" - Updates the directory servers for the Active Directory configuration named "ad1". + Updates the encryption types for the Active Directory configuration identified by ID. .EXAMPLE - Update-PfbActiveDirectory -Name "ad-prod" -Attributes @{ computer_name = "FLASHBLADE02" } + Update-PfbActiveDirectory -Name "ad1" -Attributes @{ directory_servers = @("dc02.corp.example.com") } - Changes the computer account name for the "ad-prod" Active Directory configuration. + Updates the directory servers for the Active Directory configuration named "ad1" using + the raw -Attributes hashtable. .EXAMPLE Update-PfbActiveDirectory -Id "10314f42-020d-7080-8013-000ddt400055" -Attributes @{ encryption_types = @("aes256-cts-hmac-sha1-96") } Updates the encryption types for the Active Directory configuration identified by ID. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$CaCertificate, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$CaCertificateGroup, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$DirectoryServers, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [ValidateSet('aes256-cts-hmac-sha1-96', 'aes128-cts-hmac-sha1-96', 'arcfour-hmac')] + [string[]]$EncryptionTypes, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$Fqdns, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$GlobalCatalogServers, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$JoinOu, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$KerberosServers, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$ServicePrincipalNames, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -42,10 +124,30 @@ function Update-PfbActiveDirectory { process { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } - if ($Id) { $queryParams['ids'] = $Id } + if ($Id) { $queryParams['ids'] = $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Constraint 8(a): ca_certificate/ca_certificate_group are SCALAR references (item + # schema is {id, name, resource_type}), so the parameter is [string] and the + # assignment is INLINE -- constraint 7 forbids a $caCertRef local. + $body = @{} + if ($PSBoundParameters.ContainsKey('CaCertificate')) { $body['ca_certificate'] = @{ name = $CaCertificate } } + if ($PSBoundParameters.ContainsKey('CaCertificateGroup')) { $body['ca_certificate_group'] = @{ name = $CaCertificateGroup } } + if ($PSBoundParameters.ContainsKey('DirectoryServers')) { $body['directory_servers'] = $DirectoryServers } + if ($PSBoundParameters.ContainsKey('EncryptionTypes')) { $body['encryption_types'] = $EncryptionTypes } + if ($PSBoundParameters.ContainsKey('Fqdns')) { $body['fqdns'] = $Fqdns } + if ($PSBoundParameters.ContainsKey('GlobalCatalogServers')) { $body['global_catalog_servers'] = $GlobalCatalogServers } + if ($PSBoundParameters.ContainsKey('JoinOu')) { $body['join_ou'] = $JoinOu } + if ($PSBoundParameters.ContainsKey('KerberosServers')) { $body['kerberos_servers'] = $KerberosServers } + if ($PSBoundParameters.ContainsKey('ServicePrincipalNames')) { $body['service_principal_names'] = $ServicePrincipalNames } + } + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update Active Directory')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'active-directory' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'active-directory' -Body $body -QueryParams $queryParams } } } diff --git a/Public/DirectoryService/Update-PfbDirectoryServiceRole.ps1 b/Public/DirectoryService/Update-PfbDirectoryServiceRole.ps1 index 805ac2d..5291177 100644 --- a/Public/DirectoryService/Update-PfbDirectoryServiceRole.ps1 +++ b/Public/DirectoryService/Update-PfbDirectoryServiceRole.ps1 @@ -4,20 +4,31 @@ function Update-PfbDirectoryServiceRole { Updates an existing directory service role on the FlashBlade. .DESCRIPTION The Update-PfbDirectoryServiceRole cmdlet modifies attributes of an existing directory - service role on the connected Pure Storage FlashBlade. The target role can be identified + service role on the connected Everpure FlashBlade. The target role can be identified by name or ID. Supports ShouldProcess for confirmation prompts. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the directory service role to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the directory service role to update. + .PARAMETER Group + Common Name (CN) of the directory service group containing users with authority level of + the specified role name. + .PARAMETER GroupBase + Specifies where the configured group is located in the directory tree. .PARAMETER Attributes - A hashtable of role attributes to modify (e.g., group, group_base). + A hashtable of role attributes to modify (e.g., group, group_base). Mutually exclusive + with the individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbDirectoryServiceRole -Name "ad-admins" -Attributes @{ group = "CN=FB-SuperAdmins,OU=Groups,DC=corp,DC=example,DC=com" } + Update-PfbDirectoryServiceRole -Name "ad-admins" -Group "CN=FB-SuperAdmins,OU=Groups,DC=corp,DC=example,DC=com" - Updates the group mapping for the directory service role "ad-admins". + Updates the group mapping for the directory service role "ad-admins" using a typed parameter. .EXAMPLE Update-PfbDirectoryServiceRole -Id "abc12345-6789-0abc-def0-123456789abc" -Attributes @{ group_base = "DC=corp,DC=example,DC=com" } @@ -27,12 +38,29 @@ function Update-PfbDirectoryServiceRole { Shows what would happen if the role were updated without making changes. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Group, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$GroupBase, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) @@ -44,10 +72,20 @@ function Update-PfbDirectoryServiceRole { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + $target = if ($Name) { $Name } else { $Id } + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('Group')) { $body['group'] = $Group } + if ($PSBoundParameters.ContainsKey('GroupBase')) { $body['group_base'] = $GroupBase } + } + if ($PSCmdlet.ShouldProcess($target, 'Update directory service role')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'directory-services/roles' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'directory-services/roles' -Body $body -QueryParams $queryParams } } } diff --git a/Public/FileSystem/Get-PfbFileLock.ps1 b/Public/FileSystem/Get-PfbFileLock.ps1 index f91f598..a36a25b 100644 --- a/Public/FileSystem/Get-PfbFileLock.ps1 +++ b/Public/FileSystem/Get-PfbFileLock.ps1 @@ -70,12 +70,7 @@ function Get-PfbFileLock { 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 } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-systems/locks' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/FileSystem/Get-PfbFileLockClient.ps1 b/Public/FileSystem/Get-PfbFileLockClient.ps1 index 6239145..19cc9bf 100644 --- a/Public/FileSystem/Get-PfbFileLockClient.ps1 +++ b/Public/FileSystem/Get-PfbFileLockClient.ps1 @@ -70,12 +70,7 @@ function Get-PfbFileLockClient { 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 } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-systems/locks/clients' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/FileSystem/Get-PfbFileSystem.ps1 b/Public/FileSystem/Get-PfbFileSystem.ps1 index fae391a..c52fe01 100644 --- a/Public/FileSystem/Get-PfbFileSystem.ps1 +++ b/Public/FileSystem/Get-PfbFileSystem.ps1 @@ -73,12 +73,7 @@ function Get-PfbFileSystem { 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 } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds if ($Destroyed) { $queryParams['destroyed'] = 'true' } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-systems' -QueryParams $queryParams -AutoPaginate diff --git a/Public/FileSystem/Get-PfbFileSystemAuditPolicy.ps1 b/Public/FileSystem/Get-PfbFileSystemAuditPolicy.ps1 index 1bf8fbe..40ab060 100644 --- a/Public/FileSystem/Get-PfbFileSystemAuditPolicy.ps1 +++ b/Public/FileSystem/Get-PfbFileSystemAuditPolicy.ps1 @@ -53,9 +53,7 @@ function Get-PfbFileSystemAuditPolicy { if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-systems/audit-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/FileSystem/Get-PfbFileSystemExport.ps1 b/Public/FileSystem/Get-PfbFileSystemExport.ps1 index cbf24a6..6611998 100644 --- a/Public/FileSystem/Get-PfbFileSystemExport.ps1 +++ b/Public/FileSystem/Get-PfbFileSystemExport.ps1 @@ -70,12 +70,7 @@ function Get-PfbFileSystemExport { 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 } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-system-exports' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/FileSystem/Get-PfbFileSystemGroup.ps1 b/Public/FileSystem/Get-PfbFileSystemGroup.ps1 new file mode 100644 index 0000000..37f8662 --- /dev/null +++ b/Public/FileSystem/Get-PfbFileSystemGroup.ps1 @@ -0,0 +1,54 @@ +function Get-PfbFileSystemGroup { + <# + .SYNOPSIS + Retrieves groups known to file systems on a FlashBlade array. + .DESCRIPTION + The Get-PfbFileSystemGroup cmdlet returns group identities (GID/name) associated with + one or more file systems on the connected Pure Storage FlashBlade. Distinct from + Get-PfbUsageGroup (per-group usage statistics) -- this is identity/lookup data, useful + for building -Subject hashtables for New-PfbUserGroupQuotaPolicyRule. + .PARAMETER FileSystemName + One or more file system names to filter by. + .PARAMETER FileSystemId + One or more file system IDs to filter by. + .PARAMETER GroupName + One or more group names to filter by. + .PARAMETER GroupId + One or more numeric GIDs to filter by. + .PARAMETER Filter + A server-side filter expression to narrow results. + .PARAMETER Sort + Sort field and direction. + .PARAMETER Limit + Maximum number of items to return. + .PARAMETER TotalOnly + Return only the total count, not the items. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + Get-PfbFileSystemGroup -FileSystemName "fs-home" + #> + [CmdletBinding()] + param( + [Parameter()] [string[]]$FileSystemName, + [Parameter()] [string[]]$FileSystemId, + [Parameter()] [string[]]$GroupName, + [Parameter()] [string[]]$GroupId, + [Parameter()] [string]$Filter, + [Parameter()] [string]$Sort, + [Parameter()] [int]$Limit, + [Parameter()] [switch]$TotalOnly, + [Parameter()] [PSCustomObject]$Array + ) + + Assert-PfbConnection -Array ([ref]$Array) + + $queryParams = @{} + if ($FileSystemName) { $queryParams['file_system_names'] = $FileSystemName -join ',' } + if ($FileSystemId) { $queryParams['file_system_ids'] = $FileSystemId -join ',' } + if ($GroupName) { $queryParams['group_names'] = $GroupName -join ',' } + if ($GroupId) { $queryParams['gids'] = $GroupId -join ',' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-systems/groups' -QueryParams $queryParams -AutoPaginate +} diff --git a/Public/FileSystem/Get-PfbFileSystemGroupPerformance.ps1 b/Public/FileSystem/Get-PfbFileSystemGroupPerformance.ps1 index 70126a0..0786078 100644 --- a/Public/FileSystem/Get-PfbFileSystemGroupPerformance.ps1 +++ b/Public/FileSystem/Get-PfbFileSystemGroupPerformance.ps1 @@ -85,12 +85,9 @@ function Get-PfbFileSystemGroupPerformance { end { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($allNames.Count -gt 0) { $queryParams['file_system_names'] = $allNames -join ',' } if ($allIds.Count -gt 0) { $queryParams['file_system_ids'] = $allIds -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } if ($StartTime -gt 0) { $queryParams['start_time'] = $StartTime } if ($EndTime -gt 0) { $queryParams['end_time'] = $EndTime } if ($Resolution -gt 0) { $queryParams['resolution'] = $Resolution } diff --git a/Public/FileSystem/Get-PfbFileSystemGroupQuota.ps1 b/Public/FileSystem/Get-PfbFileSystemGroupQuota.ps1 new file mode 100644 index 0000000..673fe11 --- /dev/null +++ b/Public/FileSystem/Get-PfbFileSystemGroupQuota.ps1 @@ -0,0 +1,54 @@ +function Get-PfbFileSystemGroupQuota { + <# + .SYNOPSIS + Retrieves per-group quota usage and limits for file systems on a FlashBlade array. + .DESCRIPTION + The Get-PfbFileSystemGroupQuota cmdlet returns the effective user-group-quota-policy + usage/limit entries for individual groups on one or more file systems. Distinct from + Get-PfbQuotaGroup (the legacy per-filesystem purequota/purefs quota model) -- this + cmdlet reports quotas from the newer policy-based user-group-quota-policy model. + .PARAMETER FileSystemName + One or more file system names to filter by. + .PARAMETER FileSystemId + One or more file system IDs to filter by. + .PARAMETER GroupName + One or more group names to filter by. + .PARAMETER GroupId + One or more numeric GIDs to filter by. + .PARAMETER Filter + A server-side filter expression to narrow results. + .PARAMETER Sort + Sort field and direction. + .PARAMETER Limit + Maximum number of items to return. + .PARAMETER TotalOnly + Return only the total count, not the items. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + Get-PfbFileSystemGroupQuota -FileSystemName "fs-home" + #> + [CmdletBinding()] + param( + [Parameter()] [string[]]$FileSystemName, + [Parameter()] [string[]]$FileSystemId, + [Parameter()] [string[]]$GroupName, + [Parameter()] [string[]]$GroupId, + [Parameter()] [string]$Filter, + [Parameter()] [string]$Sort, + [Parameter()] [int]$Limit, + [Parameter()] [switch]$TotalOnly, + [Parameter()] [PSCustomObject]$Array + ) + + Assert-PfbConnection -Array ([ref]$Array) + + $queryParams = @{} + if ($FileSystemName) { $queryParams['file_system_names'] = $FileSystemName -join ',' } + if ($FileSystemId) { $queryParams['file_system_ids'] = $FileSystemId -join ',' } + if ($GroupName) { $queryParams['group_names'] = $GroupName -join ',' } + if ($GroupId) { $queryParams['gids'] = $GroupId -join ',' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-system-group-quotas' -QueryParams $queryParams -AutoPaginate +} diff --git a/Public/FileSystem/Get-PfbFileSystemPolicy.ps1 b/Public/FileSystem/Get-PfbFileSystemPolicy.ps1 index 036dc43..804517d 100644 --- a/Public/FileSystem/Get-PfbFileSystemPolicy.ps1 +++ b/Public/FileSystem/Get-PfbFileSystemPolicy.ps1 @@ -53,9 +53,7 @@ function Get-PfbFileSystemPolicy { if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-systems/policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/FileSystem/Get-PfbFileSystemSession.ps1 b/Public/FileSystem/Get-PfbFileSystemSession.ps1 index 2926679..015706e 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 @@ -18,14 +18,17 @@ 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 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. @@ -36,10 +39,6 @@ function Get-PfbFileSystemSession { [ValidateNotNullOrEmpty()] [string[]]$Name, - [Parameter(ParameterSetName = 'ById')] - [ValidateNotNullOrEmpty()] - [string[]]$Id, - [Parameter()] [string]$Filter, @@ -53,6 +52,10 @@ function Get-PfbFileSystemSession { [Parameter()] [switch]$TotalOnly, + [Parameter()] + [ValidateSet('nfs', 'smb')] + [string[]]$Protocol, + [Parameter()] [PSCustomObject]$Array ) @@ -60,22 +63,16 @@ 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 } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames + if ($Protocol) { $queryParams['protocols'] = $Protocol -join ',' } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-systems/sessions' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/FileSystem/Get-PfbFileSystemStorageClass.ps1 b/Public/FileSystem/Get-PfbFileSystemStorageClass.ps1 index 7815e0a..795c297 100644 --- a/Public/FileSystem/Get-PfbFileSystemStorageClass.ps1 +++ b/Public/FileSystem/Get-PfbFileSystemStorageClass.ps1 @@ -70,12 +70,7 @@ function Get-PfbFileSystemStorageClass { 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 } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds try { Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-systems/space/storage-classes' -QueryParams $queryParams -AutoPaginate diff --git a/Public/FileSystem/Get-PfbFileSystemUser.ps1 b/Public/FileSystem/Get-PfbFileSystemUser.ps1 new file mode 100644 index 0000000..10a3433 --- /dev/null +++ b/Public/FileSystem/Get-PfbFileSystemUser.ps1 @@ -0,0 +1,58 @@ +function Get-PfbFileSystemUser { + <# + .SYNOPSIS + Retrieves users known to file systems on a FlashBlade array. + .DESCRIPTION + The Get-PfbFileSystemUser cmdlet returns user identities (UID/name/SID) associated + with one or more file systems on the connected Pure Storage FlashBlade. Distinct from + Get-PfbUsageUser (per-user usage statistics) -- this is identity/lookup data, useful + for building -Subject hashtables for New-PfbUserGroupQuotaPolicyRule. + .PARAMETER FileSystemName + One or more file system names to filter by. + .PARAMETER FileSystemId + One or more file system IDs to filter by. + .PARAMETER UserName + One or more user names to filter by. + .PARAMETER UserId + One or more numeric UIDs to filter by. + .PARAMETER UserSid + One or more user SIDs to filter by. + .PARAMETER Filter + A server-side filter expression to narrow results. + .PARAMETER Sort + Sort field and direction. + .PARAMETER Limit + Maximum number of items to return. + .PARAMETER TotalOnly + Return only the total count, not the items. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + Get-PfbFileSystemUser -FileSystemName "fs-home" + #> + [CmdletBinding()] + param( + [Parameter()] [string[]]$FileSystemName, + [Parameter()] [string[]]$FileSystemId, + [Parameter()] [string[]]$UserName, + [Parameter()] [string[]]$UserId, + [Parameter()] [string[]]$UserSid, + [Parameter()] [string]$Filter, + [Parameter()] [string]$Sort, + [Parameter()] [int]$Limit, + [Parameter()] [switch]$TotalOnly, + [Parameter()] [PSCustomObject]$Array + ) + + Assert-PfbConnection -Array ([ref]$Array) + + $queryParams = @{} + if ($FileSystemName) { $queryParams['file_system_names'] = $FileSystemName -join ',' } + if ($FileSystemId) { $queryParams['file_system_ids'] = $FileSystemId -join ',' } + if ($UserName) { $queryParams['user_names'] = $UserName -join ',' } + if ($UserId) { $queryParams['uids'] = $UserId -join ',' } + if ($UserSid) { $queryParams['user_sids'] = $UserSid -join ',' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-systems/users' -QueryParams $queryParams -AutoPaginate +} diff --git a/Public/FileSystem/Get-PfbFileSystemUserGroupQuotaPolicy.ps1 b/Public/FileSystem/Get-PfbFileSystemUserGroupQuotaPolicy.ps1 new file mode 100644 index 0000000..e6bdc79 --- /dev/null +++ b/Public/FileSystem/Get-PfbFileSystemUserGroupQuotaPolicy.ps1 @@ -0,0 +1,55 @@ +function Get-PfbFileSystemUserGroupQuotaPolicy { + <# + .SYNOPSIS + Retrieves user/group quota policies attached to file systems on a FlashBlade array. + .DESCRIPTION + The Get-PfbFileSystemUserGroupQuotaPolicy cmdlet returns the mapping between file + systems and their attached user-group-quota policies. Same underlying relationship as + Get-PfbUserGroupQuotaPolicyFileSystem, queried from the file-system side. + .PARAMETER PolicyName + One or more policy names to filter by. + .PARAMETER PolicyId + One or more policy IDs to filter by. + .PARAMETER MemberName + One or more file system names to filter by. + .PARAMETER MemberId + One or more file system IDs to filter by. + .PARAMETER Filter + A server-side filter expression to narrow results. + .PARAMETER Sort + Sort field and direction. + .PARAMETER Limit + Maximum number of items to return. + .PARAMETER TotalOnly + Return only the total count, not the items. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + Get-PfbFileSystemUserGroupQuotaPolicy -MemberName "fs1" + + Retrieves the user-group-quota policies attached to file system 'fs1'. + #> + [CmdletBinding()] + param( + [Parameter()] [string[]]$PolicyName, + [Parameter()] [string[]]$PolicyId, + [Parameter()] [string[]]$MemberName, + [Parameter()] [string[]]$MemberId, + [Parameter()] [string]$Filter, + [Parameter()] [string]$Sort, + [Parameter()] [int]$Limit, + [Parameter()] [switch]$TotalOnly, + [Parameter()] [PSCustomObject]$Array + ) + + Assert-PfbConnection -Array ([ref]$Array) + + $queryParams = @{} + if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } + if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } + if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } + if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-systems/user-group-quota-policies' -QueryParams $queryParams -AutoPaginate +} diff --git a/Public/FileSystem/Get-PfbFileSystemUserPerformance.ps1 b/Public/FileSystem/Get-PfbFileSystemUserPerformance.ps1 index f05f91a..6bba204 100644 --- a/Public/FileSystem/Get-PfbFileSystemUserPerformance.ps1 +++ b/Public/FileSystem/Get-PfbFileSystemUserPerformance.ps1 @@ -85,12 +85,9 @@ function Get-PfbFileSystemUserPerformance { end { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($allNames.Count -gt 0) { $queryParams['file_system_names'] = $allNames -join ',' } if ($allIds.Count -gt 0) { $queryParams['file_system_ids'] = $allIds -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } if ($StartTime -gt 0) { $queryParams['start_time'] = $StartTime } if ($EndTime -gt 0) { $queryParams['end_time'] = $EndTime } if ($Resolution -gt 0) { $queryParams['resolution'] = $Resolution } diff --git a/Public/FileSystem/Get-PfbFileSystemUserQuota.ps1 b/Public/FileSystem/Get-PfbFileSystemUserQuota.ps1 new file mode 100644 index 0000000..35e7791 --- /dev/null +++ b/Public/FileSystem/Get-PfbFileSystemUserQuota.ps1 @@ -0,0 +1,58 @@ +function Get-PfbFileSystemUserQuota { + <# + .SYNOPSIS + Retrieves per-user quota usage and limits for file systems on a FlashBlade array. + .DESCRIPTION + The Get-PfbFileSystemUserQuota cmdlet returns the effective user-group-quota-policy + usage/limit entries for individual users on one or more file systems. Distinct from + Get-PfbQuotaUser (the legacy per-filesystem purequota/purefs quota model) -- this cmdlet + reports quotas from the newer policy-based user-group-quota-policy model. + .PARAMETER FileSystemName + One or more file system names to filter by. + .PARAMETER FileSystemId + One or more file system IDs to filter by. + .PARAMETER UserName + One or more user names to filter by. + .PARAMETER UserId + One or more numeric UIDs to filter by. + .PARAMETER UserSid + One or more user SIDs to filter by. + .PARAMETER Filter + A server-side filter expression to narrow results. + .PARAMETER Sort + Sort field and direction. + .PARAMETER Limit + Maximum number of items to return. + .PARAMETER TotalOnly + Return only the total count, not the items. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + Get-PfbFileSystemUserQuota -FileSystemName "fs-home" + #> + [CmdletBinding()] + param( + [Parameter()] [string[]]$FileSystemName, + [Parameter()] [string[]]$FileSystemId, + [Parameter()] [string[]]$UserName, + [Parameter()] [string[]]$UserId, + [Parameter()] [string[]]$UserSid, + [Parameter()] [string]$Filter, + [Parameter()] [string]$Sort, + [Parameter()] [int]$Limit, + [Parameter()] [switch]$TotalOnly, + [Parameter()] [PSCustomObject]$Array + ) + + Assert-PfbConnection -Array ([ref]$Array) + + $queryParams = @{} + if ($FileSystemName) { $queryParams['file_system_names'] = $FileSystemName -join ',' } + if ($FileSystemId) { $queryParams['file_system_ids'] = $FileSystemId -join ',' } + if ($UserName) { $queryParams['user_names'] = $UserName -join ',' } + if ($UserId) { $queryParams['uids'] = $UserId -join ',' } + if ($UserSid) { $queryParams['user_sids'] = $UserSid -join ',' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-system-user-quotas' -QueryParams $queryParams -AutoPaginate +} diff --git a/Public/FileSystem/Get-PfbFileSystemWormPolicy.ps1 b/Public/FileSystem/Get-PfbFileSystemWormPolicy.ps1 index 8426963..6587be5 100644 --- a/Public/FileSystem/Get-PfbFileSystemWormPolicy.ps1 +++ b/Public/FileSystem/Get-PfbFileSystemWormPolicy.ps1 @@ -54,9 +54,7 @@ function Get-PfbFileSystemWormPolicy { if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-systems/worm-data-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/FileSystem/Get-PfbOpenFile.ps1 b/Public/FileSystem/Get-PfbOpenFile.ps1 index 3e27b53..cf2ead0 100644 --- a/Public/FileSystem/Get-PfbOpenFile.ps1 +++ b/Public/FileSystem/Get-PfbOpenFile.ps1 @@ -70,12 +70,7 @@ function Get-PfbOpenFile { 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 } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-systems/open-files' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/FileSystem/New-PfbFileSystemUserGroupQuotaPolicy.ps1 b/Public/FileSystem/New-PfbFileSystemUserGroupQuotaPolicy.ps1 new file mode 100644 index 0000000..2456ce4 --- /dev/null +++ b/Public/FileSystem/New-PfbFileSystemUserGroupQuotaPolicy.ps1 @@ -0,0 +1,58 @@ +function New-PfbFileSystemUserGroupQuotaPolicy { + <# + .SYNOPSIS + Attaches a user/group quota policy to a file system on a FlashBlade array. + .DESCRIPTION + The New-PfbFileSystemUserGroupQuotaPolicy cmdlet associates an existing + user-group-quota policy with a file system, queried/invoked from the file-system side. + Functionally identical to New-PfbUserGroupQuotaPolicyFileSystem. + .PARAMETER PolicyName + The name of the policy to attach. + .PARAMETER PolicyId + The ID of the policy to attach. + .PARAMETER MemberName + The name of the file system to attach the policy to. + .PARAMETER MemberId + The ID of the file system to attach the policy to. + .PARAMETER DeleteExistingUserGroupQuotaSettings + If set, deletes the file system's existing legacy purequota/purefs quota settings + when attaching this policy. + .PARAMETER IgnoreUsage + If set, existing user/group usage on the file system is not checked against the + policy's rules. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + New-PfbFileSystemUserGroupQuotaPolicy -PolicyName "quota-pol-1" -MemberName "fs1" + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + param( + [Parameter()] [string]$PolicyName, + [Parameter()] [string]$PolicyId, + [Parameter()] [string]$MemberName, + [Parameter()] [string]$MemberId, + [Parameter()] [switch]$DeleteExistingUserGroupQuotaSettings, + [Parameter()] [switch]$IgnoreUsage, + [Parameter()] [PSCustomObject]$Array + ) + + if (-not $PolicyName -and -not $PolicyId) { throw 'You must supply either -PolicyName or -PolicyId.' } + if (-not $MemberName -and -not $MemberId) { throw 'You must supply either -MemberName or -MemberId.' } + + Assert-PfbConnection -Array ([ref]$Array) + + $queryParams = @{} + if ($PolicyName) { $queryParams['policy_names'] = $PolicyName } + if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId } + if ($MemberName) { $queryParams['member_names'] = $MemberName } + if ($MemberId) { $queryParams['member_ids'] = $MemberId } + if ($DeleteExistingUserGroupQuotaSettings) { $queryParams['delete_existing_user_group_quota_settings'] = 'true' } + if ($IgnoreUsage) { $queryParams['ignore_usage'] = 'true' } + + $target = if ($MemberName) { $MemberName } else { $MemberId } + $policy = if ($PolicyName) { $PolicyName } else { $PolicyId } + + if ($PSCmdlet.ShouldProcess($target, "Attach user-group-quota policy '$policy'")) { + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'file-systems/user-group-quota-policies' -QueryParams $queryParams + } +} diff --git a/Public/FileSystem/Remove-PfbFileSystemSession.ps1 b/Public/FileSystem/Remove-PfbFileSystemSession.ps1 index 064a98b..a727af1 100644 --- a/Public/FileSystem/Remove-PfbFileSystemSession.ps1 +++ b/Public/FileSystem/Remove-PfbFileSystemSession.ps1 @@ -1,25 +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 + 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')] @@ -27,8 +52,12 @@ function Remove-PfbFileSystemSession { [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] - [string]$Id, + [Parameter(ParameterSetName = 'ByProtocol', Mandatory)] + [ValidateSet('nfs', 'smb')] + [string[]]$Protocol, + + [Parameter()] + [switch]$Force, [Parameter()] [PSCustomObject]$Array @@ -40,12 +69,23 @@ function Remove-PfbFileSystemSession { process { $queryParams = @{} - if ($Name) { $queryParams['names'] = $Name } - if ($Id) { $queryParams['ids'] = $Id } + $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/Public/FileSystem/Remove-PfbFileSystemUserGroupQuotaPolicy.ps1 b/Public/FileSystem/Remove-PfbFileSystemUserGroupQuotaPolicy.ps1 new file mode 100644 index 0000000..9fc7508 --- /dev/null +++ b/Public/FileSystem/Remove-PfbFileSystemUserGroupQuotaPolicy.ps1 @@ -0,0 +1,58 @@ +function Remove-PfbFileSystemUserGroupQuotaPolicy { + <# + .SYNOPSIS + Detaches a user/group quota policy from a file system on a FlashBlade array. + .DESCRIPTION + Functionally identical to Remove-PfbUserGroupQuotaPolicyFileSystem, invoked from the + file-system side. + .PARAMETER PolicyName + The name of the policy to detach. + .PARAMETER PolicyId + The ID of the policy to detach. + .PARAMETER MemberName + The name of the file system to detach the policy from. + .PARAMETER MemberId + The ID of the file system to detach the policy from. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + Remove-PfbFileSystemUserGroupQuotaPolicy -PolicyName "quota-pol-1" -MemberName "fs1" + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] + param( + [Parameter()] + [ValidateScript({ Assert-PfbSafeName $_ })] + [string]$PolicyName, + + [Parameter()] [string]$PolicyId, + + [Parameter()] + [ValidateScript({ Assert-PfbSafeName $_ })] + [string]$MemberName, + + [Parameter()] [string]$MemberId, + [Parameter()] [PSCustomObject]$Array + ) + + if (-not $PolicyName -and -not $PolicyId) { + throw 'You must supply either -PolicyName or -PolicyId.' + } + if (-not $MemberName -and -not $MemberId) { + throw 'You must supply either -MemberName or -MemberId.' + } + + Assert-PfbConnection -Array ([ref]$Array) + + $queryParams = @{} + if ($PolicyName) { $queryParams['policy_names'] = $PolicyName } + if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId } + if ($MemberName) { $queryParams['member_names'] = $MemberName } + if ($MemberId) { $queryParams['member_ids'] = $MemberId } + + $policy = if ($PolicyName) { $PolicyName } else { $PolicyId } + $member = if ($MemberName) { $MemberName } else { $MemberId } + + if ($PSCmdlet.ShouldProcess($member, "Detach user-group-quota policy '$policy'")) { + Invoke-PfbApiRequest -Array $Array -Method DELETE -Endpoint 'file-systems/user-group-quota-policies' -QueryParams $queryParams + } +} diff --git a/Public/FileSystem/Update-PfbFileSystemExport.ps1 b/Public/FileSystem/Update-PfbFileSystemExport.ps1 index 81d23a2..3ea23d8 100644 --- a/Public/FileSystem/Update-PfbFileSystemExport.ps1 +++ b/Public/FileSystem/Update-PfbFileSystemExport.ps1 @@ -5,17 +5,34 @@ function Update-PfbFileSystemExport { .DESCRIPTION Modifies file system export attributes such as rules, enabled state, and other export properties. Identify the export by name or ID. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the file system export to update. .PARAMETER Id The ID of the file system export to update. + .PARAMETER ExportName + The name of the export used by clients to mount the file system. + .PARAMETER Member + Name of the file system the policy is applied to. + .PARAMETER Policy + Name of the NFS export policy or SMB client policy. + .PARAMETER Server + Name of the server the export will be visible on. + .PARAMETER SharePolicy + Name of the SMB share policy (only used for SMB). .PARAMETER Attributes - A hashtable of attributes to update on the export. + A hashtable of attributes to update on the export. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, uses the default connection. .EXAMPLE - Update-PfbFileSystemExport -Name "export1" -Attributes @{ rules = "*(ro)" } - Updates the export rules to read-only. + Update-PfbFileSystemExport -Name "export1" -ExportName "/fs1" + + Renames the export used by clients to mount the file system, using a typed parameter. .EXAMPLE Update-PfbFileSystemExport -Id "abc-123" -Attributes @{ enabled = $false } Disables the specified export by ID. @@ -23,15 +40,39 @@ function Update-PfbFileSystemExport { Update-PfbFileSystemExport -Name "export1" -Attributes @{ rules = "10.0.0.0/8(rw,no_root_squash)" } Updates the export with network-restricted rules. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [string]$Id, - [Parameter()] + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$ExportName, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Member, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Policy, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Server, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$SharePolicy, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [hashtable]$Attributes, [Parameter()] @@ -43,7 +84,20 @@ function Update-PfbFileSystemExport { } process { - $body = if ($Attributes) { $Attributes } else { @{} } + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Constraint 8(a): member/policy/server/share_policy are all SCALAR references + # (item schema is {id, name, resource_type}), so the parameter is [string] and + # the assignment is INLINE -- constraint 7 forbids a local like $memberRef. + $body = @{} + if ($PSBoundParameters.ContainsKey('ExportName')) { $body['export_name'] = $ExportName } + if ($PSBoundParameters.ContainsKey('Member')) { $body['member'] = @{ name = $Member } } + if ($PSBoundParameters.ContainsKey('Policy')) { $body['policy'] = @{ name = $Policy } } + if ($PSBoundParameters.ContainsKey('Server')) { $body['server'] = @{ name = $Server } } + if ($PSBoundParameters.ContainsKey('SharePolicy')) { $body['share_policy'] = @{ name = $SharePolicy } } + } $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } diff --git a/Public/FileSystemSnapshot/Get-PfbFileSystemSnapshot.ps1 b/Public/FileSystemSnapshot/Get-PfbFileSystemSnapshot.ps1 index 9abe705..337ce05 100644 --- a/Public/FileSystemSnapshot/Get-PfbFileSystemSnapshot.ps1 +++ b/Public/FileSystemSnapshot/Get-PfbFileSystemSnapshot.ps1 @@ -68,13 +68,8 @@ function Get-PfbFileSystemSnapshot { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($allIds.Count -gt 0) { $queryParams['ids'] = $allIds -join ',' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds if ($SourceName) { $queryParams['source_names'] = $SourceName } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } if ($Destroyed) { $queryParams['destroyed'] = 'true' } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-system-snapshots' -QueryParams $queryParams -AutoPaginate diff --git a/Public/FileSystemSnapshot/Get-PfbFileSystemSnapshotPolicy.ps1 b/Public/FileSystemSnapshot/Get-PfbFileSystemSnapshotPolicy.ps1 index a57c06a..79be5df 100644 --- a/Public/FileSystemSnapshot/Get-PfbFileSystemSnapshotPolicy.ps1 +++ b/Public/FileSystemSnapshot/Get-PfbFileSystemSnapshotPolicy.ps1 @@ -53,9 +53,7 @@ function Get-PfbFileSystemSnapshotPolicy { if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-system-snapshots/policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/FileSystemSnapshot/Get-PfbFileSystemSnapshotTransfer.ps1 b/Public/FileSystemSnapshot/Get-PfbFileSystemSnapshotTransfer.ps1 index e4ef94d..7b09e7c 100644 --- a/Public/FileSystemSnapshot/Get-PfbFileSystemSnapshotTransfer.ps1 +++ b/Public/FileSystemSnapshot/Get-PfbFileSystemSnapshotTransfer.ps1 @@ -66,12 +66,7 @@ function Get-PfbFileSystemSnapshotTransfer { 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 } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-system-snapshots/transfer' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Hardware/Get-PfbBlade.ps1 b/Public/Hardware/Get-PfbBlade.ps1 index 66199e6..25feaee 100644 --- a/Public/Hardware/Get-PfbBlade.ps1 +++ b/Public/Hardware/Get-PfbBlade.ps1 @@ -37,11 +37,7 @@ function Get-PfbBlade { } 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'blades' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Hardware/Get-PfbDrive.ps1 b/Public/Hardware/Get-PfbDrive.ps1 index 83be12f..7fac085 100644 --- a/Public/Hardware/Get-PfbDrive.ps1 +++ b/Public/Hardware/Get-PfbDrive.ps1 @@ -37,11 +37,7 @@ function Get-PfbDrive { } 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'drives' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Hardware/Get-PfbHardware.ps1 b/Public/Hardware/Get-PfbHardware.ps1 index 700be6b..6816113 100644 --- a/Public/Hardware/Get-PfbHardware.ps1 +++ b/Public/Hardware/Get-PfbHardware.ps1 @@ -37,11 +37,7 @@ function Get-PfbHardware { } 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'hardware' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Hardware/Get-PfbHardwareConnector.ps1 b/Public/Hardware/Get-PfbHardwareConnector.ps1 index 94609bc..ffdb678 100644 --- a/Public/Hardware/Get-PfbHardwareConnector.ps1 +++ b/Public/Hardware/Get-PfbHardwareConnector.ps1 @@ -37,11 +37,7 @@ function Get-PfbHardwareConnector { } 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'hardware-connectors' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Hardware/Get-PfbHardwareConnectorPerformance.ps1 b/Public/Hardware/Get-PfbHardwareConnectorPerformance.ps1 index be9e0d7..cd81f87 100644 --- a/Public/Hardware/Get-PfbHardwareConnectorPerformance.ps1 +++ b/Public/Hardware/Get-PfbHardwareConnectorPerformance.ps1 @@ -55,10 +55,7 @@ function Get-PfbHardwareConnectorPerformance { } end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames if ($StartTime) { $queryParams['start_time'] = $StartTime } if ($EndTime) { $queryParams['end_time'] = $EndTime } if ($Resolution) { $queryParams['resolution'] = $Resolution } diff --git a/Public/Hardware/Get-PfbHardwareTemperature.ps1 b/Public/Hardware/Get-PfbHardwareTemperature.ps1 index 614a34e..1cbbc75 100644 --- a/Public/Hardware/Get-PfbHardwareTemperature.ps1 +++ b/Public/Hardware/Get-PfbHardwareTemperature.ps1 @@ -46,6 +46,10 @@ function Get-PfbHardwareTemperature { $queryParams = @{} if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } + # Intentionally NOT migrated to Add-PfbCommonQueryParams (issue #33): -Limit here is a + # client-side post-filter applied after the temperature filtering below. The helper would + # unconditionally send limit= server-side, changing results. + $hardware = Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'hardware' -QueryParams $queryParams -AutoPaginate # Filter to components that have temperature data diff --git a/Public/Hardware/Update-PfbHardware.ps1 b/Public/Hardware/Update-PfbHardware.ps1 index bbe148c..b530b96 100644 --- a/Public/Hardware/Update-PfbHardware.ps1 +++ b/Public/Hardware/Update-PfbHardware.ps1 @@ -4,21 +4,30 @@ function Update-PfbHardware { Updates a hardware component on a FlashBlade array. .DESCRIPTION The Update-PfbHardware cmdlet modifies attributes of a hardware component on the - connected Pure Storage FlashBlade. The component can be identified by name or ID. + connected Everpure FlashBlade. The component can be identified by name or ID. Common updates include enabling or disabling visual identification LEDs. Supports ShouldProcess for -WhatIf and -Confirm. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name - The name of the hardware component to update. + The name of the hardware component to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the hardware component to update. + .PARAMETER IdentifyEnabled + State of an LED used to visually identify the component. .PARAMETER Attributes - A hashtable of hardware attributes to modify, such as identify_enabled. + A hashtable of hardware attributes to modify. Mutually exclusive with the individual + typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbHardware -Name "CH1.FB1" -Attributes @{ identify_enabled = $true } + Update-PfbHardware -Name "CH1.FB1" -IdentifyEnabled $true - Enables the identification LED on the specified hardware component. + Enables the identification LED on the specified hardware component using a typed + parameter. .EXAMPLE Update-PfbHardware -Id "10314f42-020d-7080-8013-000ddt400001" -Attributes @{ identify_enabled = $false } @@ -28,11 +37,25 @@ function Update-PfbHardware { Shows what would happen without applying the change. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [Nullable[bool]]$IdentifyEnabled, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -42,9 +65,20 @@ function Update-PfbHardware { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Every value-carrying parameter is guarded by ContainsKey, never by truthiness -- + # see the canonical explanation in Update-PfbAdmin.ps1. + $body = @{} + if ($PSBoundParameters.ContainsKey('IdentifyEnabled')) { $body['identify_enabled'] = $IdentifyEnabled } + } + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update hardware')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'hardware' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'hardware' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Hardware/Update-PfbHardwareConnector.ps1 b/Public/Hardware/Update-PfbHardwareConnector.ps1 index 826f792..6da96ca 100644 --- a/Public/Hardware/Update-PfbHardwareConnector.ps1 +++ b/Public/Hardware/Update-PfbHardwareConnector.ps1 @@ -4,20 +4,34 @@ function Update-PfbHardwareConnector { Updates a hardware connector on a FlashBlade array. .DESCRIPTION The Update-PfbHardwareConnector cmdlet modifies attributes of a hardware connector on - the connected Pure Storage FlashBlade. The connector can be identified by name or ID. + the connected Everpure FlashBlade. The connector can be identified by name or ID. Supports ShouldProcess for -WhatIf and -Confirm. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name - The name of the hardware connector to update. + The name of the hardware connector to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the hardware connector to update. + .PARAMETER LaneSpeed + Configured speed of each lane in the connector in bits-per-second. + .PARAMETER LanesPerPort + Configured number of lanes comprising each port in the connector. + .PARAMETER PortCount + Configured number of ports in the connector (1/2/4 for QSFP). + .PARAMETER PortSpeed + Configured speed of each port in the connector in bits-per-second. .PARAMETER Attributes - A hashtable of connector attributes to modify. + A hashtable of connector attributes to modify. Mutually exclusive with the individual + typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbHardwareConnector -Name "CH1.FM1.ETH1" -Attributes @{ port_speed = 40000000000 } + Update-PfbHardwareConnector -Name "CH1.FM1.ETH1" -PortSpeed 40000000000 - Updates the port speed on the specified hardware connector. + Updates the port speed on the specified hardware connector using a typed parameter. .EXAMPLE Update-PfbHardwareConnector -Id "10314f42-020d-7080-8013-000ddt400088" -Attributes @{ enabled = $true } @@ -27,11 +41,37 @@ function Update-PfbHardwareConnector { Shows what would happen without applying the change. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$LaneSpeed, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$LanesPerPort, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$PortCount, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$PortSpeed, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -41,9 +81,24 @@ function Update-PfbHardwareConnector { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Every value-carrying parameter is guarded by ContainsKey, never by truthiness -- + # see the canonical explanation in Update-PfbAdmin.ps1. All four fields here are + # integers, so each needs an explicit-0 test (constraint 2). + $body = @{} + if ($PSBoundParameters.ContainsKey('LaneSpeed')) { $body['lane_speed'] = $LaneSpeed } + if ($PSBoundParameters.ContainsKey('LanesPerPort')) { $body['lanes_per_port'] = $LanesPerPort } + if ($PSBoundParameters.ContainsKey('PortCount')) { $body['port_count'] = $PortCount } + if ($PSBoundParameters.ContainsKey('PortSpeed')) { $body['port_speed'] = $PortSpeed } + } + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update hardware connector')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'hardware-connectors' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'hardware-connectors' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Misc/Get-PfbKeytab.ps1 b/Public/Misc/Get-PfbKeytab.ps1 index b2053e5..4aef21c 100644 --- a/Public/Misc/Get-PfbKeytab.ps1 +++ b/Public/Misc/Get-PfbKeytab.ps1 @@ -50,11 +50,7 @@ function Get-PfbKeytab { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'keytabs' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Misc/Get-PfbKeytabDownload.ps1 b/Public/Misc/Get-PfbKeytabDownload.ps1 index 7ac79c6..ba1bc29 100644 --- a/Public/Misc/Get-PfbKeytabDownload.ps1 +++ b/Public/Misc/Get-PfbKeytabDownload.ps1 @@ -55,11 +55,7 @@ function Get-PfbKeytabDownload { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'keytabs/download' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Misc/Get-PfbKmip.ps1 b/Public/Misc/Get-PfbKmip.ps1 index 3dcf928..609947f 100644 --- a/Public/Misc/Get-PfbKmip.ps1 +++ b/Public/Misc/Get-PfbKmip.ps1 @@ -55,11 +55,7 @@ function Get-PfbKmip { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'kmip' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Misc/Get-PfbLag.ps1 b/Public/Misc/Get-PfbLag.ps1 index cdc6de2..eacf583 100644 --- a/Public/Misc/Get-PfbLag.ps1 +++ b/Public/Misc/Get-PfbLag.ps1 @@ -50,11 +50,7 @@ function Get-PfbLag { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'link-aggregation-groups' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Misc/Get-PfbLegalHold.ps1 b/Public/Misc/Get-PfbLegalHold.ps1 index 078a12b..2aae654 100644 --- a/Public/Misc/Get-PfbLegalHold.ps1 +++ b/Public/Misc/Get-PfbLegalHold.ps1 @@ -58,11 +58,7 @@ function Get-PfbLegalHold { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'legal-holds' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Misc/Get-PfbLegalHoldEntity.ps1 b/Public/Misc/Get-PfbLegalHoldEntity.ps1 index fdbeb15..6413b21 100644 --- a/Public/Misc/Get-PfbLegalHoldEntity.ps1 +++ b/Public/Misc/Get-PfbLegalHoldEntity.ps1 @@ -58,13 +58,11 @@ function Get-PfbLegalHoldEntity { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($HoldName) { $queryParams['hold_names'] = $HoldName -join ',' } if ($HoldId) { $queryParams['hold_ids'] = $HoldId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'legal-holds/held-entities' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Misc/Get-PfbLifecycleRule.ps1 b/Public/Misc/Get-PfbLifecycleRule.ps1 index d0fb94e..2980e14 100644 --- a/Public/Misc/Get-PfbLifecycleRule.ps1 +++ b/Public/Misc/Get-PfbLifecycleRule.ps1 @@ -53,12 +53,8 @@ function Get-PfbLifecycleRule { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($allIds.Count -gt 0) { $queryParams['ids'] = $allIds -join ',' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds if ($BucketName) { $queryParams['bucket_names'] = $BucketName } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'lifecycle-rules' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Misc/Get-PfbMaintenanceWindow.ps1 b/Public/Misc/Get-PfbMaintenanceWindow.ps1 index 5499d13..f399834 100644 --- a/Public/Misc/Get-PfbMaintenanceWindow.ps1 +++ b/Public/Misc/Get-PfbMaintenanceWindow.ps1 @@ -51,11 +51,7 @@ function Get-PfbMaintenanceWindow { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'maintenance-windows' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Misc/Get-PfbPublicKeyUse.ps1 b/Public/Misc/Get-PfbPublicKeyUse.ps1 index f200b5e..0b7f04c 100644 --- a/Public/Misc/Get-PfbPublicKeyUse.ps1 +++ b/Public/Misc/Get-PfbPublicKeyUse.ps1 @@ -45,10 +45,7 @@ function Get-PfbPublicKeyUse { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'public-keys/uses' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Misc/Get-PfbResiliencyGroup.ps1 b/Public/Misc/Get-PfbResiliencyGroup.ps1 index 075d4b4..d3c1a23 100644 --- a/Public/Misc/Get-PfbResiliencyGroup.ps1 +++ b/Public/Misc/Get-PfbResiliencyGroup.ps1 @@ -50,11 +50,7 @@ function Get-PfbResiliencyGroup { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds try { Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'resiliency-groups' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Misc/Get-PfbResourceAccess.ps1 b/Public/Misc/Get-PfbResourceAccess.ps1 index e71e5e7..3214fe8 100644 --- a/Public/Misc/Get-PfbResourceAccess.ps1 +++ b/Public/Misc/Get-PfbResourceAccess.ps1 @@ -51,11 +51,7 @@ function Get-PfbResourceAccess { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'resource-accesses' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Misc/Get-PfbRole.ps1 b/Public/Misc/Get-PfbRole.ps1 index 3158a1e..555f45d 100644 --- a/Public/Misc/Get-PfbRole.ps1 +++ b/Public/Misc/Get-PfbRole.ps1 @@ -45,10 +45,7 @@ function Get-PfbRole { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'roles' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Misc/Get-PfbSoftwareCheck.ps1 b/Public/Misc/Get-PfbSoftwareCheck.ps1 index b752c70..3d3c668 100644 --- a/Public/Misc/Get-PfbSoftwareCheck.ps1 +++ b/Public/Misc/Get-PfbSoftwareCheck.ps1 @@ -33,8 +33,6 @@ function Get-PfbSoftwareCheck { ) Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'software-check' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Misc/Get-PfbUsageGroup.ps1 b/Public/Misc/Get-PfbUsageGroup.ps1 index 478235c..d3a4c19 100644 --- a/Public/Misc/Get-PfbUsageGroup.ps1 +++ b/Public/Misc/Get-PfbUsageGroup.ps1 @@ -36,9 +36,7 @@ function Get-PfbUsageGroup { ) Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($FileSystemName) { $queryParams['file_system_names'] = $FileSystemName } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'usage/groups' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Misc/Get-PfbUsageUser.ps1 b/Public/Misc/Get-PfbUsageUser.ps1 index 064e680..7f9e3e0 100644 --- a/Public/Misc/Get-PfbUsageUser.ps1 +++ b/Public/Misc/Get-PfbUsageUser.ps1 @@ -36,9 +36,7 @@ function Get-PfbUsageUser { ) Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($FileSystemName) { $queryParams['file_system_names'] = $FileSystemName } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'usage/users' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Misc/New-PfbLegalHoldEntity.ps1 b/Public/Misc/New-PfbLegalHoldEntity.ps1 index 56aec9d..2fd5ab9 100644 --- a/Public/Misc/New-PfbLegalHoldEntity.ps1 +++ b/Public/Misc/New-PfbLegalHoldEntity.ps1 @@ -4,36 +4,65 @@ function New-PfbLegalHoldEntity { Adds an entity to a legal hold on the FlashBlade. .DESCRIPTION The New-PfbLegalHoldEntity cmdlet associates an entity (file system, bucket, etc.) - with a legal hold on the connected Pure Storage FlashBlade. Identify the hold and - member by name, and optionally supply additional attributes. - .PARAMETER HoldName - The name of the legal hold. - .PARAMETER MemberName - The name of the entity to place under legal hold. + with a legal hold on the connected Pure Storage FlashBlade. Identify the entity by + file system, ID, name, or path, and optionally supply additional attributes. + + Whole-branch review fix: -HoldName/-MemberName (`hold_names`/`member_names`) are + removed -- `POST /legal-holds/held-entities` has no such query parameters in any + cached spec version (2.4-2.28), so they could never have had any effect. -Names and + -FileSystemNames (added by this task, real and spec-backed) already cover the same + job these were attempting. + .PARAMETER FileSystemIds + The IDs of the file systems to place under legal hold. + .PARAMETER FileSystemNames + The names of the file systems to place under legal hold. + .PARAMETER Ids + The IDs of the held entities to create. + .PARAMETER Names + The names of the held entities to create. + .PARAMETER Paths + The paths to place under legal hold. + .PARAMETER Recursive + If set to `true`, the legal hold is applied recursively to the specified path or + file system. .PARAMETER Attributes - A hashtable of additional attributes for the held-entity body. + A hashtable of additional attributes for the held-entity body. `POST + /legal-holds/held-entities` accepts no request body, so nothing supplied here is + sent to the array. Use the typed query parameters above instead. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - New-PfbLegalHoldEntity -HoldName "litigation-hold-2024" -MemberName "fs1" + New-PfbLegalHoldEntity -Names "litigation-hold-2024" -FileSystemNames "fs1" Places file system "fs1" under the legal hold "litigation-hold-2024". .EXAMPLE - New-PfbLegalHoldEntity -HoldName "litigation-hold-2024" -MemberName "fs1" -Attributes @{ type = 'file-system' } + New-PfbLegalHoldEntity -Names "litigation-hold-2024" -Paths "/dir1" -Recursive $true - Adds a held entity with explicit type attribute. + Recursively places everything under "/dir1" under the specified legal hold. .EXAMPLE - New-PfbLegalHoldEntity -HoldName "sec-investigation" -MemberName "bucket1" -Confirm:$false + New-PfbLegalHoldEntity -Names "sec-investigation" -FileSystemNames "bucket1" -Confirm:$false Adds a held entity without prompting for confirmation. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param( [Parameter()] - [string]$HoldName, + [string[]]$FileSystemIds, + + [Parameter()] + [string[]]$FileSystemNames, + + [Parameter()] + [string[]]$Ids, [Parameter()] - [string]$MemberName, + [string[]]$Names, + + [Parameter()] + [string[]]$Paths, + + [Parameter()] + [Nullable[bool]]$Recursive, [Parameter()] [hashtable]$Attributes, @@ -43,13 +72,25 @@ function New-PfbLegalHoldEntity { Assert-PfbConnection -Array ([ref]$Array) + # POST /legal-holds/held-entities accepts no request body at all -- everything this + # endpoint accepts is a query parameter (see New-PfbApiToken.ps1 for the identical shape). $body = if ($Attributes) { $Attributes } else { @{} } $queryParams = @{} - if ($HoldName) { $queryParams['hold_names'] = $HoldName } - if ($MemberName) { $queryParams['member_names'] = $MemberName } - $target = "${HoldName}:${MemberName}" + # Every newly added query parameter is guarded by ContainsKey, never truthiness -- see the + # canonical explanation in Update-PfbAdmin.ps1. An array field's `@()` must still reach the + # wire so a caller can send an explicitly empty list. + if ($PSBoundParameters.ContainsKey('FileSystemIds')) { $queryParams['file_system_ids'] = $FileSystemIds -join ',' } + if ($PSBoundParameters.ContainsKey('FileSystemNames')) { $queryParams['file_system_names'] = $FileSystemNames -join ',' } + if ($PSBoundParameters.ContainsKey('Ids')) { $queryParams['ids'] = $Ids -join ',' } + if ($PSBoundParameters.ContainsKey('Names')) { $queryParams['names'] = $Names -join ',' } + if ($PSBoundParameters.ContainsKey('Paths')) { $queryParams['paths'] = $Paths -join ',' } + if ($PSBoundParameters.ContainsKey('Recursive')) { $queryParams['recursive'] = $Recursive } + + $target = if ($PSBoundParameters.ContainsKey('Names')) { $Names -join ',' } + elseif ($PSBoundParameters.ContainsKey('FileSystemNames')) { $FileSystemNames -join ',' } + else { 'legal hold entity' } if ($PSCmdlet.ShouldProcess($target, 'Add legal hold entity')) { Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'legal-holds/held-entities' -Body $body -QueryParams $queryParams diff --git a/Public/Misc/Update-PfbKmip.ps1 b/Public/Misc/Update-PfbKmip.ps1 index 0596c2f..94cbb4b 100644 --- a/Public/Misc/Update-PfbKmip.ps1 +++ b/Public/Misc/Update-PfbKmip.ps1 @@ -10,16 +10,26 @@ function Update-PfbKmip { The name of the KMIP server configuration to update. .PARAMETER Id The ID of the KMIP server configuration to update. + .PARAMETER CaCertificate + The name of the CA certificate used to validate the authenticity of the configured + KMIP servers. + .PARAMETER CaCertificateGroup + The name of the certificate group containing CA certificates that can be used to + validate the authenticity of the configured KMIP servers. + .PARAMETER Uris + The list of URIs for the configured KMIP servers, in the format + [protocol://]hostname:port. .PARAMETER Attributes - A hashtable of properties to update on the KMIP configuration. + A hashtable of properties to update on the KMIP configuration. Mutually exclusive + with the individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbKmip -Name 'kmip-server-1' -Attributes @{ uris = @('kmip.example.com:5696') } + Update-PfbKmip -Name 'kmip-server-1' -Uris 'kmip.example.com:5696' - Updates the KMIP server URI. + Updates the KMIP server URI using a typed parameter. .EXAMPLE - Update-PfbKmip -Name 'kmip-server-1' -Attributes @{ ca_certificate_group = @{ name = 'kmip-certs' } } + Update-PfbKmip -Name 'kmip-server-1' -CaCertificateGroup 'kmip-certs' Updates the CA certificate group used by the KMIP server. .EXAMPLE @@ -27,15 +37,31 @@ function Update-PfbKmip { Updates the KMIP configuration without prompting for confirmation. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [string]$Id, - [Parameter(Mandatory)] + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$CaCertificate, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$CaCertificateGroup, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$Uris, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [hashtable]$Attributes, [Parameter()] [PSCustomObject]$Array @@ -51,8 +77,26 @@ function Update-PfbKmip { if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Every value-carrying parameter is guarded by ContainsKey, never truthiness -- see + # the canonical explanation in Update-PfbAdmin.ps1. + $body = @{} + + # Constraint 8(a): ca_certificate/ca_certificate_group are SCALAR REFERENCES (item + # schema is {id, name, resource_type}), so the parameter is [string] taking the + # name and the projection is assigned INLINE -- constraint 7 forbids a local. + if ($PSBoundParameters.ContainsKey('CaCertificate')) { $body['ca_certificate'] = @{ name = $CaCertificate } } + if ($PSBoundParameters.ContainsKey('CaCertificateGroup')) { $body['ca_certificate_group'] = @{ name = $CaCertificateGroup } } + + # uris is a plain string array, not a reference -- no name-projection needed. + if ($PSBoundParameters.ContainsKey('Uris')) { $body['uris'] = @($Uris) } + } + if ($PSCmdlet.ShouldProcess($target, 'Update KMIP configuration')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'kmip' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'kmip' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Misc/Update-PfbLag.ps1 b/Public/Misc/Update-PfbLag.ps1 index 6a8cc06..43f6416 100644 --- a/Public/Misc/Update-PfbLag.ps1 +++ b/Public/Misc/Update-PfbLag.ps1 @@ -9,28 +9,57 @@ function Update-PfbLag { The name of the LAG to update. .PARAMETER Id The ID of the LAG to update. + .PARAMETER AddPorts + Names of the ports to add to the LAG. + .PARAMETER Ports + Names of the ports that make up the LAG, replacing the existing port list. + .PARAMETER RemovePorts + Names of the ports to remove from the LAG. .PARAMETER Attributes - A hashtable of LAG attributes to update, such as ports or LACP mode. + A hashtable of LAG attributes to update, such as ports or LACP mode. Mutually + exclusive with the individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, uses the default connection. .EXAMPLE - Update-PfbLag -Name "lag1" -Attributes @{ ports = @(@{ name = "CH1.FM1.ETH1" }, @{ name = "CH1.FM1.ETH3" }) } + Update-PfbLag -Name "lag1" -Ports "CH1.FM1.ETH1", "CH1.FM1.ETH3" - Updates the ports assigned to the LAG named 'lag1'. + Updates the ports assigned to the LAG named 'lag1' using typed parameters. .EXAMPLE Update-PfbLag -Name "lag1" -Attributes @{ lacp_mode = "passive" } Changes the LACP mode of 'lag1' to passive. .EXAMPLE - Update-PfbLag -Id "10314f42-020d-7080-8013-000ddt400012" -Attributes @{ lacp_mode = "active" } + Update-PfbLag -Id "10314f42-020d-7080-8013-000ddt400012" -AddPorts "CH1.FM1.ETH5" - Updates the LAG with the specified ID to active LACP mode. + Adds a port to the LAG with the specified ID. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$AddPorts, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$Ports, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$RemovePorts, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -42,8 +71,22 @@ function Update-PfbLag { if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } $target = if ($Name) { $Name } else { $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Constraint 8(b): add_ports/ports/remove_ports are ARRAYS OF REFERENCES (item + # schema is {id, name, resource_type}), so each parameter is [string[]] and the + # projection is assigned INLINE -- constraint 7 forbids an intermediate local. + $body = @{} + if ($PSBoundParameters.ContainsKey('AddPorts')) { $body['add_ports'] = @($AddPorts | ForEach-Object { @{ name = $_ } }) } + if ($PSBoundParameters.ContainsKey('Ports')) { $body['ports'] = @($Ports | ForEach-Object { @{ name = $_ } }) } + if ($PSBoundParameters.ContainsKey('RemovePorts')) { $body['remove_ports'] = @($RemovePorts | ForEach-Object { @{ name = $_ } }) } + } + if ($PSCmdlet.ShouldProcess($target, 'Update LAG')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'link-aggregation-groups' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'link-aggregation-groups' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Misc/Update-PfbLegalHold.ps1 b/Public/Misc/Update-PfbLegalHold.ps1 index 5def732..26f1522 100644 --- a/Public/Misc/Update-PfbLegalHold.ps1 +++ b/Public/Misc/Update-PfbLegalHold.ps1 @@ -10,14 +10,17 @@ function Update-PfbLegalHold { The name of the legal hold to update. .PARAMETER Id The ID of the legal hold to update. + .PARAMETER Description + The description of the legal hold instance. .PARAMETER Attributes - A hashtable of attributes to update on the legal hold. + A hashtable of attributes to update on the legal hold. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbLegalHold -Name "litigation-hold-2024" -Attributes @{ description = 'Updated description' } + Update-PfbLegalHold -Name "litigation-hold-2024" -Description 'Updated description' - Updates the description on the specified legal hold. + Updates the description on the specified legal hold using a typed parameter. .EXAMPLE Update-PfbLegalHold -Id "12345" -Attributes @{ enabled = $false } @@ -27,15 +30,23 @@ function Update-PfbLegalHold { Enables the specified legal hold. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [string]$Id, - [Parameter()] + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Description, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [hashtable]$Attributes, [Parameter()] [PSCustomObject]$Array @@ -46,13 +57,21 @@ function Update-PfbLegalHold { } process { - $body = if ($Attributes) { $Attributes } else { @{} } - $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } $target = if ($Name) { $Name } else { $Id } + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Every value-carrying parameter is guarded by ContainsKey, never truthiness -- see + # the canonical explanation in Update-PfbAdmin.ps1. + $body = @{} + if ($PSBoundParameters.ContainsKey('Description')) { $body['description'] = $Description } + } + if ($PSCmdlet.ShouldProcess($target, 'Update legal hold')) { Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'legal-holds' -Body $body -QueryParams $queryParams } diff --git a/Public/Misc/Update-PfbLegalHoldEntity.ps1 b/Public/Misc/Update-PfbLegalHoldEntity.ps1 index 5ab5198..517f230 100644 --- a/Public/Misc/Update-PfbLegalHoldEntity.ps1 +++ b/Public/Misc/Update-PfbLegalHoldEntity.ps1 @@ -8,28 +8,61 @@ function Update-PfbLegalHoldEntity { held entity by name and supply the changed properties via Attributes. .PARAMETER Name The name of the held entity to update. + .PARAMETER FileSystemIds + The IDs of the file systems whose held entities to update. + .PARAMETER FileSystemNames + The names of the file systems whose held entities to update. + .PARAMETER Ids + The IDs of the held entities to update. + .PARAMETER Paths + The paths of the held entities to update. + .PARAMETER Recursive + If set to `true`, the update is applied recursively to the specified path or file + system. + .PARAMETER Released + If set to `true`, the held entity is released from its legal hold. .PARAMETER Attributes - A hashtable of attributes to update on the held entity. + A hashtable of attributes to update on the held entity. `PATCH + /legal-holds/held-entities` accepts no request body, so nothing supplied here is sent + to the array. Use the typed query parameters above instead. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbLegalHoldEntity -Name "fs1" -Attributes @{ description = 'Updated hold reason' } + Update-PfbLegalHoldEntity -Name "fs1" -Released $true - Updates the description on the held entity named "fs1". + Releases the held entity named "fs1" from its legal hold. .EXAMPLE - Update-PfbLegalHoldEntity -Name "bucket1" -Attributes @{ enabled = $false } + Update-PfbLegalHoldEntity -Name "bucket1" -Recursive $false - Disables the held entity named "bucket1". + Updates the held entity named "bucket1" without applying the change recursively. .EXAMPLE Update-PfbLegalHoldEntity -Name "fs1" -Attributes @{ hold_type = 'litigation' } - Updates the hold type on the specified held entity. + Updates the held entity using a raw attributes hashtable. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param( [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, + [Parameter()] + [string[]]$FileSystemIds, + + [Parameter()] + [string[]]$FileSystemNames, + + [Parameter()] + [string[]]$Ids, + + [Parameter()] + [string[]]$Paths, + + [Parameter()] + [Nullable[bool]]$Recursive, + + [Parameter()] + [Nullable[bool]]$Released, + [Parameter()] [hashtable]$Attributes, @@ -41,11 +74,22 @@ function Update-PfbLegalHoldEntity { } process { + # PATCH /legal-holds/held-entities accepts no request body at all -- everything this + # endpoint accepts is a query parameter (see New-PfbApiToken.ps1 for the identical shape). $body = if ($Attributes) { $Attributes } else { @{} } $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } + # Every newly added query parameter is guarded by ContainsKey, never truthiness -- see + # the canonical explanation in Update-PfbAdmin.ps1. + if ($PSBoundParameters.ContainsKey('FileSystemIds')) { $queryParams['file_system_ids'] = $FileSystemIds -join ',' } + if ($PSBoundParameters.ContainsKey('FileSystemNames')) { $queryParams['file_system_names'] = $FileSystemNames -join ',' } + if ($PSBoundParameters.ContainsKey('Ids')) { $queryParams['ids'] = $Ids -join ',' } + if ($PSBoundParameters.ContainsKey('Paths')) { $queryParams['paths'] = $Paths -join ',' } + if ($PSBoundParameters.ContainsKey('Recursive')) { $queryParams['recursive'] = $Recursive } + if ($PSBoundParameters.ContainsKey('Released')) { $queryParams['released'] = $Released } + if ($PSCmdlet.ShouldProcess($Name, 'Update legal hold entity')) { Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'legal-holds/held-entities' -Body $body -QueryParams $queryParams } diff --git a/Public/Misc/Update-PfbLifecycleRule.ps1 b/Public/Misc/Update-PfbLifecycleRule.ps1 index 9292fe4..d3a08c5 100644 --- a/Public/Misc/Update-PfbLifecycleRule.ps1 +++ b/Public/Misc/Update-PfbLifecycleRule.ps1 @@ -9,29 +9,94 @@ function Update-PfbLifecycleRule { The name of the lifecycle rule to update. .PARAMETER Id The ID of the lifecycle rule to update. + .PARAMETER BucketIds + The IDs of the buckets whose lifecycle rules to update. + .PARAMETER BucketNames + The names of the buckets whose lifecycle rules to update. + .PARAMETER ConfirmDate + The confirmation timestamp required to apply a change that affects existing objects. + .PARAMETER AbortIncompleteMultipartUploadsAfter + Duration of time after which incomplete multipart uploads will be aborted. + .PARAMETER Enabled + If set to `true`, this rule will be enabled. + .PARAMETER KeepCurrentVersionFor + Time after which current versions will be marked expired. + .PARAMETER KeepCurrentVersionUntil + Time after which current versions will be marked expired. + .PARAMETER KeepPreviousVersionFor + Time after which previous versions will be marked expired. + .PARAMETER Prefix + Object key prefix identifying one or more objects in the bucket. .PARAMETER Attributes A hashtable of lifecycle rule attributes to update, such as prefix, - keep_previous_version_for, or enabled. + keep_previous_version_for, or enabled. Mutually exclusive with the individual typed + parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, uses the default connection. .EXAMPLE - Update-PfbLifecycleRule -Name "expire-logs-30d" -Attributes @{ keep_previous_version_for = 5184000000 } + Update-PfbLifecycleRule -Name "expire-logs-30d" -KeepPreviousVersionFor 5184000000 - Updates the retention period of the lifecycle rule to 60 days. + Updates the retention period of the lifecycle rule to 60 days using a typed parameter. .EXAMPLE - Update-PfbLifecycleRule -Name "cleanup" -Attributes @{ enabled = $false } + Update-PfbLifecycleRule -Name "cleanup" -Enabled $false Disables the lifecycle rule named 'cleanup'. .EXAMPLE - Update-PfbLifecycleRule -Name "archive" -Attributes @{ prefix = "old/"; keep_previous_version_for = 7776000000 } + Update-PfbLifecycleRule -Name "archive" -Prefix "old/" -KeepPreviousVersionFor 7776000000 Updates the prefix filter and retention period of the 'archive' rule. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + # Constraint 17: newly added QUERY parameters are declared bare, NOT in the + # *Individual sets -- they are orthogonal to the request body and must stay usable + # alongside -Attributes. + [Parameter()] + [string[]]$BucketIds, + + [Parameter()] + [string[]]$BucketNames, + + [Parameter()] + [long]$ConfirmDate, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$AbortIncompleteMultipartUploadsAfter, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [Nullable[bool]]$Enabled, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$KeepCurrentVersionFor, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$KeepCurrentVersionUntil, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$KeepPreviousVersionFor, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Prefix, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -42,9 +107,30 @@ function Update-PfbLifecycleRule { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + + # Every value-carrying query parameter is guarded by ContainsKey, never truthiness -- + # see the canonical explanation in Update-PfbAdmin.ps1. + if ($PSBoundParameters.ContainsKey('BucketIds')) { $queryParams['bucket_ids'] = $BucketIds -join ',' } + if ($PSBoundParameters.ContainsKey('BucketNames')) { $queryParams['bucket_names'] = $BucketNames -join ',' } + if ($PSBoundParameters.ContainsKey('ConfirmDate')) { $queryParams['confirm_date'] = $ConfirmDate } + $target = if ($Name) { $Name } else { $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('AbortIncompleteMultipartUploadsAfter')) { $body['abort_incomplete_multipart_uploads_after'] = $AbortIncompleteMultipartUploadsAfter } + if ($PSBoundParameters.ContainsKey('Enabled')) { $body['enabled'] = $Enabled } + if ($PSBoundParameters.ContainsKey('KeepCurrentVersionFor')) { $body['keep_current_version_for'] = $KeepCurrentVersionFor } + if ($PSBoundParameters.ContainsKey('KeepCurrentVersionUntil')) { $body['keep_current_version_until'] = $KeepCurrentVersionUntil } + if ($PSBoundParameters.ContainsKey('KeepPreviousVersionFor')) { $body['keep_previous_version_for'] = $KeepPreviousVersionFor } + if ($PSBoundParameters.ContainsKey('Prefix')) { $body['prefix'] = $Prefix } + } + if ($PSCmdlet.ShouldProcess($target, 'Update lifecycle rule')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'lifecycle-rules' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'lifecycle-rules' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Monitoring/Get-PfbAsyncLog.ps1 b/Public/Monitoring/Get-PfbAsyncLog.ps1 index 7aa6dc3..ec431bd 100644 --- a/Public/Monitoring/Get-PfbAsyncLog.ps1 +++ b/Public/Monitoring/Get-PfbAsyncLog.ps1 @@ -58,11 +58,7 @@ function Get-PfbAsyncLog { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'logs-async' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Monitoring/Get-PfbAsyncLogDownload.ps1 b/Public/Monitoring/Get-PfbAsyncLogDownload.ps1 index 411b059..41b4984 100644 --- a/Public/Monitoring/Get-PfbAsyncLogDownload.ps1 +++ b/Public/Monitoring/Get-PfbAsyncLogDownload.ps1 @@ -49,8 +49,7 @@ function Get-PfbAsyncLogDownload { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($allIds.Count -gt 0) { $queryParams['ids'] = $allIds -join ',' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'logs-async/download' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Monitoring/Get-PfbLog.ps1 b/Public/Monitoring/Get-PfbLog.ps1 index 4ea5930..5487b92 100644 --- a/Public/Monitoring/Get-PfbLog.ps1 +++ b/Public/Monitoring/Get-PfbLog.ps1 @@ -42,9 +42,7 @@ function Get-PfbLog { $queryParams = @{} $queryParams['start_time'] = $StartTime $queryParams['end_time'] = $EndTime - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'logs' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Monitoring/Get-PfbLogTargetFileSystem.ps1 b/Public/Monitoring/Get-PfbLogTargetFileSystem.ps1 index 1cf0d40..1ad5b55 100644 --- a/Public/Monitoring/Get-PfbLogTargetFileSystem.ps1 +++ b/Public/Monitoring/Get-PfbLogTargetFileSystem.ps1 @@ -58,11 +58,7 @@ function Get-PfbLogTargetFileSystem { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds try { Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'log-targets/file-systems' -QueryParams $queryParams -AutoPaginate diff --git a/Public/Monitoring/Get-PfbLogTargetObjectStore.ps1 b/Public/Monitoring/Get-PfbLogTargetObjectStore.ps1 index c4466fe..f56ee01 100644 --- a/Public/Monitoring/Get-PfbLogTargetObjectStore.ps1 +++ b/Public/Monitoring/Get-PfbLogTargetObjectStore.ps1 @@ -58,11 +58,7 @@ function Get-PfbLogTargetObjectStore { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'log-targets/object-store' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Monitoring/Get-PfbSmtpServer.ps1 b/Public/Monitoring/Get-PfbSmtpServer.ps1 index 1964dda..550cea8 100644 --- a/Public/Monitoring/Get-PfbSmtpServer.ps1 +++ b/Public/Monitoring/Get-PfbSmtpServer.ps1 @@ -6,6 +6,12 @@ function Get-PfbSmtpServer { The Get-PfbSmtpServer cmdlet returns the SMTP server configuration from the connected Pure Storage FlashBlade. This is a singleton endpoint returning the mail relay settings. This cmdlet is an alias for Get-PfbSmtp using the newer API endpoint. + .PARAMETER Filter + A server-side filter expression to narrow results. + .PARAMETER Sort + Sort field and direction. + .PARAMETER Limit + Maximum number of items to return. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -22,7 +28,21 @@ function Get-PfbSmtpServer { Retrieves the configured SMTP relay host. #> [CmdletBinding()] - param([Parameter()] [PSCustomObject]$Array) + param( + [Parameter()] + [string]$Filter, + + [Parameter()] + [string]$Sort, + + [Parameter()] + [int]$Limit, + + [Parameter()] + [PSCustomObject]$Array + ) Assert-PfbConnection -Array ([ref]$Array) - Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'smtp-servers' + $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'smtp-servers' -QueryParams $queryParams } diff --git a/Public/Monitoring/Get-PfbSnmpAgent.ps1 b/Public/Monitoring/Get-PfbSnmpAgent.ps1 index cf7f331..6786562 100644 --- a/Public/Monitoring/Get-PfbSnmpAgent.ps1 +++ b/Public/Monitoring/Get-PfbSnmpAgent.ps1 @@ -30,6 +30,6 @@ function Get-PfbSnmpAgent { ) Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} - if ($Filter) { $queryParams['filter'] = $Filter } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'snmp-agents' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Monitoring/Get-PfbSnmpManager.ps1 b/Public/Monitoring/Get-PfbSnmpManager.ps1 index 89da5e1..5bbfa9a 100644 --- a/Public/Monitoring/Get-PfbSnmpManager.ps1 +++ b/Public/Monitoring/Get-PfbSnmpManager.ps1 @@ -52,11 +52,7 @@ function Get-PfbSnmpManager { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'snmp-managers' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Monitoring/Get-PfbSyslogServer.ps1 b/Public/Monitoring/Get-PfbSyslogServer.ps1 index 505f1ba..ab09845 100644 --- a/Public/Monitoring/Get-PfbSyslogServer.ps1 +++ b/Public/Monitoring/Get-PfbSyslogServer.ps1 @@ -51,11 +51,7 @@ function Get-PfbSyslogServer { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'syslog-servers' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Monitoring/Get-PfbSyslogServerSettings.ps1 b/Public/Monitoring/Get-PfbSyslogServerSettings.ps1 index 0d2c0e4..0ef45c4 100644 --- a/Public/Monitoring/Get-PfbSyslogServerSettings.ps1 +++ b/Public/Monitoring/Get-PfbSyslogServerSettings.ps1 @@ -5,6 +5,12 @@ function Get-PfbSyslogServerSettings { .DESCRIPTION The Get-PfbSyslogServerSettings cmdlet returns the global syslog server settings from the connected Pure Storage FlashBlade. This is a singleton endpoint. + .PARAMETER Filter + A server-side filter expression to narrow results. + .PARAMETER Sort + Sort field and direction. + .PARAMETER Limit + Maximum number of items to return. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -21,7 +27,21 @@ function Get-PfbSyslogServerSettings { Retrieves the CA certificate from the syslog settings. #> [CmdletBinding()] - param([Parameter()] [PSCustomObject]$Array) + param( + [Parameter()] + [string]$Filter, + + [Parameter()] + [string]$Sort, + + [Parameter()] + [int]$Limit, + + [Parameter()] + [PSCustomObject]$Array + ) Assert-PfbConnection -Array ([ref]$Array) - Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'syslog-servers/settings' + $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'syslog-servers/settings' -QueryParams $queryParams } diff --git a/Public/Monitoring/Update-PfbAsyncLog.ps1 b/Public/Monitoring/Update-PfbAsyncLog.ps1 index 74137ca..3ed1694 100644 --- a/Public/Monitoring/Update-PfbAsyncLog.ps1 +++ b/Public/Monitoring/Update-PfbAsyncLog.ps1 @@ -3,39 +3,55 @@ function Update-PfbAsyncLog { .SYNOPSIS Updates an asynchronous log collection job on the FlashBlade. .DESCRIPTION - The Update-PfbAsyncLog cmdlet modifies an asynchronous log collection job on the - connected Pure Storage FlashBlade. Identify the job by name or ID and supply the - changed properties via Attributes or individual parameters. - .PARAMETER Name - The name of the async log job to update. - .PARAMETER Id - The ID of the async log job to update. + The Update-PfbAsyncLog cmdlet modifies the asynchronous log collection job on the + connected Everpure FlashBlade. Supply the changed properties via Attributes or the + individual parameters. + + PATCH /logs-async takes no query parameters and no selector: the array has one async + log collection to configure, not a collection of named jobs. The endpoint requires + start_time on every request, even though the spec marks no field as required. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. + .PARAMETER EndTime + When the time window ends (in milliseconds since epoch). + .PARAMETER HardwareComponents + All of the hardware components for which logs are being processed. + .PARAMETER StartTime + When the time window starts (in milliseconds since epoch). .PARAMETER Attributes - A hashtable of attributes to update on the async log job. + A hashtable of attributes to update on the async log job. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbAsyncLog -Name "log-job-1" -Attributes @{ status = 'cancelled' } + Update-PfbAsyncLog -StartTime 1700000000000 -EndTime 1700003600000 - Cancels the async log job named "log-job-1". + Sets the processing time window using typed parameters. .EXAMPLE - Update-PfbAsyncLog -Id "12345" -Attributes @{ status = 'cancelled' } + Update-PfbAsyncLog -StartTime 1700000000000 -HardwareComponents 'CH1.FB1','CH1.FB2' - Cancels the async log job identified by ID. + Restricts log processing to two hardware components over the given window. .EXAMPLE - Update-PfbAsyncLog -Name "log-job-1" -Attributes @{ keep_until = 1700000000000 } + Update-PfbAsyncLog -Attributes @{ start_time = 1700000000000; end_time = 1700003600000 } - Updates the retention time for the specified async log job. + Equivalent to the first example, using the raw attributes hashtable. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'Individual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] - [string]$Name, + [Parameter(ParameterSetName = 'Individual')] + [long]$EndTime, + + [Parameter(ParameterSetName = 'Individual')] + [string[]]$HardwareComponents, - [Parameter(ParameterSetName = 'ById', Mandatory)] - [string]$Id, + [Parameter(ParameterSetName = 'Individual')] + [long]$StartTime, - [Parameter()] + [Parameter(ParameterSetName = 'Attributes', Mandatory)] [hashtable]$Attributes, [Parameter()] [PSCustomObject]$Array @@ -46,15 +62,29 @@ function Update-PfbAsyncLog { } process { - $body = if ($Attributes) { $Attributes } else { @{} } + if ($PSCmdlet.ParameterSetName -eq 'Attributes') { + $body = $Attributes + } + else { + # Every value-carrying parameter is guarded by ContainsKey, never by truthiness -- + # see the canonical explanation in Update-PfbAdmin.ps1. + $body = @{} + if ($PSBoundParameters.ContainsKey('EndTime')) { $body['end_time'] = $EndTime } + if ($PSBoundParameters.ContainsKey('StartTime')) { $body['start_time'] = $StartTime } - $queryParams = @{} - if ($Name) { $queryParams['names'] = $Name } - if ($Id) { $queryParams['ids'] = $Id } - $target = if ($Name) { $Name } else { $Id } + # Constraint 8(b): hardware_components is an ARRAY OF REFERENCES (item schema is + # {id, name, resource_type}), so the parameter is [string[]] and the projection is + # assigned INLINE -- constraint 7 forbids an intermediate local. + if ($PSBoundParameters.ContainsKey('HardwareComponents')) { + $body['hardware_components'] = @($HardwareComponents | ForEach-Object { @{ name = $_ } }) + } + } - if ($PSCmdlet.ShouldProcess($target, 'Update async log job')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'logs-async' -Body $body -QueryParams $queryParams + # PATCH /logs-async takes no query parameters in any spec version and ignores any that + # are sent (issue #64), so there is no per-job target to name here -- the endpoint + # operates on the array's async log collection itself. + if ($PSCmdlet.ShouldProcess('logs-async', 'Update async log job')) { + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'logs-async' -Body $body } } } diff --git a/Public/Monitoring/Update-PfbLogTargetFileSystem.ps1 b/Public/Monitoring/Update-PfbLogTargetFileSystem.ps1 index a7a83b5..150a8ec 100644 --- a/Public/Monitoring/Update-PfbLogTargetFileSystem.ps1 +++ b/Public/Monitoring/Update-PfbLogTargetFileSystem.ps1 @@ -4,38 +4,75 @@ function Update-PfbLogTargetFileSystem { Updates a log-target file-system configuration on the FlashBlade. .DESCRIPTION The Update-PfbLogTargetFileSystem cmdlet modifies a log-target file-system - configuration on the connected Pure Storage FlashBlade. Identify the target by - name or ID and supply the changed properties via Attributes. + configuration on the connected Everpure FlashBlade. Identify the target by + name or ID and supply the changed properties via Attributes or individual parameters. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name - The name of the log-target file system to update. + The name of the log-target file system to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the log-target file system to update. + .PARAMETER FileSystem + The target filesystem where audit logs will be stored. + .PARAMETER KeepFor + Specifies the period that audit logs are retained before they are deleted, in + milliseconds. + .PARAMETER KeepSize + Specifies the maximum size of audit logs to be retained. + .PARAMETER NewName + A new user-specified name for the log target. Named -NewName rather than -Name + because -Name already identifies which log target to update. .PARAMETER Attributes - A hashtable of attributes to update on the configuration. + A hashtable of attributes to update on the configuration. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbLogTargetFileSystem -Name "log-fs-target1" -Attributes @{ path = '/new-path' } + Update-PfbLogTargetFileSystem -Name "log-fs-target1" -KeepFor 86400000 - Updates the path on the log-target file-system configuration. + Updates the audit-log retention period on the log-target file-system configuration + using a typed parameter. .EXAMPLE Update-PfbLogTargetFileSystem -Id "12345" -Attributes @{ enabled = $true } Enables the log-target file system identified by ID. .EXAMPLE - Update-PfbLogTargetFileSystem -Name "log-fs-target1" -Attributes @{ file_system = @{ name = 'new-fs' } } + Update-PfbLogTargetFileSystem -Name "log-fs-target1" -FileSystem 'new-fs' - Updates the underlying file system reference. + Updates the underlying file system reference using a typed parameter. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [string]$Id, - [Parameter()] + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$FileSystem, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$KeepFor, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$KeepSize, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$NewName, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [hashtable]$Attributes, [Parameter()] [PSCustomObject]$Array @@ -46,11 +83,27 @@ function Update-PfbLogTargetFileSystem { } process { - $body = if ($Attributes) { $Attributes } else { @{} } - $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Every value-carrying parameter is guarded by ContainsKey, never by truthiness -- + # see the canonical explanation in Update-PfbAdmin.ps1. + $body = @{} + if ($PSBoundParameters.ContainsKey('KeepFor')) { $body['keep_for'] = $KeepFor } + if ($PSBoundParameters.ContainsKey('KeepSize')) { $body['keep_size'] = $KeepSize } + if ($PSBoundParameters.ContainsKey('NewName')) { $body['name'] = $NewName } + + # Constraint 8(a): file_system is a SCALAR REFERENCE (item schema is + # {id, name, resource_type}), so the parameter is [string] taking the name and the + # projection is assigned INLINE -- constraint 7 forbids an intermediate local. + if ($PSBoundParameters.ContainsKey('FileSystem')) { $body['file_system'] = @{ name = $FileSystem } } + } + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update log-target file system')) { diff --git a/Public/Monitoring/Update-PfbLogTargetObjectStore.ps1 b/Public/Monitoring/Update-PfbLogTargetObjectStore.ps1 index 2a47b4f..3c50ea0 100644 --- a/Public/Monitoring/Update-PfbLogTargetObjectStore.ps1 +++ b/Public/Monitoring/Update-PfbLogTargetObjectStore.ps1 @@ -4,38 +4,74 @@ function Update-PfbLogTargetObjectStore { Updates a log-target object-store configuration on the FlashBlade. .DESCRIPTION The Update-PfbLogTargetObjectStore cmdlet modifies a log-target object-store - configuration on the connected Pure Storage FlashBlade. Identify the target by - name or ID and supply the changed properties via Attributes. + configuration on the connected Everpure FlashBlade. Identify the target by + name or ID and supply the changed properties via Attributes or individual parameters. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name - The name of the log-target object store to update. + The name of the log-target object store to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the log-target object store to update. + .PARAMETER Bucket + Reference to the bucket where audit logs will be stored. + .PARAMETER LogNamePrefix + The prefix of the audit log object. + .PARAMETER LogRotate + The threshold after which the audit log object will be rotated. + .PARAMETER NewName + A new user-specified name for the log target. Named -NewName rather than -Name + because -Name already identifies which log target to update. .PARAMETER Attributes - A hashtable of attributes to update on the configuration. + A hashtable of attributes to update on the configuration. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbLogTargetObjectStore -Name "log-obj-target1" -Attributes @{ prefix = '/new-prefix' } + Update-PfbLogTargetObjectStore -Name "log-obj-target1" -LogNamePrefix @{ prefix = 's3auditlog' } - Updates the prefix on the log-target object-store configuration. + Updates the audit-log object-name prefix on the log-target object-store configuration + using a typed parameter. .EXAMPLE Update-PfbLogTargetObjectStore -Id "12345" -Attributes @{ enabled = $true } Enables the log-target object store identified by ID. .EXAMPLE - Update-PfbLogTargetObjectStore -Name "log-obj-target1" -Attributes @{ bucket = 'new-bucket' } + Update-PfbLogTargetObjectStore -Name "log-obj-target1" -Bucket 'new-bucket' - Updates the bucket reference on the log-target object store. + Updates the bucket reference on the log-target object store using a typed parameter. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [string]$Id, - [Parameter()] + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Bucket, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [hashtable]$LogNamePrefix, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [hashtable]$LogRotate, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$NewName, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [hashtable]$Attributes, [Parameter()] [PSCustomObject]$Array @@ -46,11 +82,32 @@ function Update-PfbLogTargetObjectStore { } process { - $body = if ($Attributes) { $Attributes } else { @{} } - $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Every value-carrying parameter is guarded by ContainsKey, never by truthiness -- + # see the canonical explanation in Update-PfbAdmin.ps1. + $body = @{} + if ($PSBoundParameters.ContainsKey('NewName')) { $body['name'] = $NewName } + + # Constraint 8(a): bucket is a SCALAR REFERENCE (item schema is + # {id, name, resource_type}), so the parameter is [string] taking the name and the + # projection is assigned INLINE -- constraint 7 forbids an intermediate local. + if ($PSBoundParameters.ContainsKey('Bucket')) { $body['bucket'] = @{ name = $Bucket } } + + # Constraint 8(c): log_name_prefix and log_rotate are COMPOSITE sub-objects + # (_auditLogNamePrefix, _auditLogRotate), not references -- neither has a `name` + # property, so projecting them into @{ name = ... } would write a field the schema + # does not have. Pass them straight through. + if ($PSBoundParameters.ContainsKey('LogNamePrefix')) { $body['log_name_prefix'] = $LogNamePrefix } + if ($PSBoundParameters.ContainsKey('LogRotate')) { $body['log_rotate'] = $LogRotate } + } + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update log-target object store')) { diff --git a/Public/Monitoring/Update-PfbSnmpManager.ps1 b/Public/Monitoring/Update-PfbSnmpManager.ps1 index a0314b5..8e3a368 100644 --- a/Public/Monitoring/Update-PfbSnmpManager.ps1 +++ b/Public/Monitoring/Update-PfbSnmpManager.ps1 @@ -3,36 +3,93 @@ function Update-PfbSnmpManager { .SYNOPSIS Updates an existing SNMP manager (trap host) on a Pure Storage FlashBlade. .DESCRIPTION - The Update-PfbSnmpManager cmdlet modifies an existing SNMP manager entry on the FlashBlade. - The manager can be identified by name or by ID. The Attributes hashtable contains the - configuration properties to update such as the host address, community string, or SNMPv3 - credentials. This cmdlet supports pipeline input by property name and the ShouldProcess pattern. + The Update-PfbSnmpManager cmdlet modifies an existing SNMP manager entry on the connected + Everpure FlashBlade. The manager can be identified by name or by ID. This cmdlet supports + pipeline input by property name and the ShouldProcess pattern. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the SNMP manager to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the SNMP manager to update. + .PARAMETER SnmpHost + DNS hostname or IP address of a computer that hosts an SNMP manager to which Purity is + to send trap messages when it generates alerts. Named -SnmpHost rather than -Host + because -Host is a PowerShell common parameter. + .PARAMETER NewName + A new user-specified name for the SNMP manager. Named -NewName rather than -Name + because -Name already identifies which SNMP manager to update. + .PARAMETER Notification + The type of notification the agent will send. + .PARAMETER V2c + The v2c configuration of SNMP, as a hashtable -- for example @{ community = 'public' }. + .PARAMETER V3 + The v3 configuration of SNMP, as a hashtable -- for example @{ auth_protocol = 'SHA' }. + .PARAMETER Version + Version of the SNMP protocol to be used by Purity in communications with the specified + manager. .PARAMETER Attributes - A hashtable containing the SNMP manager attributes to update. + A hashtable containing the SNMP manager attributes to update. Mutually exclusive with + the individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbSnmpManager -Name 'snmp-mgr01' -Attributes @{ host = '10.21.100.55' } + Update-PfbSnmpManager -Name 'snmp-mgr01' -SnmpHost '10.21.100.55' - Updates the host address for the SNMP manager named 'snmp-mgr01'. + Updates the host address for the SNMP manager named 'snmp-mgr01' using a typed parameter. .EXAMPLE - Update-PfbSnmpManager -Name 'snmp-mgr01' -Attributes @{ community = 'new-community' } + Update-PfbSnmpManager -Name 'snmp-mgr01' -V2c @{ community = 'new-community' } - Changes the community string for the specified SNMP manager. + Changes the community string for the specified SNMP manager using a typed parameter. .EXAMPLE Get-PfbSnmpManager -Name 'snmp-mgr01' | Update-PfbSnmpManager -Attributes @{ version = 'v2c'; community = 'updated' } Pipes an SNMP manager object to update its version and community string. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$SnmpHost, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$NewName, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [ValidateSet('inform', 'trap')] + [string]$Notification, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [hashtable]$V2c, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [hashtable]$V3, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [ValidateSet('v2c', 'v3')] + [string]$Version, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -43,9 +100,30 @@ function Update-PfbSnmpManager { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Every value-carrying parameter is guarded by ContainsKey, never by truthiness -- + # see the canonical explanation in Update-PfbAdmin.ps1. + $body = @{} + if ($PSBoundParameters.ContainsKey('SnmpHost')) { $body['host'] = $SnmpHost } + if ($PSBoundParameters.ContainsKey('NewName')) { $body['name'] = $NewName } + if ($PSBoundParameters.ContainsKey('Notification')) { $body['notification'] = $Notification } + if ($PSBoundParameters.ContainsKey('Version')) { $body['version'] = $Version } + + # Constraint 8(c): v2c and v3 are COMPOSITE sub-objects (_snmp_v2c, _snmp_v3_post), + # not references -- neither has a `name` property, so projecting them into + # @{ name = ... } would write a field the schema does not have. Pass them straight + # through. + if ($PSBoundParameters.ContainsKey('V2c')) { $body['v2c'] = $V2c } + if ($PSBoundParameters.ContainsKey('V3')) { $body['v3'] = $V3 } + } + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update SNMP manager')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'snmp-managers' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'snmp-managers' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Monitoring/Update-PfbSyslogServer.ps1 b/Public/Monitoring/Update-PfbSyslogServer.ps1 index 035b721..7b4fa46 100644 --- a/Public/Monitoring/Update-PfbSyslogServer.ps1 +++ b/Public/Monitoring/Update-PfbSyslogServer.ps1 @@ -4,20 +4,32 @@ function Update-PfbSyslogServer { Updates an existing syslog server configuration on a FlashBlade array. .DESCRIPTION The Update-PfbSyslogServer cmdlet modifies attributes of an existing syslog server - configuration on the connected Pure Storage FlashBlade. The target server can be + configuration on the connected Everpure FlashBlade. The target server can be identified by name or ID. Supports pipeline input and ShouldProcess for confirmation. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the syslog server to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the syslog server to update. + .PARAMETER Services + Valid values are `data-audit` and `management`. + .PARAMETER Sources + The network interfaces used for communication with the syslog server. + .PARAMETER Uri + The URI of the syslog server in the format PROTOCOL://HOSTNAME:PORT. .PARAMETER Attributes A hashtable of syslog server attributes to modify (e.g., URI, transport protocol). + Mutually exclusive with the individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbSyslogServer -Name "syslog-prod" -Attributes @{ uri = "tcp://newsyslog.example.com:514" } + Update-PfbSyslogServer -Name "syslog-prod" -Uri "tcp://newsyslog.example.com:514" - Updates the URI of the syslog server named "syslog-prod". + Updates the URI of the syslog server named "syslog-prod" using a typed parameter. .EXAMPLE Update-PfbSyslogServer -Id "10314f42-020d-7080-8013-000ddt400090" -Attributes @{ enabled = $true } @@ -27,11 +39,34 @@ function Update-PfbSyslogServer { Pipes a syslog server object and updates its URI to use TLS transport. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [ValidateSet('data-audit', 'management')] + [string[]]$Services, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$Sources, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Uri, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -42,9 +77,28 @@ function Update-PfbSyslogServer { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Every value-carrying parameter is guarded by ContainsKey, never by truthiness -- + # see the canonical explanation in Update-PfbAdmin.ps1. + $body = @{} + if ($PSBoundParameters.ContainsKey('Services')) { $body['services'] = @($Services) } + if ($PSBoundParameters.ContainsKey('Uri')) { $body['uri'] = $Uri } + + # Constraint 8(b): sources is an ARRAY OF REFERENCES (item schema is + # {id, name, resource_type}), so the parameter is [string[]] and the projection is + # assigned INLINE -- constraint 7 forbids an intermediate local. + if ($PSBoundParameters.ContainsKey('Sources')) { + $body['sources'] = @($Sources | ForEach-Object { @{ name = $_ } }) + } + } + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update syslog server')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'syslog-servers' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'syslog-servers' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Network/Get-PfbDns.ps1 b/Public/Network/Get-PfbDns.ps1 index 44079fa..d7a26e6 100644 --- a/Public/Network/Get-PfbDns.ps1 +++ b/Public/Network/Get-PfbDns.ps1 @@ -2,6 +2,12 @@ function Get-PfbDns { <# .SYNOPSIS Retrieves FlashBlade DNS configuration. + .PARAMETER Filter + A server-side filter expression to narrow results. + .PARAMETER Sort + Sort field and direction. + .PARAMETER Limit + Maximum number of items to return. .PARAMETER Array The FlashBlade connection object. .EXAMPLE @@ -9,9 +15,20 @@ function Get-PfbDns { #> [CmdletBinding()] param( + [Parameter()] + [string]$Filter, + + [Parameter()] + [string]$Sort, + + [Parameter()] + [int]$Limit, + [Parameter()] [PSCustomObject]$Array ) Assert-PfbConnection -Array ([ref]$Array) - Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'dns' -AutoPaginate + $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'dns' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Network/Get-PfbNetworkAccessPolicyMember.ps1 b/Public/Network/Get-PfbNetworkAccessPolicyMember.ps1 index 9a978fb..08dc3a5 100644 --- a/Public/Network/Get-PfbNetworkAccessPolicyMember.ps1 +++ b/Public/Network/Get-PfbNetworkAccessPolicyMember.ps1 @@ -54,9 +54,7 @@ function Get-PfbNetworkAccessPolicyMember { if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'network-access-policies/members' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Network/Get-PfbNetworkConnectionStatistics.ps1 b/Public/Network/Get-PfbNetworkConnectionStatistics.ps1 index 7862143..b00cf96 100644 --- a/Public/Network/Get-PfbNetworkConnectionStatistics.ps1 +++ b/Public/Network/Get-PfbNetworkConnectionStatistics.ps1 @@ -46,10 +46,7 @@ function Get-PfbNetworkConnectionStatistics { } end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'network-interfaces/network-connection-statistics' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Network/Get-PfbNetworkInterface.ps1 b/Public/Network/Get-PfbNetworkInterface.ps1 index 70b1530..d242c04 100644 --- a/Public/Network/Get-PfbNetworkInterface.ps1 +++ b/Public/Network/Get-PfbNetworkInterface.ps1 @@ -44,11 +44,7 @@ function Get-PfbNetworkInterface { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'network-interfaces' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Network/Get-PfbNetworkInterfaceConnector.ps1 b/Public/Network/Get-PfbNetworkInterfaceConnector.ps1 index 13d8473..ae3f291 100644 --- a/Public/Network/Get-PfbNetworkInterfaceConnector.ps1 +++ b/Public/Network/Get-PfbNetworkInterfaceConnector.ps1 @@ -51,11 +51,7 @@ function Get-PfbNetworkInterfaceConnector { } 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'network-interfaces/connectors' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Network/Get-PfbNetworkInterfaceConnectorPerformance.ps1 b/Public/Network/Get-PfbNetworkInterfaceConnectorPerformance.ps1 index 63f4d9a..41aca87 100644 --- a/Public/Network/Get-PfbNetworkInterfaceConnectorPerformance.ps1 +++ b/Public/Network/Get-PfbNetworkInterfaceConnectorPerformance.ps1 @@ -55,10 +55,7 @@ function Get-PfbNetworkInterfaceConnectorPerformance { } end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames if ($StartTime) { $queryParams['start_time'] = $StartTime } if ($EndTime) { $queryParams['end_time'] = $EndTime } if ($Resolution) { $queryParams['resolution'] = $Resolution } diff --git a/Public/Network/Get-PfbNetworkInterfaceConnectorSettings.ps1 b/Public/Network/Get-PfbNetworkInterfaceConnectorSettings.ps1 index 71abe3e..b383912 100644 --- a/Public/Network/Get-PfbNetworkInterfaceConnectorSettings.ps1 +++ b/Public/Network/Get-PfbNetworkInterfaceConnectorSettings.ps1 @@ -46,10 +46,7 @@ function Get-PfbNetworkInterfaceConnectorSettings { } end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'network-interfaces/connectors/settings' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Network/Get-PfbNetworkInterfaceNeighbor.ps1 b/Public/Network/Get-PfbNetworkInterfaceNeighbor.ps1 index d3f8636..6856ee0 100644 --- a/Public/Network/Get-PfbNetworkInterfaceNeighbor.ps1 +++ b/Public/Network/Get-PfbNetworkInterfaceNeighbor.ps1 @@ -47,10 +47,7 @@ function Get-PfbNetworkInterfaceNeighbor { } end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'network-interfaces/neighbors' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Network/Get-PfbNetworkInterfaceTlsPolicy.ps1 b/Public/Network/Get-PfbNetworkInterfaceTlsPolicy.ps1 index 82b8ecd..cd09a50 100644 --- a/Public/Network/Get-PfbNetworkInterfaceTlsPolicy.ps1 +++ b/Public/Network/Get-PfbNetworkInterfaceTlsPolicy.ps1 @@ -54,9 +54,7 @@ function Get-PfbNetworkInterfaceTlsPolicy { if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'network-interfaces/tls-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Network/Get-PfbSubnet.ps1 b/Public/Network/Get-PfbSubnet.ps1 index b9c7585..d3504bf 100644 --- a/Public/Network/Get-PfbSubnet.ps1 +++ b/Public/Network/Get-PfbSubnet.ps1 @@ -44,11 +44,7 @@ function Get-PfbSubnet { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'subnets' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Network/Invoke-PfbNetworkTrace.ps1 b/Public/Network/Invoke-PfbNetworkTrace.ps1 index 3abdedf..aaec164 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/Public/Network/New-PfbNetworkInterfaceTlsPolicy.ps1 b/Public/Network/New-PfbNetworkInterfaceTlsPolicy.ps1 index 82b75d8..17cc947 100644 --- a/Public/Network/New-PfbNetworkInterfaceTlsPolicy.ps1 +++ b/Public/Network/New-PfbNetworkInterfaceTlsPolicy.ps1 @@ -7,9 +7,19 @@ function New-PfbNetworkInterfaceTlsPolicy { interface and a TLS policy on the connected Pure Storage FlashBlade. This controls the TLS settings used for connections on the specified interface. .PARAMETER MemberName - The name of the network interface to associate with the TLS policy. + The name of the network interface to associate with the TLS policy. Sent as the + `member_names` query parameter. Mutually exclusive with -MemberId in effect (the + array rejects both being provided together), but both are declared as plain optional + parameters so either alone resolves the target interface. + .PARAMETER MemberId + The ID of the network interface to associate with the TLS policy. Sent as the + `member_ids` query parameter. .PARAMETER PolicyName - The name of the TLS policy to apply to the network interface. + The name of the TLS policy to apply to the network interface. Sent as the + `policy_names` query parameter. + .PARAMETER PolicyId + The ID of the TLS policy to apply to the network interface. Sent as the `policy_ids` + query parameter. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -24,21 +34,39 @@ function New-PfbNetworkInterfaceTlsPolicy { New-PfbNetworkInterfaceTlsPolicy -MemberName "repl-vip1" -PolicyName "tls-1.2-compat" Associates the tls-1.2-compat policy with the repl-vip1 interface. + .EXAMPLE + New-PfbNetworkInterfaceTlsPolicy -MemberId "10314f42-020d-7080-8013-000ddt400012" -PolicyId "10314f42-020d-7080-8013-000ddt400099" + + Associates the TLS policy and network interface identified by ID. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param( - [Parameter(Mandatory)] [string]$MemberName, - [Parameter(Mandatory)] [string]$PolicyName, + # -MemberName/-MemberId and -PolicyName/-PolicyId are both plain optional parameters + # rather than parameter sets: `POST /network-interfaces/tls-policies` documents that + # member_ids/member_names (and policy_ids/policy_names) cannot be provided together, + # but there is no request body here to multiply a selector axis against (constraint 4 + # is about the -Attributes axis, which this cmdlet does not have). Making either of + # -MemberName/-MemberId mandatory would make the other permanently unusable, so both + # stay optional -- the same shape already shipped on the sibling cmdlet + # New-PfbQosPolicyMember. + [Parameter()] [string]$MemberName, + [Parameter()] [string]$PolicyName, + [Parameter()] [string]$MemberId, + [Parameter()] [string]$PolicyId, [Parameter()] [PSCustomObject]$Array ) Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} - $queryParams['member_names'] = $MemberName - $queryParams['policy_names'] = $PolicyName + if ($PSBoundParameters.ContainsKey('MemberName')) { $queryParams['member_names'] = $MemberName } + if ($PSBoundParameters.ContainsKey('MemberId')) { $queryParams['member_ids'] = $MemberId } + if ($PSBoundParameters.ContainsKey('PolicyName')) { $queryParams['policy_names'] = $PolicyName } + if ($PSBoundParameters.ContainsKey('PolicyId')) { $queryParams['policy_ids'] = $PolicyId } - $target = "${MemberName}:${PolicyName}" + $memberTarget = if ($PSBoundParameters.ContainsKey('MemberName')) { $MemberName } else { $MemberId } + $policyTarget = if ($PSBoundParameters.ContainsKey('PolicyName')) { $PolicyName } else { $PolicyId } + $target = "${memberTarget}:${policyTarget}" if ($PSCmdlet.ShouldProcess($target, 'Add TLS policy to network interface')) { Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'network-interfaces/tls-policies' -QueryParams $queryParams diff --git a/Public/Network/Update-PfbDns.ps1 b/Public/Network/Update-PfbDns.ps1 index 50f01bd..ea00318 100644 --- a/Public/Network/Update-PfbDns.ps1 +++ b/Public/Network/Update-PfbDns.ps1 @@ -2,35 +2,119 @@ function Update-PfbDns { <# .SYNOPSIS Updates FlashBlade DNS configuration. + .DESCRIPTION + The Update-PfbDns cmdlet modifies attributes of a DNS configuration on the connected + Everpure FlashBlade. Supports pipeline input and ShouldProcess for confirmation + prompts. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. The -Name and -Id selectors are declared separately from + those sets, since they are query parameters orthogonal to the request body and stay + usable alongside either. + .PARAMETER Name + Selects the DNS configuration by name. Sent as the `names` query parameter. + .PARAMETER Id + Selects the DNS configuration by ID. Sent as the `ids` query parameter. .PARAMETER Domain The DNS domain name. .PARAMETER Nameservers An array of DNS nameserver IP addresses. + .PARAMETER CaCertificate + A reference to the certificate to use for validating nameservers with https + connections. + .PARAMETER CaCertificateGroup + A reference to the certificate group to use for validating nameservers with https + connections. + .PARAMETER NewName + A new user-specified name for the DNS configuration. + .PARAMETER Services + The list of services utilizing the DNS configuration. + .PARAMETER Sources + The network interfaces used for communication with the DNS server. .PARAMETER Attributes - A hashtable of DNS attributes to update. + A hashtable of DNS attributes to update. Mutually exclusive with the individual + typed parameters above. .PARAMETER Array The FlashBlade connection object. .EXAMPLE Update-PfbDns -Domain "example.com" -Nameservers "10.0.0.1", "10.0.0.2" + + Updates the domain and nameservers using typed parameters. + .EXAMPLE + Update-PfbDns -Name "mgmt-dns" -Sources "vir0" + + Restricts the mgmt-dns configuration to use the vir0 network interface. + .EXAMPLE + Update-PfbDns -Attributes @{ domain = "example.com" } + + Updates the domain using the raw -Attributes hashtable. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', DefaultParameterSetName = 'Individual')] param( - [Parameter()] [string]$Domain, - [Parameter()] [string[]]$Nameservers, - [Parameter()] [hashtable]$Attributes, + # Explicit Position restores the pre-#31 positional-calling convention: adding a + # ParameterSetName to any parameter disables PowerShell's default implicit positional + # binding for the WHOLE function (whole-branch review finding I-1), which would + # otherwise have silently broken `Update-PfbDns $domain $nameservers`. + [Parameter(ParameterSetName = 'Individual', Position = 0)] [string]$Domain, + [Parameter(ParameterSetName = 'Individual', Position = 1)] [string[]]$Nameservers, + + [Parameter()] [string]$Name, + [Parameter()] [string]$Id, + + [Parameter(ParameterSetName = 'Individual')] [string]$CaCertificate, + [Parameter(ParameterSetName = 'Individual')] [string]$CaCertificateGroup, + [Parameter(ParameterSetName = 'Individual')] [string]$NewName, + [Parameter(ParameterSetName = 'Individual')] [string[]]$Services, + [Parameter(ParameterSetName = 'Individual')] [string[]]$Sources, + + [Parameter(ParameterSetName = 'Attributes', Mandatory, Position = 0)] [hashtable]$Attributes, [Parameter()] [PSCustomObject]$Array ) Assert-PfbConnection -Array ([ref]$Array) - if ($Attributes) { $body = $Attributes } + if ($PSCmdlet.ParameterSetName -eq 'Attributes') { + $body = $Attributes + } else { + # EVERY value-carrying parameter is guarded by $PSBoundParameters.ContainsKey, never by + # truthiness -- see Update-PfbAdmin.ps1 for the full rationale. -Domain, -NewName, and + # -CaCertificate/-CaCertificateGroup are all optional [string]s, so an empty string is + # a legitimate explicit value that truthiness would silently drop. $body = @{} - if ($Domain) { $body['domain'] = $Domain } - if ($Nameservers) { $body['nameservers'] = $Nameservers } + if ($PSBoundParameters.ContainsKey('Domain')) { $body['domain'] = $Domain } + if ($PSBoundParameters.ContainsKey('Nameservers')) { $body['nameservers'] = @($Nameservers) } + + # Constraint 8(a): ca_certificate/ca_certificate_group are SCALAR references (item + # schema is {id, name, resource_type}), so the parameter is [string] and the + # projection is assigned INLINE as a name-reference hashtable -- constraint 7 forbids + # an intermediate local. + if ($PSBoundParameters.ContainsKey('CaCertificate')) { $body['ca_certificate'] = @{ name = $CaCertificate } } + if ($PSBoundParameters.ContainsKey('CaCertificateGroup')) { $body['ca_certificate_group'] = @{ name = $CaCertificateGroup } } + + # Exception: the body field is literally `name` (renames the resource), so the + # parameter is -NewName, never -DnsName -- see Update-PfbWorkload / Update-PfbDataEvictionPolicy. + if ($PSBoundParameters.ContainsKey('NewName')) { $body['name'] = $NewName } + + if ($PSBoundParameters.ContainsKey('Services')) { $body['services'] = @($Services) } + + # Constraint 8(b): sources is an ARRAY OF REFERENCES (item schema is + # {id, name, resource_type}), so the parameter is [string[]] and the projection is + # assigned INLINE. + if ($PSBoundParameters.ContainsKey('Sources')) { $body['sources'] = @($Sources | ForEach-Object { @{ name = $_ } }) } } - if ($PSCmdlet.ShouldProcess('DNS', 'Update DNS configuration')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'dns' -Body $body + # -Name/-Id are declared bare (not in the Individual/Attributes sets, constraint 17): they + # are query parameters, orthogonal to the request body, and must stay usable alongside + # -Attributes. + $queryParams = @{} + if ($PSBoundParameters.ContainsKey('Name')) { $queryParams['names'] = $Name } + if ($PSBoundParameters.ContainsKey('Id')) { $queryParams['ids'] = $Id } + + $target = if ($PSBoundParameters.ContainsKey('Name')) { $Name } elseif ($PSBoundParameters.ContainsKey('Id')) { $Id } else { 'DNS' } + if ($PSCmdlet.ShouldProcess($target, 'Update DNS configuration')) { + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'dns' -Body $body -QueryParams $queryParams } } diff --git a/Public/Network/Update-PfbNetworkInterface.ps1 b/Public/Network/Update-PfbNetworkInterface.ps1 index 048fb23..fb4b9a7 100644 --- a/Public/Network/Update-PfbNetworkInterface.ps1 +++ b/Public/Network/Update-PfbNetworkInterface.ps1 @@ -2,31 +2,76 @@ function Update-PfbNetworkInterface { <# .SYNOPSIS Updates a network interface on the FlashBlade. + .DESCRIPTION + The Update-PfbNetworkInterface cmdlet modifies attributes of a network interface on + the connected Everpure FlashBlade. The target interface can be identified by name or + ID. Supports pipeline input and ShouldProcess for confirmation prompts. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the network interface to update. .PARAMETER Id The ID of the network interface to update. .PARAMETER Address - The new IP address. + The new IPv4 or IPv6 address to associate with the network interface. + .PARAMETER AttachedServers + Servers to be associated with the network interface for data ingress. Pass an empty + array to detach the interface from all servers. + .PARAMETER RdmaEnabled + If `$true`, RDMA is enabled on the network interface. Only supported on interfaces + whose services include `data`. + .PARAMETER Services + Services and protocols that are enabled on the interface. .PARAMETER Attributes - A hashtable of attributes to update. + A hashtable of attributes to update. Mutually exclusive with the individual typed + parameters above. .PARAMETER Array The FlashBlade connection object. .EXAMPLE Update-PfbNetworkInterface -Name "vir0" -Address "10.0.0.101" + + Updates the address of "vir0" using a typed parameter. + .EXAMPLE + Update-PfbNetworkInterface -Name "vir0" -AttachedServers "CH1.FM1", "CH1.FM2" + + Attaches "vir0" to two servers for data ingress. + .EXAMPLE + Update-PfbNetworkInterface -Name "vir0" -Attributes @{ address = "10.0.0.101" } + + Updates the address of "vir0" using the raw -Attributes hashtable. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [string]$Id, - [Parameter()] + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] [string]$Address, - [Parameter()] + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$AttachedServers, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [Nullable[bool]]$RdmaEnabled, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$Services, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [hashtable]$Attributes, [Parameter()] @@ -38,12 +83,28 @@ function Update-PfbNetworkInterface { } process { - if ($Attributes) { + if ($PSCmdlet.ParameterSetName -like '*Attributes') { $body = $Attributes } else { + # EVERY value-carrying parameter is guarded by $PSBoundParameters.ContainsKey, + # never by truthiness -- see Update-PfbAdmin.ps1 for the full rationale. -Address + # previously used `if ($Address)`, which silently dropped an explicit + # -Address '' and (combined with living outside any parameter set) let + # -Attributes silently override it -- the exact silent-override failure this + # issue exists to eliminate. $body = @{} - if ($Address) { $body['address'] = $Address } + if ($PSBoundParameters.ContainsKey('Address')) { $body['address'] = $Address } + + # Constraint 8(b): attached_servers is an ARRAY OF REFERENCES (item schema is + # {id, name}), so the parameter is [string[]] and the projection is assigned + # INLINE -- constraint 7 forbids an intermediate local. + if ($PSBoundParameters.ContainsKey('AttachedServers')) { + $body['attached_servers'] = @($AttachedServers | ForEach-Object { @{ name = $_ } }) + } + + if ($PSBoundParameters.ContainsKey('RdmaEnabled')) { $body['rdma_enabled'] = $RdmaEnabled } + if ($PSBoundParameters.ContainsKey('Services')) { $body['services'] = @($Services) } } $queryParams = @{} diff --git a/Public/Network/Update-PfbNetworkInterfaceConnector.ps1 b/Public/Network/Update-PfbNetworkInterfaceConnector.ps1 index 406b966..25b73d7 100644 --- a/Public/Network/Update-PfbNetworkInterfaceConnector.ps1 +++ b/Public/Network/Update-PfbNetworkInterfaceConnector.ps1 @@ -4,14 +4,28 @@ function Update-PfbNetworkInterfaceConnector { Updates a network interface connector on a FlashBlade array. .DESCRIPTION The Update-PfbNetworkInterfaceConnector cmdlet modifies attributes of an existing - network interface connector on the connected Pure Storage FlashBlade. The connector + network interface connector on the connected Everpure FlashBlade. The connector can be identified by name or ID. Supports ShouldProcess for -WhatIf and -Confirm. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the network interface connector to update. .PARAMETER Id The ID of the network interface connector to update. + .PARAMETER LaneSpeed + Configured speed of each lane in the connector in bits-per-second. + .PARAMETER LanesPerPort + Configured number of lanes comprising each port in the connector. + .PARAMETER PortCount + Configured number of ports in the connector (1/2/4 for QSFP). + .PARAMETER PortSpeed + Configured speed of each port in the connector in bits-per-second. .PARAMETER Attributes A hashtable of connector attributes to modify, such as enabled state or port speed. + Mutually exclusive with the individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -26,12 +40,42 @@ function Update-PfbNetworkInterfaceConnector { Update-PfbNetworkInterfaceConnector -Name "CH1.FM1.ETH2" -Attributes @{ enabled = $true } -WhatIf Shows what would happen without applying the change. + .EXAMPLE + Update-PfbNetworkInterfaceConnector -Name "CH1.FM1.ETH1" -LaneSpeed 10000000000 -LanesPerPort 4 + + Sets the lane speed and lanes-per-port using typed parameters. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$LaneSpeed, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$LanesPerPort, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$PortCount, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$PortSpeed, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -42,8 +86,23 @@ function Update-PfbNetworkInterfaceConnector { if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } $target = if ($Name) { $Name } else { $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # EVERY value-carrying parameter is guarded by $PSBoundParameters.ContainsKey, + # never by truthiness -- see Update-PfbAdmin.ps1 for the full rationale. All four + # fields here are integers, so an explicit 0 must reach the wire (constraint 2). + $body = @{} + if ($PSBoundParameters.ContainsKey('LaneSpeed')) { $body['lane_speed'] = $LaneSpeed } + if ($PSBoundParameters.ContainsKey('LanesPerPort')) { $body['lanes_per_port'] = $LanesPerPort } + if ($PSBoundParameters.ContainsKey('PortCount')) { $body['port_count'] = $PortCount } + if ($PSBoundParameters.ContainsKey('PortSpeed')) { $body['port_speed'] = $PortSpeed } + } + if ($PSCmdlet.ShouldProcess($target, 'Update network interface connector')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'network-interfaces/connectors' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'network-interfaces/connectors' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Network/Update-PfbSubnet.ps1 b/Public/Network/Update-PfbSubnet.ps1 index bad99a7..d15b550 100644 --- a/Public/Network/Update-PfbSubnet.ps1 +++ b/Public/Network/Update-PfbSubnet.ps1 @@ -2,35 +2,83 @@ function Update-PfbSubnet { <# .SYNOPSIS Updates a subnet on the FlashBlade. + .DESCRIPTION + The Update-PfbSubnet cmdlet modifies attributes of a subnet on the connected Everpure + FlashBlade. The target subnet can be identified by name or ID. Supports pipeline + input and ShouldProcess for confirmation prompts. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the subnet to update. .PARAMETER Id The ID of the subnet to update. .PARAMETER Prefix - The new subnet prefix. + The IPv4 or IPv6 address to be associated with the subnet. .PARAMETER Gateway - The new gateway IP address. + The IPv4 or IPv6 address of the gateway through which the subnet communicates with the + network. .PARAMETER Mtu - The new MTU size. + Maximum message transfer unit (packet) size for the subnet in bytes. + .PARAMETER LinkAggregationGroup + A reference to the associated LAG. + .PARAMETER Vlan + The VLAN ID. .PARAMETER Attributes - A hashtable of attributes to update. + A hashtable of attributes to update. Mutually exclusive with the individual typed + parameters above. .PARAMETER Array The FlashBlade connection object. .EXAMPLE Update-PfbSubnet -Name "subnet1" -Gateway "10.0.0.254" + + Updates the gateway of "subnet1" using a typed parameter. + .EXAMPLE + Update-PfbSubnet -Name "subnet1" -Vlan 100 -LinkAggregationGroup "lag1" + + Sets the VLAN ID and link aggregation group of "subnet1". + .EXAMPLE + Update-PfbSubnet -Name "subnet1" -Attributes @{ mtu = 9000 } + + Updates the MTU of "subnet1" using the raw -Attributes hashtable. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [string]$Id, - [Parameter()] [string]$Prefix, - [Parameter()] [string]$Gateway, - [Parameter()] [int]$Mtu, - [Parameter()] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Prefix, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Gateway, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [int]$Mtu, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$LinkAggregationGroup, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [int]$Vlan, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) @@ -39,12 +87,29 @@ function Update-PfbSubnet { } process { - if ($Attributes) { $body = $Attributes } + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } else { + # EVERY value-carrying parameter is guarded by $PSBoundParameters.ContainsKey, + # never by truthiness -- see Update-PfbAdmin.ps1 for the full rationale. -Mtu + # previously used `if ($Mtu -gt 0)`, which could never send an explicit 0 (and, + # combined with living outside any parameter set, let -Attributes silently + # override it -- the exact silent-override failure this issue exists to + # eliminate). $body = @{} - if ($Prefix) { $body['prefix'] = $Prefix } - if ($Gateway) { $body['gateway'] = $Gateway } - if ($Mtu -gt 0) { $body['mtu'] = $Mtu } + if ($PSBoundParameters.ContainsKey('Prefix')) { $body['prefix'] = $Prefix } + if ($PSBoundParameters.ContainsKey('Gateway')) { $body['gateway'] = $Gateway } + if ($PSBoundParameters.ContainsKey('Mtu')) { $body['mtu'] = $Mtu } + + # Constraint 8(a): link_aggregation_group is a SCALAR reference (item schema is + # {id, name, resource_type}), so the parameter is [string] and the projection is + # assigned INLINE as a name-reference hashtable. + if ($PSBoundParameters.ContainsKey('LinkAggregationGroup')) { + $body['link_aggregation_group'] = @{ name = $LinkAggregationGroup } + } + + if ($PSBoundParameters.ContainsKey('Vlan')) { $body['vlan'] = $Vlan } } $queryParams = @{} diff --git a/Public/Node/Get-PfbNode.ps1 b/Public/Node/Get-PfbNode.ps1 index f5288a0..058cc5b 100644 --- a/Public/Node/Get-PfbNode.ps1 +++ b/Public/Node/Get-PfbNode.ps1 @@ -55,11 +55,7 @@ function Get-PfbNode { } 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds try { Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'nodes' -QueryParams $queryParams -AutoPaginate diff --git a/Public/Node/Get-PfbNodeGroup.ps1 b/Public/Node/Get-PfbNodeGroup.ps1 index 2bc4231..978d587 100644 --- a/Public/Node/Get-PfbNodeGroup.ps1 +++ b/Public/Node/Get-PfbNodeGroup.ps1 @@ -51,11 +51,7 @@ function Get-PfbNodeGroup { } 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds try { Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'node-groups' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Node/Get-PfbNodeGroupNode.ps1 b/Public/Node/Get-PfbNodeGroupNode.ps1 index 66a5888..8f00bfe 100644 --- a/Public/Node/Get-PfbNodeGroupNode.ps1 +++ b/Public/Node/Get-PfbNodeGroupNode.ps1 @@ -54,9 +54,7 @@ function Get-PfbNodeGroupNode { if ($GroupId) { $queryParams['group_ids'] = $GroupId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters try { Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'node-groups/nodes' -QueryParams $queryParams -AutoPaginate diff --git a/Public/Node/Get-PfbNodeGroupUse.ps1 b/Public/Node/Get-PfbNodeGroupUse.ps1 index c4efe45..6da8a1d 100644 --- a/Public/Node/Get-PfbNodeGroupUse.ps1 +++ b/Public/Node/Get-PfbNodeGroupUse.ps1 @@ -46,10 +46,7 @@ function Get-PfbNodeGroupUse { } end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames try { Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'node-groups/uses' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Node/New-PfbNodeGroupNode.ps1 b/Public/Node/New-PfbNodeGroupNode.ps1 index d2689f7..8f49ca2 100644 --- a/Public/Node/New-PfbNodeGroupNode.ps1 +++ b/Public/Node/New-PfbNodeGroupNode.ps1 @@ -7,9 +7,15 @@ function New-PfbNodeGroupNode { group on the connected Pure Storage FlashBlade. This assigns the specified node to the target group for workload placement purposes. .PARAMETER GroupName - The name of the node group to add the node to. + The name of the node group to add the node to. Sent as the `node_group_names` query + parameter. + .PARAMETER GroupId + The ID of the node group to add the node to. Sent as the `node_group_ids` query + parameter. .PARAMETER MemberName - The name of the node to add to the group. + The name of the node to add to the group. Sent as the `node_names` query parameter. + .PARAMETER MemberId + The ID of the node to add to the group. Sent as the `node_ids` query parameter. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -24,21 +30,37 @@ function New-PfbNodeGroupNode { New-PfbNodeGroupNode -GroupName "default-group" -MemberName "CH1.FB3" Adds node CH1.FB3 to the default-group. + .EXAMPLE + New-PfbNodeGroupNode -GroupId "10314f42-020d-7080-8013-000ddt400020" -MemberId "10314f42-020d-7080-8013-000ddt400005" + + Adds the node identified by ID to the node group identified by ID. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param( - [Parameter(Mandatory)] [string]$GroupName, - [Parameter(Mandatory)] [string]$MemberName, + # -GroupName/-GroupId and -MemberName/-MemberId are plain optional parameters rather + # than parameter sets -- see New-PfbNetworkInterfaceTlsPolicy.ps1 for the identical + # shape and rationale (no request body here to multiply a selector axis against). + [Parameter()] [string]$GroupName, + [Parameter()] [string]$MemberName, + [Parameter()] [string]$GroupId, + [Parameter()] [string]$MemberId, [Parameter()] [PSCustomObject]$Array ) Assert-PfbConnection -Array ([ref]$Array) + # Wire-correctness fix: POST /node-groups/nodes documents node_group_ids/node_group_names + # and node_ids/node_names. This cmdlet previously sent group_names/member_names, which the + # array silently ignored -- neither -GroupName nor -MemberName had any effect. $queryParams = @{} - $queryParams['group_names'] = $GroupName - $queryParams['member_names'] = $MemberName + if ($PSBoundParameters.ContainsKey('GroupName')) { $queryParams['node_group_names'] = $GroupName } + if ($PSBoundParameters.ContainsKey('GroupId')) { $queryParams['node_group_ids'] = $GroupId } + if ($PSBoundParameters.ContainsKey('MemberName')) { $queryParams['node_names'] = $MemberName } + if ($PSBoundParameters.ContainsKey('MemberId')) { $queryParams['node_ids'] = $MemberId } - $target = "${GroupName}:${MemberName}" + $groupTarget = if ($PSBoundParameters.ContainsKey('GroupName')) { $GroupName } else { $GroupId } + $memberTarget = if ($PSBoundParameters.ContainsKey('MemberName')) { $MemberName } else { $MemberId } + $target = "${groupTarget}:${memberTarget}" if ($PSCmdlet.ShouldProcess($target, 'Add node to node group')) { Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'node-groups/nodes' -QueryParams $queryParams diff --git a/Public/Node/Update-PfbNode.ps1 b/Public/Node/Update-PfbNode.ps1 index 105826e..c8f06c6 100644 --- a/Public/Node/Update-PfbNode.ps1 +++ b/Public/Node/Update-PfbNode.ps1 @@ -3,15 +3,32 @@ function Update-PfbNode { .SYNOPSIS Updates a node on a FlashBlade array. .DESCRIPTION - The Update-PfbNode cmdlet modifies attributes of a node on the connected Pure Storage + The Update-PfbNode cmdlet modifies attributes of a node on the connected Everpure FlashBlade. The node can be identified by name or ID. Supports ShouldProcess for -WhatIf and -Confirm. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the node to update. .PARAMETER Id The ID of the node to update. + .PARAMETER ManagementAddress + The control IP address of the node. A connection is made to this address to get + information on the node, such as the data addresses. + .PARAMETER NewName + A new user-specified name for the node. + .PARAMETER NodeKey + A key used to bootstrap a mTLS connection with the node being connected to. Cannot be + specified together with -ManagementAddress or -SerialNumber. + .PARAMETER SerialNumber + The serial number of the node. If the given serial number does not match the serial + number of the node, the request fails. .PARAMETER Attributes - A hashtable of node attributes to modify. + A hashtable of node attributes to modify. Mutually exclusive with the individual typed + parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -26,12 +43,42 @@ function Update-PfbNode { Update-PfbNode -Name "CH1.FB1" -Attributes @{ identify_enabled = $true } -WhatIf Shows what would happen without applying the change. + .EXAMPLE + Update-PfbNode -Name "CH1.FB1" -ManagementAddress "10.0.0.5" + + Updates the node's management address using a typed parameter. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$ManagementAddress, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$NewName, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$NodeKey, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$SerialNumber, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -42,8 +89,26 @@ function Update-PfbNode { if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } $target = if ($Name) { $Name } else { $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # EVERY value-carrying parameter is guarded by $PSBoundParameters.ContainsKey, + # never by truthiness -- see Update-PfbAdmin.ps1 for the full rationale. + $body = @{} + if ($PSBoundParameters.ContainsKey('ManagementAddress')) { $body['management_address'] = $ManagementAddress } + + # Exception: the body field is literally `name` (renames the resource), so the + # parameter is -NewName, never -NodeName -- see Update-PfbWorkload / Update-PfbDataEvictionPolicy. + if ($PSBoundParameters.ContainsKey('NewName')) { $body['name'] = $NewName } + + if ($PSBoundParameters.ContainsKey('NodeKey')) { $body['node_key'] = $NodeKey } + if ($PSBoundParameters.ContainsKey('SerialNumber')) { $body['serial_number'] = $SerialNumber } + } + if ($PSCmdlet.ShouldProcess($target, 'Update node')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'nodes' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'nodes' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Node/Update-PfbNodeGroup.ps1 b/Public/Node/Update-PfbNodeGroup.ps1 index 92b5e10..d343970 100644 --- a/Public/Node/Update-PfbNodeGroup.ps1 +++ b/Public/Node/Update-PfbNodeGroup.ps1 @@ -4,20 +4,28 @@ function Update-PfbNodeGroup { Updates an existing node group on a FlashBlade array. .DESCRIPTION The Update-PfbNodeGroup cmdlet modifies attributes of an existing node group on the - connected Pure Storage FlashBlade. The group can be identified by name or ID. - Supports ShouldProcess for -WhatIf and -Confirm. + connected Everpure FlashBlade. The group can be identified by name or ID. Supports + ShouldProcess for -WhatIf and -Confirm. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the node group to update. .PARAMETER Id The ID of the node group to update. + .PARAMETER NewName + A new user-specified name for the node group. .PARAMETER Attributes - A hashtable of node group attributes to modify. + A hashtable of node group attributes to modify. Mutually exclusive with the individual + typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE Update-PfbNodeGroup -Name "analytics-group" -Attributes @{ name = 'analytics-primary' } - Renames the node group. + Renames the node group using the raw -Attributes hashtable. .EXAMPLE Update-PfbNodeGroup -Id "10314f42-020d-7080-8013-000ddt400020" -Attributes @{ priority = 'low' } @@ -26,12 +34,33 @@ function Update-PfbNodeGroup { Update-PfbNodeGroup -Name "test-group" -Attributes @{ name = 'prod-group' } -WhatIf Shows what would happen without applying the change. + .EXAMPLE + Update-PfbNodeGroup -Name "analytics-group" -NewName "analytics-primary" + + Renames the node group using the typed -NewName parameter. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + # Exception: the body field is literally `name` (renames the resource), so the + # parameter is -NewName, never -NodeGroupName -- see Update-PfbWorkload / + # Update-PfbDataEvictionPolicy. + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$NewName, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -42,8 +71,19 @@ function Update-PfbNodeGroup { if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } $target = if ($Name) { $Name } else { $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # EVERY value-carrying parameter is guarded by $PSBoundParameters.ContainsKey, + # never by truthiness -- see Update-PfbAdmin.ps1 for the full rationale. + $body = @{} + if ($PSBoundParameters.ContainsKey('NewName')) { $body['name'] = $NewName } + } + if ($PSCmdlet.ShouldProcess($target, 'Update node group')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'node-groups' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'node-groups' -Body $body -QueryParams $queryParams } } } diff --git a/Public/ObjectStore/Get-PfbObjectStoreAccessKey.ps1 b/Public/ObjectStore/Get-PfbObjectStoreAccessKey.ps1 index f4ac5bb..ea46bc0 100644 --- a/Public/ObjectStore/Get-PfbObjectStoreAccessKey.ps1 +++ b/Public/ObjectStore/Get-PfbObjectStoreAccessKey.ps1 @@ -44,11 +44,7 @@ function Get-PfbObjectStoreAccessKey { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'object-store-access-keys' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/ObjectStore/Get-PfbObjectStoreAccessPolicy.ps1 b/Public/ObjectStore/Get-PfbObjectStoreAccessPolicy.ps1 index af9e405..be43a84 100644 --- a/Public/ObjectStore/Get-PfbObjectStoreAccessPolicy.ps1 +++ b/Public/ObjectStore/Get-PfbObjectStoreAccessPolicy.ps1 @@ -58,12 +58,7 @@ function Get-PfbObjectStoreAccessPolicy { 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 } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'object-store-access-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/ObjectStore/Get-PfbObjectStoreAccessPolicyAction.ps1 b/Public/ObjectStore/Get-PfbObjectStoreAccessPolicyAction.ps1 index 3e77670..5c66e3f 100644 --- a/Public/ObjectStore/Get-PfbObjectStoreAccessPolicyAction.ps1 +++ b/Public/ObjectStore/Get-PfbObjectStoreAccessPolicyAction.ps1 @@ -38,10 +38,7 @@ function Get-PfbObjectStoreAccessPolicyAction { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'object-store-access-policy-actions' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/ObjectStore/Get-PfbObjectStoreAccessPolicyRole.ps1 b/Public/ObjectStore/Get-PfbObjectStoreAccessPolicyRole.ps1 index 10c7094..f6f4e09 100644 --- a/Public/ObjectStore/Get-PfbObjectStoreAccessPolicyRole.ps1 +++ b/Public/ObjectStore/Get-PfbObjectStoreAccessPolicyRole.ps1 @@ -72,14 +72,11 @@ function Get-PfbObjectStoreAccessPolicyRole { end { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($allPolicyNames.Count -gt 0) { $queryParams['policy_names'] = $allPolicyNames -join ',' } if ($allPolicyIds.Count -gt 0) { $queryParams['policy_ids'] = $allPolicyIds -join ',' } if ($allMemberNames.Count -gt 0) { $queryParams['member_names'] = $allMemberNames -join ',' } if ($allMemberIds.Count -gt 0) { $queryParams['member_ids'] = $allMemberIds -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } $response = Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'object-store-access-policies/object-store-roles' -QueryParams $queryParams -AutoPaginate foreach ($item in $response) { diff --git a/Public/ObjectStore/Get-PfbObjectStoreAccessPolicyRule.ps1 b/Public/ObjectStore/Get-PfbObjectStoreAccessPolicyRule.ps1 index 1b0b682..fe61cba 100644 --- a/Public/ObjectStore/Get-PfbObjectStoreAccessPolicyRule.ps1 +++ b/Public/ObjectStore/Get-PfbObjectStoreAccessPolicyRule.ps1 @@ -65,13 +65,9 @@ function Get-PfbObjectStoreAccessPolicyRule { end { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames if ($allPolicyNames.Count -gt 0) { $queryParams['policy_names'] = $allPolicyNames -join ',' } if ($allPolicyIds.Count -gt 0) { $queryParams['policy_ids'] = $allPolicyIds -join ',' } - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'object-store-access-policies/rules' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/ObjectStore/Get-PfbObjectStoreAccessPolicyUser.ps1 b/Public/ObjectStore/Get-PfbObjectStoreAccessPolicyUser.ps1 index 00617af..46dce90 100644 --- a/Public/ObjectStore/Get-PfbObjectStoreAccessPolicyUser.ps1 +++ b/Public/ObjectStore/Get-PfbObjectStoreAccessPolicyUser.ps1 @@ -72,14 +72,11 @@ function Get-PfbObjectStoreAccessPolicyUser { end { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($allPolicyNames.Count -gt 0) { $queryParams['policy_names'] = $allPolicyNames -join ',' } if ($allPolicyIds.Count -gt 0) { $queryParams['policy_ids'] = $allPolicyIds -join ',' } if ($allMemberNames.Count -gt 0) { $queryParams['member_names'] = $allMemberNames -join ',' } if ($allMemberIds.Count -gt 0) { $queryParams['member_ids'] = $allMemberIds -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } $response = Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'object-store-access-policies/object-store-users' -QueryParams $queryParams -AutoPaginate foreach ($item in $response) { diff --git a/Public/ObjectStore/Get-PfbObjectStoreAccount.ps1 b/Public/ObjectStore/Get-PfbObjectStoreAccount.ps1 index 21f3a53..2f0f930 100644 --- a/Public/ObjectStore/Get-PfbObjectStoreAccount.ps1 +++ b/Public/ObjectStore/Get-PfbObjectStoreAccount.ps1 @@ -47,12 +47,7 @@ function Get-PfbObjectStoreAccount { 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 } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'object-store-accounts' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/ObjectStore/Get-PfbObjectStoreAccountExport.ps1 b/Public/ObjectStore/Get-PfbObjectStoreAccountExport.ps1 index 7f08f88..bd56541 100644 --- a/Public/ObjectStore/Get-PfbObjectStoreAccountExport.ps1 +++ b/Public/ObjectStore/Get-PfbObjectStoreAccountExport.ps1 @@ -57,12 +57,7 @@ function Get-PfbObjectStoreAccountExport { 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 } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'object-store-account-exports' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/ObjectStore/Get-PfbObjectStoreRemoteCredential.ps1 b/Public/ObjectStore/Get-PfbObjectStoreRemoteCredential.ps1 index bc08bf4..9865d5b 100644 --- a/Public/ObjectStore/Get-PfbObjectStoreRemoteCredential.ps1 +++ b/Public/ObjectStore/Get-PfbObjectStoreRemoteCredential.ps1 @@ -58,12 +58,7 @@ function Get-PfbObjectStoreRemoteCredential { 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 } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'object-store-remote-credentials' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/ObjectStore/Get-PfbObjectStoreRole.ps1 b/Public/ObjectStore/Get-PfbObjectStoreRole.ps1 index c3213b9..cdc6f38 100644 --- a/Public/ObjectStore/Get-PfbObjectStoreRole.ps1 +++ b/Public/ObjectStore/Get-PfbObjectStoreRole.ps1 @@ -58,12 +58,7 @@ function Get-PfbObjectStoreRole { 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 } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'object-store-roles' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/ObjectStore/Get-PfbObjectStoreRoleAccessPolicy.ps1 b/Public/ObjectStore/Get-PfbObjectStoreRoleAccessPolicy.ps1 index 3086620..ba5fec1 100644 --- a/Public/ObjectStore/Get-PfbObjectStoreRoleAccessPolicy.ps1 +++ b/Public/ObjectStore/Get-PfbObjectStoreRoleAccessPolicy.ps1 @@ -72,14 +72,11 @@ function Get-PfbObjectStoreRoleAccessPolicy { end { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($allRoleNames.Count -gt 0) { $queryParams['role_names'] = $allRoleNames -join ',' } if ($allRoleIds.Count -gt 0) { $queryParams['role_ids'] = $allRoleIds -join ',' } if ($allMemberNames.Count -gt 0) { $queryParams['member_names'] = $allMemberNames -join ',' } if ($allMemberIds.Count -gt 0) { $queryParams['member_ids'] = $allMemberIds -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'object-store-roles/object-store-access-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/ObjectStore/Get-PfbObjectStoreTrustPolicy.ps1 b/Public/ObjectStore/Get-PfbObjectStoreTrustPolicy.ps1 index eeae729..2790683 100644 --- a/Public/ObjectStore/Get-PfbObjectStoreTrustPolicy.ps1 +++ b/Public/ObjectStore/Get-PfbObjectStoreTrustPolicy.ps1 @@ -58,12 +58,9 @@ function Get-PfbObjectStoreTrustPolicy { end { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($allRoleNames.Count -gt 0) { $queryParams['role_names'] = $allRoleNames -join ',' } if ($allRoleIds.Count -gt 0) { $queryParams['role_ids'] = $allRoleIds -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'object-store-roles/object-store-trust-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/ObjectStore/Get-PfbObjectStoreTrustPolicyRule.ps1 b/Public/ObjectStore/Get-PfbObjectStoreTrustPolicyRule.ps1 index e3999db..622678c 100644 --- a/Public/ObjectStore/Get-PfbObjectStoreTrustPolicyRule.ps1 +++ b/Public/ObjectStore/Get-PfbObjectStoreTrustPolicyRule.ps1 @@ -65,13 +65,9 @@ function Get-PfbObjectStoreTrustPolicyRule { end { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames if ($allPolicyNames.Count -gt 0) { $queryParams['policy_names'] = $allPolicyNames -join ',' } if ($allPolicyIds.Count -gt 0) { $queryParams['policy_ids'] = $allPolicyIds -join ',' } - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'object-store-roles/object-store-trust-policies/rules' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/ObjectStore/Get-PfbObjectStoreUser.ps1 b/Public/ObjectStore/Get-PfbObjectStoreUser.ps1 index d69a31f..e2410ca 100644 --- a/Public/ObjectStore/Get-PfbObjectStoreUser.ps1 +++ b/Public/ObjectStore/Get-PfbObjectStoreUser.ps1 @@ -44,11 +44,7 @@ function Get-PfbObjectStoreUser { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'object-store-users' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/ObjectStore/Get-PfbObjectStoreUserAccessPolicy.ps1 b/Public/ObjectStore/Get-PfbObjectStoreUserAccessPolicy.ps1 index 9688e74..2539c63 100644 --- a/Public/ObjectStore/Get-PfbObjectStoreUserAccessPolicy.ps1 +++ b/Public/ObjectStore/Get-PfbObjectStoreUserAccessPolicy.ps1 @@ -72,14 +72,11 @@ function Get-PfbObjectStoreUserAccessPolicy { end { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($allMemberNames.Count -gt 0) { $queryParams['member_names'] = $allMemberNames -join ',' } if ($allMemberIds.Count -gt 0) { $queryParams['member_ids'] = $allMemberIds -join ',' } if ($allPolicyNames.Count -gt 0) { $queryParams['policy_names'] = $allPolicyNames -join ',' } if ($allPolicyIds.Count -gt 0) { $queryParams['policy_ids'] = $allPolicyIds -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } $response = Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'object-store-users/object-store-access-policies' -QueryParams $queryParams -AutoPaginate foreach ($item in $response) { diff --git a/Public/ObjectStore/Get-PfbObjectStoreVirtualHost.ps1 b/Public/ObjectStore/Get-PfbObjectStoreVirtualHost.ps1 index a737653..89478ac 100644 --- a/Public/ObjectStore/Get-PfbObjectStoreVirtualHost.ps1 +++ b/Public/ObjectStore/Get-PfbObjectStoreVirtualHost.ps1 @@ -58,11 +58,7 @@ function Get-PfbObjectStoreVirtualHost { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'object-store-virtual-hosts' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/ObjectStore/Update-PfbObjectStoreAccountExport.ps1 b/Public/ObjectStore/Update-PfbObjectStoreAccountExport.ps1 index 2ef07e1..312fb0c 100644 --- a/Public/ObjectStore/Update-PfbObjectStoreAccountExport.ps1 +++ b/Public/ObjectStore/Update-PfbObjectStoreAccountExport.ps1 @@ -5,14 +5,28 @@ function Update-PfbObjectStoreAccountExport { .DESCRIPTION Modifies the properties of an existing account export, such as its enabled state or export rules. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the account export to update. .PARAMETER Id The ID of the account export to update. + .PARAMETER ExportEnabled + If set to `true`, the account export is enabled. + .PARAMETER Policy + Reference to the s3 export policy that is used for the export. .PARAMETER Attributes - A hashtable of export properties to update. + A hashtable of export properties to update. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. + .EXAMPLE + Update-PfbObjectStoreAccountExport -Name "nfs-export-1" -ExportEnabled:$false + + Disables the specified account export using a typed parameter. .EXAMPLE Update-PfbObjectStoreAccountExport -Name "nfs-export-1" -Attributes @{ enabled = $false @@ -27,15 +41,27 @@ function Update-PfbObjectStoreAccountExport { Update-PfbObjectStoreAccountExport -Name "export-acct-prod" -Attributes @{} Sends an empty update to refresh the export object. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [string]$Id, - [Parameter()] + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [Nullable[bool]]$ExportEnabled, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Policy, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [hashtable]$Attributes, [Parameter()] [PSCustomObject]$Array @@ -46,12 +72,24 @@ function Update-PfbObjectStoreAccountExport { } process { - $target = if ($Name) { $Name } else { $Id } - $body = if ($Attributes) { $Attributes } else { @{} } $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('ExportEnabled')) { $body['export_enabled'] = $ExportEnabled } + + # Constraint 8(a): policy is a SCALAR REFERENCE (item schema is {id, name, + # resource_type}), so the parameter is [string] and the projection is assigned + # INLINE -- constraint 7 forbids a local variable here. + if ($PSBoundParameters.ContainsKey('Policy')) { $body['policy'] = @{ name = $Policy } } + } + + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update object store account export')) { Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'object-store-account-exports' -Body $body -QueryParams $queryParams } diff --git a/Public/ObjectStore/Update-PfbObjectStoreRemoteCredential.ps1 b/Public/ObjectStore/Update-PfbObjectStoreRemoteCredential.ps1 index a1d4340..3e787a3 100644 --- a/Public/ObjectStore/Update-PfbObjectStoreRemoteCredential.ps1 +++ b/Public/ObjectStore/Update-PfbObjectStoreRemoteCredential.ps1 @@ -6,15 +6,34 @@ function Update-PfbObjectStoreRemoteCredential { Modifies the properties of an existing remote credential, such as rotating the access key or secret key used for replication to an external S3-compatible target. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the remote credential to update. .PARAMETER Id The ID of the remote credential to update. + .PARAMETER AccessKeyId + Access Key ID to be used when connecting to a remote object store. + .PARAMETER NewName + A new user-specified name for the remote credential. + .PARAMETER Remote + Reference to the associated remote, which can either be a target or remote array. + .PARAMETER SecretAccessKey + Secret Access Key to be used when connecting to a remote object store. Treated as + sensitive: never logged or echoed via -Verbose. .PARAMETER Attributes A hashtable of credential properties to update, such as access_key_id - and secret_access_key. + and secret_access_key. Mutually exclusive with the individual typed + parameters above. .PARAMETER Array The FlashBlade connection object. + .EXAMPLE + Update-PfbObjectStoreRemoteCredential -Name "s3-repl-cred" -SecretAccessKey "newSecretKeyValue12345EXAMPLEKEY" + + Rotates the secret access key using a typed parameter. .EXAMPLE Update-PfbObjectStoreRemoteCredential -Name "s3-repl-cred" -Attributes @{ secret_access_key = "newSecretKeyValue12345EXAMPLEKEY" @@ -32,15 +51,38 @@ function Update-PfbObjectStoreRemoteCredential { } Updates the access key ID for the backup-target credential. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [string]$Id, - [Parameter()] + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$AccessKeyId, + + # EXCEPTION: the wire field is literally `name` (a rename), so the parameter is + # -NewName, never -RemoteCredentialName -- see Global Constraint on the `name` field. + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$NewName, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Remote, + + # Sensitive: no default value, never echoed via Write-Verbose. + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$SecretAccessKey, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [hashtable]$Attributes, [Parameter()] [PSCustomObject]$Array @@ -51,12 +93,27 @@ function Update-PfbObjectStoreRemoteCredential { } process { - $target = if ($Name) { $Name } else { $Id } - $body = if ($Attributes) { $Attributes } else { @{} } $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('AccessKeyId')) { $body['access_key_id'] = $AccessKeyId } + if ($PSBoundParameters.ContainsKey('NewName')) { $body['name'] = $NewName } + + # Constraint 8(a): remote is a SCALAR REFERENCE (item schema is {id, name, + # resource_type}), so the parameter is [string] and the projection is assigned + # INLINE -- constraint 7 forbids a local variable here. + if ($PSBoundParameters.ContainsKey('Remote')) { $body['remote'] = @{ name = $Remote } } + + if ($PSBoundParameters.ContainsKey('SecretAccessKey')) { $body['secret_access_key'] = $SecretAccessKey } + } + + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update object store remote credential')) { Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'object-store-remote-credentials' -Body $body -QueryParams $queryParams } diff --git a/Public/ObjectStore/Update-PfbObjectStoreRole.ps1 b/Public/ObjectStore/Update-PfbObjectStoreRole.ps1 index 6824b22..7699352 100644 --- a/Public/ObjectStore/Update-PfbObjectStoreRole.ps1 +++ b/Public/ObjectStore/Update-PfbObjectStoreRole.ps1 @@ -5,14 +5,28 @@ function Update-PfbObjectStoreRole { .DESCRIPTION Modifies the properties of an existing object store role, such as its description or assume-role policy document. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the role to update. .PARAMETER Id The ID of the role to update. + .PARAMETER Account + Reference of the associated account. + .PARAMETER MaxSessionDuration + The maximum session duration for the role in milliseconds. .PARAMETER Attributes - A hashtable of role properties to update. + A hashtable of role properties to update. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. + .EXAMPLE + Update-PfbObjectStoreRole -Name "s3-admin-role" -MaxSessionDuration 3600000 + + Sets the maximum session duration for the role using a typed parameter. .EXAMPLE Update-PfbObjectStoreRole -Name "s3-admin-role" -Attributes @{ description = "Updated admin role description" @@ -27,15 +41,27 @@ function Update-PfbObjectStoreRole { Update-PfbObjectStoreRole -Name "replication-role" -Attributes @{} Sends an empty update to refresh the role object. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [string]$Id, - [Parameter()] + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Account, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [int]$MaxSessionDuration, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [hashtable]$Attributes, [Parameter()] [PSCustomObject]$Array @@ -46,12 +72,27 @@ function Update-PfbObjectStoreRole { } process { - $target = if ($Name) { $Name } else { $Id } - $body = if ($Attributes) { $Attributes } else { @{} } $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + + # Constraint 8(a): account is a SCALAR REFERENCE (item schema is {id, name, + # resource_type}), so the parameter is [string] and the projection is assigned + # INLINE -- constraint 7 forbids a local variable here. + if ($PSBoundParameters.ContainsKey('Account')) { $body['account'] = @{ name = $Account } } + + # Constraint 2: integer body field -- guarded by ContainsKey, not truthiness, so + # an explicit -MaxSessionDuration 0 still reaches the wire. + if ($PSBoundParameters.ContainsKey('MaxSessionDuration')) { $body['max_session_duration'] = $MaxSessionDuration } + } + + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update object store role')) { Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'object-store-roles' -Body $body -QueryParams $queryParams } diff --git a/Public/ObjectStore/Update-PfbObjectStoreVirtualHost.ps1 b/Public/ObjectStore/Update-PfbObjectStoreVirtualHost.ps1 index 9f2f12f..c95c558 100644 --- a/Public/ObjectStore/Update-PfbObjectStoreVirtualHost.ps1 +++ b/Public/ObjectStore/Update-PfbObjectStoreVirtualHost.ps1 @@ -5,14 +5,34 @@ function Update-PfbObjectStoreVirtualHost { .DESCRIPTION Modifies the properties of an existing object store virtual host, such as its enabled state or associated access policies. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the virtual host to update. .PARAMETER Id The ID of the virtual host to update. + .PARAMETER AddAttachedServers + A list of new servers which are allowed to use this virtual host. + .PARAMETER AttachedServers + A list of servers which are allowed to use this virtual host. + .PARAMETER Hostname + A hostname by which the array can be addressed for virtual hosted-style S3 requests. + .PARAMETER NewName + A new user-specified name for the virtual host. + .PARAMETER RemoveAttachedServers + A list of servers which will no longer be allowed to use this virtual host. .PARAMETER Attributes - A hashtable of virtual host properties to update. + A hashtable of virtual host properties to update. Mutually exclusive with + the individual typed parameters above. .PARAMETER Array The FlashBlade connection object. + .EXAMPLE + Update-PfbObjectStoreVirtualHost -Name "s3.example.com" -Hostname "s3.myarray.com" + + Sets the hostname for the specified virtual host using a typed parameter. .EXAMPLE Update-PfbObjectStoreVirtualHost -Name "s3.example.com" -Attributes @{ enabled = $true @@ -27,15 +47,41 @@ function Update-PfbObjectStoreVirtualHost { Update-PfbObjectStoreVirtualHost -Name "data.example.com" -Attributes @{} Sends an empty update to refresh the virtual host object. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [string]$Id, - [Parameter()] + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$AddAttachedServers, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$AttachedServers, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Hostname, + + # EXCEPTION: the wire field is literally `name` (a rename), so the parameter is + # -NewName, never -VirtualHostName -- see Global Constraint on the `name` field. + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$NewName, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$RemoveAttachedServers, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] [hashtable]$Attributes, [Parameter()] [PSCustomObject]$Array @@ -46,12 +92,34 @@ function Update-PfbObjectStoreVirtualHost { } process { - $target = if ($Name) { $Name } else { $Id } - $body = if ($Attributes) { $Attributes } else { @{} } $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + + # Constraint 8(b): add_attached_servers/attached_servers/remove_attached_servers + # are ARRAYS OF REFERENCES (item schema is {id, name, resource_type}), so the + # parameters are [string[]] and each projection is assigned INLINE -- constraint 7 + # forbids an intermediate local variable here. + if ($PSBoundParameters.ContainsKey('AddAttachedServers')) { + $body['add_attached_servers'] = @($AddAttachedServers | ForEach-Object { @{ name = $_ } }) + } + if ($PSBoundParameters.ContainsKey('AttachedServers')) { + $body['attached_servers'] = @($AttachedServers | ForEach-Object { @{ name = $_ } }) + } + if ($PSBoundParameters.ContainsKey('Hostname')) { $body['hostname'] = $Hostname } + if ($PSBoundParameters.ContainsKey('NewName')) { $body['name'] = $NewName } + if ($PSBoundParameters.ContainsKey('RemoveAttachedServers')) { + $body['remove_attached_servers'] = @($RemoveAttachedServers | ForEach-Object { @{ name = $_ } }) + } + } + + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update object store virtual host')) { Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'object-store-virtual-hosts' -Body $body -QueryParams $queryParams } diff --git a/Public/Policy/Get-PfbAuditFileSystemPolicy.ps1 b/Public/Policy/Get-PfbAuditFileSystemPolicy.ps1 index d8ebc4b..06f2fe5 100644 --- a/Public/Policy/Get-PfbAuditFileSystemPolicy.ps1 +++ b/Public/Policy/Get-PfbAuditFileSystemPolicy.ps1 @@ -58,11 +58,7 @@ function Get-PfbAuditFileSystemPolicy { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'audit-file-systems-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbAuditFileSystemPolicyMember.ps1 b/Public/Policy/Get-PfbAuditFileSystemPolicyMember.ps1 index a47a9cb..1d8194e 100644 --- a/Public/Policy/Get-PfbAuditFileSystemPolicyMember.ps1 +++ b/Public/Policy/Get-PfbAuditFileSystemPolicyMember.ps1 @@ -58,13 +58,11 @@ function Get-PfbAuditFileSystemPolicyMember { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'audit-file-systems-policies/members' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbAuditFileSystemPolicyOperation.ps1 b/Public/Policy/Get-PfbAuditFileSystemPolicyOperation.ps1 index 536d9b6..59e0fd0 100644 --- a/Public/Policy/Get-PfbAuditFileSystemPolicyOperation.ps1 +++ b/Public/Policy/Get-PfbAuditFileSystemPolicyOperation.ps1 @@ -38,9 +38,7 @@ function Get-PfbAuditFileSystemPolicyOperation { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters try { Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'audit-file-systems-policy-operations' -QueryParams $queryParams -AutoPaginate diff --git a/Public/Policy/Get-PfbAuditObjectStorePolicy.ps1 b/Public/Policy/Get-PfbAuditObjectStorePolicy.ps1 index e06e592..2b31e67 100644 --- a/Public/Policy/Get-PfbAuditObjectStorePolicy.ps1 +++ b/Public/Policy/Get-PfbAuditObjectStorePolicy.ps1 @@ -58,11 +58,7 @@ function Get-PfbAuditObjectStorePolicy { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'audit-object-store-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbAuditObjectStorePolicyMember.ps1 b/Public/Policy/Get-PfbAuditObjectStorePolicyMember.ps1 index 6b8b8c1..68eb57c 100644 --- a/Public/Policy/Get-PfbAuditObjectStorePolicyMember.ps1 +++ b/Public/Policy/Get-PfbAuditObjectStorePolicyMember.ps1 @@ -58,13 +58,11 @@ function Get-PfbAuditObjectStorePolicyMember { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'audit-object-store-policies/members' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbNetworkAccessPolicy.ps1 b/Public/Policy/Get-PfbNetworkAccessPolicy.ps1 index 51a3413..76faa47 100644 --- a/Public/Policy/Get-PfbNetworkAccessPolicy.ps1 +++ b/Public/Policy/Get-PfbNetworkAccessPolicy.ps1 @@ -58,11 +58,7 @@ function Get-PfbNetworkAccessPolicy { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'network-access-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbNetworkAccessRule.ps1 b/Public/Policy/Get-PfbNetworkAccessRule.ps1 index 9f691ae..e918c28 100644 --- a/Public/Policy/Get-PfbNetworkAccessRule.ps1 +++ b/Public/Policy/Get-PfbNetworkAccessRule.ps1 @@ -63,12 +63,9 @@ function Get-PfbNetworkAccessRule { end { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $Name if ($allPolicyNames.Count -gt 0) { $queryParams['policy_names'] = $allPolicyNames -join ',' } if ($allPolicyIds.Count -gt 0) { $queryParams['policy_ids'] = $allPolicyIds -join ',' } - if ($Name) { $queryParams['names'] = $Name -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'network-access-policies/rules' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbNfsExportPolicy.ps1 b/Public/Policy/Get-PfbNfsExportPolicy.ps1 index 485782d..9673d83 100644 --- a/Public/Policy/Get-PfbNfsExportPolicy.ps1 +++ b/Public/Policy/Get-PfbNfsExportPolicy.ps1 @@ -58,11 +58,7 @@ function Get-PfbNfsExportPolicy { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'nfs-export-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbNfsExportRule.ps1 b/Public/Policy/Get-PfbNfsExportRule.ps1 index 913820e..279f0dc 100644 --- a/Public/Policy/Get-PfbNfsExportRule.ps1 +++ b/Public/Policy/Get-PfbNfsExportRule.ps1 @@ -63,12 +63,9 @@ function Get-PfbNfsExportRule { end { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $Name if ($allPolicyNames.Count -gt 0) { $queryParams['policy_names'] = $allPolicyNames -join ',' } if ($allPolicyIds.Count -gt 0) { $queryParams['policy_ids'] = $allPolicyIds -join ',' } - if ($Name) { $queryParams['names'] = $Name -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'nfs-export-policies/rules' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbPolicy.ps1 b/Public/Policy/Get-PfbPolicy.ps1 index b1e46f7..ee39a62 100644 --- a/Public/Policy/Get-PfbPolicy.ps1 +++ b/Public/Policy/Get-PfbPolicy.ps1 @@ -46,11 +46,7 @@ function Get-PfbPolicy { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbPolicyAll.ps1 b/Public/Policy/Get-PfbPolicyAll.ps1 index 4596342..707b379 100644 --- a/Public/Policy/Get-PfbPolicyAll.ps1 +++ b/Public/Policy/Get-PfbPolicyAll.ps1 @@ -51,11 +51,7 @@ function Get-PfbPolicyAll { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'policies-all' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Policy/Get-PfbPolicyAllMember.ps1 b/Public/Policy/Get-PfbPolicyAllMember.ps1 index cafb7eb..6157c85 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 ) @@ -45,12 +62,12 @@ function Get-PfbPolicyAllMember { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + if ($MemberType) { $queryParams['member_types'] = $MemberType -join ',' } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'policies-all/members' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbPolicyFileSystem.ps1 b/Public/Policy/Get-PfbPolicyFileSystem.ps1 index fd853ed..a4e1afb 100644 --- a/Public/Policy/Get-PfbPolicyFileSystem.ps1 +++ b/Public/Policy/Get-PfbPolicyFileSystem.ps1 @@ -50,13 +50,11 @@ function Get-PfbPolicyFileSystem { process { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'policies/file-systems' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbPolicyFileSystemReplicaLink.ps1 b/Public/Policy/Get-PfbPolicyFileSystemReplicaLink.ps1 index e749460..6bcd3e0 100644 --- a/Public/Policy/Get-PfbPolicyFileSystemReplicaLink.ps1 +++ b/Public/Policy/Get-PfbPolicyFileSystemReplicaLink.ps1 @@ -45,12 +45,11 @@ function Get-PfbPolicyFileSystemReplicaLink { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'policies/file-system-replica-links' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbPolicyFileSystemSnapshot.ps1 b/Public/Policy/Get-PfbPolicyFileSystemSnapshot.ps1 index 27fc096..d79a030 100644 --- a/Public/Policy/Get-PfbPolicyFileSystemSnapshot.ps1 +++ b/Public/Policy/Get-PfbPolicyFileSystemSnapshot.ps1 @@ -50,13 +50,11 @@ function Get-PfbPolicyFileSystemSnapshot { process { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'policies/file-system-snapshots' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbQosPolicy.ps1 b/Public/Policy/Get-PfbQosPolicy.ps1 index 3839968..b79de66 100644 --- a/Public/Policy/Get-PfbQosPolicy.ps1 +++ b/Public/Policy/Get-PfbQosPolicy.ps1 @@ -51,11 +51,7 @@ function Get-PfbQosPolicy { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'qos-policies' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Policy/Get-PfbQosPolicyBucket.ps1 b/Public/Policy/Get-PfbQosPolicyBucket.ps1 index 2f7eac2..87ef2ac 100644 --- a/Public/Policy/Get-PfbQosPolicyBucket.ps1 +++ b/Public/Policy/Get-PfbQosPolicyBucket.ps1 @@ -45,12 +45,11 @@ function Get-PfbQosPolicyBucket { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'qos-policies/buckets' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbQosPolicyFileSystem.ps1 b/Public/Policy/Get-PfbQosPolicyFileSystem.ps1 index b98726c..d681c5f 100644 --- a/Public/Policy/Get-PfbQosPolicyFileSystem.ps1 +++ b/Public/Policy/Get-PfbQosPolicyFileSystem.ps1 @@ -45,12 +45,11 @@ function Get-PfbQosPolicyFileSystem { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'qos-policies/file-systems' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbQosPolicyMember.ps1 b/Public/Policy/Get-PfbQosPolicyMember.ps1 index f974dd3..d278217 100644 --- a/Public/Policy/Get-PfbQosPolicyMember.ps1 +++ b/Public/Policy/Get-PfbQosPolicyMember.ps1 @@ -49,8 +49,7 @@ function Get-PfbQosPolicyMember { if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'qos-policies/members' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbS3ExportPolicy.ps1 b/Public/Policy/Get-PfbS3ExportPolicy.ps1 index af0ba4b..2c477d1 100644 --- a/Public/Policy/Get-PfbS3ExportPolicy.ps1 +++ b/Public/Policy/Get-PfbS3ExportPolicy.ps1 @@ -59,11 +59,7 @@ function Get-PfbS3ExportPolicy { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 's3-export-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbS3ExportRule.ps1 b/Public/Policy/Get-PfbS3ExportRule.ps1 index 7aba17b..82eb995 100644 --- a/Public/Policy/Get-PfbS3ExportRule.ps1 +++ b/Public/Policy/Get-PfbS3ExportRule.ps1 @@ -63,12 +63,9 @@ function Get-PfbS3ExportRule { end { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $Name if ($allPolicyNames.Count -gt 0) { $queryParams['policy_names'] = $allPolicyNames -join ',' } if ($allPolicyIds.Count -gt 0) { $queryParams['policy_ids'] = $allPolicyIds -join ',' } - if ($Name) { $queryParams['names'] = $Name -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 's3-export-policies/rules' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbSmbClientPolicy.ps1 b/Public/Policy/Get-PfbSmbClientPolicy.ps1 index 91632aa..c321618 100644 --- a/Public/Policy/Get-PfbSmbClientPolicy.ps1 +++ b/Public/Policy/Get-PfbSmbClientPolicy.ps1 @@ -58,11 +58,7 @@ function Get-PfbSmbClientPolicy { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'smb-client-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbSmbClientRule.ps1 b/Public/Policy/Get-PfbSmbClientRule.ps1 index eefdb69..61d70d1 100644 --- a/Public/Policy/Get-PfbSmbClientRule.ps1 +++ b/Public/Policy/Get-PfbSmbClientRule.ps1 @@ -63,12 +63,9 @@ function Get-PfbSmbClientRule { end { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $Name if ($allPolicyNames.Count -gt 0) { $queryParams['policy_names'] = $allPolicyNames -join ',' } if ($allPolicyIds.Count -gt 0) { $queryParams['policy_ids'] = $allPolicyIds -join ',' } - if ($Name) { $queryParams['names'] = $Name -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'smb-client-policies/rules' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbSmbSharePolicy.ps1 b/Public/Policy/Get-PfbSmbSharePolicy.ps1 index 7513f49..928d100 100644 --- a/Public/Policy/Get-PfbSmbSharePolicy.ps1 +++ b/Public/Policy/Get-PfbSmbSharePolicy.ps1 @@ -58,11 +58,7 @@ function Get-PfbSmbSharePolicy { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'smb-share-policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbSmbShareRule.ps1 b/Public/Policy/Get-PfbSmbShareRule.ps1 index 4c3d324..7a508d0 100644 --- a/Public/Policy/Get-PfbSmbShareRule.ps1 +++ b/Public/Policy/Get-PfbSmbShareRule.ps1 @@ -63,12 +63,9 @@ function Get-PfbSmbShareRule { end { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $Name if ($allPolicyNames.Count -gt 0) { $queryParams['policy_names'] = $allPolicyNames -join ',' } if ($allPolicyIds.Count -gt 0) { $queryParams['policy_ids'] = $allPolicyIds -join ',' } - if ($Name) { $queryParams['names'] = $Name -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'smb-share-policies/rules' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbSshCaPolicy.ps1 b/Public/Policy/Get-PfbSshCaPolicy.ps1 index 7e7e270..9c943e1 100644 --- a/Public/Policy/Get-PfbSshCaPolicy.ps1 +++ b/Public/Policy/Get-PfbSshCaPolicy.ps1 @@ -52,11 +52,7 @@ function Get-PfbSshCaPolicy { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'ssh-certificate-authority-policies' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Policy/Get-PfbSshCaPolicyAdmin.ps1 b/Public/Policy/Get-PfbSshCaPolicyAdmin.ps1 index 635b0ba..d0e1bdd 100644 --- a/Public/Policy/Get-PfbSshCaPolicyAdmin.ps1 +++ b/Public/Policy/Get-PfbSshCaPolicyAdmin.ps1 @@ -49,8 +49,7 @@ function Get-PfbSshCaPolicyAdmin { if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'ssh-certificate-authority-policies/admins' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbSshCaPolicyArray.ps1 b/Public/Policy/Get-PfbSshCaPolicyArray.ps1 index c10d50e..1e5efd9 100644 --- a/Public/Policy/Get-PfbSshCaPolicyArray.ps1 +++ b/Public/Policy/Get-PfbSshCaPolicyArray.ps1 @@ -49,8 +49,7 @@ function Get-PfbSshCaPolicyArray { if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'ssh-certificate-authority-policies/arrays' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbSshCaPolicyMember.ps1 b/Public/Policy/Get-PfbSshCaPolicyMember.ps1 index fbf59b9..8e68a6b 100644 --- a/Public/Policy/Get-PfbSshCaPolicyMember.ps1 +++ b/Public/Policy/Get-PfbSshCaPolicyMember.ps1 @@ -50,8 +50,7 @@ function Get-PfbSshCaPolicyMember { if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'ssh-certificate-authority-policies/members' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbStorageClassTieringPolicy.ps1 b/Public/Policy/Get-PfbStorageClassTieringPolicy.ps1 index 0689896..68c4cb0 100644 --- a/Public/Policy/Get-PfbStorageClassTieringPolicy.ps1 +++ b/Public/Policy/Get-PfbStorageClassTieringPolicy.ps1 @@ -51,11 +51,7 @@ function Get-PfbStorageClassTieringPolicy { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'storage-class-tiering-policies' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Policy/Get-PfbStorageClassTieringPolicyMember.ps1 b/Public/Policy/Get-PfbStorageClassTieringPolicyMember.ps1 index 395b023..f2329da 100644 --- a/Public/Policy/Get-PfbStorageClassTieringPolicyMember.ps1 +++ b/Public/Policy/Get-PfbStorageClassTieringPolicyMember.ps1 @@ -50,8 +50,7 @@ function Get-PfbStorageClassTieringPolicyMember { if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'storage-class-tiering-policies/members' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbTlsPolicy.ps1 b/Public/Policy/Get-PfbTlsPolicy.ps1 index 23fb046..3329d04 100644 --- a/Public/Policy/Get-PfbTlsPolicy.ps1 +++ b/Public/Policy/Get-PfbTlsPolicy.ps1 @@ -51,11 +51,7 @@ function Get-PfbTlsPolicy { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'tls-policies' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Policy/Get-PfbTlsPolicyMember.ps1 b/Public/Policy/Get-PfbTlsPolicyMember.ps1 index ee3806d..0c26b16 100644 --- a/Public/Policy/Get-PfbTlsPolicyMember.ps1 +++ b/Public/Policy/Get-PfbTlsPolicyMember.ps1 @@ -50,8 +50,7 @@ function Get-PfbTlsPolicyMember { if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'tls-policies/members' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/Get-PfbUserGroupQuotaPolicy.ps1 b/Public/Policy/Get-PfbUserGroupQuotaPolicy.ps1 new file mode 100644 index 0000000..92e6b4e --- /dev/null +++ b/Public/Policy/Get-PfbUserGroupQuotaPolicy.ps1 @@ -0,0 +1,65 @@ +function Get-PfbUserGroupQuotaPolicy { + <# + .SYNOPSIS + Retrieves user/group quota policies from a FlashBlade array. + .DESCRIPTION + The Get-PfbUserGroupQuotaPolicy cmdlet returns user-group-quota policies from the + connected Pure Storage FlashBlade. These policies define quota rules for users and + groups across one or more file systems, replacing the legacy per-filesystem + purequota/purefs quota model. + .PARAMETER Name + One or more policy names to retrieve. Accepts pipeline input. + .PARAMETER Id + One or more policy IDs to retrieve. + .PARAMETER Filter + A server-side filter expression to narrow results. + .PARAMETER Sort + Sort field and direction. + .PARAMETER Limit + Maximum number of items to return. + .PARAMETER TotalOnly + Return only the total count, not the items. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + Get-PfbUserGroupQuotaPolicy + + Retrieves all user-group-quota policies. + .EXAMPLE + Get-PfbUserGroupQuotaPolicy -Name "quota-pol-1" + + Retrieves the policy named 'quota-pol-1'. + #> + [CmdletBinding(DefaultParameterSetName = 'List')] + param( + [Parameter(ParameterSetName = 'ByName', ValueFromPipeline, ValueFromPipelineByPropertyName)] + [string[]]$Name, + + [Parameter(ParameterSetName = 'ById')] + [string[]]$Id, + + [Parameter()] [string]$Filter, + [Parameter()] [string]$Sort, + [Parameter()] [int]$Limit, + [Parameter()] [switch]$TotalOnly, + [Parameter()] [PSCustomObject]$Array + ) + + 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 = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames.ToArray() -Ids $allIds.ToArray() + + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'user-group-quota-policies' -QueryParams $queryParams -AutoPaginate + } +} diff --git a/Public/Policy/Get-PfbUserGroupQuotaPolicyFileSystem.ps1 b/Public/Policy/Get-PfbUserGroupQuotaPolicyFileSystem.ps1 new file mode 100644 index 0000000..8e5b265 --- /dev/null +++ b/Public/Policy/Get-PfbUserGroupQuotaPolicyFileSystem.ps1 @@ -0,0 +1,54 @@ +function Get-PfbUserGroupQuotaPolicyFileSystem { + <# + .SYNOPSIS + Retrieves user/group quota policy-to-file-system attachments from a FlashBlade array. + .DESCRIPTION + The Get-PfbUserGroupQuotaPolicyFileSystem cmdlet returns the mapping between + user-group-quota policies and the file systems they're attached to. + .PARAMETER PolicyName + One or more policy names to filter by. + .PARAMETER PolicyId + One or more policy IDs to filter by. + .PARAMETER MemberName + One or more file system names to filter by. + .PARAMETER MemberId + One or more file system IDs to filter by. + .PARAMETER Filter + A server-side filter expression to narrow results. + .PARAMETER Sort + Sort field and direction. + .PARAMETER Limit + Maximum number of items to return. + .PARAMETER TotalOnly + Return only the total count, not the items. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + Get-PfbUserGroupQuotaPolicyFileSystem -PolicyName "quota-pol-1" + + Retrieves the file systems 'quota-pol-1' is attached to. + #> + [CmdletBinding()] + param( + [Parameter()] [string[]]$PolicyName, + [Parameter()] [string[]]$PolicyId, + [Parameter()] [string[]]$MemberName, + [Parameter()] [string[]]$MemberId, + [Parameter()] [string]$Filter, + [Parameter()] [string]$Sort, + [Parameter()] [int]$Limit, + [Parameter()] [switch]$TotalOnly, + [Parameter()] [PSCustomObject]$Array + ) + + Assert-PfbConnection -Array ([ref]$Array) + + $queryParams = @{} + if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } + if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } + if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } + if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'user-group-quota-policies/file-systems' -QueryParams $queryParams -AutoPaginate +} diff --git a/Public/Policy/Get-PfbUserGroupQuotaPolicyMember.ps1 b/Public/Policy/Get-PfbUserGroupQuotaPolicyMember.ps1 new file mode 100644 index 0000000..25c180e --- /dev/null +++ b/Public/Policy/Get-PfbUserGroupQuotaPolicyMember.ps1 @@ -0,0 +1,54 @@ +function Get-PfbUserGroupQuotaPolicyMember { + <# + .SYNOPSIS + Retrieves user/group quota policy member associations from a FlashBlade array. + .DESCRIPTION + The Get-PfbUserGroupQuotaPolicyMember cmdlet returns the cross-reference between + user-group-quota policies and their members (file systems) on the connected Pure + Storage FlashBlade. This is a read-only view; use Get-PfbUserGroupQuotaPolicyFileSystem + to see the same relationship from the file-system-attachment endpoint. + .PARAMETER PolicyName + One or more policy names to filter by. + .PARAMETER PolicyId + One or more policy IDs to filter by. + .PARAMETER MemberName + One or more member names to filter by. + .PARAMETER MemberId + One or more member IDs to filter by. + .PARAMETER Filter + A server-side filter expression to narrow results. + .PARAMETER Sort + Sort field and direction. + .PARAMETER Limit + Maximum number of entries to return. + .PARAMETER TotalOnly + Return only the total count, not the items. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + Get-PfbUserGroupQuotaPolicyMember -PolicyName "quota-pol-1" + #> + [CmdletBinding()] + param( + [Parameter()] [string[]]$PolicyName, + [Parameter()] [string[]]$PolicyId, + [Parameter()] [string[]]$MemberName, + [Parameter()] [string[]]$MemberId, + [Parameter()] [string]$Filter, + [Parameter()] [string]$Sort, + [Parameter()] [int]$Limit, + [Parameter()] [switch]$TotalOnly, + [Parameter()] [PSCustomObject]$Array + ) + + Assert-PfbConnection -Array ([ref]$Array) + + $queryParams = @{} + if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } + if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } + if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } + if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'user-group-quota-policies/members' -QueryParams $queryParams -AutoPaginate +} diff --git a/Public/Policy/Get-PfbUserGroupQuotaPolicyRule.ps1 b/Public/Policy/Get-PfbUserGroupQuotaPolicyRule.ps1 new file mode 100644 index 0000000..aa4c7b3 --- /dev/null +++ b/Public/Policy/Get-PfbUserGroupQuotaPolicyRule.ps1 @@ -0,0 +1,71 @@ +function Get-PfbUserGroupQuotaPolicyRule { + <# + .SYNOPSIS + Retrieves user/group quota policy rules from a FlashBlade array. + .DESCRIPTION + The Get-PfbUserGroupQuotaPolicyRule cmdlet returns the individual quota rules that + belong to user-group-quota policies on the connected Pure Storage FlashBlade. + .PARAMETER PolicyName + One or more policy names to filter by. Accepts pipeline input. + .PARAMETER PolicyId + One or more policy IDs to filter by. + .PARAMETER Name + One or more rule names to retrieve. + .PARAMETER Id + One or more rule IDs to retrieve. + .PARAMETER Filter + A server-side filter expression to narrow results. + .PARAMETER Sort + Sort field and direction. + .PARAMETER Limit + Maximum number of items to return. + .PARAMETER TotalOnly + Return only the total count, not the items. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + Get-PfbUserGroupQuotaPolicyRule -PolicyName "quota-pol-1" + + Retrieves all rules belonging to the 'quota-pol-1' policy. + #> + [CmdletBinding(DefaultParameterSetName = 'List')] + param( + [Parameter(ParameterSetName = 'ByPolicyName', ValueFromPipeline, ValueFromPipelineByPropertyName)] + [string[]]$PolicyName, + + [Parameter(ParameterSetName = 'ByPolicyId')] + [string[]]$PolicyId, + + [Parameter()] [string[]]$Name, + [Parameter()] [string[]]$Id, + + [Parameter()] [string]$Filter, + [Parameter()] [string]$Sort, + [Parameter()] [int]$Limit, + [Parameter()] [switch]$TotalOnly, + [Parameter()] [PSCustomObject]$Array + ) + + begin { + if ($Name -and $Id) { + throw 'You cannot supply both -Name and -Id.' + } + Assert-PfbConnection -Array ([ref]$Array) + $allPolicyNames = [System.Collections.Generic.List[string]]::new() + $allPolicyIds = [System.Collections.Generic.List[string]]::new() + } + + process { + if ($PolicyName) { foreach ($n in $PolicyName) { $allPolicyNames.Add($n) } } + if ($PolicyId) { foreach ($i in $PolicyId) { $allPolicyIds.Add($i) } } + } + + end { + $queryParams = @{} + if ($allPolicyNames.Count -gt 0) { $queryParams['policy_names'] = $allPolicyNames -join ',' } + if ($allPolicyIds.Count -gt 0) { $queryParams['policy_ids'] = $allPolicyIds -join ',' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $Name -Ids $Id + + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'user-group-quota-policies/rules' -QueryParams $queryParams -AutoPaginate + } +} diff --git a/Public/Policy/Get-PfbWormPolicy.ps1 b/Public/Policy/Get-PfbWormPolicy.ps1 index 5849a11..f3f3081 100644 --- a/Public/Policy/Get-PfbWormPolicy.ps1 +++ b/Public/Policy/Get-PfbWormPolicy.ps1 @@ -51,11 +51,7 @@ function Get-PfbWormPolicy { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'worm-data-policies' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Policy/Get-PfbWormPolicyMember.ps1 b/Public/Policy/Get-PfbWormPolicyMember.ps1 index 4bed51d..f9f828f 100644 --- a/Public/Policy/Get-PfbWormPolicyMember.ps1 +++ b/Public/Policy/Get-PfbWormPolicyMember.ps1 @@ -50,8 +50,7 @@ function Get-PfbWormPolicyMember { if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'worm-data-policies/members' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Policy/New-PfbNetworkAccessRule.ps1 b/Public/Policy/New-PfbNetworkAccessRule.ps1 index 4310c20..bd6bf95 100644 --- a/Public/Policy/New-PfbNetworkAccessRule.ps1 +++ b/Public/Policy/New-PfbNetworkAccessRule.ps1 @@ -5,14 +5,42 @@ function New-PfbNetworkAccessRule { .DESCRIPTION Adds a new rule to a network access policy. Rules define network-level access control including client IP ranges, interfaces, protocols, and effect (allow/deny). + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER PolicyName The name of the network access policy to add the rule to. .PARAMETER PolicyId The ID of the network access policy to add the rule to. + .PARAMETER Client + Specifies the clients that will be permitted or denied access to the interface. + .PARAMETER Effect + If set to `allow`, the specified client will be permitted to access the specified + interfaces. + .PARAMETER Index + The index within the policy. + .PARAMETER Interfaces + Specifies which product interfaces this rule applies to, whether it is permitting + or denying access. .PARAMETER Attributes A hashtable defining the rule properties (client, effect, interfaces, protocols, etc.). + Mutually exclusive with the individual typed parameters above. + .PARAMETER BeforeRuleId + The ID of the rule to insert this rule before. Cannot be combined with -BeforeRuleName. + .PARAMETER BeforeRuleName + The name of the rule to insert this rule before. Cannot be combined with -BeforeRuleId. + .PARAMETER Versions + A list of versions used for concurrency control. Ordering matches the policy + names/IDs query parameter. Fails with a 412 if the resource's current version + does not match. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + New-PfbNetworkAccessRule -PolicyName "network-access-01" -Client "10.0.0.0/8" -Effect "allow" + + Creates a new rule allowing access from the specified subnet using typed parameters. .EXAMPLE New-PfbNetworkAccessRule -PolicyName "network-access-01" -Attributes @{ client = "10.0.0.0/8"; effect = "allow" } @@ -22,17 +50,43 @@ function New-PfbNetworkAccessRule { Creates a rule denying access to management interfaces from all clients. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByPolicyNameIndividual')] param( - [Parameter(ParameterSetName = 'ByPolicyName', Mandatory, Position = 0)] + [Parameter(ParameterSetName = 'ByPolicyNameIndividual', Mandatory, Position = 0)] + [Parameter(ParameterSetName = 'ByPolicyNameAttributes', Mandatory, Position = 0)] [string]$PolicyName, - [Parameter(ParameterSetName = 'ByPolicyId', Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyIdAttributes', Mandatory)] [string]$PolicyId, - [Parameter(Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [string]$Client, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [ValidateSet('allow', 'deny')] + [string]$Effect, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [int]$Index, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [ValidateSet('management-ssh', 'management-rest-api', 'management-web-ui', 'snmp', 'local-network-superuser-password-access')] + [string[]]$Interfaces, + + [Parameter(ParameterSetName = 'ByPolicyNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyIdAttributes', Mandatory)] [hashtable]$Attributes, + [Parameter()] [string]$BeforeRuleId, + [Parameter()] [string]$BeforeRuleName, + [Parameter()] [string[]]$Versions, + [Parameter()] [PSCustomObject]$Array ) @@ -41,10 +95,24 @@ function New-PfbNetworkAccessRule { $queryParams = @{} if ($PolicyName) { $queryParams['policy_names'] = $PolicyName } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId } + if ($PSBoundParameters.ContainsKey('BeforeRuleId')) { $queryParams['before_rule_id'] = $BeforeRuleId } + if ($PSBoundParameters.ContainsKey('BeforeRuleName')) { $queryParams['before_rule_name'] = $BeforeRuleName } + if ($PSBoundParameters.ContainsKey('Versions')) { $queryParams['versions'] = $Versions -join ',' } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('Client')) { $body['client'] = $Client } + if ($PSBoundParameters.ContainsKey('Effect')) { $body['effect'] = $Effect } + if ($PSBoundParameters.ContainsKey('Index')) { $body['index'] = $Index } + if ($PSBoundParameters.ContainsKey('Interfaces')) { $body['interfaces'] = @($Interfaces) } + } $target = if ($PolicyName) { $PolicyName } else { $PolicyId } if ($PSCmdlet.ShouldProcess($target, 'Create network access rule')) { - Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'network-access-policies/rules' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'network-access-policies/rules' -Body $body -QueryParams $queryParams } } diff --git a/Public/Policy/New-PfbNfsExportRule.ps1 b/Public/Policy/New-PfbNfsExportRule.ps1 index a680ee7..8dc4068 100644 --- a/Public/Policy/New-PfbNfsExportRule.ps1 +++ b/Public/Policy/New-PfbNfsExportRule.ps1 @@ -5,14 +5,60 @@ function New-PfbNfsExportRule { .DESCRIPTION Adds a new rule to an NFS export policy. Rules define client access permissions for NFS exports including client IP/hostname patterns, access level, and security. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER PolicyName The name of the NFS export policy to add the rule to. .PARAMETER PolicyId The ID of the NFS export policy to add the rule to. + .PARAMETER Access + Specifies access control for the export. + .PARAMETER Anongid + Any user whose GID is affected by an `access` of `root_squash` or `all_squash` will + have their GID mapped to `anongid`. + .PARAMETER Anonuid + Any user whose UID is affected by an `access` of `root_squash` or `all_squash` will + have their UID mapped to `anonuid`. + .PARAMETER Atime + If `true`, after a read operation has occurred, the inode access time is updated + only under certain conditions. + .PARAMETER Client + Specifies the clients that will be permitted to access the export. + .PARAMETER Fileid32bit + Whether the file id is 32 bits or not. + .PARAMETER Index + The index within the policy. + .PARAMETER Permission + Specifies which read-write client access permissions are allowed for the export. + .PARAMETER Policy + The name of the policy to which this rule belongs. + .PARAMETER RequiredTransportSecurity + Specifies the minimum transport security required for clients to access the export. + .PARAMETER Secure + If `true`, prevents NFS access to client connections coming from non-reserved ports. + .PARAMETER Security + The security flavors to use for accessing files on this mount point. .PARAMETER Attributes A hashtable defining the rule properties (client, access, permission, security, etc.). + Mutually exclusive with the individual typed parameters above. + .PARAMETER BeforeRuleId + The ID of the rule to insert this rule before. Cannot be combined with -BeforeRuleName. + .PARAMETER BeforeRuleName + The name of the rule to insert this rule before. Cannot be combined with -BeforeRuleId. + .PARAMETER Versions + A list of versions used for concurrency control. Ordering matches the policy + names/IDs query parameter. Fails with a 412 if the resource's current version + does not match. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + New-PfbNfsExportRule -PolicyName "nfs-export-01" -Client "*" -Access "root-squash" -Permission "rw" + + Creates a new rule allowing all clients with root-squash and read-write access using + typed parameters. .EXAMPLE New-PfbNfsExportRule -PolicyName "nfs-export-01" -Attributes @{ client = "*"; access = "root-squash"; permission = "rw" } @@ -22,17 +68,76 @@ function New-PfbNfsExportRule { Creates a rule for a specific subnet with no root squash. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByPolicyNameIndividual')] param( - [Parameter(ParameterSetName = 'ByPolicyName', Mandatory, Position = 0)] + [Parameter(ParameterSetName = 'ByPolicyNameIndividual', Mandatory, Position = 0)] + [Parameter(ParameterSetName = 'ByPolicyNameAttributes', Mandatory, Position = 0)] [string]$PolicyName, - [Parameter(ParameterSetName = 'ByPolicyId', Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyIdAttributes', Mandatory)] [string]$PolicyId, - [Parameter(Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [ValidateSet('root-squash', 'all-squash', 'no-squash')] + [string]$Access, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [long]$Anongid, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [long]$Anonuid, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [Nullable[bool]]$Atime, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [string]$Client, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [Nullable[bool]]$Fileid32bit, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [int]$Index, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [ValidateSet('rw', 'ro')] + [string]$Permission, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [string]$Policy, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [ValidateSet('tls', 'mutual-tls', 'none')] + [string]$RequiredTransportSecurity, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [Nullable[bool]]$Secure, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [string[]]$Security, + + [Parameter(ParameterSetName = 'ByPolicyNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyIdAttributes', Mandatory)] [hashtable]$Attributes, + [Parameter()] [string]$BeforeRuleId, + [Parameter()] [string]$BeforeRuleName, + [Parameter()] [string[]]$Versions, + [Parameter()] [PSCustomObject]$Array ) @@ -41,10 +146,32 @@ function New-PfbNfsExportRule { $queryParams = @{} if ($PolicyName) { $queryParams['policy_names'] = $PolicyName } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId } + if ($PSBoundParameters.ContainsKey('BeforeRuleId')) { $queryParams['before_rule_id'] = $BeforeRuleId } + if ($PSBoundParameters.ContainsKey('BeforeRuleName')) { $queryParams['before_rule_name'] = $BeforeRuleName } + if ($PSBoundParameters.ContainsKey('Versions')) { $queryParams['versions'] = $Versions -join ',' } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('Access')) { $body['access'] = $Access } + if ($PSBoundParameters.ContainsKey('Anongid')) { $body['anongid'] = $Anongid } + if ($PSBoundParameters.ContainsKey('Anonuid')) { $body['anonuid'] = $Anonuid } + if ($PSBoundParameters.ContainsKey('Atime')) { $body['atime'] = $Atime } + if ($PSBoundParameters.ContainsKey('Client')) { $body['client'] = $Client } + if ($PSBoundParameters.ContainsKey('Fileid32bit')) { $body['fileid_32bit'] = $Fileid32bit } + if ($PSBoundParameters.ContainsKey('Index')) { $body['index'] = $Index } + if ($PSBoundParameters.ContainsKey('Permission')) { $body['permission'] = $Permission } + if ($PSBoundParameters.ContainsKey('Policy')) { $body['policy'] = @{ name = $Policy } } + if ($PSBoundParameters.ContainsKey('RequiredTransportSecurity')) { $body['required_transport_security'] = $RequiredTransportSecurity } + if ($PSBoundParameters.ContainsKey('Secure')) { $body['secure'] = $Secure } + if ($PSBoundParameters.ContainsKey('Security')) { $body['security'] = @($Security) } + } $target = if ($PolicyName) { $PolicyName } else { $PolicyId } if ($PSCmdlet.ShouldProcess($target, 'Create NFS export rule')) { - Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'nfs-export-policies/rules' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'nfs-export-policies/rules' -Body $body -QueryParams $queryParams } } diff --git a/Public/Policy/New-PfbPolicyFileSystemReplicaLink.ps1 b/Public/Policy/New-PfbPolicyFileSystemReplicaLink.ps1 index 43a5cdc..6e6b4f2 100644 --- a/Public/Policy/New-PfbPolicyFileSystemReplicaLink.ps1 +++ b/Public/Policy/New-PfbPolicyFileSystemReplicaLink.ps1 @@ -5,35 +5,60 @@ function New-PfbPolicyFileSystemReplicaLink { .DESCRIPTION The New-PfbPolicyFileSystemReplicaLink cmdlet creates an association between a policy and a file system replica link on the connected Pure Storage FlashBlade. + + Bug fix (#31, corrected after review): `POST /policies/file-system-replica-links` has + no `member_names` query parameter, but `member_ids` IS a real, spec-declared query + parameter on this endpoint (and its GET/DELETE siblings) -- confirmed directly against + the OpenAPI spec's parameter list, which the original fix missed, causing it to remove + -MemberId entirely as if it were as invalid as -MemberName. The endpoint also identifies + the replica link by its local file system (`local_file_system_names`/ + `local_file_system_ids`) and its remote side (`remote_names`/`remote_ids`), so + -MemberName is replaced with -LocalFileSystemName/-LocalFileSystemId and + -RemoteName/-RemoteId, while -MemberId is kept (mapped to the real `member_ids` key) as + an additional, independent selector alongside them. -LocalFileSystemName keeps + -MemberName as a backward-compatible alias (whole-branch review finding I-3: matches + the identical rename on the sibling cmdlet New-PfbFileSystemReplicaLinkPolicy). .PARAMETER PolicyName The policy name. .PARAMETER PolicyId The policy ID. - .PARAMETER MemberName - The file system replica link name. + .PARAMETER LocalFileSystemName + The name of the local file system side of the replica link. Also accepts the alias + -MemberName. + .PARAMETER LocalFileSystemId + The ID of the local file system side of the replica link. .PARAMETER MemberId - The file system replica link ID. + The ID of the replica link member. Sent as the `member_ids` query parameter -- unlike + -MemberName (removed: `member_names` is not a valid query parameter for this endpoint), + `member_ids` is real and spec-declared. + .PARAMETER RemoteName + The name of the remote side of the replica link. + .PARAMETER RemoteId + The ID of the remote side of the replica link. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - New-PfbPolicyFileSystemReplicaLink -PolicyName "daily-snap" -MemberName "fs1/remote-fb" + New-PfbPolicyFileSystemReplicaLink -PolicyName "daily-snap" -LocalFileSystemName "fs1" -RemoteName "remote-fb" - Associates the policy with the replica link. + Associates the policy with the replica link identified by local file system and remote. .EXAMPLE - New-PfbPolicyFileSystemReplicaLink -PolicyName "daily-snap" -MemberName "fs1/remote-fb" -WhatIf + New-PfbPolicyFileSystemReplicaLink -PolicyName "daily-snap" -LocalFileSystemName "fs1" -RemoteName "remote-fb" -WhatIf Shows what would happen without actually creating the association. .EXAMPLE - New-PfbPolicyFileSystemReplicaLink -PolicyName "hourly-snap" -MemberName "fs2/remote-fb" + New-PfbPolicyFileSystemReplicaLink -PolicyName "hourly-snap" -LocalFileSystemId "lfs-2" -RemoteId "remote-2" - Associates the hourly snapshot policy with the specified replica link. + Associates the hourly snapshot policy with the specified replica link using IDs. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param( [Parameter()] [string]$PolicyName, [Parameter()] [string]$PolicyId, - [Parameter()] [string]$MemberName, + [Parameter()] [Alias('MemberName')] [string]$LocalFileSystemName, [Parameter()] [string]$MemberId, + [Parameter()] [string]$LocalFileSystemId, + [Parameter()] [string]$RemoteName, + [Parameter()] [string]$RemoteId, [Parameter()] [PSCustomObject]$Array ) @@ -42,10 +67,13 @@ function New-PfbPolicyFileSystemReplicaLink { $queryParams = @{} if ($PolicyName) { $queryParams['policy_names'] = $PolicyName } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId } - if ($MemberName) { $queryParams['member_names'] = $MemberName } - if ($MemberId) { $queryParams['member_ids'] = $MemberId } + if ($PSBoundParameters.ContainsKey('LocalFileSystemName')) { $queryParams['local_file_system_names'] = $LocalFileSystemName } + if ($PSBoundParameters.ContainsKey('LocalFileSystemId')) { $queryParams['local_file_system_ids'] = $LocalFileSystemId } + if ($PSBoundParameters.ContainsKey('MemberId')) { $queryParams['member_ids'] = $MemberId } + if ($PSBoundParameters.ContainsKey('RemoteName')) { $queryParams['remote_names'] = $RemoteName } + if ($PSBoundParameters.ContainsKey('RemoteId')) { $queryParams['remote_ids'] = $RemoteId } - $target = "${PolicyName}:${MemberName}" + $target = "${PolicyName}:${LocalFileSystemName}" if ($PSCmdlet.ShouldProcess($target, 'Add policy to file system replica link')) { Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'policies/file-system-replica-links' -QueryParams $queryParams diff --git a/Public/Policy/New-PfbQosPolicyMember.ps1 b/Public/Policy/New-PfbQosPolicyMember.ps1 index c16cfa2..201cf81 100644 --- a/Public/Policy/New-PfbQosPolicyMember.ps1 +++ b/Public/Policy/New-PfbQosPolicyMember.ps1 @@ -13,6 +13,8 @@ function New-PfbQosPolicyMember { The member name to add to the policy. .PARAMETER MemberId The member ID to add to the policy. + .PARAMETER MemberType + A list of member types to add to the policy. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -24,9 +26,9 @@ function New-PfbQosPolicyMember { Shows what would happen without actually adding the member. .EXAMPLE - New-PfbQosPolicyMember -PolicyName "qos-silver" -MemberName "fs2" + New-PfbQosPolicyMember -PolicyName "qos-silver" -MemberName "fs2" -MemberType "file-systems" - Associates "fs2" with the QoS policy "qos-silver". + Associates "fs2" with the QoS policy "qos-silver", specifying its member type. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param( @@ -34,6 +36,7 @@ function New-PfbQosPolicyMember { [Parameter()] [string]$PolicyId, [Parameter()] [string]$MemberName, [Parameter()] [string]$MemberId, + [Parameter()] [string[]]$MemberType, [Parameter()] [PSCustomObject]$Array ) @@ -44,6 +47,7 @@ function New-PfbQosPolicyMember { if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId } if ($MemberName) { $queryParams['member_names'] = $MemberName } if ($MemberId) { $queryParams['member_ids'] = $MemberId } + if ($PSBoundParameters.ContainsKey('MemberType')) { $queryParams['member_types'] = $MemberType -join ',' } $target = "${PolicyName}:${MemberName}" diff --git a/Public/Policy/New-PfbS3ExportRule.ps1 b/Public/Policy/New-PfbS3ExportRule.ps1 index 6544a57..6f4b2e9 100644 --- a/Public/Policy/New-PfbS3ExportRule.ps1 +++ b/Public/Policy/New-PfbS3ExportRule.ps1 @@ -5,37 +5,76 @@ function New-PfbS3ExportRule { .DESCRIPTION Adds a new rule to an S3 export policy. Rules define client access permissions and export settings within the policy. Specify the target - policy by name or ID and provide rule properties via the Attributes parameter. + policy by name or ID and provide rule properties via typed parameters or + the Attributes parameter. + + Unlike the other rule-POST endpoints, `POST /s3-export-policies/rules` requires + the caller to supply the new rule's name via the required `-Name` query + parameter -- the server does not assign it. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER PolicyName The name of the S3 export policy to add the rule to. .PARAMETER PolicyId The ID of the S3 export policy to add the rule to. + .PARAMETER Name + The name(s) to assign to the new rule. Required -- this endpoint does not + server-assign the rule name. + .PARAMETER Actions + The list of actions granted by this rule. + .PARAMETER Effect + Effect of this rule. + .PARAMETER Resources + The list of resources from the account to which this rule applies to. .PARAMETER Attributes A hashtable defining the rule properties (client, access, permission, etc.). + Mutually exclusive with the individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - New-PfbS3ExportRule -PolicyName "s3-export-01" -Attributes @{ client = "*"; access = "root-squash"; permission = "rw" } + New-PfbS3ExportRule -PolicyName "s3-export-01" -Name "rule-1" -Effect "allow" -Actions "s3:GetObject" -Resources "bucket1/*" - Creates a new rule allowing all clients with root-squash and read-write access. + Creates a new rule granting read access to a bucket using typed parameters. .EXAMPLE - New-PfbS3ExportRule -PolicyName "s3-export-01" -Attributes @{ client = "10.0.0.0/8"; access = "no-root-squash"; permission = "rw" } + New-PfbS3ExportRule -PolicyName "s3-export-01" -Name "rule-1" -Attributes @{ client = "*"; access = "root-squash"; permission = "rw" } - Creates a rule for a specific subnet with no root squash. + Creates a new rule allowing all clients with root-squash and read-write access. .EXAMPLE - New-PfbS3ExportRule -PolicyId "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -Attributes @{ client = "*"; permission = "ro" } + New-PfbS3ExportRule -PolicyId "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -Name "rule-2" -Attributes @{ client = "*"; permission = "ro" } Creates a read-only rule by policy ID. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByPolicyNameIndividual')] param( - [Parameter(ParameterSetName = 'ByPolicyName', Mandatory, Position = 0)] + [Parameter(ParameterSetName = 'ByPolicyNameIndividual', Mandatory, Position = 0)] + [Parameter(ParameterSetName = 'ByPolicyNameAttributes', Mandatory, Position = 0)] [string]$PolicyName, - [Parameter(ParameterSetName = 'ByPolicyId', Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyIdAttributes', Mandatory)] [string]$PolicyId, [Parameter(Mandatory)] + [string[]]$Name, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [string[]]$Actions, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [string]$Effect, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [string[]]$Resources, + + [Parameter(ParameterSetName = 'ByPolicyNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyIdAttributes', Mandatory)] [hashtable]$Attributes, [Parameter()] [PSCustomObject]$Array @@ -46,10 +85,21 @@ function New-PfbS3ExportRule { $queryParams = @{} if ($PolicyName) { $queryParams['policy_names'] = $PolicyName } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId } + $queryParams['names'] = $Name -join ',' + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('Actions')) { $body['actions'] = @($Actions) } + if ($PSBoundParameters.ContainsKey('Effect')) { $body['effect'] = $Effect } + if ($PSBoundParameters.ContainsKey('Resources')) { $body['resources'] = @($Resources) } + } $target = if ($PolicyName) { $PolicyName } else { $PolicyId } if ($PSCmdlet.ShouldProcess($target, 'Create S3 export rule')) { - Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 's3-export-policies/rules' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 's3-export-policies/rules' -Body $body -QueryParams $queryParams } } diff --git a/Public/Policy/New-PfbSmbClientRule.ps1 b/Public/Policy/New-PfbSmbClientRule.ps1 index 77d89eb..e48630d 100644 --- a/Public/Policy/New-PfbSmbClientRule.ps1 +++ b/Public/Policy/New-PfbSmbClientRule.ps1 @@ -5,14 +5,40 @@ function New-PfbSmbClientRule { .DESCRIPTION Adds a new rule to an SMB client policy. Rules define client access restrictions for SMB connections including client IP patterns and encryption settings. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER PolicyName The name of the SMB client policy to add the rule to. .PARAMETER PolicyId The ID of the SMB client policy to add the rule to. + .PARAMETER Client + Specifies the clients that will be permitted to access the export. + .PARAMETER Encryption + Specifies whether the remote client is required to use SMB encryption. + .PARAMETER Index + The index within the policy. + .PARAMETER Permission + Specifies which read-write client access permissions are allowed for the export. .PARAMETER Attributes - A hashtable defining the rule properties (client, encryption, etc.). + A hashtable defining the rule properties (client, encryption, etc.). Mutually + exclusive with the individual typed parameters above. + .PARAMETER BeforeRuleId + The ID of the rule to insert this rule before. Cannot be combined with -BeforeRuleName. + .PARAMETER BeforeRuleName + The name of the rule to insert this rule before. Cannot be combined with -BeforeRuleId. + .PARAMETER Versions + A list of versions used for concurrency control. Ordering matches the policy + names/IDs query parameter. Fails with a 412 if the resource's current version + does not match. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + New-PfbSmbClientRule -PolicyName "smb-client-01" -Client "10.0.0.0/8" -Encryption "required" + + Creates a rule for a specific subnet with required encryption using typed parameters. .EXAMPLE New-PfbSmbClientRule -PolicyName "smb-client-01" -Attributes @{ client = "*" } @@ -22,17 +48,43 @@ function New-PfbSmbClientRule { Creates a rule for a specific subnet with required encryption. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByPolicyNameIndividual')] param( - [Parameter(ParameterSetName = 'ByPolicyName', Mandatory, Position = 0)] + [Parameter(ParameterSetName = 'ByPolicyNameIndividual', Mandatory, Position = 0)] + [Parameter(ParameterSetName = 'ByPolicyNameAttributes', Mandatory, Position = 0)] [string]$PolicyName, - [Parameter(ParameterSetName = 'ByPolicyId', Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyIdAttributes', Mandatory)] [string]$PolicyId, - [Parameter(Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [string]$Client, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [ValidateSet('required', 'disabled', 'optional')] + [string]$Encryption, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [int]$Index, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [ValidateSet('rw', 'ro')] + [string]$Permission, + + [Parameter(ParameterSetName = 'ByPolicyNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyIdAttributes', Mandatory)] [hashtable]$Attributes, + [Parameter()] [string]$BeforeRuleId, + [Parameter()] [string]$BeforeRuleName, + [Parameter()] [string[]]$Versions, + [Parameter()] [PSCustomObject]$Array ) @@ -41,10 +93,24 @@ function New-PfbSmbClientRule { $queryParams = @{} if ($PolicyName) { $queryParams['policy_names'] = $PolicyName } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId } + if ($PSBoundParameters.ContainsKey('BeforeRuleId')) { $queryParams['before_rule_id'] = $BeforeRuleId } + if ($PSBoundParameters.ContainsKey('BeforeRuleName')) { $queryParams['before_rule_name'] = $BeforeRuleName } + if ($PSBoundParameters.ContainsKey('Versions')) { $queryParams['versions'] = $Versions -join ',' } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('Client')) { $body['client'] = $Client } + if ($PSBoundParameters.ContainsKey('Encryption')) { $body['encryption'] = $Encryption } + if ($PSBoundParameters.ContainsKey('Index')) { $body['index'] = $Index } + if ($PSBoundParameters.ContainsKey('Permission')) { $body['permission'] = $Permission } + } $target = if ($PolicyName) { $PolicyName } else { $PolicyId } if ($PSCmdlet.ShouldProcess($target, 'Create SMB client rule')) { - Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'smb-client-policies/rules' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'smb-client-policies/rules' -Body $body -QueryParams $queryParams } } diff --git a/Public/Policy/New-PfbSmbShareRule.ps1 b/Public/Policy/New-PfbSmbShareRule.ps1 index 2a19603..e3d78e1 100644 --- a/Public/Policy/New-PfbSmbShareRule.ps1 +++ b/Public/Policy/New-PfbSmbShareRule.ps1 @@ -5,14 +5,32 @@ function New-PfbSmbShareRule { .DESCRIPTION Adds a new rule to an SMB share policy. Rules define share-level access control for SMB shares including principal, permission, and change settings. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER PolicyName The name of the SMB share policy to add the rule to. .PARAMETER PolicyId The ID of the SMB share policy to add the rule to. + .PARAMETER Change + The state of the principal's Change access permission. + .PARAMETER FullControl + The state of the principal's Full Control access permission. + .PARAMETER Principal + The user or group who is the subject of this rule, and optionally their domain. + .PARAMETER Read + The state of the principal's Read access permission. .PARAMETER Attributes A hashtable defining the rule properties (principal, change, read, full_control, etc.). + Mutually exclusive with the individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + New-PfbSmbShareRule -PolicyName "smb-share-01" -Principal "Everyone" -Change "allow" + + Creates a new rule granting change access to everyone using typed parameters. .EXAMPLE New-PfbSmbShareRule -PolicyName "smb-share-01" -Attributes @{ principal = "Everyone"; change = "allow" } @@ -22,15 +40,38 @@ function New-PfbSmbShareRule { Creates a rule granting full control to a domain group. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByPolicyNameIndividual')] param( - [Parameter(ParameterSetName = 'ByPolicyName', Mandatory, Position = 0)] + [Parameter(ParameterSetName = 'ByPolicyNameIndividual', Mandatory, Position = 0)] + [Parameter(ParameterSetName = 'ByPolicyNameAttributes', Mandatory, Position = 0)] [string]$PolicyName, - [Parameter(ParameterSetName = 'ByPolicyId', Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyIdAttributes', Mandatory)] [string]$PolicyId, - [Parameter(Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [ValidateSet('allow', 'deny')] + [string]$Change, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [ValidateSet('allow', 'deny')] + [string]$FullControl, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [string]$Principal, + + [Parameter(ParameterSetName = 'ByPolicyNameIndividual')] + [Parameter(ParameterSetName = 'ByPolicyIdIndividual')] + [ValidateSet('allow', 'deny')] + [string]$Read, + + [Parameter(ParameterSetName = 'ByPolicyNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByPolicyIdAttributes', Mandatory)] [hashtable]$Attributes, [Parameter()] [PSCustomObject]$Array @@ -42,9 +83,20 @@ function New-PfbSmbShareRule { if ($PolicyName) { $queryParams['policy_names'] = $PolicyName } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId } + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('Change')) { $body['change'] = $Change } + if ($PSBoundParameters.ContainsKey('FullControl')) { $body['full_control'] = $FullControl } + if ($PSBoundParameters.ContainsKey('Principal')) { $body['principal'] = $Principal } + if ($PSBoundParameters.ContainsKey('Read')) { $body['read'] = $Read } + } + $target = if ($PolicyName) { $PolicyName } else { $PolicyId } if ($PSCmdlet.ShouldProcess($target, 'Create SMB share rule')) { - Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'smb-share-policies/rules' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'smb-share-policies/rules' -Body $body -QueryParams $queryParams } } diff --git a/Public/Policy/New-PfbUserGroupQuotaPolicy.ps1 b/Public/Policy/New-PfbUserGroupQuotaPolicy.ps1 new file mode 100644 index 0000000..2c2642b --- /dev/null +++ b/Public/Policy/New-PfbUserGroupQuotaPolicy.ps1 @@ -0,0 +1,77 @@ +function New-PfbUserGroupQuotaPolicy { + <# + .SYNOPSIS + Creates a new user/group quota policy on a FlashBlade array. + .DESCRIPTION + The New-PfbUserGroupQuotaPolicy cmdlet creates a new user-group-quota policy on the + connected Pure Storage FlashBlade. Either build a policy from scratch with -Enabled/ + -Location/-Rules, or import an existing file system's legacy purequota/purefs quota + configuration into a new policy by specifying -FileSystemName or -FileSystemId + (mutually exclusive with each other). + .PARAMETER Name + The name for the new policy. + .PARAMETER FileSystemName + The name of a file system whose existing legacy quota configuration should be + imported into this new policy. Mutually exclusive with -FileSystemId. + .PARAMETER FileSystemId + The ID of a file system whose existing legacy quota configuration should be imported + into this new policy. Mutually exclusive with -FileSystemName. + .PARAMETER Enabled + Whether the policy is enabled. Defaults to true on the array if not specified. + .PARAMETER Location + A hashtable reference to the array where the policy is defined (fleet/realm use). + .PARAMETER Rules + An array of rule hashtables to create the policy with (see New-PfbUserGroupQuotaPolicyRule + for individual rule shape, e.g. @{ subject = @{ name = 'jdoe' }; quota_type = 'user'; + quota_limit = 1073741824 }). + .PARAMETER Attributes + A hashtable used verbatim as the request body, overriding -Enabled/-Location/-Rules. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + New-PfbUserGroupQuotaPolicy -Name "quota-pol-1" -Rules @(@{ subject = @{ name = 'jdoe' }; quota_type = 'user'; quota_limit = 1073741824 }) + + Creates a policy with one user quota rule for 'jdoe'. + .EXAMPLE + New-PfbUserGroupQuotaPolicy -Name "imported-pol" -FileSystemName "fs-home" + + Creates a new policy by importing 'fs-home''s existing legacy quota configuration. + .EXAMPLE + New-PfbUserGroupQuotaPolicy -Name "quota-pol-1" -WhatIf + + Shows what would happen without actually creating the policy. + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + param( + [Parameter(Mandatory, Position = 0)] [string]$Name, + [Parameter()] [string]$FileSystemName, + [Parameter()] [string]$FileSystemId, + [Parameter()] [Nullable[bool]]$Enabled, + [Parameter()] [hashtable]$Location, + [Parameter()] [hashtable[]]$Rules, + [Parameter()] [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array + ) + + if ($FileSystemName -and $FileSystemId) { + throw '-FileSystemName and -FileSystemId cannot both be specified.' + } + + Assert-PfbConnection -Array ([ref]$Array) + + if ($Attributes) { $body = $Attributes } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('Enabled')) { $body['enabled'] = [bool]$Enabled } + if ($Location) { $body['location'] = $Location } + if ($Rules) { $body['rules'] = $Rules } + } + + $queryParams = @{ 'names' = $Name } + if ($FileSystemName) { $queryParams['file_system_names'] = $FileSystemName } + if ($FileSystemId) { $queryParams['file_system_ids'] = $FileSystemId } + + if ($PSCmdlet.ShouldProcess($Name, 'Create user-group-quota policy')) { + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'user-group-quota-policies' -Body $body -QueryParams $queryParams + } +} diff --git a/Public/Policy/New-PfbUserGroupQuotaPolicyFileSystem.ps1 b/Public/Policy/New-PfbUserGroupQuotaPolicyFileSystem.ps1 new file mode 100644 index 0000000..9b931d5 --- /dev/null +++ b/Public/Policy/New-PfbUserGroupQuotaPolicyFileSystem.ps1 @@ -0,0 +1,57 @@ +function New-PfbUserGroupQuotaPolicyFileSystem { + <# + .SYNOPSIS + Attaches a user/group quota policy to a file system on a FlashBlade array. + .DESCRIPTION + The New-PfbUserGroupQuotaPolicyFileSystem cmdlet associates an existing + user-group-quota policy with a file system on the connected Pure Storage FlashBlade. + .PARAMETER PolicyName + The name of the policy to attach. + .PARAMETER PolicyId + The ID of the policy to attach. + .PARAMETER MemberName + The name of the file system to attach the policy to. + .PARAMETER MemberId + The ID of the file system to attach the policy to. + .PARAMETER DeleteExistingUserGroupQuotaSettings + If set, deletes the file system's existing legacy purequota/purefs quota settings + when attaching this policy. + .PARAMETER IgnoreUsage + If set, existing user/group usage on the file system is not checked against the + policy's rules. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + New-PfbUserGroupQuotaPolicyFileSystem -PolicyName "quota-pol-1" -MemberName "fs1" + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + param( + [Parameter()] [string]$PolicyName, + [Parameter()] [string]$PolicyId, + [Parameter()] [string]$MemberName, + [Parameter()] [string]$MemberId, + [Parameter()] [switch]$DeleteExistingUserGroupQuotaSettings, + [Parameter()] [switch]$IgnoreUsage, + [Parameter()] [PSCustomObject]$Array + ) + + if (-not $PolicyName -and -not $PolicyId) { throw 'You must supply either -PolicyName or -PolicyId.' } + if (-not $MemberName -and -not $MemberId) { throw 'You must supply either -MemberName or -MemberId.' } + + Assert-PfbConnection -Array ([ref]$Array) + + $queryParams = @{} + if ($PolicyName) { $queryParams['policy_names'] = $PolicyName } + if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId } + if ($MemberName) { $queryParams['member_names'] = $MemberName } + if ($MemberId) { $queryParams['member_ids'] = $MemberId } + if ($DeleteExistingUserGroupQuotaSettings) { $queryParams['delete_existing_user_group_quota_settings'] = 'true' } + if ($IgnoreUsage) { $queryParams['ignore_usage'] = 'true' } + + $target = if ($MemberName) { $MemberName } else { $MemberId } + $policy = if ($PolicyName) { $PolicyName } else { $PolicyId } + + if ($PSCmdlet.ShouldProcess($target, "Attach user-group-quota policy '$policy'")) { + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'user-group-quota-policies/file-systems' -QueryParams $queryParams + } +} diff --git a/Public/Policy/New-PfbUserGroupQuotaPolicyRule.ps1 b/Public/Policy/New-PfbUserGroupQuotaPolicyRule.ps1 new file mode 100644 index 0000000..52c5c49 --- /dev/null +++ b/Public/Policy/New-PfbUserGroupQuotaPolicyRule.ps1 @@ -0,0 +1,90 @@ +function New-PfbUserGroupQuotaPolicyRule { + <# + .SYNOPSIS + Adds a quota rule to a user/group quota policy on a FlashBlade array. + .DESCRIPTION + The New-PfbUserGroupQuotaPolicyRule cmdlet adds a new rule to an existing + user-group-quota policy on the connected Pure Storage FlashBlade. A rule applies a + quota limit to a specific user or group (-Subject with -QuotaType 'user'/'group'), or + sets the policy's default for all users/groups (-QuotaType 'user-default'/ + 'group-default', which cannot be combined with -Subject). + .PARAMETER PolicyName + The name of the policy to add the rule to. + .PARAMETER PolicyId + The ID of the policy to add the rule to. + .PARAMETER Subject + A hashtable identifying the user or group the rule applies to, e.g. @{ name = 'jdoe' }, + @{ id = 1001 } (UID/GID), or @{ sid = 'S-1-5-...' }. Omit for -QuotaType + 'user-default'/'group-default'. + .PARAMETER QuotaType + The rule's quota type: 'user', 'group', 'user-default', or 'group-default'. + .PARAMETER QuotaLimit + The quota limit in bytes. Cannot be 0. + .PARAMETER Enforced + If true, exceeding the quota is a hard error; if false, it is advisory-only. + .PARAMETER Notifications + Whether to notify the affected subject: 'None' or 'Account' (default 'Account'). + .PARAMETER IgnoreUsage + If set, existing user/group usage is not checked against this rule's quota_limit. + .PARAMETER Attributes + A hashtable used verbatim as the request body, overriding -Subject/-QuotaType/ + -QuotaLimit/-Enforced/-Notifications. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + New-PfbUserGroupQuotaPolicyRule -PolicyName "quota-pol-1" -Subject @{ name = 'jdoe' } -QuotaType user -QuotaLimit 1073741824 + + Adds a 1 GiB quota rule for user 'jdoe' to 'quota-pol-1'. + .EXAMPLE + New-PfbUserGroupQuotaPolicyRule -PolicyName "quota-pol-1" -QuotaType user-default -QuotaLimit 536870912 + + Sets a 512 MiB default quota for all users under 'quota-pol-1'. + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + param( + [Parameter(ParameterSetName = 'ByPolicyName', Mandatory, Position = 0)] + [string]$PolicyName, + + [Parameter(ParameterSetName = 'ByPolicyId', Mandatory)] + [string]$PolicyId, + + [Parameter()] [hashtable]$Subject, + [Parameter()] [ValidateSet('user', 'group', 'user-default', 'group-default')] [string]$QuotaType, + + [Parameter()] + [ValidateScript({ if ($_ -eq 0) { throw '-QuotaLimit cannot be 0.' }; $true })] + [int64]$QuotaLimit, + [Parameter()] [Nullable[bool]]$Enforced, + [Parameter()] [ValidateSet('None', 'Account')] [string]$Notifications, + [Parameter()] [switch]$IgnoreUsage, + [Parameter()] [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array + ) + + if ($Subject -and $QuotaType -in @('user-default', 'group-default')) { + throw "-QuotaType '$QuotaType' cannot be combined with -Subject." + } + + Assert-PfbConnection -Array ([ref]$Array) + + if ($Attributes) { $body = $Attributes } + else { + $body = @{} + if ($Subject) { $body['subject'] = $Subject } + if ($QuotaType) { $body['quota_type'] = $QuotaType } + if ($PSBoundParameters.ContainsKey('QuotaLimit')) { $body['quota_limit'] = $QuotaLimit } + if ($PSBoundParameters.ContainsKey('Enforced')) { $body['enforced'] = [bool]$Enforced } + if ($Notifications) { $body['notifications'] = $Notifications } + } + + $queryParams = @{} + if ($PolicyName) { $queryParams['policy_names'] = $PolicyName } + if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId } + if ($IgnoreUsage) { $queryParams['ignore_usage'] = 'true' } + + $target = if ($PolicyName) { $PolicyName } else { $PolicyId } + + if ($PSCmdlet.ShouldProcess($target, 'Add user-group-quota policy rule')) { + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'user-group-quota-policies/rules' -Body $body -QueryParams $queryParams + } +} diff --git a/Public/Policy/Remove-PfbUserGroupQuotaPolicy.ps1 b/Public/Policy/Remove-PfbUserGroupQuotaPolicy.ps1 new file mode 100644 index 0000000..690c1c1 --- /dev/null +++ b/Public/Policy/Remove-PfbUserGroupQuotaPolicy.ps1 @@ -0,0 +1,45 @@ +function Remove-PfbUserGroupQuotaPolicy { + <# + .SYNOPSIS + Removes a user/group quota policy from a FlashBlade array. + .PARAMETER Name + The name of the policy to remove. + .PARAMETER Id + The ID of the policy to remove. + .PARAMETER Version + One or more version tags for optimistic concurrency control. Fails with a 412 if the + resource's current version doesn't match. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + Remove-PfbUserGroupQuotaPolicy -Name "quota-pol-1" + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] + param( + [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [ValidateScript({ Assert-PfbSafeName $_ })] + [string]$Name, + + [Parameter(ParameterSetName = 'ById', Mandatory)] + [string]$Id, + + [Parameter()] [string[]]$Version, + [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 ($Version) { $queryParams['versions'] = $Version -join ',' } + + if ($PSCmdlet.ShouldProcess($target, 'Remove user-group-quota policy')) { + Invoke-PfbApiRequest -Array $Array -Method DELETE -Endpoint 'user-group-quota-policies' -QueryParams $queryParams + } + } +} diff --git a/Public/Policy/Remove-PfbUserGroupQuotaPolicyFileSystem.ps1 b/Public/Policy/Remove-PfbUserGroupQuotaPolicyFileSystem.ps1 new file mode 100644 index 0000000..eeff43d --- /dev/null +++ b/Public/Policy/Remove-PfbUserGroupQuotaPolicyFileSystem.ps1 @@ -0,0 +1,55 @@ +function Remove-PfbUserGroupQuotaPolicyFileSystem { + <# + .SYNOPSIS + Detaches a user/group quota policy from a file system on a FlashBlade array. + .PARAMETER PolicyName + The name of the policy to detach. + .PARAMETER PolicyId + The ID of the policy to detach. + .PARAMETER MemberName + The name of the file system to detach the policy from. + .PARAMETER MemberId + The ID of the file system to detach the policy from. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + Remove-PfbUserGroupQuotaPolicyFileSystem -PolicyName "quota-pol-1" -MemberName "fs1" + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] + param( + [Parameter()] + [ValidateScript({ Assert-PfbSafeName $_ })] + [string]$PolicyName, + + [Parameter()] [string]$PolicyId, + + [Parameter()] + [ValidateScript({ Assert-PfbSafeName $_ })] + [string]$MemberName, + + [Parameter()] [string]$MemberId, + [Parameter()] [PSCustomObject]$Array + ) + + if (-not $PolicyName -and -not $PolicyId) { + throw 'You must supply either -PolicyName or -PolicyId.' + } + if (-not $MemberName -and -not $MemberId) { + throw 'You must supply either -MemberName or -MemberId.' + } + + Assert-PfbConnection -Array ([ref]$Array) + + $queryParams = @{} + if ($PolicyName) { $queryParams['policy_names'] = $PolicyName } + if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId } + if ($MemberName) { $queryParams['member_names'] = $MemberName } + if ($MemberId) { $queryParams['member_ids'] = $MemberId } + + $policy = if ($PolicyName) { $PolicyName } else { $PolicyId } + $member = if ($MemberName) { $MemberName } else { $MemberId } + + if ($PSCmdlet.ShouldProcess($member, "Detach user-group-quota policy '$policy'")) { + Invoke-PfbApiRequest -Array $Array -Method DELETE -Endpoint 'user-group-quota-policies/file-systems' -QueryParams $queryParams + } +} diff --git a/Public/Policy/Remove-PfbUserGroupQuotaPolicyRule.ps1 b/Public/Policy/Remove-PfbUserGroupQuotaPolicyRule.ps1 new file mode 100644 index 0000000..d9ba1c0 --- /dev/null +++ b/Public/Policy/Remove-PfbUserGroupQuotaPolicyRule.ps1 @@ -0,0 +1,61 @@ +function Remove-PfbUserGroupQuotaPolicyRule { + <# + .SYNOPSIS + Removes one or more user/group quota policy rules from a FlashBlade array. + .DESCRIPTION + The Remove-PfbUserGroupQuotaPolicyRule cmdlet deletes rules from a user-group-quota + policy on the connected Pure Storage FlashBlade. Target specific rules with -Name/-Id, + or clear every rule belonging to a policy with -PolicyName/-PolicyId alone. When both + -Name/-Id and -PolicyName/-PolicyId are supplied together, they combine as AND: only + the named rule(s) that also belong to the named policy/policies are removed. + .PARAMETER Name + One or more rule names to remove. + .PARAMETER Id + One or more rule IDs to remove. + .PARAMETER PolicyName + One or more policy names; removes all rules belonging to these policies. + .PARAMETER PolicyId + One or more policy IDs; removes all rules belonging to these policies. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + Remove-PfbUserGroupQuotaPolicyRule -Name "quota-pol-1.1" + .EXAMPLE + Remove-PfbUserGroupQuotaPolicyRule -PolicyName "quota-pol-1" + + Removes every rule belonging to 'quota-pol-1'. + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] + param( + [Parameter()] + [ValidateScript({ Assert-PfbSafeName $_ })] + [string[]]$Name, + + [Parameter()] [string[]]$Id, + + [Parameter()] + [ValidateScript({ Assert-PfbSafeName $_ })] + [string[]]$PolicyName, + + [Parameter()] [string[]]$PolicyId, + [Parameter()] [PSCustomObject]$Array + ) + + if (-not $Name -and -not $Id -and -not $PolicyName -and -not $PolicyId) { + throw 'You must supply at least one of -Name, -Id, -PolicyName, or -PolicyId.' + } + + Assert-PfbConnection -Array ([ref]$Array) + + $queryParams = @{} + if ($Name) { $queryParams['names'] = $Name -join ',' } + if ($Id) { $queryParams['ids'] = $Id -join ',' } + if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } + if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } + + $target = if ($Name) { $Name -join ',' } elseif ($Id) { $Id -join ',' } elseif ($PolicyName) { "policy:$($PolicyName -join ',')" } else { "policy:$($PolicyId -join ',')" } + + if ($PSCmdlet.ShouldProcess($target, 'Remove user-group-quota policy rule')) { + Invoke-PfbApiRequest -Array $Array -Method DELETE -Endpoint 'user-group-quota-policies/rules' -QueryParams $queryParams + } +} diff --git a/Public/Policy/Update-PfbQosPolicy.ps1 b/Public/Policy/Update-PfbQosPolicy.ps1 index 8d9eb2e..5f6adc9 100644 --- a/Public/Policy/Update-PfbQosPolicy.ps1 +++ b/Public/Policy/Update-PfbQosPolicy.ps1 @@ -4,19 +4,36 @@ function Update-PfbQosPolicy { Updates an existing QoS policy on a FlashBlade array. .DESCRIPTION The Update-PfbQosPolicy cmdlet modifies attributes of an existing QoS policy on the - connected Pure Storage FlashBlade. The policy can be identified by name or ID. + connected Everpure FlashBlade. The policy can be identified by name or ID. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name - The name of the QoS policy to update. + The name of the QoS policy to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the QoS policy to update. + .PARAMETER Enabled + If true, the policy is enabled. + .PARAMETER Location + Reference to the array where the policy is defined. + .PARAMETER MaxTotalBytesPerSec + The maximum allowed bytes/s totaled across all the clients. + .PARAMETER MaxTotalOpsPerSec + The maximum allowed operations/s totaled across all the clients. + .PARAMETER NewName + A new name for the QoS policy. .PARAMETER Attributes - A hashtable of QoS policy attributes to modify. + A hashtable of QoS policy attributes to modify. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbQosPolicy -Name "qos-gold" -Attributes @{ max_total_bytes_per_sec = 2147483648 } + Update-PfbQosPolicy -Name "qos-gold" -MaxTotalBytesPerSec 2147483648 - Updates the max bandwidth for the specified QoS policy to 2 GB/s. + Updates the max bandwidth for the specified QoS policy to 2 GB/s using a typed + parameter. .EXAMPLE Update-PfbQosPolicy -Id "10314f42-020d-7080-8013-000ddt400012" -Attributes @{ max_total_ops_per_sec = 20000 } @@ -26,11 +43,41 @@ function Update-PfbQosPolicy { Shows what would happen without actually updating the policy. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [Nullable[bool]]$Enabled, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Location, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$MaxTotalBytesPerSec, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$MaxTotalOpsPerSec, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$NewName, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -41,9 +88,22 @@ function Update-PfbQosPolicy { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('Enabled')) { $body['enabled'] = $Enabled } + if ($PSBoundParameters.ContainsKey('Location')) { $body['location'] = @{ name = $Location } } + if ($PSBoundParameters.ContainsKey('MaxTotalBytesPerSec')) { $body['max_total_bytes_per_sec'] = $MaxTotalBytesPerSec } + if ($PSBoundParameters.ContainsKey('MaxTotalOpsPerSec')) { $body['max_total_ops_per_sec'] = $MaxTotalOpsPerSec } + if ($PSBoundParameters.ContainsKey('NewName')) { $body['name'] = $NewName } + } + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update QoS policy')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'qos-policies' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'qos-policies' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Policy/Update-PfbSshCaPolicy.ps1 b/Public/Policy/Update-PfbSshCaPolicy.ps1 index 5c9d1e5..c6c8613 100644 --- a/Public/Policy/Update-PfbSshCaPolicy.ps1 +++ b/Public/Policy/Update-PfbSshCaPolicy.ps1 @@ -4,19 +4,37 @@ function Update-PfbSshCaPolicy { Updates an existing SSH certificate authority policy on a FlashBlade array. .DESCRIPTION The Update-PfbSshCaPolicy cmdlet modifies attributes of an existing SSH CA policy on - the connected Pure Storage FlashBlade. The policy can be identified by name or ID. + the connected Everpure FlashBlade. The policy can be identified by name or ID. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name - The name of the SSH CA policy to update. + The name of the SSH CA policy to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the SSH CA policy to update. + .PARAMETER Enabled + If true, the policy is enabled. + .PARAMETER Location + Reference to the array where the policy is defined. + .PARAMETER NewName + A new name for the SSH CA policy. + .PARAMETER SigningAuthority + A reference to the authority that will digitally sign user SSH certificates that + will be used to access the system. + .PARAMETER StaticAuthorizedPrincipals + If not specified, users affected by this policy can only log into the system when + they present an SSH certificate containing their own username as a principal. .PARAMETER Attributes - A hashtable of SSH CA policy attributes to modify. + A hashtable of SSH CA policy attributes to modify. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbSshCaPolicy -Name "ssh-ca-prod" -Attributes @{ enabled = $true } + Update-PfbSshCaPolicy -Name "ssh-ca-prod" -Enabled $true - Enables the SSH CA policy named "ssh-ca-prod". + Enables the SSH CA policy named "ssh-ca-prod" using a typed parameter. .EXAMPLE Update-PfbSshCaPolicy -Id "10314f42-020d-7080-8013-000ddt400012" -Attributes @{ public_key = "ssh-rsa NEW..." } @@ -26,11 +44,41 @@ function Update-PfbSshCaPolicy { Shows what would happen without actually updating the policy. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [Nullable[bool]]$Enabled, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Location, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$NewName, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$SigningAuthority, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$StaticAuthorizedPrincipals, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -41,9 +89,24 @@ function Update-PfbSshCaPolicy { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('Enabled')) { $body['enabled'] = $Enabled } + if ($PSBoundParameters.ContainsKey('Location')) { $body['location'] = @{ name = $Location } } + if ($PSBoundParameters.ContainsKey('NewName')) { $body['name'] = $NewName } + if ($PSBoundParameters.ContainsKey('SigningAuthority')) { $body['signing_authority'] = @{ name = $SigningAuthority } } + if ($PSBoundParameters.ContainsKey('StaticAuthorizedPrincipals')) { + $body['static_authorized_principals'] = @($StaticAuthorizedPrincipals) + } + } + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update SSH CA policy')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'ssh-certificate-authority-policies' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'ssh-certificate-authority-policies' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Policy/Update-PfbStorageClassTieringPolicy.ps1 b/Public/Policy/Update-PfbStorageClassTieringPolicy.ps1 index a05431b..5c8ca4e 100644 --- a/Public/Policy/Update-PfbStorageClassTieringPolicy.ps1 +++ b/Public/Policy/Update-PfbStorageClassTieringPolicy.ps1 @@ -4,19 +4,35 @@ function Update-PfbStorageClassTieringPolicy { Updates an existing storage class tiering policy on a FlashBlade array. .DESCRIPTION The Update-PfbStorageClassTieringPolicy cmdlet modifies attributes of an existing - storage class tiering policy on the connected Pure Storage FlashBlade. + storage class tiering policy on the connected Everpure FlashBlade. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name - The name of the tiering policy to update. + The name of the tiering policy to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the tiering policy to update. + .PARAMETER ArchivalRules + The list of archival rules for this policy. + .PARAMETER Enabled + If true, the policy is enabled. + .PARAMETER Location + Reference to the array where the policy is defined. + .PARAMETER NewName + A new name for the tiering policy. + .PARAMETER RetrievalRules + The list of retrieval rules for this policy. .PARAMETER Attributes - A hashtable of tiering policy attributes to modify. + A hashtable of tiering policy attributes to modify. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbStorageClassTieringPolicy -Name "tier-to-archive" -Attributes @{ enabled = $false } + Update-PfbStorageClassTieringPolicy -Name "tier-to-archive" -Enabled $false - Disables the tiering policy. + Disables the tiering policy using a typed parameter. .EXAMPLE Update-PfbStorageClassTieringPolicy -Id "10314f42-020d-7080-8013-000ddt400012" -Attributes @{ cooldown_period = 172800000 } @@ -26,11 +42,41 @@ function Update-PfbStorageClassTieringPolicy { Shows what would happen without actually updating the policy. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [hashtable[]]$ArchivalRules, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [Nullable[bool]]$Enabled, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Location, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$NewName, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [hashtable[]]$RetrievalRules, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -41,9 +87,25 @@ function Update-PfbStorageClassTieringPolicy { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Constraint 8(c): archival_rules/retrieval_rules are composite objects (they carry + # properties outside {id, name, resource_type}), so the parameter is [hashtable[]] + # and the value is passed straight through -- no @{ name = ... } projection. + $body = @{} + if ($PSBoundParameters.ContainsKey('ArchivalRules')) { $body['archival_rules'] = @($ArchivalRules) } + if ($PSBoundParameters.ContainsKey('Enabled')) { $body['enabled'] = $Enabled } + if ($PSBoundParameters.ContainsKey('Location')) { $body['location'] = @{ name = $Location } } + if ($PSBoundParameters.ContainsKey('NewName')) { $body['name'] = $NewName } + if ($PSBoundParameters.ContainsKey('RetrievalRules')) { $body['retrieval_rules'] = @($RetrievalRules) } + } + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update storage class tiering policy')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'storage-class-tiering-policies' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'storage-class-tiering-policies' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Policy/Update-PfbTlsPolicy.ps1 b/Public/Policy/Update-PfbTlsPolicy.ps1 index f81a53f..6b5c0e5 100644 --- a/Public/Policy/Update-PfbTlsPolicy.ps1 +++ b/Public/Policy/Update-PfbTlsPolicy.ps1 @@ -4,33 +4,114 @@ function Update-PfbTlsPolicy { Updates an existing TLS policy on a FlashBlade array. .DESCRIPTION The Update-PfbTlsPolicy cmdlet modifies attributes of an existing TLS policy on the - connected Pure Storage FlashBlade. The policy can be identified by name or ID. + connected Everpure FlashBlade. The policy can be identified by name or ID. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name - The name of the TLS policy to update. + The name of the TLS policy to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the TLS policy to update. + .PARAMETER ApplianceCertificate + A reference to a certificate that will be presented as the server certificate in + TLS negotiations with any clients that connect to appliance network addresses. + .PARAMETER ClientCertificatesRequired + If true, then all clients negotiating TLS connections with network interfaces to + which this policy applies will be required to provide their client certificate. + .PARAMETER DisabledTlsCiphers + If specified, disables the specific TLS ciphers. + .PARAMETER Enabled + If true, the policy is enabled. + .PARAMETER EnabledTlsCiphers + If specified, enables only the specified TLS ciphers. + .PARAMETER Location + Reference to the array where the policy is defined. + .PARAMETER MinTlsVersion + The minimum TLS version that will be allowed for inbound connections on IPs to + which this policy applies. + .PARAMETER NewName + A new name for the TLS policy. + .PARAMETER TrustedClientCertificateAuthority + A reference to a certificate or certificate group. + .PARAMETER VerifyClientCertificateTrust + If true, then any certificate presented by a client in TLS negotiation will undergo + strict trust verification using the certificate(s) referenced by + -TrustedClientCertificateAuthority. .PARAMETER Attributes - A hashtable of TLS policy attributes to modify. + A hashtable of TLS policy attributes to modify. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbTlsPolicy -Name "tls-strict" -Attributes @{ min_version = "1.3" } + Update-PfbTlsPolicy -Name "tls-strict" -MinTlsVersion "1.3" - Updates the minimum TLS version for the specified policy. + Updates the minimum TLS version for the specified policy using a typed parameter. .EXAMPLE - Update-PfbTlsPolicy -Id "10314f42-020d-7080-8013-000ddt400012" -Attributes @{ min_version = "1.2" } + Update-PfbTlsPolicy -Id "10314f42-020d-7080-8013-000ddt400012" -Attributes @{ min_tls_version = "1.2" } Updates the TLS policy by ID. .EXAMPLE - Update-PfbTlsPolicy -Name "tls-strict" -Attributes @{ min_version = "1.3" } -WhatIf + Update-PfbTlsPolicy -Name "tls-strict" -Attributes @{ min_tls_version = "1.3" } -WhatIf Shows what would happen without actually updating the policy. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$ApplianceCertificate, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [Nullable[bool]]$ClientCertificatesRequired, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$DisabledTlsCiphers, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [Nullable[bool]]$Enabled, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$EnabledTlsCiphers, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Location, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$MinTlsVersion, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$NewName, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$TrustedClientCertificateAuthority, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [Nullable[bool]]$VerifyClientCertificateTrust, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -41,9 +122,29 @@ function Update-PfbTlsPolicy { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('ApplianceCertificate')) { $body['appliance_certificate'] = @{ name = $ApplianceCertificate } } + if ($PSBoundParameters.ContainsKey('ClientCertificatesRequired')) { $body['client_certificates_required'] = $ClientCertificatesRequired } + if ($PSBoundParameters.ContainsKey('DisabledTlsCiphers')) { $body['disabled_tls_ciphers'] = @($DisabledTlsCiphers) } + if ($PSBoundParameters.ContainsKey('Enabled')) { $body['enabled'] = $Enabled } + if ($PSBoundParameters.ContainsKey('EnabledTlsCiphers')) { $body['enabled_tls_ciphers'] = @($EnabledTlsCiphers) } + if ($PSBoundParameters.ContainsKey('Location')) { $body['location'] = @{ name = $Location } } + if ($PSBoundParameters.ContainsKey('MinTlsVersion')) { $body['min_tls_version'] = $MinTlsVersion } + if ($PSBoundParameters.ContainsKey('NewName')) { $body['name'] = $NewName } + if ($PSBoundParameters.ContainsKey('TrustedClientCertificateAuthority')) { + $body['trusted_client_certificate_authority'] = @{ name = $TrustedClientCertificateAuthority } + } + if ($PSBoundParameters.ContainsKey('VerifyClientCertificateTrust')) { $body['verify_client_certificate_trust'] = $VerifyClientCertificateTrust } + } + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update TLS policy')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'tls-policies' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'tls-policies' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Policy/Update-PfbUserGroupQuotaPolicy.ps1 b/Public/Policy/Update-PfbUserGroupQuotaPolicy.ps1 new file mode 100644 index 0000000..dec7e6b --- /dev/null +++ b/Public/Policy/Update-PfbUserGroupQuotaPolicy.ps1 @@ -0,0 +1,69 @@ +function Update-PfbUserGroupQuotaPolicy { + <# + .SYNOPSIS + Updates an existing user/group quota policy on a FlashBlade array. + .PARAMETER Name + The name of the policy to update. + .PARAMETER Id + The ID of the policy to update. + .PARAMETER Enabled + Enable or disable the policy. + .PARAMETER Location + A hashtable reference to the array where the policy is defined (fleet/realm use). + .PARAMETER Rules + Replaces the policy's rules with this array of rule hashtables. + .PARAMETER IgnoreUsage + If set, user/group usage is not checked against the rules' quota_limits. + .PARAMETER Version + One or more version tags for optimistic concurrency control. Fails with a 412 if the + resource's current version doesn't match. + .PARAMETER Attributes + A hashtable used verbatim as the request body, overriding -Enabled/-Location/-Rules. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + Update-PfbUserGroupQuotaPolicy -Name "quota-pol-1" -Enabled:$false + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + param( + [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] + [ValidateScript({ Assert-PfbSafeName $_ })] + [string]$Name, + + [Parameter(ParameterSetName = 'ById', Mandatory)] + [string]$Id, + + [Parameter()] [Nullable[bool]]$Enabled, + [Parameter()] [hashtable]$Location, + [Parameter()] [hashtable[]]$Rules, + [Parameter()] [switch]$IgnoreUsage, + [Parameter()] [string[]]$Version, + [Parameter()] [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array + ) + + begin { + Assert-PfbConnection -Array ([ref]$Array) + } + + process { + if ($Attributes) { $body = $Attributes } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('Enabled')) { $body['enabled'] = [bool]$Enabled } + if ($Location) { $body['location'] = $Location } + if ($Rules) { $body['rules'] = $Rules } + } + + $queryParams = @{} + if ($Name) { $queryParams['names'] = $Name } + if ($Id) { $queryParams['ids'] = $Id } + if ($IgnoreUsage) { $queryParams['ignore_usage'] = 'true' } + if ($Version) { $queryParams['versions'] = $Version -join ',' } + $target = if ($Name) { $Name } else { $Id } + + if ($PSCmdlet.ShouldProcess($target, 'Update user-group-quota policy')) { + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'user-group-quota-policies' -Body $body -QueryParams $queryParams + } + } +} diff --git a/Public/Policy/Update-PfbUserGroupQuotaPolicyRule.ps1 b/Public/Policy/Update-PfbUserGroupQuotaPolicyRule.ps1 new file mode 100644 index 0000000..cecbcbe --- /dev/null +++ b/Public/Policy/Update-PfbUserGroupQuotaPolicyRule.ps1 @@ -0,0 +1,66 @@ +function Update-PfbUserGroupQuotaPolicyRule { + <# + .SYNOPSIS + Updates a user/group quota policy rule on a FlashBlade array. + .DESCRIPTION + The Update-PfbUserGroupQuotaPolicyRule cmdlet modifies an existing user-group-quota + policy rule. Only -QuotaLimit and -Notifications can be changed after creation; to + change a rule's subject or quota type, remove and recreate it. + .PARAMETER Name + The name of the rule to update. + .PARAMETER Id + The ID of the rule to update. + .PARAMETER QuotaLimit + The new quota limit in bytes. Cannot be 0. + .PARAMETER Notifications + Whether to notify the affected subject: 'None' or 'Account'. + .PARAMETER IgnoreUsage + If set, existing user/group usage is not checked against the new quota_limit. + .PARAMETER Attributes + A hashtable used verbatim as the request body, overriding -QuotaLimit/-Notifications. + .PARAMETER Array + The FlashBlade connection object. If not specified, the default connection is used. + .EXAMPLE + Update-PfbUserGroupQuotaPolicyRule -Name "quota-pol-1.1" -QuotaLimit 2147483648 + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + param( + [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] + [ValidateScript({ Assert-PfbSafeName $_ })] + [string]$Name, + + [Parameter(ParameterSetName = 'ById', Mandatory)] + [string]$Id, + + [Parameter()] + [ValidateScript({ if ($_ -eq 0) { throw '-QuotaLimit cannot be 0.' }; $true })] + [int64]$QuotaLimit, + [Parameter()] [ValidateSet('None', 'Account')] [string]$Notifications, + [Parameter()] [switch]$IgnoreUsage, + [Parameter()] [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array + ) + + begin { + Assert-PfbConnection -Array ([ref]$Array) + } + + process { + if ($Attributes) { $body = $Attributes } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('QuotaLimit')) { $body['quota_limit'] = $QuotaLimit } + if ($Notifications) { $body['notifications'] = $Notifications } + } + + $queryParams = @{} + if ($Name) { $queryParams['names'] = $Name } + if ($Id) { $queryParams['ids'] = $Id } + if ($IgnoreUsage) { $queryParams['ignore_usage'] = 'true' } + $target = if ($Name) { $Name } else { $Id } + + if ($PSCmdlet.ShouldProcess($target, 'Update user-group-quota policy rule')) { + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'user-group-quota-policies/rules' -Body $body -QueryParams $queryParams + } + } +} diff --git a/Public/Policy/Update-PfbWormPolicy.ps1 b/Public/Policy/Update-PfbWormPolicy.ps1 index dcbae28..12eb9e3 100644 --- a/Public/Policy/Update-PfbWormPolicy.ps1 +++ b/Public/Policy/Update-PfbWormPolicy.ps1 @@ -4,19 +4,43 @@ function Update-PfbWormPolicy { Updates an existing WORM data policy on a FlashBlade array. .DESCRIPTION The Update-PfbWormPolicy cmdlet modifies attributes of an existing WORM data policy - on the connected Pure Storage FlashBlade. The policy can be identified by name or ID. + on the connected Everpure FlashBlade. The policy can be identified by name or ID. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. This cmdlet is compliance-critical: -Mode, + -RetentionLock, -MinRetention, -MaxRetention and -DefaultRetention govern WORM + retention-lock behavior. No additional mutual-exclusion guard is added beyond the + parameter sets themselves -- they already make a silent override impossible. .PARAMETER Name - The name of the WORM policy to update. + The name of the WORM policy to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the WORM policy to update. + .PARAMETER DefaultRetention + Default retention period, in milliseconds. + .PARAMETER Enabled + If true, the policy is enabled. + .PARAMETER Location + Reference to the array where the policy is defined. + .PARAMETER MaxRetention + Maximum retention period, in milliseconds. + .PARAMETER MinRetention + Minimum retention period, in milliseconds. + .PARAMETER Mode + The type of the retention lock. + .PARAMETER RetentionLock + If set to `locked`, then the value of retention attributes or policy attributes + are not allowed to change. .PARAMETER Attributes - A hashtable of WORM policy attributes to modify. + A hashtable of WORM policy attributes to modify. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbWormPolicy -Name "worm-compliance" -Attributes @{ min_retention = "P90D" } + Update-PfbWormPolicy -Name "worm-compliance" -MinRetention 7776000000 - Updates the minimum retention period for the WORM policy. + Updates the minimum retention period for the WORM policy using a typed parameter. .EXAMPLE Update-PfbWormPolicy -Id "10314f42-020d-7080-8013-000ddt400012" -Attributes @{ enabled = $true } @@ -26,11 +50,50 @@ function Update-PfbWormPolicy { Shows what would happen without actually updating the policy. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$DefaultRetention, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [Nullable[bool]]$Enabled, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Location, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$MaxRetention, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [long]$MinRetention, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Mode, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [ValidateSet('unlocked', 'locked')] + [string]$RetentionLock, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -41,9 +104,29 @@ function Update-PfbWormPolicy { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Constraint 2: default_retention/max_retention/min_retention are integer body + # fields, guarded with ContainsKey (not truthiness) so an explicit 0 reaches the + # wire. Constraint 5: no extra mutual-exclusion throw is added here even though + # these fields are compliance-critical -- the parameter sets above already make + # a silent override across -Attributes and the typed parameters impossible. + $body = @{} + if ($PSBoundParameters.ContainsKey('DefaultRetention')) { $body['default_retention'] = $DefaultRetention } + if ($PSBoundParameters.ContainsKey('Enabled')) { $body['enabled'] = $Enabled } + if ($PSBoundParameters.ContainsKey('Location')) { $body['location'] = @{ name = $Location } } + if ($PSBoundParameters.ContainsKey('MaxRetention')) { $body['max_retention'] = $MaxRetention } + if ($PSBoundParameters.ContainsKey('MinRetention')) { $body['min_retention'] = $MinRetention } + if ($PSBoundParameters.ContainsKey('Mode')) { $body['mode'] = $Mode } + if ($PSBoundParameters.ContainsKey('RetentionLock')) { $body['retention_lock'] = $RetentionLock } + } + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update WORM data policy')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'worm-data-policies' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'worm-data-policies' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Presets/Get-PfbPresetWorkload.ps1 b/Public/Presets/Get-PfbPresetWorkload.ps1 index 59ad122..fb8896c 100644 --- a/Public/Presets/Get-PfbPresetWorkload.ps1 +++ b/Public/Presets/Get-PfbPresetWorkload.ps1 @@ -32,8 +32,7 @@ function Get-PfbPresetWorkload { process { $queryParams = @{} - if ($Name) { $queryParams['names'] = $Name -join ',' } - if ($Id) { $queryParams['ids'] = $Id -join ',' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $Name -Ids $Id Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'presets/workload' -QueryParams $queryParams } diff --git a/Public/Presets/Set-PfbPresetWorkload.ps1 b/Public/Presets/Set-PfbPresetWorkload.ps1 index a8bd5f3..9a4b7fa 100644 --- a/Public/Presets/Set-PfbPresetWorkload.ps1 +++ b/Public/Presets/Set-PfbPresetWorkload.ps1 @@ -44,16 +44,7 @@ function Set-PfbPresetWorkload { $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Replace workload preset (PUT)')) { - # PUT not supported by Invoke-PfbApiRequest's ValidateSet — call directly. - $apiVer = $Array.ApiVersion - $qs = ConvertTo-PfbQueryString -Parameters $queryParams - $uri = "https://$($Array.Endpoint)/api/${apiVer}/presets/workload${qs}" - $headers = @{ 'Content-Type' = 'application/json'; 'x-auth-token' = $Array.AuthToken } - $restParams = @{ Method = 'PUT'; Uri = $uri; Headers = $headers; Body = ($Attributes | ConvertTo-Json -Depth 15) } - if ($Array.SkipCertificateCheck -and $PSVersionTable.PSVersion.Major -ge 6) { - $restParams['SkipCertificateCheck'] = $true - } - Write-Verbose "FlashBlade API: PUT $uri" - Invoke-RestMethod @restParams + Invoke-PfbApiRequest -Array $Array -Method PUT -Endpoint 'presets/workload' ` + -Body $Attributes -QueryParams $queryParams } } diff --git a/Public/Quota/Get-PfbQuotaGroup.ps1 b/Public/Quota/Get-PfbQuotaGroup.ps1 index 7b11985..c10bf87 100644 --- a/Public/Quota/Get-PfbQuotaGroup.ps1 +++ b/Public/Quota/Get-PfbQuotaGroup.ps1 @@ -40,10 +40,7 @@ function Get-PfbQuotaGroup { ) Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} - if ($Name) { $queryParams['names'] = $Name -join ',' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $Name if ($FileSystemName) { $queryParams['file_system_names'] = $FileSystemName } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'quotas/groups' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Quota/Get-PfbQuotaUser.ps1 b/Public/Quota/Get-PfbQuotaUser.ps1 index c8b5fa9..1484837 100644 --- a/Public/Quota/Get-PfbQuotaUser.ps1 +++ b/Public/Quota/Get-PfbQuotaUser.ps1 @@ -45,14 +45,14 @@ function Get-PfbQuotaUser { # names parameter along with any of: file system, user IDs, or user names" -- # confirmed live against our lab array). The compound name (e.g. 'fs-share/1235') already # identifies the file system, so file_system_names is omitted in this branch. - $queryParams['names'] = $Name -join ',' + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $Name } - elseif ($FileSystemName) { - $queryParams['file_system_names'] = $FileSystemName + else { + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + if ($FileSystemName) { + $queryParams['file_system_names'] = $FileSystemName + } } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } $response = Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'quotas/users' -QueryParams $queryParams -AutoPaginate foreach ($item in $response) { if ($null -ne $item) { diff --git a/Public/Realm/Get-PfbRealm.ps1 b/Public/Realm/Get-PfbRealm.ps1 index 570ec91..ba29e16 100644 --- a/Public/Realm/Get-PfbRealm.ps1 +++ b/Public/Realm/Get-PfbRealm.ps1 @@ -71,12 +71,7 @@ function Get-PfbRealm { 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 } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds if ($Destroyed) { $queryParams['destroyed'] = 'true' } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'realms' -QueryParams $queryParams -AutoPaginate diff --git a/Public/Realm/Get-PfbRealmDefaults.ps1 b/Public/Realm/Get-PfbRealmDefaults.ps1 index 49ce10f..99f2c49 100644 --- a/Public/Realm/Get-PfbRealmDefaults.ps1 +++ b/Public/Realm/Get-PfbRealmDefaults.ps1 @@ -45,10 +45,7 @@ function Get-PfbRealmDefaults { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'realms/defaults' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Realm/Get-PfbRealmSpace.ps1 b/Public/Realm/Get-PfbRealmSpace.ps1 index af6c1a8..6bcf75b 100644 --- a/Public/Realm/Get-PfbRealmSpace.ps1 +++ b/Public/Realm/Get-PfbRealmSpace.ps1 @@ -45,10 +45,7 @@ function Get-PfbRealmSpace { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'realms/space' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Realm/Get-PfbRealmStorageClass.ps1 b/Public/Realm/Get-PfbRealmStorageClass.ps1 index 8b7bd33..14ca438 100644 --- a/Public/Realm/Get-PfbRealmStorageClass.ps1 +++ b/Public/Realm/Get-PfbRealmStorageClass.ps1 @@ -45,10 +45,7 @@ function Get-PfbRealmStorageClass { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames try { Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'realms/space/storage-classes' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Realm/Update-PfbRealmDefaults.ps1 b/Public/Realm/Update-PfbRealmDefaults.ps1 index e27a8fc..302c3e4 100644 --- a/Public/Realm/Update-PfbRealmDefaults.ps1 +++ b/Public/Realm/Update-PfbRealmDefaults.ps1 @@ -4,19 +4,27 @@ function Update-PfbRealmDefaults { Updates realm default settings on a FlashBlade array. .DESCRIPTION The Update-PfbRealmDefaults cmdlet modifies the default settings for a realm on the - connected Pure Storage FlashBlade. + connected Everpure FlashBlade. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the realm to update defaults for. .PARAMETER Id The ID of the realm to update defaults for. + .PARAMETER ObjectStore + Default configurations for object store. .PARAMETER Attributes - A hashtable of realm default attributes to modify. + A hashtable of realm default attributes to modify. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbRealmDefaults -Name "realm-prod" -Attributes @{ quota_limit = 1099511627776 } + Update-PfbRealmDefaults -Name "realm-prod" -ObjectStore @{ server = @{ name = "obj-server-1" } } - Updates the default quota limit for the specified realm. + Updates the default object store server for the specified realm using a typed parameter. .EXAMPLE Update-PfbRealmDefaults -Id "10314f42-020d-7080-8013-000ddt400012" -Attributes @{} @@ -26,11 +34,25 @@ function Update-PfbRealmDefaults { Shows what would happen without actually updating the defaults. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [hashtable[]]$ObjectStore, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -38,12 +60,28 @@ function Update-PfbRealmDefaults { } process { + # Wire-key bug fix (#31): PATCH /realms/defaults only accepts realm_ids/realm_names + # as query parameters -- there is no plain names/ids query parameter on this endpoint. + # The previous code sent names/ids, which the array does not recognize for this + # endpoint, so -Name and -Id had no effect on which realm's defaults were targeted. $queryParams = @{} - if ($Name) { $queryParams['names'] = $Name } - if ($Id) { $queryParams['ids'] = $Id } + if ($Name) { $queryParams['realm_names'] = $Name } + if ($Id) { $queryParams['realm_ids'] = $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + # Constraint 8(c): object_store is a COMPOSITE array (item schema has a property, + # `server`, outside {id, name, resource_type}), so the parameter is [hashtable[]] + # and is passed straight through -- constraint 7 forbids a $objectStoreItems local. + $body = @{} + if ($PSBoundParameters.ContainsKey('ObjectStore')) { $body['object_store'] = @($ObjectStore) } + } + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update realm defaults')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'realms/defaults' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'realms/defaults' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Replication/Get-PfbArrayConnection.ps1 b/Public/Replication/Get-PfbArrayConnection.ps1 index 7cc1b04..c53da51 100644 --- a/Public/Replication/Get-PfbArrayConnection.ps1 +++ b/Public/Replication/Get-PfbArrayConnection.ps1 @@ -4,17 +4,24 @@ function Get-PfbArrayConnection { Retrieves FlashBlade array connections (replication links between arrays). .DESCRIPTION The Get-PfbArrayConnection cmdlet returns array connection configurations from the - connected Pure Storage FlashBlade. Array connections define replication links between + connected Everpure FlashBlade. Array connections define replication links between FlashBlade arrays and are required for file system and bucket replication. Supports pipeline input, server-side filtering, and automatic pagination. - .PARAMETER Name - One or more array connection names to retrieve. Accepts pipeline input. + + An array connection has no name of its own -- the API resource carries only an id. Its + human-readable identifier is the REMOTE array's name, so -RemoteName (aliased to -Name + for compatibility) is how you select one by name. + .PARAMETER RemoteName + One or more REMOTE array names whose connections to retrieve. Aliased to -Name. Accepts + pipeline input. .PARAMETER Id - One or more array connection IDs to retrieve. + One or more array connection IDs to retrieve. Accepts pipeline input by property name. .PARAMETER Filter A server-side filter expression to narrow results (e.g., "status='connected'"). .PARAMETER Sort - Sort field and direction (e.g., "name" or "name-"). + Sort field and direction (e.g., "id" or "id-"). An array connection has no name field, so + "name" cannot sort it. The API publishes no enum of sortable fields -- use a field the + resource actually has, such as "id", "status" or "type". .PARAMETER Limit Maximum number of array connection entries to return. .PARAMETER Array @@ -24,39 +31,53 @@ function Get-PfbArrayConnection { Retrieves all array connections from the connected FlashBlade. .EXAMPLE - Get-PfbArrayConnection -Name "remote-fb-dc2" + Get-PfbArrayConnection -RemoteName "FB-B" + + Retrieves the connection to the remote array named "FB-B". + .EXAMPLE + Get-PfbArrayConnection | Where-Object type -eq 'async-replication' - Retrieves the array connection named "remote-fb-dc2". + Retrieves only the asynchronous replication connections, excluding the + system-managed fleet-management ones. .EXAMPLE - Get-PfbArrayConnection -Filter "status='connected'" -Sort "name" -Limit 10 + Get-PfbArrayConnection -Filter "status='connected'" -Sort "id" -Limit 10 - Retrieves up to 10 connected array connections sorted by name. + Retrieves up to 10 connected array connections sorted by id. #> [CmdletBinding(DefaultParameterSetName = 'List')] param( - [Parameter(ParameterSetName = 'ByName', ValueFromPipeline, ValueFromPipelineByPropertyName)] [string[]]$Name, - [Parameter(ParameterSetName = 'ById')] [string[]]$Id, + [Parameter(ParameterSetName = 'ByRemoteName', ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Alias('Name')] + [string[]]$RemoteName, + + [Parameter(ParameterSetName = 'ById', ValueFromPipelineByPropertyName)] [string[]]$Id, [Parameter()] [string]$Filter, [Parameter()] [string]$Sort, [Parameter()] [int]$Limit, [Parameter()] [PSCustomObject]$Array ) begin { Assert-PfbConnection -Array ([ref]$Array) - $allNames = [System.Collections.Generic.List[string]]::new() + $allRemoteNames = [System.Collections.Generic.List[string]]::new() $allIds = [System.Collections.Generic.List[string]]::new() } process { - if ($Name) { foreach ($n in $Name) { $allNames.Add($n) } } + if ($RemoteName) { + foreach ($n in $RemoteName) { + Assert-PfbRemoteNameNotCoerced -Value $n + $allRemoteNames.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 } + # remote_names is assigned inline rather than added to Add-PfbCommonQueryParams: it is a + # replication-family filter on 11 endpoints, not one of the ~148-endpoint common keys the + # helper centralizes, and the five other cmdlets in this module that emit it all do it + # inline too (issue #64). The comma-join is what the helper used to provide. + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Ids $allIds + if ($allRemoteNames.Count) { $queryParams['remote_names'] = $allRemoteNames -join ',' } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'array-connections' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Replication/Get-PfbArrayConnectionKey.ps1 b/Public/Replication/Get-PfbArrayConnectionKey.ps1 index 7d2f324..b7df9e1 100644 --- a/Public/Replication/Get-PfbArrayConnectionKey.ps1 +++ b/Public/Replication/Get-PfbArrayConnectionKey.ps1 @@ -46,10 +46,7 @@ function Get-PfbArrayConnectionKey { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'array-connections/connection-key' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Replication/Get-PfbArrayConnectionPath.ps1 b/Public/Replication/Get-PfbArrayConnectionPath.ps1 index 5096979..9489481 100644 --- a/Public/Replication/Get-PfbArrayConnectionPath.ps1 +++ b/Public/Replication/Get-PfbArrayConnectionPath.ps1 @@ -4,13 +4,20 @@ function Get-PfbArrayConnectionPath { Retrieves array connection path information from a FlashBlade array. .DESCRIPTION The Get-PfbArrayConnectionPath cmdlet returns network path information for array - connections on the connected Pure Storage FlashBlade. - .PARAMETER Name - One or more connection names to retrieve paths for. Accepts pipeline input. + connections on the connected Everpure FlashBlade. + + An array connection has no name of its own -- the API resource carries only an id. Its + human-readable identifier is the REMOTE array's name, so -RemoteName (aliased to -Name + for compatibility) is how you select one by name. + .PARAMETER RemoteName + One or more REMOTE array names whose connection paths to retrieve. Aliased to -Name. + Accepts pipeline input. .PARAMETER Filter A server-side filter expression to narrow results. .PARAMETER Sort - Sort field and direction (e.g., "name" or "name-"). + Sort field and direction (e.g., "id" or "id-"). An array connection path has no name + field, so "name" cannot sort it. The API publishes no enum of sortable fields -- use a + field the resource actually has, such as "id", "status" or "type". .PARAMETER Limit Maximum number of entries to return. .PARAMETER Array @@ -20,9 +27,9 @@ function Get-PfbArrayConnectionPath { Retrieves all array connection paths from the connected FlashBlade. .EXAMPLE - Get-PfbArrayConnectionPath -Name "remote-fb-dc2" + Get-PfbArrayConnectionPath -RemoteName "FB-B" - Retrieves connection paths for the specified array connection. + Retrieves connection paths for the specified remote array. .EXAMPLE Get-PfbArrayConnectionPath -Limit 10 @@ -30,25 +37,39 @@ function Get-PfbArrayConnectionPath { #> [CmdletBinding()] param( - [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] [string[]]$Name, + [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Alias('Name')] + [string[]]$RemoteName, + [Parameter()] [string]$Filter, [Parameter()] [string]$Sort, [Parameter()] [int]$Limit, [Parameter()] [PSCustomObject]$Array ) begin { Assert-PfbConnection -Array ([ref]$Array) - $allNames = [System.Collections.Generic.List[string]]::new() + $allRemoteNames = [System.Collections.Generic.List[string]]::new() } process { - if ($Name) { foreach ($n in $Name) { $allNames.Add($n) } } + if ($RemoteName) { + foreach ($n in $RemoteName) { + # This cmdlet has no -Id parameter, so a piped array-connection object falls + # through PowerShell's ByValue-with-coercion pass and gets ToString()-ed into + # -RemoteName (issue #64 follow-up). Fail here with an actionable message rather + # than sending remote_names=@{...} and letting the array reject it. + Assert-PfbRemoteNameNotCoerced -Value $n + $allRemoteNames.Add($n) + } + } } end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + # No -Names argument: GET /array-connections/path documents no names parameter in any + # spec version, and an unrecognised key is silently ignored rather than rejected, so the + # old call returned every path unfiltered (issue #64). remote_names is assigned inline + # for the reason given in Get-PfbArrayConnection.ps1. + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + if ($allRemoteNames.Count) { $queryParams['remote_names'] = $allRemoteNames -join ',' } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'array-connections/path' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Replication/Get-PfbArrayConnectionPerformanceReplication.ps1 b/Public/Replication/Get-PfbArrayConnectionPerformanceReplication.ps1 index 2b15618..8481d63 100644 --- a/Public/Replication/Get-PfbArrayConnectionPerformanceReplication.ps1 +++ b/Public/Replication/Get-PfbArrayConnectionPerformanceReplication.ps1 @@ -5,8 +5,13 @@ function Get-PfbArrayConnectionPerformanceReplication { .DESCRIPTION The Get-PfbArrayConnectionPerformanceReplication cmdlet returns replication performance metrics per array connection, including bytes sent/received and throughput. - .PARAMETER Name - One or more connection names to retrieve performance for. Accepts pipeline input. + + An array connection has no name of its own -- the API resource carries only an id. Its + human-readable identifier is the REMOTE array's name, so -RemoteName (aliased to -Name + for compatibility) is how you select one by name. + .PARAMETER RemoteName + One or more REMOTE array names whose connection performance to retrieve. Aliased to -Name. + Accepts pipeline input. .PARAMETER Filter A server-side filter expression to narrow results. .PARAMETER Sort @@ -19,6 +24,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 @@ -26,9 +34,9 @@ function Get-PfbArrayConnectionPerformanceReplication { Retrieves current replication performance for all array connections. .EXAMPLE - Get-PfbArrayConnectionPerformanceReplication -Name "remote-fb-dc2" -Resolution 86400000 + Get-PfbArrayConnectionPerformanceReplication -RemoteName "FB-B" -Resolution 86400000 - Retrieves daily replication performance for the specified connection. + Retrieves daily replication performance for the specified remote array. .EXAMPLE Get-PfbArrayConnectionPerformanceReplication -StartTime 1609459200000 -EndTime 1609545600000 @@ -36,31 +44,44 @@ function Get-PfbArrayConnectionPerformanceReplication { #> [CmdletBinding()] param( - [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] [string[]]$Name, + [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Alias('Name')] + [string[]]$RemoteName, + [Parameter()] [string]$Filter, [Parameter()] [string]$Sort, [Parameter()] [int]$Limit, [Parameter()] [long]$StartTime, [Parameter()] [long]$EndTime, [Parameter()] [long]$Resolution, + [Parameter()] + [ValidateSet('all', 'file-system', 'object-store')] + [string]$Type, [Parameter()] [PSCustomObject]$Array ) begin { Assert-PfbConnection -Array ([ref]$Array) - $allNames = [System.Collections.Generic.List[string]]::new() + $allRemoteNames = [System.Collections.Generic.List[string]]::new() } process { - if ($Name) { foreach ($n in $Name) { $allNames.Add($n) } } + if ($RemoteName) { + foreach ($n in $RemoteName) { + # No -Id parameter here either, so a piped object coerces into -RemoteName -- + # see Get-PfbArrayConnectionPath.ps1 for the binding-pass explanation. + Assert-PfbRemoteNameNotCoerced -Value $n + $allRemoteNames.Add($n) + } + } } end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + # No -Names argument, and remote_names assigned inline -- see Get-PfbArrayConnectionPath.ps1. + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters + if ($allRemoteNames.Count) { $queryParams['remote_names'] = $allRemoteNames -join ',' } 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/Public/Replication/Get-PfbBucketReplicaLink.ps1 b/Public/Replication/Get-PfbBucketReplicaLink.ps1 index 7d5705f..5ed0495 100644 --- a/Public/Replication/Get-PfbBucketReplicaLink.ps1 +++ b/Public/Replication/Get-PfbBucketReplicaLink.ps1 @@ -41,10 +41,8 @@ function Get-PfbBucketReplicaLink { ) Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($LocalBucketName) { $queryParams['local_bucket_names'] = $LocalBucketName -join ',' } if ($RemoteBucketName) { $queryParams['remote_bucket_names'] = $RemoteBucketName -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'bucket-replica-links' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Replication/Get-PfbFileSystemReplicaLink.ps1 b/Public/Replication/Get-PfbFileSystemReplicaLink.ps1 index 756e56b..a9ecb19 100644 --- a/Public/Replication/Get-PfbFileSystemReplicaLink.ps1 +++ b/Public/Replication/Get-PfbFileSystemReplicaLink.ps1 @@ -41,10 +41,8 @@ function Get-PfbFileSystemReplicaLink { ) Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($LocalFileSystemName) { $queryParams['local_file_system_names'] = $LocalFileSystemName -join ',' } if ($RemoteFileSystemName) { $queryParams['remote_file_system_names'] = $RemoteFileSystemName -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-system-replica-links' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Replication/Get-PfbFileSystemReplicaLinkPolicy.ps1 b/Public/Replication/Get-PfbFileSystemReplicaLinkPolicy.ps1 index 1784549..939c9e9 100644 --- a/Public/Replication/Get-PfbFileSystemReplicaLinkPolicy.ps1 +++ b/Public/Replication/Get-PfbFileSystemReplicaLinkPolicy.ps1 @@ -49,13 +49,11 @@ function Get-PfbFileSystemReplicaLinkPolicy { process { $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($PolicyName) { $queryParams['policy_names'] = $PolicyName -join ',' } if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-system-replica-links/policies' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Replication/Get-PfbFileSystemReplicaLinkTransfer.ps1 b/Public/Replication/Get-PfbFileSystemReplicaLinkTransfer.ps1 index 9c4dad3..9b4d043 100644 --- a/Public/Replication/Get-PfbFileSystemReplicaLinkTransfer.ps1 +++ b/Public/Replication/Get-PfbFileSystemReplicaLinkTransfer.ps1 @@ -70,12 +70,7 @@ function Get-PfbFileSystemReplicaLinkTransfer { 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 } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-system-replica-links/transfer' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Replication/Get-PfbFleet.ps1 b/Public/Replication/Get-PfbFleet.ps1 index c79b537..a42efa6 100644 --- a/Public/Replication/Get-PfbFleet.ps1 +++ b/Public/Replication/Get-PfbFleet.ps1 @@ -52,11 +52,7 @@ function Get-PfbFleet { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'fleets' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Replication/Get-PfbFleetKey.ps1 b/Public/Replication/Get-PfbFleetKey.ps1 index 183152f..9b7fa81 100644 --- a/Public/Replication/Get-PfbFleetKey.ps1 +++ b/Public/Replication/Get-PfbFleetKey.ps1 @@ -46,10 +46,7 @@ function Get-PfbFleetKey { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'fleets/fleet-key' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Replication/Get-PfbFleetMember.ps1 b/Public/Replication/Get-PfbFleetMember.ps1 index afc38a4..f455d98 100644 --- a/Public/Replication/Get-PfbFleetMember.ps1 +++ b/Public/Replication/Get-PfbFleetMember.ps1 @@ -42,11 +42,9 @@ function Get-PfbFleetMember { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters if ($FleetName) { $queryParams['fleet_names'] = $FleetName -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'fleets/members' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Replication/Get-PfbRemoteArray.ps1 b/Public/Replication/Get-PfbRemoteArray.ps1 index cb87769..9d1aea7 100644 --- a/Public/Replication/Get-PfbRemoteArray.ps1 +++ b/Public/Replication/Get-PfbRemoteArray.ps1 @@ -52,11 +52,7 @@ function Get-PfbRemoteArray { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds if ($CurrentFleetOnly) { $queryParams['current_fleet_only'] = 'true' } else { $queryParams['current_fleet_only'] = 'false' } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'remote-arrays' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Replication/Get-PfbTarget.ps1 b/Public/Replication/Get-PfbTarget.ps1 index fa52f4f..dffa180 100644 --- a/Public/Replication/Get-PfbTarget.ps1 +++ b/Public/Replication/Get-PfbTarget.ps1 @@ -52,11 +52,7 @@ function Get-PfbTarget { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'targets' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Replication/Get-PfbTargetPerformanceReplication.ps1 b/Public/Replication/Get-PfbTargetPerformanceReplication.ps1 index e555cdf..06cf262 100644 --- a/Public/Replication/Get-PfbTargetPerformanceReplication.ps1 +++ b/Public/Replication/Get-PfbTargetPerformanceReplication.ps1 @@ -55,10 +55,7 @@ function Get-PfbTargetPerformanceReplication { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames if ($StartTime) { $queryParams['start_time'] = $StartTime } if ($EndTime) { $queryParams['end_time'] = $EndTime } if ($Resolution) { $queryParams['resolution'] = $Resolution } diff --git a/Public/Replication/New-PfbArrayConnection.ps1 b/Public/Replication/New-PfbArrayConnection.ps1 index 5daacfe..0242694 100644 --- a/Public/Replication/New-PfbArrayConnection.ps1 +++ b/Public/Replication/New-PfbArrayConnection.ps1 @@ -7,14 +7,32 @@ function New-PfbArrayConnection { local Pure Storage FlashBlade and a remote array. A valid management address and connection key from the remote array are required. The connection can be created using individual parameters or a single Attributes hashtable. Supports ShouldProcess. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER ManagementAddress The management IP address or FQDN of the remote FlashBlade array. .PARAMETER ReplicationAddress The replication IP address or FQDN of the remote FlashBlade array. .PARAMETER ConnectionKey The connection key obtained from the remote FlashBlade array. + .PARAMETER CaCertificateGroup + The group of CA certificates that can be used, in addition to well-known Certificate + Authority certificates, in order to establish a secure connection to the target array. + .PARAMETER Encrypted + If this is set to $true, then all customer data replicated over the connection will be + sent over an encrypted connection using TLS, or will not be sent if a secure connection + cannot be established. + .PARAMETER Remote + The remote array. + .PARAMETER Throttle + The bandwidth throttling for the array connection, as a hashtable -- for example + @{ default_limit = 1073741824 }. .PARAMETER Attributes - A hashtable of connection attributes. When specified, individual parameters are ignored. + A hashtable of connection attributes. Mutually exclusive with the individual typed + parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -29,23 +47,59 @@ function New-PfbArrayConnection { New-PfbArrayConnection -ManagementAddress "fb-dr.example.com" -ReplicationAddress "10.0.4.100" -ConnectionKey "key-456" -WhatIf Shows what would happen if the array connection were created without actually creating it. + .EXAMPLE + New-PfbArrayConnection -ManagementAddress "remote-fb.example.com" -ConnectionKey "key-456" -Encrypted $true -Remote "remote-fb" + + Creates an encrypted array connection using typed parameters, including a reference to the remote array. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'Individual')] param( - [Parameter()] [string]$ManagementAddress, - [Parameter()] [string]$ReplicationAddress, - [Parameter()] [string]$ConnectionKey, - [Parameter()] [hashtable]$Attributes, + # Explicit Position restores the pre-#31 positional-calling convention: adding a + # ParameterSetName to any parameter disables PowerShell's default implicit positional + # binding for the WHOLE function (whole-branch review finding I-1), which would + # otherwise have silently broken `New-PfbArrayConnection $mgmt $repl $key`. + [Parameter(ParameterSetName = 'Individual', Position = 0)] [string]$ManagementAddress, + [Parameter(ParameterSetName = 'Individual', Position = 1)] [string]$ReplicationAddress, + [Parameter(ParameterSetName = 'Individual', Position = 2)] [string]$ConnectionKey, + [Parameter(ParameterSetName = 'Individual')] [string]$CaCertificateGroup, + [Parameter(ParameterSetName = 'Individual')] [Nullable[bool]]$Encrypted, + [Parameter(ParameterSetName = 'Individual')] [string]$Remote, + [Parameter(ParameterSetName = 'Individual')] [hashtable]$Throttle, + + [Parameter(ParameterSetName = 'Attributes', Mandatory, Position = 0)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) Assert-PfbConnection -Array ([ref]$Array) - if ($Attributes) { $body = $Attributes } + + if ($PSCmdlet.ParameterSetName -eq 'Attributes') { + $body = $Attributes + } else { + # Constraint 16: these three were previously bare parameters guarded by truthiness + # with no parameter set at all, which let -Attributes silently override an explicitly + # supplied -ManagementAddress/-ReplicationAddress/-ConnectionKey. They now live in the + # 'Individual' set and are guarded by ContainsKey like every other typed parameter. $body = @{} - if ($ManagementAddress) { $body['management_address'] = $ManagementAddress } - if ($ReplicationAddress) { $body['replication_addresses'] = @($ReplicationAddress) } - if ($ConnectionKey) { $body['connection_key'] = $ConnectionKey } + if ($PSBoundParameters.ContainsKey('ManagementAddress')) { $body['management_address'] = $ManagementAddress } + if ($PSBoundParameters.ContainsKey('ReplicationAddress')) { $body['replication_addresses'] = @($ReplicationAddress) } + if ($PSBoundParameters.ContainsKey('ConnectionKey')) { $body['connection_key'] = $ConnectionKey } + + # Constraint 8(a): ca_certificate_group and remote are SCALAR references (item schema + # is {id, name, resource_type}), so the parameter is [string] and the projection is + # assigned inline as a name-reference hashtable. + if ($PSBoundParameters.ContainsKey('CaCertificateGroup')) { $body['ca_certificate_group'] = @{ name = $CaCertificateGroup } } + if ($PSBoundParameters.ContainsKey('Encrypted')) { $body['encrypted'] = $Encrypted } + if ($PSBoundParameters.ContainsKey('Remote')) { $body['remote'] = @{ name = $Remote } } + + # Constraint 8(c): throttle is a COMPOSITE sub-object (_throttle: default_limit, window, + # window_limit), not a reference -- it has no `name` property, so it is passed straight + # through rather than projected into @{ name = ... }. + if ($PSBoundParameters.ContainsKey('Throttle')) { $body['throttle'] = $Throttle } } + $target = if ($ManagementAddress) { $ManagementAddress } else { 'array connection' } if ($PSCmdlet.ShouldProcess($target, 'Create array connection')) { Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'array-connections' -Body $body diff --git a/Public/Replication/New-PfbFileSystemReplicaLink.ps1 b/Public/Replication/New-PfbFileSystemReplicaLink.ps1 index 5735b9d..05c7360 100644 --- a/Public/Replication/New-PfbFileSystemReplicaLink.ps1 +++ b/Public/Replication/New-PfbFileSystemReplicaLink.ps1 @@ -23,7 +23,13 @@ function New-PfbFileSystemReplicaLink { Name of the target file system on the remote array. If omitted, the FlashBlade will name it after the local file system. .PARAMETER RemoteDefaultExports - If true, create default NFS/SMB exports on the remote file system after replication. + Controls whether default NFS/SMB exports are created on the remote file system after + replication. Tri-state: + - omitted: the array's own default is used (the FlashBlade creates default exports), + - $true: explicitly create default exports on the remote, + - $false: suppress the default exports so the remote file system has none. + Pass -RemoteDefaultExports $false to keep replica file systems export-free (previously + impossible: this was a [switch] that could only ever request 'true'). .PARAMETER Array FlashBlade connection (the source/local array). .EXAMPLE @@ -49,7 +55,7 @@ function New-PfbFileSystemReplicaLink { [string]$RemoteFileSystemName, [Parameter()] - [switch]$RemoteDefaultExports, + [Nullable[bool]]$RemoteDefaultExports, [Parameter()] [PSCustomObject]$Array @@ -62,7 +68,11 @@ function New-PfbFileSystemReplicaLink { 'remote_names' = $RemoteArrayName } if ($RemoteFileSystemName) { $queryParams['remote_file_system_names'] = $RemoteFileSystemName } - if ($RemoteDefaultExports) { $queryParams['remote_default_exports'] = 'true' } + # Send only when the caller bound it, so an omitted value defers to the array default. + # $false must reach the wire to suppress the exports, so guard on presence not truthiness. + if ($PSBoundParameters.ContainsKey('RemoteDefaultExports')) { + $queryParams['remote_default_exports'] = if ($RemoteDefaultExports) { 'true' } else { 'false' } + } # POST /file-system-replica-links requires a body even if empty $body = @{} diff --git a/Public/Replication/New-PfbFileSystemReplicaLinkPolicy.ps1 b/Public/Replication/New-PfbFileSystemReplicaLinkPolicy.ps1 index bd6e92d..86a06ca 100644 --- a/Public/Replication/New-PfbFileSystemReplicaLinkPolicy.ps1 +++ b/Public/Replication/New-PfbFileSystemReplicaLinkPolicy.ps1 @@ -5,44 +5,68 @@ function New-PfbFileSystemReplicaLinkPolicy { .DESCRIPTION Associates an existing policy with a file system replica link by specifying both the policy and replica link (member) names or IDs. + + `POST /file-system-replica-links/policies` accepts `member_ids` but has no + `member_names` query parameter at all -- it identifies the local side of the link by + name via `local_file_system_names` instead. The cmdlet previously sent `-MemberName` as + `member_names`, which this endpoint silently ignores, so name-based member selection + could never have worked. -LocalFileSystemName is the corrected parameter; -MemberName + is kept as a backward-compatible alias of it. .PARAMETER PolicyName The name of the policy to attach. .PARAMETER PolicyId The ID of the policy to attach. - .PARAMETER MemberName - The name of the replica link to attach the policy to. + .PARAMETER LocalFileSystemName + The name of the local file system side of the replica link to attach the policy to. + Sent as the `local_file_system_names` query parameter. Also accepts the alias + -MemberName. + .PARAMETER LocalFileSystemId + The ID of the local file system side of the replica link to attach the policy to. .PARAMETER MemberId - The ID of the replica link to attach the policy to. + The ID of the replica link member to attach the policy to. + .PARAMETER RemoteId + The ID of the remote array associated with the replica link. + .PARAMETER RemoteName + The name of the remote array associated with the replica link. .PARAMETER Array The FlashBlade connection object. If not specified, uses the default connection. .EXAMPLE - New-PfbFileSystemReplicaLinkPolicy -PolicyName "repl-daily" -MemberName "fs01" - Attaches the 'repl-daily' policy to the replica link for 'fs01'. + New-PfbFileSystemReplicaLinkPolicy -PolicyName "repl-daily" -LocalFileSystemName "fs01" + Attaches the 'repl-daily' policy to the replica link for local file system 'fs01'. .EXAMPLE New-PfbFileSystemReplicaLinkPolicy -PolicyId "abc-123" -MemberId "def-456" Attaches a policy to a replica link using IDs. .EXAMPLE - New-PfbFileSystemReplicaLinkPolicy -PolicyName "repl-hourly" -MemberName "fs02" -Confirm:$false + New-PfbFileSystemReplicaLinkPolicy -PolicyName "repl-hourly" -LocalFileSystemName "fs02" -Confirm:$false Attaches the policy without prompting for confirmation. + .EXAMPLE + New-PfbFileSystemReplicaLinkPolicy -PolicyName "repl-daily" -LocalFileSystemName "fs01" -RemoteName "remote-fb" + Attaches the policy, scoping the request to the replica link whose remote array is "remote-fb". #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param( [Parameter()] [string]$PolicyName, [Parameter()] [string]$PolicyId, - [Parameter()] [string]$MemberName, + [Parameter()] [Alias('MemberName')] [string]$LocalFileSystemName, [Parameter()] [string]$MemberId, + [Parameter()] [string]$LocalFileSystemId, + [Parameter()] [string]$RemoteId, + [Parameter()] [string]$RemoteName, [Parameter()] [PSCustomObject]$Array ) Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} - if ($PolicyName) { $queryParams['policy_names'] = $PolicyName } - if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId } - if ($MemberName) { $queryParams['member_names'] = $MemberName } - if ($MemberId) { $queryParams['member_ids'] = $MemberId } + if ($PSBoundParameters.ContainsKey('PolicyName')) { $queryParams['policy_names'] = $PolicyName } + if ($PSBoundParameters.ContainsKey('PolicyId')) { $queryParams['policy_ids'] = $PolicyId } + if ($PSBoundParameters.ContainsKey('LocalFileSystemName')) { $queryParams['local_file_system_names'] = $LocalFileSystemName } + if ($PSBoundParameters.ContainsKey('LocalFileSystemId')) { $queryParams['local_file_system_ids'] = $LocalFileSystemId } + if ($PSBoundParameters.ContainsKey('MemberId')) { $queryParams['member_ids'] = $MemberId } + if ($PSBoundParameters.ContainsKey('RemoteId')) { $queryParams['remote_ids'] = $RemoteId } + if ($PSBoundParameters.ContainsKey('RemoteName')) { $queryParams['remote_names'] = $RemoteName } - $target = if ($MemberName) { $MemberName } else { $MemberId } + $target = if ($LocalFileSystemName) { $LocalFileSystemName } elseif ($LocalFileSystemId) { $LocalFileSystemId } else { $MemberId } $policy = if ($PolicyName) { $PolicyName } else { $PolicyId } if ($PSCmdlet.ShouldProcess("$target", "Attach policy '$policy' to replica link")) { diff --git a/Public/Replication/New-PfbFleetMember.ps1 b/Public/Replication/New-PfbFleetMember.ps1 index d27ec3c..8605e28 100644 --- a/Public/Replication/New-PfbFleetMember.ps1 +++ b/Public/Replication/New-PfbFleetMember.ps1 @@ -4,42 +4,68 @@ function New-PfbFleetMember { Adds a member to a fleet on a FlashBlade array. .DESCRIPTION The New-PfbFleetMember cmdlet adds an array as a member of a fleet on the connected - Pure Storage FlashBlade. Both the fleet name and the member name must be specified. + Pure Storage FlashBlade. Per the FlashBlade REST API spec, `POST /fleets/members` must + be called from the array that is joining the fleet, and it identifies the fleet via a + `fleet_ids`/`fleet_names` query parameter plus a request body carrying the fleet key + (generated on any array already in the fleet) and a reference to the joining array + itself. + + CONFIRMED WIRE-CONTRACT BUG (issue #38): this cmdlet previously sent `fleet_names` and + `member_names` as bare query parameters with no request body at all. `member_names` is + not a valid query parameter for this endpoint (only `fleet_ids`/`fleet_names` are, per + the OpenAPI spec's parameter list for POST /fleets/members) and there was no way to + supply the required fleet key or self-identification, so this cmdlet could never have + succeeded against a real array. -MemberName has been removed; -Members now exposes the + actual request body so a caller can supply the correct shape, e.g.: + `-Members @{ key = $fleetKey; member = @{ id = $thisArrayId } }`. + + The fix is verified against the OpenAPI spec (FleetMemberPost schema and the POST + operation's parameter list) but has NOT been live-tested against a real fleet -- that + happens in a later task against a lab array. See docs in issue #38's + issue38-fleetmember-bug-comment.md for the full context. .PARAMETER FleetName - The fleet name to add the member to. - .PARAMETER MemberName - The member array name to add to the fleet. + The fleet name to add the member to. Sent as the `fleet_names` query parameter. + .PARAMETER FleetId + The fleet ID to add the member to. Sent as the `fleet_ids` query parameter. + .PARAMETER Members + Info about the members being added to the fleet, as a hashtable or array of hashtables + -- for example @{ key = ""; member = @{ id = "" } }. + The `key` is the fleet key generated on any array already in the fleet; `member` is a + reference to the array joining the fleet. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - New-PfbFleetMember -FleetName "fleet-prod" -MemberName "array-dc2" + New-PfbFleetMember -FleetName "fleet-prod" -Members @{ key = "1fc6297a-5183-4b7a-8d58-0182af1a2b64"; member = @{ id = "10314f42-020d-7080-8013-000ddt400012" } } - Adds "array-dc2" as a member of "fleet-prod". + Adds this array to "fleet-prod" using the fleet key generated by an existing fleet member. .EXAMPLE - New-PfbFleetMember -FleetName "fleet-dr" -MemberName "array-dc3" -WhatIf + New-PfbFleetMember -FleetId "10314f42-020d-7080-8013-000ddt400099" -Members @{ key = "key-456"; member = @{ id = "this-array-id" } } -WhatIf - Shows what would happen without actually adding the member. - .EXAMPLE - New-PfbFleetMember -FleetName "fleet-prod" -MemberName "array-dc4" -Confirm:$false - - Adds the member without prompting for confirmation. + Shows what would happen without actually adding the member, identifying the fleet by ID. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] param( - [Parameter(Mandatory)] [string]$FleetName, - [Parameter(Mandatory)] [string]$MemberName, + [Parameter()] [string]$FleetName, + [Parameter()] [string]$FleetId, + [Parameter()] [hashtable[]]$Members, [Parameter()] [PSCustomObject]$Array ) Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} - $queryParams['fleet_names'] = $FleetName - $queryParams['member_names'] = $MemberName + if ($PSBoundParameters.ContainsKey('FleetName')) { $queryParams['fleet_names'] = $FleetName } + if ($PSBoundParameters.ContainsKey('FleetId')) { $queryParams['fleet_ids'] = $FleetId } + + # Constraint 8(c): each members[] item is {key, member}, where `key` is a plain string + # outside {id, name, resource_type} -- this makes the array COMPOSITE, not an array of + # references, so it is passed straight through rather than projected into @{ name = ... }. + $body = @{} + if ($PSBoundParameters.ContainsKey('Members')) { $body['members'] = @($Members) } - $target = "${FleetName}:${MemberName}" + $target = if ($FleetName) { $FleetName } elseif ($FleetId) { $FleetId } else { 'fleet member' } if ($PSCmdlet.ShouldProcess($target, 'Add fleet member')) { - Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'fleets/members' -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'fleets/members' -Body $body -QueryParams $queryParams } } diff --git a/Public/Replication/Remove-PfbArrayConnection.ps1 b/Public/Replication/Remove-PfbArrayConnection.ps1 index 107676b..e7c299d 100644 --- a/Public/Replication/Remove-PfbArrayConnection.ps1 +++ b/Public/Replication/Remove-PfbArrayConnection.ps1 @@ -4,32 +4,49 @@ function Remove-PfbArrayConnection { Removes an array connection from a FlashBlade array. .DESCRIPTION The Remove-PfbArrayConnection cmdlet deletes a replication connection from the connected - Pure Storage FlashBlade. The target connection can be identified by name or ID. This - cmdlet has a high confirm impact and will prompt for confirmation by default. Removing - an array connection will prevent any associated replication from continuing. - .PARAMETER Name - The name of the array connection to remove. Accepts pipeline input. + Everpure FlashBlade. This cmdlet has a high confirm impact and will prompt for + confirmation by default. Removing an array connection will prevent any associated + replication from continuing. + + An array connection has no name of its own -- the API resource carries only an id. Its + human-readable identifier is the REMOTE array's name, so -RemoteName (aliased to -Name + for compatibility) is how you select one by name. + .PARAMETER RemoteName + The name of the REMOTE array whose connection to remove. Aliased to -Name. Accepts + pipeline input. .PARAMETER Id - The ID of the array connection to remove. + The ID of the array connection to remove. Accepts pipeline input by property name. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Remove-PfbArrayConnection -Name "remote-fb-old" + Remove-PfbArrayConnection -RemoteName "FB-B" + + Removes the connection to the remote array named "FB-B" after prompting for confirmation. + .EXAMPLE + Remove-PfbArrayConnection -RemoteName "FB-B" -Confirm:$false - Removes the array connection named "remote-fb-old" after prompting for confirmation. + Removes the connection without prompting. .EXAMPLE - Remove-PfbArrayConnection -Name "remote-fb-test" -Confirm:$false + Get-PfbArrayConnection | Where-Object status -eq 'disconnected' | Remove-PfbArrayConnection - Removes the array connection named "remote-fb-test" without prompting. + Removes all disconnected array connections, binding each one by its id through the + pipeline. .EXAMPLE - Get-PfbArrayConnection -Filter "status='disconnected'" | Remove-PfbArrayConnection + Get-PfbArrayConnection | + Where-Object { $_.remote.id -eq '10314f42-020d-7080-8013-000133810cd0' } | + Remove-PfbArrayConnection - Removes all disconnected array connections via pipeline input. + Selects a connection by the remote array's id. This cmdlet has no -RemoteId parameter, + and on the write endpoints remote_ids only narrows an already-scoped request, so filter + client-side and let the connection's own id bind through the pipeline. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, + [Parameter(ParameterSetName = 'ByRemoteName', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Alias('Name')] + [string]$RemoteName, + + [Parameter(ParameterSetName = 'ById', Mandatory, ValueFromPipelineByPropertyName)] [string]$Id, [Parameter()] [PSCustomObject]$Array ) begin { @@ -37,9 +54,10 @@ function Remove-PfbArrayConnection { } process { - $target = if ($Name) { $Name } else { $Id } + if ($RemoteName) { Assert-PfbRemoteNameNotCoerced -Value $RemoteName } + $target = if ($RemoteName) { $RemoteName } else { $Id } $queryParams = @{} - if ($Name) { $queryParams['names'] = $Name } + if ($RemoteName) { $queryParams['remote_names'] = $RemoteName } if ($Id) { $queryParams['ids'] = $Id } if ($PSCmdlet.ShouldProcess($target, 'Remove array connection')) { Invoke-PfbApiRequest -Array $Array -Method DELETE -Endpoint 'array-connections' -QueryParams $queryParams diff --git a/Public/Replication/Update-PfbArrayConnection.ps1 b/Public/Replication/Update-PfbArrayConnection.ps1 index b488848..4c1a04f 100644 --- a/Public/Replication/Update-PfbArrayConnection.ps1 +++ b/Public/Replication/Update-PfbArrayConnection.ps1 @@ -4,35 +4,125 @@ function Update-PfbArrayConnection { Updates an existing array connection on a FlashBlade array. .DESCRIPTION The Update-PfbArrayConnection cmdlet modifies attributes of an existing replication - connection on the connected Pure Storage FlashBlade. The target connection can be - identified by name or ID. Common updates include changing replication addresses and - connection throttling settings. Supports pipeline input and ShouldProcess. - .PARAMETER Name - The name of the array connection to update. Accepts pipeline input by property name. + connection on the connected Everpure FlashBlade. Common updates include changing + replication addresses and connection throttling settings. Supports pipeline input and + ShouldProcess. + + An array connection has no name of its own -- the API resource carries only an id. Its + human-readable identifier is the REMOTE array's name, so -RemoteName (aliased to -Name + for compatibility) is how you select one by name. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. + .PARAMETER RemoteName + The name of the REMOTE array whose connection to update. Aliased to -Name. Accepts + pipeline input by property name. .PARAMETER Id - The ID of the array connection to update. + The ID of the array connection to update. Accepts pipeline input by property name, so + connection objects from Get-PfbArrayConnection can be piped in directly. + .PARAMETER ManagementAddress + Management address of the target array. + .PARAMETER ReplicationAddresses + IP addresses and/or FQDNs of the target arrays. + .PARAMETER CaCertificateGroup + The group of CA certificates that can be used, in addition to well-known Certificate + Authority certificates, in order to establish a secure connection to the target array. + .PARAMETER Encrypted + If this is set to $true, then all customer data replicated over the connection will be + sent over an encrypted connection using TLS, or will not be sent if a secure connection + cannot be established. + .PARAMETER Remote + The remote array. + .PARAMETER Throttle + The bandwidth throttling for the array connection, as a hashtable -- for example + @{ default_limit = 1073741824 }. + .PARAMETER RemoteId + Narrows the operation to the array connection with the specified remote array ID. This + is an additional filter, not a selector: it cannot be used on its own, so combine it + with -RemoteName or -Id. To select purely by remote array ID, filter client-side -- see + the last example. .PARAMETER Attributes - A hashtable of array connection attributes to modify. + A hashtable of array connection attributes to modify. Mutually exclusive with the + individual typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE - Update-PfbArrayConnection -Name "remote-fb-dc2" -Attributes @{ management_address = "10.0.2.101" } + Update-PfbArrayConnection -RemoteName "FB-B" -Attributes @{ management_address = "10.0.2.101" } + + Updates the management address for the connection to the remote array named "FB-B". + .EXAMPLE + Update-PfbArrayConnection -Id "10314f42-020d-7080-8013-000ddt400077" -ReplicationAddresses @("10.0.3.101") + + Updates the replication address for the array connection identified by the specified ID. + .EXAMPLE + Update-PfbArrayConnection -RemoteName "FB-B" -ManagementAddress "10.0.2.101" -Encrypted $true - Updates the management address for the array connection named "remote-fb-dc2". + Updates the management address and encryption setting using typed parameters. .EXAMPLE - Update-PfbArrayConnection -Name "remote-fb-dr" -Attributes @{ replication_addresses = @("10.0.3.101") } + Get-PfbArrayConnection | Where-Object type -eq 'async-replication' | + Update-PfbArrayConnection -Throttle @{ default_limit = 1073741824 } - Updates the replication address for the "remote-fb-dr" array connection. + Throttles every asynchronous replication connection. The type filter is required, not + optional: fleet-management connections are managed by the system and reject writes. .EXAMPLE - Update-PfbArrayConnection -Id "10314f42-020d-7080-8013-000ddt400077" -Attributes @{ status = "connected" } + Get-PfbArrayConnection | + Where-Object { $_.remote.id -eq '10314f42-020d-7080-8013-000133810cd0' } | + Update-PfbArrayConnection -Throttle @{ default_limit = 1073741824 } - Updates the status of the array connection identified by the specified ID. + Selects a connection by the remote array's id. -RemoteId narrows a request that is + already scoped by -RemoteName or -Id; it cannot select on its own, so filter + client-side and let the connection's own id bind through the pipeline. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByRemoteNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByRemoteNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByRemoteNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [Alias('Name')] + [string]$RemoteName, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByRemoteNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$ManagementAddress, + + [Parameter(ParameterSetName = 'ByRemoteNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string[]]$ReplicationAddresses, + + [Parameter(ParameterSetName = 'ByRemoteNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$CaCertificateGroup, + + [Parameter(ParameterSetName = 'ByRemoteNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [Nullable[bool]]$Encrypted, + + [Parameter(ParameterSetName = 'ByRemoteNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Remote, + + [Parameter(ParameterSetName = 'ByRemoteNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [hashtable]$Throttle, + + [Parameter(ParameterSetName = 'ByRemoteNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + + # Constraint 17: remote_ids is an orthogonal query FILTER, not a body field and not a + # selector, so it is declared bare rather than added to the Individual parameter sets -- + # it must stay usable alongside -Attributes. remote_names used to live here too, but + # issue #64 promoted it into the by-name parameter sets: an array connection has no name + # of its own, so the remote array's name IS the selector. remote_ids remains a filter + # that narrows an already-selected request; see the .PARAMETER RemoteId help. + [Parameter()] [string]$RemoteId, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -41,11 +131,34 @@ function Update-PfbArrayConnection { process { $queryParams = @{} - if ($Name) { $queryParams['names'] = $Name } + if ($RemoteName) { $queryParams['remote_names'] = $RemoteName } if ($Id) { $queryParams['ids'] = $Id } - $target = if ($Name) { $Name } else { $Id } + if ($PSBoundParameters.ContainsKey('RemoteId')) { $queryParams['remote_ids'] = $RemoteId } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('ManagementAddress')) { $body['management_address'] = $ManagementAddress } + if ($PSBoundParameters.ContainsKey('ReplicationAddresses')) { $body['replication_addresses'] = @($ReplicationAddresses) } + + # Constraint 8(a): ca_certificate_group and remote are SCALAR references (item + # schema is {id, name, resource_type}), so the parameter is [string] and the + # projection is assigned inline as a name-reference hashtable. + if ($PSBoundParameters.ContainsKey('CaCertificateGroup')) { $body['ca_certificate_group'] = @{ name = $CaCertificateGroup } } + if ($PSBoundParameters.ContainsKey('Encrypted')) { $body['encrypted'] = $Encrypted } + if ($PSBoundParameters.ContainsKey('Remote')) { $body['remote'] = @{ name = $Remote } } + + # Constraint 8(c): throttle is a COMPOSITE sub-object (_throttle: default_limit, + # window, window_limit), not a reference -- it has no `name` property, so it is + # passed straight through rather than projected into @{ name = ... }. + if ($PSBoundParameters.ContainsKey('Throttle')) { $body['throttle'] = $Throttle } + } + + $target = if ($RemoteName) { $RemoteName } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update array connection')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'array-connections' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'array-connections' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Replication/Update-PfbFleet.ps1 b/Public/Replication/Update-PfbFleet.ps1 index 441f82e..3770f99 100644 --- a/Public/Replication/Update-PfbFleet.ps1 +++ b/Public/Replication/Update-PfbFleet.ps1 @@ -6,12 +6,21 @@ function Update-PfbFleet { The Update-PfbFleet cmdlet modifies attributes of an existing fleet on the connected Pure Storage FlashBlade. The fleet can be identified by name or ID. Supports pipeline input and ShouldProcess for confirmation prompts. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the fleet to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the fleet to update. + .PARAMETER NewName + A new user-specified name for the fleet. Named -NewName rather than -Name because + -Name already identifies which fleet to update. .PARAMETER Attributes - A hashtable of fleet attributes to modify. + A hashtable of fleet attributes to modify. Mutually exclusive with the individual + typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -26,12 +35,30 @@ function Update-PfbFleet { Update-PfbFleet -Name "fleet-prod" -Attributes @{} -WhatIf Shows what would happen without actually updating the fleet. + .EXAMPLE + Update-PfbFleet -Name "fleet-prod" -NewName "fleet-renamed" + + Renames the fleet named "fleet-prod" using the typed parameter. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$NewName, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -42,9 +69,18 @@ function Update-PfbFleet { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('NewName')) { $body['name'] = $NewName } + } + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update fleet')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'fleets' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'fleets' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Replication/Update-PfbTarget.ps1 b/Public/Replication/Update-PfbTarget.ps1 index fc9dd75..217a8c6 100644 --- a/Public/Replication/Update-PfbTarget.ps1 +++ b/Public/Replication/Update-PfbTarget.ps1 @@ -7,12 +7,26 @@ function Update-PfbTarget { connected Pure Storage FlashBlade. The target can be identified by name or ID. Common updates include changing the address, credentials, or connection settings. Supports pipeline input and ShouldProcess for confirmation prompts. + + The individual typed parameters and the raw -Attributes hashtable are mutually + exclusive: they live in separate parameter sets, so PowerShell rejects a mixed + invocation at bind time rather than letting -Attributes silently override an + explicitly supplied value. .PARAMETER Name The name of the replication target to update. Accepts pipeline input by property name. .PARAMETER Id The ID of the replication target to update. + .PARAMETER Address + IP address or FQDN of the target system. + .PARAMETER CaCertificateGroup + The group of CA certificates that can be used, in addition to well-known Certificate + Authority certificates, in order to establish a secure connection to the target system. + .PARAMETER NewName + A user-specified name for the target. Named -NewName rather than -Name because -Name + already identifies which target to update. .PARAMETER Attributes - A hashtable of target attributes to modify. + A hashtable of target attributes to modify. Mutually exclusive with the individual + typed parameters above. .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -27,12 +41,38 @@ function Update-PfbTarget { Update-PfbTarget -Id "10314f42-020d-7080-8013-000ddt400099" -Attributes @{ enabled = $true } Enables the replication target identified by the specified ID. + .EXAMPLE + Update-PfbTarget -Name "s3-target-aws" -Address "s3.us-east-1.amazonaws.com" -NewName "s3-target-renamed" + + Updates the address and renames the replication target using typed parameters. #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', + DefaultParameterSetName = 'ByNameIndividual')] param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter(Mandatory)] [hashtable]$Attributes, + [Parameter(ParameterSetName = 'ByNameIndividual', Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory, ValueFromPipelineByPropertyName)] + [string]$Name, + + [Parameter(ParameterSetName = 'ByIdIndividual', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [string]$Id, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$Address, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$CaCertificateGroup, + + [Parameter(ParameterSetName = 'ByNameIndividual')] + [Parameter(ParameterSetName = 'ByIdIndividual')] + [string]$NewName, + + [Parameter(ParameterSetName = 'ByNameAttributes', Mandatory)] + [Parameter(ParameterSetName = 'ByIdAttributes', Mandatory)] + [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array ) begin { @@ -43,9 +83,24 @@ function Update-PfbTarget { $queryParams = @{} if ($Name) { $queryParams['names'] = $Name } if ($Id) { $queryParams['ids'] = $Id } + + if ($PSCmdlet.ParameterSetName -like '*Attributes') { + $body = $Attributes + } + else { + $body = @{} + if ($PSBoundParameters.ContainsKey('Address')) { $body['address'] = $Address } + + # Constraint 8(a): ca_certificate_group is a SCALAR reference (item schema is + # {id, name, resource_type}), so the parameter is [string] and the projection is + # assigned inline as a name-reference hashtable. + if ($PSBoundParameters.ContainsKey('CaCertificateGroup')) { $body['ca_certificate_group'] = @{ name = $CaCertificateGroup } } + if ($PSBoundParameters.ContainsKey('NewName')) { $body['name'] = $NewName } + } + $target = if ($Name) { $Name } else { $Id } if ($PSCmdlet.ShouldProcess($target, 'Update target')) { - Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'targets' -Body $Attributes -QueryParams $queryParams + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'targets' -Body $body -QueryParams $queryParams } } } diff --git a/Public/Server/Get-PfbServer.ps1 b/Public/Server/Get-PfbServer.ps1 index 2c8c46a..c6a1c06 100644 --- a/Public/Server/Get-PfbServer.ps1 +++ b/Public/Server/Get-PfbServer.ps1 @@ -73,12 +73,7 @@ function Get-PfbServer { 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 } - if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'servers' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Support/Get-PfbAudit.ps1 b/Public/Support/Get-PfbAudit.ps1 index b797eba..dd79404 100644 --- a/Public/Support/Get-PfbAudit.ps1 +++ b/Public/Support/Get-PfbAudit.ps1 @@ -39,11 +39,7 @@ function Get-PfbAudit { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'audits' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Support/Get-PfbSupport.ps1 b/Public/Support/Get-PfbSupport.ps1 index 18f8894..8ab4202 100644 --- a/Public/Support/Get-PfbSupport.ps1 +++ b/Public/Support/Get-PfbSupport.ps1 @@ -57,10 +57,7 @@ function Get-PfbSupport { end { $queryParams = @{} - if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'support' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Support/Get-PfbSupportDiagnostics.ps1 b/Public/Support/Get-PfbSupportDiagnostics.ps1 index 1ecbb1e..e0b9437 100644 --- a/Public/Support/Get-PfbSupportDiagnostics.ps1 +++ b/Public/Support/Get-PfbSupportDiagnostics.ps1 @@ -51,11 +51,7 @@ function Get-PfbSupportDiagnostics { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'support-diagnostics' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Support/Get-PfbSupportDiagnosticsDetails.ps1 b/Public/Support/Get-PfbSupportDiagnosticsDetails.ps1 index d68d49a..b5debcf 100644 --- a/Public/Support/Get-PfbSupportDiagnosticsDetails.ps1 +++ b/Public/Support/Get-PfbSupportDiagnosticsDetails.ps1 @@ -51,11 +51,7 @@ function Get-PfbSupportDiagnosticsDetails { 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 } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'support-diagnostics/details' -QueryParams $queryParams -AutoPaginate } } diff --git a/Public/Support/Get-PfbSupportVerificationKey.ps1 b/Public/Support/Get-PfbSupportVerificationKey.ps1 index 94184d5..7bcfeae 100644 --- a/Public/Support/Get-PfbSupportVerificationKey.ps1 +++ b/Public/Support/Get-PfbSupportVerificationKey.ps1 @@ -34,8 +34,6 @@ function Get-PfbSupportVerificationKey { ) Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit -gt 0) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'support/verification-keys' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Support/Test-PfbSupport.ps1 b/Public/Support/Test-PfbSupport.ps1 index 9cfeb9e..6fb7c3a 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/Public/Workloads/Get-PfbWorkload.ps1 b/Public/Workloads/Get-PfbWorkload.ps1 index b45fd1f..f6a42df 100644 --- a/Public/Workloads/Get-PfbWorkload.ps1 +++ b/Public/Workloads/Get-PfbWorkload.ps1 @@ -46,11 +46,7 @@ function Get-PfbWorkload { process { $queryParams = @{} - if ($Name) { $queryParams['names'] = $Name -join ',' } - if ($Id) { $queryParams['ids'] = $Id -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $Name -Ids $Id if ($Destroyed) { $queryParams['destroyed'] = 'true' } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'workloads' -QueryParams $queryParams -AutoPaginate diff --git a/Public/Workloads/Get-PfbWorkloadPlacementRecommendation.ps1 b/Public/Workloads/Get-PfbWorkloadPlacementRecommendation.ps1 index 7369b01..7da4b76 100644 --- a/Public/Workloads/Get-PfbWorkloadPlacementRecommendation.ps1 +++ b/Public/Workloads/Get-PfbWorkloadPlacementRecommendation.ps1 @@ -37,11 +37,7 @@ function Get-PfbWorkloadPlacementRecommendation { Assert-PfbConnection -Array ([ref]$Array) $queryParams = @{} - if ($Name) { $queryParams['names'] = $Name -join ',' } - if ($Id) { $queryParams['ids'] = $Id -join ',' } - if ($Filter) { $queryParams['filter'] = $Filter } - if ($Sort) { $queryParams['sort'] = $Sort } - if ($Limit) { $queryParams['limit'] = $Limit } + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $Name -Ids $Id Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'workloads/placement-recommendations' -QueryParams $queryParams -AutoPaginate } diff --git a/Public/Workloads/Set-PfbWorkloadTag.ps1 b/Public/Workloads/Set-PfbWorkloadTag.ps1 index 45aa459..0fa2d4b 100644 --- a/Public/Workloads/Set-PfbWorkloadTag.ps1 +++ b/Public/Workloads/Set-PfbWorkloadTag.ps1 @@ -40,22 +40,13 @@ function Set-PfbWorkloadTag { if ($ResourceName) { $queryParams['resource_names'] = $ResourceName -join ',' } if ($ResourceId) { $queryParams['resource_ids'] = $ResourceId -join ',' } - # The batch endpoint takes a raw array of tag objects as the body — Invoke-PfbApiRequest - # expects a hashtable, so wrap the array in a dummy key and unwrap below via $Raw. - # The FB tag-batch endpoint actually receives the array directly. - $jsonBody = $Tags | ConvertTo-Json -Depth 5 - if ($PSCmdlet.ShouldProcess(($ResourceName + $ResourceId -join ', '), "Apply $($Tags.Count) tag(s)")) { - # Direct REST call bypassing the hashtable-only -Body parameter on Invoke-PfbApiRequest. - $apiVer = $Array.ApiVersion - $qs = ConvertTo-PfbQueryString -Parameters $queryParams - $uri = "https://$($Array.Endpoint)/api/${apiVer}/workloads/tags/batch${qs}" - $headers = @{ 'Content-Type' = 'application/json'; 'x-auth-token' = $Array.AuthToken } - $restParams = @{ Method = 'PUT'; Uri = $uri; Headers = $headers; Body = $jsonBody } - if ($Array.SkipCertificateCheck -and $PSVersionTable.PSVersion.Major -ge 6) { - $restParams['SkipCertificateCheck'] = $true - } - Write-Verbose "FlashBlade API: PUT $uri" - Invoke-RestMethod @restParams + # $Tags goes over as the body unmodified: this endpoint's body IS the tag array, not + # an object wrapping one. Serialisation happens in the shared path, which uses + # ConvertTo-Json -InputObject -- so the one-element array stays an array. That is the + # same guarantee the local ConvertTo-Json deleted here used to provide; the + # single-tag test asserting it is unchanged and still passes through this path. + Invoke-PfbApiRequest -Array $Array -Method PUT -Endpoint 'workloads/tags/batch' ` + -Body $Tags -QueryParams $queryParams } } diff --git a/PureStorageFlashBladePowerShell.psd1 b/PureStorageFlashBladePowerShell.psd1 index d42386e..19f40ed 100644 --- a/PureStorageFlashBladePowerShell.psd1 +++ b/PureStorageFlashBladePowerShell.psd1 @@ -1,6 +1,6 @@ @{ RootModule = 'PureStorageFlashBladePowerShell.psm1' - ModuleVersion = '2.1.2' + ModuleVersion = '2.3.0' GUID = 'b25473b3-9eb7-414d-8da1-264e10f73d86' Author = 'Pure Storage, Inc.' CompanyName = 'Pure Storage, Inc.' @@ -535,7 +535,27 @@ 'Remove-PfbLocalGroup', 'Get-PfbLocalGroupMember', 'New-PfbLocalGroupMember', - 'Remove-PfbLocalGroupMember' + 'Remove-PfbLocalGroupMember', + # User/group quota policies + 'Get-PfbUserGroupQuotaPolicy', + 'New-PfbUserGroupQuotaPolicy', + 'Update-PfbUserGroupQuotaPolicy', + 'Remove-PfbUserGroupQuotaPolicy', + 'Get-PfbUserGroupQuotaPolicyRule', + 'New-PfbUserGroupQuotaPolicyRule', + 'Update-PfbUserGroupQuotaPolicyRule', + 'Remove-PfbUserGroupQuotaPolicyRule', + 'Get-PfbUserGroupQuotaPolicyFileSystem', + 'New-PfbUserGroupQuotaPolicyFileSystem', + 'Remove-PfbUserGroupQuotaPolicyFileSystem', + 'Get-PfbUserGroupQuotaPolicyMember', + 'Get-PfbFileSystemUserGroupQuotaPolicy', + 'New-PfbFileSystemUserGroupQuotaPolicy', + 'Remove-PfbFileSystemUserGroupQuotaPolicy', + 'Get-PfbFileSystemUserQuota', + 'Get-PfbFileSystemGroupQuota', + 'Get-PfbFileSystemUser', + 'Get-PfbFileSystemGroup' ) CmdletsToExport = @() VariablesToExport = @() @@ -548,6 +568,30 @@ 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.3.0 - Typed write-cmdlet parameters + field-migration correctness fixes. + 56 New-*/Update-* write cmdlets gain typed, validated parameters for documented body/ + query fields previously reachable only via a raw -Attributes hashtable (-Attributes + retained, mutually exclusive via parameter sets). Adds user/group quota-policy cmdlets. + Fixes: array-connection cmdlets now filter on remote_names (the real wire key) instead + of a dead 'names' key that returned the full set and could fan a PATCH/DELETE out to + every connection; New-PfbFileSystemReplicaLink -RemoteDefaultExports $false can now + suppress remote default exports; -Limit honored under AutoPaginate; API errors now + include the HTTP status. Breaking: Update-PfbArrayConnection -Name is now an alias of + -RemoteName (both together is a bind error); New-PfbFileSystemReplicaLink + -RemoteDefaultExports is now [Nullable[bool]] and the bare form needs a value; + Update-PfbAsyncLog drops -Name/-Id (the endpoint has no selector). + +v2.2.0 - API version-awareness + feature-gap parameters. + Adds a generated capability map of every FlashBlade REST endpoint/parameter/field to + the API version that introduced it, and a fail-fast runtime check that rejects a call + (before any HTTP request) when the connected array's version cannot support it. The + check fails safe: a missing map / unmatched endpoint / unresolvable version is a no-op. + Also exposes seven documented query-parameter enums that cmdlets never surfaced + (Get-PfbArrayConnectionPerformanceReplication/Get-PfbArrayPerformanceReplication -Type, + Test-PfbSupport -TestType, Invoke-PfbNetworkTrace -Method, Get-PfbFileSystemSession + -Protocol, Get-PfbPolicyAllMember -MemberType), and fixes Get-/Remove-PfbFileSystemSession + (removed a non-working -Id, added a -Protocol bulk-terminate mode, corrected -Name help). + 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 diff --git a/PureStorageFlashBladePowerShell.psm1 b/PureStorageFlashBladePowerShell.psm1 index c9dbf0c..0ab0eed 100644 --- a/PureStorageFlashBladePowerShell.psm1 +++ b/PureStorageFlashBladePowerShell.psm1 @@ -4,6 +4,11 @@ $script:PfbDefaultArray = $null $script:PfbArrays = @{} +# Module-scoped API-capability state (Data/PfbCapabilityMap.json, Data/PfbVersionMap.json) +$script:PfbModuleRoot = $PSScriptRoot +$script:PfbCapabilityMap = $null +$script:PfbVersionMap = $null + # Dot-source all private functions $privatePath = Join-Path $PSScriptRoot 'Private' if (Test-Path $privatePath) { diff --git a/Reports/PfbApiDriftReport.json b/Reports/PfbApiDriftReport.json new file mode 100644 index 0000000..cf99091 --- /dev/null +++ b/Reports/PfbApiDriftReport.json @@ -0,0 +1,21112 @@ +{ + "schemaVersion": 1, + "analysedVersions": [ + "2.0", + "2.1", + "2.2", + "2.3", + "2.4", + "2.5", + "2.6", + "2.7", + "2.8", + "2.9", + "2.10", + "2.11", + "2.12", + "2.13", + "2.14", + "2.15", + "2.16", + "2.17", + "2.18", + "2.19", + "2.20", + "2.21", + "2.22", + "2.23", + "2.24", + "2.25", + "2.26", + "2.27", + "2.28" + ], + "availableSpecVersions": [ + "2.0", + "2.1", + "2.2", + "2.3", + "2.4", + "2.5", + "2.6", + "2.7", + "2.8", + "2.9", + "2.10", + "2.11", + "2.12", + "2.13", + "2.14", + "2.15", + "2.16", + "2.17", + "2.18", + "2.19", + "2.20", + "2.21", + "2.22", + "2.23", + "2.24", + "2.25", + "2.26", + "2.27", + "2.28" + ], + "versionDivergenceWarning": null, + "sinceVersion": null, + "phantomFieldCount": 40, + "uncoveredEndpoints": [ + { + "endpoint": "GET /api/login-banner", + "minVersion": "2.0" + }, + { + "endpoint": "POST /bucket-replica-links", + "minVersion": "2.0" + }, + { + "endpoint": "PATCH /bucket-replica-links", + "minVersion": "2.0" + }, + { + "endpoint": "DELETE /bucket-replica-links", + "minVersion": "2.0" + }, + { + "endpoint": "POST /certificate-groups/certificates", + "minVersion": "2.0" + }, + { + "endpoint": "DELETE /certificate-groups/certificates", + "minVersion": "2.0" + }, + { + "endpoint": "PATCH /directory-services/test", + "minVersion": "2.0" + }, + { + "endpoint": "GET /file-systems/performance", + "minVersion": "2.0" + }, + { + "endpoint": "PATCH /object-store-access-keys", + "minVersion": "2.0" + }, + { + "endpoint": "DELETE /policies/file-system-snapshots", + "minVersion": "2.0" + }, + { + "endpoint": "GET /policies/members", + "minVersion": "2.0" + }, + { + "endpoint": "PATCH /smtp-servers", + "minVersion": "2.0" + }, + { + "endpoint": "POST /kmip", + "minVersion": "2.1" + }, + { + "endpoint": "DELETE /kmip", + "minVersion": "2.1" + }, + { + "endpoint": "PATCH /object-store-access-policies", + "minVersion": "2.2" + }, + { + "endpoint": "GET /file-systems/policies-all", + "minVersion": "2.3" + }, + { + "endpoint": "PATCH /object-store-accounts", + "minVersion": "2.8" + }, + { + "endpoint": "POST /admins", + "minVersion": "2.15" + }, + { + "endpoint": "DELETE /admins", + "minVersion": "2.15" + }, + { + "endpoint": "PATCH /sso/saml2/idps/test", + "minVersion": "2.16" + }, + { + "endpoint": "GET /tls-policies/network-interfaces", + "minVersion": "2.17" + }, + { + "endpoint": "POST /tls-policies/network-interfaces", + "minVersion": "2.17" + }, + { + "endpoint": "DELETE /tls-policies/network-interfaces", + "minVersion": "2.17" + }, + { + "endpoint": "PATCH /file-system-replica-links", + "minVersion": "2.17" + }, + { + "endpoint": "PATCH /object-store-roles/object-store-trust-policies/upload", + "minVersion": "2.17" + }, + { + "endpoint": "GET /object-store-roles/object-store-trust-policies/download", + "minVersion": "2.17" + }, + { + "endpoint": "POST /nodes/batch", + "minVersion": "2.18" + }, + { + "endpoint": "POST /resource-accesses/batch", + "minVersion": "2.19" + }, + { + "endpoint": "GET /file-systems/data-eviction-policies", + "minVersion": "2.21" + }, + { + "endpoint": "POST /file-systems/data-eviction-policies", + "minVersion": "2.21" + }, + { + "endpoint": "DELETE /file-systems/data-eviction-policies", + "minVersion": "2.21" + }, + { + "endpoint": "GET /admins/management-authentication-policies", + "minVersion": "2.22" + }, + { + "endpoint": "POST /admins/management-authentication-policies", + "minVersion": "2.22" + }, + { + "endpoint": "DELETE /admins/management-authentication-policies", + "minVersion": "2.22" + }, + { + "endpoint": "GET /arrays/management-authentication-policies", + "minVersion": "2.22" + }, + { + "endpoint": "POST /arrays/management-authentication-policies", + "minVersion": "2.22" + }, + { + "endpoint": "DELETE /arrays/management-authentication-policies", + "minVersion": "2.22" + }, + { + "endpoint": "GET /management-authentication-policies", + "minVersion": "2.22" + }, + { + "endpoint": "POST /management-authentication-policies", + "minVersion": "2.22" + }, + { + "endpoint": "DELETE /management-authentication-policies", + "minVersion": "2.22" + }, + { + "endpoint": "PATCH /management-authentication-policies", + "minVersion": "2.22" + }, + { + "endpoint": "GET /management-authentication-policies/members", + "minVersion": "2.22" + }, + { + "endpoint": "POST /management-authentication-policies/members", + "minVersion": "2.22" + }, + { + "endpoint": "DELETE /management-authentication-policies/members", + "minVersion": "2.22" + }, + { + "endpoint": "GET /resiliency-groups/members", + "minVersion": "2.23" + }, + { + "endpoint": "DELETE /directory-services/local/directory-services", + "minVersion": "2.24" + }, + { + "endpoint": "PATCH /directory-services/local/directory-services", + "minVersion": "2.24" + }, + { + "endpoint": "PATCH /directory-services/local/groups", + "minVersion": "2.24" + }, + { + "endpoint": "GET /directory-services/local/users", + "minVersion": "2.24" + }, + { + "endpoint": "POST /directory-services/local/users", + "minVersion": "2.24" + }, + { + "endpoint": "DELETE /directory-services/local/users", + "minVersion": "2.24" + }, + { + "endpoint": "PATCH /directory-services/local/users", + "minVersion": "2.24" + }, + { + "endpoint": "GET /directory-services/local/users/members", + "minVersion": "2.24" + }, + { + "endpoint": "POST /directory-services/local/users/members", + "minVersion": "2.24" + }, + { + "endpoint": "DELETE /directory-services/local/users/members", + "minVersion": "2.24" + }, + { + "endpoint": "GET /support-diagnostics/settings", + "minVersion": "2.24" + }, + { + "endpoint": "PATCH /support-diagnostics/settings", + "minVersion": "2.24" + }, + { + "endpoint": "GET /software-bundle", + "minVersion": "2.24" + }, + { + "endpoint": "POST /software-bundle", + "minVersion": "2.24" + }, + { + "endpoint": "GET /software-patches", + "minVersion": "2.24" + }, + { + "endpoint": "POST /software-patches", + "minVersion": "2.24" + }, + { + "endpoint": "GET /file-system-junctions", + "minVersion": "2.25" + }, + { + "endpoint": "POST /file-system-junctions", + "minVersion": "2.25" + }, + { + "endpoint": "DELETE /file-system-junctions", + "minVersion": "2.25" + }, + { + "endpoint": "GET /realm-connections", + "minVersion": "2.25" + }, + { + "endpoint": "POST /realm-connections", + "minVersion": "2.25" + }, + { + "endpoint": "DELETE /realm-connections", + "minVersion": "2.25" + }, + { + "endpoint": "GET /realm-connections/connection-key", + "minVersion": "2.25" + }, + { + "endpoint": "POST /realm-connections/connection-key", + "minVersion": "2.25" + }, + { + "endpoint": "DELETE /realm-connections/connection-key", + "minVersion": "2.25" + }, + { + "endpoint": "GET /remote-realms", + "minVersion": "2.25" + }, + { + "endpoint": "GET /management-access-policies/roles", + "minVersion": "2.26" + }, + { + "endpoint": "POST /management-access-policies/roles", + "minVersion": "2.26" + }, + { + "endpoint": "DELETE /management-access-policies/roles", + "minVersion": "2.26" + }, + { + "endpoint": "GET /management-access-policies/roles/permissions", + "minVersion": "2.26" + }, + { + "endpoint": "POST /management-access-policies/roles/permissions", + "minVersion": "2.26" + }, + { + "endpoint": "DELETE /management-access-policies/roles/permissions", + "minVersion": "2.26" + }, + { + "endpoint": "PATCH /management-access-policies/roles/permissions", + "minVersion": "2.26" + }, + { + "endpoint": "GET /management-access-policies/roles/permissions/supported-resources", + "minVersion": "2.26" + }, + { + "endpoint": "GET /management-access-policies/rules", + "minVersion": "2.26" + }, + { + "endpoint": "POST /management-access-policies/rules", + "minVersion": "2.26" + }, + { + "endpoint": "DELETE /management-access-policies/rules", + "minVersion": "2.26" + }, + { + "endpoint": "PATCH /management-access-policies/rules", + "minVersion": "2.26" + }, + { + "endpoint": "PATCH /remote-arrays", + "minVersion": "2.26" + }, + { + "endpoint": "GET /support/system-manifest", + "minVersion": "2.26" + }, + { + "endpoint": "GET /topology-groups", + "minVersion": "2.26" + }, + { + "endpoint": "POST /topology-groups", + "minVersion": "2.26" + }, + { + "endpoint": "PATCH /topology-groups", + "minVersion": "2.26" + }, + { + "endpoint": "DELETE /topology-groups", + "minVersion": "2.26" + }, + { + "endpoint": "GET /topology-groups/arrays", + "minVersion": "2.26" + }, + { + "endpoint": "GET /topology-groups/members", + "minVersion": "2.26" + }, + { + "endpoint": "POST /topology-groups/members", + "minVersion": "2.26" + }, + { + "endpoint": "DELETE /topology-groups/members", + "minVersion": "2.26" + }, + { + "endpoint": "PATCH /active-directory/test", + "minVersion": "2.27" + }, + { + "endpoint": "POST /fleets/members/batch", + "minVersion": "2.27" + }, + { + "endpoint": "GET /storage-classes/members", + "minVersion": "2.27" + } + ], + "parameterGaps": [ + { + "endpoint": "DELETE /active-directory", + "cmdlets": [ + "Remove-PfbActiveDirectory" + ], + "missingQueryParameters": [ + "local_only" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /admins/api-tokens", + "cmdlets": [ + "Remove-PfbApiToken" + ], + "missingQueryParameters": [ + "admin_ids", + "admin_names", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /admins/cache", + "cmdlets": [ + "Remove-PfbAdminCache" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /admins/management-access-policies", + "cmdlets": [ + "Remove-PfbAdminManagementAccessPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [ + { + "matchType": "endpoint", + "match": "management-access-policies", + "kind": "liveTestingHazard", + "note": "POST/PATCH/DELETE return 403 regardless of account; not an implementation bug", + "reference": null + } + ] + }, + { + "endpoint": "DELETE /admins/ssh-certificate-authority-policies", + "cmdlets": [ + "Remove-PfbAdminSshCaPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /array-connections", + "cmdlets": [ + "Remove-PfbArrayConnection" + ], + "missingQueryParameters": [ + "context_names", + "remote_ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /arrays/ssh-certificate-authority-policies", + "cmdlets": [ + "Remove-PfbArraySshCaPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /audit-file-systems-policies", + "cmdlets": [ + "Remove-PfbAuditFileSystemPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /audit-file-systems-policies/members", + "cmdlets": [ + "Remove-PfbAuditFileSystemPolicyMember" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /audit-object-store-policies", + "cmdlets": [ + "Remove-PfbAuditObjectStorePolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /audit-object-store-policies/members", + "cmdlets": [ + "Remove-PfbAuditObjectStorePolicyMember" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /buckets", + "cmdlets": [ + "Remove-PfbBucket" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Eradicate", + "surface": "TypedUnresolved", + "file": "Public/Bucket/Remove-PfbBucket.ps1", + "line": 27 + } + ], + "escapeHatchOnly": [], + "caveat": "one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /buckets/audit-filters", + "cmdlets": [ + "Remove-PfbBucketAuditFilter" + ], + "missingQueryParameters": [ + "bucket_ids", + "bucket_names", + "context_names", + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /buckets/bucket-access-policies", + "cmdlets": [ + "Remove-PfbBucketAccessPolicy" + ], + "missingQueryParameters": [ + "bucket_ids", + "bucket_names", + "context_names", + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /buckets/bucket-access-policies/rules", + "cmdlets": [ + "Remove-PfbBucketAccessPolicyRule" + ], + "missingQueryParameters": [ + "bucket_ids", + "bucket_names", + "context_names", + "policy_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /buckets/cross-origin-resource-sharing-policies", + "cmdlets": [ + "Remove-PfbBucketCorsPolicy" + ], + "missingQueryParameters": [ + "bucket_ids", + "bucket_names", + "context_names", + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /buckets/cross-origin-resource-sharing-policies/rules", + "cmdlets": [ + "Remove-PfbBucketCorsPolicyRule" + ], + "missingQueryParameters": [ + "bucket_ids", + "bucket_names", + "context_names", + "policy_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /certificates/certificate-groups", + "cmdlets": [ + "Remove-PfbCertificateCertificateGroup" + ], + "missingQueryParameters": [ + "certificate_group_ids", + "certificate_ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /data-eviction-policies", + "cmdlets": [ + "Remove-PfbDataEvictionPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /data-eviction-policies/file-systems", + "cmdlets": [ + "Remove-PfbDataEvictionPolicyFileSystem" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /directory-services/local/groups", + "cmdlets": [ + "Remove-PfbLocalGroup" + ], + "missingQueryParameters": [ + "context_names", + "gids", + "local_directory_service_ids", + "local_directory_service_names", + "sids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /directory-services/local/groups/members", + "cmdlets": [ + "Remove-PfbLocalGroupMember" + ], + "missingQueryParameters": [ + "context_names", + "group_gids", + "group_sids", + "local_directory_service_ids", + "local_directory_service_names", + "member_ids", + "member_sids", + "member_types" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /dns", + "cmdlets": [ + "Remove-PfbDns" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /file-system-exports", + "cmdlets": [ + "Remove-PfbFileSystemExport" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /file-system-replica-links", + "cmdlets": [ + "Remove-PfbFileSystemReplicaLink" + ], + "missingQueryParameters": [ + "context_names", + "local_file_system_ids", + "remote_file_system_ids", + "remote_ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /file-system-replica-links/policies", + "cmdlets": [ + "Remove-PfbFileSystemReplicaLinkPolicy" + ], + "missingQueryParameters": [ + "context_names", + "local_file_system_ids", + "local_file_system_names", + "remote_ids", + "remote_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /file-system-snapshots", + "cmdlets": [ + "Remove-PfbFileSystemSnapshot" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Eradicate", + "surface": "TypedUnresolved", + "file": "Public/FileSystemSnapshot/Remove-PfbFileSystemSnapshot.ps1", + "line": 24 + } + ], + "escapeHatchOnly": [], + "caveat": "one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /file-system-snapshots/policies", + "cmdlets": [ + "Remove-PfbFileSystemSnapshotPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /file-system-snapshots/transfer", + "cmdlets": [ + "Remove-PfbFileSystemSnapshotTransfer" + ], + "missingQueryParameters": [ + "context_names", + "remote_ids", + "remote_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /file-systems", + "cmdlets": [ + "Remove-PfbFileSystem" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Eradicate", + "surface": "TypedUnresolved", + "file": "Public/FileSystem/Remove-PfbFileSystem.ps1", + "line": 38 + } + ], + "escapeHatchOnly": [], + "caveat": "one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /file-systems/audit-policies", + "cmdlets": [ + "Remove-PfbFileSystemAuditPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /file-systems/locks", + "cmdlets": [ + "Remove-PfbFileLock" + ], + "missingQueryParameters": [ + "client_names", + "context_names", + "file_system_ids", + "file_system_names", + "inodes", + "paths", + "recursive" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /file-systems/policies", + "cmdlets": [ + "Remove-PfbFileSystemPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /file-systems/sessions", + "cmdlets": [ + "Remove-PfbFileSystemSession" + ], + "missingQueryParameters": [ + "client_names", + "context_names", + "disruptive", + "user_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Force", + "surface": "TypedUnresolved", + "file": "Public/FileSystem/Remove-PfbFileSystemSession.ps1", + "line": 59 + } + ], + "escapeHatchOnly": [], + "caveat": "one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /file-systems/user-group-quota-policies", + "cmdlets": [ + "Remove-PfbFileSystemUserGroupQuotaPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /fleets/members", + "cmdlets": [ + "Remove-PfbFleetMember" + ], + "missingQueryParameters": [ + "member_ids", + "unreachable" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /lifecycle-rules", + "cmdlets": [ + "Remove-PfbLifecycleRule" + ], + "missingQueryParameters": [ + "bucket_ids", + "bucket_names", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /log-targets/file-systems", + "cmdlets": [ + "Remove-PfbLogTargetFileSystem" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /log-targets/object-store", + "cmdlets": [ + "Remove-PfbLogTargetObjectStore" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /management-access-policies", + "cmdlets": [ + "Remove-PfbManagementAccessPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [ + { + "matchType": "endpoint", + "match": "management-access-policies", + "kind": "liveTestingHazard", + "note": "POST/PATCH/DELETE return 403 regardless of account; not an implementation bug", + "reference": null + } + ] + }, + { + "endpoint": "DELETE /management-access-policies/admins", + "cmdlets": [ + "Remove-PfbManagementAccessPolicyAdmin" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [ + { + "matchType": "endpoint", + "match": "management-access-policies", + "kind": "liveTestingHazard", + "note": "POST/PATCH/DELETE return 403 regardless of account; not an implementation bug", + "reference": null + } + ] + }, + { + "endpoint": "DELETE /network-access-policies/rules", + "cmdlets": [ + "Remove-PfbNetworkAccessRule" + ], + "missingQueryParameters": [ + "ids", + "versions" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /network-interfaces/tls-policies", + "cmdlets": [ + "Remove-PfbNetworkInterfaceTlsPolicy" + ], + "missingQueryParameters": [ + "member_ids", + "policy_ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /nfs-export-policies", + "cmdlets": [ + "Remove-PfbNfsExportPolicy" + ], + "missingQueryParameters": [ + "context_names", + "versions" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /nfs-export-policies/rules", + "cmdlets": [ + "Remove-PfbNfsExportRule" + ], + "missingQueryParameters": [ + "context_names", + "ids", + "versions" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /node-groups/nodes", + "cmdlets": [ + "Remove-PfbNodeGroupNode" + ], + "missingQueryParameters": [ + "node_group_ids", + "node_group_names", + "node_ids", + "node_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /object-store-access-keys", + "cmdlets": [ + "Remove-PfbObjectStoreAccessKey" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /object-store-access-policies", + "cmdlets": [ + "Remove-PfbObjectStoreAccessPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /object-store-access-policies/object-store-roles", + "cmdlets": [ + "Remove-PfbObjectStoreAccessPolicyRole" + ], + "missingQueryParameters": [ + "context_names", + "member_ids", + "policy_ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /object-store-access-policies/object-store-users", + "cmdlets": [ + "Remove-PfbObjectStoreAccessPolicyUser" + ], + "missingQueryParameters": [ + "context_names", + "member_ids", + "policy_ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /object-store-access-policies/rules", + "cmdlets": [ + "Remove-PfbObjectStoreAccessPolicyRule" + ], + "missingQueryParameters": [ + "context_names", + "policy_ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /object-store-account-exports", + "cmdlets": [ + "Remove-PfbObjectStoreAccountExport" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /object-store-accounts", + "cmdlets": [ + "Remove-PfbObjectStoreAccount" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /object-store-remote-credentials", + "cmdlets": [ + "Remove-PfbObjectStoreRemoteCredential" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /object-store-roles", + "cmdlets": [ + "Remove-PfbObjectStoreRole" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /object-store-roles/object-store-access-policies", + "cmdlets": [ + "Remove-PfbObjectStoreRoleAccessPolicy" + ], + "missingQueryParameters": [ + "context_names", + "member_ids", + "policy_ids", + "policy_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /object-store-roles/object-store-trust-policies/rules", + "cmdlets": [ + "Remove-PfbObjectStoreTrustPolicyRule" + ], + "missingQueryParameters": [ + "context_names", + "indices", + "role_ids", + "role_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /object-store-users", + "cmdlets": [ + "Remove-PfbObjectStoreUser" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /object-store-users/object-store-access-policies", + "cmdlets": [ + "Remove-PfbObjectStoreUserAccessPolicy" + ], + "missingQueryParameters": [ + "context_names", + "member_ids", + "policy_ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /object-store-virtual-hosts", + "cmdlets": [ + "Remove-PfbObjectStoreVirtualHost" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /policies", + "cmdlets": [ + "Remove-PfbPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /policies/file-system-replica-links", + "cmdlets": [ + "Remove-PfbPolicyFileSystemReplicaLink" + ], + "missingQueryParameters": [ + "context_names", + "local_file_system_ids", + "local_file_system_names", + "remote_ids", + "remote_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /policies/file-systems", + "cmdlets": [ + "Remove-PfbPolicyFileSystem" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /presets/workload", + "cmdlets": [ + "Remove-PfbPresetWorkload" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /qos-policies", + "cmdlets": [ + "Remove-PfbQosPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /qos-policies/members", + "cmdlets": [ + "Remove-PfbQosPolicyMember" + ], + "missingQueryParameters": [ + "context_names", + "member_types" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /quotas/groups", + "cmdlets": [ + "Remove-PfbQuotaGroup" + ], + "missingQueryParameters": [ + "context_names", + "file_system_ids", + "file_system_names", + "gids", + "group_names", + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "FileSystemName", + "surface": "TypedUnresolved", + "file": "Public/Quota/Remove-PfbQuotaGroup.ps1", + "line": 30 + }, + { + "parameter": "GroupName", + "surface": "TypedUnresolved", + "file": "Public/Quota/Remove-PfbQuotaGroup.ps1", + "line": 31 + } + ], + "escapeHatchOnly": [], + "caveat": "one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /quotas/users", + "cmdlets": [ + "Remove-PfbQuotaUser" + ], + "missingQueryParameters": [ + "context_names", + "file_system_ids", + "file_system_names", + "names", + "uids", + "user_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "FileSystemName", + "surface": "TypedUnresolved", + "file": "Public/Quota/Remove-PfbQuotaUser.ps1", + "line": 25 + }, + { + "parameter": "UserName", + "surface": "TypedUnresolved", + "file": "Public/Quota/Remove-PfbQuotaUser.ps1", + "line": 26 + } + ], + "escapeHatchOnly": [], + "caveat": "one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /s3-export-policies", + "cmdlets": [ + "Remove-PfbS3ExportPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /s3-export-policies/rules", + "cmdlets": [ + "Remove-PfbS3ExportRule" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /servers", + "cmdlets": [ + "Remove-PfbServer" + ], + "missingQueryParameters": [ + "cascade_delete" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Eradicate", + "surface": "TypedUnresolved", + "file": "Public/Server/Remove-PfbServer.ps1", + "line": 35 + } + ], + "escapeHatchOnly": [], + "caveat": "one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /smb-client-policies", + "cmdlets": [ + "Remove-PfbSmbClientPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /smb-client-policies/rules", + "cmdlets": [ + "Remove-PfbSmbClientRule" + ], + "missingQueryParameters": [ + "context_names", + "ids", + "versions" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /smb-share-policies", + "cmdlets": [ + "Remove-PfbSmbSharePolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /smb-share-policies/rules", + "cmdlets": [ + "Remove-PfbSmbShareRule" + ], + "missingQueryParameters": [ + "context_names", + "ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /ssh-certificate-authority-policies/admins", + "cmdlets": [ + "Remove-PfbSshCaPolicyAdmin" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /ssh-certificate-authority-policies/arrays", + "cmdlets": [ + "Remove-PfbSshCaPolicyArray" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /user-group-quota-policies", + "cmdlets": [ + "Remove-PfbUserGroupQuotaPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /user-group-quota-policies/file-systems", + "cmdlets": [ + "Remove-PfbUserGroupQuotaPolicyFileSystem" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /user-group-quota-policies/rules", + "cmdlets": [ + "Remove-PfbUserGroupQuotaPolicyRule" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /workloads", + "cmdlets": [ + "Remove-PfbWorkload" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /workloads/tags", + "cmdlets": [ + "Remove-PfbWorkloadTag" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "DELETE /worm-data-policies", + "cmdlets": [ + "Remove-PfbWormPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /active-directory", + "cmdlets": [ + "Get-PfbActiveDirectory" + ], + "missingQueryParameters": [ + "ids", + "limit", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /active-directory/test", + "cmdlets": [ + "Test-PfbActiveDirectory" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "filter", + "limit", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /admins", + "cmdlets": [ + "Get-PfbAdmin" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "expose_api_token" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /admins/api-tokens", + "cmdlets": [ + "Get-PfbApiToken" + ], + "missingQueryParameters": [ + "admin_ids", + "admin_names", + "allow_errors", + "context_names", + "expose_api_token" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /admins/cache", + "cmdlets": [ + "Get-PfbAdminCache" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "refresh" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /admins/management-access-policies", + "cmdlets": [ + "Get-PfbAdminManagementAccessPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [ + { + "matchType": "endpoint", + "match": "management-access-policies", + "kind": "liveTestingHazard", + "note": "POST/PATCH/DELETE return 403 regardless of account; not an implementation bug", + "reference": null + } + ] + }, + { + "endpoint": "GET /admins/ssh-certificate-authority-policies", + "cmdlets": [ + "Get-PfbAdminSshCaPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /alert-watchers/test", + "cmdlets": [ + "Test-PfbAlertWatcher" + ], + "missingQueryParameters": [ + "filter", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /array-connections", + "cmdlets": [ + "Get-PfbArrayConnection" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "remote_ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /array-connections/connection-key", + "cmdlets": [ + "Get-PfbArrayConnectionKey" + ], + "missingQueryParameters": [ + "ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /array-connections/path", + "cmdlets": [ + "Get-PfbArrayConnectionPath" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "ids", + "remote_ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /array-connections/performance/replication", + "cmdlets": [ + "Get-PfbArrayConnectionPerformanceReplication" + ], + "missingQueryParameters": [ + "ids", + "remote_ids", + "total_only" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /arrays", + "cmdlets": [ + "Get-PfbArray", + "Test-PfbConnection" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Endpoint", + "surface": "TypedUnresolved", + "file": "Public/Connection/Test-PfbConnection.ps1", + "line": 31 + } + ], + "escapeHatchOnly": [], + "caveat": "one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "GET /arrays/clients/performance", + "cmdlets": [ + "Get-PfbArrayClientPerformance" + ], + "missingQueryParameters": [ + "names", + "protocol", + "total_only" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /arrays/clients/s3-specific-performance", + "cmdlets": [ + "Get-PfbArrayClientS3Performance" + ], + "missingQueryParameters": [ + "names", + "total_only" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /arrays/http-specific-performance", + "cmdlets": [ + "Get-PfbArrayHttpPerformance" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /arrays/nfs-specific-performance", + "cmdlets": [ + "Get-PfbArrayNfsPerformance" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /arrays/performance", + "cmdlets": [ + "Get-PfbArrayPerformance" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /arrays/performance/replication", + "cmdlets": [ + "Get-PfbArrayPerformanceReplication" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /arrays/s3-specific-performance", + "cmdlets": [ + "Get-PfbArrayS3Performance" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /arrays/space", + "cmdlets": [ + "Get-PfbArraySpace" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "end_time", + "resolution", + "start_time" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /arrays/space/storage-classes", + "cmdlets": [ + "Get-PfbArrayStorageClass" + ], + "missingQueryParameters": [ + "end_time", + "resolution", + "start_time", + "storage_class_names", + "total_only" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /arrays/ssh-certificate-authority-policies", + "cmdlets": [ + "Get-PfbArraySshCaPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /arrays/supported-time-zones", + "cmdlets": [ + "Get-PfbArraySupportedTimeZone" + ], + "missingQueryParameters": [ + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /audit-file-systems-policies", + "cmdlets": [ + "Get-PfbAuditFileSystemPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /audit-file-systems-policies/members", + "cmdlets": [ + "Get-PfbAuditFileSystemPolicyMember" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /audit-file-systems-policy-operations", + "cmdlets": [ + "Get-PfbAuditFileSystemPolicyOperation" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /audit-object-store-policies", + "cmdlets": [ + "Get-PfbAuditObjectStorePolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /audit-object-store-policies/members", + "cmdlets": [ + "Get-PfbAuditObjectStorePolicyMember" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /audits", + "cmdlets": [ + "Get-PfbAudit" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /blades", + "cmdlets": [ + "Get-PfbBlade", + "Get-PfbNode" + ], + "missingQueryParameters": [ + "total_only" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /bucket-audit-filter-actions", + "cmdlets": [ + "Get-PfbBucketAuditFilterAction" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /bucket-replica-links", + "cmdlets": [ + "Get-PfbBucketReplicaLink" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "ids", + "local_bucket_ids", + "remote_ids", + "remote_names", + "total_only" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /buckets", + "cmdlets": [ + "Get-PfbBucket" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /buckets/audit-filters", + "cmdlets": [ + "Get-PfbBucketAuditFilter" + ], + "missingQueryParameters": [ + "allow_errors", + "bucket_ids", + "bucket_names", + "context_names", + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /buckets/bucket-access-policies", + "cmdlets": [ + "Get-PfbBucketAccessPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "bucket_ids", + "bucket_names", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /buckets/bucket-access-policies/rules", + "cmdlets": [ + "Get-PfbBucketAccessPolicyRule" + ], + "missingQueryParameters": [ + "allow_errors", + "bucket_ids", + "bucket_names", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /buckets/cross-origin-resource-sharing-policies", + "cmdlets": [ + "Get-PfbBucketCorsPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "bucket_ids", + "bucket_names", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /buckets/cross-origin-resource-sharing-policies/rules", + "cmdlets": [ + "Get-PfbBucketCorsPolicyRule" + ], + "missingQueryParameters": [ + "allow_errors", + "bucket_ids", + "bucket_names", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /certificate-groups/certificates", + "cmdlets": [ + "Get-PfbCertificateGroupCertificate" + ], + "missingQueryParameters": [ + "certificate_group_ids", + "certificate_group_names", + "certificate_ids", + "certificate_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /certificate-groups/uses", + "cmdlets": [ + "Get-PfbCertificateGroupUse" + ], + "missingQueryParameters": [ + "ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /certificates/certificate-groups", + "cmdlets": [ + "Get-PfbCertificateCertificateGroup" + ], + "missingQueryParameters": [ + "certificate_group_ids", + "certificate_ids", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /certificates/uses", + "cmdlets": [ + "Get-PfbCertificateUse" + ], + "missingQueryParameters": [ + "ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /data-eviction-policies", + "cmdlets": [ + "Get-PfbDataEvictionPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /data-eviction-policies/file-systems", + "cmdlets": [ + "Get-PfbDataEvictionPolicyFileSystem" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /data-eviction-policies/members", + "cmdlets": [ + "Get-PfbDataEvictionPolicyMember" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /directory-services", + "cmdlets": [ + "Get-PfbDirectoryService" + ], + "missingQueryParameters": [ + "ids", + "limit", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /directory-services/local/directory-services", + "cmdlets": [ + "Get-PfbLocalDirectoryService" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "destroyed", + "total_item_count" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /directory-services/local/groups", + "cmdlets": [ + "Get-PfbLocalGroup" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "gids", + "sids", + "total_item_count" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /directory-services/local/groups/members", + "cmdlets": [ + "Get-PfbLocalGroupMember" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "group_gids", + "group_sids", + "member_ids", + "member_sids", + "member_types", + "total_item_count" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /directory-services/roles", + "cmdlets": [ + "Get-PfbDirectoryServiceRole" + ], + "missingQueryParameters": [ + "role_ids", + "role_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /directory-services/roles/management-access-policies", + "cmdlets": [ + "Get-PfbDirectoryServiceRoleManagementPolicy" + ], + "missingQueryParameters": [ + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [ + { + "matchType": "endpoint", + "match": "management-access-policies", + "kind": "liveTestingHazard", + "note": "POST/PATCH/DELETE return 403 regardless of account; not an implementation bug", + "reference": null + } + ] + }, + { + "endpoint": "GET /directory-services/test", + "cmdlets": [ + "Test-PfbDirectoryService" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "filter", + "ids", + "limit", + "names", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /dns", + "cmdlets": [ + "Get-PfbDns" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "ids", + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /drives", + "cmdlets": [ + "Get-PfbDrive" + ], + "missingQueryParameters": [ + "total_only" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-system-exports", + "cmdlets": [ + "Get-PfbFileSystemExport" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "workload_ids", + "workload_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-system-group-quotas", + "cmdlets": [ + "Get-PfbFileSystemGroupQuota" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-system-replica-links", + "cmdlets": [ + "Get-PfbFileSystemReplicaLink" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "ids", + "local_file_system_ids", + "remote_file_system_ids", + "remote_ids", + "remote_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-system-replica-links/policies", + "cmdlets": [ + "Get-PfbFileSystemReplicaLinkPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "local_file_system_ids", + "local_file_system_names", + "remote_file_system_ids", + "remote_file_system_names", + "remote_ids", + "remote_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-system-replica-links/transfer", + "cmdlets": [ + "Get-PfbFileSystemReplicaLinkTransfer" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "names_or_owner_names", + "remote_ids", + "remote_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-system-snapshots", + "cmdlets": [ + "Get-PfbFileSystemSnapshot" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "names_or_owner_names", + "owner_ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-system-snapshots/policies", + "cmdlets": [ + "Get-PfbFileSystemSnapshotPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-system-snapshots/transfer", + "cmdlets": [ + "Get-PfbFileSystemSnapshotTransfer" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "names_or_owner_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-system-user-quotas", + "cmdlets": [ + "Get-PfbFileSystemUserQuota" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-systems", + "cmdlets": [ + "Get-PfbFileSystem" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "workload_ids", + "workload_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-systems/audit-policies", + "cmdlets": [ + "Get-PfbFileSystemAuditPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-systems/groups", + "cmdlets": [ + "Get-PfbFileSystemGroup" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-systems/groups/performance", + "cmdlets": [ + "Get-PfbFileSystemGroupPerformance" + ], + "missingQueryParameters": [ + "gids", + "group_names", + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-systems/locks", + "cmdlets": [ + "Get-PfbFileLock" + ], + "missingQueryParameters": [ + "allow_errors", + "client_names", + "context_names", + "file_system_ids", + "file_system_names", + "inodes", + "paths" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-systems/locks/clients", + "cmdlets": [ + "Get-PfbFileLockClient" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-systems/open-files", + "cmdlets": [ + "Get-PfbOpenFile" + ], + "missingQueryParameters": [ + "client_names", + "file_system_ids", + "file_system_names", + "paths", + "protocols", + "session_names", + "user_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-systems/policies", + "cmdlets": [ + "Get-PfbFileSystemPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-systems/sessions", + "cmdlets": [ + "Get-PfbFileSystemSession" + ], + "missingQueryParameters": [ + "allow_errors", + "client_names", + "context_names", + "user_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-systems/space/storage-classes", + "cmdlets": [ + "Get-PfbFileSystemStorageClass" + ], + "missingQueryParameters": [ + "storage_class_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-systems/user-group-quota-policies", + "cmdlets": [ + "Get-PfbFileSystemUserGroupQuotaPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-systems/users", + "cmdlets": [ + "Get-PfbFileSystemUser" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-systems/users/performance", + "cmdlets": [ + "Get-PfbFileSystemUserPerformance" + ], + "missingQueryParameters": [ + "names", + "uids", + "user_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /file-systems/worm-data-policies", + "cmdlets": [ + "Get-PfbFileSystemWormPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /fleets", + "cmdlets": [ + "Get-PfbFleet" + ], + "missingQueryParameters": [ + "total_only" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /fleets/fleet-key", + "cmdlets": [ + "Get-PfbFleetKey" + ], + "missingQueryParameters": [ + "total_only" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /fleets/members", + "cmdlets": [ + "Get-PfbFleetMember" + ], + "missingQueryParameters": [ + "fleet_ids", + "member_ids", + "total_only" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /hardware-connectors/performance", + "cmdlets": [ + "Get-PfbHardwareConnectorPerformance" + ], + "missingQueryParameters": [ + "ids", + "total_only" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /keytabs/download", + "cmdlets": [ + "Get-PfbKeytabDownload" + ], + "missingQueryParameters": [ + "keytab_ids", + "keytab_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /legal-holds/held-entities", + "cmdlets": [ + "Get-PfbLegalHoldEntity" + ], + "missingQueryParameters": [ + "file_system_ids", + "file_system_names", + "ids", + "names", + "paths" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /lifecycle-rules", + "cmdlets": [ + "Get-PfbLifecycleRule" + ], + "missingQueryParameters": [ + "allow_errors", + "bucket_ids", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /log-targets/file-systems", + "cmdlets": [ + "Get-PfbLogTargetFileSystem" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /log-targets/object-store", + "cmdlets": [ + "Get-PfbLogTargetObjectStore" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /management-access-policies", + "cmdlets": [ + "Get-PfbManagementAccessPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [ + { + "matchType": "endpoint", + "match": "management-access-policies", + "kind": "liveTestingHazard", + "note": "POST/PATCH/DELETE return 403 regardless of account; not an implementation bug", + "reference": null + } + ] + }, + { + "endpoint": "GET /management-access-policies/admins", + "cmdlets": [ + "Get-PfbManagementAccessPolicyAdmin" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [ + { + "matchType": "endpoint", + "match": "management-access-policies", + "kind": "liveTestingHazard", + "note": "POST/PATCH/DELETE return 403 regardless of account; not an implementation bug", + "reference": null + } + ] + }, + { + "endpoint": "GET /management-access-policies/directory-services/roles", + "cmdlets": [ + "Get-PfbManagementAccessPolicyDirectoryRole" + ], + "missingQueryParameters": [ + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [ + { + "matchType": "endpoint", + "match": "management-access-policies", + "kind": "liveTestingHazard", + "note": "POST/PATCH/DELETE return 403 regardless of account; not an implementation bug", + "reference": null + } + ] + }, + { + "endpoint": "GET /management-access-policies/members", + "cmdlets": [ + "Get-PfbManagementAccessPolicyMember" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [ + { + "matchType": "endpoint", + "match": "management-access-policies", + "kind": "liveTestingHazard", + "note": "POST/PATCH/DELETE return 403 regardless of account; not an implementation bug", + "reference": null + } + ] + }, + { + "endpoint": "GET /network-access-policies/rules", + "cmdlets": [ + "Get-PfbNetworkAccessRule" + ], + "missingQueryParameters": [ + "ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /network-interfaces/connectors/performance", + "cmdlets": [ + "Get-PfbNetworkInterfaceConnectorPerformance" + ], + "missingQueryParameters": [ + "ids", + "total_only" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /network-interfaces/connectors/settings", + "cmdlets": [ + "Get-PfbNetworkInterfaceConnectorSettings" + ], + "missingQueryParameters": [ + "ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /network-interfaces/neighbors", + "cmdlets": [ + "Get-PfbNetworkInterfaceNeighbor" + ], + "missingQueryParameters": [ + "local_port_names", + "total_item_count" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /network-interfaces/network-connection-statistics", + "cmdlets": [ + "Get-PfbNetworkConnectionStatistics" + ], + "missingQueryParameters": [ + "current_state", + "local_host", + "local_port", + "remote_host", + "remote_port" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /network-interfaces/ping", + "cmdlets": [ + "Invoke-PfbNetworkPing" + ], + "missingQueryParameters": [ + "component_name", + "print_latency", + "resolve_hostname", + "source" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /network-interfaces/trace", + "cmdlets": [ + "Invoke-PfbNetworkTrace" + ], + "missingQueryParameters": [ + "component_name", + "discover_mtu", + "fragment_packet", + "port", + "resolve_hostname", + "source" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /nfs-export-policies", + "cmdlets": [ + "Get-PfbNfsExportPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "workload_ids", + "workload_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /nfs-export-policies/rules", + "cmdlets": [ + "Get-PfbNfsExportRule" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /node-groups/nodes", + "cmdlets": [ + "Get-PfbNodeGroupNode" + ], + "missingQueryParameters": [ + "node_group_ids", + "node_group_names", + "node_ids", + "node_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /node-groups/uses", + "cmdlets": [ + "Get-PfbNodeGroupUse" + ], + "missingQueryParameters": [ + "ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /nodes", + "cmdlets": [ + "Get-PfbNode" + ], + "missingQueryParameters": [ + "total_only" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /object-store-access-keys", + "cmdlets": [ + "Get-PfbObjectStoreAccessKey" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /object-store-access-policies", + "cmdlets": [ + "Get-PfbObjectStoreAccessPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "exclude_rules" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /object-store-access-policies/object-store-roles", + "cmdlets": [ + "Get-PfbObjectStoreAccessPolicyRole" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /object-store-access-policies/object-store-users", + "cmdlets": [ + "Get-PfbObjectStoreAccessPolicyUser" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /object-store-access-policies/rules", + "cmdlets": [ + "Get-PfbObjectStoreAccessPolicyRule" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /object-store-access-policy-actions", + "cmdlets": [ + "Get-PfbObjectStoreAccessPolicyAction" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /object-store-account-exports", + "cmdlets": [ + "Get-PfbObjectStoreAccountExport" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /object-store-accounts", + "cmdlets": [ + "Get-PfbObjectStoreAccount" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /object-store-remote-credentials", + "cmdlets": [ + "Get-PfbObjectStoreRemoteCredential" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /object-store-roles", + "cmdlets": [ + "Get-PfbObjectStoreRole" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /object-store-roles/object-store-access-policies", + "cmdlets": [ + "Get-PfbObjectStoreRoleAccessPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "policy_ids", + "policy_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /object-store-roles/object-store-trust-policies", + "cmdlets": [ + "Get-PfbObjectStoreTrustPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /object-store-roles/object-store-trust-policies/rules", + "cmdlets": [ + "Get-PfbObjectStoreTrustPolicyRule" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "indices", + "role_ids", + "role_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /object-store-users", + "cmdlets": [ + "Get-PfbObjectStoreUser" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /object-store-users/object-store-access-policies", + "cmdlets": [ + "Get-PfbObjectStoreUserAccessPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /object-store-virtual-hosts", + "cmdlets": [ + "Get-PfbObjectStoreVirtualHost" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /password-policies", + "cmdlets": [ + "Get-PfbPasswordPolicy" + ], + "missingQueryParameters": [ + "ids", + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /policies", + "cmdlets": [ + "Get-PfbPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "workload_ids", + "workload_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /policies-all", + "cmdlets": [ + "Get-PfbPolicyAll" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /policies-all/members", + "cmdlets": [ + "Get-PfbPolicyAllMember" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "local_file_system_ids", + "local_file_system_names", + "remote_file_system_ids", + "remote_file_system_names", + "remote_ids", + "remote_names", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /policies/file-system-replica-links", + "cmdlets": [ + "Get-PfbPolicyFileSystemReplicaLink" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "local_file_system_ids", + "local_file_system_names", + "remote_file_system_ids", + "remote_file_system_names", + "remote_ids", + "remote_names", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /policies/file-system-snapshots", + "cmdlets": [ + "Get-PfbPolicyFileSystemSnapshot" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /policies/file-systems", + "cmdlets": [ + "Get-PfbPolicyFileSystem" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /presets/workload", + "cmdlets": [ + "Get-PfbPresetWorkload" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /public-keys/uses", + "cmdlets": [ + "Get-PfbPublicKeyUse" + ], + "missingQueryParameters": [ + "ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /qos-policies", + "cmdlets": [ + "Get-PfbQosPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /qos-policies/buckets", + "cmdlets": [ + "Get-PfbQosPolicyBucket" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /qos-policies/file-systems", + "cmdlets": [ + "Get-PfbQosPolicyFileSystem" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /qos-policies/members", + "cmdlets": [ + "Get-PfbQosPolicyMember" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "member_types", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /quotas/groups", + "cmdlets": [ + "Get-PfbQuotaGroup" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "file_system_ids", + "gids", + "group_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /quotas/settings", + "cmdlets": [ + "Get-PfbQuotaSettings" + ], + "missingQueryParameters": [ + "ids", + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /quotas/users", + "cmdlets": [ + "Get-PfbQuotaUser" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "file_system_ids", + "uids", + "user_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /realms", + "cmdlets": [ + "Get-PfbRealm" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /realms/defaults", + "cmdlets": [ + "Get-PfbRealmDefaults" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "realm_ids", + "realm_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /realms/space", + "cmdlets": [ + "Get-PfbRealmSpace" + ], + "missingQueryParameters": [ + "end_time", + "ids", + "resolution", + "start_time", + "total_only", + "type" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /realms/space/storage-classes", + "cmdlets": [ + "Get-PfbRealmStorageClass" + ], + "missingQueryParameters": [ + "end_time", + "ids", + "resolution", + "start_time", + "storage_class_names", + "total_only" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /remote-arrays", + "cmdlets": [ + "Get-PfbRemoteArray" + ], + "missingQueryParameters": [ + "total_only" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /roles", + "cmdlets": [ + "Get-PfbRole" + ], + "missingQueryParameters": [ + "ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /s3-export-policies", + "cmdlets": [ + "Get-PfbS3ExportPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /s3-export-policies/rules", + "cmdlets": [ + "Get-PfbS3ExportRule" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /servers", + "cmdlets": [ + "Get-PfbServer" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /smb-client-policies", + "cmdlets": [ + "Get-PfbSmbClientPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "workload_ids", + "workload_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /smb-client-policies/rules", + "cmdlets": [ + "Get-PfbSmbClientRule" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /smb-share-policies", + "cmdlets": [ + "Get-PfbSmbSharePolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "workload_ids", + "workload_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /smb-share-policies/rules", + "cmdlets": [ + "Get-PfbSmbShareRule" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /smtp-servers", + "cmdlets": [ + "Get-PfbSmtpServer" + ], + "missingQueryParameters": [ + "ids", + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /snmp-agents", + "cmdlets": [ + "Get-PfbSnmpAgent" + ], + "missingQueryParameters": [ + "ids", + "limit", + "names", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /snmp-managers/test", + "cmdlets": [ + "Test-PfbSnmpManager" + ], + "missingQueryParameters": [ + "filter", + "limit", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /software-check", + "cmdlets": [ + "Get-PfbSoftwareCheck" + ], + "missingQueryParameters": [ + "ids", + "names", + "software_names", + "software_versions", + "total_item_count" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /ssh-certificate-authority-policies", + "cmdlets": [ + "Get-PfbSshCaPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /ssh-certificate-authority-policies/admins", + "cmdlets": [ + "Get-PfbSshCaPolicyAdmin" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /ssh-certificate-authority-policies/arrays", + "cmdlets": [ + "Get-PfbSshCaPolicyArray" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /ssh-certificate-authority-policies/members", + "cmdlets": [ + "Get-PfbSshCaPolicyMember" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /sso/saml2/idps/test", + "cmdlets": [ + "Test-PfbSaml2Idp" + ], + "missingQueryParameters": [ + "filter", + "limit", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /storage-class-tiering-policies/members", + "cmdlets": [ + "Get-PfbStorageClassTieringPolicyMember" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /support", + "cmdlets": [ + "Get-PfbSupport" + ], + "missingQueryParameters": [ + "ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /support/test", + "cmdlets": [ + "Test-PfbSupport" + ], + "missingQueryParameters": [ + "filter", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /syslog-servers", + "cmdlets": [ + "Get-PfbSyslogServer" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /syslog-servers/settings", + "cmdlets": [ + "Get-PfbSyslogServerSettings" + ], + "missingQueryParameters": [ + "ids", + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /targets", + "cmdlets": [ + "Get-PfbTarget" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /targets/performance/replication", + "cmdlets": [ + "Get-PfbTargetPerformanceReplication" + ], + "missingQueryParameters": [ + "ids", + "total_only" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /tls-policies", + "cmdlets": [ + "Get-PfbTlsPolicy" + ], + "missingQueryParameters": [ + "effective", + "purity_defined" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /tls-policies/members", + "cmdlets": [ + "Get-PfbTlsPolicyMember" + ], + "missingQueryParameters": [ + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /usage/groups", + "cmdlets": [ + "Get-PfbUsageGroup" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "file_system_ids", + "gids", + "group_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /usage/users", + "cmdlets": [ + "Get-PfbUsageUser" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "file_system_ids", + "uids", + "user_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /user-group-quota-policies", + "cmdlets": [ + "Get-PfbUserGroupQuotaPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "ids", + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Id", + "surface": "TypedUnresolved", + "file": "Public/Policy/Get-PfbUserGroupQuotaPolicy.ps1", + "line": 38 + }, + { + "parameter": "Name", + "surface": "TypedUnresolved", + "file": "Public/Policy/Get-PfbUserGroupQuotaPolicy.ps1", + "line": 35 + } + ], + "escapeHatchOnly": [], + "caveat": "one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "GET /user-group-quota-policies/file-systems", + "cmdlets": [ + "Get-PfbUserGroupQuotaPolicyFileSystem" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /user-group-quota-policies/members", + "cmdlets": [ + "Get-PfbUserGroupQuotaPolicyMember" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /user-group-quota-policies/rules", + "cmdlets": [ + "Get-PfbUserGroupQuotaPolicyRule" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /workloads", + "cmdlets": [ + "Get-PfbWorkload" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /workloads/placement-recommendations", + "cmdlets": [ + "Get-PfbWorkloadPlacementRecommendation" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /workloads/tags", + "cmdlets": [ + "Get-PfbWorkloadTag" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /worm-data-policies", + "cmdlets": [ + "Get-PfbWormPolicy" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "GET /worm-data-policies/members", + "cmdlets": [ + "Get-PfbWormPolicyMember" + ], + "missingQueryParameters": [ + "allow_errors", + "context_names", + "sort" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /admins", + "cmdlets": [ + "Update-PfbAdmin" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + { + "name": "role", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "Deprecated.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/Update-PfbAdmin.ps1", + "paramBlockLine": 93, + "payloadVariable": "body", + "assignmentStyle": "index", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /admins/settings", + "cmdlets": [ + "Update-PfbAdminSetting" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + { + "name": "lockout_duration", + "type": "integer", + "format": "int64", + "specRequired": false, + "synopsis": "The lockout duration, in milliseconds, if a user has reached the maximum number of login attempts.", + "suggestedPowerShellType": "[long]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/Update-PfbAdminSetting.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "max_login_attempts", + "type": "integer", + "format": "int32", + "specRequired": false, + "synopsis": "The maximum number of failed login attempts allowed before the user is locked out.", + "suggestedPowerShellType": "[int]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/Update-PfbAdminSetting.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "min_password_length", + "type": "integer", + "format": "int32", + "specRequired": false, + "synopsis": "The minimum password length.", + "suggestedPowerShellType": "[int]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/Update-PfbAdminSetting.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /alert-watchers", + "cmdlets": [ + "Update-PfbAlertWatcher" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + "enabled" + ], + "readOnlyFields": [ + "id", + "name" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Alert/Update-PfbAlertWatcher.ps1", + "line": 32 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /alerts", + "cmdlets": [ + "Update-PfbAlert" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + "flagged" + ], + "readOnlyFields": [ + "action", + "code", + "component_name", + "component_type", + "created", + "description", + "duration", + "id", + "index", + "knowledge_base_url", + "name", + "notified", + "severity", + "state", + "summary", + "updated", + "variables" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Flagged", + "surface": "AttributesOnly", + "file": "Public/Alert/Update-PfbAlert.ps1", + "line": 26 + } + ], + "escapeHatchOnly": [ + "Flagged" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /api-clients", + "cmdlets": [ + "Update-PfbApiClient" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + { + "name": "max_role", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "Deprecated.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/Update-PfbApiClient.ps1", + "paramBlockLine": 61, + "payloadVariable": "body", + "assignmentStyle": "index", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "access_policies", + "access_token_ttl_in_ms", + "id", + "issuer", + "key_id", + "name", + "public_key" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /array-connections", + "cmdlets": [ + "Update-PfbArrayConnection" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "context", + "id", + "os", + "status", + "type", + "version" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /arrays", + "cmdlets": [ + "Update-PfbArray" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + { + "name": "banner", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "A string to be shown when logging in to the array.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Array/Update-PfbArray.ps1", + "paramBlockLine": 29, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "default_inbound_tls_policy", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "The default TLS policy governing inbound traffic from clients accessing the array.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Array/Update-PfbArray.ps1", + "paramBlockLine": 29, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "eradication_config", + "type": null, + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Array/Update-PfbArray.ps1", + "paramBlockLine": 29, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "idle_timeout", + "type": "integer", + "format": "int32", + "specRequired": false, + "synopsis": "Idle timeout in milliseconds.", + "suggestedPowerShellType": "[int]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Array/Update-PfbArray.ps1", + "paramBlockLine": 29, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "name", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "A user-specified name.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/Array/Update-PfbArray.ps1", + "paramBlockLine": 29, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "network_access_policy", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "The network access policy governing which clients are allowed or denied access to different array interfaces.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Array/Update-PfbArray.ps1", + "paramBlockLine": 29, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "ntp_servers", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Array/Update-PfbArray.ps1", + "paramBlockLine": 29, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "time_zone", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The time zone to use for the array.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Array/Update-PfbArray.ps1", + "paramBlockLine": 29, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "_as_of", + "context", + "encryption", + "id", + "os", + "product_type", + "revision", + "security_update", + "smb_mode", + "version" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /arrays/erasures", + "cmdlets": [ + "Update-PfbArrayErasure" + ], + "missingQueryParameters": [ + "delete_sanitization_certificate", + "eradicate_all_data", + "finalize" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /arrays/eula", + "cmdlets": [ + "Update-PfbArrayEula" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + { + "name": "signature", + "type": null, + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Array/Update-PfbArrayEula.ps1", + "paramBlockLine": 28, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "agreement" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /audit-file-systems-policies", + "cmdlets": [ + "Update-PfbAuditFileSystemPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "add_log_targets", + "control_type", + "enabled", + "location", + "log_targets", + "name", + "remove_log_targets", + "rules" + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Policy/Update-PfbAuditFileSystemPolicy.ps1", + "line": 40 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /audit-object-store-policies", + "cmdlets": [ + "Update-PfbAuditObjectStorePolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "add_log_targets", + "enabled", + "location", + "log_targets", + "name", + "remove_log_targets" + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Policy/Update-PfbAuditObjectStorePolicy.ps1", + "line": 40 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /buckets", + "cmdlets": [ + "Remove-PfbBucket", + "Update-PfbBucket" + ], + "missingQueryParameters": [ + "cancel_in_progress_storage_class_transition", + "context_names", + "ignore_usage" + ], + "missingBodyProperties": [ + "destroyed", + "eradication_config", + "hard_limit_enabled", + "object_lock_config", + "public_access_config", + "qos_policy", + "retention_lock", + "storage_class" + ], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Destroyed", + "surface": "AttributesOnly", + "file": "Public/Bucket/Update-PfbBucket.ps1", + "line": 37 + }, + { + "parameter": "Eradicate", + "surface": "TypedUnresolved", + "file": "Public/Bucket/Remove-PfbBucket.ps1", + "line": 27 + } + ], + "escapeHatchOnly": [ + "Destroyed" + ], + "caveat": "body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /buckets/audit-filters", + "cmdlets": [ + "Update-PfbBucketAuditFilter" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /certificates", + "cmdlets": [ + "Update-PfbCertificate" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "issued_by", + "issued_to", + "realms", + "status", + "valid_from", + "valid_to" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /data-eviction-policies", + "cmdlets": [ + "Update-PfbDataEvictionPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "enabled", + "location" + ], + "readOnlyFields": [ + "context", + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "TypedUnresolved", + "file": "Public/DataEviction/Update-PfbDataEvictionPolicy.ps1", + "line": 37 + } + ], + "escapeHatchOnly": [], + "caveat": "one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /directory-services", + "cmdlets": [ + "Update-PfbDirectoryService" + ], + "missingQueryParameters": [ + "ids", + "names" + ], + "missingBodyProperties": [ + "base_dn", + "bind_password", + "bind_user", + "ca_certificate", + "ca_certificate_group", + "enabled", + "management", + "nfs", + "smb", + "uris" + ], + "readOnlyFields": [ + "id", + "name", + "services" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Name", + "surface": "AttributesOnly", + "file": "Public/DirectoryService/Update-PfbDirectoryService.ps1", + "line": 32 + } + ], + "escapeHatchOnly": [ + "Name" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /directory-services/roles", + "cmdlets": [ + "Update-PfbDirectoryServiceRole" + ], + "missingQueryParameters": [ + "role_ids", + "role_names" + ], + "missingBodyProperties": [ + { + "name": "role", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "Deprecated.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/DirectoryService/Update-PfbDirectoryServiceRole.ps1", + "paramBlockLine": 64, + "payloadVariable": "body", + "assignmentStyle": "index", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "id", + "management_access_policies", + "name" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /dns", + "cmdlets": [ + "Update-PfbDns" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "context", + "id", + "realms" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /file-system-exports", + "cmdlets": [ + "Update-PfbFileSystemExport" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "context", + "enabled", + "id", + "name", + "policy_type", + "status" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /file-system-snapshots", + "cmdlets": [ + "Remove-PfbFileSystemSnapshot" + ], + "missingQueryParameters": [ + "context_names", + "latest_replica" + ], + "missingBodyProperties": [ + "destroyed", + "name", + "owner", + "policy", + "source" + ], + "readOnlyFields": [ + "context", + "created", + "id", + "owner_destroyed", + "policies", + "suffix", + "time_remaining" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Eradicate", + "surface": "TypedUnresolved", + "file": "Public/FileSystemSnapshot/Remove-PfbFileSystemSnapshot.ps1", + "line": 24 + } + ], + "escapeHatchOnly": [], + "caveat": "one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /file-systems", + "cmdlets": [ + "Remove-PfbFileSystem", + "Update-PfbFileSystem" + ], + "missingQueryParameters": [ + "cancel_in_progress_storage_class_transition", + "context_names", + "discard_detailed_permissions", + "ignore_usage" + ], + "missingBodyProperties": [ + "abort_quiesce", + "default_group_quota", + "default_user_quota", + "destroyed", + "fast_remove_directory_enabled", + "group_ownership", + "hard_limit_enabled", + "http", + "multi_protocol", + "name", + "nfs", + "qos_policy", + "quiesce", + "skip_quiesce", + "smb", + "snapshot_directory_enabled", + "source", + "storage_class", + "workload", + "writable" + ], + "readOnlyFields": [ + "created", + "id", + "promotion_status", + "time_remaining" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Destroyed", + "surface": "AttributesOnly", + "file": "Public/FileSystem/Update-PfbFileSystem.ps1", + "line": 93 + }, + { + "parameter": "Eradicate", + "surface": "TypedUnresolved", + "file": "Public/FileSystem/Remove-PfbFileSystem.ps1", + "line": 38 + }, + { + "parameter": "HardLimitEnabled", + "surface": "AttributesOnly", + "file": "Public/FileSystem/Update-PfbFileSystem.ps1", + "line": 69 + }, + { + "parameter": "HttpEnabled", + "surface": "AttributesOnly", + "file": "Public/FileSystem/Update-PfbFileSystem.ps1", + "line": 90 + }, + { + "parameter": "NfsEnabled", + "surface": "AttributesOnly", + "file": "Public/FileSystem/Update-PfbFileSystem.ps1", + "line": 72 + }, + { + "parameter": "NfsExportPolicy", + "surface": "AttributesOnly", + "file": "Public/FileSystem/Update-PfbFileSystem.ps1", + "line": 78 + }, + { + "parameter": "NfsRules", + "surface": "AttributesOnly", + "file": "Public/FileSystem/Update-PfbFileSystem.ps1", + "line": 75 + }, + { + "parameter": "SmbClientPolicy", + "surface": "AttributesOnly", + "file": "Public/FileSystem/Update-PfbFileSystem.ps1", + "line": 87 + }, + { + "parameter": "SmbEnabled", + "surface": "AttributesOnly", + "file": "Public/FileSystem/Update-PfbFileSystem.ps1", + "line": 81 + }, + { + "parameter": "SmbSharePolicy", + "surface": "AttributesOnly", + "file": "Public/FileSystem/Update-PfbFileSystem.ps1", + "line": 84 + } + ], + "escapeHatchOnly": [ + "Destroyed", + "HardLimitEnabled", + "HttpEnabled", + "NfsEnabled", + "NfsExportPolicy", + "NfsRules", + "SmbClientPolicy", + "SmbEnabled", + "SmbSharePolicy" + ], + "caveat": "body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /hardware", + "cmdlets": [ + "Update-PfbHardware" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "data_mac", + "details", + "id", + "index", + "management_mac", + "model", + "name", + "part_number", + "sensor_readings", + "serial", + "slot", + "speed", + "status", + "temperature", + "type" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /hardware-connectors", + "cmdlets": [ + "Update-PfbHardwareConnector" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "connector_type", + "id", + "name", + "transceiver_type" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /kmip", + "cmdlets": [ + "Update-PfbKmip" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "id", + "name" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /legal-holds", + "cmdlets": [ + "Update-PfbLegalHold" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "id", + "name", + "realms" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /lifecycle-rules", + "cmdlets": [ + "Update-PfbLifecycleRule" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /log-targets/file-systems", + "cmdlets": [ + "Update-PfbLogTargetFileSystem" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "id" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /log-targets/object-store", + "cmdlets": [ + "Update-PfbLogTargetObjectStore" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "id" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /logs-async", + "cmdlets": [ + "Update-PfbAsyncLog" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "available_files", + "id", + "last_request_time", + "name", + "processing", + "progress" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /management-access-policies", + "cmdlets": [ + "Update-PfbManagementAccessPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "context", + "id", + "is_local", + "policy_type", + "realms", + "version" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [ + { + "matchType": "endpoint", + "match": "management-access-policies", + "kind": "liveTestingHazard", + "note": "POST/PATCH/DELETE return 403 regardless of account; not an implementation bug", + "reference": null + } + ] + }, + { + "endpoint": "PATCH /network-access-policies", + "cmdlets": [ + "Update-PfbNetworkAccessPolicy" + ], + "missingQueryParameters": [ + "versions" + ], + "missingBodyProperties": [ + "enabled", + "location", + "name", + "rules" + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms", + "version" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Policy/Update-PfbNetworkAccessPolicy.ps1", + "line": 42 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /network-access-policies/rules", + "cmdlets": [ + "Update-PfbNetworkAccessRule" + ], + "missingQueryParameters": [ + "before_rule_id", + "before_rule_name", + "ids", + "versions" + ], + "missingBodyProperties": [ + { + "name": "client", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Specifies the clients that will be permitted or denied access to the interface.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbNetworkAccessRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "effect", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "If set to `allow`, the specified client will be permitted to access the specified interfaces.", + "suggestedPowerShellType": "[string]", + "enumValues": [ + "allow", + "deny" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Policy/Update-PfbNetworkAccessRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "index", + "type": "integer", + "format": "int32", + "specRequired": false, + "synopsis": "The index within the policy.", + "suggestedPowerShellType": "[int]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbNetworkAccessRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "interfaces", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "Specifies which product interfaces this rule applies to, whether it is permitting or denying access.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [ + "management-ssh", + "management-rest-api", + "management-web-ui", + "snmp", + "local-network-superuser-password-access" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Policy/Update-PfbNetworkAccessRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "policy", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "The policy to which this rule belongs.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbNetworkAccessRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "id", + "name", + "policy_version" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /network-interfaces/connectors", + "cmdlets": [ + "Update-PfbNetworkInterfaceConnector" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "connector_type", + "id", + "name", + "transceiver_type" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /nfs-export-policies", + "cmdlets": [ + "Update-PfbNfsExportPolicy" + ], + "missingQueryParameters": [ + "context_names", + "versions" + ], + "missingBodyProperties": [ + "enabled", + "location", + "name", + "rules" + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms", + "version" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Policy/Update-PfbNfsExportPolicy.ps1", + "line": 42 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /nfs-export-policies/rules", + "cmdlets": [ + "Update-PfbNfsExportRule" + ], + "missingQueryParameters": [ + "before_rule_id", + "before_rule_name", + "context_names", + "ids", + "versions" + ], + "missingBodyProperties": [ + { + "name": "access", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Specifies access control for the export.", + "suggestedPowerShellType": "[string]", + "enumValues": [ + "root-squash", + "all-squash", + "no-squash" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Policy/Update-PfbNfsExportRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "anongid", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Any user whose GID is affected by an `access` of `root_squash` or `all_squash` will have their GID mapped to `anongid`.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbNfsExportRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "anonuid", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Any user whose UID is affected by an `access` of `root_squash` or `all_squash` will have their UID mapped to `anonuid`.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbNfsExportRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "atime", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "If `true`, after a read operation has occurred, the inode access time is updated only if any of the following conditions is true: the previous access time is less than the inode modify time, the previous access time is less than the inode change time, or the previous access time is more than 24 hours ago.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbNfsExportRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "client", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Specifies the clients that will be permitted to access the export.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbNfsExportRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "fileid_32bit", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "Whether the file id is 32 bits or not.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbNfsExportRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "index", + "type": "integer", + "format": "int32", + "specRequired": false, + "synopsis": "The index within the policy.", + "suggestedPowerShellType": "[int]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbNfsExportRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "permission", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Specifies which read-write client access permissions are allowed for the export.", + "suggestedPowerShellType": "[string]", + "enumValues": [ + "rw", + "ro" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Policy/Update-PfbNfsExportRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "required_transport_security", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Specifies the minimum transport security required for clients to access the export.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbNfsExportRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "secure", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "If `true`, prevents NFS access to client connections coming from non-reserved ports.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbNfsExportRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "security", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The security flavors to use for accessing files on this mount point.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbNfsExportRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "id", + "name" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /nodes", + "cmdlets": [ + "Update-PfbNode" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "capacity", + "chassis_serial_number", + "data_addresses", + "details", + "id", + "raw_capacity", + "status", + "unique" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /object-store-access-policies/rules", + "cmdlets": [ + "Update-PfbObjectStoreAccessPolicyRule" + ], + "missingQueryParameters": [ + "context_names", + "enforce_action_restrictions", + "policy_ids", + "policy_names" + ], + "missingBodyProperties": [ + { + "name": "actions", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The list of actions granted by this rule.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/ObjectStore/Update-PfbObjectStoreAccessPolicyRule.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "conditions", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "Conditions used to limit the scope which this rule applies to.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/Update-PfbObjectStoreAccessPolicyRule.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "effect", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Effect of this rule.", + "suggestedPowerShellType": "[string]", + "enumValues": [ + "allow", + "deny" + ], + "enumStatus": "matched", + "target": { + "file": "Public/ObjectStore/Update-PfbObjectStoreAccessPolicyRule.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "resources", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The list of resources which this rule applies to.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/Update-PfbObjectStoreAccessPolicyRule.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /object-store-account-exports", + "cmdlets": [ + "Update-PfbObjectStoreAccountExport" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /object-store-remote-credentials", + "cmdlets": [ + "Update-PfbObjectStoreRemoteCredential" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "context", + "id", + "realms" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /object-store-roles", + "cmdlets": [ + "Update-PfbObjectStoreRole" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "context", + "created", + "id", + "name", + "prn", + "trusted_entities" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /object-store-roles/object-store-trust-policies/rules", + "cmdlets": [ + "Update-PfbObjectStoreTrustPolicyRule" + ], + "missingQueryParameters": [ + "context_names", + "indices", + "policy_names", + "role_ids", + "role_names" + ], + "missingBodyProperties": [ + { + "name": "actions", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The list of role-assumption actions granted by this rule to the respective role.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/ObjectStore/Update-PfbObjectStoreTrustPolicyRule.ps1", + "paramBlockLine": 38, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "conditions", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "Conditions used to limit the scope which this rule applies to.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/Update-PfbObjectStoreTrustPolicyRule.ps1", + "paramBlockLine": 38, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "policy", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "The policy to which this rule belongs.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/Update-PfbObjectStoreTrustPolicyRule.ps1", + "paramBlockLine": 38, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "principals", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "List of Identity Providers", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/Update-PfbObjectStoreTrustPolicyRule.ps1", + "paramBlockLine": 38, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "effect" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /object-store-virtual-hosts", + "cmdlets": [ + "Update-PfbObjectStoreVirtualHost" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "id" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /password-policies", + "cmdlets": [ + "Update-PfbPasswordPolicy" + ], + "missingQueryParameters": [ + "ids", + "names" + ], + "missingBodyProperties": [ + { + "name": "enabled", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "If `true`, the policy is enabled.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/Update-PfbPasswordPolicy.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "enforce_dictionary_check", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "If `true`, test password against dictionary of known leaked passwords.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/Update-PfbPasswordPolicy.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "enforce_username_check", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "If `true`, the username cannot be a substring of the password.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/Update-PfbPasswordPolicy.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "location", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "Reference to the array where the policy is defined.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/Update-PfbPasswordPolicy.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "lockout_duration", + "type": "integer", + "format": "int64", + "specRequired": false, + "synopsis": "The lockout duration, in milliseconds, if a user is locked out after reaching the maximum number of login attempts.", + "suggestedPowerShellType": "[long]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/Update-PfbPasswordPolicy.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "max_login_attempts", + "type": "integer", + "format": "int32", + "specRequired": false, + "synopsis": "Maximum number of failed login attempts allowed before the user is locked out.", + "suggestedPowerShellType": "[int]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/Update-PfbPasswordPolicy.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "max_password_age", + "type": "integer", + "format": "int64", + "specRequired": false, + "synopsis": "The maximum age of password before password change is required.", + "suggestedPowerShellType": "[long]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/Update-PfbPasswordPolicy.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "min_character_groups", + "type": "integer", + "format": "int32", + "specRequired": false, + "synopsis": "The minimum number of character groups ([a-z], [A-Z], [0-9], other) required to be present in a password.", + "suggestedPowerShellType": "[int]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/Update-PfbPasswordPolicy.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "min_characters_per_group", + "type": "integer", + "format": "int32", + "specRequired": false, + "synopsis": "The minimum number of characters per group to count the group as present.", + "suggestedPowerShellType": "[int]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/Update-PfbPasswordPolicy.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "min_password_age", + "type": "integer", + "format": "int64", + "specRequired": false, + "synopsis": "The minimum age, in milliseconds, of password before password change is allowed.", + "suggestedPowerShellType": "[long]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/Update-PfbPasswordPolicy.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "min_password_length", + "type": "integer", + "format": "int32", + "specRequired": false, + "synopsis": "Minimum password length.", + "suggestedPowerShellType": "[int]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/Update-PfbPasswordPolicy.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "name", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "A user-specified name.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/Admin/Update-PfbPasswordPolicy.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "password_history", + "type": "integer", + "format": "int32", + "specRequired": false, + "synopsis": "The number of passwords tracked to prevent reuse of passwords.", + "suggestedPowerShellType": "[int]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/Update-PfbPasswordPolicy.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /policies", + "cmdlets": [ + "Update-PfbPolicy" + ], + "missingQueryParameters": [ + "context_names", + "destroy_snapshots" + ], + "missingBodyProperties": [ + "add_rules", + "enabled", + "location", + "remove_rules" + ], + "readOnlyFields": [ + "id", + "is_local", + "name", + "policy_type", + "realms", + "retention_lock" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Policy/Update-PfbPolicy.ps1", + "line": 28 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /presets/workload", + "cmdlets": [ + "Update-PfbPresetWorkload" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /qos-policies", + "cmdlets": [ + "Update-PfbQosPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "context", + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /quotas/groups", + "cmdlets": [ + "Update-PfbQuotaGroup" + ], + "missingQueryParameters": [ + "context_names", + "file_system_ids", + "file_system_names", + "gids", + "group_names", + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "name" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "FileSystemName", + "surface": "AttributesOnly", + "file": "Public/Quota/Update-PfbQuotaGroup.ps1", + "line": 35 + }, + { + "parameter": "GroupName", + "surface": "AttributesOnly", + "file": "Public/Quota/Update-PfbQuotaGroup.ps1", + "line": 36 + } + ], + "escapeHatchOnly": [ + "FileSystemName", + "GroupName" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /quotas/settings", + "cmdlets": [ + "Update-PfbQuotaSettings" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + { + "name": "contact", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The contact information that will be provided in any notifications sent directly to users and groups.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Quota/Update-PfbQuotaSettings.ps1", + "paramBlockLine": 28, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "direct_notifications_enabled", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "Are notifications regarding space usage and quotas being sent directly to user and group emails?", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Quota/Update-PfbQuotaSettings.ps1", + "paramBlockLine": 28, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "id", + "name" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /quotas/users", + "cmdlets": [ + "Update-PfbQuotaUser" + ], + "missingQueryParameters": [ + "context_names", + "file_system_ids", + "file_system_names", + "names", + "uids", + "user_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "name" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "FileSystemName", + "surface": "AttributesOnly", + "file": "Public/Quota/Update-PfbQuotaUser.ps1", + "line": 30 + }, + { + "parameter": "UserName", + "surface": "AttributesOnly", + "file": "Public/Quota/Update-PfbQuotaUser.ps1", + "line": 31 + } + ], + "escapeHatchOnly": [ + "FileSystemName", + "UserName" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /rapid-data-locking", + "cmdlets": [ + "Update-PfbRapidDataLocking" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + { + "name": "enabled", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "`True` if the Rapid Data Locking feature is enabled.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Misc/Update-PfbRapidDataLocking.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "kmip_server", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "The KMIP server configuration associated with RDL.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Misc/Update-PfbRapidDataLocking.ps1", + "paramBlockLine": 31, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /realms", + "cmdlets": [ + "Remove-PfbRealm", + "Update-PfbRealm" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + "default_inbound_tls_policy", + "destroyed", + "name" + ], + "readOnlyFields": [ + "id" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Destroyed", + "surface": "AttributesOnly", + "file": "Public/Realm/Update-PfbRealm.ps1", + "line": 31 + }, + { + "parameter": "Eradicate", + "surface": "TypedUnresolved", + "file": "Public/Realm/Remove-PfbRealm.ps1", + "line": 32 + } + ], + "escapeHatchOnly": [ + "Destroyed" + ], + "caveat": "body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /realms/defaults", + "cmdlets": [ + "Update-PfbRealmDefaults" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "context", + "realm" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /s3-export-policies", + "cmdlets": [ + "Update-PfbS3ExportPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "enabled", + "name", + "rules" + ], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Policy/Update-PfbS3ExportPolicy.ps1", + "line": 42 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /s3-export-policies/rules", + "cmdlets": [ + "Update-PfbS3ExportRule" + ], + "missingQueryParameters": [ + "context_names", + "policy_ids", + "policy_names" + ], + "missingBodyProperties": [ + { + "name": "actions", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The list of actions granted by this rule.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/Policy/Update-PfbS3ExportRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "effect", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Effect of this rule.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/Policy/Update-PfbS3ExportRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "resources", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The list of resources from the account to which this rule applies to.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbS3ExportRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /smb-client-policies", + "cmdlets": [ + "Update-PfbSmbClientPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "access_based_enumeration_enabled", + "enabled", + "location", + "name", + "rules" + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms", + "version" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Policy/Update-PfbSmbClientPolicy.ps1", + "line": 42 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /smb-client-policies/rules", + "cmdlets": [ + "Update-PfbSmbClientRule" + ], + "missingQueryParameters": [ + "before_rule_id", + "before_rule_name", + "context_names", + "ids", + "versions" + ], + "missingBodyProperties": [ + { + "name": "client", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Specifies the clients that will be permitted to access the export.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbSmbClientRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "encryption", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Specifies whether the remote client is required to use SMB encryption.", + "suggestedPowerShellType": "[string]", + "enumValues": [ + "required", + "disabled", + "optional" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Policy/Update-PfbSmbClientRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "index", + "type": "integer", + "format": "int32", + "specRequired": false, + "synopsis": "The index within the policy.", + "suggestedPowerShellType": "[int]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbSmbClientRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "permission", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Specifies which read-write client access permissions are allowed for the export.", + "suggestedPowerShellType": "[string]", + "enumValues": [ + "rw", + "ro" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Policy/Update-PfbSmbClientRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "policy", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "The policy to which this rule belongs.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbSmbClientRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "context", + "id", + "name", + "policy_version" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /smb-share-policies", + "cmdlets": [ + "Update-PfbSmbSharePolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "enabled", + "location", + "name", + "rules" + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Policy/Update-PfbSmbSharePolicy.ps1", + "line": 42 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /smb-share-policies/rules", + "cmdlets": [ + "Update-PfbSmbShareRule" + ], + "missingQueryParameters": [ + "context_names", + "ids", + "policy_ids", + "policy_names" + ], + "missingBodyProperties": [ + { + "name": "change", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The state of the principal's Change access permission.", + "suggestedPowerShellType": "[string]", + "enumValues": [ + "allow", + "deny" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Policy/Update-PfbSmbShareRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "full_control", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The state of the principal's Full Control access permission.", + "suggestedPowerShellType": "[string]", + "enumValues": [ + "allow", + "deny" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Policy/Update-PfbSmbShareRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "policy", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "The policy to which this rule belongs.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbSmbShareRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "principal", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The user or group who is the subject of this rule, and their domain.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/Update-PfbSmbShareRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "read", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The state of the principal's Read access permission.", + "suggestedPowerShellType": "[string]", + "enumValues": [ + "allow", + "deny" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Policy/Update-PfbSmbShareRule.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "id", + "name" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /snmp-agents", + "cmdlets": [ + "Update-PfbSnmpAgent" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + { + "name": "v2c", + "type": null, + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Monitoring/Update-PfbSnmpAgent.ps1", + "paramBlockLine": 32, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "v3", + "type": null, + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Monitoring/Update-PfbSnmpAgent.ps1", + "paramBlockLine": 32, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "version", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Version of the SNMP protocol to be used by an SNMP manager in communications with Purity's SNMP agent.", + "suggestedPowerShellType": "[string]", + "enumValues": [ + "v2c", + "v3" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Monitoring/Update-PfbSnmpAgent.ps1", + "paramBlockLine": 32, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "engine_id", + "id", + "name" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /snmp-managers", + "cmdlets": [ + "Update-PfbSnmpManager" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "id" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /ssh-certificate-authority-policies", + "cmdlets": [ + "Update-PfbSshCaPolicy" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "context", + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /sso/oidc/idps", + "cmdlets": [ + "Update-PfbOidcIdp" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "prn" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /sso/saml2/idps", + "cmdlets": [ + "Update-PfbSaml2Idp" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "id", + "prn" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /storage-class-tiering-policies", + "cmdlets": [ + "Update-PfbStorageClassTieringPolicy" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /subnets", + "cmdlets": [ + "Update-PfbSubnet" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "enabled", + "id", + "interfaces", + "name", + "services" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /support", + "cmdlets": [ + "Update-PfbSupport" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + { + "name": "edge_agent_update_enabled", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "The switch to enable opt-in for edge agent updates.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Support/Update-PfbSupport.ps1", + "paramBlockLine": 5, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "edge_management_enabled", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "The switch to enable opt-in for edge management.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Support/Update-PfbSupport.ps1", + "paramBlockLine": 5, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "phonehome_enabled", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "The switch to enable phonehome.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Support/Update-PfbSupport.ps1", + "paramBlockLine": 5, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "proxy", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Support/Update-PfbSupport.ps1", + "paramBlockLine": 5, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "remote_assist_active", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "The switch to open all remote-assist sessions.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Support/Update-PfbSupport.ps1", + "paramBlockLine": 5, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "remote_assist_duration", + "type": "integer", + "format": "int64", + "specRequired": false, + "synopsis": "Specifies the duration of the remote-assist session in milliseconds.", + "suggestedPowerShellType": "[long]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Support/Update-PfbSupport.ps1", + "paramBlockLine": 5, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "id", + "name", + "remote_assist_expires", + "remote_assist_opened", + "remote_assist_paths", + "remote_assist_status" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /support/verification-keys", + "cmdlets": [ + "Update-PfbSupportVerificationKey" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + { + "name": "signed_verification_key", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The text of the signed verification key.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Support/Update-PfbSupportVerificationKey.ps1", + "paramBlockLine": 28, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /syslog-servers/settings", + "cmdlets": [ + "Update-PfbSyslogServerSettings" + ], + "missingQueryParameters": [ + "ids", + "names" + ], + "missingBodyProperties": [ + { + "name": "ca_certificate", + "type": "object", + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Monitoring/Update-PfbSyslogServerSettings.ps1", + "paramBlockLine": 28, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "ca_certificate_group", + "type": "object", + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Monitoring/Update-PfbSyslogServerSettings.ps1", + "paramBlockLine": 28, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "id", + "name" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /targets", + "cmdlets": [ + "Update-PfbTarget" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "id", + "status", + "status_details" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /tls-policies", + "cmdlets": [ + "Update-PfbTlsPolicy" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /user-group-quota-policies", + "cmdlets": [ + "Update-PfbUserGroupQuotaPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "enabled", + "name" + ], + "readOnlyFields": [ + "context", + "id", + "is_local", + "policy_type", + "realms", + "version" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Policy/Update-PfbUserGroupQuotaPolicy.ps1", + "line": 36 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /user-group-quota-policies/rules", + "cmdlets": [ + "Update-PfbUserGroupQuotaPolicyRule" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "id", + "name" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /workloads", + "cmdlets": [ + "Update-PfbWorkload" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "destroyed" + ], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Destroyed", + "surface": "TypedUnresolved", + "file": "Public/Workloads/Update-PfbWorkload.ps1", + "line": 36 + } + ], + "escapeHatchOnly": [], + "caveat": "one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PATCH /worm-data-policies", + "cmdlets": [ + "Update-PfbWormPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "context", + "id", + "is_local", + "name", + "policy_type", + "realms" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /active-directory", + "cmdlets": [ + "New-PfbActiveDirectory" + ], + "missingQueryParameters": [ + "join_existing_account", + "names" + ], + "missingBodyProperties": [ + "ca_certificate", + "ca_certificate_group", + "computer_name", + "directory_servers", + "domain", + "encryption_types", + "fqdns", + "global_catalog_servers", + "join_ou", + "kerberos_servers", + "password", + "service_principal_names", + "user" + ], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Name", + "surface": "AttributesOnly", + "file": "Public/DirectoryService/New-PfbActiveDirectory.ps1", + "line": 40 + } + ], + "escapeHatchOnly": [ + "Name" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /admins/api-tokens", + "cmdlets": [ + "New-PfbApiToken" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /admins/management-access-policies", + "cmdlets": [ + "New-PfbAdminManagementAccessPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [ + { + "matchType": "endpoint", + "match": "management-access-policies", + "kind": "liveTestingHazard", + "note": "POST/PATCH/DELETE return 403 regardless of account; not an implementation bug", + "reference": null + } + ] + }, + { + "endpoint": "POST /admins/ssh-certificate-authority-policies", + "cmdlets": [ + "New-PfbAdminSshCaPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /api-clients", + "cmdlets": [ + "New-PfbApiClient" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + { + "name": "access_policies", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The access policies allowed for ID Tokens issued by this API client.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbApiClient.ps1", + "paramBlockLine": 36, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "access_token_ttl_in_ms", + "type": "integer", + "format": "int64", + "specRequired": false, + "synopsis": "The TTL (Time To Live) duration for which the exchanged access token is valid.", + "suggestedPowerShellType": "[long]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbApiClient.ps1", + "paramBlockLine": 36, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "issuer", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The name of the identity provider that will be issuing ID Tokens for this API client.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbApiClient.ps1", + "paramBlockLine": 36, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "max_role", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "Deprecated.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbApiClient.ps1", + "paramBlockLine": 36, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "public_key", + "type": "string", + "format": null, + "specRequired": true, + "synopsis": "The API client's PEM formatted (Base64 encoded) RSA public key.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbApiClient.ps1", + "paramBlockLine": 36, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /array-connections", + "cmdlets": [ + "New-PfbArrayConnection" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "context", + "id", + "os", + "status", + "type", + "version" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /arrays/erasures", + "cmdlets": [ + "New-PfbArrayErasure" + ], + "missingQueryParameters": [ + "eradicate_all_data", + "preserve_configuration_data", + "skip_phonehome_check" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /arrays/ssh-certificate-authority-policies", + "cmdlets": [ + "New-PfbArraySshCaPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /audit-file-systems-policies", + "cmdlets": [ + "New-PfbAuditFileSystemPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "control_type", + "enabled", + "location", + "log_targets", + "name", + "rules" + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Policy/New-PfbAuditFileSystemPolicy.ps1", + "line": 35 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /audit-file-systems-policies/members", + "cmdlets": [ + "New-PfbAuditFileSystemPolicyMember" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /audit-object-store-policies", + "cmdlets": [ + "New-PfbAuditObjectStorePolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "enabled", + "location", + "log_targets", + "name" + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Policy/New-PfbAuditObjectStorePolicy.ps1", + "line": 35 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /audit-object-store-policies/members", + "cmdlets": [ + "New-PfbAuditObjectStorePolicyMember" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /buckets", + "cmdlets": [ + "New-PfbBucket" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + { + "name": "bucket_type", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The bucket type for the bucket.", + "suggestedPowerShellType": "[string]", + "enumValues": [ + "classic", + "multi-site-writable" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Bucket/New-PfbBucket.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "index", + "hasAttributes": true + } + }, + { + "name": "eradication_config", + "type": null, + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Bucket/New-PfbBucket.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "index", + "hasAttributes": true + } + }, + { + "name": "hard_limit_enabled", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "If set to `true`, the bucket's size, as defined by `quota_limit`, is used as a hard limit quota.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Bucket/New-PfbBucket.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "index", + "hasAttributes": true + } + }, + { + "name": "object_lock_config", + "type": null, + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Bucket/New-PfbBucket.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "index", + "hasAttributes": true + } + }, + { + "name": "retention_lock", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "If set to `ratcheted`, then `object_lock_config.default_retention_mode` cannot be changed if set to `compliance`.", + "suggestedPowerShellType": "[string]", + "enumValues": [ + "unlocked", + "ratcheted" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Bucket/New-PfbBucket.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "index", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /buckets/audit-filters", + "cmdlets": [ + "New-PfbBucketAuditFilter" + ], + "missingQueryParameters": [ + "bucket_ids", + "bucket_names", + "context_names", + "names" + ], + "missingBodyProperties": [ + { + "name": "actions", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The list of ops to be audited by this filter.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/Bucket/New-PfbBucketAuditFilter.ps1", + "paramBlockLine": 38, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "s3_prefixes", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The list of object name prefixes.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Bucket/New-PfbBucketAuditFilter.ps1", + "paramBlockLine": 38, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /buckets/bucket-access-policies", + "cmdlets": [ + "New-PfbBucketAccessPolicy" + ], + "missingQueryParameters": [ + "bucket_ids", + "bucket_names", + "context_names" + ], + "missingBodyProperties": [ + { + "name": "rules", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Bucket/New-PfbBucketAccessPolicy.ps1", + "paramBlockLine": 36, + "payloadVariable": null, + "assignmentStyle": null, + "hasAttributes": false + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /buckets/bucket-access-policies/rules", + "cmdlets": [ + "New-PfbBucketAccessPolicyRule" + ], + "missingQueryParameters": [ + "bucket_ids", + "bucket_names", + "context_names", + "names" + ], + "missingBodyProperties": [ + { + "name": "actions", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The list of actions granted by this rule.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/Bucket/New-PfbBucketAccessPolicyRule.ps1", + "paramBlockLine": 42, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "principals", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "The principals to which this rule applies.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Bucket/New-PfbBucketAccessPolicyRule.ps1", + "paramBlockLine": 42, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "resources", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The list of resources which this rule applies to.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Bucket/New-PfbBucketAccessPolicyRule.ps1", + "paramBlockLine": 42, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "effect" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /buckets/cross-origin-resource-sharing-policies", + "cmdlets": [ + "New-PfbBucketCorsPolicy" + ], + "missingQueryParameters": [ + "bucket_ids", + "bucket_names", + "context_names" + ], + "missingBodyProperties": [ + { + "name": "rules", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Bucket/New-PfbBucketCorsPolicy.ps1", + "paramBlockLine": 36, + "payloadVariable": null, + "assignmentStyle": null, + "hasAttributes": false + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /buckets/cross-origin-resource-sharing-policies/rules", + "cmdlets": [ + "New-PfbBucketCorsPolicyRule" + ], + "missingQueryParameters": [ + "bucket_ids", + "bucket_names", + "context_names", + "names", + "policy_names" + ], + "missingBodyProperties": [ + { + "name": "allowed_headers", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "A list of headers that are permitted to be included in cross-origin requests to access a bucket.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Bucket/New-PfbBucketCorsPolicyRule.ps1", + "paramBlockLine": 32, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "allowed_methods", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "A list of HTTP methods that are permitted for cross-origin requests to access a bucket.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Bucket/New-PfbBucketCorsPolicyRule.ps1", + "paramBlockLine": 32, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "allowed_origins", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "A list of origins (domains) that are permitted to make cross-origin requests to access a bucket.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Bucket/New-PfbBucketCorsPolicyRule.ps1", + "paramBlockLine": 32, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /certificates", + "cmdlets": [ + "New-PfbCertificate" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + { + "name": "certificate", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The text of the certificate.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Certificate/New-PfbCertificate.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "certificate_type", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The type of certificate.", + "suggestedPowerShellType": "[string]", + "enumValues": [ + "appliance", + "external" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Certificate/New-PfbCertificate.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "common_name", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The common name field listed in the certificate.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Certificate/New-PfbCertificate.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "country", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The country field listed in the certificate.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Certificate/New-PfbCertificate.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "days", + "type": "integer", + "format": "int32", + "specRequired": false, + "synopsis": "The number of days that the self-signed certificate is valid.", + "suggestedPowerShellType": "[int]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Certificate/New-PfbCertificate.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "email", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The email field listed in the certificate.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Certificate/New-PfbCertificate.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "intermediate_certificate", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Intermediate certificate chains.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Certificate/New-PfbCertificate.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "key_algorithm", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The key algorithm used to generate the certificate.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Certificate/New-PfbCertificate.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "key_size", + "type": "integer", + "format": "int32", + "specRequired": false, + "synopsis": "The size (in bits) of the private key for the certificate.", + "suggestedPowerShellType": "[int]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Certificate/New-PfbCertificate.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "locality", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The locality field listed in the certificate.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Certificate/New-PfbCertificate.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "organization", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The organization field listed in the certificate.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Certificate/New-PfbCertificate.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "organizational_unit", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The organizational unit field listed in the certificate.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Certificate/New-PfbCertificate.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "passphrase", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The passphrase used to encrypt `private_key`.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Certificate/New-PfbCertificate.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "private_key", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The text of the private key.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Certificate/New-PfbCertificate.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "state", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The state/province field listed in the certificate.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/Certificate/New-PfbCertificate.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "subject_alternative_names", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The alternative names that are secured by this certificate.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Certificate/New-PfbCertificate.ps1", + "paramBlockLine": 36, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "issued_by", + "issued_to", + "realms", + "status", + "valid_from", + "valid_to" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /certificates/certificate-signing-requests", + "cmdlets": [ + "New-PfbCertificateSigningRequest" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + "certificate", + "common_name", + "country", + "email", + "locality", + "organization", + "organizational_unit", + "state", + "subject_alternative_names" + ], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Name", + "surface": "AttributesOnly", + "file": "Public/Certificate/New-PfbCertificateSigningRequest.ps1", + "line": 29 + } + ], + "escapeHatchOnly": [ + "Name" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /data-eviction-policies", + "cmdlets": [ + "New-PfbDataEvictionPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "enabled", + "location", + "name" + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Disabled", + "surface": "TypedUnresolved", + "file": "Public/DataEviction/New-PfbDataEvictionPolicy.ps1", + "line": 31 + } + ], + "escapeHatchOnly": [], + "caveat": "one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /data-eviction-policies/file-systems", + "cmdlets": [ + "Add-PfbDataEvictionPolicyFileSystem" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /directory-services/local/directory-services", + "cmdlets": [ + "New-PfbLocalDirectoryService" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /directory-services/local/groups", + "cmdlets": [ + "New-PfbLocalGroup" + ], + "missingQueryParameters": [ + "context_names", + "local_directory_service_ids", + "local_directory_service_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /directory-services/local/groups/members", + "cmdlets": [ + "New-PfbLocalGroupMember" + ], + "missingQueryParameters": [ + "context_names", + "group_gids", + "group_sids", + "local_directory_service_ids" + ], + "missingBodyProperties": [ + "members" + ], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Member", + "surface": "TypedUnresolved", + "file": "Public/DirectoryService/New-PfbLocalGroupMember.ps1", + "line": 35 + } + ], + "escapeHatchOnly": [], + "caveat": "one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /directory-services/roles", + "cmdlets": [ + "New-PfbDirectoryServiceRole" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + { + "name": "group", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Common Name (CN) of the directory service group containing users with authority level of the specified role name.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/DirectoryService/New-PfbDirectoryServiceRole.ps1", + "paramBlockLine": 33, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "group_base", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Specifies where the configured group is located in the directory tree.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/DirectoryService/New-PfbDirectoryServiceRole.ps1", + "paramBlockLine": 33, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "management_access_policies", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "List of management access policies associated with the directory service role.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/DirectoryService/New-PfbDirectoryServiceRole.ps1", + "paramBlockLine": 33, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "role", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "Deprecated.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/DirectoryService/New-PfbDirectoryServiceRole.ps1", + "paramBlockLine": 33, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /dns", + "cmdlets": [ + "New-PfbDns" + ], + "missingQueryParameters": [ + "context_names", + "names" + ], + "missingBodyProperties": [ + "ca_certificate", + "ca_certificate_group", + "domain", + "nameservers", + "services", + "sources" + ], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Name", + "surface": "AttributesOnly", + "file": "Public/Network/New-PfbDns.ps1", + "line": 29 + } + ], + "escapeHatchOnly": [ + "Name" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /file-system-exports", + "cmdlets": [ + "New-PfbFileSystemExport" + ], + "missingQueryParameters": [ + "context_names", + "member_ids", + "policy_ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /file-system-replica-links", + "cmdlets": [ + "New-PfbFileSystemReplicaLink" + ], + "missingQueryParameters": [ + "context_names", + "ids", + "local_file_system_ids", + "remote_ids" + ], + "missingBodyProperties": [ + { + "name": "direction", + "type": null, + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object]", + "enumValues": [ + "inbound", + "outbound" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Replication/New-PfbFileSystemReplicaLink.ps1", + "paramBlockLine": 55, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": false + } + }, + { + "name": "link_type", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Type of the replica link.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Replication/New-PfbFileSystemReplicaLink.ps1", + "paramBlockLine": 55, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": false + } + }, + { + "name": "local_file_system", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "Reference to a local file system.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Replication/New-PfbFileSystemReplicaLink.ps1", + "paramBlockLine": 55, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": false + } + }, + { + "name": "policies", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Replication/New-PfbFileSystemReplicaLink.ps1", + "paramBlockLine": 55, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": false + } + }, + { + "name": "remote", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "Reference to a remote array or realm.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Replication/New-PfbFileSystemReplicaLink.ps1", + "paramBlockLine": 55, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": false + } + }, + { + "name": "remote_file_system", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "Reference to a remote file system.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Replication/New-PfbFileSystemReplicaLink.ps1", + "paramBlockLine": 55, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": false + } + } + ], + "readOnlyFields": [ + "context", + "id", + "lag", + "recovery_point", + "status", + "status_details" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /file-system-replica-links/policies", + "cmdlets": [ + "New-PfbFileSystemReplicaLinkPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /file-system-snapshots", + "cmdlets": [ + "New-PfbFileSystemSnapshot" + ], + "missingQueryParameters": [ + "context_names", + "source_ids", + "source_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "SourceName", + "surface": "TypedUnresolved", + "file": "Public/FileSystemSnapshot/New-PfbFileSystemSnapshot.ps1", + "line": 36 + } + ], + "escapeHatchOnly": [], + "caveat": "one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /file-systems", + "cmdlets": [ + "New-PfbFileSystem" + ], + "missingQueryParameters": [ + "context_names", + "default_exports", + "discard_non_snapshotted_data", + "include_snapshot", + "overwrite", + "policy_ids", + "policy_names" + ], + "missingBodyProperties": [ + "fast_remove_directory_enabled", + "hard_limit_enabled", + "http", + "multi_protocol", + "nfs", + "node_group", + "smb", + "snapshot_directory_enabled", + "workload", + "writable" + ], + "readOnlyFields": [ + "requested_promotion_state" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "FastRemoveDirectoryEnabled", + "surface": "AttributesOnly", + "file": "Public/FileSystem/New-PfbFileSystem.ps1", + "line": 139 + }, + { + "parameter": "HardLimit", + "surface": "AttributesOnly", + "file": "Public/FileSystem/New-PfbFileSystem.ps1", + "line": 93 + }, + { + "parameter": "Http", + "surface": "AttributesOnly", + "file": "Public/FileSystem/New-PfbFileSystem.ps1", + "line": 126 + }, + { + "parameter": "MultiProtocolAccessControlStyle", + "surface": "AttributesOnly", + "file": "Public/FileSystem/New-PfbFileSystem.ps1", + "line": 129 + }, + { + "parameter": "Nfs", + "surface": "AttributesOnly", + "file": "Public/FileSystem/New-PfbFileSystem.ps1", + "line": 102 + }, + { + "parameter": "NfsExportPolicy", + "surface": "AttributesOnly", + "file": "Public/FileSystem/New-PfbFileSystem.ps1", + "line": 114 + }, + { + "parameter": "NfsRules", + "surface": "AttributesOnly", + "file": "Public/FileSystem/New-PfbFileSystem.ps1", + "line": 111 + }, + { + "parameter": "NfsV3", + "surface": "AttributesOnly", + "file": "Public/FileSystem/New-PfbFileSystem.ps1", + "line": 105 + }, + { + "parameter": "NfsV41", + "surface": "AttributesOnly", + "file": "Public/FileSystem/New-PfbFileSystem.ps1", + "line": 108 + }, + { + "parameter": "SafeguardAcls", + "surface": "AttributesOnly", + "file": "Public/FileSystem/New-PfbFileSystem.ps1", + "line": 133 + }, + { + "parameter": "Smb", + "surface": "AttributesOnly", + "file": "Public/FileSystem/New-PfbFileSystem.ps1", + "line": 117 + }, + { + "parameter": "SmbClientPolicy", + "surface": "AttributesOnly", + "file": "Public/FileSystem/New-PfbFileSystem.ps1", + "line": 123 + }, + { + "parameter": "SmbSharePolicy", + "surface": "AttributesOnly", + "file": "Public/FileSystem/New-PfbFileSystem.ps1", + "line": 120 + }, + { + "parameter": "SnapshotDirectoryEnabled", + "surface": "AttributesOnly", + "file": "Public/FileSystem/New-PfbFileSystem.ps1", + "line": 136 + }, + { + "parameter": "Writable", + "surface": "AttributesOnly", + "file": "Public/FileSystem/New-PfbFileSystem.ps1", + "line": 150 + } + ], + "escapeHatchOnly": [ + "FastRemoveDirectoryEnabled", + "HardLimit", + "Http", + "MultiProtocolAccessControlStyle", + "Nfs", + "NfsExportPolicy", + "NfsRules", + "NfsV3", + "NfsV41", + "SafeguardAcls", + "Smb", + "SmbClientPolicy", + "SmbSharePolicy", + "SnapshotDirectoryEnabled", + "Writable" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /file-systems/audit-policies", + "cmdlets": [ + "New-PfbFileSystemAuditPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /file-systems/locks/nlm-reclamations", + "cmdlets": [ + "New-PfbNlmReclamation" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /file-systems/policies", + "cmdlets": [ + "New-PfbFileSystemPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /file-systems/user-group-quota-policies", + "cmdlets": [ + "New-PfbFileSystemUserGroupQuotaPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /fleets", + "cmdlets": [ + "New-PfbFleet" + ], + "missingQueryParameters": [ + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Name", + "surface": "AttributesOnly", + "file": "Public/Replication/New-PfbFleet.ps1", + "line": 30 + } + ], + "escapeHatchOnly": [ + "Name" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /keytabs", + "cmdlets": [ + "New-PfbKeytab" + ], + "missingQueryParameters": [ + "name_prefixes" + ], + "missingBodyProperties": [ + { + "name": "source", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "A reference to the Active Directory configuration for the computer account whose keys will be rotated in order to create new keytabs for all of its registered service principal names.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Misc/New-PfbKeytab.ps1", + "paramBlockLine": 26, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /keytabs/upload", + "cmdlets": [ + "New-PfbKeytabUpload" + ], + "missingQueryParameters": [ + "name_prefixes" + ], + "missingBodyProperties": [ + { + "name": "keytab_file", + "type": null, + "format": null, + "specRequired": true, + "synopsis": null, + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Misc/New-PfbKeytabUpload.ps1", + "paramBlockLine": 28, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /legal-holds", + "cmdlets": [ + "New-PfbLegalHold" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + { + "name": "description", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The description of the legal hold instance.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Misc/New-PfbLegalHold.ps1", + "paramBlockLine": 36, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "id", + "name", + "realms" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /lifecycle-rules", + "cmdlets": [ + "New-PfbLifecycleRule" + ], + "missingQueryParameters": [ + "confirm_date", + "context_names" + ], + "missingBodyProperties": [ + { + "name": "abort_incomplete_multipart_uploads_after", + "type": "integer", + "format": "int64", + "specRequired": false, + "synopsis": "Duration of time after which incomplete multipart uploads will be aborted.", + "suggestedPowerShellType": "[long]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Misc/New-PfbLifecycleRule.ps1", + "paramBlockLine": 33, + "payloadVariable": "body", + "assignmentStyle": "index", + "hasAttributes": true + } + }, + { + "name": "keep_current_version_for", + "type": "integer", + "format": "int64", + "specRequired": false, + "synopsis": "Time after which current versions will be marked expired.", + "suggestedPowerShellType": "[long]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Misc/New-PfbLifecycleRule.ps1", + "paramBlockLine": 33, + "payloadVariable": "body", + "assignmentStyle": "index", + "hasAttributes": true + } + }, + { + "name": "keep_current_version_until", + "type": "integer", + "format": "int64", + "specRequired": false, + "synopsis": "Time after which current versions will be marked expired.", + "suggestedPowerShellType": "[long]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Misc/New-PfbLifecycleRule.ps1", + "paramBlockLine": 33, + "payloadVariable": "body", + "assignmentStyle": "index", + "hasAttributes": true + } + }, + { + "name": "keep_previous_version_for", + "type": "integer", + "format": "int64", + "specRequired": false, + "synopsis": "Time after which previous versions will be marked expired.", + "suggestedPowerShellType": "[long]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Misc/New-PfbLifecycleRule.ps1", + "paramBlockLine": 33, + "payloadVariable": "body", + "assignmentStyle": "index", + "hasAttributes": true + } + }, + { + "name": "prefix", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Object key prefix identifying one or more objects in the bucket.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Misc/New-PfbLifecycleRule.ps1", + "paramBlockLine": 33, + "payloadVariable": "body", + "assignmentStyle": "index", + "hasAttributes": true + } + }, + { + "name": "rule_id", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Identifier for the rule that is unique to the bucket that it applies to.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Misc/New-PfbLifecycleRule.ps1", + "paramBlockLine": 33, + "payloadVariable": "body", + "assignmentStyle": "index", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /link-aggregation-groups", + "cmdlets": [ + "New-PfbLag" + ], + "missingQueryParameters": [ + "names" + ], + "missingBodyProperties": [ + "ports" + ], + "readOnlyFields": [ + "id", + "lag_speed", + "mac_address", + "name", + "port_speed", + "status" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Name", + "surface": "AttributesOnly", + "file": "Public/Misc/New-PfbLag.ps1", + "line": 29 + } + ], + "escapeHatchOnly": [ + "Name" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /log-targets/file-systems", + "cmdlets": [ + "New-PfbLogTargetFileSystem" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + { + "name": "file_system", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "The target filesystem where audit logs will be stored.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Monitoring/New-PfbLogTargetFileSystem.ps1", + "paramBlockLine": 36, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "keep_for", + "type": "integer", + "format": "int64", + "specRequired": false, + "synopsis": "Specifies the period that audit logs are retained before they are deleted, in milliseconds.", + "suggestedPowerShellType": "[long]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Monitoring/New-PfbLogTargetFileSystem.ps1", + "paramBlockLine": 36, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "keep_size", + "type": "integer", + "format": "int64", + "specRequired": false, + "synopsis": "Specifies the maximum size of audit logs to be retained.", + "suggestedPowerShellType": "[long]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Monitoring/New-PfbLogTargetFileSystem.ps1", + "paramBlockLine": 36, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "name", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "A user-specified name.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/Monitoring/New-PfbLogTargetFileSystem.ps1", + "paramBlockLine": 36, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "id" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /log-targets/object-store", + "cmdlets": [ + "New-PfbLogTargetObjectStore" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + { + "name": "bucket", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "Reference to the bucket where audit logs will be stored.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Monitoring/New-PfbLogTargetObjectStore.ps1", + "paramBlockLine": 36, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "log_name_prefix", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "The prefix of the audit log object.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Monitoring/New-PfbLogTargetObjectStore.ps1", + "paramBlockLine": 36, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "log_rotate", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "The threshold after which the audit log object will be rotated.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Monitoring/New-PfbLogTargetObjectStore.ps1", + "paramBlockLine": 36, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "name", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "A user-specified name.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/Monitoring/New-PfbLogTargetObjectStore.ps1", + "paramBlockLine": 36, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "id" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /maintenance-windows", + "cmdlets": [ + "New-PfbMaintenanceWindow" + ], + "missingQueryParameters": [ + "names" + ], + "missingBodyProperties": [ + { + "name": "timeout", + "type": "integer", + "format": "int32", + "specRequired": false, + "synopsis": "Duration of a maintenance window measured in milliseconds.", + "suggestedPowerShellType": "[int]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Misc/New-PfbMaintenanceWindow.ps1", + "paramBlockLine": 28, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /management-access-policies", + "cmdlets": [ + "New-PfbManagementAccessPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + { + "name": "aggregation_strategy", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "When this is set to `least-common-permissions`, any users to whom this policy applies can receive no access rights exceeding those defined in this policy's capability and resource.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbManagementAccessPolicy.ps1", + "paramBlockLine": 32, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "enabled", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "If `true`, the policy is enabled.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbManagementAccessPolicy.ps1", + "paramBlockLine": 32, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "location", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "Reference to the array where the policy is defined.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbManagementAccessPolicy.ps1", + "paramBlockLine": 32, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "name", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "A user-specified name.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/Admin/New-PfbManagementAccessPolicy.ps1", + "paramBlockLine": 32, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "rules", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "All of the rules that are part of this policy.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbManagementAccessPolicy.ps1", + "paramBlockLine": 32, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [ + { + "matchType": "endpoint", + "match": "management-access-policies", + "kind": "liveTestingHazard", + "note": "POST/PATCH/DELETE return 403 regardless of account; not an implementation bug", + "reference": null + } + ] + }, + { + "endpoint": "POST /management-access-policies/admins", + "cmdlets": [ + "New-PfbManagementAccessPolicyAdmin" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [ + { + "matchType": "endpoint", + "match": "management-access-policies", + "kind": "liveTestingHazard", + "note": "POST/PATCH/DELETE return 403 regardless of account; not an implementation bug", + "reference": null + } + ] + }, + { + "endpoint": "POST /network-access-policies/rules", + "cmdlets": [ + "New-PfbNetworkAccessRule" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "id", + "name" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /network-interfaces", + "cmdlets": [ + "New-PfbNetworkInterface" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + { + "name": "rdma_enabled", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "If `true` indicated that RDMA is enabled on the network interface.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Network/New-PfbNetworkInterface.ps1", + "paramBlockLine": 66, + "payloadVariable": "body", + "assignmentStyle": "index", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "id", + "name", + "realms" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /nfs-export-policies", + "cmdlets": [ + "New-PfbNfsExportPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "enabled", + "location", + "name", + "rules" + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Policy/New-PfbNfsExportPolicy.ps1", + "line": 36 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /nfs-export-policies/rules", + "cmdlets": [ + "New-PfbNfsExportRule" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "context", + "id", + "name", + "policy_version" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /node-groups", + "cmdlets": [ + "New-PfbNodeGroup" + ], + "missingQueryParameters": [ + "names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Name", + "surface": "AttributesOnly", + "file": "Public/Node/New-PfbNodeGroup.ps1", + "line": 30 + } + ], + "escapeHatchOnly": [ + "Name" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /object-store-access-keys", + "cmdlets": [ + "New-PfbObjectStoreAccessKey" + ], + "missingQueryParameters": [ + "context_names", + "names" + ], + "missingBodyProperties": [ + { + "name": "secret_access_key", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The secret access key to import from another FlashBlade.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreAccessKey.ps1", + "paramBlockLine": 17, + "payloadVariable": "body", + "assignmentStyle": "literal", + "hasAttributes": false + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /object-store-access-policies", + "cmdlets": [ + "New-PfbObjectStoreAccessPolicy" + ], + "missingQueryParameters": [ + "context_names", + "enforce_action_restrictions" + ], + "missingBodyProperties": [ + { + "name": "description", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "A description of the policy, optionally specified when the policy is created.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreAccessPolicy.ps1", + "paramBlockLine": 58, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "rules", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreAccessPolicy.ps1", + "paramBlockLine": 58, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /object-store-access-policies/object-store-roles", + "cmdlets": [ + "New-PfbObjectStoreAccessPolicyRole" + ], + "missingQueryParameters": [ + "context_names", + "member_ids", + "policy_ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /object-store-access-policies/object-store-users", + "cmdlets": [ + "New-PfbObjectStoreAccessPolicyUser" + ], + "missingQueryParameters": [ + "context_names", + "member_ids", + "policy_ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /object-store-access-policies/rules", + "cmdlets": [ + "New-PfbObjectStoreAccessPolicyRule" + ], + "missingQueryParameters": [ + "context_names", + "enforce_action_restrictions", + "names", + "policy_ids" + ], + "missingBodyProperties": [ + { + "name": "actions", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The list of actions granted by this rule.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreAccessPolicyRule.ps1", + "paramBlockLine": 45, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "conditions", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "Conditions used to limit the scope which this rule applies to.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreAccessPolicyRule.ps1", + "paramBlockLine": 45, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "effect", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Effect of this rule.", + "suggestedPowerShellType": "[string]", + "enumValues": [ + "allow", + "deny" + ], + "enumStatus": "matched", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreAccessPolicyRule.ps1", + "paramBlockLine": 45, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "resources", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The list of resources which this rule applies to.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreAccessPolicyRule.ps1", + "paramBlockLine": 45, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /object-store-account-exports", + "cmdlets": [ + "New-PfbObjectStoreAccountExport" + ], + "missingQueryParameters": [ + "context_names", + "member_ids", + "member_names", + "policy_ids", + "policy_names" + ], + "missingBodyProperties": [ + { + "name": "export_enabled", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "If set to `true`, the account export is enabled.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreAccountExport.ps1", + "paramBlockLine": 38, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "server", + "type": null, + "format": null, + "specRequired": true, + "synopsis": "Reference to the server the export will be visible on.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreAccountExport.ps1", + "paramBlockLine": 38, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /object-store-accounts", + "cmdlets": [ + "New-PfbObjectStoreAccount" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + { + "name": "account_exports", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "A list of exports to be created for the account.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreAccount.ps1", + "paramBlockLine": 17, + "payloadVariable": null, + "assignmentStyle": null, + "hasAttributes": false + } + }, + { + "name": "bucket_defaults", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "Default settings to be applied to newly created buckets associated with this account.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreAccount.ps1", + "paramBlockLine": 17, + "payloadVariable": null, + "assignmentStyle": null, + "hasAttributes": false + } + }, + { + "name": "hard_limit_enabled", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "If set to `true`, the account's size, as defined by `quota_limit`, is used as a hard limit quota.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreAccount.ps1", + "paramBlockLine": 17, + "payloadVariable": null, + "assignmentStyle": null, + "hasAttributes": false + } + }, + { + "name": "quota_limit", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The effective quota limit to be applied against the size of the account, displayed in bytes.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreAccount.ps1", + "paramBlockLine": 17, + "payloadVariable": null, + "assignmentStyle": null, + "hasAttributes": false + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /object-store-remote-credentials", + "cmdlets": [ + "New-PfbObjectStoreRemoteCredential" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + { + "name": "access_key_id", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Access Key ID to be used when connecting to a remote object store.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreRemoteCredential.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "secret_access_key", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "Secret Access Key to be used when connecting to a remote object store.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreRemoteCredential.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /object-store-roles", + "cmdlets": [ + "New-PfbObjectStoreRole" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + { + "name": "max_session_duration", + "type": "integer", + "format": null, + "specRequired": false, + "synopsis": "Maximum session duration in milliseconds.", + "suggestedPowerShellType": "[int]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreRole.ps1", + "paramBlockLine": 37, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /object-store-roles/object-store-access-policies", + "cmdlets": [ + "New-PfbObjectStoreRoleAccessPolicy" + ], + "missingQueryParameters": [ + "context_names", + "member_ids", + "policy_ids", + "policy_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /object-store-roles/object-store-trust-policies/rules", + "cmdlets": [ + "New-PfbObjectStoreTrustPolicyRule" + ], + "missingQueryParameters": [ + "context_names", + "names", + "role_ids", + "role_names" + ], + "missingBodyProperties": [ + { + "name": "actions", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The list of role-assumption actions granted by this rule to the respective role.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreTrustPolicyRule.ps1", + "paramBlockLine": 45, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "conditions", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "Conditions used to limit the scope which this rule applies to.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreTrustPolicyRule.ps1", + "paramBlockLine": 45, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "policy", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "The policy to which this rule belongs.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreTrustPolicyRule.ps1", + "paramBlockLine": 45, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "principals", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "List of Identity Providers", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreTrustPolicyRule.ps1", + "paramBlockLine": 45, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "effect" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /object-store-users", + "cmdlets": [ + "New-PfbObjectStoreUser" + ], + "missingQueryParameters": [ + "context_names", + "full_access" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /object-store-users/object-store-access-policies", + "cmdlets": [ + "New-PfbObjectStoreUserAccessPolicy" + ], + "missingQueryParameters": [ + "context_names", + "member_ids", + "policy_ids" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /object-store-virtual-hosts", + "cmdlets": [ + "New-PfbObjectStoreVirtualHost" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + { + "name": "attached_servers", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "A list of servers which are allowed to use this virtual host.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/ObjectStore/New-PfbObjectStoreVirtualHost.ps1", + "paramBlockLine": 42, + "payloadVariable": "body", + "assignmentStyle": "index", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "context", + "id", + "name", + "realms" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /policies", + "cmdlets": [ + "New-PfbPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "enabled", + "location", + "name" + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms", + "retention_lock" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Policy/New-PfbPolicy.ps1", + "line": 23 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /policies/file-system-replica-links", + "cmdlets": [ + "New-PfbPolicyFileSystemReplicaLink" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /policies/file-systems", + "cmdlets": [ + "New-PfbPolicyFileSystem" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /presets/workload", + "cmdlets": [ + "New-PfbPresetWorkload" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + { + "name": "description", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "A brief description of the workload the preset will configure.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/New-PfbPresetWorkload.ps1", + "paramBlockLine": 44, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "directory_configurations", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The file systems and managed directories that will be provisioned by the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/New-PfbPresetWorkload.ps1", + "paramBlockLine": 44, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "export_configurations", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "NFS, SMB, and SMB share policy configuration to be specified in file system and directory exports.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/New-PfbPresetWorkload.ps1", + "paramBlockLine": 44, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "parameters", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The parameters to prompt the user when they deploy workloads from the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/New-PfbPresetWorkload.ps1", + "paramBlockLine": 44, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "periodic_replication_configurations", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The periodic replication configurations that can be applied to storage resources (such as volumes) within the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/New-PfbPresetWorkload.ps1", + "paramBlockLine": 44, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "placement_configurations", + "type": "array", + "format": null, + "specRequired": true, + "synopsis": "The placement configurations that can be applied to storage resources (such as volumes) within the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/New-PfbPresetWorkload.ps1", + "paramBlockLine": 44, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "platform_features", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object[]]", + "enumValues": [ + "fa_block", + "fa_file", + "fb_file" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Presets/New-PfbPresetWorkload.ps1", + "paramBlockLine": 44, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "qos_configurations", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The QoS configurations that can be applied to storage resources (such as volumes) within the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/New-PfbPresetWorkload.ps1", + "paramBlockLine": 44, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "quota_configurations", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The quota configurations that can be applied to storage resources (such as file systems and directories) within the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/New-PfbPresetWorkload.ps1", + "paramBlockLine": 44, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "snapshot_configurations", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The snapshot configurations that can be applied to storage resources (such as volumes) within the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/New-PfbPresetWorkload.ps1", + "paramBlockLine": 44, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "volume_configurations", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The volumes that will be provisioned by the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/New-PfbPresetWorkload.ps1", + "paramBlockLine": 44, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "workload_tags", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The tags that will be associated with workloads provisioned by the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/New-PfbPresetWorkload.ps1", + "paramBlockLine": 44, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "workload_type", + "type": "string", + "format": null, + "specRequired": true, + "synopsis": "The type of workload the preset will configure.", + "suggestedPowerShellType": "[string]", + "enumValues": [ + "Clarity", + "Epic", + "Exchange", + "File", + "MsSQL", + "MySQL", + "Oracle", + "PostgreSQL", + "SAP-Hana", + "SAP", + "VDI", + "VSI", + "Wfs", + "Zerto", + "Custom" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Presets/New-PfbPresetWorkload.ps1", + "paramBlockLine": 44, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "revision" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /public-keys", + "cmdlets": [ + "New-PfbPublicKey" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + { + "name": "public_key", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The text of the public key.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbPublicKey.ps1", + "paramBlockLine": 37, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /qos-policies", + "cmdlets": [ + "New-PfbQosPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + { + "name": "enabled", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "If `true`, the policy is enabled.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/New-PfbQosPolicy.ps1", + "paramBlockLine": 33, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "location", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "Reference to the array where the policy is defined.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/New-PfbQosPolicy.ps1", + "paramBlockLine": 33, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "max_total_bytes_per_sec", + "type": "integer", + "format": "int64", + "specRequired": false, + "synopsis": "The maximum allowed bytes/s totaled across all the clients.", + "suggestedPowerShellType": "[long]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/New-PfbQosPolicy.ps1", + "paramBlockLine": 33, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "max_total_ops_per_sec", + "type": "integer", + "format": "int64", + "specRequired": false, + "synopsis": "The maximum allowed operations/s totaled across all the clients.", + "suggestedPowerShellType": "[long]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Policy/New-PfbQosPolicy.ps1", + "paramBlockLine": 33, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "name", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "A user-specified name.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/Policy/New-PfbQosPolicy.ps1", + "paramBlockLine": 33, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "context", + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /qos-policies/members", + "cmdlets": [ + "New-PfbQosPolicyMember" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /quotas/groups", + "cmdlets": [ + "New-PfbQuotaGroup" + ], + "missingQueryParameters": [ + "context_names", + "file_system_ids", + "file_system_names", + "gids", + "group_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "name" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /quotas/users", + "cmdlets": [ + "New-PfbQuotaUser" + ], + "missingQueryParameters": [ + "context_names", + "file_system_ids", + "file_system_names", + "uids", + "user_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "name" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "FileSystemName", + "surface": "AttributesOnly", + "file": "Public/Quota/New-PfbQuotaUser.ps1", + "line": 42 + }, + { + "parameter": "UserId", + "surface": "AttributesOnly", + "file": "Public/Quota/New-PfbQuotaUser.ps1", + "line": 44 + }, + { + "parameter": "UserName", + "surface": "AttributesOnly", + "file": "Public/Quota/New-PfbQuotaUser.ps1", + "line": 43 + } + ], + "escapeHatchOnly": [ + "FileSystemName", + "UserId", + "UserName" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /realms", + "cmdlets": [ + "New-PfbRealm" + ], + "missingQueryParameters": [ + "without_default_access_list" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /s3-export-policies", + "cmdlets": [ + "New-PfbS3ExportPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "enabled", + "rules" + ], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Policy/New-PfbS3ExportPolicy.ps1", + "line": 36 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /s3-export-policies/rules", + "cmdlets": [ + "New-PfbS3ExportRule" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /servers", + "cmdlets": [ + "New-PfbServer" + ], + "missingQueryParameters": [ + "create_ds", + "create_local_directory_service" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "CreateDirectoryService", + "surface": "AttributesOnly", + "file": "Public/Server/New-PfbServer.ps1", + "line": 40 + } + ], + "escapeHatchOnly": [ + "CreateDirectoryService" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /smb-client-policies", + "cmdlets": [ + "New-PfbSmbClientPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "access_based_enumeration_enabled", + "enabled", + "location", + "name", + "rules" + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Policy/New-PfbSmbClientPolicy.ps1", + "line": 36 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /smb-client-policies/rules", + "cmdlets": [ + "New-PfbSmbClientRule" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "id", + "name" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /smb-share-policies", + "cmdlets": [ + "New-PfbSmbSharePolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "enabled", + "location", + "name", + "rules" + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Policy/New-PfbSmbSharePolicy.ps1", + "line": 36 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /smb-share-policies/rules", + "cmdlets": [ + "New-PfbSmbShareRule" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [ + "id", + "name" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /snmp-managers", + "cmdlets": [ + "New-PfbSnmpManager" + ], + "missingQueryParameters": [ + "names" + ], + "missingBodyProperties": [ + "host", + "notification", + "v2c", + "v3", + "version" + ], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Name", + "surface": "AttributesOnly", + "file": "Public/Monitoring/New-PfbSnmpManager.ps1", + "line": 33 + } + ], + "escapeHatchOnly": [ + "Name" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /software-check", + "cmdlets": [ + "New-PfbSoftwareCheck" + ], + "missingQueryParameters": [ + "software_names", + "software_versions" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /ssh-certificate-authority-policies", + "cmdlets": [ + "New-PfbSshCaPolicy" + ], + "missingQueryParameters": [ + "names" + ], + "missingBodyProperties": [ + "enabled", + "location", + "name", + "signing_authority", + "static_authorized_principals" + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Name", + "surface": "AttributesOnly", + "file": "Public/Policy/New-PfbSshCaPolicy.ps1", + "line": 30 + } + ], + "escapeHatchOnly": [ + "Name" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /ssh-certificate-authority-policies/admins", + "cmdlets": [ + "New-PfbSshCaPolicyAdmin" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /ssh-certificate-authority-policies/arrays", + "cmdlets": [ + "New-PfbSshCaPolicyArray" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /sso/oidc/idps", + "cmdlets": [ + "New-PfbOidcIdp" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + { + "name": "enabled", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "If set to `true`, the OIDC SSO configuration is enabled.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbOidcIdp.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "idp", + "type": null, + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbOidcIdp.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "services", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "Services that the OIDC SSO authentication is used for.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/Admin/New-PfbOidcIdp.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "prn" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /sso/saml2/idps", + "cmdlets": [ + "New-PfbSaml2Idp" + ], + "missingQueryParameters": [], + "missingBodyProperties": [ + { + "name": "array_url", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "The URL of the array.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbSaml2Idp.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "binding", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "SAML2 binding to use for the request from Flashblade to the Identity Provider.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbSaml2Idp.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "enabled", + "type": "boolean", + "format": null, + "specRequired": false, + "synopsis": "If set to `true`, the SAML2 SSO configuration is enabled.", + "suggestedPowerShellType": "[bool]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbSaml2Idp.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "idp", + "type": null, + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbSaml2Idp.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "management", + "type": null, + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbSaml2Idp.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "services", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "Services that the SAML2 SSO authentication is used for.", + "suggestedPowerShellType": "[string[]]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/Admin/New-PfbSaml2Idp.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + }, + { + "name": "sp", + "type": null, + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Admin/New-PfbSaml2Idp.ps1", + "paramBlockLine": 39, + "payloadVariable": "body", + "assignmentStyle": "unknown", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "prn" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /storage-class-tiering-policies", + "cmdlets": [ + "New-PfbStorageClassTieringPolicy" + ], + "missingQueryParameters": [ + "names" + ], + "missingBodyProperties": [ + "archival_rules", + "enabled", + "location", + "name", + "retrieval_rules" + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Name", + "surface": "AttributesOnly", + "file": "Public/Policy/New-PfbStorageClassTieringPolicy.ps1", + "line": 30 + } + ], + "escapeHatchOnly": [ + "Name" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /subnets", + "cmdlets": [ + "New-PfbSubnet" + ], + "missingQueryParameters": [], + "missingBodyProperties": [], + "readOnlyFields": [ + "enabled", + "id", + "interfaces", + "name", + "services" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /support-diagnostics", + "cmdlets": [ + "New-PfbSupportDiagnostics" + ], + "missingQueryParameters": [ + "analysis_period_end_time", + "analysis_period_start_time" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /syslog-servers", + "cmdlets": [ + "New-PfbSyslogServer" + ], + "missingQueryParameters": [ + "names" + ], + "missingBodyProperties": [ + "services", + "sources", + "uri" + ], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Name", + "surface": "AttributesOnly", + "file": "Public/Monitoring/New-PfbSyslogServer.ps1", + "line": 32 + } + ], + "escapeHatchOnly": [ + "Name" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /targets", + "cmdlets": [ + "New-PfbTarget" + ], + "missingQueryParameters": [ + "names" + ], + "missingBodyProperties": [ + "address" + ], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Name", + "surface": "AttributesOnly", + "file": "Public/Replication/New-PfbTarget.ps1", + "line": 31 + } + ], + "escapeHatchOnly": [ + "Name" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /tls-policies", + "cmdlets": [ + "New-PfbTlsPolicy" + ], + "missingQueryParameters": [ + "names" + ], + "missingBodyProperties": [ + "appliance_certificate", + "client_certificates_required", + "disabled_tls_ciphers", + "enabled", + "enabled_tls_ciphers", + "location", + "min_tls_version", + "name", + "trusted_client_certificate_authority", + "verify_client_certificate_trust" + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Name", + "surface": "AttributesOnly", + "file": "Public/Policy/New-PfbTlsPolicy.ps1", + "line": 29 + } + ], + "escapeHatchOnly": [ + "Name" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /user-group-quota-policies", + "cmdlets": [ + "New-PfbUserGroupQuotaPolicy" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "enabled", + "name" + ], + "readOnlyFields": [ + "id", + "is_local", + "policy_type", + "realms" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enabled", + "surface": "AttributesOnly", + "file": "Public/Policy/New-PfbUserGroupQuotaPolicy.ps1", + "line": 49 + } + ], + "escapeHatchOnly": [ + "Enabled" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /user-group-quota-policies/file-systems", + "cmdlets": [ + "New-PfbUserGroupQuotaPolicyFileSystem" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /user-group-quota-policies/rules", + "cmdlets": [ + "New-PfbUserGroupQuotaPolicyRule" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "enforced" + ], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Enforced", + "surface": "AttributesOnly", + "file": "Public/Policy/New-PfbUserGroupQuotaPolicyRule.ps1", + "line": 57 + } + ], + "escapeHatchOnly": [ + "Enforced" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /workloads", + "cmdlets": [ + "New-PfbWorkload" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "POST /workloads/placement-recommendations", + "cmdlets": [ + "New-PfbWorkloadPlacementRecommendation" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + "additional_constraints", + "parameters", + "preset", + "projection_months", + "recommendation_engine", + "results_limit" + ], + "readOnlyFields": [ + "context", + "created", + "expires", + "id", + "more_results_available", + "name", + "progress", + "results", + "status" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Inputs", + "surface": "TypedUnresolved", + "file": "Public/Workloads/New-PfbWorkloadPlacementRecommendation.ps1", + "line": 29 + } + ], + "escapeHatchOnly": [], + "caveat": "one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "POST /worm-data-policies", + "cmdlets": [ + "New-PfbWormPolicy" + ], + "missingQueryParameters": [ + "context_names", + "names" + ], + "missingBodyProperties": [ + "default_retention", + "enabled", + "location", + "max_retention", + "min_retention", + "mode", + "retention_lock" + ], + "readOnlyFields": [ + "context", + "id", + "is_local", + "name", + "policy_type", + "realms" + ], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Name", + "surface": "AttributesOnly", + "file": "Public/Policy/New-PfbWormPolicy.ps1", + "line": 30 + } + ], + "escapeHatchOnly": [ + "Name" + ], + "caveat": "body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability" + }, + "annotations": [] + }, + { + "endpoint": "PUT /presets/workload", + "cmdlets": [ + "Set-PfbPresetWorkload" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [ + { + "name": "description", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "A brief description of the workload the preset will configure.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/Set-PfbPresetWorkload.ps1", + "paramBlockLine": 35, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "directory_configurations", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The file systems and managed directories that will be provisioned by the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/Set-PfbPresetWorkload.ps1", + "paramBlockLine": 35, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "export_configurations", + "type": null, + "format": null, + "specRequired": false, + "synopsis": "NFS, SMB, and SMB share policy configuration to be specified in file system and directory exports.", + "suggestedPowerShellType": "[object]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/Set-PfbPresetWorkload.ps1", + "paramBlockLine": 35, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "name", + "type": "string", + "format": null, + "specRequired": false, + "synopsis": "A user-specified name.", + "suggestedPowerShellType": "[string]", + "enumValues": [], + "enumStatus": "not-found-in-resource", + "target": { + "file": "Public/Presets/Set-PfbPresetWorkload.ps1", + "paramBlockLine": 35, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "parameters", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The parameters to prompt the user when they deploy workloads from the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/Set-PfbPresetWorkload.ps1", + "paramBlockLine": 35, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "periodic_replication_configurations", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The periodic replication configurations that can be applied to storage resources (such as volumes) within the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/Set-PfbPresetWorkload.ps1", + "paramBlockLine": 35, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "placement_configurations", + "type": "array", + "format": null, + "specRequired": true, + "synopsis": "The placement configurations that can be applied to storage resources (such as volumes) within the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/Set-PfbPresetWorkload.ps1", + "paramBlockLine": 35, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "platform_features", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": null, + "suggestedPowerShellType": "[object[]]", + "enumValues": [ + "fa_block", + "fa_file", + "fb_file" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Presets/Set-PfbPresetWorkload.ps1", + "paramBlockLine": 35, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "qos_configurations", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The QoS configurations that can be applied to storage resources (such as volumes) within the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/Set-PfbPresetWorkload.ps1", + "paramBlockLine": 35, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "quota_configurations", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The quota configurations that can be applied to storage resources (such as file systems and directories) within the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/Set-PfbPresetWorkload.ps1", + "paramBlockLine": 35, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "snapshot_configurations", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The snapshot configurations that can be applied to storage resources (such as volumes) within the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/Set-PfbPresetWorkload.ps1", + "paramBlockLine": 35, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "volume_configurations", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The volumes that will be provisioned by the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/Set-PfbPresetWorkload.ps1", + "paramBlockLine": 35, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "workload_tags", + "type": "array", + "format": null, + "specRequired": false, + "synopsis": "The tags that will be associated with workloads provisioned by the preset.", + "suggestedPowerShellType": "[object[]]", + "enumValues": [], + "enumStatus": "no-spec-enum-found", + "target": { + "file": "Public/Presets/Set-PfbPresetWorkload.ps1", + "paramBlockLine": 35, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + }, + { + "name": "workload_type", + "type": "string", + "format": null, + "specRequired": true, + "synopsis": "The type of workload the preset will configure.", + "suggestedPowerShellType": "[string]", + "enumValues": [ + "Clarity", + "Epic", + "Exchange", + "File", + "MsSQL", + "MySQL", + "Oracle", + "PostgreSQL", + "SAP-Hana", + "SAP", + "VDI", + "VSI", + "Wfs", + "Zerto", + "Custom" + ], + "enumStatus": "matched", + "target": { + "file": "Public/Presets/Set-PfbPresetWorkload.ps1", + "paramBlockLine": 35, + "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", + "hasAttributes": true + } + } + ], + "readOnlyFields": [ + "context", + "id", + "revision" + ], + "confidence": { + "level": "high", + "unresolvedParameters": [], + "escapeHatchOnly": [], + "caveat": "" + }, + "annotations": [] + }, + { + "endpoint": "PUT /workloads/tags/batch", + "cmdlets": [ + "Set-PfbWorkloadTag" + ], + "missingQueryParameters": [ + "context_names" + ], + "missingBodyProperties": [], + "readOnlyFields": [], + "confidence": { + "level": "partial", + "unresolvedParameters": [ + { + "parameter": "Tags", + "surface": "TypedUnresolved", + "file": "Public/Workloads/Set-PfbWorkloadTag.ps1", + "line": 30 + } + ], + "escapeHatchOnly": [], + "caveat": "one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability" + }, + "annotations": [] + } + ], + "systemicGaps": [ + { + "name": "context_names", + "endpointCount": 270, + "queryEndpointCount": 270, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /admins/api-tokens", + "DELETE /admins/cache", + "DELETE /admins/management-access-policies", + "DELETE /admins/ssh-certificate-authority-policies", + "DELETE /array-connections", + "DELETE /arrays/ssh-certificate-authority-policies", + "DELETE /audit-file-systems-policies", + "DELETE /audit-file-systems-policies/members", + "DELETE /audit-object-store-policies", + "DELETE /audit-object-store-policies/members", + "DELETE /buckets/audit-filters", + "DELETE /buckets/bucket-access-policies", + "DELETE /buckets/bucket-access-policies/rules", + "DELETE /buckets/cross-origin-resource-sharing-policies", + "DELETE /buckets/cross-origin-resource-sharing-policies/rules", + "DELETE /data-eviction-policies", + "DELETE /data-eviction-policies/file-systems", + "DELETE /directory-services/local/groups", + "DELETE /directory-services/local/groups/members", + "DELETE /dns", + "DELETE /file-system-exports", + "DELETE /file-system-replica-links", + "DELETE /file-system-replica-links/policies", + "DELETE /file-system-snapshots/policies", + "DELETE /file-system-snapshots/transfer", + "DELETE /file-systems/audit-policies", + "DELETE /file-systems/locks", + "DELETE /file-systems/policies", + "DELETE /file-systems/user-group-quota-policies", + "DELETE /lifecycle-rules", + "DELETE /log-targets/file-systems", + "DELETE /log-targets/object-store", + "DELETE /management-access-policies", + "DELETE /management-access-policies/admins", + "DELETE /nfs-export-policies", + "DELETE /nfs-export-policies/rules", + "DELETE /object-store-access-keys", + "DELETE /object-store-access-policies", + "DELETE /object-store-access-policies/object-store-roles", + "DELETE /object-store-access-policies/object-store-users", + "DELETE /object-store-access-policies/rules", + "DELETE /object-store-account-exports", + "DELETE /object-store-accounts", + "DELETE /object-store-remote-credentials", + "DELETE /object-store-roles", + "DELETE /object-store-roles/object-store-access-policies", + "DELETE /object-store-roles/object-store-trust-policies/rules", + "DELETE /object-store-users", + "DELETE /object-store-users/object-store-access-policies", + "DELETE /object-store-virtual-hosts", + "DELETE /policies", + "DELETE /policies/file-system-replica-links", + "DELETE /policies/file-systems", + "DELETE /presets/workload", + "DELETE /qos-policies", + "DELETE /qos-policies/members", + "DELETE /s3-export-policies", + "DELETE /s3-export-policies/rules", + "DELETE /smb-client-policies", + "DELETE /smb-client-policies/rules", + "DELETE /smb-share-policies", + "DELETE /smb-share-policies/rules", + "DELETE /ssh-certificate-authority-policies/admins", + "DELETE /ssh-certificate-authority-policies/arrays", + "DELETE /user-group-quota-policies", + "DELETE /user-group-quota-policies/file-systems", + "DELETE /user-group-quota-policies/rules", + "DELETE /workloads", + "DELETE /workloads/tags", + "DELETE /worm-data-policies", + "GET /active-directory/test", + "GET /admins", + "GET /admins/api-tokens", + "GET /admins/cache", + "GET /admins/management-access-policies", + "GET /admins/ssh-certificate-authority-policies", + "GET /array-connections", + "GET /array-connections/path", + "GET /arrays/http-specific-performance", + "GET /arrays/nfs-specific-performance", + "GET /arrays/performance", + "GET /arrays/performance/replication", + "GET /arrays/s3-specific-performance", + "GET /arrays/space", + "GET /arrays/ssh-certificate-authority-policies", + "GET /audit-file-systems-policies", + "GET /audit-file-systems-policies/members", + "GET /audit-file-systems-policy-operations", + "GET /audit-object-store-policies", + "GET /audit-object-store-policies/members", + "GET /audits", + "GET /bucket-audit-filter-actions", + "GET /bucket-replica-links", + "GET /buckets", + "GET /buckets/audit-filters", + "GET /buckets/bucket-access-policies", + "GET /buckets/bucket-access-policies/rules", + "GET /buckets/cross-origin-resource-sharing-policies", + "GET /buckets/cross-origin-resource-sharing-policies/rules", + "GET /data-eviction-policies", + "GET /data-eviction-policies/file-systems", + "GET /data-eviction-policies/members", + "GET /directory-services/local/directory-services", + "GET /directory-services/local/groups", + "GET /directory-services/local/groups/members", + "GET /directory-services/test", + "GET /dns", + "GET /file-system-exports", + "GET /file-system-group-quotas", + "GET /file-system-replica-links", + "GET /file-system-replica-links/policies", + "GET /file-system-replica-links/transfer", + "GET /file-system-snapshots", + "GET /file-system-snapshots/policies", + "GET /file-system-snapshots/transfer", + "GET /file-system-user-quotas", + "GET /file-systems", + "GET /file-systems/audit-policies", + "GET /file-systems/groups", + "GET /file-systems/locks", + "GET /file-systems/locks/clients", + "GET /file-systems/policies", + "GET /file-systems/sessions", + "GET /file-systems/user-group-quota-policies", + "GET /file-systems/users", + "GET /file-systems/worm-data-policies", + "GET /lifecycle-rules", + "GET /log-targets/file-systems", + "GET /log-targets/object-store", + "GET /management-access-policies", + "GET /management-access-policies/admins", + "GET /management-access-policies/members", + "GET /nfs-export-policies", + "GET /nfs-export-policies/rules", + "GET /object-store-access-keys", + "GET /object-store-access-policies", + "GET /object-store-access-policies/object-store-roles", + "GET /object-store-access-policies/object-store-users", + "GET /object-store-access-policies/rules", + "GET /object-store-access-policy-actions", + "GET /object-store-account-exports", + "GET /object-store-accounts", + "GET /object-store-remote-credentials", + "GET /object-store-roles", + "GET /object-store-roles/object-store-access-policies", + "GET /object-store-roles/object-store-trust-policies", + "GET /object-store-roles/object-store-trust-policies/rules", + "GET /object-store-users", + "GET /object-store-users/object-store-access-policies", + "GET /object-store-virtual-hosts", + "GET /policies", + "GET /policies-all", + "GET /policies-all/members", + "GET /policies/file-system-replica-links", + "GET /policies/file-system-snapshots", + "GET /policies/file-systems", + "GET /presets/workload", + "GET /qos-policies", + "GET /qos-policies/buckets", + "GET /qos-policies/file-systems", + "GET /qos-policies/members", + "GET /quotas/groups", + "GET /quotas/users", + "GET /realms", + "GET /realms/defaults", + "GET /s3-export-policies", + "GET /s3-export-policies/rules", + "GET /servers", + "GET /smb-client-policies", + "GET /smb-client-policies/rules", + "GET /smb-share-policies", + "GET /smb-share-policies/rules", + "GET /ssh-certificate-authority-policies", + "GET /ssh-certificate-authority-policies/admins", + "GET /ssh-certificate-authority-policies/arrays", + "GET /ssh-certificate-authority-policies/members", + "GET /storage-class-tiering-policies/members", + "GET /syslog-servers", + "GET /targets", + "GET /usage/groups", + "GET /usage/users", + "GET /user-group-quota-policies/file-systems", + "GET /user-group-quota-policies/members", + "GET /user-group-quota-policies/rules", + "GET /workloads", + "GET /workloads/placement-recommendations", + "GET /workloads/tags", + "GET /worm-data-policies", + "GET /worm-data-policies/members", + "PATCH /admins", + "PATCH /array-connections", + "PATCH /buckets/audit-filters", + "PATCH /dns", + "PATCH /file-system-exports", + "PATCH /lifecycle-rules", + "PATCH /log-targets/file-systems", + "PATCH /log-targets/object-store", + "PATCH /management-access-policies", + "PATCH /nfs-export-policies/rules", + "PATCH /object-store-access-policies/rules", + "PATCH /object-store-account-exports", + "PATCH /object-store-remote-credentials", + "PATCH /object-store-roles", + "PATCH /object-store-roles/object-store-trust-policies/rules", + "PATCH /object-store-virtual-hosts", + "PATCH /presets/workload", + "PATCH /qos-policies", + "PATCH /realms/defaults", + "PATCH /s3-export-policies/rules", + "PATCH /smb-client-policies/rules", + "PATCH /smb-share-policies/rules", + "PATCH /user-group-quota-policies/rules", + "PATCH /worm-data-policies", + "POST /admins/api-tokens", + "POST /admins/management-access-policies", + "POST /admins/ssh-certificate-authority-policies", + "POST /array-connections", + "POST /arrays/ssh-certificate-authority-policies", + "POST /audit-file-systems-policies/members", + "POST /audit-object-store-policies/members", + "POST /buckets", + "POST /buckets/audit-filters", + "POST /buckets/bucket-access-policies", + "POST /buckets/bucket-access-policies/rules", + "POST /buckets/cross-origin-resource-sharing-policies", + "POST /buckets/cross-origin-resource-sharing-policies/rules", + "POST /data-eviction-policies/file-systems", + "POST /directory-services/local/directory-services", + "POST /directory-services/local/groups", + "POST /file-system-exports", + "POST /file-system-replica-links", + "POST /file-system-replica-links/policies", + "POST /file-systems/audit-policies", + "POST /file-systems/locks/nlm-reclamations", + "POST /file-systems/policies", + "POST /file-systems/user-group-quota-policies", + "POST /lifecycle-rules", + "POST /log-targets/file-systems", + "POST /log-targets/object-store", + "POST /management-access-policies", + "POST /management-access-policies/admins", + "POST /nfs-export-policies/rules", + "POST /object-store-access-keys", + "POST /object-store-access-policies", + "POST /object-store-access-policies/object-store-roles", + "POST /object-store-access-policies/object-store-users", + "POST /object-store-access-policies/rules", + "POST /object-store-account-exports", + "POST /object-store-accounts", + "POST /object-store-remote-credentials", + "POST /object-store-roles", + "POST /object-store-roles/object-store-access-policies", + "POST /object-store-roles/object-store-trust-policies/rules", + "POST /object-store-users", + "POST /object-store-users/object-store-access-policies", + "POST /object-store-virtual-hosts", + "POST /policies/file-system-replica-links", + "POST /policies/file-systems", + "POST /presets/workload", + "POST /qos-policies", + "POST /qos-policies/members", + "POST /quotas/groups", + "POST /s3-export-policies/rules", + "POST /smb-client-policies/rules", + "POST /smb-share-policies/rules", + "POST /ssh-certificate-authority-policies/admins", + "POST /ssh-certificate-authority-policies/arrays", + "POST /user-group-quota-policies/file-systems", + "POST /workloads", + "PUT /presets/workload" + ], + "annotations": [ + { + "matchType": "field", + "match": "context_names", + "kind": "designDecision", + "note": "not yet implemented", + "reference": "docs/design/fusion-context-injection.md" + } + ] + }, + { + "name": "allow_errors", + "endpointCount": 118, + "queryEndpointCount": 118, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /active-directory/test", + "GET /admins", + "GET /admins/api-tokens", + "GET /admins/cache", + "GET /admins/management-access-policies", + "GET /admins/ssh-certificate-authority-policies", + "GET /array-connections", + "GET /array-connections/path", + "GET /arrays/http-specific-performance", + "GET /arrays/nfs-specific-performance", + "GET /arrays/performance", + "GET /arrays/performance/replication", + "GET /arrays/s3-specific-performance", + "GET /arrays/space", + "GET /arrays/ssh-certificate-authority-policies", + "GET /audit-file-systems-policies", + "GET /audit-file-systems-policies/members", + "GET /audit-file-systems-policy-operations", + "GET /audit-object-store-policies", + "GET /audit-object-store-policies/members", + "GET /audits", + "GET /bucket-audit-filter-actions", + "GET /bucket-replica-links", + "GET /buckets", + "GET /buckets/audit-filters", + "GET /buckets/bucket-access-policies", + "GET /buckets/bucket-access-policies/rules", + "GET /buckets/cross-origin-resource-sharing-policies", + "GET /buckets/cross-origin-resource-sharing-policies/rules", + "GET /data-eviction-policies", + "GET /data-eviction-policies/file-systems", + "GET /data-eviction-policies/members", + "GET /directory-services/local/directory-services", + "GET /directory-services/local/groups", + "GET /directory-services/local/groups/members", + "GET /directory-services/test", + "GET /dns", + "GET /file-system-exports", + "GET /file-system-group-quotas", + "GET /file-system-replica-links", + "GET /file-system-replica-links/policies", + "GET /file-system-replica-links/transfer", + "GET /file-system-snapshots", + "GET /file-system-snapshots/policies", + "GET /file-system-snapshots/transfer", + "GET /file-system-user-quotas", + "GET /file-systems", + "GET /file-systems/audit-policies", + "GET /file-systems/groups", + "GET /file-systems/locks", + "GET /file-systems/locks/clients", + "GET /file-systems/policies", + "GET /file-systems/sessions", + "GET /file-systems/user-group-quota-policies", + "GET /file-systems/users", + "GET /file-systems/worm-data-policies", + "GET /lifecycle-rules", + "GET /log-targets/file-systems", + "GET /log-targets/object-store", + "GET /management-access-policies", + "GET /management-access-policies/admins", + "GET /management-access-policies/members", + "GET /nfs-export-policies", + "GET /nfs-export-policies/rules", + "GET /object-store-access-keys", + "GET /object-store-access-policies", + "GET /object-store-access-policies/object-store-roles", + "GET /object-store-access-policies/object-store-users", + "GET /object-store-access-policies/rules", + "GET /object-store-access-policy-actions", + "GET /object-store-account-exports", + "GET /object-store-accounts", + "GET /object-store-remote-credentials", + "GET /object-store-roles", + "GET /object-store-roles/object-store-access-policies", + "GET /object-store-roles/object-store-trust-policies", + "GET /object-store-roles/object-store-trust-policies/rules", + "GET /object-store-users", + "GET /object-store-users/object-store-access-policies", + "GET /object-store-virtual-hosts", + "GET /policies", + "GET /policies-all", + "GET /policies-all/members", + "GET /policies/file-system-replica-links", + "GET /policies/file-system-snapshots", + "GET /policies/file-systems", + "GET /qos-policies", + "GET /qos-policies/buckets", + "GET /qos-policies/file-systems", + "GET /qos-policies/members", + "GET /quotas/groups", + "GET /quotas/users", + "GET /realms", + "GET /realms/defaults", + "GET /s3-export-policies", + "GET /s3-export-policies/rules", + "GET /servers", + "GET /smb-client-policies", + "GET /smb-client-policies/rules", + "GET /smb-share-policies", + "GET /smb-share-policies/rules", + "GET /ssh-certificate-authority-policies", + "GET /ssh-certificate-authority-policies/admins", + "GET /ssh-certificate-authority-policies/arrays", + "GET /ssh-certificate-authority-policies/members", + "GET /storage-class-tiering-policies/members", + "GET /syslog-servers", + "GET /targets", + "GET /usage/groups", + "GET /usage/users", + "GET /user-group-quota-policies/file-systems", + "GET /user-group-quota-policies/members", + "GET /user-group-quota-policies/rules", + "GET /workloads", + "GET /workloads/placement-recommendations", + "GET /workloads/tags", + "GET /worm-data-policies", + "GET /worm-data-policies/members" + ], + "annotations": [ + { + "matchType": "field", + "match": "allow_errors", + "kind": "designDecision", + "note": "not yet implemented", + "reference": "docs/design/fusion-context-injection.md" + } + ] + }, + { + "name": "ids", + "endpointCount": 43, + "queryEndpointCount": 43, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /network-access-policies/rules", + "DELETE /nfs-export-policies/rules", + "DELETE /smb-client-policies/rules", + "DELETE /smb-share-policies/rules", + "GET /active-directory", + "GET /array-connections/connection-key", + "GET /array-connections/path", + "GET /array-connections/performance/replication", + "GET /bucket-replica-links", + "GET /certificate-groups/uses", + "GET /certificates/uses", + "GET /directory-services", + "GET /directory-services/test", + "GET /dns", + "GET /file-system-replica-links", + "GET /hardware-connectors/performance", + "GET /legal-holds/held-entities", + "GET /network-access-policies/rules", + "GET /network-interfaces/connectors/performance", + "GET /network-interfaces/connectors/settings", + "GET /nfs-export-policies/rules", + "GET /node-groups/uses", + "GET /password-policies", + "GET /public-keys/uses", + "GET /quotas/settings", + "GET /realms/space", + "GET /realms/space/storage-classes", + "GET /roles", + "GET /smb-client-policies/rules", + "GET /smb-share-policies/rules", + "GET /smtp-servers", + "GET /snmp-agents", + "GET /software-check", + "GET /support", + "GET /syslog-servers/settings", + "GET /targets/performance/replication", + "PATCH /network-access-policies/rules", + "PATCH /nfs-export-policies/rules", + "PATCH /password-policies", + "PATCH /smb-client-policies/rules", + "PATCH /smb-share-policies/rules", + "PATCH /syslog-servers/settings", + "POST /file-system-replica-links" + ], + "annotations": [] + }, + { + "name": "names", + "endpointCount": 31, + "queryEndpointCount": 31, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /buckets/audit-filters", + "DELETE /buckets/bucket-access-policies", + "DELETE /buckets/cross-origin-resource-sharing-policies", + "GET /arrays/clients/performance", + "GET /arrays/clients/s3-specific-performance", + "GET /arrays/supported-time-zones", + "GET /audit-file-systems-policy-operations", + "GET /bucket-audit-filter-actions", + "GET /buckets/audit-filters", + "GET /directory-services/test", + "GET /dns", + "GET /file-systems/groups/performance", + "GET /file-systems/users/performance", + "GET /legal-holds/held-entities", + "GET /object-store-access-policy-actions", + "GET /object-store-roles/object-store-trust-policies", + "GET /password-policies", + "GET /quotas/settings", + "GET /smtp-servers", + "GET /snmp-agents", + "GET /software-check", + "GET /syslog-servers/settings", + "PATCH /password-policies", + "PATCH /syslog-servers/settings", + "POST /buckets/audit-filters", + "POST /buckets/bucket-access-policies/rules", + "POST /buckets/cross-origin-resource-sharing-policies/rules", + "POST /maintenance-windows", + "POST /object-store-access-keys", + "POST /object-store-access-policies/rules", + "POST /object-store-roles/object-store-trust-policies/rules" + ], + "annotations": [] + }, + { + "name": "sort", + "endpointCount": 28, + "queryEndpointCount": 28, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /active-directory", + "GET /active-directory/test", + "GET /admins/management-access-policies", + "GET /admins/ssh-certificate-authority-policies", + "GET /alert-watchers/test", + "GET /arrays/ssh-certificate-authority-policies", + "GET /certificates/certificate-groups", + "GET /directory-services", + "GET /directory-services/roles/management-access-policies", + "GET /directory-services/test", + "GET /management-access-policies/admins", + "GET /management-access-policies/directory-services/roles", + "GET /management-access-policies/members", + "GET /policies-all/members", + "GET /policies/file-system-replica-links", + "GET /qos-policies/buckets", + "GET /qos-policies/file-systems", + "GET /qos-policies/members", + "GET /snmp-agents", + "GET /snmp-managers/test", + "GET /ssh-certificate-authority-policies/admins", + "GET /ssh-certificate-authority-policies/arrays", + "GET /ssh-certificate-authority-policies/members", + "GET /sso/saml2/idps/test", + "GET /storage-class-tiering-policies/members", + "GET /support/test", + "GET /tls-policies/members", + "GET /worm-data-policies/members" + ], + "annotations": [] + }, + { + "name": "bucket_ids", + "endpointCount": 17, + "queryEndpointCount": 17, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /buckets/audit-filters", + "DELETE /buckets/bucket-access-policies", + "DELETE /buckets/bucket-access-policies/rules", + "DELETE /buckets/cross-origin-resource-sharing-policies", + "DELETE /buckets/cross-origin-resource-sharing-policies/rules", + "DELETE /lifecycle-rules", + "GET /buckets/audit-filters", + "GET /buckets/bucket-access-policies", + "GET /buckets/bucket-access-policies/rules", + "GET /buckets/cross-origin-resource-sharing-policies", + "GET /buckets/cross-origin-resource-sharing-policies/rules", + "GET /lifecycle-rules", + "POST /buckets/audit-filters", + "POST /buckets/bucket-access-policies", + "POST /buckets/bucket-access-policies/rules", + "POST /buckets/cross-origin-resource-sharing-policies", + "POST /buckets/cross-origin-resource-sharing-policies/rules" + ], + "annotations": [] + }, + { + "name": "policy_ids", + "endpointCount": 17, + "queryEndpointCount": 17, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /network-interfaces/tls-policies", + "DELETE /object-store-access-policies/object-store-roles", + "DELETE /object-store-access-policies/object-store-users", + "DELETE /object-store-access-policies/rules", + "DELETE /object-store-roles/object-store-access-policies", + "DELETE /object-store-users/object-store-access-policies", + "GET /object-store-roles/object-store-access-policies", + "PATCH /object-store-access-policies/rules", + "PATCH /s3-export-policies/rules", + "PATCH /smb-share-policies/rules", + "POST /file-system-exports", + "POST /object-store-access-policies/object-store-roles", + "POST /object-store-access-policies/object-store-users", + "POST /object-store-access-policies/rules", + "POST /object-store-account-exports", + "POST /object-store-roles/object-store-access-policies", + "POST /object-store-users/object-store-access-policies" + ], + "annotations": [] + }, + { + "name": "total_only", + "endpointCount": 17, + "queryEndpointCount": 17, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /array-connections/performance/replication", + "GET /arrays/clients/performance", + "GET /arrays/clients/s3-specific-performance", + "GET /arrays/space/storage-classes", + "GET /blades", + "GET /bucket-replica-links", + "GET /drives", + "GET /fleets", + "GET /fleets/fleet-key", + "GET /fleets/members", + "GET /hardware-connectors/performance", + "GET /network-interfaces/connectors/performance", + "GET /nodes", + "GET /realms/space", + "GET /realms/space/storage-classes", + "GET /remote-arrays", + "GET /targets/performance/replication" + ], + "annotations": [] + }, + { + "name": "bucket_names", + "endpointCount": 16, + "queryEndpointCount": 16, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /buckets/audit-filters", + "DELETE /buckets/bucket-access-policies", + "DELETE /buckets/bucket-access-policies/rules", + "DELETE /buckets/cross-origin-resource-sharing-policies", + "DELETE /buckets/cross-origin-resource-sharing-policies/rules", + "DELETE /lifecycle-rules", + "GET /buckets/audit-filters", + "GET /buckets/bucket-access-policies", + "GET /buckets/bucket-access-policies/rules", + "GET /buckets/cross-origin-resource-sharing-policies", + "GET /buckets/cross-origin-resource-sharing-policies/rules", + "POST /buckets/audit-filters", + "POST /buckets/bucket-access-policies", + "POST /buckets/bucket-access-policies/rules", + "POST /buckets/cross-origin-resource-sharing-policies", + "POST /buckets/cross-origin-resource-sharing-policies/rules" + ], + "annotations": [] + }, + { + "name": "member_ids", + "endpointCount": 15, + "queryEndpointCount": 15, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /directory-services/local/groups/members", + "DELETE /fleets/members", + "DELETE /network-interfaces/tls-policies", + "DELETE /object-store-access-policies/object-store-roles", + "DELETE /object-store-access-policies/object-store-users", + "DELETE /object-store-roles/object-store-access-policies", + "DELETE /object-store-users/object-store-access-policies", + "GET /directory-services/local/groups/members", + "GET /fleets/members", + "POST /file-system-exports", + "POST /object-store-access-policies/object-store-roles", + "POST /object-store-access-policies/object-store-users", + "POST /object-store-account-exports", + "POST /object-store-roles/object-store-access-policies", + "POST /object-store-users/object-store-access-policies" + ], + "annotations": [] + }, + { + "name": "remote_ids", + "endpointCount": 15, + "queryEndpointCount": 15, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /array-connections", + "DELETE /file-system-replica-links", + "DELETE /file-system-replica-links/policies", + "DELETE /file-system-snapshots/transfer", + "DELETE /policies/file-system-replica-links", + "GET /array-connections", + "GET /array-connections/path", + "GET /array-connections/performance/replication", + "GET /bucket-replica-links", + "GET /file-system-replica-links", + "GET /file-system-replica-links/policies", + "GET /file-system-replica-links/transfer", + "GET /policies-all/members", + "GET /policies/file-system-replica-links", + "POST /file-system-replica-links" + ], + "annotations": [] + }, + { + "name": "policy_names", + "endpointCount": 11, + "queryEndpointCount": 11, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /buckets/bucket-access-policies/rules", + "DELETE /buckets/cross-origin-resource-sharing-policies/rules", + "DELETE /object-store-roles/object-store-access-policies", + "GET /object-store-roles/object-store-access-policies", + "PATCH /object-store-access-policies/rules", + "PATCH /object-store-roles/object-store-trust-policies/rules", + "PATCH /s3-export-policies/rules", + "PATCH /smb-share-policies/rules", + "POST /buckets/cross-origin-resource-sharing-policies/rules", + "POST /object-store-account-exports", + "POST /object-store-roles/object-store-access-policies" + ], + "annotations": [] + }, + { + "name": "file_system_ids", + "endpointCount": 9, + "queryEndpointCount": 9, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /file-systems/locks", + "GET /file-systems/locks", + "GET /file-systems/open-files", + "GET /legal-holds/held-entities", + "GET /quotas/groups", + "GET /quotas/users", + "GET /usage/groups", + "GET /usage/users", + "POST /quotas/groups" + ], + "annotations": [] + }, + { + "name": "remote_names", + "endpointCount": 9, + "queryEndpointCount": 9, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /file-system-replica-links/policies", + "DELETE /file-system-snapshots/transfer", + "DELETE /policies/file-system-replica-links", + "GET /bucket-replica-links", + "GET /file-system-replica-links", + "GET /file-system-replica-links/policies", + "GET /file-system-replica-links/transfer", + "GET /policies-all/members", + "GET /policies/file-system-replica-links" + ], + "annotations": [] + }, + { + "name": "local_file_system_ids", + "endpointCount": 8, + "queryEndpointCount": 8, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /file-system-replica-links", + "DELETE /file-system-replica-links/policies", + "DELETE /policies/file-system-replica-links", + "GET /file-system-replica-links", + "GET /file-system-replica-links/policies", + "GET /policies-all/members", + "GET /policies/file-system-replica-links", + "POST /file-system-replica-links" + ], + "annotations": [] + }, + { + "name": "actions", + "endpointCount": 7, + "queryEndpointCount": 0, + "bodyEndpointCount": 7, + "endpoints": [ + "PATCH /object-store-access-policies/rules", + "PATCH /object-store-roles/object-store-trust-policies/rules", + "PATCH /s3-export-policies/rules", + "POST /buckets/audit-filters", + "POST /buckets/bucket-access-policies/rules", + "POST /object-store-access-policies/rules", + "POST /object-store-roles/object-store-trust-policies/rules" + ], + "annotations": [] + }, + { + "name": "limit", + "endpointCount": 7, + "queryEndpointCount": 7, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /active-directory", + "GET /active-directory/test", + "GET /directory-services", + "GET /directory-services/test", + "GET /snmp-agents", + "GET /snmp-managers/test", + "GET /sso/saml2/idps/test" + ], + "annotations": [] + }, + { + "name": "name", + "endpointCount": 7, + "queryEndpointCount": 0, + "bodyEndpointCount": 7, + "endpoints": [ + "PATCH /arrays", + "PATCH /password-policies", + "POST /log-targets/file-systems", + "POST /log-targets/object-store", + "POST /management-access-policies", + "POST /qos-policies", + "PUT /presets/workload" + ], + "annotations": [] + }, + { + "name": "versions", + "endpointCount": 7, + "queryEndpointCount": 7, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /network-access-policies/rules", + "DELETE /nfs-export-policies", + "DELETE /nfs-export-policies/rules", + "DELETE /smb-client-policies/rules", + "PATCH /network-access-policies/rules", + "PATCH /nfs-export-policies/rules", + "PATCH /smb-client-policies/rules" + ], + "annotations": [] + }, + { + "name": "enabled", + "endpointCount": 6, + "queryEndpointCount": 0, + "bodyEndpointCount": 6, + "endpoints": [ + "PATCH /password-policies", + "PATCH /rapid-data-locking", + "POST /management-access-policies", + "POST /qos-policies", + "POST /sso/oidc/idps", + "POST /sso/saml2/idps" + ], + "annotations": [] + }, + { + "name": "filter", + "endpointCount": 6, + "queryEndpointCount": 6, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /active-directory/test", + "GET /alert-watchers/test", + "GET /directory-services/test", + "GET /snmp-managers/test", + "GET /sso/saml2/idps/test", + "GET /support/test" + ], + "annotations": [] + }, + { + "name": "gids", + "endpointCount": 6, + "queryEndpointCount": 6, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /directory-services/local/groups", + "GET /directory-services/local/groups", + "GET /file-systems/groups/performance", + "GET /quotas/groups", + "GET /usage/groups", + "POST /quotas/groups" + ], + "annotations": [] + }, + { + "name": "role_ids", + "endpointCount": 6, + "queryEndpointCount": 6, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /object-store-roles/object-store-trust-policies/rules", + "GET /directory-services/roles", + "GET /object-store-roles/object-store-trust-policies/rules", + "PATCH /directory-services/roles", + "PATCH /object-store-roles/object-store-trust-policies/rules", + "POST /object-store-roles/object-store-trust-policies/rules" + ], + "annotations": [] + }, + { + "name": "role_names", + "endpointCount": 6, + "queryEndpointCount": 6, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /object-store-roles/object-store-trust-policies/rules", + "GET /directory-services/roles", + "GET /object-store-roles/object-store-trust-policies/rules", + "PATCH /directory-services/roles", + "PATCH /object-store-roles/object-store-trust-policies/rules", + "POST /object-store-roles/object-store-trust-policies/rules" + ], + "annotations": [] + }, + { + "name": "workload_ids", + "endpointCount": 6, + "queryEndpointCount": 6, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /file-system-exports", + "GET /file-systems", + "GET /nfs-export-policies", + "GET /policies", + "GET /smb-client-policies", + "GET /smb-share-policies" + ], + "annotations": [] + }, + { + "name": "workload_names", + "endpointCount": 6, + "queryEndpointCount": 6, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /file-system-exports", + "GET /file-systems", + "GET /nfs-export-policies", + "GET /policies", + "GET /smb-client-policies", + "GET /smb-share-policies" + ], + "annotations": [] + }, + { + "name": "file_system_names", + "endpointCount": 5, + "queryEndpointCount": 5, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /file-systems/locks", + "GET /file-systems/locks", + "GET /file-systems/open-files", + "GET /legal-holds/held-entities", + "POST /quotas/groups" + ], + "annotations": [] + }, + { + "name": "local_file_system_names", + "endpointCount": 5, + "queryEndpointCount": 5, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /file-system-replica-links/policies", + "DELETE /policies/file-system-replica-links", + "GET /file-system-replica-links/policies", + "GET /policies-all/members", + "GET /policies/file-system-replica-links" + ], + "annotations": [] + }, + { + "name": "policy", + "endpointCount": 5, + "queryEndpointCount": 0, + "bodyEndpointCount": 5, + "endpoints": [ + "PATCH /network-access-policies/rules", + "PATCH /object-store-roles/object-store-trust-policies/rules", + "PATCH /smb-client-policies/rules", + "PATCH /smb-share-policies/rules", + "POST /object-store-roles/object-store-trust-policies/rules" + ], + "annotations": [] + }, + { + "name": "remote_file_system_ids", + "endpointCount": 5, + "queryEndpointCount": 5, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /file-system-replica-links", + "GET /file-system-replica-links", + "GET /file-system-replica-links/policies", + "GET /policies-all/members", + "GET /policies/file-system-replica-links" + ], + "annotations": [] + }, + { + "name": "total_item_count", + "endpointCount": 5, + "queryEndpointCount": 5, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /directory-services/local/directory-services", + "GET /directory-services/local/groups", + "GET /directory-services/local/groups/members", + "GET /network-interfaces/neighbors", + "GET /software-check" + ], + "annotations": [] + }, + { + "name": "user_names", + "endpointCount": 5, + "queryEndpointCount": 5, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /file-systems/open-files", + "GET /file-systems/sessions", + "GET /file-systems/users/performance", + "GET /quotas/users", + "GET /usage/users" + ], + "annotations": [] + }, + { + "name": "client_names", + "endpointCount": 4, + "queryEndpointCount": 4, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /file-systems/locks", + "GET /file-systems/locks", + "GET /file-systems/open-files", + "GET /file-systems/sessions" + ], + "annotations": [] + }, + { + "name": "conditions", + "endpointCount": 4, + "queryEndpointCount": 0, + "bodyEndpointCount": 4, + "endpoints": [ + "PATCH /object-store-access-policies/rules", + "PATCH /object-store-roles/object-store-trust-policies/rules", + "POST /object-store-access-policies/rules", + "POST /object-store-roles/object-store-trust-policies/rules" + ], + "annotations": [] + }, + { + "name": "description", + "endpointCount": 4, + "queryEndpointCount": 0, + "bodyEndpointCount": 4, + "endpoints": [ + "POST /legal-holds", + "POST /object-store-access-policies", + "POST /presets/workload", + "PUT /presets/workload" + ], + "annotations": [] + }, + { + "name": "effect", + "endpointCount": 4, + "queryEndpointCount": 0, + "bodyEndpointCount": 4, + "endpoints": [ + "PATCH /network-access-policies/rules", + "PATCH /object-store-access-policies/rules", + "PATCH /s3-export-policies/rules", + "POST /object-store-access-policies/rules" + ], + "annotations": [] + }, + { + "name": "end_time", + "endpointCount": 4, + "queryEndpointCount": 4, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /arrays/space", + "GET /arrays/space/storage-classes", + "GET /realms/space", + "GET /realms/space/storage-classes" + ], + "annotations": [] + }, + { + "name": "group_names", + "endpointCount": 4, + "queryEndpointCount": 4, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /file-systems/groups/performance", + "GET /quotas/groups", + "GET /usage/groups", + "POST /quotas/groups" + ], + "annotations": [] + }, + { + "name": "member_types", + "endpointCount": 4, + "queryEndpointCount": 4, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /directory-services/local/groups/members", + "DELETE /qos-policies/members", + "GET /directory-services/local/groups/members", + "GET /qos-policies/members" + ], + "annotations": [] + }, + { + "name": "paths", + "endpointCount": 4, + "queryEndpointCount": 4, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /file-systems/locks", + "GET /file-systems/locks", + "GET /file-systems/open-files", + "GET /legal-holds/held-entities" + ], + "annotations": [] + }, + { + "name": "resolution", + "endpointCount": 4, + "queryEndpointCount": 4, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /arrays/space", + "GET /arrays/space/storage-classes", + "GET /realms/space", + "GET /realms/space/storage-classes" + ], + "annotations": [] + }, + { + "name": "resources", + "endpointCount": 4, + "queryEndpointCount": 0, + "bodyEndpointCount": 4, + "endpoints": [ + "PATCH /object-store-access-policies/rules", + "PATCH /s3-export-policies/rules", + "POST /buckets/bucket-access-policies/rules", + "POST /object-store-access-policies/rules" + ], + "annotations": [] + }, + { + "name": "rules", + "endpointCount": 4, + "queryEndpointCount": 0, + "bodyEndpointCount": 4, + "endpoints": [ + "POST /buckets/bucket-access-policies", + "POST /buckets/cross-origin-resource-sharing-policies", + "POST /management-access-policies", + "POST /object-store-access-policies" + ], + "annotations": [] + }, + { + "name": "start_time", + "endpointCount": 4, + "queryEndpointCount": 4, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /arrays/space", + "GET /arrays/space/storage-classes", + "GET /realms/space", + "GET /realms/space/storage-classes" + ], + "annotations": [] + }, + { + "name": "before_rule_id", + "endpointCount": 3, + "queryEndpointCount": 3, + "bodyEndpointCount": 0, + "endpoints": [ + "PATCH /network-access-policies/rules", + "PATCH /nfs-export-policies/rules", + "PATCH /smb-client-policies/rules" + ], + "annotations": [] + }, + { + "name": "before_rule_name", + "endpointCount": 3, + "queryEndpointCount": 3, + "bodyEndpointCount": 0, + "endpoints": [ + "PATCH /network-access-policies/rules", + "PATCH /nfs-export-policies/rules", + "PATCH /smb-client-policies/rules" + ], + "annotations": [] + }, + { + "name": "certificate_group_ids", + "endpointCount": 3, + "queryEndpointCount": 3, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /certificates/certificate-groups", + "GET /certificate-groups/certificates", + "GET /certificates/certificate-groups" + ], + "annotations": [] + }, + { + "name": "certificate_ids", + "endpointCount": 3, + "queryEndpointCount": 3, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /certificates/certificate-groups", + "GET /certificate-groups/certificates", + "GET /certificates/certificate-groups" + ], + "annotations": [] + }, + { + "name": "client", + "endpointCount": 3, + "queryEndpointCount": 0, + "bodyEndpointCount": 3, + "endpoints": [ + "PATCH /network-access-policies/rules", + "PATCH /nfs-export-policies/rules", + "PATCH /smb-client-policies/rules" + ], + "annotations": [] + }, + { + "name": "enforce_action_restrictions", + "endpointCount": 3, + "queryEndpointCount": 3, + "bodyEndpointCount": 0, + "endpoints": [ + "PATCH /object-store-access-policies/rules", + "POST /object-store-access-policies", + "POST /object-store-access-policies/rules" + ], + "annotations": [] + }, + { + "name": "index", + "endpointCount": 3, + "queryEndpointCount": 0, + "bodyEndpointCount": 3, + "endpoints": [ + "PATCH /network-access-policies/rules", + "PATCH /nfs-export-policies/rules", + "PATCH /smb-client-policies/rules" + ], + "annotations": [] + }, + { + "name": "indices", + "endpointCount": 3, + "queryEndpointCount": 3, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /object-store-roles/object-store-trust-policies/rules", + "GET /object-store-roles/object-store-trust-policies/rules", + "PATCH /object-store-roles/object-store-trust-policies/rules" + ], + "annotations": [] + }, + { + "name": "local_directory_service_ids", + "endpointCount": 3, + "queryEndpointCount": 3, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /directory-services/local/groups", + "DELETE /directory-services/local/groups/members", + "POST /directory-services/local/groups" + ], + "annotations": [] + }, + { + "name": "local_directory_service_names", + "endpointCount": 3, + "queryEndpointCount": 3, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /directory-services/local/groups", + "DELETE /directory-services/local/groups/members", + "POST /directory-services/local/groups" + ], + "annotations": [] + }, + { + "name": "location", + "endpointCount": 3, + "queryEndpointCount": 0, + "bodyEndpointCount": 3, + "endpoints": [ + "PATCH /password-policies", + "POST /management-access-policies", + "POST /qos-policies" + ], + "annotations": [] + }, + { + "name": "names_or_owner_names", + "endpointCount": 3, + "queryEndpointCount": 3, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /file-system-replica-links/transfer", + "GET /file-system-snapshots", + "GET /file-system-snapshots/transfer" + ], + "annotations": [] + }, + { + "name": "principals", + "endpointCount": 3, + "queryEndpointCount": 0, + "bodyEndpointCount": 3, + "endpoints": [ + "PATCH /object-store-roles/object-store-trust-policies/rules", + "POST /buckets/bucket-access-policies/rules", + "POST /object-store-roles/object-store-trust-policies/rules" + ], + "annotations": [] + }, + { + "name": "remote_file_system_names", + "endpointCount": 3, + "queryEndpointCount": 3, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /file-system-replica-links/policies", + "GET /policies-all/members", + "GET /policies/file-system-replica-links" + ], + "annotations": [] + }, + { + "name": "role", + "endpointCount": 3, + "queryEndpointCount": 0, + "bodyEndpointCount": 3, + "endpoints": [ + "PATCH /admins", + "PATCH /directory-services/roles", + "POST /directory-services/roles" + ], + "annotations": [] + }, + { + "name": "source", + "endpointCount": 3, + "queryEndpointCount": 2, + "bodyEndpointCount": 1, + "endpoints": [ + "GET /network-interfaces/ping", + "GET /network-interfaces/trace", + "POST /keytabs" + ], + "annotations": [] + }, + { + "name": "storage_class_names", + "endpointCount": 3, + "queryEndpointCount": 3, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /arrays/space/storage-classes", + "GET /file-systems/space/storage-classes", + "GET /realms/space/storage-classes" + ], + "annotations": [] + }, + { + "name": "uids", + "endpointCount": 3, + "queryEndpointCount": 3, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /file-systems/users/performance", + "GET /quotas/users", + "GET /usage/users" + ], + "annotations": [] + }, + { + "name": "admin_ids", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /admins/api-tokens", + "GET /admins/api-tokens" + ], + "annotations": [] + }, + { + "name": "admin_names", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /admins/api-tokens", + "GET /admins/api-tokens" + ], + "annotations": [] + }, + { + "name": "component_name", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /network-interfaces/ping", + "GET /network-interfaces/trace" + ], + "annotations": [] + }, + { + "name": "directory_configurations", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "POST /presets/workload", + "PUT /presets/workload" + ], + "annotations": [] + }, + { + "name": "eradicate_all_data", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "PATCH /arrays/erasures", + "POST /arrays/erasures" + ], + "annotations": [] + }, + { + "name": "eradication_config", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "PATCH /arrays", + "POST /buckets" + ], + "annotations": [] + }, + { + "name": "export_configurations", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "POST /presets/workload", + "PUT /presets/workload" + ], + "annotations": [] + }, + { + "name": "expose_api_token", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /admins", + "GET /admins/api-tokens" + ], + "annotations": [] + }, + { + "name": "group_gids", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /directory-services/local/groups/members", + "GET /directory-services/local/groups/members" + ], + "annotations": [] + }, + { + "name": "group_sids", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /directory-services/local/groups/members", + "GET /directory-services/local/groups/members" + ], + "annotations": [] + }, + { + "name": "hard_limit_enabled", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "POST /buckets", + "POST /object-store-accounts" + ], + "annotations": [] + }, + { + "name": "idp", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "POST /sso/oidc/idps", + "POST /sso/saml2/idps" + ], + "annotations": [] + }, + { + "name": "inodes", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /file-systems/locks", + "GET /file-systems/locks" + ], + "annotations": [] + }, + { + "name": "lockout_duration", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "PATCH /admins/settings", + "PATCH /password-policies" + ], + "annotations": [] + }, + { + "name": "max_login_attempts", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "PATCH /admins/settings", + "PATCH /password-policies" + ], + "annotations": [] + }, + { + "name": "max_role", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "PATCH /api-clients", + "POST /api-clients" + ], + "annotations": [] + }, + { + "name": "member_sids", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /directory-services/local/groups/members", + "GET /directory-services/local/groups/members" + ], + "annotations": [] + }, + { + "name": "min_password_length", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "PATCH /admins/settings", + "PATCH /password-policies" + ], + "annotations": [] + }, + { + "name": "name_prefixes", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "POST /keytabs", + "POST /keytabs/upload" + ], + "annotations": [] + }, + { + "name": "node_group_ids", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /node-groups/nodes", + "GET /node-groups/nodes" + ], + "annotations": [] + }, + { + "name": "node_group_names", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /node-groups/nodes", + "GET /node-groups/nodes" + ], + "annotations": [] + }, + { + "name": "node_ids", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /node-groups/nodes", + "GET /node-groups/nodes" + ], + "annotations": [] + }, + { + "name": "node_names", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /node-groups/nodes", + "GET /node-groups/nodes" + ], + "annotations": [] + }, + { + "name": "parameters", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "POST /presets/workload", + "PUT /presets/workload" + ], + "annotations": [] + }, + { + "name": "periodic_replication_configurations", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "POST /presets/workload", + "PUT /presets/workload" + ], + "annotations": [] + }, + { + "name": "permission", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "PATCH /nfs-export-policies/rules", + "PATCH /smb-client-policies/rules" + ], + "annotations": [] + }, + { + "name": "placement_configurations", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "POST /presets/workload", + "PUT /presets/workload" + ], + "annotations": [] + }, + { + "name": "platform_features", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "POST /presets/workload", + "PUT /presets/workload" + ], + "annotations": [] + }, + { + "name": "public_key", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "POST /api-clients", + "POST /public-keys" + ], + "annotations": [] + }, + { + "name": "qos_configurations", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "POST /presets/workload", + "PUT /presets/workload" + ], + "annotations": [] + }, + { + "name": "quota_configurations", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "POST /presets/workload", + "PUT /presets/workload" + ], + "annotations": [] + }, + { + "name": "resolve_hostname", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /network-interfaces/ping", + "GET /network-interfaces/trace" + ], + "annotations": [] + }, + { + "name": "secret_access_key", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "POST /object-store-access-keys", + "POST /object-store-remote-credentials" + ], + "annotations": [] + }, + { + "name": "services", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "POST /sso/oidc/idps", + "POST /sso/saml2/idps" + ], + "annotations": [] + }, + { + "name": "sids", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /directory-services/local/groups", + "GET /directory-services/local/groups" + ], + "annotations": [] + }, + { + "name": "snapshot_configurations", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "POST /presets/workload", + "PUT /presets/workload" + ], + "annotations": [] + }, + { + "name": "software_names", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /software-check", + "POST /software-check" + ], + "annotations": [] + }, + { + "name": "software_versions", + "endpointCount": 2, + "queryEndpointCount": 2, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /software-check", + "POST /software-check" + ], + "annotations": [] + }, + { + "name": "volume_configurations", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "POST /presets/workload", + "PUT /presets/workload" + ], + "annotations": [] + }, + { + "name": "workload_tags", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "POST /presets/workload", + "PUT /presets/workload" + ], + "annotations": [] + }, + { + "name": "workload_type", + "endpointCount": 2, + "queryEndpointCount": 0, + "bodyEndpointCount": 2, + "endpoints": [ + "POST /presets/workload", + "PUT /presets/workload" + ], + "annotations": [] + }, + { + "name": "abort_incomplete_multipart_uploads_after", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /lifecycle-rules" + ], + "annotations": [] + }, + { + "name": "access", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /nfs-export-policies/rules" + ], + "annotations": [] + }, + { + "name": "access_key_id", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /object-store-remote-credentials" + ], + "annotations": [] + }, + { + "name": "access_policies", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /api-clients" + ], + "annotations": [] + }, + { + "name": "access_token_ttl_in_ms", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /api-clients" + ], + "annotations": [] + }, + { + "name": "account_exports", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /object-store-accounts" + ], + "annotations": [] + }, + { + "name": "aggregation_strategy", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /management-access-policies" + ], + "annotations": [] + }, + { + "name": "allowed_headers", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /buckets/cross-origin-resource-sharing-policies/rules" + ], + "annotations": [] + }, + { + "name": "allowed_methods", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /buckets/cross-origin-resource-sharing-policies/rules" + ], + "annotations": [] + }, + { + "name": "allowed_origins", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /buckets/cross-origin-resource-sharing-policies/rules" + ], + "annotations": [] + }, + { + "name": "analysis_period_end_time", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "POST /support-diagnostics" + ], + "annotations": [] + }, + { + "name": "analysis_period_start_time", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "POST /support-diagnostics" + ], + "annotations": [] + }, + { + "name": "anongid", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /nfs-export-policies/rules" + ], + "annotations": [] + }, + { + "name": "anonuid", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /nfs-export-policies/rules" + ], + "annotations": [] + }, + { + "name": "array_url", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /sso/saml2/idps" + ], + "annotations": [] + }, + { + "name": "atime", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /nfs-export-policies/rules" + ], + "annotations": [] + }, + { + "name": "attached_servers", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /object-store-virtual-hosts" + ], + "annotations": [] + }, + { + "name": "banner", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /arrays" + ], + "annotations": [] + }, + { + "name": "binding", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /sso/saml2/idps" + ], + "annotations": [] + }, + { + "name": "bucket", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /log-targets/object-store" + ], + "annotations": [] + }, + { + "name": "bucket_defaults", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /object-store-accounts" + ], + "annotations": [] + }, + { + "name": "bucket_type", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /buckets" + ], + "annotations": [] + }, + { + "name": "ca_certificate", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /syslog-servers/settings" + ], + "annotations": [] + }, + { + "name": "ca_certificate_group", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /syslog-servers/settings" + ], + "annotations": [] + }, + { + "name": "certificate", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /certificates" + ], + "annotations": [] + }, + { + "name": "certificate_group_names", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /certificate-groups/certificates" + ], + "annotations": [] + }, + { + "name": "certificate_names", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /certificate-groups/certificates" + ], + "annotations": [] + }, + { + "name": "certificate_type", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /certificates" + ], + "annotations": [] + }, + { + "name": "change", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /smb-share-policies/rules" + ], + "annotations": [] + }, + { + "name": "common_name", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /certificates" + ], + "annotations": [] + }, + { + "name": "confirm_date", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "POST /lifecycle-rules" + ], + "annotations": [] + }, + { + "name": "contact", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /quotas/settings" + ], + "annotations": [] + }, + { + "name": "country", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /certificates" + ], + "annotations": [] + }, + { + "name": "current_state", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /network-interfaces/network-connection-statistics" + ], + "annotations": [] + }, + { + "name": "days", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /certificates" + ], + "annotations": [] + }, + { + "name": "default_inbound_tls_policy", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /arrays" + ], + "annotations": [] + }, + { + "name": "delete_sanitization_certificate", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "PATCH /arrays/erasures" + ], + "annotations": [] + }, + { + "name": "destroyed", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /directory-services/local/directory-services" + ], + "annotations": [] + }, + { + "name": "direct_notifications_enabled", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /quotas/settings" + ], + "annotations": [] + }, + { + "name": "direction", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /file-system-replica-links" + ], + "annotations": [] + }, + { + "name": "discover_mtu", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /network-interfaces/trace" + ], + "annotations": [] + }, + { + "name": "edge_agent_update_enabled", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /support" + ], + "annotations": [] + }, + { + "name": "edge_management_enabled", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /support" + ], + "annotations": [] + }, + { + "name": "effective", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /tls-policies" + ], + "annotations": [] + }, + { + "name": "email", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /certificates" + ], + "annotations": [] + }, + { + "name": "encryption", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /smb-client-policies/rules" + ], + "annotations": [] + }, + { + "name": "enforce_dictionary_check", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /password-policies" + ], + "annotations": [] + }, + { + "name": "enforce_username_check", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /password-policies" + ], + "annotations": [] + }, + { + "name": "exclude_rules", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /object-store-access-policies" + ], + "annotations": [] + }, + { + "name": "export_enabled", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /object-store-account-exports" + ], + "annotations": [] + }, + { + "name": "file_system", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /log-targets/file-systems" + ], + "annotations": [] + }, + { + "name": "fileid_32bit", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /nfs-export-policies/rules" + ], + "annotations": [] + }, + { + "name": "finalize", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "PATCH /arrays/erasures" + ], + "annotations": [] + }, + { + "name": "fleet_ids", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /fleets/members" + ], + "annotations": [] + }, + { + "name": "fragment_packet", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /network-interfaces/trace" + ], + "annotations": [] + }, + { + "name": "full_access", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "POST /object-store-users" + ], + "annotations": [] + }, + { + "name": "full_control", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /smb-share-policies/rules" + ], + "annotations": [] + }, + { + "name": "group", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /directory-services/roles" + ], + "annotations": [] + }, + { + "name": "group_base", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /directory-services/roles" + ], + "annotations": [] + }, + { + "name": "idle_timeout", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /arrays" + ], + "annotations": [] + }, + { + "name": "interfaces", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /network-access-policies/rules" + ], + "annotations": [] + }, + { + "name": "intermediate_certificate", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /certificates" + ], + "annotations": [] + }, + { + "name": "issuer", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /api-clients" + ], + "annotations": [] + }, + { + "name": "keep_current_version_for", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /lifecycle-rules" + ], + "annotations": [] + }, + { + "name": "keep_current_version_until", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /lifecycle-rules" + ], + "annotations": [] + }, + { + "name": "keep_for", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /log-targets/file-systems" + ], + "annotations": [] + }, + { + "name": "keep_previous_version_for", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /lifecycle-rules" + ], + "annotations": [] + }, + { + "name": "keep_size", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /log-targets/file-systems" + ], + "annotations": [] + }, + { + "name": "key_algorithm", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /certificates" + ], + "annotations": [] + }, + { + "name": "key_size", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /certificates" + ], + "annotations": [] + }, + { + "name": "keytab_file", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /keytabs/upload" + ], + "annotations": [] + }, + { + "name": "keytab_ids", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /keytabs/download" + ], + "annotations": [] + }, + { + "name": "keytab_names", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /keytabs/download" + ], + "annotations": [] + }, + { + "name": "kmip_server", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /rapid-data-locking" + ], + "annotations": [] + }, + { + "name": "link_type", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /file-system-replica-links" + ], + "annotations": [] + }, + { + "name": "local_bucket_ids", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /bucket-replica-links" + ], + "annotations": [] + }, + { + "name": "local_file_system", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /file-system-replica-links" + ], + "annotations": [] + }, + { + "name": "local_host", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /network-interfaces/network-connection-statistics" + ], + "annotations": [] + }, + { + "name": "local_only", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /active-directory" + ], + "annotations": [] + }, + { + "name": "local_port", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /network-interfaces/network-connection-statistics" + ], + "annotations": [] + }, + { + "name": "local_port_names", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /network-interfaces/neighbors" + ], + "annotations": [] + }, + { + "name": "locality", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /certificates" + ], + "annotations": [] + }, + { + "name": "log_name_prefix", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /log-targets/object-store" + ], + "annotations": [] + }, + { + "name": "log_rotate", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /log-targets/object-store" + ], + "annotations": [] + }, + { + "name": "management", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /sso/saml2/idps" + ], + "annotations": [] + }, + { + "name": "management_access_policies", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /directory-services/roles" + ], + "annotations": [] + }, + { + "name": "max_password_age", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /password-policies" + ], + "annotations": [] + }, + { + "name": "max_session_duration", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /object-store-roles" + ], + "annotations": [] + }, + { + "name": "max_total_bytes_per_sec", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /qos-policies" + ], + "annotations": [] + }, + { + "name": "max_total_ops_per_sec", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /qos-policies" + ], + "annotations": [] + }, + { + "name": "member_names", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "POST /object-store-account-exports" + ], + "annotations": [] + }, + { + "name": "min_character_groups", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /password-policies" + ], + "annotations": [] + }, + { + "name": "min_characters_per_group", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /password-policies" + ], + "annotations": [] + }, + { + "name": "min_password_age", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /password-policies" + ], + "annotations": [] + }, + { + "name": "network_access_policy", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /arrays" + ], + "annotations": [] + }, + { + "name": "ntp_servers", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /arrays" + ], + "annotations": [] + }, + { + "name": "object_lock_config", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /buckets" + ], + "annotations": [] + }, + { + "name": "organization", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /certificates" + ], + "annotations": [] + }, + { + "name": "organizational_unit", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /certificates" + ], + "annotations": [] + }, + { + "name": "owner_ids", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /file-system-snapshots" + ], + "annotations": [] + }, + { + "name": "passphrase", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /certificates" + ], + "annotations": [] + }, + { + "name": "password_history", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /password-policies" + ], + "annotations": [] + }, + { + "name": "phonehome_enabled", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /support" + ], + "annotations": [] + }, + { + "name": "policies", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /file-system-replica-links" + ], + "annotations": [] + }, + { + "name": "port", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /network-interfaces/trace" + ], + "annotations": [] + }, + { + "name": "prefix", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /lifecycle-rules" + ], + "annotations": [] + }, + { + "name": "preserve_configuration_data", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "POST /arrays/erasures" + ], + "annotations": [] + }, + { + "name": "principal", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /smb-share-policies/rules" + ], + "annotations": [] + }, + { + "name": "print_latency", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /network-interfaces/ping" + ], + "annotations": [] + }, + { + "name": "private_key", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /certificates" + ], + "annotations": [] + }, + { + "name": "protocol", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /arrays/clients/performance" + ], + "annotations": [] + }, + { + "name": "protocols", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /file-systems/open-files" + ], + "annotations": [] + }, + { + "name": "proxy", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /support" + ], + "annotations": [] + }, + { + "name": "purity_defined", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /tls-policies" + ], + "annotations": [] + }, + { + "name": "quota_limit", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /object-store-accounts" + ], + "annotations": [] + }, + { + "name": "rdma_enabled", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /network-interfaces" + ], + "annotations": [] + }, + { + "name": "read", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /smb-share-policies/rules" + ], + "annotations": [] + }, + { + "name": "realm_ids", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /realms/defaults" + ], + "annotations": [] + }, + { + "name": "realm_names", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /realms/defaults" + ], + "annotations": [] + }, + { + "name": "recursive", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /file-systems/locks" + ], + "annotations": [] + }, + { + "name": "refresh", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /admins/cache" + ], + "annotations": [] + }, + { + "name": "remote", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /file-system-replica-links" + ], + "annotations": [] + }, + { + "name": "remote_assist_active", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /support" + ], + "annotations": [] + }, + { + "name": "remote_assist_duration", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /support" + ], + "annotations": [] + }, + { + "name": "remote_file_system", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /file-system-replica-links" + ], + "annotations": [] + }, + { + "name": "remote_host", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /network-interfaces/network-connection-statistics" + ], + "annotations": [] + }, + { + "name": "remote_port", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /network-interfaces/network-connection-statistics" + ], + "annotations": [] + }, + { + "name": "required_transport_security", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /nfs-export-policies/rules" + ], + "annotations": [] + }, + { + "name": "retention_lock", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /buckets" + ], + "annotations": [] + }, + { + "name": "rule_id", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /lifecycle-rules" + ], + "annotations": [] + }, + { + "name": "s3_prefixes", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /buckets/audit-filters" + ], + "annotations": [] + }, + { + "name": "secure", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /nfs-export-policies/rules" + ], + "annotations": [] + }, + { + "name": "security", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /nfs-export-policies/rules" + ], + "annotations": [] + }, + { + "name": "server", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /object-store-account-exports" + ], + "annotations": [] + }, + { + "name": "session_names", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /file-systems/open-files" + ], + "annotations": [] + }, + { + "name": "signature", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /arrays/eula" + ], + "annotations": [] + }, + { + "name": "signed_verification_key", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /support/verification-keys" + ], + "annotations": [] + }, + { + "name": "skip_phonehome_check", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "POST /arrays/erasures" + ], + "annotations": [] + }, + { + "name": "sp", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /sso/saml2/idps" + ], + "annotations": [] + }, + { + "name": "state", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /certificates" + ], + "annotations": [] + }, + { + "name": "subject_alternative_names", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /certificates" + ], + "annotations": [] + }, + { + "name": "time_zone", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /arrays" + ], + "annotations": [] + }, + { + "name": "timeout", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "POST /maintenance-windows" + ], + "annotations": [] + }, + { + "name": "type", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "GET /realms/space" + ], + "annotations": [] + }, + { + "name": "unreachable", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "DELETE /fleets/members" + ], + "annotations": [] + }, + { + "name": "v2c", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /snmp-agents" + ], + "annotations": [] + }, + { + "name": "v3", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /snmp-agents" + ], + "annotations": [] + }, + { + "name": "version", + "endpointCount": 1, + "queryEndpointCount": 0, + "bodyEndpointCount": 1, + "endpoints": [ + "PATCH /snmp-agents" + ], + "annotations": [] + }, + { + "name": "without_default_access_list", + "endpointCount": 1, + "queryEndpointCount": 1, + "bodyEndpointCount": 0, + "endpoints": [ + "POST /realms" + ], + "annotations": [] + } + ], + "conventionStrength": [ + { + "name": "names", + "cmdletCount": 308, + "cmdlets": [ + "Get-PfbActiveDirectory", + "Get-PfbAdmin", + "Get-PfbAdminCache", + "Get-PfbAlert", + "Get-PfbAlertWatcher", + "Get-PfbApiClient", + "Get-PfbApiToken", + "Get-PfbArrayConnectionKey", + "Get-PfbAsyncLog", + "Get-PfbAsyncLogDownload", + "Get-PfbAudit", + "Get-PfbAuditFileSystemPolicy", + "Get-PfbAuditObjectStorePolicy", + "Get-PfbBlade", + "Get-PfbBucket", + "Get-PfbBucketAccessPolicy", + "Get-PfbBucketAccessPolicyRule", + "Get-PfbBucketCorsPolicy", + "Get-PfbBucketCorsPolicyRule", + "Get-PfbBucketPerformance", + "Get-PfbBucketS3Performance", + "Get-PfbCertificate", + "Get-PfbCertificateGroup", + "Get-PfbCertificateGroupCertificate", + "Get-PfbCertificateGroupUse", + "Get-PfbCertificateUse", + "Get-PfbDataEvictionPolicy", + "Get-PfbDirectoryService", + "Get-PfbDirectoryServiceRole", + "Get-PfbDrive", + "Get-PfbFileLock", + "Get-PfbFileLockClient", + "Get-PfbFileSystem", + "Get-PfbFileSystemExport", + "Get-PfbFileSystemReplicaLinkTransfer", + "Get-PfbFileSystemSession", + "Get-PfbFileSystemSnapshot", + "Get-PfbFileSystemSnapshotTransfer", + "Get-PfbFileSystemStorageClass", + "Get-PfbFleet", + "Get-PfbFleetKey", + "Get-PfbHardware", + "Get-PfbHardwareConnector", + "Get-PfbHardwareConnectorPerformance", + "Get-PfbHardwareTemperature", + "Get-PfbKeytab", + "Get-PfbKeytabDownload", + "Get-PfbKmip", + "Get-PfbLag", + "Get-PfbLegalHold", + "Get-PfbLifecycleRule", + "Get-PfbLocalDirectoryService", + "Get-PfbLocalGroup", + "Get-PfbLogTargetFileSystem", + "Get-PfbLogTargetObjectStore", + "Get-PfbMaintenanceWindow", + "Get-PfbManagementAccessPolicy", + "Get-PfbNetworkAccessPolicy", + "Get-PfbNetworkAccessRule", + "Get-PfbNetworkConnectionStatistics", + "Get-PfbNetworkInterface", + "Get-PfbNetworkInterfaceConnector", + "Get-PfbNetworkInterfaceConnectorPerformance", + "Get-PfbNetworkInterfaceConnectorSettings", + "Get-PfbNetworkInterfaceNeighbor", + "Get-PfbNfsExportPolicy", + "Get-PfbNfsExportRule", + "Get-PfbNode", + "Get-PfbNodeGroup", + "Get-PfbNodeGroupUse", + "Get-PfbObjectStoreAccessKey", + "Get-PfbObjectStoreAccessPolicy", + "Get-PfbObjectStoreAccessPolicyRule", + "Get-PfbObjectStoreAccount", + "Get-PfbObjectStoreAccountExport", + "Get-PfbObjectStoreRemoteCredential", + "Get-PfbObjectStoreRole", + "Get-PfbObjectStoreTrustPolicyRule", + "Get-PfbObjectStoreUser", + "Get-PfbObjectStoreVirtualHost", + "Get-PfbOidcIdp", + "Get-PfbOpenFile", + "Get-PfbPolicy", + "Get-PfbPolicyAll", + "Get-PfbPresetWorkload", + "Get-PfbPublicKey", + "Get-PfbPublicKeyUse", + "Get-PfbQosPolicy", + "Get-PfbQuotaGroup", + "Get-PfbQuotaUser", + "Get-PfbRealm", + "Get-PfbRealmDefaults", + "Get-PfbRealmSpace", + "Get-PfbRealmStorageClass", + "Get-PfbRemoteArray", + "Get-PfbResiliencyGroup", + "Get-PfbResourceAccess", + "Get-PfbRole", + "Get-PfbS3ExportPolicy", + "Get-PfbS3ExportRule", + "Get-PfbSaml2Idp", + "Get-PfbServer", + "Get-PfbSession", + "Get-PfbSmbClientPolicy", + "Get-PfbSmbClientRule", + "Get-PfbSmbSharePolicy", + "Get-PfbSmbShareRule", + "Get-PfbSnmpManager", + "Get-PfbSshCaPolicy", + "Get-PfbStorageClassTieringPolicy", + "Get-PfbSubnet", + "Get-PfbSupport", + "Get-PfbSupportDiagnostics", + "Get-PfbSupportDiagnosticsDetails", + "Get-PfbSyslogServer", + "Get-PfbTarget", + "Get-PfbTargetPerformanceReplication", + "Get-PfbTlsPolicy", + "Get-PfbUserGroupQuotaPolicyRule", + "Get-PfbWorkload", + "Get-PfbWorkloadPlacementRecommendation", + "Get-PfbWormPolicy", + "New-PfbAlertWatcher", + "New-PfbApiClient", + "New-PfbAuditFileSystemPolicy", + "New-PfbAuditObjectStorePolicy", + "New-PfbBucket", + "New-PfbCertificate", + "New-PfbCertificateGroup", + "New-PfbDataEvictionPolicy", + "New-PfbDirectoryServiceRole", + "New-PfbFileSystem", + "New-PfbLegalHold", + "New-PfbLegalHoldEntity", + "New-PfbLocalDirectoryService", + "New-PfbLocalGroup", + "New-PfbLogTargetFileSystem", + "New-PfbLogTargetObjectStore", + "New-PfbManagementAccessPolicy", + "New-PfbNetworkInterface", + "New-PfbNfsExportPolicy", + "New-PfbNlmReclamation", + "New-PfbObjectStoreAccessPolicy", + "New-PfbObjectStoreAccount", + "New-PfbObjectStoreAccountExport", + "New-PfbObjectStoreRemoteCredential", + "New-PfbObjectStoreRole", + "New-PfbObjectStoreUser", + "New-PfbObjectStoreVirtualHost", + "New-PfbOidcIdp", + "New-PfbPolicy", + "New-PfbPresetWorkload", + "New-PfbPublicKey", + "New-PfbQosPolicy", + "New-PfbRealm", + "New-PfbS3ExportPolicy", + "New-PfbS3ExportRule", + "New-PfbSaml2Idp", + "New-PfbServer", + "New-PfbSmbClientPolicy", + "New-PfbSmbSharePolicy", + "New-PfbSubnet", + "New-PfbUserGroupQuotaPolicy", + "New-PfbWorkload", + "Remove-PfbActiveDirectory", + "Remove-PfbAdminCache", + "Remove-PfbAlertWatcher", + "Remove-PfbApiClient", + "Remove-PfbApiToken", + "Remove-PfbAuditFileSystemPolicy", + "Remove-PfbAuditObjectStorePolicy", + "Remove-PfbBucket", + "Remove-PfbBucketAccessPolicyRule", + "Remove-PfbBucketCorsPolicyRule", + "Remove-PfbCertificate", + "Remove-PfbCertificateGroup", + "Remove-PfbDataEvictionPolicy", + "Remove-PfbDirectoryServiceRole", + "Remove-PfbDns", + "Remove-PfbFileLock", + "Remove-PfbFileSystem", + "Remove-PfbFileSystemExport", + "Remove-PfbFileSystemSession", + "Remove-PfbFileSystemSnapshot", + "Remove-PfbFileSystemSnapshotTransfer", + "Remove-PfbFleet", + "Remove-PfbKeytab", + "Remove-PfbLag", + "Remove-PfbLegalHold", + "Remove-PfbLifecycleRule", + "Remove-PfbLocalGroup", + "Remove-PfbLogTargetFileSystem", + "Remove-PfbLogTargetObjectStore", + "Remove-PfbMaintenanceWindow", + "Remove-PfbManagementAccessPolicy", + "Remove-PfbNetworkAccessRule", + "Remove-PfbNetworkInterface", + "Remove-PfbNfsExportPolicy", + "Remove-PfbNfsExportRule", + "Remove-PfbNodeGroup", + "Remove-PfbObjectStoreAccessKey", + "Remove-PfbObjectStoreAccessPolicy", + "Remove-PfbObjectStoreAccessPolicyRule", + "Remove-PfbObjectStoreAccount", + "Remove-PfbObjectStoreAccountExport", + "Remove-PfbObjectStoreRemoteCredential", + "Remove-PfbObjectStoreRole", + "Remove-PfbObjectStoreTrustPolicyRule", + "Remove-PfbObjectStoreUser", + "Remove-PfbObjectStoreVirtualHost", + "Remove-PfbOidcIdp", + "Remove-PfbOpenFile", + "Remove-PfbPolicy", + "Remove-PfbPresetWorkload", + "Remove-PfbPublicKey", + "Remove-PfbQosPolicy", + "Remove-PfbRealm", + "Remove-PfbResourceAccess", + "Remove-PfbS3ExportPolicy", + "Remove-PfbS3ExportRule", + "Remove-PfbSaml2Idp", + "Remove-PfbServer", + "Remove-PfbSmbClientPolicy", + "Remove-PfbSmbClientRule", + "Remove-PfbSmbSharePolicy", + "Remove-PfbSmbShareRule", + "Remove-PfbSnmpManager", + "Remove-PfbSshCaPolicy", + "Remove-PfbStorageClassTieringPolicy", + "Remove-PfbSubnet", + "Remove-PfbSyslogServer", + "Remove-PfbTarget", + "Remove-PfbTlsPolicy", + "Remove-PfbUserGroupQuotaPolicy", + "Remove-PfbUserGroupQuotaPolicyRule", + "Remove-PfbWorkload", + "Remove-PfbWormPolicy", + "Set-PfbPresetWorkload", + "Test-PfbActiveDirectory", + "Test-PfbAlertWatcher", + "Test-PfbKmip", + "Test-PfbSaml2Idp", + "Test-PfbSnmpManager", + "Test-PfbSyslogServer", + "Update-PfbActiveDirectory", + "Update-PfbAdmin", + "Update-PfbAlert", + "Update-PfbAlertWatcher", + "Update-PfbApiClient", + "Update-PfbAuditFileSystemPolicy", + "Update-PfbAuditObjectStorePolicy", + "Update-PfbBucket", + "Update-PfbBucketAuditFilter", + "Update-PfbCertificate", + "Update-PfbDataEvictionPolicy", + "Update-PfbDirectoryServiceRole", + "Update-PfbDns", + "Update-PfbFileSystem", + "Update-PfbFileSystemExport", + "Update-PfbFleet", + "Update-PfbHardware", + "Update-PfbHardwareConnector", + "Update-PfbKmip", + "Update-PfbLag", + "Update-PfbLegalHold", + "Update-PfbLegalHoldEntity", + "Update-PfbLifecycleRule", + "Update-PfbLogTargetFileSystem", + "Update-PfbLogTargetObjectStore", + "Update-PfbManagementAccessPolicy", + "Update-PfbNetworkAccessPolicy", + "Update-PfbNetworkAccessRule", + "Update-PfbNetworkInterface", + "Update-PfbNetworkInterfaceConnector", + "Update-PfbNfsExportPolicy", + "Update-PfbNfsExportRule", + "Update-PfbNode", + "Update-PfbNodeGroup", + "Update-PfbObjectStoreAccessPolicyRule", + "Update-PfbObjectStoreAccountExport", + "Update-PfbObjectStoreRemoteCredential", + "Update-PfbObjectStoreRole", + "Update-PfbObjectStoreTrustPolicyRule", + "Update-PfbObjectStoreVirtualHost", + "Update-PfbOidcIdp", + "Update-PfbPolicy", + "Update-PfbPresetWorkload", + "Update-PfbQosPolicy", + "Update-PfbRealm", + "Update-PfbS3ExportPolicy", + "Update-PfbS3ExportRule", + "Update-PfbSaml2Idp", + "Update-PfbServer", + "Update-PfbSmbClientPolicy", + "Update-PfbSmbClientRule", + "Update-PfbSmbSharePolicy", + "Update-PfbSmbShareRule", + "Update-PfbSnmpManager", + "Update-PfbSshCaPolicy", + "Update-PfbStorageClassTieringPolicy", + "Update-PfbSubnet", + "Update-PfbSyslogServer", + "Update-PfbTarget", + "Update-PfbTlsPolicy", + "Update-PfbUserGroupQuotaPolicy", + "Update-PfbUserGroupQuotaPolicyRule", + "Update-PfbWorkload", + "Update-PfbWormPolicy" + ] + }, + { + "name": "ids", + "cmdletCount": 223, + "cmdlets": [ + "Get-PfbAdmin", + "Get-PfbAdminCache", + "Get-PfbAlert", + "Get-PfbAlertWatcher", + "Get-PfbApiClient", + "Get-PfbApiToken", + "Get-PfbArrayConnection", + "Get-PfbAsyncLog", + "Get-PfbAsyncLogDownload", + "Get-PfbAudit", + "Get-PfbAuditFileSystemPolicy", + "Get-PfbAuditObjectStorePolicy", + "Get-PfbBlade", + "Get-PfbBucket", + "Get-PfbBucketAccessPolicy", + "Get-PfbBucketAccessPolicyRule", + "Get-PfbBucketCorsPolicy", + "Get-PfbBucketCorsPolicyRule", + "Get-PfbBucketPerformance", + "Get-PfbBucketS3Performance", + "Get-PfbCertificate", + "Get-PfbCertificateGroup", + "Get-PfbCertificateGroupCertificate", + "Get-PfbDataEvictionPolicy", + "Get-PfbDirectoryServiceRole", + "Get-PfbDrive", + "Get-PfbFileLock", + "Get-PfbFileLockClient", + "Get-PfbFileSystem", + "Get-PfbFileSystemExport", + "Get-PfbFileSystemReplicaLinkTransfer", + "Get-PfbFileSystemSnapshot", + "Get-PfbFileSystemSnapshotTransfer", + "Get-PfbFileSystemStorageClass", + "Get-PfbFleet", + "Get-PfbHardware", + "Get-PfbHardwareConnector", + "Get-PfbKeytab", + "Get-PfbKeytabDownload", + "Get-PfbKmip", + "Get-PfbLag", + "Get-PfbLegalHold", + "Get-PfbLifecycleRule", + "Get-PfbLocalDirectoryService", + "Get-PfbLocalGroup", + "Get-PfbLogTargetFileSystem", + "Get-PfbLogTargetObjectStore", + "Get-PfbMaintenanceWindow", + "Get-PfbManagementAccessPolicy", + "Get-PfbNetworkAccessPolicy", + "Get-PfbNetworkInterface", + "Get-PfbNetworkInterfaceConnector", + "Get-PfbNfsExportPolicy", + "Get-PfbNode", + "Get-PfbNodeGroup", + "Get-PfbObjectStoreAccessKey", + "Get-PfbObjectStoreAccessPolicy", + "Get-PfbObjectStoreAccount", + "Get-PfbObjectStoreAccountExport", + "Get-PfbObjectStoreRemoteCredential", + "Get-PfbObjectStoreRole", + "Get-PfbObjectStoreUser", + "Get-PfbObjectStoreVirtualHost", + "Get-PfbOidcIdp", + "Get-PfbOpenFile", + "Get-PfbPolicy", + "Get-PfbPolicyAll", + "Get-PfbPresetWorkload", + "Get-PfbPublicKey", + "Get-PfbQosPolicy", + "Get-PfbRealm", + "Get-PfbRemoteArray", + "Get-PfbResiliencyGroup", + "Get-PfbResourceAccess", + "Get-PfbS3ExportPolicy", + "Get-PfbSaml2Idp", + "Get-PfbServer", + "Get-PfbSession", + "Get-PfbSmbClientPolicy", + "Get-PfbSmbSharePolicy", + "Get-PfbSnmpManager", + "Get-PfbSshCaPolicy", + "Get-PfbStorageClassTieringPolicy", + "Get-PfbSubnet", + "Get-PfbSupportDiagnostics", + "Get-PfbSupportDiagnosticsDetails", + "Get-PfbSyslogServer", + "Get-PfbTarget", + "Get-PfbTlsPolicy", + "Get-PfbUserGroupQuotaPolicyRule", + "Get-PfbWorkload", + "Get-PfbWorkloadPlacementRecommendation", + "Get-PfbWormPolicy", + "New-PfbLegalHoldEntity", + "Remove-PfbActiveDirectory", + "Remove-PfbAdminCache", + "Remove-PfbAlertWatcher", + "Remove-PfbApiClient", + "Remove-PfbApiToken", + "Remove-PfbArrayConnection", + "Remove-PfbAuditFileSystemPolicy", + "Remove-PfbAuditObjectStorePolicy", + "Remove-PfbBucket", + "Remove-PfbCertificate", + "Remove-PfbCertificateGroup", + "Remove-PfbDataEvictionPolicy", + "Remove-PfbDirectoryServiceRole", + "Remove-PfbDns", + "Remove-PfbFileLock", + "Remove-PfbFileSystem", + "Remove-PfbFileSystemExport", + "Remove-PfbFileSystemReplicaLink", + "Remove-PfbFileSystemSnapshot", + "Remove-PfbFileSystemSnapshotTransfer", + "Remove-PfbFleet", + "Remove-PfbKeytab", + "Remove-PfbLag", + "Remove-PfbLegalHold", + "Remove-PfbLifecycleRule", + "Remove-PfbLocalGroup", + "Remove-PfbLogTargetFileSystem", + "Remove-PfbLogTargetObjectStore", + "Remove-PfbMaintenanceWindow", + "Remove-PfbManagementAccessPolicy", + "Remove-PfbNetworkInterface", + "Remove-PfbNfsExportPolicy", + "Remove-PfbNodeGroup", + "Remove-PfbObjectStoreAccessKey", + "Remove-PfbObjectStoreAccessPolicy", + "Remove-PfbObjectStoreAccount", + "Remove-PfbObjectStoreAccountExport", + "Remove-PfbObjectStoreRemoteCredential", + "Remove-PfbObjectStoreRole", + "Remove-PfbObjectStoreUser", + "Remove-PfbObjectStoreVirtualHost", + "Remove-PfbOidcIdp", + "Remove-PfbOpenFile", + "Remove-PfbPolicy", + "Remove-PfbPresetWorkload", + "Remove-PfbPublicKey", + "Remove-PfbQosPolicy", + "Remove-PfbRealm", + "Remove-PfbResourceAccess", + "Remove-PfbS3ExportPolicy", + "Remove-PfbSaml2Idp", + "Remove-PfbServer", + "Remove-PfbSmbClientPolicy", + "Remove-PfbSmbSharePolicy", + "Remove-PfbSnmpManager", + "Remove-PfbSshCaPolicy", + "Remove-PfbStorageClassTieringPolicy", + "Remove-PfbSubnet", + "Remove-PfbSyslogServer", + "Remove-PfbTarget", + "Remove-PfbTlsPolicy", + "Remove-PfbUserGroupQuotaPolicy", + "Remove-PfbUserGroupQuotaPolicyRule", + "Remove-PfbWorkload", + "Remove-PfbWormPolicy", + "Set-PfbPresetWorkload", + "Test-PfbActiveDirectory", + "Test-PfbAlertWatcher", + "Test-PfbKmip", + "Test-PfbSaml2Idp", + "Test-PfbSnmpManager", + "Test-PfbSyslogServer", + "Update-PfbActiveDirectory", + "Update-PfbAdmin", + "Update-PfbAlert", + "Update-PfbAlertWatcher", + "Update-PfbApiClient", + "Update-PfbArrayConnection", + "Update-PfbAuditFileSystemPolicy", + "Update-PfbAuditObjectStorePolicy", + "Update-PfbBucket", + "Update-PfbCertificate", + "Update-PfbDataEvictionPolicy", + "Update-PfbDirectoryServiceRole", + "Update-PfbDns", + "Update-PfbFileSystem", + "Update-PfbFileSystemExport", + "Update-PfbFleet", + "Update-PfbHardware", + "Update-PfbHardwareConnector", + "Update-PfbKmip", + "Update-PfbLag", + "Update-PfbLegalHold", + "Update-PfbLegalHoldEntity", + "Update-PfbLifecycleRule", + "Update-PfbLogTargetFileSystem", + "Update-PfbLogTargetObjectStore", + "Update-PfbManagementAccessPolicy", + "Update-PfbNetworkAccessPolicy", + "Update-PfbNetworkInterface", + "Update-PfbNetworkInterfaceConnector", + "Update-PfbNfsExportPolicy", + "Update-PfbNode", + "Update-PfbNodeGroup", + "Update-PfbObjectStoreAccountExport", + "Update-PfbObjectStoreRemoteCredential", + "Update-PfbObjectStoreRole", + "Update-PfbObjectStoreVirtualHost", + "Update-PfbOidcIdp", + "Update-PfbPolicy", + "Update-PfbPresetWorkload", + "Update-PfbQosPolicy", + "Update-PfbRealm", + "Update-PfbS3ExportPolicy", + "Update-PfbSaml2Idp", + "Update-PfbServer", + "Update-PfbSmbClientPolicy", + "Update-PfbSmbSharePolicy", + "Update-PfbSnmpManager", + "Update-PfbSshCaPolicy", + "Update-PfbStorageClassTieringPolicy", + "Update-PfbSubnet", + "Update-PfbSyslogServer", + "Update-PfbTarget", + "Update-PfbTlsPolicy", + "Update-PfbUserGroupQuotaPolicy", + "Update-PfbUserGroupQuotaPolicyRule", + "Update-PfbWorkload", + "Update-PfbWormPolicy" + ] + }, + { + "name": "filter", + "cmdletCount": 202, + "cmdlets": [ + "Get-PfbActiveDirectory", + "Get-PfbAdmin", + "Get-PfbAdminCache", + "Get-PfbAdminManagementAccessPolicy", + "Get-PfbAdminSetting", + "Get-PfbAdminSshCaPolicy", + "Get-PfbAlert", + "Get-PfbAlertWatcher", + "Get-PfbApiClient", + "Get-PfbApiToken", + "Get-PfbArray", + "Get-PfbArrayClientPerformance", + "Get-PfbArrayClientS3Performance", + "Get-PfbArrayConnection", + "Get-PfbArrayConnectionKey", + "Get-PfbArrayConnectionPath", + "Get-PfbArrayConnectionPerformanceReplication", + "Get-PfbArrayErasure", + "Get-PfbArrayEula", + "Get-PfbArrayFactoryResetToken", + "Get-PfbArrayHttpPerformance", + "Get-PfbArrayNfsPerformance", + "Get-PfbArrayPerformanceReplication", + "Get-PfbArrayS3Performance", + "Get-PfbArraySshCaPolicy", + "Get-PfbArrayStorageClass", + "Get-PfbArraySupportedTimeZone", + "Get-PfbAsyncLog", + "Get-PfbAudit", + "Get-PfbAuditFileSystemPolicy", + "Get-PfbAuditFileSystemPolicyMember", + "Get-PfbAuditFileSystemPolicyOperation", + "Get-PfbAuditObjectStorePolicy", + "Get-PfbAuditObjectStorePolicyMember", + "Get-PfbBlade", + "Get-PfbBucket", + "Get-PfbBucketAccessPolicy", + "Get-PfbBucketAccessPolicyRule", + "Get-PfbBucketAuditFilter", + "Get-PfbBucketAuditFilterAction", + "Get-PfbBucketCorsPolicy", + "Get-PfbBucketCorsPolicyRule", + "Get-PfbBucketPerformance", + "Get-PfbBucketReplicaLink", + "Get-PfbBucketS3Performance", + "Get-PfbCertificate", + "Get-PfbCertificateCertificateGroup", + "Get-PfbCertificateGroup", + "Get-PfbCertificateGroupCertificate", + "Get-PfbCertificateGroupUse", + "Get-PfbCertificateUse", + "Get-PfbDataEvictionPolicy", + "Get-PfbDataEvictionPolicyFileSystem", + "Get-PfbDataEvictionPolicyMember", + "Get-PfbDirectoryService", + "Get-PfbDirectoryServiceRole", + "Get-PfbDirectoryServiceRoleManagementPolicy", + "Get-PfbDns", + "Get-PfbDrive", + "Get-PfbFileLock", + "Get-PfbFileLockClient", + "Get-PfbFileSystem", + "Get-PfbFileSystemAuditPolicy", + "Get-PfbFileSystemExport", + "Get-PfbFileSystemGroup", + "Get-PfbFileSystemGroupPerformance", + "Get-PfbFileSystemGroupQuota", + "Get-PfbFileSystemPolicy", + "Get-PfbFileSystemReplicaLink", + "Get-PfbFileSystemReplicaLinkPolicy", + "Get-PfbFileSystemReplicaLinkTransfer", + "Get-PfbFileSystemSession", + "Get-PfbFileSystemSnapshot", + "Get-PfbFileSystemSnapshotPolicy", + "Get-PfbFileSystemSnapshotTransfer", + "Get-PfbFileSystemStorageClass", + "Get-PfbFileSystemUser", + "Get-PfbFileSystemUserGroupQuotaPolicy", + "Get-PfbFileSystemUserPerformance", + "Get-PfbFileSystemUserQuota", + "Get-PfbFileSystemWormPolicy", + "Get-PfbFleet", + "Get-PfbFleetKey", + "Get-PfbFleetMember", + "Get-PfbHardware", + "Get-PfbHardwareConnector", + "Get-PfbHardwareConnectorPerformance", + "Get-PfbKeytab", + "Get-PfbKeytabDownload", + "Get-PfbKmip", + "Get-PfbLag", + "Get-PfbLegalHold", + "Get-PfbLegalHoldEntity", + "Get-PfbLifecycleRule", + "Get-PfbLocalDirectoryService", + "Get-PfbLocalGroup", + "Get-PfbLocalGroupMember", + "Get-PfbLog", + "Get-PfbLogTargetFileSystem", + "Get-PfbLogTargetObjectStore", + "Get-PfbMaintenanceWindow", + "Get-PfbManagementAccessPolicy", + "Get-PfbManagementAccessPolicyAdmin", + "Get-PfbManagementAccessPolicyDirectoryRole", + "Get-PfbManagementAccessPolicyMember", + "Get-PfbNetworkAccessPolicy", + "Get-PfbNetworkAccessPolicyMember", + "Get-PfbNetworkAccessRule", + "Get-PfbNetworkConnectionStatistics", + "Get-PfbNetworkInterface", + "Get-PfbNetworkInterfaceConnector", + "Get-PfbNetworkInterfaceConnectorPerformance", + "Get-PfbNetworkInterfaceConnectorSettings", + "Get-PfbNetworkInterfaceNeighbor", + "Get-PfbNetworkInterfaceTlsPolicy", + "Get-PfbNfsExportPolicy", + "Get-PfbNfsExportRule", + "Get-PfbNode", + "Get-PfbNodeGroup", + "Get-PfbNodeGroupNode", + "Get-PfbNodeGroupUse", + "Get-PfbObjectStoreAccessKey", + "Get-PfbObjectStoreAccessPolicy", + "Get-PfbObjectStoreAccessPolicyAction", + "Get-PfbObjectStoreAccessPolicyRole", + "Get-PfbObjectStoreAccessPolicyRule", + "Get-PfbObjectStoreAccessPolicyUser", + "Get-PfbObjectStoreAccount", + "Get-PfbObjectStoreAccountExport", + "Get-PfbObjectStoreRemoteCredential", + "Get-PfbObjectStoreRole", + "Get-PfbObjectStoreRoleAccessPolicy", + "Get-PfbObjectStoreTrustPolicy", + "Get-PfbObjectStoreTrustPolicyRule", + "Get-PfbObjectStoreUser", + "Get-PfbObjectStoreUserAccessPolicy", + "Get-PfbObjectStoreVirtualHost", + "Get-PfbOidcIdp", + "Get-PfbOpenFile", + "Get-PfbPasswordPolicy", + "Get-PfbPolicy", + "Get-PfbPolicyAll", + "Get-PfbPolicyAllMember", + "Get-PfbPolicyFileSystem", + "Get-PfbPolicyFileSystemReplicaLink", + "Get-PfbPolicyFileSystemSnapshot", + "Get-PfbPublicKey", + "Get-PfbPublicKeyUse", + "Get-PfbQosPolicy", + "Get-PfbQosPolicyBucket", + "Get-PfbQosPolicyFileSystem", + "Get-PfbQosPolicyMember", + "Get-PfbQuotaGroup", + "Get-PfbQuotaUser", + "Get-PfbRealm", + "Get-PfbRealmDefaults", + "Get-PfbRealmSpace", + "Get-PfbRealmStorageClass", + "Get-PfbRemoteArray", + "Get-PfbResiliencyGroup", + "Get-PfbResourceAccess", + "Get-PfbRole", + "Get-PfbS3ExportPolicy", + "Get-PfbS3ExportRule", + "Get-PfbSaml2Idp", + "Get-PfbServer", + "Get-PfbSession", + "Get-PfbSmbClientPolicy", + "Get-PfbSmbClientRule", + "Get-PfbSmbSharePolicy", + "Get-PfbSmbShareRule", + "Get-PfbSmtpServer", + "Get-PfbSnmpAgent", + "Get-PfbSnmpManager", + "Get-PfbSoftwareCheck", + "Get-PfbSshCaPolicy", + "Get-PfbSshCaPolicyAdmin", + "Get-PfbSshCaPolicyArray", + "Get-PfbSshCaPolicyMember", + "Get-PfbStorageClassTieringPolicy", + "Get-PfbStorageClassTieringPolicyMember", + "Get-PfbSubnet", + "Get-PfbSupport", + "Get-PfbSupportDiagnostics", + "Get-PfbSupportDiagnosticsDetails", + "Get-PfbSupportVerificationKey", + "Get-PfbSyslogServer", + "Get-PfbSyslogServerSettings", + "Get-PfbTarget", + "Get-PfbTargetPerformanceReplication", + "Get-PfbTlsPolicy", + "Get-PfbTlsPolicyMember", + "Get-PfbUsageGroup", + "Get-PfbUsageUser", + "Get-PfbUserGroupQuotaPolicy", + "Get-PfbUserGroupQuotaPolicyFileSystem", + "Get-PfbUserGroupQuotaPolicyMember", + "Get-PfbUserGroupQuotaPolicyRule", + "Get-PfbWorkload", + "Get-PfbWorkloadPlacementRecommendation", + "Get-PfbWormPolicy", + "Get-PfbWormPolicyMember" + ] + }, + { + "name": "limit", + "cmdletCount": 199, + "cmdlets": [ + "Get-PfbAdmin", + "Get-PfbAdminCache", + "Get-PfbAdminManagementAccessPolicy", + "Get-PfbAdminSetting", + "Get-PfbAdminSshCaPolicy", + "Get-PfbAlert", + "Get-PfbAlertWatcher", + "Get-PfbApiClient", + "Get-PfbApiToken", + "Get-PfbArray", + "Get-PfbArrayClientPerformance", + "Get-PfbArrayClientS3Performance", + "Get-PfbArrayConnection", + "Get-PfbArrayConnectionKey", + "Get-PfbArrayConnectionPath", + "Get-PfbArrayConnectionPerformanceReplication", + "Get-PfbArrayErasure", + "Get-PfbArrayEula", + "Get-PfbArrayFactoryResetToken", + "Get-PfbArrayHttpPerformance", + "Get-PfbArrayNfsPerformance", + "Get-PfbArrayPerformanceReplication", + "Get-PfbArrayS3Performance", + "Get-PfbArraySshCaPolicy", + "Get-PfbArrayStorageClass", + "Get-PfbArraySupportedTimeZone", + "Get-PfbAsyncLog", + "Get-PfbAudit", + "Get-PfbAuditFileSystemPolicy", + "Get-PfbAuditFileSystemPolicyMember", + "Get-PfbAuditFileSystemPolicyOperation", + "Get-PfbAuditObjectStorePolicy", + "Get-PfbAuditObjectStorePolicyMember", + "Get-PfbBlade", + "Get-PfbBucket", + "Get-PfbBucketAccessPolicy", + "Get-PfbBucketAccessPolicyRule", + "Get-PfbBucketAuditFilter", + "Get-PfbBucketAuditFilterAction", + "Get-PfbBucketCorsPolicy", + "Get-PfbBucketCorsPolicyRule", + "Get-PfbBucketPerformance", + "Get-PfbBucketReplicaLink", + "Get-PfbBucketS3Performance", + "Get-PfbCertificate", + "Get-PfbCertificateCertificateGroup", + "Get-PfbCertificateGroup", + "Get-PfbCertificateGroupCertificate", + "Get-PfbCertificateGroupUse", + "Get-PfbCertificateUse", + "Get-PfbDataEvictionPolicy", + "Get-PfbDataEvictionPolicyFileSystem", + "Get-PfbDataEvictionPolicyMember", + "Get-PfbDirectoryServiceRole", + "Get-PfbDirectoryServiceRoleManagementPolicy", + "Get-PfbDns", + "Get-PfbDrive", + "Get-PfbFileLock", + "Get-PfbFileLockClient", + "Get-PfbFileSystem", + "Get-PfbFileSystemAuditPolicy", + "Get-PfbFileSystemExport", + "Get-PfbFileSystemGroup", + "Get-PfbFileSystemGroupPerformance", + "Get-PfbFileSystemGroupQuota", + "Get-PfbFileSystemPolicy", + "Get-PfbFileSystemReplicaLink", + "Get-PfbFileSystemReplicaLinkPolicy", + "Get-PfbFileSystemReplicaLinkTransfer", + "Get-PfbFileSystemSession", + "Get-PfbFileSystemSnapshot", + "Get-PfbFileSystemSnapshotPolicy", + "Get-PfbFileSystemSnapshotTransfer", + "Get-PfbFileSystemStorageClass", + "Get-PfbFileSystemUser", + "Get-PfbFileSystemUserGroupQuotaPolicy", + "Get-PfbFileSystemUserPerformance", + "Get-PfbFileSystemUserQuota", + "Get-PfbFileSystemWormPolicy", + "Get-PfbFleet", + "Get-PfbFleetKey", + "Get-PfbFleetMember", + "Get-PfbHardware", + "Get-PfbHardwareConnector", + "Get-PfbHardwareConnectorPerformance", + "Get-PfbKeytab", + "Get-PfbKeytabDownload", + "Get-PfbKmip", + "Get-PfbLag", + "Get-PfbLegalHold", + "Get-PfbLegalHoldEntity", + "Get-PfbLifecycleRule", + "Get-PfbLocalDirectoryService", + "Get-PfbLocalGroup", + "Get-PfbLocalGroupMember", + "Get-PfbLog", + "Get-PfbLogTargetFileSystem", + "Get-PfbLogTargetObjectStore", + "Get-PfbMaintenanceWindow", + "Get-PfbManagementAccessPolicy", + "Get-PfbManagementAccessPolicyAdmin", + "Get-PfbManagementAccessPolicyDirectoryRole", + "Get-PfbManagementAccessPolicyMember", + "Get-PfbNetworkAccessPolicy", + "Get-PfbNetworkAccessPolicyMember", + "Get-PfbNetworkAccessRule", + "Get-PfbNetworkConnectionStatistics", + "Get-PfbNetworkInterface", + "Get-PfbNetworkInterfaceConnector", + "Get-PfbNetworkInterfaceConnectorPerformance", + "Get-PfbNetworkInterfaceConnectorSettings", + "Get-PfbNetworkInterfaceNeighbor", + "Get-PfbNetworkInterfaceTlsPolicy", + "Get-PfbNfsExportPolicy", + "Get-PfbNfsExportRule", + "Get-PfbNode", + "Get-PfbNodeGroup", + "Get-PfbNodeGroupNode", + "Get-PfbNodeGroupUse", + "Get-PfbObjectStoreAccessKey", + "Get-PfbObjectStoreAccessPolicy", + "Get-PfbObjectStoreAccessPolicyAction", + "Get-PfbObjectStoreAccessPolicyRole", + "Get-PfbObjectStoreAccessPolicyRule", + "Get-PfbObjectStoreAccessPolicyUser", + "Get-PfbObjectStoreAccount", + "Get-PfbObjectStoreAccountExport", + "Get-PfbObjectStoreRemoteCredential", + "Get-PfbObjectStoreRole", + "Get-PfbObjectStoreRoleAccessPolicy", + "Get-PfbObjectStoreTrustPolicy", + "Get-PfbObjectStoreTrustPolicyRule", + "Get-PfbObjectStoreUser", + "Get-PfbObjectStoreUserAccessPolicy", + "Get-PfbObjectStoreVirtualHost", + "Get-PfbOidcIdp", + "Get-PfbOpenFile", + "Get-PfbPasswordPolicy", + "Get-PfbPolicy", + "Get-PfbPolicyAll", + "Get-PfbPolicyAllMember", + "Get-PfbPolicyFileSystem", + "Get-PfbPolicyFileSystemReplicaLink", + "Get-PfbPolicyFileSystemSnapshot", + "Get-PfbPublicKey", + "Get-PfbPublicKeyUse", + "Get-PfbQosPolicy", + "Get-PfbQosPolicyBucket", + "Get-PfbQosPolicyFileSystem", + "Get-PfbQosPolicyMember", + "Get-PfbQuotaGroup", + "Get-PfbQuotaUser", + "Get-PfbRealm", + "Get-PfbRealmDefaults", + "Get-PfbRealmSpace", + "Get-PfbRealmStorageClass", + "Get-PfbRemoteArray", + "Get-PfbResiliencyGroup", + "Get-PfbResourceAccess", + "Get-PfbRole", + "Get-PfbS3ExportPolicy", + "Get-PfbS3ExportRule", + "Get-PfbSaml2Idp", + "Get-PfbServer", + "Get-PfbSession", + "Get-PfbSmbClientPolicy", + "Get-PfbSmbClientRule", + "Get-PfbSmbSharePolicy", + "Get-PfbSmbShareRule", + "Get-PfbSmtpServer", + "Get-PfbSnmpManager", + "Get-PfbSoftwareCheck", + "Get-PfbSshCaPolicy", + "Get-PfbSshCaPolicyAdmin", + "Get-PfbSshCaPolicyArray", + "Get-PfbSshCaPolicyMember", + "Get-PfbStorageClassTieringPolicy", + "Get-PfbStorageClassTieringPolicyMember", + "Get-PfbSubnet", + "Get-PfbSupport", + "Get-PfbSupportDiagnostics", + "Get-PfbSupportDiagnosticsDetails", + "Get-PfbSupportVerificationKey", + "Get-PfbSyslogServer", + "Get-PfbSyslogServerSettings", + "Get-PfbTarget", + "Get-PfbTargetPerformanceReplication", + "Get-PfbTlsPolicy", + "Get-PfbTlsPolicyMember", + "Get-PfbUsageGroup", + "Get-PfbUsageUser", + "Get-PfbUserGroupQuotaPolicy", + "Get-PfbUserGroupQuotaPolicyFileSystem", + "Get-PfbUserGroupQuotaPolicyMember", + "Get-PfbUserGroupQuotaPolicyRule", + "Get-PfbWorkload", + "Get-PfbWorkloadPlacementRecommendation", + "Get-PfbWormPolicy", + "Get-PfbWormPolicyMember" + ] + }, + { + "name": "sort", + "cmdletCount": 180, + "cmdlets": [ + "Get-PfbAdmin", + "Get-PfbAdminCache", + "Get-PfbAdminSetting", + "Get-PfbAlert", + "Get-PfbAlertWatcher", + "Get-PfbApiClient", + "Get-PfbApiToken", + "Get-PfbArray", + "Get-PfbArrayClientPerformance", + "Get-PfbArrayClientS3Performance", + "Get-PfbArrayConnection", + "Get-PfbArrayConnectionKey", + "Get-PfbArrayConnectionPath", + "Get-PfbArrayConnectionPerformanceReplication", + "Get-PfbArrayErasure", + "Get-PfbArrayEula", + "Get-PfbArrayFactoryResetToken", + "Get-PfbArrayHttpPerformance", + "Get-PfbArrayNfsPerformance", + "Get-PfbArrayPerformanceReplication", + "Get-PfbArrayS3Performance", + "Get-PfbArrayStorageClass", + "Get-PfbArraySupportedTimeZone", + "Get-PfbAsyncLog", + "Get-PfbAudit", + "Get-PfbAuditFileSystemPolicy", + "Get-PfbAuditFileSystemPolicyMember", + "Get-PfbAuditFileSystemPolicyOperation", + "Get-PfbAuditObjectStorePolicy", + "Get-PfbAuditObjectStorePolicyMember", + "Get-PfbBlade", + "Get-PfbBucket", + "Get-PfbBucketAccessPolicy", + "Get-PfbBucketAccessPolicyRule", + "Get-PfbBucketAuditFilter", + "Get-PfbBucketAuditFilterAction", + "Get-PfbBucketCorsPolicy", + "Get-PfbBucketCorsPolicyRule", + "Get-PfbBucketPerformance", + "Get-PfbBucketReplicaLink", + "Get-PfbBucketS3Performance", + "Get-PfbCertificate", + "Get-PfbCertificateGroup", + "Get-PfbCertificateGroupCertificate", + "Get-PfbCertificateGroupUse", + "Get-PfbCertificateUse", + "Get-PfbDataEvictionPolicy", + "Get-PfbDataEvictionPolicyFileSystem", + "Get-PfbDataEvictionPolicyMember", + "Get-PfbDirectoryServiceRole", + "Get-PfbDns", + "Get-PfbDrive", + "Get-PfbFileLock", + "Get-PfbFileLockClient", + "Get-PfbFileSystem", + "Get-PfbFileSystemAuditPolicy", + "Get-PfbFileSystemExport", + "Get-PfbFileSystemGroup", + "Get-PfbFileSystemGroupPerformance", + "Get-PfbFileSystemGroupQuota", + "Get-PfbFileSystemPolicy", + "Get-PfbFileSystemReplicaLink", + "Get-PfbFileSystemReplicaLinkPolicy", + "Get-PfbFileSystemReplicaLinkTransfer", + "Get-PfbFileSystemSession", + "Get-PfbFileSystemSnapshot", + "Get-PfbFileSystemSnapshotPolicy", + "Get-PfbFileSystemSnapshotTransfer", + "Get-PfbFileSystemStorageClass", + "Get-PfbFileSystemUser", + "Get-PfbFileSystemUserGroupQuotaPolicy", + "Get-PfbFileSystemUserPerformance", + "Get-PfbFileSystemUserQuota", + "Get-PfbFileSystemWormPolicy", + "Get-PfbFleet", + "Get-PfbFleetKey", + "Get-PfbFleetMember", + "Get-PfbHardware", + "Get-PfbHardwareConnector", + "Get-PfbHardwareConnectorPerformance", + "Get-PfbKeytab", + "Get-PfbKeytabDownload", + "Get-PfbKmip", + "Get-PfbLag", + "Get-PfbLegalHold", + "Get-PfbLegalHoldEntity", + "Get-PfbLifecycleRule", + "Get-PfbLocalDirectoryService", + "Get-PfbLocalGroup", + "Get-PfbLocalGroupMember", + "Get-PfbLog", + "Get-PfbLogTargetFileSystem", + "Get-PfbLogTargetObjectStore", + "Get-PfbMaintenanceWindow", + "Get-PfbManagementAccessPolicy", + "Get-PfbNetworkAccessPolicy", + "Get-PfbNetworkAccessPolicyMember", + "Get-PfbNetworkAccessRule", + "Get-PfbNetworkConnectionStatistics", + "Get-PfbNetworkInterface", + "Get-PfbNetworkInterfaceConnector", + "Get-PfbNetworkInterfaceConnectorPerformance", + "Get-PfbNetworkInterfaceConnectorSettings", + "Get-PfbNetworkInterfaceNeighbor", + "Get-PfbNetworkInterfaceTlsPolicy", + "Get-PfbNfsExportPolicy", + "Get-PfbNfsExportRule", + "Get-PfbNode", + "Get-PfbNodeGroup", + "Get-PfbNodeGroupNode", + "Get-PfbNodeGroupUse", + "Get-PfbObjectStoreAccessKey", + "Get-PfbObjectStoreAccessPolicy", + "Get-PfbObjectStoreAccessPolicyAction", + "Get-PfbObjectStoreAccessPolicyRole", + "Get-PfbObjectStoreAccessPolicyRule", + "Get-PfbObjectStoreAccessPolicyUser", + "Get-PfbObjectStoreAccount", + "Get-PfbObjectStoreAccountExport", + "Get-PfbObjectStoreRemoteCredential", + "Get-PfbObjectStoreRole", + "Get-PfbObjectStoreRoleAccessPolicy", + "Get-PfbObjectStoreTrustPolicy", + "Get-PfbObjectStoreTrustPolicyRule", + "Get-PfbObjectStoreUser", + "Get-PfbObjectStoreUserAccessPolicy", + "Get-PfbObjectStoreVirtualHost", + "Get-PfbOidcIdp", + "Get-PfbOpenFile", + "Get-PfbPasswordPolicy", + "Get-PfbPolicy", + "Get-PfbPolicyAll", + "Get-PfbPolicyFileSystem", + "Get-PfbPolicyFileSystemSnapshot", + "Get-PfbPublicKey", + "Get-PfbPublicKeyUse", + "Get-PfbQosPolicy", + "Get-PfbQuotaGroup", + "Get-PfbQuotaUser", + "Get-PfbRealm", + "Get-PfbRealmDefaults", + "Get-PfbRealmSpace", + "Get-PfbRealmStorageClass", + "Get-PfbRemoteArray", + "Get-PfbResiliencyGroup", + "Get-PfbResourceAccess", + "Get-PfbRole", + "Get-PfbS3ExportPolicy", + "Get-PfbS3ExportRule", + "Get-PfbSaml2Idp", + "Get-PfbServer", + "Get-PfbSession", + "Get-PfbSmbClientPolicy", + "Get-PfbSmbClientRule", + "Get-PfbSmbSharePolicy", + "Get-PfbSmbShareRule", + "Get-PfbSmtpServer", + "Get-PfbSnmpManager", + "Get-PfbSoftwareCheck", + "Get-PfbSshCaPolicy", + "Get-PfbStorageClassTieringPolicy", + "Get-PfbSubnet", + "Get-PfbSupport", + "Get-PfbSupportDiagnostics", + "Get-PfbSupportDiagnosticsDetails", + "Get-PfbSupportVerificationKey", + "Get-PfbSyslogServer", + "Get-PfbSyslogServerSettings", + "Get-PfbTarget", + "Get-PfbTargetPerformanceReplication", + "Get-PfbTlsPolicy", + "Get-PfbUsageGroup", + "Get-PfbUsageUser", + "Get-PfbUserGroupQuotaPolicy", + "Get-PfbUserGroupQuotaPolicyFileSystem", + "Get-PfbUserGroupQuotaPolicyMember", + "Get-PfbUserGroupQuotaPolicyRule", + "Get-PfbWorkload", + "Get-PfbWorkloadPlacementRecommendation", + "Get-PfbWormPolicy" + ] + }, + { + "name": "policy_names", + "cmdletCount": 118, + "cmdlets": [ + "Add-PfbDataEvictionPolicyFileSystem", + "Get-PfbAdminManagementAccessPolicy", + "Get-PfbAdminSshCaPolicy", + "Get-PfbArraySshCaPolicy", + "Get-PfbAuditFileSystemPolicyMember", + "Get-PfbAuditObjectStorePolicyMember", + "Get-PfbBucketAccessPolicy", + "Get-PfbBucketAccessPolicyRule", + "Get-PfbBucketCorsPolicy", + "Get-PfbBucketCorsPolicyRule", + "Get-PfbDataEvictionPolicyFileSystem", + "Get-PfbDataEvictionPolicyMember", + "Get-PfbDirectoryServiceRoleManagementPolicy", + "Get-PfbFileSystemAuditPolicy", + "Get-PfbFileSystemPolicy", + "Get-PfbFileSystemReplicaLinkPolicy", + "Get-PfbFileSystemSnapshotPolicy", + "Get-PfbFileSystemUserGroupQuotaPolicy", + "Get-PfbFileSystemWormPolicy", + "Get-PfbManagementAccessPolicyAdmin", + "Get-PfbManagementAccessPolicyDirectoryRole", + "Get-PfbManagementAccessPolicyMember", + "Get-PfbNetworkAccessPolicyMember", + "Get-PfbNetworkAccessRule", + "Get-PfbNetworkInterfaceTlsPolicy", + "Get-PfbNfsExportRule", + "Get-PfbObjectStoreAccessPolicyRole", + "Get-PfbObjectStoreAccessPolicyRule", + "Get-PfbObjectStoreAccessPolicyUser", + "Get-PfbObjectStoreTrustPolicyRule", + "Get-PfbObjectStoreUserAccessPolicy", + "Get-PfbPolicyAllMember", + "Get-PfbPolicyFileSystem", + "Get-PfbPolicyFileSystemReplicaLink", + "Get-PfbPolicyFileSystemSnapshot", + "Get-PfbQosPolicyBucket", + "Get-PfbQosPolicyFileSystem", + "Get-PfbQosPolicyMember", + "Get-PfbS3ExportRule", + "Get-PfbSmbClientRule", + "Get-PfbSmbShareRule", + "Get-PfbSshCaPolicyAdmin", + "Get-PfbSshCaPolicyArray", + "Get-PfbSshCaPolicyMember", + "Get-PfbStorageClassTieringPolicyMember", + "Get-PfbTlsPolicyMember", + "Get-PfbUserGroupQuotaPolicyFileSystem", + "Get-PfbUserGroupQuotaPolicyMember", + "Get-PfbUserGroupQuotaPolicyRule", + "Get-PfbWormPolicyMember", + "New-PfbAdminManagementAccessPolicy", + "New-PfbAdminSshCaPolicy", + "New-PfbArraySshCaPolicy", + "New-PfbAuditFileSystemPolicyMember", + "New-PfbAuditObjectStorePolicyMember", + "New-PfbBucketAccessPolicy", + "New-PfbBucketAccessPolicyRule", + "New-PfbBucketCorsPolicy", + "New-PfbDirectoryServiceRoleManagementPolicy", + "New-PfbFileSystemAuditPolicy", + "New-PfbFileSystemExport", + "New-PfbFileSystemPolicy", + "New-PfbFileSystemReplicaLinkPolicy", + "New-PfbFileSystemUserGroupQuotaPolicy", + "New-PfbManagementAccessPolicyAdmin", + "New-PfbManagementAccessPolicyDirectoryRole", + "New-PfbNetworkAccessRule", + "New-PfbNetworkInterfaceTlsPolicy", + "New-PfbNfsExportRule", + "New-PfbObjectStoreAccessPolicyRole", + "New-PfbObjectStoreAccessPolicyRule", + "New-PfbObjectStoreAccessPolicyUser", + "New-PfbObjectStoreTrustPolicyRule", + "New-PfbObjectStoreUserAccessPolicy", + "New-PfbPolicyFileSystem", + "New-PfbPolicyFileSystemReplicaLink", + "New-PfbQosPolicyMember", + "New-PfbS3ExportRule", + "New-PfbSmbClientRule", + "New-PfbSmbShareRule", + "New-PfbSshCaPolicyAdmin", + "New-PfbSshCaPolicyArray", + "New-PfbUserGroupQuotaPolicyFileSystem", + "New-PfbUserGroupQuotaPolicyRule", + "Remove-PfbAdminManagementAccessPolicy", + "Remove-PfbAdminSshCaPolicy", + "Remove-PfbArraySshCaPolicy", + "Remove-PfbAuditFileSystemPolicyMember", + "Remove-PfbAuditObjectStorePolicyMember", + "Remove-PfbBucketAccessPolicy", + "Remove-PfbBucketCorsPolicy", + "Remove-PfbDataEvictionPolicyFileSystem", + "Remove-PfbDirectoryServiceRoleManagementPolicy", + "Remove-PfbFileSystemAuditPolicy", + "Remove-PfbFileSystemPolicy", + "Remove-PfbFileSystemReplicaLinkPolicy", + "Remove-PfbFileSystemSnapshotPolicy", + "Remove-PfbFileSystemUserGroupQuotaPolicy", + "Remove-PfbManagementAccessPolicyAdmin", + "Remove-PfbManagementAccessPolicyDirectoryRole", + "Remove-PfbNetworkAccessRule", + "Remove-PfbNetworkInterfaceTlsPolicy", + "Remove-PfbNfsExportRule", + "Remove-PfbObjectStoreAccessPolicyRole", + "Remove-PfbObjectStoreAccessPolicyRule", + "Remove-PfbObjectStoreAccessPolicyUser", + "Remove-PfbObjectStoreTrustPolicyRule", + "Remove-PfbObjectStoreUserAccessPolicy", + "Remove-PfbPolicyFileSystem", + "Remove-PfbPolicyFileSystemReplicaLink", + "Remove-PfbQosPolicyMember", + "Remove-PfbS3ExportRule", + "Remove-PfbSmbClientRule", + "Remove-PfbSmbShareRule", + "Remove-PfbSshCaPolicyAdmin", + "Remove-PfbSshCaPolicyArray", + "Remove-PfbUserGroupQuotaPolicyFileSystem", + "Remove-PfbUserGroupQuotaPolicyRule" + ] + }, + { + "name": "member_names", + "cmdletCount": 105, + "cmdlets": [ + "Add-PfbDataEvictionPolicyFileSystem", + "Get-PfbAdminManagementAccessPolicy", + "Get-PfbAdminSshCaPolicy", + "Get-PfbArraySshCaPolicy", + "Get-PfbAuditFileSystemPolicyMember", + "Get-PfbAuditObjectStorePolicyMember", + "Get-PfbBucketAccessPolicy", + "Get-PfbBucketAccessPolicyRule", + "Get-PfbBucketAuditFilter", + "Get-PfbBucketCorsPolicy", + "Get-PfbBucketCorsPolicyRule", + "Get-PfbDataEvictionPolicyFileSystem", + "Get-PfbDataEvictionPolicyMember", + "Get-PfbDirectoryServiceRoleManagementPolicy", + "Get-PfbFileSystemAuditPolicy", + "Get-PfbFileSystemPolicy", + "Get-PfbFileSystemReplicaLinkPolicy", + "Get-PfbFileSystemSnapshotPolicy", + "Get-PfbFileSystemUserGroupQuotaPolicy", + "Get-PfbFileSystemWormPolicy", + "Get-PfbFleetMember", + "Get-PfbLegalHoldEntity", + "Get-PfbLocalGroupMember", + "Get-PfbManagementAccessPolicyAdmin", + "Get-PfbManagementAccessPolicyDirectoryRole", + "Get-PfbManagementAccessPolicyMember", + "Get-PfbNetworkAccessPolicyMember", + "Get-PfbNetworkInterfaceTlsPolicy", + "Get-PfbNodeGroupNode", + "Get-PfbObjectStoreAccessPolicyRole", + "Get-PfbObjectStoreAccessPolicyUser", + "Get-PfbObjectStoreRoleAccessPolicy", + "Get-PfbObjectStoreUserAccessPolicy", + "Get-PfbPolicyAllMember", + "Get-PfbPolicyFileSystem", + "Get-PfbPolicyFileSystemReplicaLink", + "Get-PfbPolicyFileSystemSnapshot", + "Get-PfbQosPolicyBucket", + "Get-PfbQosPolicyFileSystem", + "Get-PfbQosPolicyMember", + "Get-PfbSshCaPolicyAdmin", + "Get-PfbSshCaPolicyArray", + "Get-PfbSshCaPolicyMember", + "Get-PfbStorageClassTieringPolicyMember", + "Get-PfbTlsPolicyMember", + "Get-PfbUserGroupQuotaPolicyFileSystem", + "Get-PfbUserGroupQuotaPolicyMember", + "Get-PfbWormPolicyMember", + "New-PfbAdminManagementAccessPolicy", + "New-PfbAdminSshCaPolicy", + "New-PfbArraySshCaPolicy", + "New-PfbAuditFileSystemPolicyMember", + "New-PfbAuditObjectStorePolicyMember", + "New-PfbBucketAccessPolicy", + "New-PfbBucketAccessPolicyRule", + "New-PfbBucketAuditFilter", + "New-PfbBucketCorsPolicy", + "New-PfbDirectoryServiceRoleManagementPolicy", + "New-PfbFileSystemAuditPolicy", + "New-PfbFileSystemExport", + "New-PfbFileSystemPolicy", + "New-PfbFileSystemUserGroupQuotaPolicy", + "New-PfbManagementAccessPolicyAdmin", + "New-PfbManagementAccessPolicyDirectoryRole", + "New-PfbNetworkInterfaceTlsPolicy", + "New-PfbObjectStoreAccessPolicyRole", + "New-PfbObjectStoreAccessPolicyUser", + "New-PfbObjectStoreRoleAccessPolicy", + "New-PfbObjectStoreUserAccessPolicy", + "New-PfbPolicyFileSystem", + "New-PfbQosPolicyMember", + "New-PfbSshCaPolicyAdmin", + "New-PfbSshCaPolicyArray", + "New-PfbUserGroupQuotaPolicyFileSystem", + "Remove-PfbAdminManagementAccessPolicy", + "Remove-PfbAdminSshCaPolicy", + "Remove-PfbArraySshCaPolicy", + "Remove-PfbAuditFileSystemPolicyMember", + "Remove-PfbAuditObjectStorePolicyMember", + "Remove-PfbBucketAccessPolicy", + "Remove-PfbBucketAuditFilter", + "Remove-PfbBucketCorsPolicy", + "Remove-PfbDataEvictionPolicyFileSystem", + "Remove-PfbDirectoryServiceRoleManagementPolicy", + "Remove-PfbFileSystemAuditPolicy", + "Remove-PfbFileSystemPolicy", + "Remove-PfbFileSystemReplicaLinkPolicy", + "Remove-PfbFileSystemSnapshotPolicy", + "Remove-PfbFileSystemUserGroupQuotaPolicy", + "Remove-PfbFleetMember", + "Remove-PfbLocalGroupMember", + "Remove-PfbManagementAccessPolicyAdmin", + "Remove-PfbManagementAccessPolicyDirectoryRole", + "Remove-PfbNetworkInterfaceTlsPolicy", + "Remove-PfbNodeGroupNode", + "Remove-PfbObjectStoreAccessPolicyRole", + "Remove-PfbObjectStoreAccessPolicyUser", + "Remove-PfbObjectStoreRoleAccessPolicy", + "Remove-PfbObjectStoreUserAccessPolicy", + "Remove-PfbPolicyFileSystem", + "Remove-PfbPolicyFileSystemReplicaLink", + "Remove-PfbQosPolicyMember", + "Remove-PfbSshCaPolicyAdmin", + "Remove-PfbSshCaPolicyArray", + "Remove-PfbUserGroupQuotaPolicyFileSystem" + ] + }, + { + "name": "policy_ids", + "cmdletCount": 98, + "cmdlets": [ + "Add-PfbDataEvictionPolicyFileSystem", + "Get-PfbAdminManagementAccessPolicy", + "Get-PfbAdminSshCaPolicy", + "Get-PfbArraySshCaPolicy", + "Get-PfbAuditFileSystemPolicyMember", + "Get-PfbAuditObjectStorePolicyMember", + "Get-PfbBucketAccessPolicy", + "Get-PfbDataEvictionPolicyFileSystem", + "Get-PfbDataEvictionPolicyMember", + "Get-PfbDirectoryServiceRoleManagementPolicy", + "Get-PfbFileSystemAuditPolicy", + "Get-PfbFileSystemPolicy", + "Get-PfbFileSystemReplicaLinkPolicy", + "Get-PfbFileSystemSnapshotPolicy", + "Get-PfbFileSystemUserGroupQuotaPolicy", + "Get-PfbFileSystemWormPolicy", + "Get-PfbManagementAccessPolicyAdmin", + "Get-PfbManagementAccessPolicyDirectoryRole", + "Get-PfbManagementAccessPolicyMember", + "Get-PfbNetworkAccessPolicyMember", + "Get-PfbNetworkAccessRule", + "Get-PfbNetworkInterfaceTlsPolicy", + "Get-PfbNfsExportRule", + "Get-PfbObjectStoreAccessPolicyRole", + "Get-PfbObjectStoreAccessPolicyRule", + "Get-PfbObjectStoreAccessPolicyUser", + "Get-PfbObjectStoreTrustPolicyRule", + "Get-PfbObjectStoreUserAccessPolicy", + "Get-PfbPolicyAllMember", + "Get-PfbPolicyFileSystem", + "Get-PfbPolicyFileSystemReplicaLink", + "Get-PfbPolicyFileSystemSnapshot", + "Get-PfbQosPolicyBucket", + "Get-PfbQosPolicyFileSystem", + "Get-PfbQosPolicyMember", + "Get-PfbS3ExportRule", + "Get-PfbSmbClientRule", + "Get-PfbSmbShareRule", + "Get-PfbSshCaPolicyAdmin", + "Get-PfbSshCaPolicyArray", + "Get-PfbSshCaPolicyMember", + "Get-PfbStorageClassTieringPolicyMember", + "Get-PfbTlsPolicyMember", + "Get-PfbUserGroupQuotaPolicyFileSystem", + "Get-PfbUserGroupQuotaPolicyMember", + "Get-PfbUserGroupQuotaPolicyRule", + "Get-PfbWormPolicyMember", + "New-PfbAdminManagementAccessPolicy", + "New-PfbAdminSshCaPolicy", + "New-PfbArraySshCaPolicy", + "New-PfbAuditFileSystemPolicyMember", + "New-PfbAuditObjectStorePolicyMember", + "New-PfbDirectoryServiceRoleManagementPolicy", + "New-PfbFileSystemAuditPolicy", + "New-PfbFileSystemPolicy", + "New-PfbFileSystemReplicaLinkPolicy", + "New-PfbFileSystemUserGroupQuotaPolicy", + "New-PfbManagementAccessPolicyAdmin", + "New-PfbManagementAccessPolicyDirectoryRole", + "New-PfbNetworkAccessRule", + "New-PfbNetworkInterfaceTlsPolicy", + "New-PfbNfsExportRule", + "New-PfbPolicyFileSystem", + "New-PfbPolicyFileSystemReplicaLink", + "New-PfbQosPolicyMember", + "New-PfbS3ExportRule", + "New-PfbSmbClientRule", + "New-PfbSmbShareRule", + "New-PfbSshCaPolicyAdmin", + "New-PfbSshCaPolicyArray", + "New-PfbUserGroupQuotaPolicyFileSystem", + "New-PfbUserGroupQuotaPolicyRule", + "Remove-PfbAdminManagementAccessPolicy", + "Remove-PfbAdminSshCaPolicy", + "Remove-PfbArraySshCaPolicy", + "Remove-PfbAuditFileSystemPolicyMember", + "Remove-PfbAuditObjectStorePolicyMember", + "Remove-PfbDataEvictionPolicyFileSystem", + "Remove-PfbDirectoryServiceRoleManagementPolicy", + "Remove-PfbFileSystemAuditPolicy", + "Remove-PfbFileSystemPolicy", + "Remove-PfbFileSystemReplicaLinkPolicy", + "Remove-PfbFileSystemSnapshotPolicy", + "Remove-PfbFileSystemUserGroupQuotaPolicy", + "Remove-PfbManagementAccessPolicyAdmin", + "Remove-PfbManagementAccessPolicyDirectoryRole", + "Remove-PfbNetworkAccessRule", + "Remove-PfbNfsExportRule", + "Remove-PfbPolicyFileSystem", + "Remove-PfbPolicyFileSystemReplicaLink", + "Remove-PfbQosPolicyMember", + "Remove-PfbS3ExportRule", + "Remove-PfbSmbClientRule", + "Remove-PfbSmbShareRule", + "Remove-PfbSshCaPolicyAdmin", + "Remove-PfbSshCaPolicyArray", + "Remove-PfbUserGroupQuotaPolicyFileSystem", + "Remove-PfbUserGroupQuotaPolicyRule" + ] + }, + { + "name": "member_ids", + "cmdletCount": 85, + "cmdlets": [ + "Add-PfbDataEvictionPolicyFileSystem", + "Get-PfbAdminManagementAccessPolicy", + "Get-PfbAdminSshCaPolicy", + "Get-PfbArraySshCaPolicy", + "Get-PfbAuditFileSystemPolicyMember", + "Get-PfbAuditObjectStorePolicyMember", + "Get-PfbBucketAccessPolicy", + "Get-PfbBucketAuditFilter", + "Get-PfbBucketCorsPolicy", + "Get-PfbDataEvictionPolicyFileSystem", + "Get-PfbDataEvictionPolicyMember", + "Get-PfbDirectoryServiceRoleManagementPolicy", + "Get-PfbFileSystemAuditPolicy", + "Get-PfbFileSystemPolicy", + "Get-PfbFileSystemReplicaLinkPolicy", + "Get-PfbFileSystemSnapshotPolicy", + "Get-PfbFileSystemUserGroupQuotaPolicy", + "Get-PfbFileSystemWormPolicy", + "Get-PfbLegalHoldEntity", + "Get-PfbManagementAccessPolicyAdmin", + "Get-PfbManagementAccessPolicyDirectoryRole", + "Get-PfbManagementAccessPolicyMember", + "Get-PfbNetworkAccessPolicyMember", + "Get-PfbNetworkInterfaceTlsPolicy", + "Get-PfbNodeGroupNode", + "Get-PfbObjectStoreAccessPolicyRole", + "Get-PfbObjectStoreAccessPolicyUser", + "Get-PfbObjectStoreRoleAccessPolicy", + "Get-PfbObjectStoreUserAccessPolicy", + "Get-PfbPolicyAllMember", + "Get-PfbPolicyFileSystem", + "Get-PfbPolicyFileSystemReplicaLink", + "Get-PfbPolicyFileSystemSnapshot", + "Get-PfbQosPolicyBucket", + "Get-PfbQosPolicyFileSystem", + "Get-PfbQosPolicyMember", + "Get-PfbSshCaPolicyAdmin", + "Get-PfbSshCaPolicyArray", + "Get-PfbSshCaPolicyMember", + "Get-PfbStorageClassTieringPolicyMember", + "Get-PfbTlsPolicyMember", + "Get-PfbUserGroupQuotaPolicyFileSystem", + "Get-PfbUserGroupQuotaPolicyMember", + "Get-PfbWormPolicyMember", + "New-PfbAdminManagementAccessPolicy", + "New-PfbAdminSshCaPolicy", + "New-PfbArraySshCaPolicy", + "New-PfbAuditFileSystemPolicyMember", + "New-PfbAuditObjectStorePolicyMember", + "New-PfbDirectoryServiceRoleManagementPolicy", + "New-PfbFileSystemAuditPolicy", + "New-PfbFileSystemPolicy", + "New-PfbFileSystemReplicaLinkPolicy", + "New-PfbFileSystemUserGroupQuotaPolicy", + "New-PfbManagementAccessPolicyAdmin", + "New-PfbManagementAccessPolicyDirectoryRole", + "New-PfbNetworkInterfaceTlsPolicy", + "New-PfbPolicyFileSystem", + "New-PfbPolicyFileSystemReplicaLink", + "New-PfbQosPolicyMember", + "New-PfbSshCaPolicyAdmin", + "New-PfbSshCaPolicyArray", + "New-PfbUserGroupQuotaPolicyFileSystem", + "Remove-PfbAdminManagementAccessPolicy", + "Remove-PfbAdminSshCaPolicy", + "Remove-PfbArraySshCaPolicy", + "Remove-PfbAuditFileSystemPolicyMember", + "Remove-PfbAuditObjectStorePolicyMember", + "Remove-PfbBucketAuditFilter", + "Remove-PfbBucketCorsPolicy", + "Remove-PfbDataEvictionPolicyFileSystem", + "Remove-PfbDirectoryServiceRoleManagementPolicy", + "Remove-PfbFileSystemAuditPolicy", + "Remove-PfbFileSystemPolicy", + "Remove-PfbFileSystemReplicaLinkPolicy", + "Remove-PfbFileSystemSnapshotPolicy", + "Remove-PfbFileSystemUserGroupQuotaPolicy", + "Remove-PfbManagementAccessPolicyAdmin", + "Remove-PfbManagementAccessPolicyDirectoryRole", + "Remove-PfbPolicyFileSystem", + "Remove-PfbPolicyFileSystemReplicaLink", + "Remove-PfbQosPolicyMember", + "Remove-PfbSshCaPolicyAdmin", + "Remove-PfbSshCaPolicyArray", + "Remove-PfbUserGroupQuotaPolicyFileSystem" + ] + }, + { + "name": "total_only", + "cmdletCount": 39, + "cmdlets": [ + "Get-PfbBucket", + "Get-PfbBucketPerformance", + "Get-PfbBucketS3Performance", + "Get-PfbFileLock", + "Get-PfbFileLockClient", + "Get-PfbFileSystem", + "Get-PfbFileSystemExport", + "Get-PfbFileSystemGroup", + "Get-PfbFileSystemGroupPerformance", + "Get-PfbFileSystemGroupQuota", + "Get-PfbFileSystemReplicaLinkTransfer", + "Get-PfbFileSystemSession", + "Get-PfbFileSystemSnapshot", + "Get-PfbFileSystemSnapshotTransfer", + "Get-PfbFileSystemStorageClass", + "Get-PfbFileSystemUser", + "Get-PfbFileSystemUserGroupQuotaPolicy", + "Get-PfbFileSystemUserPerformance", + "Get-PfbFileSystemUserQuota", + "Get-PfbObjectStoreAccessPolicy", + "Get-PfbObjectStoreAccessPolicyAction", + "Get-PfbObjectStoreAccessPolicyRole", + "Get-PfbObjectStoreAccessPolicyRule", + "Get-PfbObjectStoreAccessPolicyUser", + "Get-PfbObjectStoreAccount", + "Get-PfbObjectStoreAccountExport", + "Get-PfbObjectStoreRemoteCredential", + "Get-PfbObjectStoreRole", + "Get-PfbObjectStoreRoleAccessPolicy", + "Get-PfbObjectStoreTrustPolicy", + "Get-PfbObjectStoreTrustPolicyRule", + "Get-PfbObjectStoreUserAccessPolicy", + "Get-PfbOpenFile", + "Get-PfbRealm", + "Get-PfbServer", + "Get-PfbUserGroupQuotaPolicy", + "Get-PfbUserGroupQuotaPolicyFileSystem", + "Get-PfbUserGroupQuotaPolicyMember", + "Get-PfbUserGroupQuotaPolicyRule" + ] + }, + { + "name": "name", + "cmdletCount": 20, + "cmdlets": [ + "Update-PfbDataEvictionPolicy", + "Update-PfbDns", + "Update-PfbFleet", + "Update-PfbLogTargetFileSystem", + "Update-PfbLogTargetObjectStore", + "Update-PfbManagementAccessPolicy", + "Update-PfbNode", + "Update-PfbNodeGroup", + "Update-PfbObjectStoreRemoteCredential", + "Update-PfbObjectStoreVirtualHost", + "Update-PfbOidcIdp", + "Update-PfbPresetWorkload", + "Update-PfbQosPolicy", + "Update-PfbSaml2Idp", + "Update-PfbSnmpManager", + "Update-PfbSshCaPolicy", + "Update-PfbStorageClassTieringPolicy", + "Update-PfbTarget", + "Update-PfbTlsPolicy", + "Update-PfbWorkload" + ] + }, + { + "name": "end_time", + "cmdletCount": 17, + "cmdlets": [ + "Get-PfbArrayClientPerformance", + "Get-PfbArrayClientS3Performance", + "Get-PfbArrayConnectionPerformanceReplication", + "Get-PfbArrayHttpPerformance", + "Get-PfbArrayNfsPerformance", + "Get-PfbArrayPerformance", + "Get-PfbArrayPerformanceReplication", + "Get-PfbArrayS3Performance", + "Get-PfbBucketPerformance", + "Get-PfbBucketS3Performance", + "Get-PfbFileSystemGroupPerformance", + "Get-PfbFileSystemUserPerformance", + "Get-PfbHardwareConnectorPerformance", + "Get-PfbLog", + "Get-PfbNetworkInterfaceConnectorPerformance", + "Get-PfbTargetPerformanceReplication", + "Update-PfbAsyncLog" + ] + }, + { + "name": "start_time", + "cmdletCount": 17, + "cmdlets": [ + "Get-PfbArrayClientPerformance", + "Get-PfbArrayClientS3Performance", + "Get-PfbArrayConnectionPerformanceReplication", + "Get-PfbArrayHttpPerformance", + "Get-PfbArrayNfsPerformance", + "Get-PfbArrayPerformance", + "Get-PfbArrayPerformanceReplication", + "Get-PfbArrayS3Performance", + "Get-PfbBucketPerformance", + "Get-PfbBucketS3Performance", + "Get-PfbFileSystemGroupPerformance", + "Get-PfbFileSystemUserPerformance", + "Get-PfbHardwareConnectorPerformance", + "Get-PfbLog", + "Get-PfbNetworkInterfaceConnectorPerformance", + "Get-PfbTargetPerformanceReplication", + "Update-PfbAsyncLog" + ] + }, + { + "name": "resolution", + "cmdletCount": 15, + "cmdlets": [ + "Get-PfbArrayClientPerformance", + "Get-PfbArrayClientS3Performance", + "Get-PfbArrayConnectionPerformanceReplication", + "Get-PfbArrayHttpPerformance", + "Get-PfbArrayNfsPerformance", + "Get-PfbArrayPerformance", + "Get-PfbArrayPerformanceReplication", + "Get-PfbArrayS3Performance", + "Get-PfbBucketPerformance", + "Get-PfbBucketS3Performance", + "Get-PfbFileSystemGroupPerformance", + "Get-PfbFileSystemUserPerformance", + "Get-PfbHardwareConnectorPerformance", + "Get-PfbNetworkInterfaceConnectorPerformance", + "Get-PfbTargetPerformanceReplication" + ] + }, + { + "name": "file_system_names", + "cmdletCount": 13, + "cmdlets": [ + "Get-PfbFileSystemGroup", + "Get-PfbFileSystemGroupPerformance", + "Get-PfbFileSystemGroupQuota", + "Get-PfbFileSystemUser", + "Get-PfbFileSystemUserPerformance", + "Get-PfbFileSystemUserQuota", + "Get-PfbQuotaGroup", + "Get-PfbQuotaUser", + "Get-PfbUsageGroup", + "Get-PfbUsageUser", + "New-PfbLegalHoldEntity", + "New-PfbUserGroupQuotaPolicy", + "Update-PfbLegalHoldEntity" + ] + }, + { + "name": "enabled", + "cmdletCount": 10, + "cmdlets": [ + "Update-PfbApiClient", + "Update-PfbLifecycleRule", + "Update-PfbManagementAccessPolicy", + "Update-PfbOidcIdp", + "Update-PfbQosPolicy", + "Update-PfbSaml2Idp", + "Update-PfbSshCaPolicy", + "Update-PfbStorageClassTieringPolicy", + "Update-PfbTlsPolicy", + "Update-PfbWormPolicy" + ] + }, + { + "name": "file_system_ids", + "cmdletCount": 9, + "cmdlets": [ + "Get-PfbFileSystemGroup", + "Get-PfbFileSystemGroupPerformance", + "Get-PfbFileSystemGroupQuota", + "Get-PfbFileSystemUser", + "Get-PfbFileSystemUserPerformance", + "Get-PfbFileSystemUserQuota", + "New-PfbLegalHoldEntity", + "New-PfbUserGroupQuotaPolicy", + "Update-PfbLegalHoldEntity" + ] + }, + { + "name": "remote_names", + "cmdletCount": 9, + "cmdlets": [ + "Get-PfbArrayConnection", + "Get-PfbArrayConnectionPath", + "Get-PfbArrayConnectionPerformanceReplication", + "New-PfbFileSystemReplicaLink", + "New-PfbFileSystemReplicaLinkPolicy", + "New-PfbPolicyFileSystemReplicaLink", + "Remove-PfbArrayConnection", + "Remove-PfbFileSystemReplicaLink", + "Update-PfbArrayConnection" + ] + }, + { + "name": "location", + "cmdletCount": 8, + "cmdlets": [ + "New-PfbUserGroupQuotaPolicy", + "Update-PfbManagementAccessPolicy", + "Update-PfbQosPolicy", + "Update-PfbSshCaPolicy", + "Update-PfbStorageClassTieringPolicy", + "Update-PfbTlsPolicy", + "Update-PfbUserGroupQuotaPolicy", + "Update-PfbWormPolicy" + ] + }, + { + "name": "group_names", + "cmdletCount": 7, + "cmdlets": [ + "Get-PfbFileSystemGroup", + "Get-PfbFileSystemGroupQuota", + "Get-PfbLocalGroupMember", + "Get-PfbNodeGroupNode", + "New-PfbLocalGroupMember", + "Remove-PfbLocalGroupMember", + "Remove-PfbNodeGroupNode" + ] + }, + { + "name": "ca_certificate_group", + "cmdletCount": 6, + "cmdlets": [ + "New-PfbArrayConnection", + "Update-PfbActiveDirectory", + "Update-PfbArrayConnection", + "Update-PfbDns", + "Update-PfbKmip", + "Update-PfbTarget" + ] + }, + { + "name": "services", + "cmdletCount": 6, + "cmdlets": [ + "New-PfbNetworkInterface", + "Update-PfbDns", + "Update-PfbNetworkInterface", + "Update-PfbOidcIdp", + "Update-PfbSaml2Idp", + "Update-PfbSyslogServer" + ] + }, + { + "name": "destroyed", + "cmdletCount": 5, + "cmdlets": [ + "Get-PfbBucket", + "Get-PfbFileSystem", + "Get-PfbFileSystemSnapshot", + "Get-PfbRealm", + "Get-PfbWorkload" + ] + }, + { + "name": "local_file_system_names", + "cmdletCount": 5, + "cmdlets": [ + "Get-PfbFileSystemReplicaLink", + "New-PfbFileSystemReplicaLink", + "New-PfbFileSystemReplicaLinkPolicy", + "New-PfbPolicyFileSystemReplicaLink", + "Remove-PfbFileSystemReplicaLink" + ] + }, + { + "name": "rules", + "cmdletCount": 5, + "cmdlets": [ + "New-PfbPolicy", + "New-PfbUserGroupQuotaPolicy", + "Update-PfbManagementAccessPolicy", + "Update-PfbPolicy", + "Update-PfbUserGroupQuotaPolicy" + ] + }, + { + "name": "versions", + "cmdletCount": 5, + "cmdlets": [ + "New-PfbNetworkAccessRule", + "New-PfbNfsExportRule", + "New-PfbSmbClientRule", + "Remove-PfbUserGroupQuotaPolicy", + "Update-PfbUserGroupQuotaPolicy" + ] + }, + { + "name": "quota_limit", + "cmdletCount": 4, + "cmdlets": [ + "New-PfbBucket", + "New-PfbUserGroupQuotaPolicyRule", + "Update-PfbBucket", + "Update-PfbUserGroupQuotaPolicyRule" + ] + }, + { + "name": "role_names", + "cmdletCount": 4, + "cmdlets": [ + "Get-PfbObjectStoreRoleAccessPolicy", + "Get-PfbObjectStoreTrustPolicy", + "New-PfbObjectStoreRoleAccessPolicy", + "Remove-PfbObjectStoreRoleAccessPolicy" + ] + }, + { + "name": "type", + "cmdletCount": 4, + "cmdlets": [ + "Get-PfbArrayConnectionPerformanceReplication", + "Get-PfbArrayPerformanceReplication", + "Get-PfbArraySpace", + "New-PfbNetworkInterface" + ] + }, + { + "name": "attached_servers", + "cmdletCount": 3, + "cmdlets": [ + "New-PfbNetworkInterface", + "Update-PfbNetworkInterface", + "Update-PfbObjectStoreVirtualHost" + ] + }, + { + "name": "before_rule_id", + "cmdletCount": 3, + "cmdlets": [ + "New-PfbNetworkAccessRule", + "New-PfbNfsExportRule", + "New-PfbSmbClientRule" + ] + }, + { + "name": "before_rule_name", + "cmdletCount": 3, + "cmdlets": [ + "New-PfbNetworkAccessRule", + "New-PfbNfsExportRule", + "New-PfbSmbClientRule" + ] + }, + { + "name": "bucket_names", + "cmdletCount": 3, + "cmdlets": [ + "Get-PfbLifecycleRule", + "Update-PfbBucketAuditFilter", + "Update-PfbLifecycleRule" + ] + }, + { + "name": "ca_certificate", + "cmdletCount": 3, + "cmdlets": [ + "Update-PfbActiveDirectory", + "Update-PfbDns", + "Update-PfbKmip" + ] + }, + { + "name": "certificate_group_names", + "cmdletCount": 3, + "cmdlets": [ + "Get-PfbCertificateCertificateGroup", + "New-PfbCertificateCertificateGroup", + "Remove-PfbCertificateCertificateGroup" + ] + }, + { + "name": "certificate_names", + "cmdletCount": 3, + "cmdlets": [ + "Get-PfbCertificateCertificateGroup", + "New-PfbCertificateCertificateGroup", + "Remove-PfbCertificateCertificateGroup" + ] + }, + { + "name": "client", + "cmdletCount": 3, + "cmdlets": [ + "New-PfbNetworkAccessRule", + "New-PfbNfsExportRule", + "New-PfbSmbClientRule" + ] + }, + { + "name": "index", + "cmdletCount": 3, + "cmdlets": [ + "New-PfbNetworkAccessRule", + "New-PfbNfsExportRule", + "New-PfbSmbClientRule" + ] + }, + { + "name": "keep_size", + "cmdletCount": 3, + "cmdlets": [ + "New-PfbDataEvictionPolicy", + "Update-PfbDataEvictionPolicy", + "Update-PfbLogTargetFileSystem" + ] + }, + { + "name": "policy", + "cmdletCount": 3, + "cmdlets": [ + "New-PfbNfsExportRule", + "Update-PfbFileSystemExport", + "Update-PfbObjectStoreAccountExport" + ] + }, + { + "name": "prefix", + "cmdletCount": 3, + "cmdlets": [ + "New-PfbSubnet", + "Update-PfbLifecycleRule", + "Update-PfbSubnet" + ] + }, + { + "name": "remote", + "cmdletCount": 3, + "cmdlets": [ + "New-PfbArrayConnection", + "Update-PfbArrayConnection", + "Update-PfbObjectStoreRemoteCredential" + ] + }, + { + "name": "remote_file_system_names", + "cmdletCount": 3, + "cmdlets": [ + "Get-PfbFileSystemReplicaLink", + "New-PfbFileSystemReplicaLink", + "Remove-PfbFileSystemReplicaLink" + ] + }, + { + "name": "remote_ids", + "cmdletCount": 3, + "cmdlets": [ + "New-PfbFileSystemReplicaLinkPolicy", + "New-PfbPolicyFileSystemReplicaLink", + "Update-PfbArrayConnection" + ] + }, + { + "name": "actions", + "cmdletCount": 2, + "cmdlets": [ + "New-PfbS3ExportRule", + "Update-PfbBucketAuditFilter" + ] + }, + { + "name": "bucket", + "cmdletCount": 2, + "cmdlets": [ + "New-PfbLifecycleRule", + "Update-PfbLogTargetObjectStore" + ] + }, + { + "name": "bucket_ids", + "cmdletCount": 2, + "cmdlets": [ + "Update-PfbBucketAuditFilter", + "Update-PfbLifecycleRule" + ] + }, + { + "name": "effect", + "cmdletCount": 2, + "cmdlets": [ + "New-PfbNetworkAccessRule", + "New-PfbS3ExportRule" + ] + }, + { + "name": "email", + "cmdletCount": 2, + "cmdlets": [ + "New-PfbLocalGroup", + "Update-PfbCertificate" + ] + }, + { + "name": "file_system", + "cmdletCount": 2, + "cmdlets": [ + "New-PfbQuotaGroup", + "Update-PfbLogTargetFileSystem" + ] + }, + { + "name": "gids", + "cmdletCount": 2, + "cmdlets": [ + "Get-PfbFileSystemGroup", + "Get-PfbFileSystemGroupQuota" + ] + }, + { + "name": "group", + "cmdletCount": 2, + "cmdlets": [ + "New-PfbQuotaGroup", + "Update-PfbDirectoryServiceRole" + ] + }, + { + "name": "idp", + "cmdletCount": 2, + "cmdlets": [ + "Update-PfbOidcIdp", + "Update-PfbSaml2Idp" + ] + }, + { + "name": "local_file_system_ids", + "cmdletCount": 2, + "cmdlets": [ + "New-PfbFileSystemReplicaLinkPolicy", + "New-PfbPolicyFileSystemReplicaLink" + ] + }, + { + "name": "member_types", + "cmdletCount": 2, + "cmdlets": [ + "Get-PfbPolicyAllMember", + "New-PfbQosPolicyMember" + ] + }, + { + "name": "paths", + "cmdletCount": 2, + "cmdlets": [ + "New-PfbLegalHoldEntity", + "Update-PfbLegalHoldEntity" + ] + }, + { + "name": "permission", + "cmdletCount": 2, + "cmdlets": [ + "New-PfbNfsExportRule", + "New-PfbSmbClientRule" + ] + }, + { + "name": "protocols", + "cmdletCount": 2, + "cmdlets": [ + "Get-PfbFileSystemSession", + "Remove-PfbFileSystemSession" + ] + }, + { + "name": "recursive", + "cmdletCount": 2, + "cmdlets": [ + "New-PfbLegalHoldEntity", + "Update-PfbLegalHoldEntity" + ] + }, + { + "name": "role_ids", + "cmdletCount": 2, + "cmdlets": [ + "Get-PfbObjectStoreRoleAccessPolicy", + "Get-PfbObjectStoreTrustPolicy" + ] + }, + { + "name": "server", + "cmdletCount": 2, + "cmdlets": [ + "New-PfbFileSystemExport", + "Update-PfbFileSystemExport" + ] + }, + { + "name": "uids", + "cmdletCount": 2, + "cmdlets": [ + "Get-PfbFileSystemUser", + "Get-PfbFileSystemUserQuota" + ] + }, + { + "name": "user_names", + "cmdletCount": 2, + "cmdlets": [ + "Get-PfbFileSystemUser", + "Get-PfbFileSystemUserQuota" + ] + }, + { + "name": "abort_incomplete_multipart_uploads_after", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbLifecycleRule" + ] + }, + { + "name": "access", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbNfsExportRule" + ] + }, + { + "name": "access_key_id", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbObjectStoreRemoteCredential" + ] + }, + { + "name": "admin_ids", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbApiToken" + ] + }, + { + "name": "admin_names", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbApiToken" + ] + }, + { + "name": "aggregation_strategy", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbManagementAccessPolicy" + ] + }, + { + "name": "anongid", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbNfsExportRule" + ] + }, + { + "name": "anonuid", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbNfsExportRule" + ] + }, + { + "name": "array_url", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbSaml2Idp" + ] + }, + { + "name": "atime", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbNfsExportRule" + ] + }, + { + "name": "binding", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbSaml2Idp" + ] + }, + { + "name": "certificate", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbCertificate" + ] + }, + { + "name": "certificate_group_ids", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbCertificateCertificateGroup" + ] + }, + { + "name": "certificate_ids", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbCertificateCertificateGroup" + ] + }, + { + "name": "certificate_type", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbCertificate" + ] + }, + { + "name": "change", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbSmbShareRule" + ] + }, + { + "name": "common_name", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbCertificate" + ] + }, + { + "name": "confirm_date", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbLifecycleRule" + ] + }, + { + "name": "country", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbCertificate" + ] + }, + { + "name": "days", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbCertificate" + ] + }, + { + "name": "description", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbLegalHold" + ] + }, + { + "name": "encryption", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbSmbClientRule" + ] + }, + { + "name": "eradication_config", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbFileSystem" + ] + }, + { + "name": "export_enabled", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbObjectStoreAccountExport" + ] + }, + { + "name": "fileid_32bit", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbNfsExportRule" + ] + }, + { + "name": "fleet_ids", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbFleetMember" + ] + }, + { + "name": "full_control", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbSmbShareRule" + ] + }, + { + "name": "group_base", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbDirectoryServiceRole" + ] + }, + { + "name": "interfaces", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbNetworkAccessRule" + ] + }, + { + "name": "intermediate_certificate", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbCertificate" + ] + }, + { + "name": "keep_current_version_for", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbLifecycleRule" + ] + }, + { + "name": "keep_current_version_until", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbLifecycleRule" + ] + }, + { + "name": "keep_for", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbLogTargetFileSystem" + ] + }, + { + "name": "keep_previous_version_for", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbLifecycleRule" + ] + }, + { + "name": "key_algorithm", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbCertificate" + ] + }, + { + "name": "key_size", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbCertificate" + ] + }, + { + "name": "local_directory_service_names", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbLocalGroupMember" + ] + }, + { + "name": "locality", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbCertificate" + ] + }, + { + "name": "log_name_prefix", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbLogTargetObjectStore" + ] + }, + { + "name": "log_rotate", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbLogTargetObjectStore" + ] + }, + { + "name": "management", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbSaml2Idp" + ] + }, + { + "name": "management_access_policies", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbAdmin" + ] + }, + { + "name": "max_session_duration", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbObjectStoreRole" + ] + }, + { + "name": "max_total_bytes_per_sec", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbQosPolicy" + ] + }, + { + "name": "max_total_ops_per_sec", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbQosPolicy" + ] + }, + { + "name": "node_group_ids", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbNodeGroupNode" + ] + }, + { + "name": "node_group_names", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbNodeGroupNode" + ] + }, + { + "name": "node_ids", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbNodeGroupNode" + ] + }, + { + "name": "node_names", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbNodeGroupNode" + ] + }, + { + "name": "organization", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbCertificate" + ] + }, + { + "name": "organizational_unit", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbCertificate" + ] + }, + { + "name": "parameters", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbWorkload" + ] + }, + { + "name": "passphrase", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbCertificate" + ] + }, + { + "name": "principal", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbSmbShareRule" + ] + }, + { + "name": "private_key", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbCertificate" + ] + }, + { + "name": "protocol", + "cmdletCount": 1, + "cmdlets": [ + "Get-PfbArrayPerformance" + ] + }, + { + "name": "public_key", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbAdmin" + ] + }, + { + "name": "rdma_enabled", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbNetworkInterface" + ] + }, + { + "name": "read", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbSmbShareRule" + ] + }, + { + "name": "realm_ids", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbRealmDefaults" + ] + }, + { + "name": "realm_names", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbRealmDefaults" + ] + }, + { + "name": "required_transport_security", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbNfsExportRule" + ] + }, + { + "name": "resources", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbS3ExportRule" + ] + }, + { + "name": "retention_lock", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbWormPolicy" + ] + }, + { + "name": "s3_prefixes", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbBucketAuditFilter" + ] + }, + { + "name": "secret_access_key", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbObjectStoreRemoteCredential" + ] + }, + { + "name": "secure", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbNfsExportRule" + ] + }, + { + "name": "security", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbNfsExportRule" + ] + }, + { + "name": "source", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbFileSystem" + ] + }, + { + "name": "sp", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbSaml2Idp" + ] + }, + { + "name": "state", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbCertificate" + ] + }, + { + "name": "subject_alternative_names", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbCertificate" + ] + }, + { + "name": "timeout", + "cmdletCount": 1, + "cmdlets": [ + "New-PfbApiToken" + ] + }, + { + "name": "v2c", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbSnmpManager" + ] + }, + { + "name": "v3", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbSnmpManager" + ] + }, + { + "name": "version", + "cmdletCount": 1, + "cmdlets": [ + "Update-PfbSnmpManager" + ] + }, + { + "name": "access_policies", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "access_token_ttl_in_ms", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "account_exports", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "allow_errors", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "allowed_headers", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "allowed_methods", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "allowed_origins", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "analysis_period_end_time", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "analysis_period_start_time", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "banner", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "bucket_defaults", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "bucket_type", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "client_names", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "component_name", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "conditions", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "contact", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "context_names", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "current_state", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "default_inbound_tls_policy", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "delete_sanitization_certificate", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "direct_notifications_enabled", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "direction", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "directory_configurations", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "discover_mtu", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "edge_agent_update_enabled", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "edge_management_enabled", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "effective", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "enforce_action_restrictions", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "enforce_dictionary_check", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "enforce_username_check", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "eradicate_all_data", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "exclude_rules", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "export_configurations", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "expose_api_token", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "finalize", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "fragment_packet", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "full_access", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "group_gids", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "group_sids", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "hard_limit_enabled", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "idle_timeout", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "indices", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "inodes", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "issuer", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "keytab_file", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "keytab_ids", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "keytab_names", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "kmip_server", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "link_type", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "local_bucket_ids", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "local_directory_service_ids", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "local_file_system", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "local_host", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "local_only", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "local_port", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "local_port_names", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "lockout_duration", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "max_login_attempts", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "max_password_age", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "max_role", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "member_sids", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "min_character_groups", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "min_characters_per_group", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "min_password_age", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "min_password_length", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "name_prefixes", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "names_or_owner_names", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "network_access_policy", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "ntp_servers", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "object_lock_config", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "owner_ids", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "password_history", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "periodic_replication_configurations", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "phonehome_enabled", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "placement_configurations", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "platform_features", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "policies", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "port", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "preserve_configuration_data", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "principals", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "print_latency", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "proxy", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "purity_defined", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "qos_configurations", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "quota_configurations", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "refresh", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "remote_assist_active", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "remote_assist_duration", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "remote_file_system", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "remote_file_system_ids", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "remote_host", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "remote_port", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "resolve_hostname", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "role", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "rule_id", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "session_names", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "sids", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "signature", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "signed_verification_key", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "skip_phonehome_check", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "snapshot_configurations", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "software_names", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "software_versions", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "storage_class_names", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "time_zone", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "total_item_count", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "unreachable", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "volume_configurations", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "without_default_access_list", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "workload_ids", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "workload_names", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "workload_tags", + "cmdletCount": 0, + "cmdlets": [] + }, + { + "name": "workload_type", + "cmdletCount": 0, + "cmdlets": [] + } + ], + "validateSetDrift": [], + "newValidateSetCandidates": [ + { + "cmdlet": "Get-PfbPolicyAllMember", + "parameter": "MemberType", + "wireName": "member_types", + "specValues": [ + "file-systems", + "file-system-snapshots", + "file-system-replica-links", + "object-store-users" + ], + "recommendation": "ArgumentCompleter" + } + ], + "responseFieldRemovals": [ + { + "endpoint": "GET /arrays/performance/replication", + "field": "remote", + "location": "items", + "introducedVersion": "2.0", + "lastSeenVersion": "2.10" + }, + { + "endpoint": "GET /file-system-snapshots", + "field": "copyable", + "location": "items", + "introducedVersion": "2.0", + "lastSeenVersion": "2.10" + }, + { + "endpoint": "GET /network-interfaces", + "field": "server", + "location": "items", + "introducedVersion": "2.16", + "lastSeenVersion": "2.19" + }, + { + "endpoint": "PATCH /file-system-snapshots", + "field": "copyable", + "location": "items", + "introducedVersion": "2.0", + "lastSeenVersion": "2.10" + }, + { + "endpoint": "PATCH /network-interfaces", + "field": "server", + "location": "items", + "introducedVersion": "2.16", + "lastSeenVersion": "2.19" + }, + { + "endpoint": "POST /file-system-snapshots", + "field": "copyable", + "location": "items", + "introducedVersion": "2.0", + "lastSeenVersion": "2.10" + }, + { + "endpoint": "POST /network-interfaces", + "field": "server", + "location": "items", + "introducedVersion": "2.16", + "lastSeenVersion": "2.19" + } + ], + "responseFieldRenameCandidates": [ + { + "endpoint": "GET /network-interfaces", + "location": "items", + "from": "server", + "to": "attached_servers", + "version": "2.20" + }, + { + "endpoint": "PATCH /network-interfaces", + "location": "items", + "from": "server", + "to": "attached_servers", + "version": "2.20" + }, + { + "endpoint": "POST /network-interfaces", + "location": "items", + "from": "server", + "to": "attached_servers", + "version": "2.20" + } + ], + "unhandledResponseEnvelopeFields": [ + { + "field": "errors", + "endpointCount": 140 + }, + { + "field": "total", + "endpointCount": 28 + }, + { + "field": "context", + "endpointCount": 7 + }, + { + "field": "access_token", + "endpointCount": 1 + }, + { + "field": "expires_in", + "endpointCount": 1 + }, + { + "field": "issued_token_type", + "endpointCount": 1 + }, + { + "field": "login_banner", + "endpointCount": 1 + }, + { + "field": "more_items_remaining", + "endpointCount": 1 + }, + { + "field": "token_type", + "endpointCount": 1 + }, + { + "field": "username", + "endpointCount": 1 + }, + { + "field": "versions", + "endpointCount": 1 + } + ], + "contextCardinality": { + "componentSourceVersion": "2.28", + "http207SignalAvailable": true, + "endpointsWithContextNames": 376, + "multiContextCapable": 135, + "signalDisagreements": [ + { + "endpoint": "GET /arrays/ssh-certificate-authority-policies", + "method": "GET", + "contextComponent": "Context_names_get", + "shape": "rule-says-capable-but-declares-no-207", + "ruleSaysMultiValue": true, + "componentSaysMultiValue": true, + "declaresAllowErrors": true, + "declaresHttp207": false, + "multiValueSignals": [ + "allow_errors", + "component" + ], + "sizeOneSignals": [ + "http207" + ], + "summary": "signals split -- multi-value: allow_errors, component; size-1: http207; module rule says multi-value" + }, + { + "endpoint": "GET /audit-file-systems-policy-operations", + "method": "GET", + "contextComponent": "Context_names_get", + "shape": "rule-says-capable-but-declares-no-207", + "ruleSaysMultiValue": true, + "componentSaysMultiValue": true, + "declaresAllowErrors": true, + "declaresHttp207": false, + "multiValueSignals": [ + "allow_errors", + "component" + ], + "sizeOneSignals": [ + "http207" + ], + "summary": "signals split -- multi-value: allow_errors, component; size-1: http207; module rule says multi-value" + }, + { + "endpoint": "GET /log-targets/file-systems", + "method": "GET", + "contextComponent": "Context_names_get", + "shape": "rule-says-capable-but-declares-no-207", + "ruleSaysMultiValue": true, + "componentSaysMultiValue": true, + "declaresAllowErrors": true, + "declaresHttp207": false, + "multiValueSignals": [ + "allow_errors", + "component" + ], + "sizeOneSignals": [ + "http207" + ], + "summary": "signals split -- multi-value: allow_errors, component; size-1: http207; module rule says multi-value" + }, + { + "endpoint": "GET /presets/workload", + "method": "GET", + "contextComponent": "Context_names_get", + "shape": "component-says-multi-value-but-no-allow-errors", + "ruleSaysMultiValue": false, + "componentSaysMultiValue": true, + "declaresAllowErrors": false, + "declaresHttp207": false, + "multiValueSignals": [ + "component" + ], + "sizeOneSignals": [ + "allow_errors", + "http207" + ], + "summary": "signals split -- multi-value: component; size-1: allow_errors, http207; module rule says size-1" + }, + { + "endpoint": "GET /realms", + "method": "GET", + "contextComponent": "Context_names_get", + "shape": "rule-says-capable-but-declares-no-207", + "ruleSaysMultiValue": true, + "componentSaysMultiValue": true, + "declaresAllowErrors": true, + "declaresHttp207": false, + "multiValueSignals": [ + "allow_errors", + "component" + ], + "sizeOneSignals": [ + "http207" + ], + "summary": "signals split -- multi-value: allow_errors, component; size-1: http207; module rule says multi-value" + }, + { + "endpoint": "GET /topology-groups", + "method": "GET", + "contextComponent": "Context_names_get", + "shape": "component-says-multi-value-but-no-allow-errors", + "ruleSaysMultiValue": false, + "componentSaysMultiValue": true, + "declaresAllowErrors": false, + "declaresHttp207": false, + "multiValueSignals": [ + "component" + ], + "sizeOneSignals": [ + "allow_errors", + "http207" + ], + "summary": "signals split -- multi-value: component; size-1: allow_errors, http207; module rule says size-1" + }, + { + "endpoint": "GET /topology-groups/arrays", + "method": "GET", + "contextComponent": "Context_names_get", + "shape": "component-says-multi-value-but-no-allow-errors", + "ruleSaysMultiValue": false, + "componentSaysMultiValue": true, + "declaresAllowErrors": false, + "declaresHttp207": false, + "multiValueSignals": [ + "component" + ], + "sizeOneSignals": [ + "allow_errors", + "http207" + ], + "summary": "signals split -- multi-value: component; size-1: allow_errors, http207; module rule says size-1" + }, + { + "endpoint": "GET /topology-groups/members", + "method": "GET", + "contextComponent": "Context_names_get", + "shape": "component-says-multi-value-but-no-allow-errors", + "ruleSaysMultiValue": false, + "componentSaysMultiValue": true, + "declaresAllowErrors": false, + "declaresHttp207": false, + "multiValueSignals": [ + "component" + ], + "sizeOneSignals": [ + "allow_errors", + "http207" + ], + "summary": "signals split -- multi-value: component; size-1: allow_errors, http207; module rule says size-1" + }, + { + "endpoint": "PATCH /directory-services/test", + "method": "PATCH", + "contextComponent": "Context_names", + "shape": "size-1-component-but-declares-allow-errors", + "ruleSaysMultiValue": false, + "componentSaysMultiValue": false, + "declaresAllowErrors": true, + "declaresHttp207": false, + "multiValueSignals": [ + "allow_errors" + ], + "sizeOneSignals": [ + "component", + "http207" + ], + "summary": "signals split -- multi-value: allow_errors; size-1: component, http207; module rule says size-1" + } + ], + "sizeOneWithAllowErrors": [ + { + "endpoint": "PATCH /directory-services/test", + "method": "PATCH", + "contextComponent": "Context_names" + } + ], + "unresolvedComponents": [] + } +} diff --git a/Reports/PfbApiDriftReport.md b/Reports/PfbApiDriftReport.md new file mode 100644 index 0000000..ca94f04 --- /dev/null +++ b/Reports/PfbApiDriftReport.md @@ -0,0 +1,925 @@ +# API Drift Report + +Generated by `tools/Build-PfbApiDriftReport.ps1` (29 analysed REST versions; 29 available on disk under `tools/specs/`). + +Reporting only -- no `Public/` cmdlet is edited by this script. + +## How to read this report + +This report accepts **false positives in order to eliminate false negatives**. A field is listed as missing even though the module can already set it, when a parameter covering it could not be traced to a wire name. + +**Detection.** Only possible where `confidence.level` is `partial`. When it is `high` (`unresolvedParameters` empty), the row carries no false-positive risk from this mechanism. + +**Resolution procedure:** +1. Open the named parameter at the given `file:line` and follow where its value goes. +2. If it reaches the wire under the same name as the reported gap -> the gap is a false positive AND a tooling bug: the parser does not recognise that idiom. File it as a parser gap and fix the parser. +3. If it reaches the wire under a different name -> the reported gap may still be real; check that field against the spec. +4. If it never reaches the wire -> the gap is real. + +**Why this trade is right:** a false positive costs a reader one `file:line` lookup; a false negative costs an undetected gap indefinitely. Every false positive here is a parser-gap detector -- it either fixes the tool permanently for every endpoint, or confirms a real gap. + +## Summary + +- Uncovered endpoints: 96 +- Endpoints with parameter gaps: 438 +- Missing body properties (addable): 422 +- Missing query parameters (addable): 952 +- Read-only body fields (not addable -- see the Read-only fields section below): 382 +- Phantom fields silently excluded (accumulated in the capability map, absent from the newest analysed spec): 40 +- Partial-confidence endpoints (see `How to read this report` above, and each row's marker in the Parameter gaps table): 59 +- Systemic gaps (distinct field names collapsed across high-confidence endpoints, detailed below): 252 +- ValidateSet drift: 0 +- New ValidateSet candidates: 1 +- Context cardinality signal disagreements (fb2.28): 9 +- Removed response fields: **7** +- Response rename candidates: **3** +- Envelope fields not read by `Invoke-PfbApiRequest`: **11** + +## Systemic gaps + +One finding per distinct wire field name, collapsed across every endpoint where a high-confidence gap exists (decision 7) -- turns hundreds of per-endpoint rows into a handful of real, actionable decisions. "Cmdlets already using this name" is decision 8's convention-strength ranking: a high count means closing the remaining gaps for this name is a mechanical batch fix; zero means no established convention exists to extend at all -- closing it is an architectural decision, not a mechanical one. + +Showing the top 25 of 252 findings by endpoint count -- the full list is in the JSON manifest's `systemicGaps`, nothing is dropped there. + +| Field name | Endpoints | Query | Body | Cmdlets already using this name | Annotation | +|---|---|---|---|---|---| +| `context_names` | 270 | 270 | 0 | 0 | not yet implemented | +| `allow_errors` | 118 | 118 | 0 | 0 | not yet implemented | +| `ids` | 43 | 43 | 0 | 223 | | +| `names` | 31 | 31 | 0 | 308 | | +| `sort` | 28 | 28 | 0 | 180 | | +| `bucket_ids` | 17 | 17 | 0 | 2 | | +| `policy_ids` | 17 | 17 | 0 | 98 | | +| `total_only` | 17 | 17 | 0 | 39 | | +| `bucket_names` | 16 | 16 | 0 | 3 | | +| `member_ids` | 15 | 15 | 0 | 85 | | +| `remote_ids` | 15 | 15 | 0 | 3 | | +| `policy_names` | 11 | 11 | 0 | 118 | | +| `file_system_ids` | 9 | 9 | 0 | 9 | | +| `remote_names` | 9 | 9 | 0 | 9 | | +| `local_file_system_ids` | 8 | 8 | 0 | 2 | | +| `actions` | 7 | 0 | 7 | 2 | | +| `limit` | 7 | 7 | 0 | 199 | | +| `name` | 7 | 0 | 7 | 20 | | +| `versions` | 7 | 7 | 0 | 5 | | +| `enabled` | 6 | 0 | 6 | 10 | | +| `filter` | 6 | 6 | 0 | 202 | | +| `gids` | 6 | 6 | 0 | 2 | | +| `role_ids` | 6 | 6 | 0 | 2 | | +| `role_names` | 6 | 6 | 0 | 4 | | +| `workload_ids` | 6 | 6 | 0 | 0 | | + +## Parameter gaps + +Endpoints an existing cmdlet already calls, where the capability map knows of a query parameter or addable body property the cmdlet does not yet expose. Read-only fields (never addable, regardless of confidence) are reported separately below, never blended into either column here. + +| Endpoint | Cmdlets | Missing query parameters | Missing body properties | Confidence | Notes | +|---|---|---|---|---|---| +| `DELETE /active-directory` | Remove-PfbActiveDirectory | local_only | | `high` | | +| `DELETE /admins/api-tokens` | Remove-PfbApiToken | admin_ids, admin_names, context_names | | `high` | | +| `DELETE /admins/cache` | Remove-PfbAdminCache | context_names | | `high` | | +| `DELETE /admins/management-access-policies` | Remove-PfbAdminManagementAccessPolicy | context_names | | `high` | POST/PATCH/DELETE return 403 regardless of account; not an implementation bug | +| `DELETE /admins/ssh-certificate-authority-policies` | Remove-PfbAdminSshCaPolicy | context_names | | `high` | | +| `DELETE /array-connections` | Remove-PfbArrayConnection | context_names, remote_ids | | `high` | | +| `DELETE /arrays/ssh-certificate-authority-policies` | Remove-PfbArraySshCaPolicy | context_names | | `high` | | +| `DELETE /audit-file-systems-policies` | Remove-PfbAuditFileSystemPolicy | context_names | | `high` | | +| `DELETE /audit-file-systems-policies/members` | Remove-PfbAuditFileSystemPolicyMember | context_names | | `high` | | +| `DELETE /audit-object-store-policies` | Remove-PfbAuditObjectStorePolicy | context_names | | `high` | | +| `DELETE /audit-object-store-policies/members` | Remove-PfbAuditObjectStorePolicyMember | context_names | | `high` | | +| `DELETE /buckets` | Remove-PfbBucket | context_names | | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `DELETE /buckets/audit-filters` | Remove-PfbBucketAuditFilter | bucket_ids, bucket_names, context_names, names | | `high` | | +| `DELETE /buckets/bucket-access-policies` | Remove-PfbBucketAccessPolicy | bucket_ids, bucket_names, context_names, names | | `high` | | +| `DELETE /buckets/bucket-access-policies/rules` | Remove-PfbBucketAccessPolicyRule | bucket_ids, bucket_names, context_names, policy_names | | `high` | | +| `DELETE /buckets/cross-origin-resource-sharing-policies` | Remove-PfbBucketCorsPolicy | bucket_ids, bucket_names, context_names, names | | `high` | | +| `DELETE /buckets/cross-origin-resource-sharing-policies/rules` | Remove-PfbBucketCorsPolicyRule | bucket_ids, bucket_names, context_names, policy_names | | `high` | | +| `DELETE /certificates/certificate-groups` | Remove-PfbCertificateCertificateGroup | certificate_group_ids, certificate_ids | | `high` | | +| `DELETE /data-eviction-policies` | Remove-PfbDataEvictionPolicy | context_names | | `high` | | +| `DELETE /data-eviction-policies/file-systems` | Remove-PfbDataEvictionPolicyFileSystem | context_names | | `high` | | +| `DELETE /directory-services/local/groups` | Remove-PfbLocalGroup | context_names, gids, local_directory_service_ids, local_directory_service_names, sids | | `high` | | +| `DELETE /directory-services/local/groups/members` | Remove-PfbLocalGroupMember | context_names, group_gids, group_sids, local_directory_service_ids, local_directory_service_names, member_ids, member_sids, member_types | | `high` | | +| `DELETE /dns` | Remove-PfbDns | context_names | | `high` | | +| `DELETE /file-system-exports` | Remove-PfbFileSystemExport | context_names | | `high` | | +| `DELETE /file-system-replica-links` | Remove-PfbFileSystemReplicaLink | context_names, local_file_system_ids, remote_file_system_ids, remote_ids | | `high` | | +| `DELETE /file-system-replica-links/policies` | Remove-PfbFileSystemReplicaLinkPolicy | context_names, local_file_system_ids, local_file_system_names, remote_ids, remote_names | | `high` | | +| `DELETE /file-system-snapshots` | Remove-PfbFileSystemSnapshot | context_names | | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `DELETE /file-system-snapshots/policies` | Remove-PfbFileSystemSnapshotPolicy | context_names | | `high` | | +| `DELETE /file-system-snapshots/transfer` | Remove-PfbFileSystemSnapshotTransfer | context_names, remote_ids, remote_names | | `high` | | +| `DELETE /file-systems` | Remove-PfbFileSystem | context_names | | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `DELETE /file-systems/audit-policies` | Remove-PfbFileSystemAuditPolicy | context_names | | `high` | | +| `DELETE /file-systems/locks` | Remove-PfbFileLock | client_names, context_names, file_system_ids, file_system_names, inodes, paths, recursive | | `high` | | +| `DELETE /file-systems/policies` | Remove-PfbFileSystemPolicy | context_names | | `high` | | +| `DELETE /file-systems/sessions` | Remove-PfbFileSystemSession | client_names, context_names, disruptive, user_names | | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `DELETE /file-systems/user-group-quota-policies` | Remove-PfbFileSystemUserGroupQuotaPolicy | context_names | | `high` | | +| `DELETE /fleets/members` | Remove-PfbFleetMember | member_ids, unreachable | | `high` | | +| `DELETE /lifecycle-rules` | Remove-PfbLifecycleRule | bucket_ids, bucket_names, context_names | | `high` | | +| `DELETE /log-targets/file-systems` | Remove-PfbLogTargetFileSystem | context_names | | `high` | | +| `DELETE /log-targets/object-store` | Remove-PfbLogTargetObjectStore | context_names | | `high` | | +| `DELETE /management-access-policies` | Remove-PfbManagementAccessPolicy | context_names | | `high` | POST/PATCH/DELETE return 403 regardless of account; not an implementation bug | +| `DELETE /management-access-policies/admins` | Remove-PfbManagementAccessPolicyAdmin | context_names | | `high` | POST/PATCH/DELETE return 403 regardless of account; not an implementation bug | +| `DELETE /network-access-policies/rules` | Remove-PfbNetworkAccessRule | ids, versions | | `high` | | +| `DELETE /network-interfaces/tls-policies` | Remove-PfbNetworkInterfaceTlsPolicy | member_ids, policy_ids | | `high` | | +| `DELETE /nfs-export-policies` | Remove-PfbNfsExportPolicy | context_names, versions | | `high` | | +| `DELETE /nfs-export-policies/rules` | Remove-PfbNfsExportRule | context_names, ids, versions | | `high` | | +| `DELETE /node-groups/nodes` | Remove-PfbNodeGroupNode | node_group_ids, node_group_names, node_ids, node_names | | `high` | | +| `DELETE /object-store-access-keys` | Remove-PfbObjectStoreAccessKey | context_names | | `high` | | +| `DELETE /object-store-access-policies` | Remove-PfbObjectStoreAccessPolicy | context_names | | `high` | | +| `DELETE /object-store-access-policies/object-store-roles` | Remove-PfbObjectStoreAccessPolicyRole | context_names, member_ids, policy_ids | | `high` | | +| `DELETE /object-store-access-policies/object-store-users` | Remove-PfbObjectStoreAccessPolicyUser | context_names, member_ids, policy_ids | | `high` | | +| `DELETE /object-store-access-policies/rules` | Remove-PfbObjectStoreAccessPolicyRule | context_names, policy_ids | | `high` | | +| `DELETE /object-store-account-exports` | Remove-PfbObjectStoreAccountExport | context_names | | `high` | | +| `DELETE /object-store-accounts` | Remove-PfbObjectStoreAccount | context_names | | `high` | | +| `DELETE /object-store-remote-credentials` | Remove-PfbObjectStoreRemoteCredential | context_names | | `high` | | +| `DELETE /object-store-roles` | Remove-PfbObjectStoreRole | context_names | | `high` | | +| `DELETE /object-store-roles/object-store-access-policies` | Remove-PfbObjectStoreRoleAccessPolicy | context_names, member_ids, policy_ids, policy_names | | `high` | | +| `DELETE /object-store-roles/object-store-trust-policies/rules` | Remove-PfbObjectStoreTrustPolicyRule | context_names, indices, role_ids, role_names | | `high` | | +| `DELETE /object-store-users` | Remove-PfbObjectStoreUser | context_names | | `high` | | +| `DELETE /object-store-users/object-store-access-policies` | Remove-PfbObjectStoreUserAccessPolicy | context_names, member_ids, policy_ids | | `high` | | +| `DELETE /object-store-virtual-hosts` | Remove-PfbObjectStoreVirtualHost | context_names | | `high` | | +| `DELETE /policies` | Remove-PfbPolicy | context_names | | `high` | | +| `DELETE /policies/file-system-replica-links` | Remove-PfbPolicyFileSystemReplicaLink | context_names, local_file_system_ids, local_file_system_names, remote_ids, remote_names | | `high` | | +| `DELETE /policies/file-systems` | Remove-PfbPolicyFileSystem | context_names | | `high` | | +| `DELETE /presets/workload` | Remove-PfbPresetWorkload | context_names | | `high` | | +| `DELETE /qos-policies` | Remove-PfbQosPolicy | context_names | | `high` | | +| `DELETE /qos-policies/members` | Remove-PfbQosPolicyMember | context_names, member_types | | `high` | | +| `DELETE /quotas/groups` | Remove-PfbQuotaGroup | context_names, file_system_ids, file_system_names, gids, group_names, names | | `partial` -- /!\ 2 unresolved params (see Partial-confidence detail below) | | +| `DELETE /quotas/users` | Remove-PfbQuotaUser | context_names, file_system_ids, file_system_names, names, uids, user_names | | `partial` -- /!\ 2 unresolved params (see Partial-confidence detail below) | | +| `DELETE /s3-export-policies` | Remove-PfbS3ExportPolicy | context_names | | `high` | | +| `DELETE /s3-export-policies/rules` | Remove-PfbS3ExportRule | context_names | | `high` | | +| `DELETE /servers` | Remove-PfbServer | cascade_delete | | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `DELETE /smb-client-policies` | Remove-PfbSmbClientPolicy | context_names | | `high` | | +| `DELETE /smb-client-policies/rules` | Remove-PfbSmbClientRule | context_names, ids, versions | | `high` | | +| `DELETE /smb-share-policies` | Remove-PfbSmbSharePolicy | context_names | | `high` | | +| `DELETE /smb-share-policies/rules` | Remove-PfbSmbShareRule | context_names, ids | | `high` | | +| `DELETE /ssh-certificate-authority-policies/admins` | Remove-PfbSshCaPolicyAdmin | context_names | | `high` | | +| `DELETE /ssh-certificate-authority-policies/arrays` | Remove-PfbSshCaPolicyArray | context_names | | `high` | | +| `DELETE /user-group-quota-policies` | Remove-PfbUserGroupQuotaPolicy | context_names | | `high` | | +| `DELETE /user-group-quota-policies/file-systems` | Remove-PfbUserGroupQuotaPolicyFileSystem | context_names | | `high` | | +| `DELETE /user-group-quota-policies/rules` | Remove-PfbUserGroupQuotaPolicyRule | context_names | | `high` | | +| `DELETE /workloads` | Remove-PfbWorkload | context_names | | `high` | | +| `DELETE /workloads/tags` | Remove-PfbWorkloadTag | context_names | | `high` | | +| `DELETE /worm-data-policies` | Remove-PfbWormPolicy | context_names | | `high` | | +| `GET /active-directory` | Get-PfbActiveDirectory | ids, limit, sort | | `high` | | +| `GET /active-directory/test` | Test-PfbActiveDirectory | allow_errors, context_names, filter, limit, sort | | `high` | | +| `GET /admins` | Get-PfbAdmin | allow_errors, context_names, expose_api_token | | `high` | | +| `GET /admins/api-tokens` | Get-PfbApiToken | admin_ids, admin_names, allow_errors, context_names, expose_api_token | | `high` | | +| `GET /admins/cache` | Get-PfbAdminCache | allow_errors, context_names, refresh | | `high` | | +| `GET /admins/management-access-policies` | Get-PfbAdminManagementAccessPolicy | allow_errors, context_names, sort | | `high` | POST/PATCH/DELETE return 403 regardless of account; not an implementation bug | +| `GET /admins/ssh-certificate-authority-policies` | Get-PfbAdminSshCaPolicy | allow_errors, context_names, sort | | `high` | | +| `GET /alert-watchers/test` | Test-PfbAlertWatcher | filter, sort | | `high` | | +| `GET /array-connections` | Get-PfbArrayConnection | allow_errors, context_names, remote_ids | | `high` | | +| `GET /array-connections/connection-key` | Get-PfbArrayConnectionKey | ids | | `high` | | +| `GET /array-connections/path` | Get-PfbArrayConnectionPath | allow_errors, context_names, ids, remote_ids | | `high` | | +| `GET /array-connections/performance/replication` | Get-PfbArrayConnectionPerformanceReplication | ids, remote_ids, total_only | | `high` | | +| `GET /arrays` | Get-PfbArray, Test-PfbConnection | allow_errors, context_names | | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `GET /arrays/clients/performance` | Get-PfbArrayClientPerformance | names, protocol, total_only | | `high` | | +| `GET /arrays/clients/s3-specific-performance` | Get-PfbArrayClientS3Performance | names, total_only | | `high` | | +| `GET /arrays/http-specific-performance` | Get-PfbArrayHttpPerformance | allow_errors, context_names | | `high` | | +| `GET /arrays/nfs-specific-performance` | Get-PfbArrayNfsPerformance | allow_errors, context_names | | `high` | | +| `GET /arrays/performance` | Get-PfbArrayPerformance | allow_errors, context_names | | `high` | | +| `GET /arrays/performance/replication` | Get-PfbArrayPerformanceReplication | allow_errors, context_names | | `high` | | +| `GET /arrays/s3-specific-performance` | Get-PfbArrayS3Performance | allow_errors, context_names | | `high` | | +| `GET /arrays/space` | Get-PfbArraySpace | allow_errors, context_names, end_time, resolution, start_time | | `high` | | +| `GET /arrays/space/storage-classes` | Get-PfbArrayStorageClass | end_time, resolution, start_time, storage_class_names, total_only | | `high` | | +| `GET /arrays/ssh-certificate-authority-policies` | Get-PfbArraySshCaPolicy | allow_errors, context_names, sort | | `high` | | +| `GET /arrays/supported-time-zones` | Get-PfbArraySupportedTimeZone | names | | `high` | | +| `GET /audit-file-systems-policies` | Get-PfbAuditFileSystemPolicy | allow_errors, context_names | | `high` | | +| `GET /audit-file-systems-policies/members` | Get-PfbAuditFileSystemPolicyMember | allow_errors, context_names | | `high` | | +| `GET /audit-file-systems-policy-operations` | Get-PfbAuditFileSystemPolicyOperation | allow_errors, context_names, names | | `high` | | +| `GET /audit-object-store-policies` | Get-PfbAuditObjectStorePolicy | allow_errors, context_names | | `high` | | +| `GET /audit-object-store-policies/members` | Get-PfbAuditObjectStorePolicyMember | allow_errors, context_names | | `high` | | +| `GET /audits` | Get-PfbAudit | allow_errors, context_names | | `high` | | +| `GET /blades` | Get-PfbBlade, Get-PfbNode | total_only | | `high` | | +| `GET /bucket-audit-filter-actions` | Get-PfbBucketAuditFilterAction | allow_errors, context_names, names | | `high` | | +| `GET /bucket-replica-links` | Get-PfbBucketReplicaLink | allow_errors, context_names, ids, local_bucket_ids, remote_ids, remote_names, total_only | | `high` | | +| `GET /buckets` | Get-PfbBucket | allow_errors, context_names | | `high` | | +| `GET /buckets/audit-filters` | Get-PfbBucketAuditFilter | allow_errors, bucket_ids, bucket_names, context_names, names | | `high` | | +| `GET /buckets/bucket-access-policies` | Get-PfbBucketAccessPolicy | allow_errors, bucket_ids, bucket_names, context_names | | `high` | | +| `GET /buckets/bucket-access-policies/rules` | Get-PfbBucketAccessPolicyRule | allow_errors, bucket_ids, bucket_names, context_names | | `high` | | +| `GET /buckets/cross-origin-resource-sharing-policies` | Get-PfbBucketCorsPolicy | allow_errors, bucket_ids, bucket_names, context_names | | `high` | | +| `GET /buckets/cross-origin-resource-sharing-policies/rules` | Get-PfbBucketCorsPolicyRule | allow_errors, bucket_ids, bucket_names, context_names | | `high` | | +| `GET /certificate-groups/certificates` | Get-PfbCertificateGroupCertificate | certificate_group_ids, certificate_group_names, certificate_ids, certificate_names | | `high` | | +| `GET /certificate-groups/uses` | Get-PfbCertificateGroupUse | ids | | `high` | | +| `GET /certificates/certificate-groups` | Get-PfbCertificateCertificateGroup | certificate_group_ids, certificate_ids, sort | | `high` | | +| `GET /certificates/uses` | Get-PfbCertificateUse | ids | | `high` | | +| `GET /data-eviction-policies` | Get-PfbDataEvictionPolicy | allow_errors, context_names | | `high` | | +| `GET /data-eviction-policies/file-systems` | Get-PfbDataEvictionPolicyFileSystem | allow_errors, context_names | | `high` | | +| `GET /data-eviction-policies/members` | Get-PfbDataEvictionPolicyMember | allow_errors, context_names | | `high` | | +| `GET /directory-services` | Get-PfbDirectoryService | ids, limit, sort | | `high` | | +| `GET /directory-services/local/directory-services` | Get-PfbLocalDirectoryService | allow_errors, context_names, destroyed, total_item_count | | `high` | | +| `GET /directory-services/local/groups` | Get-PfbLocalGroup | allow_errors, context_names, gids, sids, total_item_count | | `high` | | +| `GET /directory-services/local/groups/members` | Get-PfbLocalGroupMember | allow_errors, context_names, group_gids, group_sids, member_ids, member_sids, member_types, total_item_count | | `high` | | +| `GET /directory-services/roles` | Get-PfbDirectoryServiceRole | role_ids, role_names | | `high` | | +| `GET /directory-services/roles/management-access-policies` | Get-PfbDirectoryServiceRoleManagementPolicy | sort | | `high` | POST/PATCH/DELETE return 403 regardless of account; not an implementation bug | +| `GET /directory-services/test` | Test-PfbDirectoryService | allow_errors, context_names, filter, ids, limit, names, sort | | `high` | | +| `GET /dns` | Get-PfbDns | allow_errors, context_names, ids, names | | `high` | | +| `GET /drives` | Get-PfbDrive | total_only | | `high` | | +| `GET /file-system-exports` | Get-PfbFileSystemExport | allow_errors, context_names, workload_ids, workload_names | | `high` | | +| `GET /file-system-group-quotas` | Get-PfbFileSystemGroupQuota | allow_errors, context_names | | `high` | | +| `GET /file-system-replica-links` | Get-PfbFileSystemReplicaLink | allow_errors, context_names, ids, local_file_system_ids, remote_file_system_ids, remote_ids, remote_names | | `high` | | +| `GET /file-system-replica-links/policies` | Get-PfbFileSystemReplicaLinkPolicy | allow_errors, context_names, local_file_system_ids, local_file_system_names, remote_file_system_ids, remote_file_system_names, remote_ids, remote_names | | `high` | | +| `GET /file-system-replica-links/transfer` | Get-PfbFileSystemReplicaLinkTransfer | allow_errors, context_names, names_or_owner_names, remote_ids, remote_names | | `high` | | +| `GET /file-system-snapshots` | Get-PfbFileSystemSnapshot | allow_errors, context_names, names_or_owner_names, owner_ids | | `high` | | +| `GET /file-system-snapshots/policies` | Get-PfbFileSystemSnapshotPolicy | allow_errors, context_names | | `high` | | +| `GET /file-system-snapshots/transfer` | Get-PfbFileSystemSnapshotTransfer | allow_errors, context_names, names_or_owner_names | | `high` | | +| `GET /file-system-user-quotas` | Get-PfbFileSystemUserQuota | allow_errors, context_names | | `high` | | +| `GET /file-systems` | Get-PfbFileSystem | allow_errors, context_names, workload_ids, workload_names | | `high` | | +| `GET /file-systems/audit-policies` | Get-PfbFileSystemAuditPolicy | allow_errors, context_names | | `high` | | +| `GET /file-systems/groups` | Get-PfbFileSystemGroup | allow_errors, context_names | | `high` | | +| `GET /file-systems/groups/performance` | Get-PfbFileSystemGroupPerformance | gids, group_names, names | | `high` | | +| `GET /file-systems/locks` | Get-PfbFileLock | allow_errors, client_names, context_names, file_system_ids, file_system_names, inodes, paths | | `high` | | +| `GET /file-systems/locks/clients` | Get-PfbFileLockClient | allow_errors, context_names | | `high` | | +| `GET /file-systems/open-files` | Get-PfbOpenFile | client_names, file_system_ids, file_system_names, paths, protocols, session_names, user_names | | `high` | | +| `GET /file-systems/policies` | Get-PfbFileSystemPolicy | allow_errors, context_names | | `high` | | +| `GET /file-systems/sessions` | Get-PfbFileSystemSession | allow_errors, client_names, context_names, user_names | | `high` | | +| `GET /file-systems/space/storage-classes` | Get-PfbFileSystemStorageClass | storage_class_names | | `high` | | +| `GET /file-systems/user-group-quota-policies` | Get-PfbFileSystemUserGroupQuotaPolicy | allow_errors, context_names | | `high` | | +| `GET /file-systems/users` | Get-PfbFileSystemUser | allow_errors, context_names | | `high` | | +| `GET /file-systems/users/performance` | Get-PfbFileSystemUserPerformance | names, uids, user_names | | `high` | | +| `GET /file-systems/worm-data-policies` | Get-PfbFileSystemWormPolicy | allow_errors, context_names | | `high` | | +| `GET /fleets` | Get-PfbFleet | total_only | | `high` | | +| `GET /fleets/fleet-key` | Get-PfbFleetKey | total_only | | `high` | | +| `GET /fleets/members` | Get-PfbFleetMember | fleet_ids, member_ids, total_only | | `high` | | +| `GET /hardware-connectors/performance` | Get-PfbHardwareConnectorPerformance | ids, total_only | | `high` | | +| `GET /keytabs/download` | Get-PfbKeytabDownload | keytab_ids, keytab_names | | `high` | | +| `GET /legal-holds/held-entities` | Get-PfbLegalHoldEntity | file_system_ids, file_system_names, ids, names, paths | | `high` | | +| `GET /lifecycle-rules` | Get-PfbLifecycleRule | allow_errors, bucket_ids, context_names | | `high` | | +| `GET /log-targets/file-systems` | Get-PfbLogTargetFileSystem | allow_errors, context_names | | `high` | | +| `GET /log-targets/object-store` | Get-PfbLogTargetObjectStore | allow_errors, context_names | | `high` | | +| `GET /management-access-policies` | Get-PfbManagementAccessPolicy | allow_errors, context_names | | `high` | POST/PATCH/DELETE return 403 regardless of account; not an implementation bug | +| `GET /management-access-policies/admins` | Get-PfbManagementAccessPolicyAdmin | allow_errors, context_names, sort | | `high` | POST/PATCH/DELETE return 403 regardless of account; not an implementation bug | +| `GET /management-access-policies/directory-services/roles` | Get-PfbManagementAccessPolicyDirectoryRole | sort | | `high` | POST/PATCH/DELETE return 403 regardless of account; not an implementation bug | +| `GET /management-access-policies/members` | Get-PfbManagementAccessPolicyMember | allow_errors, context_names, sort | | `high` | POST/PATCH/DELETE return 403 regardless of account; not an implementation bug | +| `GET /network-access-policies/rules` | Get-PfbNetworkAccessRule | ids | | `high` | | +| `GET /network-interfaces/connectors/performance` | Get-PfbNetworkInterfaceConnectorPerformance | ids, total_only | | `high` | | +| `GET /network-interfaces/connectors/settings` | Get-PfbNetworkInterfaceConnectorSettings | ids | | `high` | | +| `GET /network-interfaces/neighbors` | Get-PfbNetworkInterfaceNeighbor | local_port_names, total_item_count | | `high` | | +| `GET /network-interfaces/network-connection-statistics` | Get-PfbNetworkConnectionStatistics | current_state, local_host, local_port, remote_host, remote_port | | `high` | | +| `GET /network-interfaces/ping` | Invoke-PfbNetworkPing | component_name, print_latency, resolve_hostname, source | | `high` | | +| `GET /network-interfaces/trace` | Invoke-PfbNetworkTrace | component_name, discover_mtu, fragment_packet, port, resolve_hostname, source | | `high` | | +| `GET /nfs-export-policies` | Get-PfbNfsExportPolicy | allow_errors, context_names, workload_ids, workload_names | | `high` | | +| `GET /nfs-export-policies/rules` | Get-PfbNfsExportRule | allow_errors, context_names, ids | | `high` | | +| `GET /node-groups/nodes` | Get-PfbNodeGroupNode | node_group_ids, node_group_names, node_ids, node_names | | `high` | | +| `GET /node-groups/uses` | Get-PfbNodeGroupUse | ids | | `high` | | +| `GET /nodes` | Get-PfbNode | total_only | | `high` | | +| `GET /object-store-access-keys` | Get-PfbObjectStoreAccessKey | allow_errors, context_names | | `high` | | +| `GET /object-store-access-policies` | Get-PfbObjectStoreAccessPolicy | allow_errors, context_names, exclude_rules | | `high` | | +| `GET /object-store-access-policies/object-store-roles` | Get-PfbObjectStoreAccessPolicyRole | allow_errors, context_names | | `high` | | +| `GET /object-store-access-policies/object-store-users` | Get-PfbObjectStoreAccessPolicyUser | allow_errors, context_names | | `high` | | +| `GET /object-store-access-policies/rules` | Get-PfbObjectStoreAccessPolicyRule | allow_errors, context_names | | `high` | | +| `GET /object-store-access-policy-actions` | Get-PfbObjectStoreAccessPolicyAction | allow_errors, context_names, names | | `high` | | +| `GET /object-store-account-exports` | Get-PfbObjectStoreAccountExport | allow_errors, context_names | | `high` | | +| `GET /object-store-accounts` | Get-PfbObjectStoreAccount | allow_errors, context_names | | `high` | | +| `GET /object-store-remote-credentials` | Get-PfbObjectStoreRemoteCredential | allow_errors, context_names | | `high` | | +| `GET /object-store-roles` | Get-PfbObjectStoreRole | allow_errors, context_names | | `high` | | +| `GET /object-store-roles/object-store-access-policies` | Get-PfbObjectStoreRoleAccessPolicy | allow_errors, context_names, policy_ids, policy_names | | `high` | | +| `GET /object-store-roles/object-store-trust-policies` | Get-PfbObjectStoreTrustPolicy | allow_errors, context_names, names | | `high` | | +| `GET /object-store-roles/object-store-trust-policies/rules` | Get-PfbObjectStoreTrustPolicyRule | allow_errors, context_names, indices, role_ids, role_names | | `high` | | +| `GET /object-store-users` | Get-PfbObjectStoreUser | allow_errors, context_names | | `high` | | +| `GET /object-store-users/object-store-access-policies` | Get-PfbObjectStoreUserAccessPolicy | allow_errors, context_names | | `high` | | +| `GET /object-store-virtual-hosts` | Get-PfbObjectStoreVirtualHost | allow_errors, context_names | | `high` | | +| `GET /password-policies` | Get-PfbPasswordPolicy | ids, names | | `high` | | +| `GET /policies` | Get-PfbPolicy | allow_errors, context_names, workload_ids, workload_names | | `high` | | +| `GET /policies-all` | Get-PfbPolicyAll | allow_errors, context_names | | `high` | | +| `GET /policies-all/members` | Get-PfbPolicyAllMember | allow_errors, context_names, local_file_system_ids, local_file_system_names, remote_file_system_ids, remote_file_system_names, remote_ids, remote_names, sort | | `high` | | +| `GET /policies/file-system-replica-links` | Get-PfbPolicyFileSystemReplicaLink | allow_errors, context_names, local_file_system_ids, local_file_system_names, remote_file_system_ids, remote_file_system_names, remote_ids, remote_names, sort | | `high` | | +| `GET /policies/file-system-snapshots` | Get-PfbPolicyFileSystemSnapshot | allow_errors, context_names | | `high` | | +| `GET /policies/file-systems` | Get-PfbPolicyFileSystem | allow_errors, context_names | | `high` | | +| `GET /presets/workload` | Get-PfbPresetWorkload | context_names | | `high` | | +| `GET /public-keys/uses` | Get-PfbPublicKeyUse | ids | | `high` | | +| `GET /qos-policies` | Get-PfbQosPolicy | allow_errors, context_names | | `high` | | +| `GET /qos-policies/buckets` | Get-PfbQosPolicyBucket | allow_errors, context_names, sort | | `high` | | +| `GET /qos-policies/file-systems` | Get-PfbQosPolicyFileSystem | allow_errors, context_names, sort | | `high` | | +| `GET /qos-policies/members` | Get-PfbQosPolicyMember | allow_errors, context_names, member_types, sort | | `high` | | +| `GET /quotas/groups` | Get-PfbQuotaGroup | allow_errors, context_names, file_system_ids, gids, group_names | | `high` | | +| `GET /quotas/settings` | Get-PfbQuotaSettings | ids, names | | `high` | | +| `GET /quotas/users` | Get-PfbQuotaUser | allow_errors, context_names, file_system_ids, uids, user_names | | `high` | | +| `GET /realms` | Get-PfbRealm | allow_errors, context_names | | `high` | | +| `GET /realms/defaults` | Get-PfbRealmDefaults | allow_errors, context_names, realm_ids, realm_names | | `high` | | +| `GET /realms/space` | Get-PfbRealmSpace | end_time, ids, resolution, start_time, total_only, type | | `high` | | +| `GET /realms/space/storage-classes` | Get-PfbRealmStorageClass | end_time, ids, resolution, start_time, storage_class_names, total_only | | `high` | | +| `GET /remote-arrays` | Get-PfbRemoteArray | total_only | | `high` | | +| `GET /roles` | Get-PfbRole | ids | | `high` | | +| `GET /s3-export-policies` | Get-PfbS3ExportPolicy | allow_errors, context_names | | `high` | | +| `GET /s3-export-policies/rules` | Get-PfbS3ExportRule | allow_errors, context_names | | `high` | | +| `GET /servers` | Get-PfbServer | allow_errors, context_names | | `high` | | +| `GET /smb-client-policies` | Get-PfbSmbClientPolicy | allow_errors, context_names, workload_ids, workload_names | | `high` | | +| `GET /smb-client-policies/rules` | Get-PfbSmbClientRule | allow_errors, context_names, ids | | `high` | | +| `GET /smb-share-policies` | Get-PfbSmbSharePolicy | allow_errors, context_names, workload_ids, workload_names | | `high` | | +| `GET /smb-share-policies/rules` | Get-PfbSmbShareRule | allow_errors, context_names, ids | | `high` | | +| `GET /smtp-servers` | Get-PfbSmtpServer | ids, names | | `high` | | +| `GET /snmp-agents` | Get-PfbSnmpAgent | ids, limit, names, sort | | `high` | | +| `GET /snmp-managers/test` | Test-PfbSnmpManager | filter, limit, sort | | `high` | | +| `GET /software-check` | Get-PfbSoftwareCheck | ids, names, software_names, software_versions, total_item_count | | `high` | | +| `GET /ssh-certificate-authority-policies` | Get-PfbSshCaPolicy | allow_errors, context_names | | `high` | | +| `GET /ssh-certificate-authority-policies/admins` | Get-PfbSshCaPolicyAdmin | allow_errors, context_names, sort | | `high` | | +| `GET /ssh-certificate-authority-policies/arrays` | Get-PfbSshCaPolicyArray | allow_errors, context_names, sort | | `high` | | +| `GET /ssh-certificate-authority-policies/members` | Get-PfbSshCaPolicyMember | allow_errors, context_names, sort | | `high` | | +| `GET /sso/saml2/idps/test` | Test-PfbSaml2Idp | filter, limit, sort | | `high` | | +| `GET /storage-class-tiering-policies/members` | Get-PfbStorageClassTieringPolicyMember | allow_errors, context_names, sort | | `high` | | +| `GET /support` | Get-PfbSupport | ids | | `high` | | +| `GET /support/test` | Test-PfbSupport | filter, sort | | `high` | | +| `GET /syslog-servers` | Get-PfbSyslogServer | allow_errors, context_names | | `high` | | +| `GET /syslog-servers/settings` | Get-PfbSyslogServerSettings | ids, names | | `high` | | +| `GET /targets` | Get-PfbTarget | allow_errors, context_names | | `high` | | +| `GET /targets/performance/replication` | Get-PfbTargetPerformanceReplication | ids, total_only | | `high` | | +| `GET /tls-policies` | Get-PfbTlsPolicy | effective, purity_defined | | `high` | | +| `GET /tls-policies/members` | Get-PfbTlsPolicyMember | sort | | `high` | | +| `GET /usage/groups` | Get-PfbUsageGroup | allow_errors, context_names, file_system_ids, gids, group_names | | `high` | | +| `GET /usage/users` | Get-PfbUsageUser | allow_errors, context_names, file_system_ids, uids, user_names | | `high` | | +| `GET /user-group-quota-policies` | Get-PfbUserGroupQuotaPolicy | allow_errors, context_names, ids, names | | `partial` -- /!\ 2 unresolved params (see Partial-confidence detail below) | | +| `GET /user-group-quota-policies/file-systems` | Get-PfbUserGroupQuotaPolicyFileSystem | allow_errors, context_names | | `high` | | +| `GET /user-group-quota-policies/members` | Get-PfbUserGroupQuotaPolicyMember | allow_errors, context_names | | `high` | | +| `GET /user-group-quota-policies/rules` | Get-PfbUserGroupQuotaPolicyRule | allow_errors, context_names | | `high` | | +| `GET /workloads` | Get-PfbWorkload | allow_errors, context_names | | `high` | | +| `GET /workloads/placement-recommendations` | Get-PfbWorkloadPlacementRecommendation | allow_errors, context_names | | `high` | | +| `GET /workloads/tags` | Get-PfbWorkloadTag | allow_errors, context_names | | `high` | | +| `GET /worm-data-policies` | Get-PfbWormPolicy | allow_errors, context_names | | `high` | | +| `GET /worm-data-policies/members` | Get-PfbWormPolicyMember | allow_errors, context_names, sort | | `high` | | +| `PATCH /admins` | Update-PfbAdmin | context_names | role | `high` | | +| `PATCH /admins/settings` | Update-PfbAdminSetting | | lockout_duration, max_login_attempts, min_password_length | `high` | | +| `PATCH /alert-watchers` | Update-PfbAlertWatcher | | enabled | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `PATCH /alerts` | Update-PfbAlert | | flagged | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `PATCH /api-clients` | Update-PfbApiClient | | max_role | `high` | | +| `PATCH /array-connections` | Update-PfbArrayConnection | context_names | | `high` | | +| `PATCH /arrays` | Update-PfbArray | | banner, default_inbound_tls_policy, eradication_config, idle_timeout, name, network_access_policy, ntp_servers, time_zone | `high` | | +| `PATCH /arrays/erasures` | Update-PfbArrayErasure | delete_sanitization_certificate, eradicate_all_data, finalize | | `high` | | +| `PATCH /arrays/eula` | Update-PfbArrayEula | | signature | `high` | | +| `PATCH /audit-file-systems-policies` | Update-PfbAuditFileSystemPolicy | context_names | add_log_targets, control_type, enabled, location, log_targets, name, remove_log_targets, rules | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `PATCH /audit-object-store-policies` | Update-PfbAuditObjectStorePolicy | context_names | add_log_targets, enabled, location, log_targets, name, remove_log_targets | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `PATCH /buckets` | Remove-PfbBucket, Update-PfbBucket | cancel_in_progress_storage_class_transition, context_names, ignore_usage | destroyed, eradication_config, hard_limit_enabled, object_lock_config, public_access_config, qos_policy, retention_lock, storage_class | `partial` -- /!\ 2 unresolved params (see Partial-confidence detail below) | | +| `PATCH /buckets/audit-filters` | Update-PfbBucketAuditFilter | context_names | | `high` | | +| `PATCH /certificates` | Update-PfbCertificate | | | `high` | | +| `PATCH /data-eviction-policies` | Update-PfbDataEvictionPolicy | context_names | enabled, location | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `PATCH /directory-services` | Update-PfbDirectoryService | ids, names | base_dn, bind_password, bind_user, ca_certificate, ca_certificate_group, enabled, management, nfs, smb, uris | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `PATCH /directory-services/roles` | Update-PfbDirectoryServiceRole | role_ids, role_names | role | `high` | | +| `PATCH /dns` | Update-PfbDns | context_names | | `high` | | +| `PATCH /file-system-exports` | Update-PfbFileSystemExport | context_names | | `high` | | +| `PATCH /file-system-snapshots` | Remove-PfbFileSystemSnapshot | context_names, latest_replica | destroyed, name, owner, policy, source | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `PATCH /file-systems` | Remove-PfbFileSystem, Update-PfbFileSystem | cancel_in_progress_storage_class_transition, context_names, discard_detailed_permissions, ignore_usage | abort_quiesce, default_group_quota, default_user_quota, destroyed, fast_remove_directory_enabled, group_ownership, hard_limit_enabled, http, multi_protocol, name, nfs, qos_policy, quiesce, skip_quiesce, smb, snapshot_directory_enabled, source, storage_class, workload, writable | `partial` -- /!\ 10 unresolved params (see Partial-confidence detail below) | | +| `PATCH /hardware` | Update-PfbHardware | | | `high` | | +| `PATCH /hardware-connectors` | Update-PfbHardwareConnector | | | `high` | | +| `PATCH /kmip` | Update-PfbKmip | | | `high` | | +| `PATCH /legal-holds` | Update-PfbLegalHold | | | `high` | | +| `PATCH /lifecycle-rules` | Update-PfbLifecycleRule | context_names | | `high` | | +| `PATCH /log-targets/file-systems` | Update-PfbLogTargetFileSystem | context_names | | `high` | | +| `PATCH /log-targets/object-store` | Update-PfbLogTargetObjectStore | context_names | | `high` | | +| `PATCH /logs-async` | Update-PfbAsyncLog | | | `high` | | +| `PATCH /management-access-policies` | Update-PfbManagementAccessPolicy | context_names | | `high` | POST/PATCH/DELETE return 403 regardless of account; not an implementation bug | +| `PATCH /network-access-policies` | Update-PfbNetworkAccessPolicy | versions | enabled, location, name, rules | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `PATCH /network-access-policies/rules` | Update-PfbNetworkAccessRule | before_rule_id, before_rule_name, ids, versions | client, effect, index, interfaces, policy | `high` | | +| `PATCH /network-interfaces/connectors` | Update-PfbNetworkInterfaceConnector | | | `high` | | +| `PATCH /nfs-export-policies` | Update-PfbNfsExportPolicy | context_names, versions | enabled, location, name, rules | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `PATCH /nfs-export-policies/rules` | Update-PfbNfsExportRule | before_rule_id, before_rule_name, context_names, ids, versions | access, anongid, anonuid, atime, client, fileid_32bit, index, permission, required_transport_security, secure, security | `high` | | +| `PATCH /nodes` | Update-PfbNode | | | `high` | | +| `PATCH /object-store-access-policies/rules` | Update-PfbObjectStoreAccessPolicyRule | context_names, enforce_action_restrictions, policy_ids, policy_names | actions, conditions, effect, resources | `high` | | +| `PATCH /object-store-account-exports` | Update-PfbObjectStoreAccountExport | context_names | | `high` | | +| `PATCH /object-store-remote-credentials` | Update-PfbObjectStoreRemoteCredential | context_names | | `high` | | +| `PATCH /object-store-roles` | Update-PfbObjectStoreRole | context_names | | `high` | | +| `PATCH /object-store-roles/object-store-trust-policies/rules` | Update-PfbObjectStoreTrustPolicyRule | context_names, indices, policy_names, role_ids, role_names | actions, conditions, policy, principals | `high` | | +| `PATCH /object-store-virtual-hosts` | Update-PfbObjectStoreVirtualHost | context_names | | `high` | | +| `PATCH /password-policies` | Update-PfbPasswordPolicy | ids, names | enabled, enforce_dictionary_check, enforce_username_check, location, lockout_duration, max_login_attempts, max_password_age, min_character_groups, min_characters_per_group, min_password_age, min_password_length, name, password_history | `high` | | +| `PATCH /policies` | Update-PfbPolicy | context_names, destroy_snapshots | add_rules, enabled, location, remove_rules | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `PATCH /presets/workload` | Update-PfbPresetWorkload | context_names | | `high` | | +| `PATCH /qos-policies` | Update-PfbQosPolicy | context_names | | `high` | | +| `PATCH /quotas/groups` | Update-PfbQuotaGroup | context_names, file_system_ids, file_system_names, gids, group_names, names | | `partial` -- /!\ 2 unresolved params (see Partial-confidence detail below) | | +| `PATCH /quotas/settings` | Update-PfbQuotaSettings | | contact, direct_notifications_enabled | `high` | | +| `PATCH /quotas/users` | Update-PfbQuotaUser | context_names, file_system_ids, file_system_names, names, uids, user_names | | `partial` -- /!\ 2 unresolved params (see Partial-confidence detail below) | | +| `PATCH /rapid-data-locking` | Update-PfbRapidDataLocking | | enabled, kmip_server | `high` | | +| `PATCH /realms` | Remove-PfbRealm, Update-PfbRealm | | default_inbound_tls_policy, destroyed, name | `partial` -- /!\ 2 unresolved params (see Partial-confidence detail below) | | +| `PATCH /realms/defaults` | Update-PfbRealmDefaults | context_names | | `high` | | +| `PATCH /s3-export-policies` | Update-PfbS3ExportPolicy | context_names | enabled, name, rules | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `PATCH /s3-export-policies/rules` | Update-PfbS3ExportRule | context_names, policy_ids, policy_names | actions, effect, resources | `high` | | +| `PATCH /smb-client-policies` | Update-PfbSmbClientPolicy | context_names | access_based_enumeration_enabled, enabled, location, name, rules | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `PATCH /smb-client-policies/rules` | Update-PfbSmbClientRule | before_rule_id, before_rule_name, context_names, ids, versions | client, encryption, index, permission, policy | `high` | | +| `PATCH /smb-share-policies` | Update-PfbSmbSharePolicy | context_names | enabled, location, name, rules | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `PATCH /smb-share-policies/rules` | Update-PfbSmbShareRule | context_names, ids, policy_ids, policy_names | change, full_control, policy, principal, read | `high` | | +| `PATCH /snmp-agents` | Update-PfbSnmpAgent | | v2c, v3, version | `high` | | +| `PATCH /snmp-managers` | Update-PfbSnmpManager | | | `high` | | +| `PATCH /ssh-certificate-authority-policies` | Update-PfbSshCaPolicy | | | `high` | | +| `PATCH /sso/oidc/idps` | Update-PfbOidcIdp | | | `high` | | +| `PATCH /sso/saml2/idps` | Update-PfbSaml2Idp | | | `high` | | +| `PATCH /storage-class-tiering-policies` | Update-PfbStorageClassTieringPolicy | | | `high` | | +| `PATCH /subnets` | Update-PfbSubnet | | | `high` | | +| `PATCH /support` | Update-PfbSupport | | edge_agent_update_enabled, edge_management_enabled, phonehome_enabled, proxy, remote_assist_active, remote_assist_duration | `high` | | +| `PATCH /support/verification-keys` | Update-PfbSupportVerificationKey | | signed_verification_key | `high` | | +| `PATCH /syslog-servers/settings` | Update-PfbSyslogServerSettings | ids, names | ca_certificate, ca_certificate_group | `high` | | +| `PATCH /targets` | Update-PfbTarget | | | `high` | | +| `PATCH /tls-policies` | Update-PfbTlsPolicy | | | `high` | | +| `PATCH /user-group-quota-policies` | Update-PfbUserGroupQuotaPolicy | context_names | enabled, name | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `PATCH /user-group-quota-policies/rules` | Update-PfbUserGroupQuotaPolicyRule | context_names | | `high` | | +| `PATCH /workloads` | Update-PfbWorkload | context_names | destroyed | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `PATCH /worm-data-policies` | Update-PfbWormPolicy | context_names | | `high` | | +| `POST /active-directory` | New-PfbActiveDirectory | join_existing_account, names | ca_certificate, ca_certificate_group, computer_name, directory_servers, domain, encryption_types, fqdns, global_catalog_servers, join_ou, kerberos_servers, password, service_principal_names, user | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /admins/api-tokens` | New-PfbApiToken | context_names | | `high` | | +| `POST /admins/management-access-policies` | New-PfbAdminManagementAccessPolicy | context_names | | `high` | POST/PATCH/DELETE return 403 regardless of account; not an implementation bug | +| `POST /admins/ssh-certificate-authority-policies` | New-PfbAdminSshCaPolicy | context_names | | `high` | | +| `POST /api-clients` | New-PfbApiClient | | access_policies, access_token_ttl_in_ms, issuer, max_role, public_key | `high` | | +| `POST /array-connections` | New-PfbArrayConnection | context_names | | `high` | | +| `POST /arrays/erasures` | New-PfbArrayErasure | eradicate_all_data, preserve_configuration_data, skip_phonehome_check | | `high` | | +| `POST /arrays/ssh-certificate-authority-policies` | New-PfbArraySshCaPolicy | context_names | | `high` | | +| `POST /audit-file-systems-policies` | New-PfbAuditFileSystemPolicy | context_names | control_type, enabled, location, log_targets, name, rules | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /audit-file-systems-policies/members` | New-PfbAuditFileSystemPolicyMember | context_names | | `high` | | +| `POST /audit-object-store-policies` | New-PfbAuditObjectStorePolicy | context_names | enabled, location, log_targets, name | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /audit-object-store-policies/members` | New-PfbAuditObjectStorePolicyMember | context_names | | `high` | | +| `POST /buckets` | New-PfbBucket | context_names | bucket_type, eradication_config, hard_limit_enabled, object_lock_config, retention_lock | `high` | | +| `POST /buckets/audit-filters` | New-PfbBucketAuditFilter | bucket_ids, bucket_names, context_names, names | actions, s3_prefixes | `high` | | +| `POST /buckets/bucket-access-policies` | New-PfbBucketAccessPolicy | bucket_ids, bucket_names, context_names | rules | `high` | | +| `POST /buckets/bucket-access-policies/rules` | New-PfbBucketAccessPolicyRule | bucket_ids, bucket_names, context_names, names | actions, principals, resources | `high` | | +| `POST /buckets/cross-origin-resource-sharing-policies` | New-PfbBucketCorsPolicy | bucket_ids, bucket_names, context_names | rules | `high` | | +| `POST /buckets/cross-origin-resource-sharing-policies/rules` | New-PfbBucketCorsPolicyRule | bucket_ids, bucket_names, context_names, names, policy_names | allowed_headers, allowed_methods, allowed_origins | `high` | | +| `POST /certificates` | New-PfbCertificate | | certificate, certificate_type, common_name, country, days, email, intermediate_certificate, key_algorithm, key_size, locality, organization, organizational_unit, passphrase, private_key, state, subject_alternative_names | `high` | | +| `POST /certificates/certificate-signing-requests` | New-PfbCertificateSigningRequest | | certificate, common_name, country, email, locality, organization, organizational_unit, state, subject_alternative_names | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /data-eviction-policies` | New-PfbDataEvictionPolicy | context_names | enabled, location, name | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /data-eviction-policies/file-systems` | Add-PfbDataEvictionPolicyFileSystem | context_names | | `high` | | +| `POST /directory-services/local/directory-services` | New-PfbLocalDirectoryService | context_names | | `high` | | +| `POST /directory-services/local/groups` | New-PfbLocalGroup | context_names, local_directory_service_ids, local_directory_service_names | | `high` | | +| `POST /directory-services/local/groups/members` | New-PfbLocalGroupMember | context_names, group_gids, group_sids, local_directory_service_ids | members | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /directory-services/roles` | New-PfbDirectoryServiceRole | | group, group_base, management_access_policies, role | `high` | | +| `POST /dns` | New-PfbDns | context_names, names | ca_certificate, ca_certificate_group, domain, nameservers, services, sources | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /file-system-exports` | New-PfbFileSystemExport | context_names, member_ids, policy_ids | | `high` | | +| `POST /file-system-replica-links` | New-PfbFileSystemReplicaLink | context_names, ids, local_file_system_ids, remote_ids | direction, link_type, local_file_system, policies, remote, remote_file_system | `high` | | +| `POST /file-system-replica-links/policies` | New-PfbFileSystemReplicaLinkPolicy | context_names | | `high` | | +| `POST /file-system-snapshots` | New-PfbFileSystemSnapshot | context_names, source_ids, source_names | | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /file-systems` | New-PfbFileSystem | context_names, default_exports, discard_non_snapshotted_data, include_snapshot, overwrite, policy_ids, policy_names | fast_remove_directory_enabled, hard_limit_enabled, http, multi_protocol, nfs, node_group, smb, snapshot_directory_enabled, workload, writable | `partial` -- /!\ 15 unresolved params (see Partial-confidence detail below) | | +| `POST /file-systems/audit-policies` | New-PfbFileSystemAuditPolicy | context_names | | `high` | | +| `POST /file-systems/locks/nlm-reclamations` | New-PfbNlmReclamation | context_names | | `high` | | +| `POST /file-systems/policies` | New-PfbFileSystemPolicy | context_names | | `high` | | +| `POST /file-systems/user-group-quota-policies` | New-PfbFileSystemUserGroupQuotaPolicy | context_names | | `high` | | +| `POST /fleets` | New-PfbFleet | names | | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /keytabs` | New-PfbKeytab | name_prefixes | source | `high` | | +| `POST /keytabs/upload` | New-PfbKeytabUpload | name_prefixes | keytab_file | `high` | | +| `POST /legal-holds` | New-PfbLegalHold | | description | `high` | | +| `POST /lifecycle-rules` | New-PfbLifecycleRule | confirm_date, context_names | abort_incomplete_multipart_uploads_after, keep_current_version_for, keep_current_version_until, keep_previous_version_for, prefix, rule_id | `high` | | +| `POST /link-aggregation-groups` | New-PfbLag | names | ports | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /log-targets/file-systems` | New-PfbLogTargetFileSystem | context_names | file_system, keep_for, keep_size, name | `high` | | +| `POST /log-targets/object-store` | New-PfbLogTargetObjectStore | context_names | bucket, log_name_prefix, log_rotate, name | `high` | | +| `POST /maintenance-windows` | New-PfbMaintenanceWindow | names | timeout | `high` | | +| `POST /management-access-policies` | New-PfbManagementAccessPolicy | context_names | aggregation_strategy, enabled, location, name, rules | `high` | POST/PATCH/DELETE return 403 regardless of account; not an implementation bug | +| `POST /management-access-policies/admins` | New-PfbManagementAccessPolicyAdmin | context_names | | `high` | POST/PATCH/DELETE return 403 regardless of account; not an implementation bug | +| `POST /network-access-policies/rules` | New-PfbNetworkAccessRule | | | `high` | | +| `POST /network-interfaces` | New-PfbNetworkInterface | | rdma_enabled | `high` | | +| `POST /nfs-export-policies` | New-PfbNfsExportPolicy | context_names | enabled, location, name, rules | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /nfs-export-policies/rules` | New-PfbNfsExportRule | context_names | | `high` | | +| `POST /node-groups` | New-PfbNodeGroup | names | | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /object-store-access-keys` | New-PfbObjectStoreAccessKey | context_names, names | secret_access_key | `high` | | +| `POST /object-store-access-policies` | New-PfbObjectStoreAccessPolicy | context_names, enforce_action_restrictions | description, rules | `high` | | +| `POST /object-store-access-policies/object-store-roles` | New-PfbObjectStoreAccessPolicyRole | context_names, member_ids, policy_ids | | `high` | | +| `POST /object-store-access-policies/object-store-users` | New-PfbObjectStoreAccessPolicyUser | context_names, member_ids, policy_ids | | `high` | | +| `POST /object-store-access-policies/rules` | New-PfbObjectStoreAccessPolicyRule | context_names, enforce_action_restrictions, names, policy_ids | actions, conditions, effect, resources | `high` | | +| `POST /object-store-account-exports` | New-PfbObjectStoreAccountExport | context_names, member_ids, member_names, policy_ids, policy_names | export_enabled, server | `high` | | +| `POST /object-store-accounts` | New-PfbObjectStoreAccount | context_names | account_exports, bucket_defaults, hard_limit_enabled, quota_limit | `high` | | +| `POST /object-store-remote-credentials` | New-PfbObjectStoreRemoteCredential | context_names | access_key_id, secret_access_key | `high` | | +| `POST /object-store-roles` | New-PfbObjectStoreRole | context_names | max_session_duration | `high` | | +| `POST /object-store-roles/object-store-access-policies` | New-PfbObjectStoreRoleAccessPolicy | context_names, member_ids, policy_ids, policy_names | | `high` | | +| `POST /object-store-roles/object-store-trust-policies/rules` | New-PfbObjectStoreTrustPolicyRule | context_names, names, role_ids, role_names | actions, conditions, policy, principals | `high` | | +| `POST /object-store-users` | New-PfbObjectStoreUser | context_names, full_access | | `high` | | +| `POST /object-store-users/object-store-access-policies` | New-PfbObjectStoreUserAccessPolicy | context_names, member_ids, policy_ids | | `high` | | +| `POST /object-store-virtual-hosts` | New-PfbObjectStoreVirtualHost | context_names | attached_servers | `high` | | +| `POST /policies` | New-PfbPolicy | context_names | enabled, location, name | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /policies/file-system-replica-links` | New-PfbPolicyFileSystemReplicaLink | context_names | | `high` | | +| `POST /policies/file-systems` | New-PfbPolicyFileSystem | context_names | | `high` | | +| `POST /presets/workload` | New-PfbPresetWorkload | context_names | description, directory_configurations, export_configurations, parameters, periodic_replication_configurations, placement_configurations, platform_features, qos_configurations, quota_configurations, snapshot_configurations, volume_configurations, workload_tags, workload_type | `high` | | +| `POST /public-keys` | New-PfbPublicKey | | public_key | `high` | | +| `POST /qos-policies` | New-PfbQosPolicy | context_names | enabled, location, max_total_bytes_per_sec, max_total_ops_per_sec, name | `high` | | +| `POST /qos-policies/members` | New-PfbQosPolicyMember | context_names | | `high` | | +| `POST /quotas/groups` | New-PfbQuotaGroup | context_names, file_system_ids, file_system_names, gids, group_names | | `high` | | +| `POST /quotas/users` | New-PfbQuotaUser | context_names, file_system_ids, file_system_names, uids, user_names | | `partial` -- /!\ 3 unresolved params (see Partial-confidence detail below) | | +| `POST /realms` | New-PfbRealm | without_default_access_list | | `high` | | +| `POST /s3-export-policies` | New-PfbS3ExportPolicy | context_names | enabled, rules | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /s3-export-policies/rules` | New-PfbS3ExportRule | context_names | | `high` | | +| `POST /servers` | New-PfbServer | create_ds, create_local_directory_service | | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /smb-client-policies` | New-PfbSmbClientPolicy | context_names | access_based_enumeration_enabled, enabled, location, name, rules | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /smb-client-policies/rules` | New-PfbSmbClientRule | context_names | | `high` | | +| `POST /smb-share-policies` | New-PfbSmbSharePolicy | context_names | enabled, location, name, rules | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /smb-share-policies/rules` | New-PfbSmbShareRule | context_names | | `high` | | +| `POST /snmp-managers` | New-PfbSnmpManager | names | host, notification, v2c, v3, version | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /software-check` | New-PfbSoftwareCheck | software_names, software_versions | | `high` | | +| `POST /ssh-certificate-authority-policies` | New-PfbSshCaPolicy | names | enabled, location, name, signing_authority, static_authorized_principals | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /ssh-certificate-authority-policies/admins` | New-PfbSshCaPolicyAdmin | context_names | | `high` | | +| `POST /ssh-certificate-authority-policies/arrays` | New-PfbSshCaPolicyArray | context_names | | `high` | | +| `POST /sso/oidc/idps` | New-PfbOidcIdp | | enabled, idp, services | `high` | | +| `POST /sso/saml2/idps` | New-PfbSaml2Idp | | array_url, binding, enabled, idp, management, services, sp | `high` | | +| `POST /storage-class-tiering-policies` | New-PfbStorageClassTieringPolicy | names | archival_rules, enabled, location, name, retrieval_rules | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /subnets` | New-PfbSubnet | | | `high` | | +| `POST /support-diagnostics` | New-PfbSupportDiagnostics | analysis_period_end_time, analysis_period_start_time | | `high` | | +| `POST /syslog-servers` | New-PfbSyslogServer | names | services, sources, uri | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /targets` | New-PfbTarget | names | address | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /tls-policies` | New-PfbTlsPolicy | names | appliance_certificate, client_certificates_required, disabled_tls_ciphers, enabled, enabled_tls_ciphers, location, min_tls_version, name, trusted_client_certificate_authority, verify_client_certificate_trust | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /user-group-quota-policies` | New-PfbUserGroupQuotaPolicy | context_names | enabled, name | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /user-group-quota-policies/file-systems` | New-PfbUserGroupQuotaPolicyFileSystem | context_names | | `high` | | +| `POST /user-group-quota-policies/rules` | New-PfbUserGroupQuotaPolicyRule | context_names | enforced | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /workloads` | New-PfbWorkload | context_names | | `high` | | +| `POST /workloads/placement-recommendations` | New-PfbWorkloadPlacementRecommendation | context_names | additional_constraints, parameters, preset, projection_months, recommendation_engine, results_limit | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `POST /worm-data-policies` | New-PfbWormPolicy | context_names, names | default_retention, enabled, location, max_retention, min_retention, mode, retention_lock | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | +| `PUT /presets/workload` | Set-PfbPresetWorkload | context_names | description, directory_configurations, export_configurations, name, parameters, periodic_replication_configurations, placement_configurations, platform_features, qos_configurations, quota_configurations, snapshot_configurations, volume_configurations, workload_tags, workload_type | `high` | | +| `PUT /workloads/tags/batch` | Set-PfbWorkloadTag | context_names | | `partial` -- /!\ 1 unresolved param (see Partial-confidence detail below) | | + +### Partial-confidence detail + +Per the decision-6 procedure above: open each parameter at its `file:line` and follow where its value goes. + +| Endpoint | Parameter | Surface | File:Line | Caveat | +|---|---|---|---|---| +| `DELETE /buckets` | `-Eradicate` | TypedUnresolved | `Public/Bucket/Remove-PfbBucket.ps1:27` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `DELETE /file-system-snapshots` | `-Eradicate` | TypedUnresolved | `Public/FileSystemSnapshot/Remove-PfbFileSystemSnapshot.ps1:24` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `DELETE /file-systems` | `-Eradicate` | TypedUnresolved | `Public/FileSystem/Remove-PfbFileSystem.ps1:38` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `DELETE /file-systems/sessions` | `-Force` | TypedUnresolved | `Public/FileSystem/Remove-PfbFileSystemSession.ps1:59` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `DELETE /quotas/groups` | `-FileSystemName` | TypedUnresolved | `Public/Quota/Remove-PfbQuotaGroup.ps1:30` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `DELETE /quotas/groups` | `-GroupName` | TypedUnresolved | `Public/Quota/Remove-PfbQuotaGroup.ps1:31` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `DELETE /quotas/users` | `-FileSystemName` | TypedUnresolved | `Public/Quota/Remove-PfbQuotaUser.ps1:25` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `DELETE /quotas/users` | `-UserName` | TypedUnresolved | `Public/Quota/Remove-PfbQuotaUser.ps1:26` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `DELETE /servers` | `-Eradicate` | TypedUnresolved | `Public/Server/Remove-PfbServer.ps1:35` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `GET /arrays` | `-Endpoint` | TypedUnresolved | `Public/Connection/Test-PfbConnection.ps1:31` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `GET /user-group-quota-policies` | `-Id` | TypedUnresolved | `Public/Policy/Get-PfbUserGroupQuotaPolicy.ps1:38` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `GET /user-group-quota-policies` | `-Name` | TypedUnresolved | `Public/Policy/Get-PfbUserGroupQuotaPolicy.ps1:35` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `PATCH /alert-watchers` | `-Enabled` | AttributesOnly | `Public/Alert/Update-PfbAlertWatcher.ps1:32` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `PATCH /alerts` | `-Flagged` | AttributesOnly | `Public/Alert/Update-PfbAlert.ps1:26` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `PATCH /audit-file-systems-policies` | `-Enabled` | AttributesOnly | `Public/Policy/Update-PfbAuditFileSystemPolicy.ps1:40` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `PATCH /audit-object-store-policies` | `-Enabled` | AttributesOnly | `Public/Policy/Update-PfbAuditObjectStorePolicy.ps1:40` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `PATCH /buckets` | `-Destroyed` | AttributesOnly | `Public/Bucket/Update-PfbBucket.ps1:37` | body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability | +| `PATCH /buckets` | `-Eradicate` | TypedUnresolved | `Public/Bucket/Remove-PfbBucket.ps1:27` | body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability | +| `PATCH /data-eviction-policies` | `-Enabled` | TypedUnresolved | `Public/DataEviction/Update-PfbDataEvictionPolicy.ps1:37` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `PATCH /directory-services` | `-Name` | AttributesOnly | `Public/DirectoryService/Update-PfbDirectoryService.ps1:32` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `PATCH /file-system-snapshots` | `-Eradicate` | TypedUnresolved | `Public/FileSystemSnapshot/Remove-PfbFileSystemSnapshot.ps1:24` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `PATCH /file-systems` | `-Destroyed` | AttributesOnly | `Public/FileSystem/Update-PfbFileSystem.ps1:93` | body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability | +| `PATCH /file-systems` | `-Eradicate` | TypedUnresolved | `Public/FileSystem/Remove-PfbFileSystem.ps1:38` | body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability | +| `PATCH /file-systems` | `-HardLimitEnabled` | AttributesOnly | `Public/FileSystem/Update-PfbFileSystem.ps1:69` | body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability | +| `PATCH /file-systems` | `-HttpEnabled` | AttributesOnly | `Public/FileSystem/Update-PfbFileSystem.ps1:90` | body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability | +| `PATCH /file-systems` | `-NfsEnabled` | AttributesOnly | `Public/FileSystem/Update-PfbFileSystem.ps1:72` | body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability | +| `PATCH /file-systems` | `-NfsExportPolicy` | AttributesOnly | `Public/FileSystem/Update-PfbFileSystem.ps1:78` | body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability | +| `PATCH /file-systems` | `-NfsRules` | AttributesOnly | `Public/FileSystem/Update-PfbFileSystem.ps1:75` | body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability | +| `PATCH /file-systems` | `-SmbClientPolicy` | AttributesOnly | `Public/FileSystem/Update-PfbFileSystem.ps1:87` | body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability | +| `PATCH /file-systems` | `-SmbEnabled` | AttributesOnly | `Public/FileSystem/Update-PfbFileSystem.ps1:81` | body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability | +| `PATCH /file-systems` | `-SmbSharePolicy` | AttributesOnly | `Public/FileSystem/Update-PfbFileSystem.ps1:84` | body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability | +| `PATCH /network-access-policies` | `-Enabled` | AttributesOnly | `Public/Policy/Update-PfbNetworkAccessPolicy.ps1:42` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `PATCH /nfs-export-policies` | `-Enabled` | AttributesOnly | `Public/Policy/Update-PfbNfsExportPolicy.ps1:42` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `PATCH /policies` | `-Enabled` | AttributesOnly | `Public/Policy/Update-PfbPolicy.ps1:28` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `PATCH /quotas/groups` | `-FileSystemName` | AttributesOnly | `Public/Quota/Update-PfbQuotaGroup.ps1:35` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `PATCH /quotas/groups` | `-GroupName` | AttributesOnly | `Public/Quota/Update-PfbQuotaGroup.ps1:36` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `PATCH /quotas/users` | `-FileSystemName` | AttributesOnly | `Public/Quota/Update-PfbQuotaUser.ps1:30` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `PATCH /quotas/users` | `-UserName` | AttributesOnly | `Public/Quota/Update-PfbQuotaUser.ps1:31` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `PATCH /realms` | `-Destroyed` | AttributesOnly | `Public/Realm/Update-PfbRealm.ps1:31` | body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability | +| `PATCH /realms` | `-Eradicate` | TypedUnresolved | `Public/Realm/Remove-PfbRealm.ps1:32` | body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability | +| `PATCH /s3-export-policies` | `-Enabled` | AttributesOnly | `Public/Policy/Update-PfbS3ExportPolicy.ps1:42` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `PATCH /smb-client-policies` | `-Enabled` | AttributesOnly | `Public/Policy/Update-PfbSmbClientPolicy.ps1:42` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `PATCH /smb-share-policies` | `-Enabled` | AttributesOnly | `Public/Policy/Update-PfbSmbSharePolicy.ps1:42` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `PATCH /user-group-quota-policies` | `-Enabled` | AttributesOnly | `Public/Policy/Update-PfbUserGroupQuotaPolicy.ps1:36` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `PATCH /workloads` | `-Destroyed` | TypedUnresolved | `Public/Workloads/Update-PfbWorkload.ps1:36` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `POST /active-directory` | `-Name` | AttributesOnly | `Public/DirectoryService/New-PfbActiveDirectory.ps1:40` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /audit-file-systems-policies` | `-Enabled` | AttributesOnly | `Public/Policy/New-PfbAuditFileSystemPolicy.ps1:35` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /audit-object-store-policies` | `-Enabled` | AttributesOnly | `Public/Policy/New-PfbAuditObjectStorePolicy.ps1:35` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /certificates/certificate-signing-requests` | `-Name` | AttributesOnly | `Public/Certificate/New-PfbCertificateSigningRequest.ps1:29` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /data-eviction-policies` | `-Disabled` | TypedUnresolved | `Public/DataEviction/New-PfbDataEvictionPolicy.ps1:31` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `POST /directory-services/local/groups/members` | `-Member` | TypedUnresolved | `Public/DirectoryService/New-PfbLocalGroupMember.ps1:35` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `POST /dns` | `-Name` | AttributesOnly | `Public/Network/New-PfbDns.ps1:29` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /file-system-snapshots` | `-SourceName` | TypedUnresolved | `Public/FileSystemSnapshot/New-PfbFileSystemSnapshot.ps1:36` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `POST /file-systems` | `-FastRemoveDirectoryEnabled` | AttributesOnly | `Public/FileSystem/New-PfbFileSystem.ps1:139` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /file-systems` | `-HardLimit` | AttributesOnly | `Public/FileSystem/New-PfbFileSystem.ps1:93` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /file-systems` | `-Http` | AttributesOnly | `Public/FileSystem/New-PfbFileSystem.ps1:126` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /file-systems` | `-MultiProtocolAccessControlStyle` | AttributesOnly | `Public/FileSystem/New-PfbFileSystem.ps1:129` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /file-systems` | `-Nfs` | AttributesOnly | `Public/FileSystem/New-PfbFileSystem.ps1:102` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /file-systems` | `-NfsExportPolicy` | AttributesOnly | `Public/FileSystem/New-PfbFileSystem.ps1:114` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /file-systems` | `-NfsRules` | AttributesOnly | `Public/FileSystem/New-PfbFileSystem.ps1:111` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /file-systems` | `-NfsV3` | AttributesOnly | `Public/FileSystem/New-PfbFileSystem.ps1:105` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /file-systems` | `-NfsV41` | AttributesOnly | `Public/FileSystem/New-PfbFileSystem.ps1:108` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /file-systems` | `-SafeguardAcls` | AttributesOnly | `Public/FileSystem/New-PfbFileSystem.ps1:133` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /file-systems` | `-Smb` | AttributesOnly | `Public/FileSystem/New-PfbFileSystem.ps1:117` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /file-systems` | `-SmbClientPolicy` | AttributesOnly | `Public/FileSystem/New-PfbFileSystem.ps1:123` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /file-systems` | `-SmbSharePolicy` | AttributesOnly | `Public/FileSystem/New-PfbFileSystem.ps1:120` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /file-systems` | `-SnapshotDirectoryEnabled` | AttributesOnly | `Public/FileSystem/New-PfbFileSystem.ps1:136` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /file-systems` | `-Writable` | AttributesOnly | `Public/FileSystem/New-PfbFileSystem.ps1:150` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /fleets` | `-Name` | AttributesOnly | `Public/Replication/New-PfbFleet.ps1:30` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /link-aggregation-groups` | `-Name` | AttributesOnly | `Public/Misc/New-PfbLag.ps1:29` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /nfs-export-policies` | `-Enabled` | AttributesOnly | `Public/Policy/New-PfbNfsExportPolicy.ps1:36` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /node-groups` | `-Name` | AttributesOnly | `Public/Node/New-PfbNodeGroup.ps1:30` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /policies` | `-Enabled` | AttributesOnly | `Public/Policy/New-PfbPolicy.ps1:23` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /quotas/users` | `-FileSystemName` | AttributesOnly | `Public/Quota/New-PfbQuotaUser.ps1:42` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /quotas/users` | `-UserId` | AttributesOnly | `Public/Quota/New-PfbQuotaUser.ps1:44` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /quotas/users` | `-UserName` | AttributesOnly | `Public/Quota/New-PfbQuotaUser.ps1:43` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /s3-export-policies` | `-Enabled` | AttributesOnly | `Public/Policy/New-PfbS3ExportPolicy.ps1:36` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /servers` | `-CreateDirectoryService` | AttributesOnly | `Public/Server/New-PfbServer.ps1:40` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /smb-client-policies` | `-Enabled` | AttributesOnly | `Public/Policy/New-PfbSmbClientPolicy.ps1:36` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /smb-share-policies` | `-Enabled` | AttributesOnly | `Public/Policy/New-PfbSmbSharePolicy.ps1:36` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /snmp-managers` | `-Name` | AttributesOnly | `Public/Monitoring/New-PfbSnmpManager.ps1:33` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /ssh-certificate-authority-policies` | `-Name` | AttributesOnly | `Public/Policy/New-PfbSshCaPolicy.ps1:30` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /storage-class-tiering-policies` | `-Name` | AttributesOnly | `Public/Policy/New-PfbStorageClassTieringPolicy.ps1:30` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /syslog-servers` | `-Name` | AttributesOnly | `Public/Monitoring/New-PfbSyslogServer.ps1:32` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /targets` | `-Name` | AttributesOnly | `Public/Replication/New-PfbTarget.ps1:31` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /tls-policies` | `-Name` | AttributesOnly | `Public/Policy/New-PfbTlsPolicy.ps1:29` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /user-group-quota-policies` | `-Enabled` | AttributesOnly | `Public/Policy/New-PfbUserGroupQuotaPolicy.ps1:49` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /user-group-quota-policies/rules` | `-Enforced` | AttributesOnly | `Public/Policy/New-PfbUserGroupQuotaPolicyRule.ps1:57` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `POST /workloads/placement-recommendations` | `-Inputs` | TypedUnresolved | `Public/Workloads/New-PfbWorkloadPlacementRecommendation.ps1:29` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | +| `POST /worm-data-policies` | `-Name` | AttributesOnly | `Public/Policy/New-PfbWormPolicy.ps1:30` | body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability | +| `PUT /workloads/tags/batch` | `-Tags` | TypedUnresolved | `Public/Workloads/Set-PfbWorkloadTag.ps1:30` | one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability | + +## Read-only fields (not addressable) + +The capability map knows these body properties exist, but the newest analysed spec marks them read-only -- no `Public/` cmdlet can ever set them, on any confidence level. Listed for completeness only; never merged into the Parameter gaps table above. + +| Endpoint | Cmdlets | Read-only fields | +|---|---|---| +| `PATCH /alert-watchers` | Update-PfbAlertWatcher | id, name | +| `PATCH /alerts` | Update-PfbAlert | action, code, component_name, component_type, created, description, duration, id, index, knowledge_base_url, name, notified, severity, state, summary, updated, variables | +| `PATCH /api-clients` | Update-PfbApiClient | access_policies, access_token_ttl_in_ms, id, issuer, key_id, name, public_key | +| `PATCH /array-connections` | Update-PfbArrayConnection | context, id, os, status, type, version | +| `PATCH /arrays` | Update-PfbArray | _as_of, context, encryption, id, os, product_type, revision, security_update, smb_mode, version | +| `PATCH /arrays/eula` | Update-PfbArrayEula | agreement | +| `PATCH /audit-file-systems-policies` | Update-PfbAuditFileSystemPolicy | id, is_local, policy_type, realms | +| `PATCH /audit-object-store-policies` | Update-PfbAuditObjectStorePolicy | id, is_local, policy_type, realms | +| `PATCH /certificates` | Update-PfbCertificate | issued_by, issued_to, realms, status, valid_from, valid_to | +| `PATCH /data-eviction-policies` | Update-PfbDataEvictionPolicy | context, id, is_local, policy_type, realms | +| `PATCH /directory-services` | Update-PfbDirectoryService | id, name, services | +| `PATCH /directory-services/roles` | Update-PfbDirectoryServiceRole | id, management_access_policies, name | +| `PATCH /dns` | Update-PfbDns | context, id, realms | +| `PATCH /file-system-exports` | Update-PfbFileSystemExport | context, enabled, id, name, policy_type, status | +| `PATCH /file-system-snapshots` | Remove-PfbFileSystemSnapshot | context, created, id, owner_destroyed, policies, suffix, time_remaining | +| `PATCH /file-systems` | Remove-PfbFileSystem, Update-PfbFileSystem | created, id, promotion_status, time_remaining | +| `PATCH /hardware` | Update-PfbHardware | data_mac, details, id, index, management_mac, model, name, part_number, sensor_readings, serial, slot, speed, status, temperature, type | +| `PATCH /hardware-connectors` | Update-PfbHardwareConnector | connector_type, id, name, transceiver_type | +| `PATCH /kmip` | Update-PfbKmip | id, name | +| `PATCH /legal-holds` | Update-PfbLegalHold | id, name, realms | +| `PATCH /log-targets/file-systems` | Update-PfbLogTargetFileSystem | id | +| `PATCH /log-targets/object-store` | Update-PfbLogTargetObjectStore | id | +| `PATCH /logs-async` | Update-PfbAsyncLog | available_files, id, last_request_time, name, processing, progress | +| `PATCH /management-access-policies` | Update-PfbManagementAccessPolicy | context, id, is_local, policy_type, realms, version | +| `PATCH /network-access-policies` | Update-PfbNetworkAccessPolicy | id, is_local, policy_type, realms, version | +| `PATCH /network-access-policies/rules` | Update-PfbNetworkAccessRule | id, name, policy_version | +| `PATCH /network-interfaces/connectors` | Update-PfbNetworkInterfaceConnector | connector_type, id, name, transceiver_type | +| `PATCH /nfs-export-policies` | Update-PfbNfsExportPolicy | id, is_local, policy_type, realms, version | +| `PATCH /nfs-export-policies/rules` | Update-PfbNfsExportRule | id, name | +| `PATCH /nodes` | Update-PfbNode | capacity, chassis_serial_number, data_addresses, details, id, raw_capacity, status, unique | +| `PATCH /object-store-remote-credentials` | Update-PfbObjectStoreRemoteCredential | context, id, realms | +| `PATCH /object-store-roles` | Update-PfbObjectStoreRole | context, created, id, name, prn, trusted_entities | +| `PATCH /object-store-roles/object-store-trust-policies/rules` | Update-PfbObjectStoreTrustPolicyRule | effect | +| `PATCH /object-store-virtual-hosts` | Update-PfbObjectStoreVirtualHost | id | +| `PATCH /password-policies` | Update-PfbPasswordPolicy | id, is_local, policy_type, realms | +| `PATCH /policies` | Update-PfbPolicy | id, is_local, name, policy_type, realms, retention_lock | +| `PATCH /qos-policies` | Update-PfbQosPolicy | context, id, is_local, policy_type, realms | +| `PATCH /quotas/groups` | Update-PfbQuotaGroup | name | +| `PATCH /quotas/settings` | Update-PfbQuotaSettings | id, name | +| `PATCH /quotas/users` | Update-PfbQuotaUser | name | +| `PATCH /realms` | Remove-PfbRealm, Update-PfbRealm | id | +| `PATCH /realms/defaults` | Update-PfbRealmDefaults | context, realm | +| `PATCH /smb-client-policies` | Update-PfbSmbClientPolicy | id, is_local, policy_type, realms, version | +| `PATCH /smb-client-policies/rules` | Update-PfbSmbClientRule | context, id, name, policy_version | +| `PATCH /smb-share-policies` | Update-PfbSmbSharePolicy | id, is_local, policy_type, realms | +| `PATCH /smb-share-policies/rules` | Update-PfbSmbShareRule | id, name | +| `PATCH /snmp-agents` | Update-PfbSnmpAgent | engine_id, id, name | +| `PATCH /snmp-managers` | Update-PfbSnmpManager | id | +| `PATCH /ssh-certificate-authority-policies` | Update-PfbSshCaPolicy | context, id, is_local, policy_type, realms | +| `PATCH /sso/oidc/idps` | Update-PfbOidcIdp | prn | +| `PATCH /sso/saml2/idps` | Update-PfbSaml2Idp | id, prn | +| `PATCH /storage-class-tiering-policies` | Update-PfbStorageClassTieringPolicy | id, is_local, policy_type, realms | +| `PATCH /subnets` | Update-PfbSubnet | enabled, id, interfaces, name, services | +| `PATCH /support` | Update-PfbSupport | id, name, remote_assist_expires, remote_assist_opened, remote_assist_paths, remote_assist_status | +| `PATCH /syslog-servers/settings` | Update-PfbSyslogServerSettings | id, name | +| `PATCH /targets` | Update-PfbTarget | id, status, status_details | +| `PATCH /tls-policies` | Update-PfbTlsPolicy | id, is_local, policy_type, realms | +| `PATCH /user-group-quota-policies` | Update-PfbUserGroupQuotaPolicy | context, id, is_local, policy_type, realms, version | +| `PATCH /user-group-quota-policies/rules` | Update-PfbUserGroupQuotaPolicyRule | id, name | +| `PATCH /worm-data-policies` | Update-PfbWormPolicy | context, id, is_local, name, policy_type, realms | +| `POST /array-connections` | New-PfbArrayConnection | context, id, os, status, type, version | +| `POST /audit-file-systems-policies` | New-PfbAuditFileSystemPolicy | id, is_local, policy_type, realms | +| `POST /audit-object-store-policies` | New-PfbAuditObjectStorePolicy | id, is_local, policy_type, realms | +| `POST /buckets/bucket-access-policies/rules` | New-PfbBucketAccessPolicyRule | effect | +| `POST /certificates` | New-PfbCertificate | issued_by, issued_to, realms, status, valid_from, valid_to | +| `POST /data-eviction-policies` | New-PfbDataEvictionPolicy | id, is_local, policy_type, realms | +| `POST /file-system-replica-links` | New-PfbFileSystemReplicaLink | context, id, lag, recovery_point, status, status_details | +| `POST /file-systems` | New-PfbFileSystem | requested_promotion_state | +| `POST /legal-holds` | New-PfbLegalHold | id, name, realms | +| `POST /link-aggregation-groups` | New-PfbLag | id, lag_speed, mac_address, name, port_speed, status | +| `POST /log-targets/file-systems` | New-PfbLogTargetFileSystem | id | +| `POST /log-targets/object-store` | New-PfbLogTargetObjectStore | id | +| `POST /management-access-policies` | New-PfbManagementAccessPolicy | id, is_local, policy_type, realms | +| `POST /network-access-policies/rules` | New-PfbNetworkAccessRule | id, name | +| `POST /network-interfaces` | New-PfbNetworkInterface | id, name, realms | +| `POST /nfs-export-policies` | New-PfbNfsExportPolicy | id, is_local, policy_type, realms | +| `POST /nfs-export-policies/rules` | New-PfbNfsExportRule | context, id, name, policy_version | +| `POST /object-store-roles/object-store-trust-policies/rules` | New-PfbObjectStoreTrustPolicyRule | effect | +| `POST /object-store-virtual-hosts` | New-PfbObjectStoreVirtualHost | context, id, name, realms | +| `POST /policies` | New-PfbPolicy | id, is_local, policy_type, realms, retention_lock | +| `POST /presets/workload` | New-PfbPresetWorkload | revision | +| `POST /qos-policies` | New-PfbQosPolicy | context, id, is_local, policy_type, realms | +| `POST /quotas/groups` | New-PfbQuotaGroup | name | +| `POST /quotas/users` | New-PfbQuotaUser | name | +| `POST /smb-client-policies` | New-PfbSmbClientPolicy | id, is_local, policy_type, realms | +| `POST /smb-client-policies/rules` | New-PfbSmbClientRule | id, name | +| `POST /smb-share-policies` | New-PfbSmbSharePolicy | id, is_local, policy_type, realms | +| `POST /smb-share-policies/rules` | New-PfbSmbShareRule | id, name | +| `POST /ssh-certificate-authority-policies` | New-PfbSshCaPolicy | id, is_local, policy_type, realms | +| `POST /sso/oidc/idps` | New-PfbOidcIdp | prn | +| `POST /sso/saml2/idps` | New-PfbSaml2Idp | prn | +| `POST /storage-class-tiering-policies` | New-PfbStorageClassTieringPolicy | id, is_local, policy_type, realms | +| `POST /subnets` | New-PfbSubnet | enabled, id, interfaces, name, services | +| `POST /tls-policies` | New-PfbTlsPolicy | id, is_local, policy_type, realms | +| `POST /user-group-quota-policies` | New-PfbUserGroupQuotaPolicy | id, is_local, policy_type, realms | +| `POST /workloads/placement-recommendations` | New-PfbWorkloadPlacementRecommendation | context, created, expires, id, more_results_available, name, progress, results, status | +| `POST /worm-data-policies` | New-PfbWormPolicy | context, id, is_local, name, policy_type, realms | +| `PUT /presets/workload` | Set-PfbPresetWorkload | context, id, revision | + +## Response-shape drift + +Cmdlets pass API responses through raw -- there is no projection anywhere in `Public/`, so the module holds no schema of its own. A **removed or renamed** response field therefore reaches user scripts as a silent `$null`, which no other report category can detect. **Added** response fields are deliberately not reported: they surface automatically and need no module change. + +### Removed response fields + +| Endpoint | Location | Field | Introduced | Last seen | +|---|---|---|---|---| +| `GET /arrays/performance/replication` | items | `remote` | 2.0 | 2.10 | +| `GET /file-system-snapshots` | items | `copyable` | 2.0 | 2.10 | +| `GET /network-interfaces` | items | `server` | 2.16 | 2.19 | +| `PATCH /file-system-snapshots` | items | `copyable` | 2.0 | 2.10 | +| `PATCH /network-interfaces` | items | `server` | 2.16 | 2.19 | +| `POST /file-system-snapshots` | items | `copyable` | 2.0 | 2.10 | +| `POST /network-interfaces` | items | `server` | 2.16 | 2.19 | + +### Rename candidates + +_A removal and an addition adjacent in version on the same endpoint. Suggestive, not proof -- confirm against the spec before acting._ + +| Endpoint | Location | From | To | Version | +|---|---|---|---|---| +| `GET /network-interfaces` | items | `server` | `attached_servers` | 2.20 | +| `PATCH /network-interfaces` | items | `server` | `attached_servers` | 2.20 | +| `POST /network-interfaces` | items | `server` | `attached_servers` | 2.20 | + +### Response envelope fields not read by `Invoke-PfbApiRequest` + +_Informational coverage observation, not a correctness claim -- many envelope keys legitimately need no handling there. It cannot tell whether a field is handled correctly, only whether it is referenced at all._ + +| Envelope field | Endpoints declaring it | +|---|---| +| `errors` | 140 | +| `total` | 28 | +| `context` | 7 | +| `access_token` | 1 | +| `expires_in` | 1 | +| `issued_token_type` | 1 | +| `login_banner` | 1 | +| `more_items_remaining` | 1 | +| `token_type` | 1 | +| `username` | 1 | +| `versions` | 1 | + +## Uncovered endpoints + +| Endpoint | Introduced in | +|---|---| +| `GET /api/login-banner` | 2.0 | +| `POST /bucket-replica-links` | 2.0 | +| `PATCH /bucket-replica-links` | 2.0 | +| `DELETE /bucket-replica-links` | 2.0 | +| `POST /certificate-groups/certificates` | 2.0 | +| `DELETE /certificate-groups/certificates` | 2.0 | +| `PATCH /directory-services/test` | 2.0 | +| `GET /file-systems/performance` | 2.0 | +| `PATCH /object-store-access-keys` | 2.0 | +| `DELETE /policies/file-system-snapshots` | 2.0 | +| `GET /policies/members` | 2.0 | +| `PATCH /smtp-servers` | 2.0 | +| `POST /kmip` | 2.1 | +| `DELETE /kmip` | 2.1 | +| `PATCH /object-store-access-policies` | 2.2 | +| `GET /file-systems/policies-all` | 2.3 | +| `PATCH /object-store-accounts` | 2.8 | +| `POST /admins` | 2.15 | +| `DELETE /admins` | 2.15 | +| `PATCH /sso/saml2/idps/test` | 2.16 | +| `GET /tls-policies/network-interfaces` | 2.17 | +| `POST /tls-policies/network-interfaces` | 2.17 | +| `DELETE /tls-policies/network-interfaces` | 2.17 | +| `PATCH /file-system-replica-links` | 2.17 | +| `PATCH /object-store-roles/object-store-trust-policies/upload` | 2.17 | +| `GET /object-store-roles/object-store-trust-policies/download` | 2.17 | +| `POST /nodes/batch` | 2.18 | +| `POST /resource-accesses/batch` | 2.19 | +| `GET /file-systems/data-eviction-policies` | 2.21 | +| `POST /file-systems/data-eviction-policies` | 2.21 | +| `DELETE /file-systems/data-eviction-policies` | 2.21 | +| `GET /admins/management-authentication-policies` | 2.22 | +| `POST /admins/management-authentication-policies` | 2.22 | +| `DELETE /admins/management-authentication-policies` | 2.22 | +| `GET /arrays/management-authentication-policies` | 2.22 | +| `POST /arrays/management-authentication-policies` | 2.22 | +| `DELETE /arrays/management-authentication-policies` | 2.22 | +| `GET /management-authentication-policies` | 2.22 | +| `POST /management-authentication-policies` | 2.22 | +| `DELETE /management-authentication-policies` | 2.22 | +| `PATCH /management-authentication-policies` | 2.22 | +| `GET /management-authentication-policies/members` | 2.22 | +| `POST /management-authentication-policies/members` | 2.22 | +| `DELETE /management-authentication-policies/members` | 2.22 | +| `GET /resiliency-groups/members` | 2.23 | +| `DELETE /directory-services/local/directory-services` | 2.24 | +| `PATCH /directory-services/local/directory-services` | 2.24 | +| `PATCH /directory-services/local/groups` | 2.24 | +| `GET /directory-services/local/users` | 2.24 | +| `POST /directory-services/local/users` | 2.24 | +| `DELETE /directory-services/local/users` | 2.24 | +| `PATCH /directory-services/local/users` | 2.24 | +| `GET /directory-services/local/users/members` | 2.24 | +| `POST /directory-services/local/users/members` | 2.24 | +| `DELETE /directory-services/local/users/members` | 2.24 | +| `GET /support-diagnostics/settings` | 2.24 | +| `PATCH /support-diagnostics/settings` | 2.24 | +| `GET /software-bundle` | 2.24 | +| `POST /software-bundle` | 2.24 | +| `GET /software-patches` | 2.24 | +| `POST /software-patches` | 2.24 | +| `GET /file-system-junctions` | 2.25 | +| `POST /file-system-junctions` | 2.25 | +| `DELETE /file-system-junctions` | 2.25 | +| `GET /realm-connections` | 2.25 | +| `POST /realm-connections` | 2.25 | +| `DELETE /realm-connections` | 2.25 | +| `GET /realm-connections/connection-key` | 2.25 | +| `POST /realm-connections/connection-key` | 2.25 | +| `DELETE /realm-connections/connection-key` | 2.25 | +| `GET /remote-realms` | 2.25 | +| `GET /management-access-policies/roles` | 2.26 | +| `POST /management-access-policies/roles` | 2.26 | +| `DELETE /management-access-policies/roles` | 2.26 | +| `GET /management-access-policies/roles/permissions` | 2.26 | +| `POST /management-access-policies/roles/permissions` | 2.26 | +| `DELETE /management-access-policies/roles/permissions` | 2.26 | +| `PATCH /management-access-policies/roles/permissions` | 2.26 | +| `GET /management-access-policies/roles/permissions/supported-resources` | 2.26 | +| `GET /management-access-policies/rules` | 2.26 | +| `POST /management-access-policies/rules` | 2.26 | +| `DELETE /management-access-policies/rules` | 2.26 | +| `PATCH /management-access-policies/rules` | 2.26 | +| `PATCH /remote-arrays` | 2.26 | +| `GET /support/system-manifest` | 2.26 | +| `GET /topology-groups` | 2.26 | +| `POST /topology-groups` | 2.26 | +| `PATCH /topology-groups` | 2.26 | +| `DELETE /topology-groups` | 2.26 | +| `GET /topology-groups/arrays` | 2.26 | +| `GET /topology-groups/members` | 2.26 | +| `POST /topology-groups/members` | 2.26 | +| `DELETE /topology-groups/members` | 2.26 | +| `PATCH /active-directory/test` | 2.27 | +| `POST /fleets/members/batch` | 2.27 | +| `GET /storage-classes/members` | 2.27 | + +## New ValidateSet candidates + +| Cmdlet | Parameter | Spec values | +|---|---|---| +| `Get-PfbPolicyAllMember` | `-MemberType` | file-systems, file-system-snapshots, file-system-replica-links, object-store-users | + +## Spec-vs-module assumptions: `context_names` cardinality + +Every category above asks *"does the module cover what the API offers?"*. This one asks a different question: *"is an assumption baked into the module still true?"* + +The module's cardinality rule -- **an endpoint is multi-context-capable if and only if its `context_names` resolves to component `Context_names_get` AND the endpoint declares `allow_errors`** -- is declared in exactly one place, `Private/Test-PfbContextMultiValueCapable.ps1`, which this check *executes* rather than re-derives. The HTTP verb is not the rule; it survives only as a fallback for an endpoint absent from the capability map. See section 8 of `docs/design/fusion-context-injection.md`. + +This is a **cross-signal** check, not a rule-versus-spec one. The earlier two-signal shape was structurally unable to see the defect that actually exists: four fleet-scoped GETs reference the multi-value component *and* are size-1 on the wire, so the rule and the component agreed while both were wrong. Any endpoint whose available signals do not all agree is reported below, including the case where the component agrees with the rule but the remaining signals dissent. + +Measured against **fb2.28** (the newest analysed version; the capability map's component data is last-seen-wins, so it describes that version's wire shape). 376 endpoints declare `context_names`, of which **135** satisfy the rule. + +The HTTP 207 signal is read from `tools/specs/fb2.28.json` -- the capability map records the request surface only and holds no response data, so 207 is a corroborating signal for this report and never a runtime gate. + +**A disagreement is not automatically a spec defect.** The case worth catching is a genuine future multi-context endpoint, where the *module* would be the wrong side. A human decides which signal is wrong, each time. + +**9 endpoint(s) with disagreeing signals**, grouped by shape. + +#### `component-says-multi-value-but-no-allow-errors` (4) + +The component claims fan-out while the endpoint offers no partial-failure story. Strong evidence of a spec defect: this is the shape of the four fleet-scoped GETs confirmed size-1 on the wire, and of `DELETE /management-access-policies` before its 2.28 correction. + +| Endpoint | Method | Component | Declares `allow_errors` | Declares 207 | Module rule | +|---|---|---|---|---|---| +| `GET /presets/workload` | GET | `Context_names_get` | no | no | size-1 | +| `GET /topology-groups` | GET | `Context_names_get` | no | no | size-1 | +| `GET /topology-groups/arrays` | GET | `Context_names_get` | no | no | size-1 | +| `GET /topology-groups/members` | GET | `Context_names_get` | no | no | size-1 | + +#### `rule-says-capable-but-declares-no-207` (4) + +Component and `allow_errors` both say fan-out, but no HTTP 207 is declared, so partial failure has no documented response shape. Status genuinely unknown -- which is precisely why 207 is a corroborating signal here and never a runtime gate: treating these as size-1 would block calls that may well work. + +| Endpoint | Method | Component | Declares `allow_errors` | Declares 207 | Module rule | +|---|---|---|---|---|---| +| `GET /arrays/ssh-certificate-authority-policies` | GET | `Context_names_get` | yes | no | multi-value | +| `GET /audit-file-systems-policy-operations` | GET | `Context_names_get` | yes | no | multi-value | +| `GET /log-targets/file-systems` | GET | `Context_names_get` | yes | no | multi-value | +| `GET /realms` | GET | `Context_names_get` | yes | no | multi-value | + +#### `size-1-component-but-declares-allow-errors` (1) + +The mirror case -- a size-1 component on an endpoint that nonetheless declares `allow_errors`. Named again below. + +| Endpoint | Method | Component | Declares `allow_errors` | Declares 207 | Module rule | +|---|---|---|---|---|---| +| `PATCH /directory-services/test` | PATCH | `Context_names` | yes | no | size-1 | + + +### Named case: size-1 component declaring `allow_errors` + +A **subset** of the findings above (shape `size-1-component-but-declares-allow-errors`), not an additional finding. Called out by name because it is a known, characterized case. + +- `PATCH /directory-services/test` + +Not live-verified: these carry mutating verbs and were not probed during the 2026-08-01 testing (Appendix A, "Not verified"). + diff --git a/Reports/PfbFieldCmdletMap.json b/Reports/PfbFieldCmdletMap.json new file mode 100644 index 0000000..df3fce1 --- /dev/null +++ b/Reports/PfbFieldCmdletMap.json @@ -0,0 +1,20593 @@ +{ + "schemaVersion": 1, + "generatedFrom": [ + "2.0", + "2.1", + "2.2", + "2.3", + "2.4", + "2.5", + "2.6", + "2.7", + "2.8", + "2.9", + "2.10", + "2.11", + "2.12", + "2.13", + "2.14", + "2.15", + "2.16", + "2.17", + "2.18", + "2.19", + "2.20", + "2.21", + "2.22", + "2.23", + "2.24", + "2.25", + "2.26", + "2.27", + "2.28" + ], + "entries": [ + { + "cmdlet": "Add-PfbDataEvictionPolicyFileSystem", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Add-PfbDataEvictionPolicyFileSystem", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Add-PfbDataEvictionPolicyFileSystem", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Add-PfbDataEvictionPolicyFileSystem", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbActiveDirectory", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbActiveDirectory", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdmin", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdmin", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdmin", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdmin", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdmin", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminCache", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminCache", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminCache", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminCache", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminCache", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminManagementAccessPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminManagementAccessPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminManagementAccessPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminManagementAccessPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminManagementAccessPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminManagementAccessPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminSetting", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminSetting", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminSetting", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminSshCaPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminSshCaPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminSshCaPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminSshCaPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminSshCaPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAdminSshCaPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAlert", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAlert", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAlert", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAlert", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAlert", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAlertWatcher", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAlertWatcher", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAlertWatcher", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAlertWatcher", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAlertWatcher", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbApiClient", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbApiClient", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbApiClient", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbApiClient", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbApiClient", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbApiToken", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbApiToken", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbApiToken", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbApiToken", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbApiToken", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArray", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArray", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArray", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayClientPerformance", + "parameter": "EndTime", + "wireName": "end_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayClientPerformance", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayClientPerformance", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayClientPerformance", + "parameter": "Resolution", + "wireName": "resolution", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayClientPerformance", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayClientPerformance", + "parameter": "StartTime", + "wireName": "start_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayClientS3Performance", + "parameter": "EndTime", + "wireName": "end_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayClientS3Performance", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayClientS3Performance", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayClientS3Performance", + "parameter": "Resolution", + "wireName": "resolution", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayClientS3Performance", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayClientS3Performance", + "parameter": "StartTime", + "wireName": "start_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnection", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnection", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnection", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnection", + "parameter": "RemoteName", + "wireName": "remote_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnection", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnectionKey", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnectionKey", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnectionKey", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnectionKey", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnectionPath", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnectionPath", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnectionPath", + "parameter": "RemoteName", + "wireName": "remote_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnectionPath", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnectionPerformanceReplication", + "parameter": "EndTime", + "wireName": "end_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnectionPerformanceReplication", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnectionPerformanceReplication", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnectionPerformanceReplication", + "parameter": "RemoteName", + "wireName": "remote_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnectionPerformanceReplication", + "parameter": "Resolution", + "wireName": "resolution", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnectionPerformanceReplication", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayConnectionPerformanceReplication", + "parameter": "StartTime", + "wireName": "start_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayErasure", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayErasure", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayErasure", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayEula", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayEula", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayEula", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayFactoryResetToken", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayFactoryResetToken", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayFactoryResetToken", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayHttpPerformance", + "parameter": "EndTime", + "wireName": "end_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayHttpPerformance", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayHttpPerformance", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayHttpPerformance", + "parameter": "Resolution", + "wireName": "resolution", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayHttpPerformance", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayHttpPerformance", + "parameter": "StartTime", + "wireName": "start_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayNfsPerformance", + "parameter": "EndTime", + "wireName": "end_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayNfsPerformance", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayNfsPerformance", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayNfsPerformance", + "parameter": "Resolution", + "wireName": "resolution", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayNfsPerformance", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayNfsPerformance", + "parameter": "StartTime", + "wireName": "start_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayPerformance", + "parameter": "EndTime", + "wireName": "end_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayPerformance", + "parameter": "Resolution", + "wireName": "resolution", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayPerformance", + "parameter": "StartTime", + "wireName": "start_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayPerformanceReplication", + "parameter": "EndTime", + "wireName": "end_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayPerformanceReplication", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayPerformanceReplication", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayPerformanceReplication", + "parameter": "Resolution", + "wireName": "resolution", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayPerformanceReplication", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayPerformanceReplication", + "parameter": "StartTime", + "wireName": "start_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayS3Performance", + "parameter": "EndTime", + "wireName": "end_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayS3Performance", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayS3Performance", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayS3Performance", + "parameter": "Resolution", + "wireName": "resolution", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayS3Performance", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayS3Performance", + "parameter": "StartTime", + "wireName": "start_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArraySshCaPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArraySshCaPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArraySshCaPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArraySshCaPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArraySshCaPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArraySshCaPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayStorageClass", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayStorageClass", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArrayStorageClass", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArraySupportedTimeZone", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArraySupportedTimeZone", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbArraySupportedTimeZone", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAsyncLog", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAsyncLog", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAsyncLog", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAsyncLog", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAsyncLog", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAsyncLogDownload", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAsyncLogDownload", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAudit", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAudit", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAudit", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAudit", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAudit", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditFileSystemPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditFileSystemPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditFileSystemPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditFileSystemPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditFileSystemPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditFileSystemPolicyMember", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditFileSystemPolicyMember", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditFileSystemPolicyMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditFileSystemPolicyMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditFileSystemPolicyMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditFileSystemPolicyMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditFileSystemPolicyMember", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditFileSystemPolicyOperation", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditFileSystemPolicyOperation", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditFileSystemPolicyOperation", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditObjectStorePolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditObjectStorePolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditObjectStorePolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditObjectStorePolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditObjectStorePolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditObjectStorePolicyMember", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditObjectStorePolicyMember", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditObjectStorePolicyMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditObjectStorePolicyMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditObjectStorePolicyMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditObjectStorePolicyMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbAuditObjectStorePolicyMember", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBlade", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBlade", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBlade", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBlade", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBlade", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucket", + "parameter": "Destroyed", + "wireName": "destroyed", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucket", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucket", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucket", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucket", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucket", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucket", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAccessPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAccessPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAccessPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAccessPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAccessPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAccessPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAccessPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAccessPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAccessPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAccessPolicyRule", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAccessPolicyRule", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAccessPolicyRule", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAccessPolicyRule", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAccessPolicyRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAccessPolicyRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAccessPolicyRule", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAuditFilter", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAuditFilter", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAuditFilter", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAuditFilter", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAuditFilter", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAuditFilterAction", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAuditFilterAction", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketAuditFilterAction", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketCorsPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketCorsPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketCorsPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketCorsPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketCorsPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketCorsPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketCorsPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketCorsPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketCorsPolicyRule", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketCorsPolicyRule", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketCorsPolicyRule", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketCorsPolicyRule", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketCorsPolicyRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketCorsPolicyRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketCorsPolicyRule", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketPerformance", + "parameter": "EndTime", + "wireName": "end_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketPerformance", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketPerformance", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketPerformance", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketPerformance", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketPerformance", + "parameter": "Resolution", + "wireName": "resolution", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketPerformance", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketPerformance", + "parameter": "StartTime", + "wireName": "start_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketPerformance", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketReplicaLink", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketReplicaLink", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketReplicaLink", + "parameter": "LocalBucketName", + "wireName": "local_bucket_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketReplicaLink", + "parameter": "RemoteBucketName", + "wireName": "remote_bucket_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketReplicaLink", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketS3Performance", + "parameter": "EndTime", + "wireName": "end_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketS3Performance", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketS3Performance", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketS3Performance", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketS3Performance", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketS3Performance", + "parameter": "Resolution", + "wireName": "resolution", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketS3Performance", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketS3Performance", + "parameter": "StartTime", + "wireName": "start_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbBucketS3Performance", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificate", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificate", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificate", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificate", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificate", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateCertificateGroup", + "parameter": "CertificateGroupName", + "wireName": "certificate_group_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateCertificateGroup", + "parameter": "CertificateName", + "wireName": "certificate_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateCertificateGroup", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateCertificateGroup", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateGroup", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateGroup", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateGroup", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateGroup", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateGroup", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateGroupCertificate", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateGroupCertificate", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateGroupCertificate", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateGroupCertificate", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateGroupCertificate", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateGroupUse", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateGroupUse", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateGroupUse", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateGroupUse", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateUse", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateUse", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateUse", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbCertificateUse", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicyFileSystem", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicyFileSystem", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicyFileSystem", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicyFileSystem", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicyFileSystem", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicyFileSystem", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicyFileSystem", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicyMember", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicyMember", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicyMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicyMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicyMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicyMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDataEvictionPolicyMember", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDirectoryService", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDirectoryService", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDirectoryServiceRole", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDirectoryServiceRole", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDirectoryServiceRole", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDirectoryServiceRole", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDirectoryServiceRole", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDirectoryServiceRoleManagementPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDirectoryServiceRoleManagementPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDirectoryServiceRoleManagementPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDirectoryServiceRoleManagementPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDirectoryServiceRoleManagementPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDirectoryServiceRoleManagementPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDns", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDns", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDns", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDrive", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDrive", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDrive", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDrive", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbDrive", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileLock", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileLock", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileLock", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileLock", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileLock", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileLock", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileLockClient", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileLockClient", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileLockClient", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileLockClient", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileLockClient", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileLockClient", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystem", + "parameter": "Destroyed", + "wireName": "destroyed", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystem", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystem", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystem", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystem", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystem", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystem", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemAuditPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemAuditPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemAuditPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemAuditPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemAuditPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemAuditPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemAuditPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemExport", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemExport", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemExport", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemExport", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemExport", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemExport", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroup", + "parameter": "FileSystemId", + "wireName": "file_system_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroup", + "parameter": "FileSystemName", + "wireName": "file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroup", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroup", + "parameter": "GroupId", + "wireName": "gids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroup", + "parameter": "GroupName", + "wireName": "group_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroup", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroup", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroup", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroupPerformance", + "parameter": "EndTime", + "wireName": "end_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroupPerformance", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroupPerformance", + "parameter": "Id", + "wireName": "file_system_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroupPerformance", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroupPerformance", + "parameter": "Name", + "wireName": "file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroupPerformance", + "parameter": "Resolution", + "wireName": "resolution", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroupPerformance", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroupPerformance", + "parameter": "StartTime", + "wireName": "start_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroupPerformance", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroupQuota", + "parameter": "FileSystemId", + "wireName": "file_system_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroupQuota", + "parameter": "FileSystemName", + "wireName": "file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroupQuota", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroupQuota", + "parameter": "GroupId", + "wireName": "gids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroupQuota", + "parameter": "GroupName", + "wireName": "group_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroupQuota", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroupQuota", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemGroupQuota", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLink", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLink", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLink", + "parameter": "LocalFileSystemName", + "wireName": "local_file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLink", + "parameter": "RemoteFileSystemName", + "wireName": "remote_file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLink", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLinkPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLinkPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLinkPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLinkPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLinkPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLinkPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLinkPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLinkTransfer", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLinkTransfer", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLinkTransfer", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLinkTransfer", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLinkTransfer", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemReplicaLinkTransfer", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSession", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSession", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSession", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSession", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSession", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshot", + "parameter": "Destroyed", + "wireName": "destroyed", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshot", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshot", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshot", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshot", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshot", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshot", + "parameter": "SourceName", + "wireName": "source_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshot", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshotPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshotPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshotPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshotPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshotPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshotPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshotPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshotTransfer", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshotTransfer", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshotTransfer", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshotTransfer", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshotTransfer", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemSnapshotTransfer", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemStorageClass", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemStorageClass", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemStorageClass", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemStorageClass", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemStorageClass", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemStorageClass", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUser", + "parameter": "FileSystemId", + "wireName": "file_system_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUser", + "parameter": "FileSystemName", + "wireName": "file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUser", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUser", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUser", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUser", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUser", + "parameter": "UserId", + "wireName": "uids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUser", + "parameter": "UserName", + "wireName": "user_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUser", + "parameter": "UserSid", + "wireName": "user_sids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserPerformance", + "parameter": "EndTime", + "wireName": "end_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserPerformance", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserPerformance", + "parameter": "Id", + "wireName": "file_system_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserPerformance", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserPerformance", + "parameter": "Name", + "wireName": "file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserPerformance", + "parameter": "Resolution", + "wireName": "resolution", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserPerformance", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserPerformance", + "parameter": "StartTime", + "wireName": "start_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserPerformance", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserQuota", + "parameter": "FileSystemId", + "wireName": "file_system_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserQuota", + "parameter": "FileSystemName", + "wireName": "file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserQuota", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserQuota", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserQuota", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserQuota", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserQuota", + "parameter": "UserId", + "wireName": "uids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserQuota", + "parameter": "UserName", + "wireName": "user_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemUserQuota", + "parameter": "UserSid", + "wireName": "user_sids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemWormPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemWormPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemWormPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemWormPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemWormPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemWormPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFileSystemWormPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFleet", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFleet", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFleet", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFleet", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFleet", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFleetKey", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFleetKey", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFleetKey", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFleetKey", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFleetMember", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFleetMember", + "parameter": "FleetName", + "wireName": "fleet_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFleetMember", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFleetMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbFleetMember", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardware", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardware", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardware", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardware", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardware", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardwareConnector", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardwareConnector", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardwareConnector", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardwareConnector", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardwareConnector", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardwareConnectorPerformance", + "parameter": "EndTime", + "wireName": "end_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardwareConnectorPerformance", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardwareConnectorPerformance", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardwareConnectorPerformance", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardwareConnectorPerformance", + "parameter": "Resolution", + "wireName": "resolution", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardwareConnectorPerformance", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardwareConnectorPerformance", + "parameter": "StartTime", + "wireName": "start_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbHardwareTemperature", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbKeytab", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbKeytab", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbKeytab", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbKeytab", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbKeytab", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbKeytabDownload", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbKeytabDownload", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbKeytabDownload", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbKeytabDownload", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbKeytabDownload", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbKmip", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbKmip", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbKmip", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbKmip", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbKmip", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLag", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLag", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLag", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLag", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLag", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLegalHold", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLegalHold", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLegalHold", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLegalHold", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLegalHold", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLegalHoldEntity", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLegalHoldEntity", + "parameter": "HoldId", + "wireName": "hold_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLegalHoldEntity", + "parameter": "HoldName", + "wireName": "hold_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLegalHoldEntity", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLegalHoldEntity", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLegalHoldEntity", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLegalHoldEntity", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLifecycleRule", + "parameter": "BucketName", + "wireName": "bucket_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLifecycleRule", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLifecycleRule", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLifecycleRule", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLifecycleRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLifecycleRule", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLocalDirectoryService", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLocalDirectoryService", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLocalDirectoryService", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLocalDirectoryService", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLocalDirectoryService", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLocalGroup", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLocalGroup", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLocalGroup", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLocalGroup", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLocalGroup", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLocalGroupMember", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLocalGroupMember", + "parameter": "Group", + "wireName": "group_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLocalGroupMember", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLocalGroupMember", + "parameter": "Member", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLocalGroupMember", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLog", + "parameter": "EndTime", + "wireName": "end_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLog", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLog", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLog", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLog", + "parameter": "StartTime", + "wireName": "start_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLogTargetFileSystem", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLogTargetFileSystem", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLogTargetFileSystem", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLogTargetFileSystem", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLogTargetFileSystem", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLogTargetObjectStore", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLogTargetObjectStore", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLogTargetObjectStore", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLogTargetObjectStore", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbLogTargetObjectStore", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbMaintenanceWindow", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbMaintenanceWindow", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbMaintenanceWindow", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbMaintenanceWindow", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbMaintenanceWindow", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyAdmin", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyAdmin", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyAdmin", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyAdmin", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyAdmin", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyAdmin", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyDirectoryRole", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyDirectoryRole", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyDirectoryRole", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyDirectoryRole", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyDirectoryRole", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyDirectoryRole", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyMember", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyMember", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbManagementAccessPolicyMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessPolicyMember", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessPolicyMember", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessPolicyMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessPolicyMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessPolicyMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessPolicyMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessPolicyMember", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessRule", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessRule", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkAccessRule", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkConnectionStatistics", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkConnectionStatistics", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkConnectionStatistics", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkConnectionStatistics", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterface", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterface", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterface", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterface", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterface", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceConnector", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceConnector", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceConnector", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceConnector", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceConnector", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceConnectorPerformance", + "parameter": "EndTime", + "wireName": "end_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceConnectorPerformance", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceConnectorPerformance", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceConnectorPerformance", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceConnectorPerformance", + "parameter": "Resolution", + "wireName": "resolution", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceConnectorPerformance", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceConnectorPerformance", + "parameter": "StartTime", + "wireName": "start_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceConnectorSettings", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceConnectorSettings", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceConnectorSettings", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceConnectorSettings", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceNeighbor", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceNeighbor", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceNeighbor", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceNeighbor", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceTlsPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceTlsPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceTlsPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceTlsPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceTlsPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceTlsPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNetworkInterfaceTlsPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNfsExportPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNfsExportPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNfsExportPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNfsExportPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNfsExportPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNfsExportRule", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNfsExportRule", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNfsExportRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNfsExportRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNfsExportRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNfsExportRule", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNode", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNode", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNode", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNode", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNode", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNodeGroup", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNodeGroup", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNodeGroup", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNodeGroup", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNodeGroup", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNodeGroupNode", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNodeGroupNode", + "parameter": "GroupId", + "wireName": "group_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNodeGroupNode", + "parameter": "GroupName", + "wireName": "group_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNodeGroupNode", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNodeGroupNode", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNodeGroupNode", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNodeGroupNode", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNodeGroupUse", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNodeGroupUse", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNodeGroupUse", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbNodeGroupUse", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessKey", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessKey", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessKey", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessKey", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessKey", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicy", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyAction", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyAction", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyAction", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyAction", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyRole", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyRole", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyRole", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyRole", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyRole", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyRole", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyRole", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyRole", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyRule", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyRule", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyRule", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyRule", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyUser", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyUser", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyUser", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyUser", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyUser", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyUser", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyUser", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccessPolicyUser", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccount", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccount", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccount", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccount", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccount", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccount", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccountExport", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccountExport", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccountExport", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccountExport", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccountExport", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreAccountExport", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRemoteCredential", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRemoteCredential", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRemoteCredential", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRemoteCredential", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRemoteCredential", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRemoteCredential", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRole", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRole", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRole", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRole", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRole", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRole", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRoleAccessPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRoleAccessPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRoleAccessPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRoleAccessPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRoleAccessPolicy", + "parameter": "RoleId", + "wireName": "role_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRoleAccessPolicy", + "parameter": "RoleName", + "wireName": "role_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRoleAccessPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreRoleAccessPolicy", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreTrustPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreTrustPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreTrustPolicy", + "parameter": "RoleId", + "wireName": "role_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreTrustPolicy", + "parameter": "RoleName", + "wireName": "role_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreTrustPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreTrustPolicy", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreTrustPolicyRule", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreTrustPolicyRule", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreTrustPolicyRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreTrustPolicyRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreTrustPolicyRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreTrustPolicyRule", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreTrustPolicyRule", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreUser", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreUser", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreUser", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreUser", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreUser", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreUserAccessPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreUserAccessPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreUserAccessPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreUserAccessPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreUserAccessPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreUserAccessPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreUserAccessPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreUserAccessPolicy", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreVirtualHost", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreVirtualHost", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreVirtualHost", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreVirtualHost", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbObjectStoreVirtualHost", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbOidcIdp", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbOidcIdp", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbOidcIdp", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbOidcIdp", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbOidcIdp", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbOpenFile", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbOpenFile", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbOpenFile", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbOpenFile", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbOpenFile", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbOpenFile", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPasswordPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPasswordPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPasswordPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyAll", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyAll", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyAll", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyAll", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyAll", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyAllMember", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyAllMember", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyAllMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyAllMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyAllMember", + "parameter": "MemberType", + "wireName": "member_types", + "status": "matched", + "matchedKey": "GET policies-all/members#member_types", + "specValues": [ + "file-systems", + "file-system-snapshots", + "file-system-replica-links", + "object-store-users" + ], + "stableSinceOldestVersion": false, + "recommendation": "ArgumentCompleter" + }, + { + "cmdlet": "Get-PfbPolicyAllMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyAllMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystem", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystem", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystem", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystem", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystem", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystem", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystem", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystemReplicaLink", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystemReplicaLink", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystemReplicaLink", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystemReplicaLink", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystemReplicaLink", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystemReplicaLink", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystemSnapshot", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystemSnapshot", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystemSnapshot", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystemSnapshot", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystemSnapshot", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystemSnapshot", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPolicyFileSystemSnapshot", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPresetWorkload", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPresetWorkload", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPublicKey", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPublicKey", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPublicKey", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPublicKey", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPublicKey", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPublicKeyUse", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPublicKeyUse", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPublicKeyUse", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbPublicKeyUse", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyBucket", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyBucket", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyBucket", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyBucket", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyBucket", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyBucket", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyFileSystem", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyFileSystem", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyFileSystem", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyFileSystem", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyFileSystem", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyFileSystem", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyMember", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyMember", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQosPolicyMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQuotaGroup", + "parameter": "FileSystemName", + "wireName": "file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQuotaGroup", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQuotaGroup", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQuotaGroup", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQuotaGroup", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQuotaUser", + "parameter": "FileSystemName", + "wireName": "file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQuotaUser", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQuotaUser", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQuotaUser", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbQuotaUser", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealm", + "parameter": "Destroyed", + "wireName": "destroyed", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealm", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealm", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealm", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealm", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealm", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealm", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealmDefaults", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealmDefaults", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealmDefaults", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealmDefaults", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealmSpace", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealmSpace", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealmSpace", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealmSpace", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealmStorageClass", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealmStorageClass", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealmStorageClass", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRealmStorageClass", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRemoteArray", + "parameter": "CurrentFleetOnly", + "wireName": "current_fleet_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRemoteArray", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRemoteArray", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRemoteArray", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRemoteArray", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRemoteArray", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbResiliencyGroup", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbResiliencyGroup", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbResiliencyGroup", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbResiliencyGroup", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbResiliencyGroup", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbResourceAccess", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbResourceAccess", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbResourceAccess", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbResourceAccess", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbResourceAccess", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRole", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRole", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRole", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbRole", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbS3ExportPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbS3ExportPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbS3ExportPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbS3ExportPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbS3ExportPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbS3ExportRule", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbS3ExportRule", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbS3ExportRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbS3ExportRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbS3ExportRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbS3ExportRule", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSaml2Idp", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSaml2Idp", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSaml2Idp", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSaml2Idp", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSaml2Idp", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbServer", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbServer", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbServer", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbServer", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbServer", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbServer", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSession", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSession", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSession", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSession", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSession", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbClientPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbClientPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbClientPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbClientPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbClientPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbClientRule", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbClientRule", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbClientRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbClientRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbClientRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbClientRule", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbSharePolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbSharePolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbSharePolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbSharePolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbSharePolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbShareRule", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbShareRule", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbShareRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbShareRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbShareRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmbShareRule", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmtpServer", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmtpServer", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSmtpServer", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSnmpAgent", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSnmpManager", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSnmpManager", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSnmpManager", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSnmpManager", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSnmpManager", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSoftwareCheck", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSoftwareCheck", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSoftwareCheck", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyAdmin", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyAdmin", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyAdmin", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyAdmin", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyAdmin", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyAdmin", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyArray", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyArray", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyArray", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyArray", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyArray", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyArray", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyMember", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyMember", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSshCaPolicyMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbStorageClassTieringPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbStorageClassTieringPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbStorageClassTieringPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbStorageClassTieringPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbStorageClassTieringPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbStorageClassTieringPolicyMember", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbStorageClassTieringPolicyMember", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbStorageClassTieringPolicyMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbStorageClassTieringPolicyMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbStorageClassTieringPolicyMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbStorageClassTieringPolicyMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSubnet", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSubnet", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSubnet", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSubnet", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSubnet", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSupport", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSupport", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSupport", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSupport", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSupportDiagnostics", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSupportDiagnostics", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSupportDiagnostics", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSupportDiagnostics", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSupportDiagnostics", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSupportDiagnosticsDetails", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSupportDiagnosticsDetails", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSupportDiagnosticsDetails", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSupportDiagnosticsDetails", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSupportDiagnosticsDetails", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSupportVerificationKey", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSupportVerificationKey", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSupportVerificationKey", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSyslogServer", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSyslogServer", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSyslogServer", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSyslogServer", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSyslogServer", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSyslogServerSettings", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSyslogServerSettings", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbSyslogServerSettings", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTarget", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTarget", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTarget", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTarget", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTarget", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTargetPerformanceReplication", + "parameter": "EndTime", + "wireName": "end_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTargetPerformanceReplication", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTargetPerformanceReplication", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTargetPerformanceReplication", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTargetPerformanceReplication", + "parameter": "Resolution", + "wireName": "resolution", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTargetPerformanceReplication", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTargetPerformanceReplication", + "parameter": "StartTime", + "wireName": "start_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTlsPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTlsPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTlsPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTlsPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTlsPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTlsPolicyMember", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTlsPolicyMember", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTlsPolicyMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTlsPolicyMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTlsPolicyMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbTlsPolicyMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUsageGroup", + "parameter": "FileSystemName", + "wireName": "file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUsageGroup", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUsageGroup", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUsageGroup", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUsageUser", + "parameter": "FileSystemName", + "wireName": "file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUsageUser", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUsageUser", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUsageUser", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicy", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyMember", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyMember", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyMember", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyMember", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyRule", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyRule", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyRule", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyRule", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicyRule", + "parameter": "TotalOnly", + "wireName": "total_only", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWorkload", + "parameter": "Destroyed", + "wireName": "destroyed", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWorkload", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWorkload", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWorkload", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWorkload", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWorkload", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWorkloadPlacementRecommendation", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWorkloadPlacementRecommendation", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWorkloadPlacementRecommendation", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWorkloadPlacementRecommendation", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWorkloadPlacementRecommendation", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWorkloadTag", + "parameter": "Namespace", + "wireName": "namespaces", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWorkloadTag", + "parameter": "ResourceId", + "wireName": "resource_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWorkloadTag", + "parameter": "ResourceName", + "wireName": "resource_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWormPolicy", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWormPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWormPolicy", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWormPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWormPolicy", + "parameter": "Sort", + "wireName": "sort", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWormPolicyMember", + "parameter": "Filter", + "wireName": "filter", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWormPolicyMember", + "parameter": "Limit", + "wireName": "limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWormPolicyMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWormPolicyMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWormPolicyMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Get-PfbWormPolicyMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Invoke-PfbNetworkPing", + "parameter": "Count", + "wireName": "count", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Invoke-PfbNetworkPing", + "parameter": "Destination", + "wireName": "destination", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Invoke-PfbNetworkPing", + "parameter": "PacketSize", + "wireName": "packet_size", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Invoke-PfbNetworkPing", + "parameter": "SourceName", + "wireName": "source.name", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Invoke-PfbNetworkTrace", + "parameter": "Destination", + "wireName": "destination", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Invoke-PfbNetworkTrace", + "parameter": "SourceName", + "wireName": "source.name", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAdminManagementAccessPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAdminManagementAccessPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAdminManagementAccessPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAdminManagementAccessPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAdminSshCaPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAdminSshCaPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAdminSshCaPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAdminSshCaPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAlertWatcher", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbApiClient", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbApiToken", + "parameter": "Id", + "wireName": "admin_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbApiToken", + "parameter": "Name", + "wireName": "admin_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbApiToken", + "parameter": "Timeout", + "wireName": "timeout", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbArrayConnection", + "parameter": "CaCertificateGroup", + "wireName": "ca_certificate_group", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbArrayConnection", + "parameter": "ConnectionKey", + "wireName": "connection_key", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbArrayConnection", + "parameter": "Encrypted", + "wireName": "encrypted", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbArrayConnection", + "parameter": "ManagementAddress", + "wireName": "management_address", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbArrayConnection", + "parameter": "Remote", + "wireName": "remote", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbArrayConnection", + "parameter": "ReplicationAddress", + "wireName": "replication_addresses", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbArrayConnection", + "parameter": "Throttle", + "wireName": "throttle", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbArraySshCaPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbArraySshCaPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbArraySshCaPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbArraySshCaPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAuditFileSystemPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAuditFileSystemPolicyMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAuditFileSystemPolicyMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAuditFileSystemPolicyMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAuditFileSystemPolicyMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAuditObjectStorePolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAuditObjectStorePolicyMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAuditObjectStorePolicyMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAuditObjectStorePolicyMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbAuditObjectStorePolicyMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbBucket", + "parameter": "Account", + "wireName": "account", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbBucket", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbBucket", + "parameter": "QuotaLimit", + "wireName": "quota_limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbBucketAccessPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbBucketAccessPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbBucketAccessPolicyRule", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbBucketAccessPolicyRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbBucketAuditFilter", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbBucketCorsPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbBucketCorsPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbCertificate", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbCertificateCertificateGroup", + "parameter": "CertificateGroupId", + "wireName": "certificate_group_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbCertificateCertificateGroup", + "parameter": "CertificateGroupName", + "wireName": "certificate_group_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbCertificateCertificateGroup", + "parameter": "CertificateId", + "wireName": "certificate_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbCertificateCertificateGroup", + "parameter": "CertificateName", + "wireName": "certificate_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbCertificateGroup", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbDataEvictionPolicy", + "parameter": "KeepSize", + "wireName": "keep_size", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbDataEvictionPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbDirectoryServiceRole", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbDirectoryServiceRoleManagementPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbDirectoryServiceRoleManagementPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbDirectoryServiceRoleManagementPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbDirectoryServiceRoleManagementPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "DefaultGroupQuota", + "wireName": "default_group_quota", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "DefaultUserQuota", + "wireName": "default_user_quota", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "Provisioned", + "wireName": "provisioned", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "QosPolicy", + "wireName": "qos_policy", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "SourceSnapshot", + "wireName": "source", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemAuditPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemAuditPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemAuditPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemAuditPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemExport", + "parameter": "ExportName", + "wireName": "export_name", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemExport", + "parameter": "FileSystem", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemExport", + "parameter": "Policy", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemExport", + "parameter": "Server", + "wireName": "server", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemExport", + "parameter": "SharePolicy", + "wireName": "share_policy", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemReplicaLink", + "parameter": "LocalFileSystemName", + "wireName": "local_file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemReplicaLink", + "parameter": "RemoteArrayName", + "wireName": "remote_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemReplicaLink", + "parameter": "RemoteDefaultExports", + "wireName": "remote_default_exports", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemReplicaLink", + "parameter": "RemoteFileSystemName", + "wireName": "remote_file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemReplicaLinkPolicy", + "parameter": "LocalFileSystemId", + "wireName": "local_file_system_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemReplicaLinkPolicy", + "parameter": "LocalFileSystemName", + "wireName": "local_file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemReplicaLinkPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemReplicaLinkPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemReplicaLinkPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemReplicaLinkPolicy", + "parameter": "RemoteId", + "wireName": "remote_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemReplicaLinkPolicy", + "parameter": "RemoteName", + "wireName": "remote_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemSnapshot", + "parameter": "Send", + "wireName": "send", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemSnapshot", + "parameter": "Suffix", + "wireName": "suffix", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemSnapshot", + "parameter": "Targets", + "wireName": "targets", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "DeleteExistingUserGroupQuotaSettings", + "wireName": "delete_existing_user_group_quota_settings", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "IgnoreUsage", + "wireName": "ignore_usage", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFleetMember", + "parameter": "FleetId", + "wireName": "fleet_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFleetMember", + "parameter": "FleetName", + "wireName": "fleet_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbFleetMember", + "parameter": "Members", + "wireName": "members", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbLegalHold", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbLegalHoldEntity", + "parameter": "FileSystemIds", + "wireName": "file_system_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbLegalHoldEntity", + "parameter": "FileSystemNames", + "wireName": "file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbLegalHoldEntity", + "parameter": "Ids", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbLegalHoldEntity", + "parameter": "Names", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbLegalHoldEntity", + "parameter": "Paths", + "wireName": "paths", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbLegalHoldEntity", + "parameter": "Recursive", + "wireName": "recursive", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbLifecycleRule", + "parameter": "BucketName", + "wireName": "bucket", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbLocalDirectoryService", + "parameter": "Domain", + "wireName": "domain", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbLocalDirectoryService", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbLocalGroup", + "parameter": "Email", + "wireName": "email", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbLocalGroup", + "parameter": "Gid", + "wireName": "gid", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbLocalGroup", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbLocalGroupMember", + "parameter": "Group", + "wireName": "group_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbLocalGroupMember", + "parameter": "LocalDirectoryService", + "wireName": "local_directory_service_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbLogTargetFileSystem", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbLogTargetObjectStore", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbManagementAccessPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbManagementAccessPolicyAdmin", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbManagementAccessPolicyAdmin", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbManagementAccessPolicyAdmin", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbManagementAccessPolicyAdmin", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbManagementAccessPolicyDirectoryRole", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbManagementAccessPolicyDirectoryRole", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbManagementAccessPolicyDirectoryRole", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbManagementAccessPolicyDirectoryRole", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNetworkAccessRule", + "parameter": "BeforeRuleId", + "wireName": "before_rule_id", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNetworkAccessRule", + "parameter": "BeforeRuleName", + "wireName": "before_rule_name", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNetworkAccessRule", + "parameter": "Client", + "wireName": "client", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNetworkAccessRule", + "parameter": "Index", + "wireName": "index", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNetworkAccessRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNetworkAccessRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNetworkAccessRule", + "parameter": "Versions", + "wireName": "versions", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNetworkInterface", + "parameter": "Address", + "wireName": "address", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNetworkInterface", + "parameter": "AttachedServers", + "wireName": "attached_servers", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNetworkInterface", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNetworkInterfaceTlsPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNetworkInterfaceTlsPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNetworkInterfaceTlsPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNetworkInterfaceTlsPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNfsExportPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNfsExportRule", + "parameter": "Anongid", + "wireName": "anongid", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNfsExportRule", + "parameter": "Anonuid", + "wireName": "anonuid", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNfsExportRule", + "parameter": "Atime", + "wireName": "atime", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNfsExportRule", + "parameter": "BeforeRuleId", + "wireName": "before_rule_id", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNfsExportRule", + "parameter": "BeforeRuleName", + "wireName": "before_rule_name", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNfsExportRule", + "parameter": "Client", + "wireName": "client", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNfsExportRule", + "parameter": "Fileid32bit", + "wireName": "fileid_32bit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNfsExportRule", + "parameter": "Index", + "wireName": "index", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNfsExportRule", + "parameter": "Policy", + "wireName": "policy", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNfsExportRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNfsExportRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNfsExportRule", + "parameter": "Secure", + "wireName": "secure", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNfsExportRule", + "parameter": "Security", + "wireName": "security", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNfsExportRule", + "parameter": "Versions", + "wireName": "versions", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNlmReclamation", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNodeGroupNode", + "parameter": "GroupId", + "wireName": "node_group_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNodeGroupNode", + "parameter": "GroupName", + "wireName": "node_group_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNodeGroupNode", + "parameter": "MemberId", + "wireName": "node_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbNodeGroupNode", + "parameter": "MemberName", + "wireName": "node_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreAccessKey", + "parameter": "UserName", + "wireName": "user", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreAccessPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreAccessPolicyRole", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreAccessPolicyRole", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreAccessPolicyRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreAccessPolicyUser", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreAccessPolicyUser", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreAccount", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreAccountExport", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreRemoteCredential", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreRole", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreRoleAccessPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreRoleAccessPolicy", + "parameter": "RoleName", + "wireName": "role_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreTrustPolicyRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreUser", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreUserAccessPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreUserAccessPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreVirtualHost", + "parameter": "Hostname", + "wireName": "hostname", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbObjectStoreVirtualHost", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbOidcIdp", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbPolicy", + "parameter": "Rules", + "wireName": "rules", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbPolicyFileSystem", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbPolicyFileSystem", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbPolicyFileSystem", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbPolicyFileSystem", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbPolicyFileSystemReplicaLink", + "parameter": "LocalFileSystemId", + "wireName": "local_file_system_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbPolicyFileSystemReplicaLink", + "parameter": "LocalFileSystemName", + "wireName": "local_file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbPolicyFileSystemReplicaLink", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbPolicyFileSystemReplicaLink", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbPolicyFileSystemReplicaLink", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbPolicyFileSystemReplicaLink", + "parameter": "RemoteId", + "wireName": "remote_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbPolicyFileSystemReplicaLink", + "parameter": "RemoteName", + "wireName": "remote_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbPresetWorkload", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbPresetWorkload", + "parameter": "SkipVerifyDeployable", + "wireName": "skip_verify_deployable", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbPublicKey", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbQosPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbQosPolicyMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbQosPolicyMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbQosPolicyMember", + "parameter": "MemberType", + "wireName": "member_types", + "status": "collision", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbQosPolicyMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbQosPolicyMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbQuotaGroup", + "parameter": "FileSystemName", + "wireName": "file_system", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbQuotaGroup", + "parameter": "GroupName", + "wireName": "group", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbQuotaGroup", + "parameter": "Quota", + "wireName": "quota", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbQuotaUser", + "parameter": "Quota", + "wireName": "quota", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbRealm", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbS3ExportPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbS3ExportRule", + "parameter": "Actions", + "wireName": "actions", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbS3ExportRule", + "parameter": "Effect", + "wireName": "effect", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbS3ExportRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbS3ExportRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbS3ExportRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbS3ExportRule", + "parameter": "Resources", + "wireName": "resources", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSaml2Idp", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbServer", + "parameter": "DnsName", + "wireName": "dns", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbServer", + "parameter": "LocalDirectoryService", + "wireName": "local_directory_service", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbServer", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSmbClientPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSmbClientRule", + "parameter": "BeforeRuleId", + "wireName": "before_rule_id", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSmbClientRule", + "parameter": "BeforeRuleName", + "wireName": "before_rule_name", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSmbClientRule", + "parameter": "Client", + "wireName": "client", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSmbClientRule", + "parameter": "Index", + "wireName": "index", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSmbClientRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSmbClientRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSmbClientRule", + "parameter": "Versions", + "wireName": "versions", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSmbSharePolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSmbShareRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSmbShareRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSmbShareRule", + "parameter": "Principal", + "wireName": "principal", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSshCaPolicyAdmin", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSshCaPolicyAdmin", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSshCaPolicyAdmin", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSshCaPolicyAdmin", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSshCaPolicyArray", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSshCaPolicyArray", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSshCaPolicyArray", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSshCaPolicyArray", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSubnet", + "parameter": "Gateway", + "wireName": "gateway", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSubnet", + "parameter": "LinkAggregationGroupName", + "wireName": "link_aggregation_group", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSubnet", + "parameter": "Mtu", + "wireName": "mtu", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSubnet", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSubnet", + "parameter": "Prefix", + "wireName": "prefix", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbSubnet", + "parameter": "Vlan", + "wireName": "vlan", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicy", + "parameter": "FileSystemId", + "wireName": "file_system_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicy", + "parameter": "FileSystemName", + "wireName": "file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicy", + "parameter": "Location", + "wireName": "location", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicy", + "parameter": "Rules", + "wireName": "rules", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "DeleteExistingUserGroupQuotaSettings", + "wireName": "delete_existing_user_group_quota_settings", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "IgnoreUsage", + "wireName": "ignore_usage", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicyRule", + "parameter": "IgnoreUsage", + "wireName": "ignore_usage", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicyRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicyRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicyRule", + "parameter": "QuotaLimit", + "wireName": "quota_limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicyRule", + "parameter": "Subject", + "wireName": "subject", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbWorkload", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbWorkload", + "parameter": "Parameters", + "wireName": "parameters", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbWorkload", + "parameter": "PresetId", + "wireName": "preset_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbWorkload", + "parameter": "PresetName", + "wireName": "preset_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbWorkloadPlacementRecommendation", + "parameter": "PlacementName", + "wireName": "placement_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbWorkloadPlacementRecommendation", + "parameter": "PresetId", + "wireName": "preset_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "New-PfbWorkloadPlacementRecommendation", + "parameter": "PresetName", + "wireName": "preset_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbActiveDirectory", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbActiveDirectory", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAdminCache", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAdminCache", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAdminManagementAccessPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAdminManagementAccessPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAdminManagementAccessPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAdminManagementAccessPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAdminSshCaPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAdminSshCaPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAdminSshCaPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAdminSshCaPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAlertWatcher", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAlertWatcher", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbApiClient", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbApiClient", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbApiToken", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbApiToken", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbArrayConnection", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbArrayConnection", + "parameter": "RemoteName", + "wireName": "remote_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbArraySshCaPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbArraySshCaPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbArraySshCaPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbArraySshCaPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAuditFileSystemPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAuditFileSystemPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAuditFileSystemPolicyMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAuditFileSystemPolicyMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAuditFileSystemPolicyMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAuditFileSystemPolicyMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAuditObjectStorePolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAuditObjectStorePolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAuditObjectStorePolicyMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAuditObjectStorePolicyMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAuditObjectStorePolicyMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbAuditObjectStorePolicyMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbBucket", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbBucket", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbBucketAccessPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbBucketAccessPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbBucketAccessPolicyRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbBucketAuditFilter", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbBucketAuditFilter", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbBucketCorsPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbBucketCorsPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbBucketCorsPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbBucketCorsPolicyRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbCertificate", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbCertificate", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbCertificateCertificateGroup", + "parameter": "CertificateGroupName", + "wireName": "certificate_group_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbCertificateCertificateGroup", + "parameter": "CertificateName", + "wireName": "certificate_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbCertificateGroup", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbCertificateGroup", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbDataEvictionPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbDataEvictionPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbDataEvictionPolicyFileSystem", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbDataEvictionPolicyFileSystem", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbDataEvictionPolicyFileSystem", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbDataEvictionPolicyFileSystem", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbDirectoryServiceRole", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbDirectoryServiceRole", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbDirectoryServiceRoleManagementPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbDirectoryServiceRoleManagementPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbDirectoryServiceRoleManagementPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbDirectoryServiceRoleManagementPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbDns", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbDns", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileLock", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileLock", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystem", + "parameter": "DeleteLinkOnEradication", + "wireName": "delete_link_on_eradication", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystem", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystem", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemAuditPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemAuditPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemAuditPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemAuditPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemExport", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemExport", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemReplicaLink", + "parameter": "CancelInProgressTransfers", + "wireName": "cancel_in_progress_transfers", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemReplicaLink", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemReplicaLink", + "parameter": "LocalFileSystemName", + "wireName": "local_file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemReplicaLink", + "parameter": "RemoteArrayName", + "wireName": "remote_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemReplicaLink", + "parameter": "RemoteFileSystemName", + "wireName": "remote_file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemReplicaLinkPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemReplicaLinkPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemReplicaLinkPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemReplicaLinkPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemSession", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemSnapshot", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemSnapshot", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemSnapshotPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemSnapshotPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemSnapshotPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemSnapshotPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemSnapshotTransfer", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemSnapshotTransfer", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFileSystemUserGroupQuotaPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFleet", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFleet", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFleetMember", + "parameter": "FleetName", + "wireName": "fleet_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbFleetMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbKeytab", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbKeytab", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbLag", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbLag", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbLegalHold", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbLegalHold", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbLifecycleRule", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbLifecycleRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbLocalGroup", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbLocalGroup", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbLocalGroupMember", + "parameter": "Group", + "wireName": "group_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbLocalGroupMember", + "parameter": "Member", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbLogTargetFileSystem", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbLogTargetFileSystem", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbLogTargetObjectStore", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbLogTargetObjectStore", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbMaintenanceWindow", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbMaintenanceWindow", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbManagementAccessPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbManagementAccessPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbManagementAccessPolicyAdmin", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbManagementAccessPolicyAdmin", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbManagementAccessPolicyAdmin", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbManagementAccessPolicyAdmin", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbManagementAccessPolicyDirectoryRole", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbManagementAccessPolicyDirectoryRole", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbManagementAccessPolicyDirectoryRole", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbManagementAccessPolicyDirectoryRole", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbNetworkAccessRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbNetworkAccessRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbNetworkAccessRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbNetworkInterface", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbNetworkInterface", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbNetworkInterfaceTlsPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbNetworkInterfaceTlsPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbNfsExportPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbNfsExportPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbNfsExportRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbNfsExportRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbNfsExportRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbNodeGroup", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbNodeGroup", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbNodeGroupNode", + "parameter": "GroupName", + "wireName": "group_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbNodeGroupNode", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreAccessKey", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreAccessKey", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreAccessPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreAccessPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreAccessPolicyRole", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreAccessPolicyRole", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreAccessPolicyRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreAccessPolicyRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreAccessPolicyUser", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreAccessPolicyUser", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreAccount", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreAccount", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreAccountExport", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreAccountExport", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreRemoteCredential", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreRemoteCredential", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreRole", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreRole", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreRoleAccessPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreRoleAccessPolicy", + "parameter": "RoleName", + "wireName": "role_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreTrustPolicyRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreTrustPolicyRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreUser", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreUser", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreUserAccessPolicy", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreUserAccessPolicy", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreVirtualHost", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbObjectStoreVirtualHost", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbOidcIdp", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbOidcIdp", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbOpenFile", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbOpenFile", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbPolicyFileSystem", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbPolicyFileSystem", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbPolicyFileSystem", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbPolicyFileSystem", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbPolicyFileSystemReplicaLink", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbPolicyFileSystemReplicaLink", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbPolicyFileSystemReplicaLink", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbPolicyFileSystemReplicaLink", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbPresetWorkload", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbPresetWorkload", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbPublicKey", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbPublicKey", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbQosPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbQosPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbQosPolicyMember", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbQosPolicyMember", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbQosPolicyMember", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbQosPolicyMember", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbRealm", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbRealm", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbResourceAccess", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbResourceAccess", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbS3ExportPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbS3ExportPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbS3ExportRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbS3ExportRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbS3ExportRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSaml2Idp", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSaml2Idp", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbServer", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbServer", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSmbClientPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSmbClientPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSmbClientRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSmbClientRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSmbClientRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSmbSharePolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSmbSharePolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSmbShareRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSmbShareRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSmbShareRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSnmpManager", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSnmpManager", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSshCaPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSshCaPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSshCaPolicyAdmin", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSshCaPolicyAdmin", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSshCaPolicyAdmin", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSshCaPolicyAdmin", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSshCaPolicyArray", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSshCaPolicyArray", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSshCaPolicyArray", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSshCaPolicyArray", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbStorageClassTieringPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbStorageClassTieringPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSubnet", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSubnet", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSyslogServer", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbSyslogServer", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbTarget", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbTarget", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbTlsPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbTlsPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbUserGroupQuotaPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbUserGroupQuotaPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbUserGroupQuotaPolicy", + "parameter": "Version", + "wireName": "versions", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "MemberId", + "wireName": "member_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "MemberName", + "wireName": "member_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbUserGroupQuotaPolicyFileSystem", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbUserGroupQuotaPolicyRule", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbUserGroupQuotaPolicyRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbUserGroupQuotaPolicyRule", + "parameter": "PolicyId", + "wireName": "policy_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbUserGroupQuotaPolicyRule", + "parameter": "PolicyName", + "wireName": "policy_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbWorkload", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbWorkload", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbWorkloadTag", + "parameter": "Key", + "wireName": "keys", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbWorkloadTag", + "parameter": "Namespace", + "wireName": "namespaces", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbWorkloadTag", + "parameter": "ResourceId", + "wireName": "resource_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbWorkloadTag", + "parameter": "ResourceName", + "wireName": "resource_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbWormPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Remove-PfbWormPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Set-PfbPresetWorkload", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Set-PfbPresetWorkload", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Set-PfbPresetWorkload", + "parameter": "SkipVerifyDeployable", + "wireName": "skip_verify_deployable", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Set-PfbWorkloadTag", + "parameter": "ResourceId", + "wireName": "resource_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Set-PfbWorkloadTag", + "parameter": "ResourceName", + "wireName": "resource_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Test-PfbActiveDirectory", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Test-PfbActiveDirectory", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Test-PfbAlertWatcher", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Test-PfbAlertWatcher", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Test-PfbKmip", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Test-PfbKmip", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Test-PfbSaml2Idp", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Test-PfbSaml2Idp", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Test-PfbSnmpManager", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Test-PfbSnmpManager", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Test-PfbSyslogServer", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Test-PfbSyslogServer", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbActiveDirectory", + "parameter": "CaCertificate", + "wireName": "ca_certificate", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbActiveDirectory", + "parameter": "CaCertificateGroup", + "wireName": "ca_certificate_group", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbActiveDirectory", + "parameter": "DirectoryServers", + "wireName": "directory_servers", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbActiveDirectory", + "parameter": "Fqdns", + "wireName": "fqdns", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbActiveDirectory", + "parameter": "GlobalCatalogServers", + "wireName": "global_catalog_servers", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbActiveDirectory", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbActiveDirectory", + "parameter": "JoinOu", + "wireName": "join_ou", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbActiveDirectory", + "parameter": "KerberosServers", + "wireName": "kerberos_servers", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbActiveDirectory", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbActiveDirectory", + "parameter": "ServicePrincipalNames", + "wireName": "service_principal_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAdmin", + "parameter": "AuthorizationModel", + "wireName": "authorization_model", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAdmin", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAdmin", + "parameter": "Locked", + "wireName": "locked", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAdmin", + "parameter": "ManagementAccessPolicies", + "wireName": "management_access_policies", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAdmin", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAdmin", + "parameter": "OldPassword", + "wireName": "old_password", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAdmin", + "parameter": "Password", + "wireName": "password", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAdmin", + "parameter": "PublicKey", + "wireName": "public_key", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAlert", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAlert", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAlertWatcher", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAlertWatcher", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbApiClient", + "parameter": "Enabled", + "wireName": "enabled", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbApiClient", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbApiClient", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbArrayConnection", + "parameter": "CaCertificateGroup", + "wireName": "ca_certificate_group", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbArrayConnection", + "parameter": "Encrypted", + "wireName": "encrypted", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbArrayConnection", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbArrayConnection", + "parameter": "ManagementAddress", + "wireName": "management_address", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbArrayConnection", + "parameter": "Remote", + "wireName": "remote", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbArrayConnection", + "parameter": "RemoteId", + "wireName": "remote_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbArrayConnection", + "parameter": "RemoteName", + "wireName": "remote_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbArrayConnection", + "parameter": "ReplicationAddresses", + "wireName": "replication_addresses", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbArrayConnection", + "parameter": "Throttle", + "wireName": "throttle", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAsyncLog", + "parameter": "EndTime", + "wireName": "end_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAsyncLog", + "parameter": "HardwareComponents", + "wireName": "hardware_components", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAsyncLog", + "parameter": "StartTime", + "wireName": "start_time", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAuditFileSystemPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAuditFileSystemPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAuditObjectStorePolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbAuditObjectStorePolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbBucket", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbBucket", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbBucket", + "parameter": "QuotaLimit", + "wireName": "quota_limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbBucketAuditFilter", + "parameter": "Actions", + "wireName": "actions", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbBucketAuditFilter", + "parameter": "FilterNames", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbBucketAuditFilter", + "parameter": "MemberId", + "wireName": "bucket_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbBucketAuditFilter", + "parameter": "MemberName", + "wireName": "bucket_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbBucketAuditFilter", + "parameter": "S3Prefixes", + "wireName": "s3_prefixes", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "Certificate", + "wireName": "certificate", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "CommonName", + "wireName": "common_name", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "Country", + "wireName": "country", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "Days", + "wireName": "days", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "Email", + "wireName": "email", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "GenerateNewKey", + "wireName": "generate_new_key", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "IntermediateCertificate", + "wireName": "intermediate_certificate", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "KeyAlgorithm", + "wireName": "key_algorithm", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "KeySize", + "wireName": "key_size", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "Locality", + "wireName": "locality", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "Organization", + "wireName": "organization", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "OrganizationalUnit", + "wireName": "organizational_unit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "Passphrase", + "wireName": "passphrase", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "PrivateKey", + "wireName": "private_key", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "State", + "wireName": "state", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbCertificate", + "parameter": "SubjectAlternativeNames", + "wireName": "subject_alternative_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbDataEvictionPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbDataEvictionPolicy", + "parameter": "KeepSize", + "wireName": "keep_size", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbDataEvictionPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbDataEvictionPolicy", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbDirectoryServiceRole", + "parameter": "Group", + "wireName": "group", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbDirectoryServiceRole", + "parameter": "GroupBase", + "wireName": "group_base", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbDirectoryServiceRole", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbDirectoryServiceRole", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbDns", + "parameter": "CaCertificate", + "wireName": "ca_certificate", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbDns", + "parameter": "CaCertificateGroup", + "wireName": "ca_certificate_group", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbDns", + "parameter": "Domain", + "wireName": "domain", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbDns", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbDns", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbDns", + "parameter": "Nameservers", + "wireName": "nameservers", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbDns", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbDns", + "parameter": "Services", + "wireName": "services", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbDns", + "parameter": "Sources", + "wireName": "sources", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbFileSystem", + "parameter": "DiscardNonSnapshottedData", + "wireName": "discard_non_snapshotted_data", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbFileSystem", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbFileSystem", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbFileSystem", + "parameter": "Provisioned", + "wireName": "provisioned", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbFileSystemExport", + "parameter": "ExportName", + "wireName": "export_name", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbFileSystemExport", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbFileSystemExport", + "parameter": "Member", + "wireName": "member", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbFileSystemExport", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbFileSystemExport", + "parameter": "Policy", + "wireName": "policy", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbFileSystemExport", + "parameter": "Server", + "wireName": "server", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbFileSystemExport", + "parameter": "SharePolicy", + "wireName": "share_policy", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbFleet", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbFleet", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbFleet", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbHardware", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbHardware", + "parameter": "IdentifyEnabled", + "wireName": "identify_enabled", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbHardware", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbHardwareConnector", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbHardwareConnector", + "parameter": "LaneSpeed", + "wireName": "lane_speed", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbHardwareConnector", + "parameter": "LanesPerPort", + "wireName": "lanes_per_port", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbHardwareConnector", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbHardwareConnector", + "parameter": "PortCount", + "wireName": "port_count", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbHardwareConnector", + "parameter": "PortSpeed", + "wireName": "port_speed", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbKmip", + "parameter": "CaCertificate", + "wireName": "ca_certificate", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbKmip", + "parameter": "CaCertificateGroup", + "wireName": "ca_certificate_group", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbKmip", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbKmip", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbKmip", + "parameter": "Uris", + "wireName": "uris", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLag", + "parameter": "AddPorts", + "wireName": "add_ports", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLag", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLag", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLag", + "parameter": "Ports", + "wireName": "ports", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLag", + "parameter": "RemovePorts", + "wireName": "remove_ports", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLegalHold", + "parameter": "Description", + "wireName": "description", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLegalHold", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLegalHold", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLegalHoldEntity", + "parameter": "FileSystemIds", + "wireName": "file_system_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLegalHoldEntity", + "parameter": "FileSystemNames", + "wireName": "file_system_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLegalHoldEntity", + "parameter": "Ids", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLegalHoldEntity", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLegalHoldEntity", + "parameter": "Paths", + "wireName": "paths", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLegalHoldEntity", + "parameter": "Recursive", + "wireName": "recursive", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLegalHoldEntity", + "parameter": "Released", + "wireName": "released", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLifecycleRule", + "parameter": "AbortIncompleteMultipartUploadsAfter", + "wireName": "abort_incomplete_multipart_uploads_after", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLifecycleRule", + "parameter": "BucketIds", + "wireName": "bucket_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLifecycleRule", + "parameter": "BucketNames", + "wireName": "bucket_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLifecycleRule", + "parameter": "ConfirmDate", + "wireName": "confirm_date", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLifecycleRule", + "parameter": "Enabled", + "wireName": "enabled", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLifecycleRule", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLifecycleRule", + "parameter": "KeepCurrentVersionFor", + "wireName": "keep_current_version_for", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLifecycleRule", + "parameter": "KeepCurrentVersionUntil", + "wireName": "keep_current_version_until", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLifecycleRule", + "parameter": "KeepPreviousVersionFor", + "wireName": "keep_previous_version_for", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLifecycleRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLifecycleRule", + "parameter": "Prefix", + "wireName": "prefix", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLogTargetFileSystem", + "parameter": "FileSystem", + "wireName": "file_system", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLogTargetFileSystem", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLogTargetFileSystem", + "parameter": "KeepFor", + "wireName": "keep_for", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLogTargetFileSystem", + "parameter": "KeepSize", + "wireName": "keep_size", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLogTargetFileSystem", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLogTargetFileSystem", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLogTargetObjectStore", + "parameter": "Bucket", + "wireName": "bucket", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLogTargetObjectStore", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLogTargetObjectStore", + "parameter": "LogNamePrefix", + "wireName": "log_name_prefix", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLogTargetObjectStore", + "parameter": "LogRotate", + "wireName": "log_rotate", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLogTargetObjectStore", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbLogTargetObjectStore", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbManagementAccessPolicy", + "parameter": "AggregationStrategy", + "wireName": "aggregation_strategy", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbManagementAccessPolicy", + "parameter": "Enabled", + "wireName": "enabled", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbManagementAccessPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbManagementAccessPolicy", + "parameter": "Location", + "wireName": "location", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbManagementAccessPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbManagementAccessPolicy", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbManagementAccessPolicy", + "parameter": "Rules", + "wireName": "rules", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNetworkAccessPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNetworkAccessPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNetworkAccessRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNetworkInterface", + "parameter": "Address", + "wireName": "address", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNetworkInterface", + "parameter": "AttachedServers", + "wireName": "attached_servers", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNetworkInterface", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNetworkInterface", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNetworkInterface", + "parameter": "RdmaEnabled", + "wireName": "rdma_enabled", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNetworkInterface", + "parameter": "Services", + "wireName": "services", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNetworkInterfaceConnector", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNetworkInterfaceConnector", + "parameter": "LaneSpeed", + "wireName": "lane_speed", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNetworkInterfaceConnector", + "parameter": "LanesPerPort", + "wireName": "lanes_per_port", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNetworkInterfaceConnector", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNetworkInterfaceConnector", + "parameter": "PortCount", + "wireName": "port_count", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNetworkInterfaceConnector", + "parameter": "PortSpeed", + "wireName": "port_speed", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNfsExportPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNfsExportPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNfsExportRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNode", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNode", + "parameter": "ManagementAddress", + "wireName": "management_address", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNode", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNode", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNode", + "parameter": "NodeKey", + "wireName": "node_key", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNode", + "parameter": "SerialNumber", + "wireName": "serial_number", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNodeGroup", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNodeGroup", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbNodeGroup", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreAccessPolicyRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreAccountExport", + "parameter": "ExportEnabled", + "wireName": "export_enabled", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreAccountExport", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreAccountExport", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreAccountExport", + "parameter": "Policy", + "wireName": "policy", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreRemoteCredential", + "parameter": "AccessKeyId", + "wireName": "access_key_id", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreRemoteCredential", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreRemoteCredential", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreRemoteCredential", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreRemoteCredential", + "parameter": "Remote", + "wireName": "remote", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreRemoteCredential", + "parameter": "SecretAccessKey", + "wireName": "secret_access_key", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreRole", + "parameter": "Account", + "wireName": "account", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreRole", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreRole", + "parameter": "MaxSessionDuration", + "wireName": "max_session_duration", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreRole", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreTrustPolicyRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreVirtualHost", + "parameter": "AddAttachedServers", + "wireName": "add_attached_servers", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreVirtualHost", + "parameter": "AttachedServers", + "wireName": "attached_servers", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreVirtualHost", + "parameter": "Hostname", + "wireName": "hostname", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreVirtualHost", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreVirtualHost", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreVirtualHost", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbObjectStoreVirtualHost", + "parameter": "RemoveAttachedServers", + "wireName": "remove_attached_servers", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbOidcIdp", + "parameter": "Enabled", + "wireName": "enabled", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbOidcIdp", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbOidcIdp", + "parameter": "Idp", + "wireName": "idp", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbOidcIdp", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbOidcIdp", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbOidcIdp", + "parameter": "Services", + "wireName": "services", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbPolicy", + "parameter": "Rules", + "wireName": "rules", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbPresetWorkload", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbPresetWorkload", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbPresetWorkload", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbQosPolicy", + "parameter": "Enabled", + "wireName": "enabled", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbQosPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbQosPolicy", + "parameter": "Location", + "wireName": "location", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbQosPolicy", + "parameter": "MaxTotalBytesPerSec", + "wireName": "max_total_bytes_per_sec", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbQosPolicy", + "parameter": "MaxTotalOpsPerSec", + "wireName": "max_total_ops_per_sec", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbQosPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbQosPolicy", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbQuotaGroup", + "parameter": "Quota", + "wireName": "quota", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbQuotaUser", + "parameter": "Quota", + "wireName": "quota", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbRealm", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbRealm", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbRealmDefaults", + "parameter": "Id", + "wireName": "realm_ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbRealmDefaults", + "parameter": "Name", + "wireName": "realm_names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbRealmDefaults", + "parameter": "ObjectStore", + "wireName": "object_store", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbS3ExportPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbS3ExportPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbS3ExportRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSaml2Idp", + "parameter": "ArrayUrl", + "wireName": "array_url", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSaml2Idp", + "parameter": "Binding", + "wireName": "binding", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSaml2Idp", + "parameter": "Enabled", + "wireName": "enabled", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSaml2Idp", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSaml2Idp", + "parameter": "Idp", + "wireName": "idp", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSaml2Idp", + "parameter": "Management", + "wireName": "management", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSaml2Idp", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSaml2Idp", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSaml2Idp", + "parameter": "Services", + "wireName": "services", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSaml2Idp", + "parameter": "Sp", + "wireName": "sp", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbServer", + "parameter": "DnsName", + "wireName": "dns", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbServer", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbServer", + "parameter": "LocalDirectoryService", + "wireName": "local_directory_service", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbServer", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSmbClientPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSmbClientPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSmbClientRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSmbSharePolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSmbSharePolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSmbShareRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSmtp", + "parameter": "RelayHost", + "wireName": "relay_host", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSmtp", + "parameter": "SenderDomain", + "wireName": "sender_domain", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSnmpManager", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSnmpManager", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSnmpManager", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSnmpManager", + "parameter": "SnmpHost", + "wireName": "host", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSnmpManager", + "parameter": "V2c", + "wireName": "v2c", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSnmpManager", + "parameter": "V3", + "wireName": "v3", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSshCaPolicy", + "parameter": "Enabled", + "wireName": "enabled", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSshCaPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSshCaPolicy", + "parameter": "Location", + "wireName": "location", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSshCaPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSshCaPolicy", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSshCaPolicy", + "parameter": "SigningAuthority", + "wireName": "signing_authority", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSshCaPolicy", + "parameter": "StaticAuthorizedPrincipals", + "wireName": "static_authorized_principals", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbStorageClassTieringPolicy", + "parameter": "ArchivalRules", + "wireName": "archival_rules", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbStorageClassTieringPolicy", + "parameter": "Enabled", + "wireName": "enabled", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbStorageClassTieringPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbStorageClassTieringPolicy", + "parameter": "Location", + "wireName": "location", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbStorageClassTieringPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbStorageClassTieringPolicy", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbStorageClassTieringPolicy", + "parameter": "RetrievalRules", + "wireName": "retrieval_rules", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSubnet", + "parameter": "Gateway", + "wireName": "gateway", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSubnet", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSubnet", + "parameter": "LinkAggregationGroup", + "wireName": "link_aggregation_group", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSubnet", + "parameter": "Mtu", + "wireName": "mtu", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSubnet", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSubnet", + "parameter": "Prefix", + "wireName": "prefix", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSubnet", + "parameter": "Vlan", + "wireName": "vlan", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSyslogServer", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSyslogServer", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSyslogServer", + "parameter": "Sources", + "wireName": "sources", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbSyslogServer", + "parameter": "Uri", + "wireName": "uri", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbTarget", + "parameter": "Address", + "wireName": "address", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbTarget", + "parameter": "CaCertificateGroup", + "wireName": "ca_certificate_group", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbTarget", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbTarget", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbTarget", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbTlsPolicy", + "parameter": "ApplianceCertificate", + "wireName": "appliance_certificate", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbTlsPolicy", + "parameter": "ClientCertificatesRequired", + "wireName": "client_certificates_required", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbTlsPolicy", + "parameter": "DisabledTlsCiphers", + "wireName": "disabled_tls_ciphers", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbTlsPolicy", + "parameter": "Enabled", + "wireName": "enabled", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbTlsPolicy", + "parameter": "EnabledTlsCiphers", + "wireName": "enabled_tls_ciphers", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbTlsPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbTlsPolicy", + "parameter": "Location", + "wireName": "location", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbTlsPolicy", + "parameter": "MinTlsVersion", + "wireName": "min_tls_version", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbTlsPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbTlsPolicy", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbTlsPolicy", + "parameter": "TrustedClientCertificateAuthority", + "wireName": "trusted_client_certificate_authority", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbTlsPolicy", + "parameter": "VerifyClientCertificateTrust", + "wireName": "verify_client_certificate_trust", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbUserGroupQuotaPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbUserGroupQuotaPolicy", + "parameter": "IgnoreUsage", + "wireName": "ignore_usage", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbUserGroupQuotaPolicy", + "parameter": "Location", + "wireName": "location", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbUserGroupQuotaPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbUserGroupQuotaPolicy", + "parameter": "Rules", + "wireName": "rules", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbUserGroupQuotaPolicy", + "parameter": "Version", + "wireName": "versions", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbUserGroupQuotaPolicyRule", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbUserGroupQuotaPolicyRule", + "parameter": "IgnoreUsage", + "wireName": "ignore_usage", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbUserGroupQuotaPolicyRule", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbUserGroupQuotaPolicyRule", + "parameter": "QuotaLimit", + "wireName": "quota_limit", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbWorkload", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbWorkload", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbWorkload", + "parameter": "NewName", + "wireName": "name", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbWormPolicy", + "parameter": "DefaultRetention", + "wireName": "default_retention", + "status": "not-found-in-resource", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbWormPolicy", + "parameter": "Enabled", + "wireName": "enabled", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbWormPolicy", + "parameter": "Id", + "wireName": "ids", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbWormPolicy", + "parameter": "Location", + "wireName": "location", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbWormPolicy", + "parameter": "MaxRetention", + "wireName": "max_retention", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbWormPolicy", + "parameter": "MinRetention", + "wireName": "min_retention", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbWormPolicy", + "parameter": "Mode", + "wireName": "mode", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + }, + { + "cmdlet": "Update-PfbWormPolicy", + "parameter": "Name", + "wireName": "names", + "status": "no-spec-enum-found", + "matchedKey": null, + "specValues": null, + "stableSinceOldestVersion": null, + "recommendation": null + } + ], + "attributesOnly": [ + { + "cmdlet": "New-PfbActiveDirectory", + "parameter": "Name" + }, + { + "cmdlet": "New-PfbAuditFileSystemPolicy", + "parameter": "Enabled" + }, + { + "cmdlet": "New-PfbAuditObjectStorePolicy", + "parameter": "Enabled" + }, + { + "cmdlet": "New-PfbCertificateSigningRequest", + "parameter": "Name" + }, + { + "cmdlet": "New-PfbDns", + "parameter": "Name" + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "FastRemoveDirectoryEnabled" + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "HardLimit" + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "Http" + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "MultiProtocolAccessControlStyle" + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "Nfs" + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "NfsExportPolicy" + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "NfsRules" + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "NfsV3" + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "NfsV41" + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "SafeguardAcls" + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "Smb" + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "SmbClientPolicy" + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "SmbSharePolicy" + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "SnapshotDirectoryEnabled" + }, + { + "cmdlet": "New-PfbFileSystem", + "parameter": "Writable" + }, + { + "cmdlet": "New-PfbFleet", + "parameter": "Name" + }, + { + "cmdlet": "New-PfbLag", + "parameter": "Name" + }, + { + "cmdlet": "New-PfbNfsExportPolicy", + "parameter": "Enabled" + }, + { + "cmdlet": "New-PfbNodeGroup", + "parameter": "Name" + }, + { + "cmdlet": "New-PfbPolicy", + "parameter": "Enabled" + }, + { + "cmdlet": "New-PfbQuotaUser", + "parameter": "FileSystemName" + }, + { + "cmdlet": "New-PfbQuotaUser", + "parameter": "UserId" + }, + { + "cmdlet": "New-PfbQuotaUser", + "parameter": "UserName" + }, + { + "cmdlet": "New-PfbS3ExportPolicy", + "parameter": "Enabled" + }, + { + "cmdlet": "New-PfbServer", + "parameter": "CreateDirectoryService" + }, + { + "cmdlet": "New-PfbSmbClientPolicy", + "parameter": "Enabled" + }, + { + "cmdlet": "New-PfbSmbSharePolicy", + "parameter": "Enabled" + }, + { + "cmdlet": "New-PfbSnmpManager", + "parameter": "Name" + }, + { + "cmdlet": "New-PfbSshCaPolicy", + "parameter": "Name" + }, + { + "cmdlet": "New-PfbStorageClassTieringPolicy", + "parameter": "Name" + }, + { + "cmdlet": "New-PfbSyslogServer", + "parameter": "Name" + }, + { + "cmdlet": "New-PfbTarget", + "parameter": "Name" + }, + { + "cmdlet": "New-PfbTlsPolicy", + "parameter": "Name" + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicy", + "parameter": "Enabled" + }, + { + "cmdlet": "New-PfbUserGroupQuotaPolicyRule", + "parameter": "Enforced" + }, + { + "cmdlet": "New-PfbWormPolicy", + "parameter": "Name" + }, + { + "cmdlet": "Update-PfbAlert", + "parameter": "Flagged" + }, + { + "cmdlet": "Update-PfbAlertWatcher", + "parameter": "Enabled" + }, + { + "cmdlet": "Update-PfbAuditFileSystemPolicy", + "parameter": "Enabled" + }, + { + "cmdlet": "Update-PfbAuditObjectStorePolicy", + "parameter": "Enabled" + }, + { + "cmdlet": "Update-PfbBucket", + "parameter": "Destroyed" + }, + { + "cmdlet": "Update-PfbDirectoryService", + "parameter": "Name" + }, + { + "cmdlet": "Update-PfbFileSystem", + "parameter": "Destroyed" + }, + { + "cmdlet": "Update-PfbFileSystem", + "parameter": "HardLimitEnabled" + }, + { + "cmdlet": "Update-PfbFileSystem", + "parameter": "HttpEnabled" + }, + { + "cmdlet": "Update-PfbFileSystem", + "parameter": "NfsEnabled" + }, + { + "cmdlet": "Update-PfbFileSystem", + "parameter": "NfsExportPolicy" + }, + { + "cmdlet": "Update-PfbFileSystem", + "parameter": "NfsRules" + }, + { + "cmdlet": "Update-PfbFileSystem", + "parameter": "SmbClientPolicy" + }, + { + "cmdlet": "Update-PfbFileSystem", + "parameter": "SmbEnabled" + }, + { + "cmdlet": "Update-PfbFileSystem", + "parameter": "SmbSharePolicy" + }, + { + "cmdlet": "Update-PfbNetworkAccessPolicy", + "parameter": "Enabled" + }, + { + "cmdlet": "Update-PfbNfsExportPolicy", + "parameter": "Enabled" + }, + { + "cmdlet": "Update-PfbPolicy", + "parameter": "Enabled" + }, + { + "cmdlet": "Update-PfbQuotaGroup", + "parameter": "FileSystemName" + }, + { + "cmdlet": "Update-PfbQuotaGroup", + "parameter": "GroupName" + }, + { + "cmdlet": "Update-PfbQuotaUser", + "parameter": "FileSystemName" + }, + { + "cmdlet": "Update-PfbQuotaUser", + "parameter": "UserName" + }, + { + "cmdlet": "Update-PfbRealm", + "parameter": "Destroyed" + }, + { + "cmdlet": "Update-PfbS3ExportPolicy", + "parameter": "Enabled" + }, + { + "cmdlet": "Update-PfbSmbClientPolicy", + "parameter": "Enabled" + }, + { + "cmdlet": "Update-PfbSmbSharePolicy", + "parameter": "Enabled" + }, + { + "cmdlet": "Update-PfbUserGroupQuotaPolicy", + "parameter": "Enabled" + } + ], + "typedUnresolved": [ + { + "cmdlet": "Connect-PfbArray", + "parameter": "ApiToken" + }, + { + "cmdlet": "Connect-PfbArray", + "parameter": "ApiVersion" + }, + { + "cmdlet": "Connect-PfbArray", + "parameter": "ClientId" + }, + { + "cmdlet": "Connect-PfbArray", + "parameter": "Credential" + }, + { + "cmdlet": "Connect-PfbArray", + "parameter": "Endpoint" + }, + { + "cmdlet": "Connect-PfbArray", + "parameter": "HttpTimeout" + }, + { + "cmdlet": "Connect-PfbArray", + "parameter": "IgnoreCertificateError" + }, + { + "cmdlet": "Connect-PfbArray", + "parameter": "Issuer" + }, + { + "cmdlet": "Connect-PfbArray", + "parameter": "KeyId" + }, + { + "cmdlet": "Connect-PfbArray", + "parameter": "Password" + }, + { + "cmdlet": "Connect-PfbArray", + "parameter": "PrivateKeyFile" + }, + { + "cmdlet": "Connect-PfbArray", + "parameter": "PrivateKeyPassword" + }, + { + "cmdlet": "Connect-PfbArray", + "parameter": "Username" + }, + { + "cmdlet": "Get-PfbAlert", + "parameter": "Flagged" + }, + { + "cmdlet": "Get-PfbApiVersion", + "parameter": "Endpoint" + }, + { + "cmdlet": "Get-PfbApiVersion", + "parameter": "IgnoreCertificateError" + }, + { + "cmdlet": "Get-PfbConnection", + "parameter": "Endpoint" + }, + { + "cmdlet": "Get-PfbHardwareTemperature", + "parameter": "Limit" + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicy", + "parameter": "Id" + }, + { + "cmdlet": "Get-PfbUserGroupQuotaPolicy", + "parameter": "Name" + }, + { + "cmdlet": "New-PfbDataEvictionPolicy", + "parameter": "Disabled" + }, + { + "cmdlet": "New-PfbFileSystemSnapshot", + "parameter": "SourceName" + }, + { + "cmdlet": "New-PfbLocalGroupMember", + "parameter": "Member" + }, + { + "cmdlet": "New-PfbWorkloadPlacementRecommendation", + "parameter": "Inputs" + }, + { + "cmdlet": "Remove-PfbBucket", + "parameter": "Eradicate" + }, + { + "cmdlet": "Remove-PfbFileSystem", + "parameter": "Eradicate" + }, + { + "cmdlet": "Remove-PfbFileSystemSession", + "parameter": "Force" + }, + { + "cmdlet": "Remove-PfbFileSystemSnapshot", + "parameter": "Eradicate" + }, + { + "cmdlet": "Remove-PfbQuotaGroup", + "parameter": "FileSystemName" + }, + { + "cmdlet": "Remove-PfbQuotaGroup", + "parameter": "GroupName" + }, + { + "cmdlet": "Remove-PfbQuotaUser", + "parameter": "FileSystemName" + }, + { + "cmdlet": "Remove-PfbQuotaUser", + "parameter": "UserName" + }, + { + "cmdlet": "Remove-PfbRealm", + "parameter": "Eradicate" + }, + { + "cmdlet": "Remove-PfbServer", + "parameter": "Eradicate" + }, + { + "cmdlet": "Set-PfbCredential", + "parameter": "Credential" + }, + { + "cmdlet": "Set-PfbWorkloadTag", + "parameter": "Tags" + }, + { + "cmdlet": "Test-PfbConnection", + "parameter": "Endpoint" + }, + { + "cmdlet": "Update-PfbDataEvictionPolicy", + "parameter": "Enabled" + }, + { + "cmdlet": "Update-PfbWorkload", + "parameter": "Destroyed" + } + ] +} diff --git a/Reports/PfbFieldCmdletMapping.md b/Reports/PfbFieldCmdletMapping.md new file mode 100644 index 0000000..86be9c2 --- /dev/null +++ b/Reports/PfbFieldCmdletMapping.md @@ -0,0 +1,160 @@ +# Field-to-Cmdlet Mapping Report + +Generated by `tools/Build-PfbFieldCmdletMap.ps1` (29 REST versions). + +Reporting only -- no `Public/` cmdlet is edited by this script. Every `matched` row below is a candidate for a follow-up decision, not an action already taken. + +## Summary + +- matched: 1 +- collision: 1 +- not-found-in-resource: 29 +- no-spec-enum-found: 1981 + +| Cmdlet | Parameter | Wire name | Status | Spec values | Recommendation | +|---|---|---|---|---|---| +| `Get-PfbPolicyAllMember` | `-MemberType` | member_types | matched | file-systems, file-system-snapshots, file-system-replica-links, object-store-users | ArgumentCompleter | +| `New-PfbQosPolicyMember` | `-MemberType` | member_types | collision | | | +| `New-PfbS3ExportRule` | `-Actions` | actions | not-found-in-resource | | | +| `New-PfbS3ExportRule` | `-Effect` | effect | not-found-in-resource | | | +| `Update-PfbBucketAuditFilter` | `-Actions` | actions | not-found-in-resource | | | +| `Update-PfbCertificate` | `-State` | state | not-found-in-resource | | | +| `Update-PfbDataEvictionPolicy` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbDns` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbDns` | `-Services` | services | not-found-in-resource | | | +| `Update-PfbFleet` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbLogTargetFileSystem` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbLogTargetObjectStore` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbManagementAccessPolicy` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbNetworkInterface` | `-Services` | services | not-found-in-resource | | | +| `Update-PfbNode` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbNodeGroup` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbObjectStoreRemoteCredential` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbObjectStoreVirtualHost` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbOidcIdp` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbOidcIdp` | `-Services` | services | not-found-in-resource | | | +| `Update-PfbPresetWorkload` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbQosPolicy` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbSaml2Idp` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbSaml2Idp` | `-Services` | services | not-found-in-resource | | | +| `Update-PfbSnmpManager` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbSshCaPolicy` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbStorageClassTieringPolicy` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbTarget` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbTlsPolicy` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbWorkload` | `-NewName` | name | not-found-in-resource | | | +| `Update-PfbWormPolicy` | `-DefaultRetention` | default_retention | not-found-in-resource | | | + +## Attributes-only parameters (no typed field to attach either mechanism to): 68 + +- `New-PfbActiveDirectory -Name` +- `New-PfbAuditFileSystemPolicy -Enabled` +- `New-PfbAuditObjectStorePolicy -Enabled` +- `New-PfbCertificateSigningRequest -Name` +- `New-PfbDns -Name` +- `New-PfbFileSystem -FastRemoveDirectoryEnabled` +- `New-PfbFileSystem -HardLimit` +- `New-PfbFileSystem -Http` +- `New-PfbFileSystem -MultiProtocolAccessControlStyle` +- `New-PfbFileSystem -Nfs` +- `New-PfbFileSystem -NfsExportPolicy` +- `New-PfbFileSystem -NfsRules` +- `New-PfbFileSystem -NfsV3` +- `New-PfbFileSystem -NfsV41` +- `New-PfbFileSystem -SafeguardAcls` +- `New-PfbFileSystem -Smb` +- `New-PfbFileSystem -SmbClientPolicy` +- `New-PfbFileSystem -SmbSharePolicy` +- `New-PfbFileSystem -SnapshotDirectoryEnabled` +- `New-PfbFileSystem -Writable` +- `New-PfbFleet -Name` +- `New-PfbLag -Name` +- `New-PfbNfsExportPolicy -Enabled` +- `New-PfbNodeGroup -Name` +- `New-PfbPolicy -Enabled` +- `New-PfbQuotaUser -FileSystemName` +- `New-PfbQuotaUser -UserId` +- `New-PfbQuotaUser -UserName` +- `New-PfbS3ExportPolicy -Enabled` +- `New-PfbServer -CreateDirectoryService` +- `New-PfbSmbClientPolicy -Enabled` +- `New-PfbSmbSharePolicy -Enabled` +- `New-PfbSnmpManager -Name` +- `New-PfbSshCaPolicy -Name` +- `New-PfbStorageClassTieringPolicy -Name` +- `New-PfbSyslogServer -Name` +- `New-PfbTarget -Name` +- `New-PfbTlsPolicy -Name` +- `New-PfbUserGroupQuotaPolicy -Enabled` +- `New-PfbUserGroupQuotaPolicyRule -Enforced` +- `New-PfbWormPolicy -Name` +- `Update-PfbAlert -Flagged` +- `Update-PfbAlertWatcher -Enabled` +- `Update-PfbAuditFileSystemPolicy -Enabled` +- `Update-PfbAuditObjectStorePolicy -Enabled` +- `Update-PfbBucket -Destroyed` +- `Update-PfbDirectoryService -Name` +- `Update-PfbFileSystem -Destroyed` +- `Update-PfbFileSystem -HardLimitEnabled` +- `Update-PfbFileSystem -HttpEnabled` +- `Update-PfbFileSystem -NfsEnabled` +- `Update-PfbFileSystem -NfsExportPolicy` +- `Update-PfbFileSystem -NfsRules` +- `Update-PfbFileSystem -SmbClientPolicy` +- `Update-PfbFileSystem -SmbEnabled` +- `Update-PfbFileSystem -SmbSharePolicy` +- `Update-PfbNetworkAccessPolicy -Enabled` +- `Update-PfbNfsExportPolicy -Enabled` +- `Update-PfbPolicy -Enabled` +- `Update-PfbQuotaGroup -FileSystemName` +- `Update-PfbQuotaGroup -GroupName` +- `Update-PfbQuotaUser -FileSystemName` +- `Update-PfbQuotaUser -UserName` +- `Update-PfbRealm -Destroyed` +- `Update-PfbS3ExportPolicy -Enabled` +- `Update-PfbSmbClientPolicy -Enabled` +- `Update-PfbSmbSharePolicy -Enabled` +- `Update-PfbUserGroupQuotaPolicy -Enabled` + +## Typed but unresolved wire name (needs manual inspection): 39 + +- `Connect-PfbArray -ApiToken` +- `Connect-PfbArray -ApiVersion` +- `Connect-PfbArray -ClientId` +- `Connect-PfbArray -Credential` +- `Connect-PfbArray -Endpoint` +- `Connect-PfbArray -HttpTimeout` +- `Connect-PfbArray -IgnoreCertificateError` +- `Connect-PfbArray -Issuer` +- `Connect-PfbArray -KeyId` +- `Connect-PfbArray -Password` +- `Connect-PfbArray -PrivateKeyFile` +- `Connect-PfbArray -PrivateKeyPassword` +- `Connect-PfbArray -Username` +- `Get-PfbAlert -Flagged` +- `Get-PfbApiVersion -Endpoint` +- `Get-PfbApiVersion -IgnoreCertificateError` +- `Get-PfbConnection -Endpoint` +- `Get-PfbHardwareTemperature -Limit` +- `Get-PfbUserGroupQuotaPolicy -Id` +- `Get-PfbUserGroupQuotaPolicy -Name` +- `New-PfbDataEvictionPolicy -Disabled` +- `New-PfbFileSystemSnapshot -SourceName` +- `New-PfbLocalGroupMember -Member` +- `New-PfbWorkloadPlacementRecommendation -Inputs` +- `Remove-PfbBucket -Eradicate` +- `Remove-PfbFileSystem -Eradicate` +- `Remove-PfbFileSystemSession -Force` +- `Remove-PfbFileSystemSnapshot -Eradicate` +- `Remove-PfbQuotaGroup -FileSystemName` +- `Remove-PfbQuotaGroup -GroupName` +- `Remove-PfbQuotaUser -FileSystemName` +- `Remove-PfbQuotaUser -UserName` +- `Remove-PfbRealm -Eradicate` +- `Remove-PfbServer -Eradicate` +- `Set-PfbCredential -Credential` +- `Set-PfbWorkloadTag -Tags` +- `Test-PfbConnection -Endpoint` +- `Update-PfbDataEvictionPolicy -Enabled` +- `Update-PfbWorkload -Destroyed` + diff --git a/Reports/PfbValueEnumMap.json b/Reports/PfbValueEnumMap.json new file mode 100644 index 0000000..d2b73a7 --- /dev/null +++ b/Reports/PfbValueEnumMap.json @@ -0,0 +1,4434 @@ +{ + "schemaVersion": 1, + "generatedFrom": [ + "2.0", + "2.1", + "2.2", + "2.3", + "2.4", + "2.5", + "2.6", + "2.7", + "2.8", + "2.9", + "2.10", + "2.11", + "2.12", + "2.13", + "2.14", + "2.15", + "2.16", + "2.17", + "2.18", + "2.19", + "2.20", + "2.21", + "2.22", + "2.23", + "2.24", + "2.25", + "2.26", + "2.27", + "2.28" + ], + "entryCount": 301, + "unparsedCount": 127, + "entries": { + "Alert.severity": { + "values": [ + "info", + "warning", + "critical" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "severity" + }, + "Alert.state": { + "values": [ + "open", + "closing", + "closed", + "waiting to downgrade" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "state" + }, + "AlertWatcher.minimum_notification_severity": { + "values": [ + "info", + "warning", + "critical" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "minimum_notification_severity" + }, + "Array.os": { + "values": [ + "Purity//FA", + "Purity//FB" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "os" + }, + "ArrayConnection.status": { + "values": [ + "connected", + "partially_connected", + "connecting", + "incompatible" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "status" + }, + "ArrayConnectionPath.status": { + "values": [ + "connected", + "connecting" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "status" + }, + "Audit.user_interface": { + "values": [ + "CLI", + "GUI", + "REST" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "user_interface" + }, + "Blade.status": { + "values": [ + "critical", + "evacuated", + "evacuating", + "healthy", + "identifying", + "unhealthy", + "unknown", + "unused" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "status" + }, + "Bucket.versioning": { + "values": [ + "none", + "enabled", + "suspended" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "versioning" + }, + "BucketReplicaLink.direction": { + "values": [ + "inbound", + "outbound" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "direction" + }, + "Certificate.certificate_type": { + "values": [ + "appliance", + "external" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "certificate_type" + }, + "Certificate.status": { + "values": [ + "self-signed", + "imported" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "status" + }, + "FileSystem.promotion_status": { + "values": [ + "promoted", + "demoted", + "demoting", + "promoting" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "promotion_status" + }, + "FileSystem.requested_promotion_state": { + "values": [ + "promoted", + "demoted" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "requested_promotion_state" + }, + "FileSystemReplicaLink.direction": { + "values": [ + "inbound", + "outbound" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "direction" + }, + "FileSystemSnapshotTransfer.direction": { + "values": [ + "inbound", + "outbound" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "direction" + }, + "FileSystemSnapshotTransfer.status": { + "values": [ + "completed", + "in-progress", + "queued" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "status" + }, + "Hardware.status": { + "values": [ + "critical", + "healthy", + "identifying", + "unhealthy", + "unknown", + "unused" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "status" + }, + "Hardware.type": { + "values": [ + "bay", + "ch", + "eth", + "fan", + "fb", + "fm", + "pwr", + "xfm" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "type" + }, + "HardwareConnector.connector_type": { + "values": [ + "QSFP", + "QSFP+", + "QSFP28", + "QSFP56", + "QSFP-DD", + "RJ-45", + "-" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "connector_type" + }, + "LinkAggregationGroup.status": { + "values": [ + "critical", + "healthy", + "identifying", + "unclaimed", + "unhealthy", + "unrecognized", + "unused" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "status" + }, + "SnmpAgent.version": { + "values": [ + "v2c", + "v3" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "version" + }, + "SnmpManager.notification": { + "values": [ + "inform", + "trap" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "notification" + }, + "SnmpManager.version": { + "values": [ + "v2c", + "v3" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "version" + }, + "Support.remote_assist_status": { + "values": [ + "connected", + "partially_connected", + "reconnecting", + "disconnected" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "remote_assist_status" + }, + "Target.status": { + "values": [ + "connected", + "connecting" + ], + "minVersion": "2.0", + "kind": "schema", + "name": "status" + }, + "GET array-connections/performance/replication#type": { + "values": [ + "all", + "file-system", + "object-store" + ], + "minVersion": "2.0", + "kind": "inline-parameter", + "name": "type" + }, + "GET arrays/performance#protocol": { + "values": [ + "all", + "HTTP", + "SMB", + "NFS", + "S3" + ], + "minVersion": "2.0", + "kind": "inline-parameter", + "name": "protocol" + }, + "GET arrays/performance/replication#type": { + "values": [ + "all", + "file-system", + "object-store" + ], + "minVersion": "2.0", + "kind": "inline-parameter", + "name": "type" + }, + "GET arrays/space#type": { + "values": [ + "array", + "file-system", + "object-store" + ], + "minVersion": "2.0", + "kind": "inline-parameter", + "name": "type" + }, + "GET file-systems/performance#protocol": { + "values": [ + "all", + "HTTP", + "SMB", + "NFS", + "S3" + ], + "minVersion": "2.0", + "kind": "inline-parameter", + "name": "protocol" + }, + "GET support/test#test_type": { + "values": [ + "all", + "phonehome", + "remote-assist" + ], + "minVersion": "2.0", + "kind": "inline-parameter", + "name": "test_type" + }, + "Array.smb_mode": { + "values": [ + "ad-auto", + "ad-rfc2307", + "guest", + "native" + ], + "minVersion": "2.2", + "kind": "schema", + "name": "smb_mode" + }, + "ObjectStoreAccessPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.2", + "kind": "schema", + "name": "policy_type" + }, + "ObjectStoreAccessPolicyRule.effect": { + "values": [ + "allow" + ], + "minVersion": "2.2", + "kind": "schema", + "name": "effect" + }, + "Policy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.2", + "kind": "schema", + "name": "policy_type" + }, + "PolicyBase.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.2", + "kind": "schema", + "name": "policy_type" + }, + "GET policies/members#member_types": { + "values": [ + "file-systems", + "file-system-snapshots", + "file-system-replica-links", + "object-store-users" + ], + "minVersion": "2.2", + "kind": "inline-parameter", + "name": "member_types" + }, + "GET policies-all/members#member_types": { + "values": [ + "file-systems", + "file-system-snapshots", + "file-system-replica-links", + "object-store-users" + ], + "minVersion": "2.2", + "kind": "inline-parameter", + "name": "member_types" + }, + "NfsExportPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.3", + "kind": "schema", + "name": "policy_type" + }, + "NfsExportPolicyRule.access": { + "values": [ + "root-squash", + "all-squash", + "no-squash" + ], + "minVersion": "2.3", + "kind": "schema", + "name": "access" + }, + "NfsExportPolicyRule.permission": { + "values": [ + "rw", + "ro" + ], + "minVersion": "2.3", + "kind": "schema", + "name": "permission" + }, + "Session.event": { + "values": [ + "failed authentication", + "user session", + "login", + "logout", + "API token obtained", + "request without session" + ], + "minVersion": "2.3", + "kind": "schema", + "name": "event" + }, + "Session.method": { + "values": [ + "API token", + "password", + "ssh certificate", + "public key" + ], + "minVersion": "2.3", + "kind": "schema", + "name": "method" + }, + "Session.user_interface": { + "values": [ + "CLI", + "GUI", + "REST" + ], + "minVersion": "2.3", + "kind": "schema", + "name": "user_interface" + }, + "Drive.status": { + "values": [ + "evacuated", + "evacuating", + "healthy", + "unhealthy", + "unused", + "updating" + ], + "minVersion": "2.4", + "kind": "schema", + "name": "status" + }, + "Policy.retention_lock": { + "values": [ + "locked", + "unlocked" + ], + "minVersion": "2.5", + "kind": "schema", + "name": "retention_lock" + }, + "Array.product_type": { + "values": [ + "FlashBlade", + "FlashBlade//S", + "FlashBlade//S200", + "FlashBladeS500" + ], + "minVersion": "2.6", + "kind": "schema", + "name": "product_type" + }, + "GET network-interfaces/trace#method": { + "values": [ + "icmp", + "tcp", + "udp" + ], + "minVersion": "2.6", + "kind": "inline-parameter", + "name": "method" + }, + "VerificationKey.name": { + "values": [ + "access" + ], + "minVersion": "2.7", + "kind": "schema", + "name": "name" + }, + "Bucket.retention_lock": { + "values": [ + "unlocked", + "ratcheted" + ], + "minVersion": "2.8", + "kind": "schema", + "name": "retention_lock" + }, + "Drive.type": { + "values": [ + "DFM", + "DFMe" + ], + "minVersion": "2.9", + "kind": "schema", + "name": "type" + }, + "FileLock.access_type": { + "values": [ + "shared", + "exclusive", + "no-access" + ], + "minVersion": "2.10", + "kind": "schema", + "name": "access_type" + }, + "FileSession.authentication": { + "values": [ + "KRB", + "NTLMv2" + ], + "minVersion": "2.10", + "kind": "schema", + "name": "authentication" + }, + "FileSession.protocol": { + "values": [ + "nfs", + "smb" + ], + "minVersion": "2.10", + "kind": "schema", + "name": "protocol" + }, + "SmbClientPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.10", + "kind": "schema", + "name": "policy_type" + }, + "SmbClientPolicyRule.permission": { + "values": [ + "rw", + "ro" + ], + "minVersion": "2.10", + "kind": "schema", + "name": "permission" + }, + "SmbSharePolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.10", + "kind": "schema", + "name": "policy_type" + }, + "SmbSharePolicyRule.change": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.10", + "kind": "schema", + "name": "change" + }, + "SmbSharePolicyRule.full_control": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.10", + "kind": "schema", + "name": "full_control" + }, + "SmbSharePolicyRule.read": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.10", + "kind": "schema", + "name": "read" + }, + "GET file-systems/sessions#protocols": { + "values": [ + "nfs", + "smb" + ], + "minVersion": "2.10", + "kind": "inline-parameter", + "name": "protocols" + }, + "DELETE file-systems/sessions#protocols": { + "values": [ + "nfs", + "smb" + ], + "minVersion": "2.10", + "kind": "inline-parameter", + "name": "protocols" + }, + "SmbClientPolicyRule.encryption": { + "values": [ + "required", + "disabled", + "optional" + ], + "minVersion": "2.11", + "kind": "schema", + "name": "encryption" + }, + "Bucket.public_status": { + "values": [ + "bucket-and-objects-not-public", + "only-authorized-users-of-this-account", + "public" + ], + "minVersion": "2.12", + "kind": "schema", + "name": "public_status" + }, + "BucketAccessPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.12", + "kind": "schema", + "name": "policy_type" + }, + "CrossOriginResourceSharingPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.12", + "kind": "schema", + "name": "policy_type" + }, + "FileSystem.group_ownership": { + "values": [ + "creator", + "parent-directory" + ], + "minVersion": "2.13", + "kind": "schema", + "name": "group_ownership" + }, + "NetworkAccessPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.13", + "kind": "schema", + "name": "policy_type" + }, + "NetworkAccessPolicyRule.effect": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.13", + "kind": "schema", + "name": "effect" + }, + "NetworkAccessPolicyRule.interfaces": { + "values": [ + "management-ssh", + "management-rest-api", + "management-web-ui", + "snmp", + "local-network-superuser-password-access" + ], + "minVersion": "2.13", + "kind": "schema", + "name": "interfaces" + }, + "AuditFileSystemsPolicies.policy_type": { + "values": [ + "nfs", + "object-access", + "smb-client", + "smb-share", + "snapshot" + ], + "minVersion": "2.14", + "kind": "schema", + "name": "policy_type" + }, + "PublicKey.algorithm": { + "values": [ + "rsa", + "rsassa-pss", + "dsa", + "ec", + "ed25519" + ], + "minVersion": "2.14", + "kind": "schema", + "name": "algorithm" + }, + "SshCertificateAuthorityPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.14", + "kind": "schema", + "name": "policy_type" + }, + "SyslogServer.services": { + "values": [ + "data-audit", + "management" + ], + "minVersion": "2.14", + "kind": "schema", + "name": "services" + }, + "SMTP.encryption_mode": { + "values": [ + "starttls" + ], + "minVersion": "2.15", + "kind": "schema", + "name": "encryption_mode" + }, + "WormDataPolicy.policy_type": { + "values": [ + "snapshot", + "worm-data" + ], + "minVersion": "2.15", + "kind": "schema", + "name": "policy_type" + }, + "WormDataPolicy.retention_lock": { + "values": [ + "unlocked", + "locked" + ], + "minVersion": "2.15", + "kind": "schema", + "name": "retention_lock" + }, + "FileSystemExport.status": { + "values": [ + "policy_disabled", + "protocol_disabled" + ], + "minVersion": "2.16", + "kind": "schema", + "name": "status" + }, + "PasswordPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.16", + "kind": "schema", + "name": "policy_type" + }, + "SoftwareChecks.status": { + "values": [ + "queued", + "running", + "passed", + "failed" + ], + "minVersion": "2.16", + "kind": "schema", + "name": "status" + }, + "SupportDiagnosticsDetails.severity": { + "values": [ + "info", + "warning", + "critical" + ], + "minVersion": "2.16", + "kind": "schema", + "name": "severity" + }, + "DELETE servers#cascade_delete": { + "values": [ + "directory-services" + ], + "minVersion": "2.16", + "kind": "inline-parameter", + "name": "cascade_delete" + }, + "SyslogServerPatch.services": { + "values": [ + "data-audit", + "management" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "services" + }, + "TrustPolicyRulePost.effect": { + "values": [ + "allow" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "effect" + }, + "AuditFileSystemsPolicyNoContext.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "policy_type" + }, + "PolicyBaseContext.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "policy_type" + }, + "AuditFileSystemsPoliciesPost.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "policy_type" + }, + "NfsExportPolicyRuleInPolicy.access": { + "values": [ + "root-squash", + "all-squash", + "no-squash" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "access" + }, + "NfsExportPolicyRuleInPolicy.permission": { + "values": [ + "rw", + "ro" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "permission" + }, + "NfsExportPolicyRuleBase.access": { + "values": [ + "root-squash", + "all-squash", + "no-squash" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "access" + }, + "NfsExportPolicyRuleBase.permission": { + "values": [ + "rw", + "ro" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "permission" + }, + "TrustPolicyRuleWithContext.effect": { + "values": [ + "allow" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "effect" + }, + "NetworkAccessPolicyRulePostBase.interfaces": { + "values": [ + "management-ssh", + "management-rest-api", + "management-web-ui", + "snmp", + "local-network-superuser-password-access" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "interfaces" + }, + "NetworkAccessPolicyRulePostBase.effect": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "effect" + }, + "NetworkAccessPolicyRuleBase.interfaces": { + "values": [ + "management-ssh", + "management-rest-api", + "management-web-ui", + "snmp", + "local-network-superuser-password-access" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "interfaces" + }, + "NetworkAccessPolicyRuleBase.effect": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "effect" + }, + "_replicaLinkBuiltIn.direction": { + "values": [ + "inbound", + "outbound" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "direction" + }, + "_referenceWritable.resource_type": { + "values": [ + "hosts", + "host-groups", + "network-interfaces", + "pods", + "ports", + "pod-replica-links", + "subnets", + "volumes", + "volume-snapshots", + "volume-groups", + "directories", + "policies/nfs", + "policies/smb", + "policies/snapshot" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "resource_type" + }, + "_softwareChecksChecks.status": { + "values": [ + "running", + "failed", + "passed" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "status" + }, + "BucketPost.bucket_type": { + "values": [ + "classic", + "multi-site-writable" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "bucket_type" + }, + "BucketPost.retention_lock": { + "values": [ + "unlocked", + "ratcheted" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "retention_lock" + }, + "FleetMember.status": { + "values": [ + "joining", + "joined", + "removing" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "status" + }, + "_support_remote_assist_paths.status": { + "values": [ + "reconnecting", + "connected", + "disconnected", + "unknown" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "status" + }, + "PolicyRuleObjectAccessPost.effect": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "effect" + }, + "SmbClientPolicyPost.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "policy_type" + }, + "QosPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "policy_type" + }, + "SnmpManagerPost.notification": { + "values": [ + "inform", + "trap" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "notification" + }, + "SnmpManagerPost.version": { + "values": [ + "v2c", + "v3" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "version" + }, + "SmbSharePolicyRulePost.full_control": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "full_control" + }, + "SmbSharePolicyRulePost.read": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "read" + }, + "SmbSharePolicyRulePost.change": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "change" + }, + "SyslogServerContext.services": { + "values": [ + "data-audit", + "management" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "services" + }, + "PolicyRuleObjectAccessBulkManage.effect": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "effect" + }, + "SshCertificateAuthorityPolicyPost.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "policy_type" + }, + "FileSystemPatch.promotion_status": { + "values": [ + "promoted", + "demoted" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "promotion_status" + }, + "FileSystemPatch.requested_promotion_state": { + "values": [ + "promoted", + "demoted" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "requested_promotion_state" + }, + "FileSystemPatch.group_ownership": { + "values": [ + "creator", + "parent-directory" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "group_ownership" + }, + "SmbClientPolicyRuleBase.encryption": { + "values": [ + "required", + "disabled", + "optional" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "encryption" + }, + "SmbClientPolicyRuleBase.permission": { + "values": [ + "rw", + "ro" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "permission" + }, + "ActiveDirectoryPatch.encryption_types": { + "values": [ + "aes256-cts-hmac-sha1-96", + "aes128-cts-hmac-sha1-96", + "arcfour-hmac" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "encryption_types" + }, + "SmbSharePolicyPost.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "policy_type" + }, + "NetworkAccessPolicyRuleInPolicy.interfaces": { + "values": [ + "management-ssh", + "management-rest-api", + "management-web-ui", + "snmp", + "local-network-superuser-password-access" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "interfaces" + }, + "NetworkAccessPolicyRuleInPolicy.effect": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "effect" + }, + "PolicyPatch.retention_lock": { + "values": [ + "locked", + "unlocked" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "retention_lock" + }, + "AuditFileSystemsPoliciesPatch.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "policy_type" + }, + "_reference.resource_type": { + "values": [ + "hosts", + "host-groups", + "network-interfaces", + "pods", + "ports", + "pod-replica-links", + "subnets", + "volumes", + "volume-snapshots", + "volume-groups", + "directories", + "policies/nfs", + "policies/smb", + "policies/snapshot" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "resource_type" + }, + "PolicyBaseRenameable.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "policy_type" + }, + "SmbClientPolicyRulePostBase.encryption": { + "values": [ + "required", + "disabled", + "optional" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "encryption" + }, + "SmbClientPolicyRulePostBase.permission": { + "values": [ + "rw", + "ro" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "permission" + }, + "AuditFileSystemsPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "policy_type" + }, + "_snmp_v3.auth_protocol": { + "values": [ + "MD5", + "SHA" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "auth_protocol" + }, + "_snmp_v3.privacy_protocol": { + "values": [ + "AES", + "DES" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "privacy_protocol" + }, + "_supportDiagnosticsSeverityCount.severity": { + "values": [ + "info", + "warning", + "critical" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "severity" + }, + "NetworkAccessPolicyRulePost.interfaces": { + "values": [ + "management-ssh", + "management-rest-api", + "management-web-ui", + "snmp", + "local-network-superuser-password-access" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "interfaces" + }, + "NetworkAccessPolicyRulePost.effect": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "effect" + }, + "SmtpServer.encryption_mode": { + "values": [ + "starttls" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "encryption_mode" + }, + "SoftwareCheck.status": { + "values": [ + "queued", + "running", + "passed", + "failed" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "status" + }, + "_bucketEradicationConfig.eradication_mode": { + "values": [ + "permission-based", + "retention-based" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "eradication_mode" + }, + "_bucketEradicationConfig.manual_eradication": { + "values": [ + "disabled", + "enabled" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "manual_eradication" + }, + "TlsPolicyPost.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "policy_type" + }, + "TlsPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "policy_type" + }, + "NfsExportPolicyPost.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "policy_type" + }, + "ActiveDirectoryPost.encryption_types": { + "values": [ + "aes256-cts-hmac-sha1-96", + "aes128-cts-hmac-sha1-96", + "arcfour-hmac" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "encryption_types" + }, + "ObjectStoreTrustPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "policy_type" + }, + "FileSystemPost.requested_promotion_state": { + "values": [ + "promoted", + "demoted" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "requested_promotion_state" + }, + "FileSystemPost.group_ownership": { + "values": [ + "creator", + "parent-directory" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "group_ownership" + }, + "NetworkInterfacesConnectorsSettingRoce.network_congestion_mode": { + "values": [ + "lossy", + "lossless" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "network_congestion_mode" + }, + "NetworkInterfacesConnectorsSettingRoce.trust_mode": { + "values": [ + "pcp", + "dscp" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "trust_mode" + }, + "_multiProtocolPost.access_control_style": { + "values": [ + "nfs", + "smb", + "shared", + "independent", + "mode-bits" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "access_control_style" + }, + "ArrayConnectionPost.type": { + "values": [ + "async-replication", + "fleet-management" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "type" + }, + "ArrayConnectionPost.status": { + "values": [ + "connected", + "partially_connected", + "connecting", + "incompatible" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "status" + }, + "PolicyRuleObjectAccess.effect": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "effect" + }, + "_multiProtocol.access_control_style": { + "values": [ + "nfs", + "smb", + "shared", + "independent", + "mode-bits" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "access_control_style" + }, + "_objectLockConfigBase.default_retention_mode": { + "values": [ + "compliance", + "governance" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "default_retention_mode" + }, + "_snmp_v3_post.auth_protocol": { + "values": [ + "MD5", + "SHA" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "auth_protocol" + }, + "_snmp_v3_post.privacy_protocol": { + "values": [ + "AES", + "DES" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "privacy_protocol" + }, + "_objectLockConfigResponse.default_retention_mode": { + "values": [ + "compliance", + "governance" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "default_retention_mode" + }, + "_objectLockConfigResponse.default_retention": { + "values": [ + "86400000", + "3153600000000" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "default_retention" + }, + "ArrayConnection.type": { + "values": [ + "async-replication", + "fleet-management" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "type" + }, + "BucketPatch.versioning": { + "values": [ + "none", + "enabled", + "suspended" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "versioning" + }, + "BucketPatch.retention_lock": { + "values": [ + "unlocked", + "ratcheted" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "retention_lock" + }, + "AlertWatcherPost.minimum_notification_severity": { + "values": [ + "info", + "warning", + "critical" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "minimum_notification_severity" + }, + "CertificatePost.certificate_type": { + "values": [ + "appliance", + "external" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "certificate_type" + }, + "CertificatePost.status": { + "values": [ + "self-signed", + "imported" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "status" + }, + "SmbClientPolicyRulePostInPolicy.encryption": { + "values": [ + "required", + "disabled", + "optional" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "encryption" + }, + "SmbClientPolicyRulePostInPolicy.permission": { + "values": [ + "rw", + "ro" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "permission" + }, + "ArrayConnectionPath.type": { + "values": [ + "async-replication", + "fleet-management" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "type" + }, + "TrustPolicyRule.effect": { + "values": [ + "allow" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "effect" + }, + "_wormDataPolicyRetentionConfig.retention_lock": { + "values": [ + "unlocked", + "locked" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "retention_lock" + }, + "SmbSharePolicyRuleWithContext.full_control": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "full_control" + }, + "SmbSharePolicyRuleWithContext.read": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "read" + }, + "SmbSharePolicyRuleWithContext.change": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "change" + }, + "_arrayEradicationConfig.eradication_delay": { + "values": [ + "86400000", + "86400000", + "86400000", + "2592000000" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "eradication_delay" + }, + "_locationReference.resource_type": { + "values": [ + "hosts", + "host-groups", + "network-interfaces", + "pods", + "ports", + "pod-replica-links", + "subnets", + "volumes", + "volume-snapshots", + "volume-groups", + "directories", + "policies/nfs", + "policies/smb", + "policies/snapshot" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "resource_type" + }, + "_fileSystemEradicationConfig.eradication_mode": { + "values": [ + "permission-based", + "retention-based" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "eradication_mode" + }, + "_fileSystemEradicationConfig.manual_eradication": { + "values": [ + "disabled", + "enabled" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "manual_eradication" + }, + "SyslogServerPost.services": { + "values": [ + "data-audit", + "management" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "services" + }, + "_objectLockConfigRequestBody.default_retention_mode": { + "values": [ + "compliance", + "governance" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "default_retention_mode" + }, + "_objectLockConfigRequestBody.default_retention": { + "values": [ + "86400000", + "3153600000000" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "default_retention" + }, + "SmbClientPolicyRulePost.encryption": { + "values": [ + "required", + "disabled", + "optional" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "encryption" + }, + "SmbClientPolicyRulePost.permission": { + "values": [ + "rw", + "ro" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "permission" + }, + "RemoteArray.os": { + "values": [ + "Purity", + "Purity//FA", + "Purity//FB" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "os" + }, + "TargetWithContext.status": { + "values": [ + "connected", + "connecting" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "status" + }, + "LegalHoldHeldEntity.status": { + "values": [ + "applied", + "applying", + "releasing" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "status" + }, + "SmbClientPolicyRuleInPolicy.encryption": { + "values": [ + "required", + "disabled", + "optional" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "encryption" + }, + "SmbClientPolicyRuleInPolicy.permission": { + "values": [ + "rw", + "ro" + ], + "minVersion": "2.17", + "kind": "schema", + "name": "permission" + }, + "Protocols": { + "values": [ + "nfs", + "smb" + ], + "minVersion": "2.17", + "kind": "parameter", + "name": "protocols" + }, + "Protocol": { + "values": [ + "all", + "HTTP", + "SMB", + "NFS", + "S3" + ], + "minVersion": "2.17", + "kind": "parameter", + "name": "protocol" + }, + "Type": { + "values": [ + "array", + "file-system", + "object-store" + ], + "minVersion": "2.17", + "kind": "parameter", + "name": "type" + }, + "Member_types": { + "values": [ + "file-systems", + "file-system-snapshots", + "file-system-replica-links", + "object-store-users", + "object-store-accounts" + ], + "minVersion": "2.17", + "kind": "parameter", + "name": "member_types" + }, + "Protocols_required": { + "values": [ + "nfs", + "smb" + ], + "minVersion": "2.17", + "kind": "parameter", + "name": "protocols" + }, + "StorageClassNames": { + "values": [ + "S500X-S", + "flashblade-s500x-s", + "S500X-A", + "flashblade-s500x-a" + ], + "minVersion": "2.17", + "kind": "parameter", + "name": "storage_class_names" + }, + "Cascade_delete": { + "values": [ + "directory-services" + ], + "minVersion": "2.17", + "kind": "parameter", + "name": "cascade_delete" + }, + "Test_type": { + "values": [ + "all", + "phonehome", + "remote-assist" + ], + "minVersion": "2.17", + "kind": "parameter", + "name": "test_type" + }, + "Type_for_performance": { + "values": [ + "all", + "file-system", + "object-store" + ], + "minVersion": "2.17", + "kind": "parameter", + "name": "type" + }, + "AuditFileSystemsPolicy.control_type": { + "values": [ + "policy", + "sacl" + ], + "minVersion": "2.18", + "kind": "schema", + "name": "control_type" + }, + "StorageClassTieringPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.18", + "kind": "schema", + "name": "policy_type" + }, + "AuditFileSystemsPoliciesPost.control_type": { + "values": [ + "policy", + "sacl" + ], + "minVersion": "2.18", + "kind": "schema", + "name": "control_type" + }, + "TieringPolicyArchivalRule.type": { + "values": [ + "LATEST_ACCESS", + "LATEST_MODIFICATION" + ], + "minVersion": "2.18", + "kind": "schema", + "name": "type" + }, + "TieringPolicyRetrievalRule.type": { + "values": [ + "ON_READ", + "ONLY_ON_WRITE" + ], + "minVersion": "2.18", + "kind": "schema", + "name": "type" + }, + "AuditFileSystemsPolicyNoContext.control_type": { + "values": [ + "policy", + "sacl" + ], + "minVersion": "2.18", + "kind": "schema", + "name": "control_type" + }, + "HardwareTemperatures.status": { + "values": [ + "HEALTHY", + "NON_CRITICAL", + "CRITICAL", + "UNRECOVERABLE", + "UNKNOWN" + ], + "minVersion": "2.18", + "kind": "schema", + "name": "status" + }, + "AuditFileSystemsPolicyPatchBase.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.18", + "kind": "schema", + "name": "policy_type" + }, + "Protocol_clients": { + "values": [ + "NFS", + "S3" + ], + "minVersion": "2.18", + "kind": "parameter", + "name": "protocol" + }, + "ManagementAccessPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.19", + "kind": "schema", + "name": "policy_type" + }, + "ManagementAccessPolicyPost.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.19", + "kind": "schema", + "name": "policy_type" + }, + "S3ExportPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.20", + "kind": "schema", + "name": "policy_type" + }, + "CertificatePatch.certificate_type": { + "values": [ + "appliance", + "external" + ], + "minVersion": "2.20", + "kind": "schema", + "name": "certificate_type" + }, + "CertificatePatch.status": { + "values": [ + "self-signed", + "imported" + ], + "minVersion": "2.20", + "kind": "schema", + "name": "status" + }, + "AuditObjectStorePoliciesPost.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.20", + "kind": "schema", + "name": "policy_type" + }, + "AuditObjectStorePoliciesPatch.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.20", + "kind": "schema", + "name": "policy_type" + }, + "_certificateBase.certificate_type": { + "values": [ + "appliance", + "external" + ], + "minVersion": "2.20", + "kind": "schema", + "name": "certificate_type" + }, + "_certificateBase.status": { + "values": [ + "self-signed", + "imported" + ], + "minVersion": "2.20", + "kind": "schema", + "name": "status" + }, + "AuditObjectStorePolicyWithWritableReference.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.20", + "kind": "schema", + "name": "policy_type" + }, + "AuditObjectStorePolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.20", + "kind": "schema", + "name": "policy_type" + }, + "AuditObjectStorePolicyWithContext.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.20", + "kind": "schema", + "name": "policy_type" + }, + "_networkInterfaceNeighborNeighborChassisId.type": { + "values": [ + "ifname", + "ifalias", + "local", + "mac", + "ip", + "unhandled" + ], + "minVersion": "2.20", + "kind": "schema", + "name": "type" + }, + "_networkInterfaceNeighborNeighborPortId.type": { + "values": [ + "ifname", + "ifalias", + "local", + "mac", + "ip", + "unhandled" + ], + "minVersion": "2.20", + "kind": "schema", + "name": "type" + }, + "DataEvictionPolicyPost.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.21", + "kind": "schema", + "name": "policy_type" + }, + "DataEvictionPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.21", + "kind": "schema", + "name": "policy_type" + }, + "ManagementAuthenticationPolicyPost.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.22", + "kind": "schema", + "name": "policy_type" + }, + "ManagementAuthenticationPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.22", + "kind": "schema", + "name": "policy_type" + }, + "NfsExportPolicyPatch.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.22", + "kind": "schema", + "name": "policy_type" + }, + "NfsExportPolicyRulePatch.access": { + "values": [ + "root-squash", + "all-squash", + "no-squash" + ], + "minVersion": "2.22", + "kind": "schema", + "name": "access" + }, + "NfsExportPolicyRulePatch.permission": { + "values": [ + "rw", + "ro" + ], + "minVersion": "2.22", + "kind": "schema", + "name": "permission" + }, + "PolicyPost.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.22", + "kind": "schema", + "name": "policy_type" + }, + "PolicyPost.retention_lock": { + "values": [ + "locked", + "unlocked" + ], + "minVersion": "2.22", + "kind": "schema", + "name": "retention_lock" + }, + "SnapshotPolicyPatch.policy_type": { + "values": [ + "snapshot", + "worm-data" + ], + "minVersion": "2.22", + "kind": "schema", + "name": "policy_type" + }, + "SnapshotPolicyPatch.retention_lock": { + "values": [ + "locked", + "unlocked" + ], + "minVersion": "2.22", + "kind": "schema", + "name": "retention_lock" + }, + "SmbClientPolicyPatch.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.22", + "kind": "schema", + "name": "policy_type" + }, + "SmbSharePolicyPatch.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.22", + "kind": "schema", + "name": "policy_type" + }, + "NfsExportPolicyPatchRuleInPolicy.access": { + "values": [ + "root-squash", + "all-squash", + "no-squash" + ], + "minVersion": "2.22", + "kind": "schema", + "name": "access" + }, + "NfsExportPolicyPatchRuleInPolicy.permission": { + "values": [ + "rw", + "ro" + ], + "minVersion": "2.22", + "kind": "schema", + "name": "permission" + }, + "NfsExportPolicyRuleBasePatchRequest.access": { + "values": [ + "root-squash", + "all-squash", + "no-squash" + ], + "minVersion": "2.22", + "kind": "schema", + "name": "access" + }, + "NfsExportPolicyRuleBasePatchRequest.permission": { + "values": [ + "rw", + "ro" + ], + "minVersion": "2.22", + "kind": "schema", + "name": "permission" + }, + "SnapshotWormDataPolicyBase.policy_type": { + "values": [ + "snapshot", + "worm-data" + ], + "minVersion": "2.22", + "kind": "schema", + "name": "policy_type" + }, + "Management_access_policies_member_types": { + "values": [ + "admins", + "arrays" + ], + "minVersion": "2.22", + "kind": "parameter", + "name": "member_types" + }, + "FileSystemExportPatch.status": { + "values": [ + "policy_disabled", + "protocol_disabled" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "status" + }, + "PresetWorkload.platform_features": { + "values": [ + "fa_block", + "fa_file", + "fb_file" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "platform_features" + }, + "PresetWorkload.workload_type": { + "values": [ + "Clarity", + "Epic", + "Exchange", + "File", + "MsSQL", + "MySQL", + "Oracle", + "PostgreSQL", + "SAP-Hana", + "SAP", + "VDI", + "VSI", + "Wfs", + "Zerto", + "Custom" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "workload_type" + }, + "PresetWorkloadPost.platform_features": { + "values": [ + "fa_block", + "fa_file", + "fb_file" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "platform_features" + }, + "PresetWorkloadPost.workload_type": { + "values": [ + "Clarity", + "Epic", + "Exchange", + "File", + "MsSQL", + "MySQL", + "Oracle", + "PostgreSQL", + "SAP-Hana", + "SAP", + "VDI", + "VSI", + "Wfs", + "Zerto", + "Custom" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "workload_type" + }, + "WorkloadPlacementRecommendation.recommendation_engine": { + "values": [ + "pure1", + "local", + "best-available" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "recommendation_engine" + }, + "WorkloadPlacementRecommendation.status": { + "values": [ + "processing", + "completed", + "failed" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "status" + }, + "_workloadConfigurationReferencePost.resource_type": { + "values": [ + "hosts", + "host-groups", + "network-interfaces", + "pods", + "ports", + "pod-replica-links", + "subnets", + "volumes", + "volume-snapshots", + "volume-groups", + "directories", + "policies/nfs", + "policies/smb", + "policies/snapshot" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "resource_type" + }, + "_workloadConfigurationReferencePatch.resource_type": { + "values": [ + "hosts", + "host-groups", + "network-interfaces", + "pods", + "ports", + "pod-replica-links", + "subnets", + "volumes", + "volume-snapshots", + "volume-groups", + "directories", + "policies/nfs", + "policies/smb", + "policies/snapshot" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "resource_type" + }, + "_fileSystemExportBase.status": { + "values": [ + "policy_disabled", + "protocol_disabled" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "status" + }, + "_presetWorkloadBase.platform_features": { + "values": [ + "fa_block", + "fa_file", + "fb_file" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "platform_features" + }, + "_presetWorkloadBase.workload_type": { + "values": [ + "Clarity", + "Epic", + "Exchange", + "File", + "MsSQL", + "MySQL", + "Oracle", + "PostgreSQL", + "SAP-Hana", + "SAP", + "VDI", + "VSI", + "Wfs", + "Zerto", + "Custom" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "workload_type" + }, + "_workloadPlacementRecommendationResult.score": { + "values": [ + "optimal", + "optimal-with-warnings", + "acceptable" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "score" + }, + "_workloadConfigurationReference.resource_type": { + "values": [ + "hosts", + "host-groups", + "network-interfaces", + "pods", + "ports", + "pod-replica-links", + "subnets", + "volumes", + "volume-snapshots", + "volume-groups", + "directories", + "policies/nfs", + "policies/smb", + "policies/snapshot" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "resource_type" + }, + "_presetWorkloadParameter.type": { + "values": [ + "string", + "integer", + "boolean", + "resource_reference" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "type" + }, + "_presetWorkloadQuotaConfiguration.policy_action": { + "values": [ + "always_create", + "create_or_reuse" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "policy_action" + }, + "_presetWorkloadSnapshotConfiguration.policy_action": { + "values": [ + "always_create", + "create_or_reuse" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "policy_action" + }, + "ResiliencyGroup.status": { + "values": [ + "critical", + "healthy", + "unhealthy" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "status" + }, + "_workloadPlacementRecommendationGenericTarget.resource_type": { + "values": [ + "hosts", + "host-groups", + "network-interfaces", + "pods", + "ports", + "pod-replica-links", + "subnets", + "volumes", + "volume-snapshots", + "volume-groups", + "directories", + "policies/nfs", + "policies/smb", + "policies/snapshot" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "resource_type" + }, + "_presetWorkloadDirectoryConfigurationMultiProtocol.access_control_style": { + "values": [ + "nfs", + "smb", + "shared", + "independent", + "mode-bits" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "access_control_style" + }, + "_presetWorkloadExportConfigurationNfs.policy_action": { + "values": [ + "always_create", + "create_or_reuse" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "policy_action" + }, + "_presetWorkloadExportConfigurationSmb.policy_action": { + "values": [ + "always_create", + "create_or_reuse" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "policy_action" + }, + "_presetWorkloadExportConfigurationSmbShare.policy_action": { + "values": [ + "always_create", + "create_or_reuse" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "policy_action" + }, + "_presetWorkloadMetadata.subtype": { + "values": [ + "size", + "iops", + "bandwidth", + "time", + "duration" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "subtype" + }, + "_presetWorkloadPolicyAction.policy_action": { + "values": [ + "always_create", + "create_or_reuse" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "policy_action" + }, + "_workloadPlacementRecommendationTarget.resource_type": { + "values": [ + "hosts", + "host-groups", + "network-interfaces", + "pods", + "ports", + "pod-replica-links", + "subnets", + "volumes", + "volume-snapshots", + "volume-groups", + "directories", + "policies/nfs", + "policies/smb", + "policies/snapshot" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "resource_type" + }, + "_workloadPlacementRecommendationWarning.code": { + "values": [ + "limit_volumes_count", + "limit_pods_count" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "code" + }, + "_workloadPlacementRecommendationWarning.message": { + "values": [ + "Adding the workload on the array would exceed the limit on volumes", + "Adding the workload on the array would exceed the limit on pods" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "message" + }, + "_presetWorkloadExportConfigurationNfsRule.access": { + "values": [ + "root-squash", + "all-squash", + "no-root-squash" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "access" + }, + "_presetWorkloadExportConfigurationNfsRule.nfs_version": { + "values": [ + "nfsv3", + "nfsv4" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "nfs_version" + }, + "_presetWorkloadExportConfigurationNfsRule.permission": { + "values": [ + "rw", + "ro" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "permission" + }, + "_presetWorkloadExportConfigurationSmbRule.encryption": { + "values": [ + "required", + "disabled", + "optional" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "encryption" + }, + "_presetWorkloadExportConfigurationSmbRule.permission": { + "values": [ + "rw", + "ro" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "permission" + }, + "_presetWorkloadExportConfigurationSmbShareRule.change": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "change" + }, + "_presetWorkloadExportConfigurationSmbShareRule.full_control": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "full_control" + }, + "_presetWorkloadExportConfigurationSmbShareRule.read": { + "values": [ + "allow", + "deny" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "read" + }, + "_presetWorkloadConstraintsResourceReferenceAllowedValues.resource_type": { + "values": [ + "servers", + "storage-classes", + "remote-arrays" + ], + "minVersion": "2.23", + "kind": "schema", + "name": "resource_type" + }, + "Admin.admin_type": { + "values": [ + "local-user", + "remote-user", + "service-account" + ], + "minVersion": "2.24", + "kind": "schema", + "name": "admin_type" + }, + "SoftwareBundle.status": { + "values": [ + "downloading", + "failed", + "ready", + "verifying" + ], + "minVersion": "2.24", + "kind": "schema", + "name": "status" + }, + "SoftwarePatch.status": { + "values": [ + "available", + "downloaded", + "downloading", + "download_failed", + "installing", + "installed", + "failed", + "not_applicable" + ], + "minVersion": "2.24", + "kind": "schema", + "name": "status" + }, + "LocalMemberTypes": { + "values": [ + "User", + "Group" + ], + "minVersion": "2.24", + "kind": "parameter", + "name": "member_types" + }, + "AuditFileSystemsPoliciesPatch.control_type": { + "values": [ + "policy", + "sacl" + ], + "minVersion": "2.25", + "kind": "schema", + "name": "control_type" + }, + "UserGroupQuotaPolicyPost.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.25", + "kind": "schema", + "name": "policy_type" + }, + "UserGroupQuotaPolicy.policy_type": { + "values": [ + "alert", + "audit", + "bucket-access", + "cross-origin-resource-sharing", + "network-access", + "nfs", + "object-access", + "s3-export", + "smb-client", + "smb-share", + "ssh-certificate-authority", + "telemetry-metrics" + ], + "minVersion": "2.25", + "kind": "schema", + "name": "policy_type" + }, + "UserGroupQuotaPolicyRulePost.notifications": { + "values": [ + "None", + "Account" + ], + "minVersion": "2.25", + "kind": "schema", + "name": "notifications" + }, + "UserGroupQuotaPolicyRulePost.quota_type": { + "values": [ + "user", + "group", + "user-default", + "group-default" + ], + "minVersion": "2.25", + "kind": "schema", + "name": "quota_type" + }, + "UserGroupQuotaPolicyRulePatch.notifications": { + "values": [ + "None", + "Account" + ], + "minVersion": "2.25", + "kind": "schema", + "name": "notifications" + }, + "AuditFileSystemsPolicyPatchBase.control_type": { + "values": [ + "policy", + "sacl" + ], + "minVersion": "2.25", + "kind": "schema", + "name": "control_type" + }, + "UserGroupQuotaPolicyRuleInPolicy.notifications": { + "values": [ + "None", + "Account" + ], + "minVersion": "2.25", + "kind": "schema", + "name": "notifications" + }, + "UserGroupQuotaPolicyRuleInPolicy.quota_type": { + "values": [ + "user", + "group", + "user-default", + "group-default" + ], + "minVersion": "2.25", + "kind": "schema", + "name": "quota_type" + }, + "UserGroupQuotaPolicyRule.notifications": { + "values": [ + "None", + "Account" + ], + "minVersion": "2.25", + "kind": "schema", + "name": "notifications" + }, + "UserGroupQuotaPolicyRule.quota_type": { + "values": [ + "user", + "group", + "user-default", + "group-default" + ], + "minVersion": "2.25", + "kind": "schema", + "name": "quota_type" + }, + "UserGroupQuotaPolicyRuleBase.notifications": { + "values": [ + "None", + "Account" + ], + "minVersion": "2.25", + "kind": "schema", + "name": "notifications" + }, + "UserGroupQuotaPolicyRuleBase.quota_type": { + "values": [ + "user", + "group", + "user-default", + "group-default" + ], + "minVersion": "2.25", + "kind": "schema", + "name": "quota_type" + }, + "ManagementAccessPolicyRolePermissionPost.actions": { + "values": [ + "get", + "post", + "patch", + "delete", + "all" + ], + "minVersion": "2.26", + "kind": "schema", + "name": "actions" + }, + "ManagementAccessPolicyRolePermissionPatch.actions": { + "values": [ + "get", + "post", + "patch", + "delete", + "all" + ], + "minVersion": "2.26", + "kind": "schema", + "name": "actions" + }, + "_policyManagementAccessRolePermissionPost.actions": { + "values": [ + "get", + "post", + "patch", + "delete", + "all" + ], + "minVersion": "2.26", + "kind": "schema", + "name": "actions" + }, + "_policyManagementAccessRolePermissionPatch.actions": { + "values": [ + "get", + "post", + "patch", + "delete", + "all" + ], + "minVersion": "2.26", + "kind": "schema", + "name": "actions" + }, + "ManagementAccessPolicyRolePermission.actions": { + "values": [ + "get", + "post", + "patch", + "delete", + "all" + ], + "minVersion": "2.26", + "kind": "schema", + "name": "actions" + }, + "TgroupMember.status": { + "values": [ + "joining", + "joined", + "removing" + ], + "minVersion": "2.26", + "kind": "schema", + "name": "status" + }, + "_policyManagementAccessRolePermission.actions": { + "values": [ + "get", + "post", + "patch", + "delete", + "all" + ], + "minVersion": "2.26", + "kind": "schema", + "name": "actions" + }, + "FleetMemberBatchResult.status": { + "values": [ + "joining", + "joined", + "failed" + ], + "minVersion": "2.27", + "kind": "schema", + "name": "status" + }, + "_fixedReferenceWithoutId.resource_type": { + "values": [ + "hosts", + "host-groups", + "network-interfaces", + "pods", + "ports", + "pod-replica-links", + "subnets", + "volumes", + "volume-snapshots", + "volume-groups", + "directories", + "policies/nfs", + "policies/smb", + "policies/snapshot", + "topology-groups/arrays" + ], + "minVersion": "2.27", + "kind": "schema", + "name": "resource_type" + } + }, + "unparsed": [ + { + "key": "Array.idle_timeout", + "kind": "schema", + "name": "idle_timeout", + "version": "2.0", + "triggerText": "Valid values are\nin the range of 300000 and 10800000." + }, + { + "key": "GET network-interfaces/ping#component_name", + "kind": "inline-parameter", + "name": "component_name", + "version": "2.6", + "triggerText": "Valid values are controllers and blades from hardware list." + }, + { + "key": "GET network-interfaces/trace#component_name", + "kind": "inline-parameter", + "name": "component_name", + "version": "2.6", + "triggerText": "Valid values are controllers and blades from hardware list." + }, + { + "key": "FileLock.protocol", + "kind": "schema", + "name": "protocol", + "version": "2.10", + "triggerText": "Valid values include `NLM`, `NFSv4." + }, + { + "key": "GET arrays/space/storage-classes#storage_class_names", + "kind": "inline-parameter", + "name": "storage_class_names", + "version": "2.16", + "triggerText": "Valid values include 'S500X-S' and 'S500X-A'." + }, + { + "key": "PolicyBaseContext.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "_fixedReferenceWithIsLocal.resource_type", + "kind": "schema", + "name": "resource_type", + "version": "2.17", + "triggerText": "Valid values are the unique part of the resource's REST endpoint." + }, + { + "key": "NfsExportPolicyRule.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ObjectStoreVirtualHost.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "WormDataPolicy.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "_fixedLocationReference.resource_type", + "kind": "schema", + "name": "resource_type", + "version": "2.17", + "triggerText": "Valid values are the unique part of the resource's REST endpoint." + }, + { + "key": "ArrayPerformance.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "TrustPolicyRuleWithContext.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "BucketAccessPolicyRuleWithContext.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "FileSystem.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "SmbClientPolicy.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "LifecycleRule.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ObjectStoreAccessPolicyAction.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "FileSystemReplicaLink.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ArrayS3SpecificPerformance.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "GroupQuota.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "CrossOriginResourceSharingPolicyRuleWithContext.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "FileSystemSnapshot.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "Bucket.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "_context.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "FileSystemSnapshotTransfer.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "_fixedReference.resource_type", + "kind": "schema", + "name": "resource_type", + "version": "2.17", + "triggerText": "Valid values are the unique part of the resource's REST endpoint." + }, + { + "key": "SyslogServerContext.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ObjectStoreUser.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "BucketReplicaLink.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ArrayHttpSpecificPerformance.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "PolicyPatch.policy_type", + "kind": "schema", + "name": "policy_type", + "version": "2.17", + "triggerText": "Valid values include `alert`, `audit`, `bucket-access`,\n`cross-origin-resource-sharing`, `network-access`, `nfs`, `object-access`,\n`s3-export`, smb-client`, `smb-share`, `snapshot`, `ssh-certificate-authority`,\nand `worm-data`." + }, + { + "key": "PolicyPatch.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet." + }, + { + "key": "RelationshipPerformanceReplication.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "AuditFileSystemsPolicy.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "NfsExportPolicy.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "SmbClientPolicyRule.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ObjectStoreAccessPolicy.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "TlsPolicyPost.min_tls_version", + "kind": "schema", + "name": "min_tls_version", + "version": "2.17", + "triggerText": "Valid values include `default`, `TLSv1." + }, + { + "key": "PolicyMemberContext.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "TlsPolicy.min_tls_version", + "kind": "schema", + "name": "min_tls_version", + "version": "2.17", + "triggerText": "Valid values include `default`, `TLSv1." + }, + { + "key": "ObjectStoreTrustPolicy.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ObjectStoreAccount.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ObjectStoreRemoteCredentials.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ObjectStoreRole.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ServerContext.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ArrayNfsSpecificPerformance.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "_fixedReferenceWithType.resource_type", + "kind": "schema", + "name": "resource_type", + "version": "2.17", + "triggerText": "Valid values are the unique part of the resource's REST endpoint." + }, + { + "key": "ArrayConnectionPost.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "PolicyRuleObjectAccess.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "CrossOriginResourceSharingPolicy.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "SmbSharePolicy.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ArrayConnection.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "FileSystemExport.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ObjectStoreAccessKey.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "PolicyMemberWithRemote.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ArrayConnectionPath.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "BucketAccessPolicy.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "Policy.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "PolicyFileSystemSnapshot.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "SmbSharePolicyRuleWithContext.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "_fixedReferenceNoId.resource_type", + "kind": "schema", + "name": "resource_type", + "version": "2.17", + "triggerText": "Valid values are the unique part of the resource's REST endpoint." + }, + { + "key": "_fixedReferenceWithRemote.resource_type", + "kind": "schema", + "name": "resource_type", + "version": "2.17", + "triggerText": "Valid values are the unique part of the resource's REST endpoint." + }, + { + "key": "CrossOriginResourceSharingPolicyRule.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "UserQuota.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ArraySpace.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "TargetWithContext.context", + "kind": "schema", + "name": "context", + "version": "2.17", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "Ping_trace_component", + "kind": "parameter", + "name": "component_name", + "version": "2.17", + "triggerText": "Valid values are controllers\nand blades from hardware list." + }, + { + "key": "AuditFileSystemsPolicyRuleInPolicy.op_status", + "kind": "schema", + "name": "op_status", + "version": "2.18", + "triggerText": "Valid values include 'success' or failure'." + }, + { + "key": "AuditFileSystemsPolicyRuleBase.op_status", + "kind": "schema", + "name": "op_status", + "version": "2.18", + "triggerText": "Valid values include 'success' or failure'." + }, + { + "key": "ObjectStoreAccountExport.context", + "kind": "schema", + "name": "context", + "version": "2.20", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "RealmDefaults.context", + "kind": "schema", + "name": "context", + "version": "2.20", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "S3ExportPolicy.context", + "kind": "schema", + "name": "context", + "version": "2.20", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "S3ExportPolicyRule.context", + "kind": "schema", + "name": "context", + "version": "2.20", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "LogTargetsObjectStoreWithContext.context", + "kind": "schema", + "name": "context", + "version": "2.20", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "AuditObjectStorePolicyWithContext.context", + "kind": "schema", + "name": "context", + "version": "2.20", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "BucketAuditFilterWithContext.context", + "kind": "schema", + "name": "context", + "version": "2.20", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "BucketAuditFilterActionWithContext.context", + "kind": "schema", + "name": "context", + "version": "2.20", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "DataEvictionPolicy.context", + "kind": "schema", + "name": "context", + "version": "2.21", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "Array.context", + "kind": "schema", + "name": "context", + "version": "2.22", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "PolicyMemberWithRemoteGetResponse.context", + "kind": "schema", + "name": "context", + "version": "2.22", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "PolicyMemberWithRemoteResponse.context", + "kind": "schema", + "name": "context", + "version": "2.22", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ManagementAuthenticationPolicy.context", + "kind": "schema", + "name": "context", + "version": "2.22", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "Admin.context", + "kind": "schema", + "name": "context", + "version": "2.22", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "FileSystemLockNlmReclamation.context", + "kind": "schema", + "name": "context", + "version": "2.22", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "FileLock.context", + "kind": "schema", + "name": "context", + "version": "2.22", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "FileSystemClient.context", + "kind": "schema", + "name": "context", + "version": "2.22", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "FileSession.context", + "kind": "schema", + "name": "context", + "version": "2.22", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "FileSystemExportPatch.context", + "kind": "schema", + "name": "context", + "version": "2.23", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "QosPolicy.context", + "kind": "schema", + "name": "context", + "version": "2.23", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "PresetWorkload.context", + "kind": "schema", + "name": "context", + "version": "2.23", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "WorkloadPlacementRecommendation.context", + "kind": "schema", + "name": "context", + "version": "2.23", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "TestResultWithContext.context", + "kind": "schema", + "name": "context", + "version": "2.23", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "_fileSystemExportBase.context", + "kind": "schema", + "name": "context", + "version": "2.23", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "Workload.context", + "kind": "schema", + "name": "context", + "version": "2.23", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "Tag.context", + "kind": "schema", + "name": "context", + "version": "2.23", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "_workloadConfigurationFixedReference.resource_type", + "kind": "schema", + "name": "resource_type", + "version": "2.23", + "triggerText": "Valid values are the unique part of the resource's REST endpoint." + }, + { + "key": "_workloadPreset.resource_type", + "kind": "schema", + "name": "resource_type", + "version": "2.23", + "triggerText": "Valid values are the unique part of the resource's REST endpoint." + }, + { + "key": "LocalDirectoryService.context", + "kind": "schema", + "name": "context", + "version": "2.24", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "LocalGroupPatch.context", + "kind": "schema", + "name": "context", + "version": "2.24", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ManagementAccessPolicy.context", + "kind": "schema", + "name": "context", + "version": "2.24", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "RealmGetResponse.context", + "kind": "schema", + "name": "context", + "version": "2.24", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "SshCertificateAuthorityPolicy.context", + "kind": "schema", + "name": "context", + "version": "2.24", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "AdminApiToken.context", + "kind": "schema", + "name": "context", + "version": "2.24", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "AdminCache.context", + "kind": "schema", + "name": "context", + "version": "2.24", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "LocalGroup.context", + "kind": "schema", + "name": "context", + "version": "2.24", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "LocalMember.context", + "kind": "schema", + "name": "context", + "version": "2.24", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "LocalUser.context", + "kind": "schema", + "name": "context", + "version": "2.24", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "Dns.context", + "kind": "schema", + "name": "context", + "version": "2.25", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "FileSystemJunction.context", + "kind": "schema", + "name": "context", + "version": "2.25", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "UserGroupQuotaPolicy.context", + "kind": "schema", + "name": "context", + "version": "2.25", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "UserGroupQuotaPolicyRule.context", + "kind": "schema", + "name": "context", + "version": "2.25", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "FileSystemGroupQuota.context", + "kind": "schema", + "name": "context", + "version": "2.25", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "FileSystemGroup.context", + "kind": "schema", + "name": "context", + "version": "2.25", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "FileSystemUserQuota.context", + "kind": "schema", + "name": "context", + "version": "2.25", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "FileSystemUser.context", + "kind": "schema", + "name": "context", + "version": "2.25", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ManagementAccessPolicyRule.context", + "kind": "schema", + "name": "context", + "version": "2.26", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ManagementAccessPolicyRole.context", + "kind": "schema", + "name": "context", + "version": "2.26", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ManagementAccessPolicyRolePermission.context", + "kind": "schema", + "name": "context", + "version": "2.26", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "SupportSystemManifest.context", + "kind": "schema", + "name": "context", + "version": "2.26", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "Tgroup.context", + "kind": "schema", + "name": "context", + "version": "2.26", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "TgroupMember.context", + "kind": "schema", + "name": "context", + "version": "2.26", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "_policyManagementAccessRole.context", + "kind": "schema", + "name": "context", + "version": "2.26", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "_policyManagementAccessRolePermission.context", + "kind": "schema", + "name": "context", + "version": "2.26", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "ManagementAccessPolicyRolePermissionSupportedResource.context", + "kind": "schema", + "name": "context", + "version": "2.26", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "_policyManagementAccessRolePermissionSupportedResource.context", + "kind": "schema", + "name": "context", + "version": "2.26", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + }, + { + "key": "Audit.context", + "kind": "schema", + "name": "context", + "version": "2.28", + "triggerText": "Valid values include a reference to any array which is a member of the same fleet\nor to the fleet itself." + } + ] +} diff --git a/Reports/PfbValueEnumReconciliation.md b/Reports/PfbValueEnumReconciliation.md new file mode 100644 index 0000000..5457330 --- /dev/null +++ b/Reports/PfbValueEnumReconciliation.md @@ -0,0 +1,20 @@ +# Value-Enum Reconciliation Report + +Generated by `tools/Build-PfbValueEnumMap.ps1` against `Reports/PfbValueEnumMap.json` (29 REST versions, 301 entries). + +Compares every hand-written `ValidateSet` in `Public/` that encodes a spec-documented value enum against the newly extracted prose data. Report only — no `Public/` cmdlet is edited by this script. See `Value-Enum-Extraction-Work.md` for the full non-goal list. + +| File:Line | Parameter | Hand-written values | Spec values | Status | Note | +|---|---|---|---|---|---| +| `Public/Alert/New-PfbAlertWatcher.ps1:22` | `-MinimumSeverity` | info, warning, error, critical | info, warning, critical | **stale** | hand-written set has but spec does not list: error | +| `Public/Alert/Update-PfbAlertWatcher.ps1:29` | `-MinimumSeverity` | info, warning, critical | info, warning, critical | **exact-match** | | +| `Public/Bucket/New-PfbBucket.ps1:29` | `-Versioning` | enabled, suspended, none | none, enabled, suspended | **exact-match** | | +| `Public/Bucket/Update-PfbBucket.ps1:31` | `-Versioning` | enabled, suspended, none | none, enabled, suspended | **exact-match** | | +| `Public/FileSystem/New-PfbFileSystem.ps1:130` | `-MultiProtocolAccessControlStyle` | nfs, smb, shared, independent, mode-bits | nfs, smb, shared, independent, mode-bits | **exact-match** | | +| `Public/FileSystem/New-PfbFileSystem.ps1:143` | `-GroupOwnership` | creator, parent-directory | creator, parent-directory | **exact-match** | | +| `Public/FileSystem/New-PfbFileSystem.ps1:147` | `-EradicationMode` | permission-based, retention-based | permission-based, retention-based | **exact-match** | | +| `Public/FileSystem/Update-PfbFileSystem.ps1:97` | `-RequestedPromotionState` | promoted, demoted | promoted, demoted | **exact-match** | | +| `Public/Array/Get-PfbArrayPerformance.ps1:28` | `-Protocol` | nfs, smb, http, s3 | | **collision** | matches 5 distinct (schema, property) entries with different value sets: GET arrays/performance#protocol; GET file-systems/performance#protocol; FileSession.protocol; Protocol; Protocol_clients | +| `Public/Network/New-PfbNetworkInterface.ps1:52` | `-Services` | data, egress-only, management, replication, support | | **not-found-in-resource** | field 'services' not found under any of [NetworkInterface]-hinted schemas; found elsewhere: SyslogServer.services; SyslogServerPatch.services; SyslogServerContext.services; SyslogServerPost.services | +| `Public/Network/New-PfbNetworkInterface.ps1:59` | `-Type` | vip | | **not-found-in-resource** | field 'type' not found under any of [NetworkInterface]-hinted schemas; found elsewhere: Hardware.type; GET array-connections/performance/replication#type; GET arrays/performance/replication#type; GET arrays/space#type; Drive.type; ArrayConnectionPost.type; ArrayConnection.type; ArrayConnectionPath.type; Type; Type_for_performance; TieringPolicyArchivalRule.type; TieringPolicyRetrievalRule.type; _networkInterfaceNeighborNeighborChassisId.type; _networkInterfaceNeighborNeighborPortId.type; _presetWorkloadParameter.type | + diff --git a/Reports/README.md b/Reports/README.md new file mode 100644 index 0000000..bf236d2 --- /dev/null +++ b/Reports/README.md @@ -0,0 +1,28 @@ +# Reports/ + +Human/agent-facing advisory output from the `tools/` generator scripts. Nothing under +this directory is read by the shipped module at runtime — see `Data/` for that (`Data/PfbCapabilityMap.json`, `Data/PfbVersionMap.json`). + +Regenerate everything here by running, in order (each requires `tools/specs/` cached +locally first — see `tools/README.md`'s `Update-PfbApiSpecs.ps1` step): + +```powershell +./tools/Build-PfbValueEnumMap.ps1 +./tools/Build-PfbFieldCmdletMap.ps1 +./tools/Build-PfbApiDriftReport.ps1 +``` + +| File | Answers | Generated by | +|---|---|---| +| `PfbValueEnumMap.json` | "What legal values does the spec document for this field?" | `tools/Build-PfbValueEnumMap.ps1` | +| `PfbValueEnumReconciliation.md` | "Does an existing hand-written `ValidateSet` match the spec?" | `tools/Build-PfbValueEnumMap.ps1` | +| `PfbFieldCmdletMap.json` | "Which typed `Public/` parameters lacking a `ValidateSet` today should get one?" | `tools/Build-PfbFieldCmdletMap.ps1` | +| `PfbFieldCmdletMapping.md` | Same, as a readable table. | `tools/Build-PfbFieldCmdletMap.ps1` | +| `PfbApiDriftReport.json` / `.md` | "What's changed in the API that this module hasn't caught up to yet?" — new endpoints with no cmdlet, new parameters on endpoints we already call, drift on existing `ValidateSet`s, and new `ValidateSet` candidates. Pass `-SinceVersion ''` to isolate just the newest release's additions (uncovered endpoints + parameter gaps only) instead of the full backlog. | `tools/Build-PfbApiDriftReport.ps1` | + +All of the above are **reporting only** — none of them edit any `Public/` cmdlet. A human +(or an agent, on request) reads a report and decides what, if anything, to build next. + +**Orienting yourself cold:** if you've been handed this repo and told "something changed +in the API," start at `PfbApiDriftReport.md` — it's the newest, most targeted summary and +links back to the other three reports for detail. diff --git a/Tests/Add-PfbCommonQueryParams.Tests.ps1 b/Tests/Add-PfbCommonQueryParams.Tests.ps1 new file mode 100644 index 0000000..626c2db --- /dev/null +++ b/Tests/Add-PfbCommonQueryParams.Tests.ps1 @@ -0,0 +1,329 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + Import-Module "$PSScriptRoot/../PureStorageFlashBladePowerShell.psd1" -Force +} + +Describe 'Add-PfbCommonQueryParams' { + Context 'Filter parameter' { + It 'adds Filter to $Into when present in BoundParameters' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{ Filter = 'name=~foo' } + } { + param($Into, $BoundParameters) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters + $Into.ContainsKey('filter') | Should -BeTrue + $Into['filter'] | Should -Be 'name=~foo' + } + } + + It 'does not add Filter to $Into when absent from BoundParameters' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{} + } { + param($Into, $BoundParameters) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters + $Into.ContainsKey('filter') | Should -BeFalse + } + } + + It 'adds empty-string Filter when explicitly passed' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{ Filter = '' } + } { + param($Into, $BoundParameters) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters + $Into.ContainsKey('filter') | Should -BeTrue + $Into['filter'] | Should -Be '' + } + } + } + + Context 'Sort parameter' { + It 'adds Sort to $Into when present in BoundParameters' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{ Sort = 'name' } + } { + param($Into, $BoundParameters) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters + $Into.ContainsKey('sort') | Should -BeTrue + $Into['sort'] | Should -Be 'name' + } + } + + It 'does not add Sort to $Into when absent from BoundParameters' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{} + } { + param($Into, $BoundParameters) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters + $Into.ContainsKey('sort') | Should -BeFalse + } + } + } + + Context 'Limit parameter' { + It 'adds Limit to $Into when present in BoundParameters' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{ Limit = 100 } + } { + param($Into, $BoundParameters) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters + $Into.ContainsKey('limit') | Should -BeTrue + $Into['limit'] | Should -Be 100 + } + } + + It 'does not add Limit to $Into when absent from BoundParameters' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{} + } { + param($Into, $BoundParameters) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters + $Into.ContainsKey('limit') | Should -BeFalse + } + } + + It 'adds Limit 0 when explicitly passed (proves ContainsKey semantics, not truthiness)' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{ Limit = 0 } + } { + param($Into, $BoundParameters) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters + $Into.ContainsKey('limit') | Should -BeTrue + $Into['limit'] | Should -Be 0 + } + } + } + + Context 'TotalOnly parameter' { + It 'adds total_only = true to $Into when TotalOnly is present in BoundParameters' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{ TotalOnly = $true } + } { + param($Into, $BoundParameters) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters + $Into.ContainsKey('total_only') | Should -BeTrue + $Into['total_only'] | Should -Be 'true' + } + } + + It 'does not add total_only to $Into when TotalOnly is absent from BoundParameters' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{} + } { + param($Into, $BoundParameters) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters + $Into.ContainsKey('total_only') | Should -BeFalse + } + } + } + + Context 'Names parameter' { + It 'adds names joined by comma when Names are provided' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{} + Names = @('fs1', 'fs2', 'fs3') + } { + param($Into, $BoundParameters, $Names) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters -Names $Names + $Into.ContainsKey('names') | Should -BeTrue + $Into['names'] | Should -Be 'fs1,fs2,fs3' + } + } + + It 'does not add names when Names are not provided' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{} + } { + param($Into, $BoundParameters) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters + $Into.ContainsKey('names') | Should -BeFalse + } + } + + It 'does not add names when Names is an empty array' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{} + Names = @() + } { + param($Into, $BoundParameters, $Names) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters -Names $Names + $Into.ContainsKey('names') | Should -BeFalse + } + } + + It 'adds single Name when only one is provided' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{} + Names = @('fs1') + } { + param($Into, $BoundParameters, $Names) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters -Names $Names + $Into.ContainsKey('names') | Should -BeTrue + $Into['names'] | Should -Be 'fs1' + } + } + } + + Context 'Ids parameter' { + It 'adds ids joined by comma when Ids are provided' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{} + Ids = @('id1', 'id2', 'id3') + } { + param($Into, $BoundParameters, $Ids) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters -Ids $Ids + $Into.ContainsKey('ids') | Should -BeTrue + $Into['ids'] | Should -Be 'id1,id2,id3' + } + } + + It 'does not add ids when Ids are not provided' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{} + } { + param($Into, $BoundParameters) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters + $Into.ContainsKey('ids') | Should -BeFalse + } + } + + It 'does not add ids when Ids is an empty array' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{} + Ids = @() + } { + param($Into, $BoundParameters, $Ids) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters -Ids $Ids + $Into.ContainsKey('ids') | Should -BeFalse + } + } + + It 'adds single Id when only one is provided' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{} + Ids = @('id1') + } { + param($Into, $BoundParameters, $Ids) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters -Ids $Ids + $Into.ContainsKey('ids') | Should -BeTrue + $Into['ids'] | Should -Be 'id1' + } + } + } + + Context 'Names and Ids independence' { + It 'adds only Names when only Names are provided' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{} + Names = @('fs1', 'fs2') + } { + param($Into, $BoundParameters, $Names) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters -Names $Names + $Into.ContainsKey('names') | Should -BeTrue + $Into['names'] | Should -Be 'fs1,fs2' + $Into.ContainsKey('ids') | Should -BeFalse + } + } + + It 'adds only Ids when only Ids are provided' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{} + Ids = @('id1', 'id2') + } { + param($Into, $BoundParameters, $Ids) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters -Ids $Ids + $Into.ContainsKey('ids') | Should -BeTrue + $Into['ids'] | Should -Be 'id1,id2' + $Into.ContainsKey('names') | Should -BeFalse + } + } + + It 'adds both Names and Ids when both are provided' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{} + Names = @('fs1', 'fs2') + Ids = @('id1', 'id2') + } { + param($Into, $BoundParameters, $Names, $Ids) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters -Names $Names -Ids $Ids + $Into.ContainsKey('names') | Should -BeTrue + $Into['names'] | Should -Be 'fs1,fs2' + $Into.ContainsKey('ids') | Should -BeTrue + $Into['ids'] | Should -Be 'id1,id2' + } + } + } + + Context 'In-place mutation' { + It 'mutates the $Into hashtable in place' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{ existing_key = 'existing_value' } + BoundParameters = @{ Filter = 'test'; Limit = 50 } + Names = @('name1') + } { + param($Into, $BoundParameters, $Names) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters -Names $Names + # Verify original key is still there + $Into['existing_key'] | Should -Be 'existing_value' + # Verify new keys were added + $Into['filter'] | Should -Be 'test' + $Into['limit'] | Should -Be 50 + $Into['names'] | Should -Be 'name1' + } + } + + It 'function returns nothing (hashtable is mutated, not returned)' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{ Filter = 'test' } + } { + param($Into, $BoundParameters) + $result = Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters + $result | Should -BeNullOrEmpty + } + } + } + + Context 'All parameters together' { + It 'handles all four scalar parameters and both array parameters in one call' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + Into = @{} + BoundParameters = @{ Filter = 'name=test'; Sort = 'name'; Limit = 25; TotalOnly = $true } + Names = @('fs1', 'fs2') + Ids = @('id1') + } { + param($Into, $BoundParameters, $Names, $Ids) + Add-PfbCommonQueryParams -Into $Into -BoundParameters $BoundParameters -Names $Names -Ids $Ids + $Into['filter'] | Should -Be 'name=test' + $Into['sort'] | Should -Be 'name' + $Into['limit'] | Should -Be 25 + $Into['total_only'] | Should -Be 'true' + $Into['names'] | Should -Be 'fs1,fs2' + $Into['ids'] | Should -Be 'id1' + } + } + } +} diff --git a/Tests/Assert-PfbApiCapability.Tests.ps1 b/Tests/Assert-PfbApiCapability.Tests.ps1 new file mode 100644 index 0000000..92b2e8a --- /dev/null +++ b/Tests/Assert-PfbApiCapability.Tests.ps1 @@ -0,0 +1,251 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } +<# +.SYNOPSIS + Unit tests for the Private Assert-PfbApiCapability capability-check gate. +.DESCRIPTION + Injects a small synthetic capability/version map into module state rather than relying + on the real committed Data/PfbCapabilityMap.json, so these tests are independent of + what the real map currently contains. +#> + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:originalModuleRoot = InModuleScope PureStorageFlashBladePowerShell { $script:PfbModuleRoot } + + function New-TestArray { + param([string]$ApiVersion) + [PSCustomObject]@{ ApiVersion = $ApiVersion } + } +} + +Describe 'Assert-PfbApiCapability' { + BeforeEach { + InModuleScope PureStorageFlashBladePowerShell { + $script:PfbCapabilityMap = [PSCustomObject]@{ + endpoints = [PSCustomObject]@{ + 'GET /widgets' = [PSCustomObject]@{ + minVersion = '9.0' + parameters = [PSCustomObject]@{ sort = '9.1' } + bodyProperties = [PSCustomObject]@{} + } + 'POST /widgets' = [PSCustomObject]@{ + minVersion = '9.1' + parameters = [PSCustomObject]@{} + bodyProperties = [PSCustomObject]@{ name = '9.0'; color = '9.2' } + } + } + } + $script:PfbVersionMap = [PSCustomObject]@{ + '9.0' = [PSCustomObject]@{ purity = '5.0.0' } + '9.1' = [PSCustomObject]@{ purity = '5.1.0' } + } + } + } + + AfterEach { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ root = $script:originalModuleRoot } { + $script:PfbCapabilityMap = $null + $script:PfbVersionMap = $null + $script:PfbModuleRoot = $root + } + } + + It 'does not throw when the endpoint and its query parameter are both supported by the connected version' { + $array = New-TestArray -ApiVersion '9.1' + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + { Assert-PfbApiCapability -Array $array -Method GET -Endpoint 'widgets' -QueryParams @{ sort = 'name' } } | + Should -Not -Throw + } + } + + It 'throws when the endpoint itself requires a newer version than the connected array' { + $array = New-TestArray -ApiVersion '9.0' + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + { Assert-PfbApiCapability -Array $array -Method POST -Endpoint 'widgets' } | + Should -Throw -ExpectedMessage '*POST /widgets requires REST 9.1*' + } + } + + It 'throws naming the specific query parameter that requires a newer version' { + $array = New-TestArray -ApiVersion '9.0' + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + { Assert-PfbApiCapability -Array $array -Method GET -Endpoint 'widgets' -QueryParams @{ sort = 'newest' } } | + Should -Throw -ExpectedMessage "*parameter 'sort'*requires REST 9.1*" + } + } + + It 'ignores a query parameter whose value is null or empty (never sent, so never a violation)' { + $array = New-TestArray -ApiVersion '9.0' + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + { Assert-PfbApiCapability -Array $array -Method GET -Endpoint 'widgets' -QueryParams @{ sort = '' } } | + Should -Not -Throw + } + } + + It 'throws naming the specific request-body field that requires a newer version' { + $array = New-TestArray -ApiVersion '9.1' + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + { Assert-PfbApiCapability -Array $array -Method POST -Endpoint 'widgets' -Body @{ name = 'x'; color = 'red' } } | + Should -Throw -ExpectedMessage "*request-body field 'color'*requires REST 9.2*" + } + } + + It 'does not throw for an endpoint absent from the capability map (safety valve for stale/uncovered maps)' { + $array = New-TestArray -ApiVersion '1.0' + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + { Assert-PfbApiCapability -Array $array -Method GET -Endpoint 'not-in-map' } | Should -Not -Throw + } + } + + It 'includes the Purity//FB version in the message when the version map has an entry for both sides' { + $array = New-TestArray -ApiVersion '9.0' + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + { Assert-PfbApiCapability -Array $array -Method POST -Endpoint 'widgets' } | + Should -Throw -ExpectedMessage '*Purity//FB 5.1.0*Purity//FB 5.0.0*' + } + } + + It 'omits the Purity//FB parenthetical cleanly when no version map is available' { + InModuleScope PureStorageFlashBladePowerShell { $script:PfbVersionMap = $null } + $array = New-TestArray -ApiVersion '9.0' + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + $errorMessage = $null + try { Assert-PfbApiCapability -Array $array -Method POST -Endpoint 'widgets' } + catch { $errorMessage = $_.Exception.Message } + $errorMessage | Should -BeLike '*requires REST 9.1*' + $errorMessage | Should -Not -BeLike '*Purity*' + } + } + + It 'no-ops entirely when the capability map itself is unavailable' { + InModuleScope PureStorageFlashBladePowerShell { + $script:PfbCapabilityMap = $null + $script:PfbModuleRoot = 'TestDrive:\nonexistent' + } + $array = New-TestArray -ApiVersion '1.0' + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + { Assert-PfbApiCapability -Array $array -Method GET -Endpoint 'widgets' } | Should -Not -Throw + } + } +} + +# Kept in its own Describe with its own fixture so the hashtable-path expectations above -- +# the regression gate for widening -Body -- stay untouched. +Describe 'Assert-PfbApiCapability - array request bodies' { + BeforeEach { + InModuleScope PureStorageFlashBladePowerShell { + $script:PfbCapabilityMap = [PSCustomObject]@{ + endpoints = [PSCustomObject]@{ + 'PUT /widgets/batch' = [PSCustomObject]@{ + minVersion = '9.1' + parameters = [PSCustomObject]@{ resource_names = '9.2' } + # Mirrors the real map: every array-bodied endpoint in + # Data/PfbCapabilityMap.json has an empty bodyProperties, because the + # spec walk never descends into an array schema's "items". + bodyProperties = [PSCustomObject]@{} + } + # Contrived: a bodyProperties entry on an array-bodied endpoint, which the + # real map cannot currently produce. Pins the deliberate decision that an + # array body's elements are not checked against it. + 'PUT /gadgets/batch' = [PSCustomObject]@{ + minVersion = '9.0' + parameters = [PSCustomObject]@{} + bodyProperties = [PSCustomObject]@{ color = '9.9' } + } + } + } + $script:PfbVersionMap = [PSCustomObject]@{ + '9.0' = [PSCustomObject]@{ purity = '5.0.0' } + '9.1' = [PSCustomObject]@{ purity = '5.1.0' } + } + } + } + + AfterEach { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ root = $script:originalModuleRoot } { + $script:PfbCapabilityMap = $null + $script:PfbVersionMap = $null + $script:PfbModuleRoot = $root + } + } + + It 'accepts an array body without throwing when the endpoint is supported' { + $array = New-TestArray -ApiVersion '9.1' + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + $tags = [hashtable[]]@(@{ key = 'team'; value = 'analytics' }) + { Assert-PfbApiCapability -Array $array -Method PUT -Endpoint 'widgets/batch' -Body $tags } | + Should -Not -Throw + } + } + + It 'still enforces the endpoint minVersion for an array body' { + $array = New-TestArray -ApiVersion '9.0' + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + $tags = [hashtable[]]@(@{ key = 'team'; value = 'analytics' }) + { Assert-PfbApiCapability -Array $array -Method PUT -Endpoint 'widgets/batch' -Body $tags } | + Should -Throw -ExpectedMessage '*PUT /widgets/batch requires REST 9.1*' + } + } + + It 'still enforces query-parameter gating for an array body' { + $array = New-TestArray -ApiVersion '9.1' + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + $tags = [hashtable[]]@(@{ key = 'team'; value = 'analytics' }) + { Assert-PfbApiCapability -Array $array -Method PUT -Endpoint 'widgets/batch' -Body $tags -QueryParams @{ resource_names = 'wl1' } } | + Should -Throw -ExpectedMessage "*parameter 'resource_names'*requires REST 9.2*" + } + } + + It 'does not check an array element field against bodyProperties' { + $array = New-TestArray -ApiVersion '9.0' + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + $elements = [hashtable[]]@(@{ color = 'red' }) + { Assert-PfbApiCapability -Array $array -Method PUT -Endpoint 'gadgets/batch' -Body $elements } | + Should -Not -Throw + } + } + + It 'still checks a hashtable body against the same bodyProperties entry' { + $array = New-TestArray -ApiVersion '9.0' + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + { Assert-PfbApiCapability -Array $array -Method PUT -Endpoint 'gadgets/batch' -Body @{ color = 'red' } } | + Should -Throw -ExpectedMessage "*request-body field 'color'*requires REST 9.9*" + } + } + + It 'does not throw on an empty array body' { + $array = New-TestArray -ApiVersion '9.1' + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + { Assert-PfbApiCapability -Array $array -Method PUT -Endpoint 'widgets/batch' -Body @() } | + Should -Not -Throw + } + } + + # -Body was widened to [System.Collections.ICollection], not to [object]: the shapes + # [hashtable] used to reject must still be rejected at bind time. IEnumerable would have + # silently converted 42 and $true into the strings "42"/"True". + It 'still rejects a non-collection body at bind time' -ForEach @( + @{ Label = 'int'; Value = 42 } + @{ Label = 'string'; Value = 'not-a-body' } + @{ Label = 'PSCustomObject'; Value = [PSCustomObject]@{ color = 'red' } } + ) { + $array = New-TestArray -ApiVersion '9.1' + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array; value = $Value } { + { Assert-PfbApiCapability -Array $array -Method PUT -Endpoint 'widgets/batch' -Body $value } | + Should -Throw + } + } + + # Every bodyless call reaches this function as -Body $null (Invoke-PfbApiRequest forwards + # its own unbound $Body unconditionally), so the widened type must still accept it. + It 'still accepts an explicit $null body' { + $array = New-TestArray -ApiVersion '9.1' + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + { Assert-PfbApiCapability -Array $array -Method PUT -Endpoint 'widgets/batch' -Body $null } | + Should -Not -Throw + } + } +} diff --git a/Tests/Build-PfbApiDriftReport.Tests.ps1 b/Tests/Build-PfbApiDriftReport.Tests.ps1 new file mode 100644 index 0000000..c5af6a0 --- /dev/null +++ b/Tests/Build-PfbApiDriftReport.Tests.ps1 @@ -0,0 +1,1049 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } +<# +.SYNOPSIS + Integration tests for tools/Build-PfbApiDriftReport.ps1 against small synthetic + fixtures (capability map, field-cmdlet map, Public/Private trees, spec files) -- no + dependency on the real cached specs in tools/specs/, plus one real-artifact check. +#> + +BeforeAll { + $repoRoot = Split-Path -Parent $PSScriptRoot + $script:builderScript = Join-Path $repoRoot 'tools/Build-PfbApiDriftReport.ps1' + + $script:fixtureRoot = Join-Path $TestDrive 'fixture' + $publicDir = Join-Path $fixtureRoot 'Public/Fixture' + $privateDir = Join-Path $fixtureRoot 'Private' + $specsDir = Join-Path $fixtureRoot 'specs' + New-Item -ItemType Directory -Path $publicDir, $privateDir, $specsDir -Force | Out-Null + + Set-Content -Path (Join-Path $publicDir 'Get-PfbFixtureArrayPerformance.ps1') -Value @' +function Get-PfbFixtureArrayPerformance { + [CmdletBinding()] + param( + [Parameter()] [PSCustomObject]$Array, + [Parameter()] + [ValidateSet('nfs', 'smb', 'http', 's3')] + [string]$Protocol + ) + $queryParams = @{} + if ($Protocol) { $queryParams['protocol'] = $Protocol } + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'arrays/performance' -QueryParams $queryParams -AutoPaginate +} +'@ + + # Task 5 fixture: a fully-typed (high-confidence) write cmdlet whose endpoint has real + # addable body-property gaps (color/count/tags) enriched with type/synopsis/enum/target. + # -Label IS resolved (index-form `$body['label'] = $Label`), establishing 'index' as + # this cmdlet's own dominant AssignmentStyle for Task 5's target coordinates. + Set-Content -Path (Join-Path $publicDir 'Update-PfbFixtureWidget.ps1') -Value @' +function Update-PfbFixtureWidget { + [CmdletBinding()] + param( + [Parameter()] [PSCustomObject]$Array, + [Parameter(Mandatory)] [string]$Name, + [Parameter()] [string]$Label + ) + $queryParams = @{} + $queryParams['names'] = $Name + $body = @{} + if ($Label) { $body['label'] = $Label } + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'widgets' -Body $body -QueryParams $queryParams +} +'@ + + # Task 5 fixture: a PARTIAL-confidence write cmdlet (an unresolved -Tags parameter with + # a real -Attributes escape hatch) whose endpoint ALSO has an addable body-property gap + # ('label') -- this gap must stay a bare string, never enriched, per this task's + # confidence-gating design (see Build-PfbApiDriftReport.ps1's own .NOTES). + Set-Content -Path (Join-Path $publicDir 'Update-PfbFixtureGizmo.ps1') -Value @' +function Update-PfbFixtureGizmo { + [CmdletBinding()] + param( + [Parameter()] [PSCustomObject]$Array, + [Parameter()] [string[]]$Tags, + [Parameter(Mandatory)] [hashtable]$Attributes + ) + if ($Tags) { Write-Verbose ($Tags -join ',') } + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'gizmos' -Body $Attributes +} +'@ + + # v1: Protocol has 4 values, matching the fixture cmdlet's ValidateSet exactly. Also + # declares 'region' (matching the capability map's claim it's introduced at 9.0) so + # Build-PfbApiDriftReport.ps1's phantom-field cross-check (against the SINGLE newest + # analysed spec -- see Get-PfbParameterCoverageGaps's -CurrentSpecCapabilities) doesn't + # treat it as withdrawn-from-the-API just because this fixture spec's JSON and the + # hand-written capability map fixture would otherwise disagree. + $specV1 = [ordered]@{ + openapi = '3.0.1'; info = @{ version = '9.0' } + paths = [ordered]@{ + '/arrays/performance' = [ordered]@{ + get = [ordered]@{ + parameters = @( + [ordered]@{ name = 'protocol'; 'in' = 'query'; schema = [ordered]@{ type = 'string' }; description = 'Valid values are `nfs`, `smb`, `http`, and `s3`.' } + [ordered]@{ name = 'region'; 'in' = 'query'; schema = [ordered]@{ type = 'string' } } + ) + } + } + } + components = [ordered]@{ schemas = [ordered]@{} } + } + # v2: spec adds 'all' -- the real Get-PfbArrayPerformance -Protocol bug shape. Also + # carries 'region' and 'timezone' forward (see specV1's note above) -- this is the + # NEWEST analysed spec (capability map's generatedFrom ends at 9.1), so it's the one + # Build-PfbApiDriftReport.ps1 actually re-parses for phantom-field exclusion AND (Task + # 5) for enrichment's synopsis/type/array-item lookups and the enum join's schema-kind + # history. + $specV2 = [ordered]@{ + openapi = '3.0.1'; info = @{ version = '9.1' } + paths = [ordered]@{ + '/arrays/performance' = [ordered]@{ + get = [ordered]@{ + parameters = @( + [ordered]@{ name = 'protocol'; 'in' = 'query'; schema = [ordered]@{ type = 'string' }; description = 'Valid values are `all`, `nfs`, `smb`, `http`, and `s3`.' } + [ordered]@{ name = 'region'; 'in' = 'query'; schema = [ordered]@{ type = 'string' } } + [ordered]@{ name = 'timezone'; 'in' = 'query'; schema = [ordered]@{ type = 'string' } } + ) + } + } + '/gadgets' = [ordered]@{ get = [ordered]@{ parameters = @() } } + '/widgets' = [ordered]@{ + patch = [ordered]@{ + requestBody = [ordered]@{ + content = [ordered]@{ + 'application/json' = [ordered]@{ schema = [ordered]@{ '$ref' = '#/components/schemas/WidgetPatch' } } + } + } + } + } + '/gizmos' = [ordered]@{ + patch = [ordered]@{ + # Deliberately fully INLINE (no $ref anywhere in the chain) -- exercises + # OwnerSchema = $null (Task 5's Get-PfbBodyPropertySynopsis/array-item + # lookups both return $null for this field, by design). + requestBody = [ordered]@{ + content = [ordered]@{ + 'application/json' = [ordered]@{ + schema = [ordered]@{ + type = 'object' + properties = [ordered]@{ label = [ordered]@{ type = 'string'; description = 'The gizmo label.' } } + } + } + } + } + } + } + } + components = [ordered]@{ + schemas = [ordered]@{ + WidgetPatch = [ordered]@{ + type = 'object' + properties = [ordered]@{ + color = [ordered]@{ type = 'string'; description = 'The fixture widget color. Valid values are `red`, `blue`, and `green`.' } + count = [ordered]@{ type = 'integer'; format = 'int64'; description = 'Number of fixture widgets.' } + tags = [ordered]@{ type = 'array'; items = [ordered]@{ type = 'string' }; description = 'Fixture widget tags.' } + } + } + } + } + } + $specV1 | ConvertTo-Json -Depth 20 | Set-Content -Path (Join-Path $specsDir 'fb9.0.json') + $specV2 | ConvertTo-Json -Depth 20 | Set-Content -Path (Join-Path $specsDir 'fb9.1.json') + + $script:capabilityMapPath = Join-Path $fixtureRoot 'PfbCapabilityMap.json' + [ordered]@{ + schemaVersion = 1 + generatedFrom = @('9.0', '9.1') + endpoints = [ordered]@{ + 'GET /arrays/performance' = [ordered]@{ minVersion = '9.0'; parameters = [ordered]@{ protocol = '9.0'; region = '9.0'; timezone = '9.1'; 'X-Request-ID' = '9.0'; continuation_token = '9.0'; offset = '9.0' }; bodyProperties = [ordered]@{} } + 'GET /gadgets' = [ordered]@{ minVersion = '9.1'; parameters = [ordered]@{}; bodyProperties = [ordered]@{} } + 'GET /widgets' = [ordered]@{ minVersion = '9.0'; parameters = [ordered]@{}; bodyProperties = [ordered]@{} } + 'PATCH /widgets' = [ordered]@{ minVersion = '9.0'; parameters = [ordered]@{}; bodyProperties = [ordered]@{ color = '9.0'; count = '9.0'; tags = '9.0' } } + 'PATCH /gizmos' = [ordered]@{ minVersion = '9.1'; parameters = [ordered]@{}; bodyProperties = [ordered]@{ label = '9.1' } } + } + } | ConvertTo-Json -Depth 20 | Set-Content -Path $capabilityMapPath + + $script:fieldCmdletMapPath = Join-Path $fixtureRoot 'PfbFieldCmdletMap.json' + [ordered]@{ + schemaVersion = 1 + generatedFrom = @('9.0', '9.1') + entries = @( + [ordered]@{ cmdlet = 'New-PfbFixtureWidget'; parameter = 'Color'; wireName = 'color'; status = 'matched'; matchedKey = 'Widget.color'; specValues = @('red', 'blue'); stableSinceOldestVersion = $true; recommendation = 'ValidateSet' } + ) + attributesOnly = @() + typedUnresolved = @() + } | ConvertTo-Json -Depth 20 | Set-Content -Path $fieldCmdletMapPath + + $script:outputPath = Join-Path $TestDrive 'output/PfbApiDriftReport.json' + $script:reportPath = Join-Path $TestDrive 'output/PfbApiDriftReport.md' + + & $builderScript -SpecsDirectory $specsDir -PublicDirectory $publicDir -PrivateDirectory $privateDir ` + -CapabilityMapPath $capabilityMapPath -FieldCmdletMapPath $fieldCmdletMapPath ` + -OutputPath $outputPath -ReportPath $reportPath + + $script:manifest = Get-Content -Path $outputPath -Raw | ConvertFrom-Json -Depth 20 +} + +Describe 'Build-PfbApiDriftReport' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + It 'category 1: flags GET /gadgets as an uncovered endpoint' { + ($manifest.uncoveredEndpoints | Where-Object { $_.endpoint -eq 'GET /gadgets' }) | Should -Not -BeNullOrEmpty + } + + It 'category 3: flags the Protocol ValidateSet missing the spec''s newly-added "all" value' { + $rec = $manifest.validateSetDrift | Where-Object { $_.cmdlet -eq 'Get-PfbFixtureArrayPerformance' -and $_.parameter -eq 'Protocol' } + $rec.missingValues | Should -Contain 'all' + } + + It 'category 4: passes Build-PfbFieldCmdletMap.ps1''s matched entries through unchanged' { + $rec = $manifest.newValidateSetCandidates | Where-Object { $_.cmdlet -eq 'New-PfbFixtureWidget' } + $rec.parameter | Should -Be 'Color' + } + + It 'writes both a JSON manifest and a Markdown report' { + Test-Path $outputPath | Should -BeTrue + Test-Path $reportPath | Should -BeTrue + } + + It 'the JSON manifest contains no non-deterministic content (no timestamp fields)' { + $manifest.PSObject.Properties.Name | Should -Not -Contain 'generatedAt' + $manifest.PSObject.Properties.Name | Should -Not -Contain 'timestamp' + } + + It 'without -SinceVersion, sinceVersion is not set and older gaps are present' { + $manifest.sinceVersion | Should -BeNullOrEmpty + ($manifest.uncoveredEndpoints | Where-Object { $_.endpoint -eq 'GET /widgets' }) | Should -Not -BeNullOrEmpty + } + + It 'never reports X-Request-ID as a missing query parameter, even though the fixture endpoint has it' { + $gap = $manifest.parameterGaps | Where-Object { $_.endpoint -eq 'GET /arrays/performance' } + $gap.missingQueryParameters | Should -Not -Contain 'X-Request-ID' + $gap.missingQueryParameters | Should -Contain 'region' + } + + It 'never reports continuation_token or offset as a missing query parameter, even though the fixture endpoint has both' { + $gap = $manifest.parameterGaps | Where-Object { $_.endpoint -eq 'GET /arrays/performance' } + $gap.missingQueryParameters | Should -Not -Contain 'continuation_token' + $gap.missingQueryParameters | Should -Not -Contain 'offset' + $gap.missingQueryParameters | Should -Contain 'region' + } + + Context 'Task 5: enrichment + enum join on a high-confidence endpoint (PATCH /widgets)' { + BeforeAll { + $script:widgetGap = $manifest.parameterGaps | Where-Object { $_.endpoint -eq 'PATCH /widgets' } + $script:colorRecord = $widgetGap.missingBodyProperties | Where-Object { $_.name -eq 'color' } + $script:countRecord = $widgetGap.missingBodyProperties | Where-Object { $_.name -eq 'count' } + $script:tagsRecord = $widgetGap.missingBodyProperties | Where-Object { $_.name -eq 'tags' } + } + + It 'is high-confidence (fully typed cmdlet, no unresolved surface)' { + $widgetGap.confidence.level | Should -Be 'high' + } + + It 'turns each addable body-property gap into a RECORD, not a bare string' { + $colorRecord | Should -Not -BeNullOrEmpty + $colorRecord.name | Should -Be 'color' + $colorRecord.type | Should -Be 'string' + } + + It 'resolves enumStatus matched and enumValues via the real Resolve-PfbFieldValueEnum join (not a bare-name lookup)' { + $colorRecord.enumStatus | Should -Be 'matched' + $colorRecord.enumValues | Should -Be @('red', 'blue', 'green') + } + + It 'extracts synopsis as the first sentence only, newline-normalised' { + $colorRecord.synopsis | Should -Be 'The fixture widget color.' + } + + It 'maps type:integer,format:int64 to suggestedPowerShellType [long], never the truncating [int]' { + $countRecord.format | Should -Be 'int64' + $countRecord.suggestedPowerShellType | Should -Be '[long]' + } + + It 'maps type:array with inline items.type:string to suggestedPowerShellType [string[]]' { + $tagsRecord.type | Should -Be 'array' + $tagsRecord.suggestedPowerShellType | Should -Be '[string[]]' + } + + It 'never maps specRequired to [Parameter(Mandatory)] -- specRequired is present as plain metadata only' { + $colorRecord.PSObject.Properties.Name | Should -Contain 'specRequired' + $colorRecord.specRequired | Should -Be $false + } + + It 'carries target insertion-point coordinates matching this cmdlet''s own dominant (index) assignment style' { + $colorRecord.target.file | Should -Match 'Update-PfbFixtureWidget\.ps1$' + $colorRecord.target.payloadVariable | Should -Be 'body' + $colorRecord.target.assignmentStyle | Should -Be 'index' + $colorRecord.target.hasAttributes | Should -BeFalse + $colorRecord.target.paramBlockLine | Should -BeGreaterThan 0 + } + } + + It 'REGRESSION: a high-confidence endpoint with an EMPTY MissingBodyProperties serializes as [] in JSON, never a phantom one-element array with a blank name' { + # Real-data-only bug, invisible to any in-memory-only check: a query-only + # high-confidence gap (GET /arrays/performance has body-property gaps at all here) + # -- `$missingBodyProperties = if (...) {...} else {...}` (missing an outer @() + # around the WHOLE if/else, only wrapping each branch's own content) let an empty + # result collapse to a value that read as 0 elements in-memory but round-tripped + # through ConvertTo-Json/ConvertFrom-Json into a 1-element array containing a + # single $null -- inflating the high-confidence addable-gap total from 402 to 682 + # on the real capability map. $manifest here is loaded from the actual JSON FILE + # this script wrote (see BeforeAll), not the in-memory array, so this test would + # NOT have caught the bug if it only inspected pre-serialization objects. + $perfGap = $manifest.parameterGaps | Where-Object { $_.endpoint -eq 'GET /arrays/performance' } + $perfGap.confidence.level | Should -Be 'high' + @($perfGap.missingBodyProperties).Count | Should -Be 0 + } + + Context 'Task 5: the query-vs-body vs. confidence-level enrichment asymmetry' { + BeforeAll { + $script:gizmoGap = $manifest.parameterGaps | Where-Object { $_.endpoint -eq 'PATCH /gizmos' } + } + + It 'PATCH /gizmos is partial-confidence (an unresolved -Tags parameter with an -Attributes escape hatch)' { + $gizmoGap.confidence.level | Should -Be 'partial' + } + + It 'leaves a partial-confidence endpoint''s missingBodyProperties as BARE STRINGS, never enriched records' { + $gizmoGap.missingBodyProperties | Should -Contain 'label' + ($gizmoGap.missingBodyProperties | Where-Object { $_ -is [string] }) | Should -Be @('label') + } + + It 'leaves missingQueryParameters as bare strings on a high-confidence endpoint too (query gaps are never enriched, regardless of confidence)' { + $arraysPerfGap = $manifest.parameterGaps | Where-Object { $_.endpoint -eq 'GET /arrays/performance' } + ($arraysPerfGap.missingQueryParameters | ForEach-Object { $_ -is [string] }) | Should -Not -Contain $false + } + } +} + +Describe 'Build-PfbApiDriftReport -SinceVersion filter' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + BeforeAll { + $script:filteredOutputPath = Join-Path $TestDrive 'output/PfbApiDriftReportSince.json' + $script:filteredReportPath = Join-Path $TestDrive 'output/PfbApiDriftReportSince.md' + & $builderScript -SpecsDirectory $specsDir -PublicDirectory $publicDir -PrivateDirectory $privateDir ` + -CapabilityMapPath $capabilityMapPath -FieldCmdletMapPath $fieldCmdletMapPath ` + -OutputPath $filteredOutputPath -ReportPath $filteredReportPath -SinceVersion '9.0' + $script:filteredManifest = Get-Content -Path $filteredOutputPath -Raw | ConvertFrom-Json -Depth 20 + $script:filteredReportText = Get-Content -Path $filteredReportPath -Raw + } + + It 'records the requested SinceVersion in the manifest' { + $filteredManifest.sinceVersion | Should -Be '9.0' + } + + It 'excludes an uncovered endpoint introduced at or before -SinceVersion' { + ($filteredManifest.uncoveredEndpoints | Where-Object { $_.endpoint -eq 'GET /widgets' }) | Should -BeNullOrEmpty + } + + It 'keeps an uncovered endpoint introduced after -SinceVersion' { + ($filteredManifest.uncoveredEndpoints | Where-Object { $_.endpoint -eq 'GET /gadgets' }) | Should -Not -BeNullOrEmpty + } + + It 'filters a parameter gap down to only fields introduced after -SinceVersion' { + $gap = $filteredManifest.parameterGaps | Where-Object { $_.endpoint -eq 'GET /arrays/performance' } + $gap.missingQueryParameters | Should -Be @('timezone') + } + + It 'notes the SinceVersion filter in the Markdown report' { + $filteredReportText | Should -Match 'introduced after REST 9\.0' + } +} + +Describe 'Build-PfbApiDriftReport determinism (Task 8: end-to-end round-trip, same inputs, two separate output paths, byte-identical)' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + # Existing coverage elsewhere in this file only asserts "no timestamp fields" (see the + # non-deterministic-content test above) and, inside tools/lib/PfbApiDriftTools.ps1 itself, + # alphabetical-ordering guarantees on individual collections (Sort-Object on + # MissingQueryParameters/MissingBodyProperties/ReadOnlyFields -- see that file's own + # load-bearing comment on why: Dictionary/PSCustomObject property enumeration order is not + # guaranteed stable run-to-run, and two Hashtable-staged candidate sets were once observed to + # reorder purely from .NET's per-process-randomized string hash codes). Neither is an + # end-to-end test: nothing here has ever run the FULL builder script twice against IDENTICAL + # inputs and diffed the two written files byte-for-byte. That gap matters more now than it did + # before this task: Tasks 4-7 added three entirely new collections (systemicGaps, + # conventionStrength, plus per-row confidence/annotations) since the last such check would have + # been meaningful, and the weekly CI workflow (update-api-capability-map.yml) opens a PR on ANY + # diff in the committed Reports/ output -- a single non-deterministic field anywhere in the + # manifest would make that job spuriously fire on every run, forever, even with zero real API + # drift. + BeforeAll { + $script:detOutputA = Join-Path $TestDrive 'determinism/runA/report.json' + $script:detReportA = Join-Path $TestDrive 'determinism/runA/report.md' + $script:detOutputB = Join-Path $TestDrive 'determinism/runB/report.json' + $script:detReportB = Join-Path $TestDrive 'determinism/runB/report.md' + & $builderScript -SpecsDirectory $specsDir -PublicDirectory $publicDir -PrivateDirectory $privateDir ` + -CapabilityMapPath $capabilityMapPath -FieldCmdletMapPath $fieldCmdletMapPath ` + -OutputPath $detOutputA -ReportPath $detReportA + & $builderScript -SpecsDirectory $specsDir -PublicDirectory $publicDir -PrivateDirectory $privateDir ` + -CapabilityMapPath $capabilityMapPath -FieldCmdletMapPath $fieldCmdletMapPath ` + -OutputPath $detOutputB -ReportPath $detReportB + } + + It 'produces a byte-identical JSON manifest across two independent runs against the same inputs' { + $hashA = (Get-FileHash -Path $detOutputA -Algorithm SHA256).Hash + $hashB = (Get-FileHash -Path $detOutputB -Algorithm SHA256).Hash + $hashA | Should -Be $hashB + } + + It 'produces a byte-identical Markdown report across two independent runs against the same inputs' { + $hashA = (Get-FileHash -Path $detReportA -Algorithm SHA256).Hash + $hashB = (Get-FileHash -Path $detReportB -Algorithm SHA256).Hash + $hashA | Should -Be $hashB + } +} + +Describe 'Build-PfbApiDriftReport (real generated artifacts, skips gracefully if absent)' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + BeforeAll { + $script:realCapabilityMapPath = Join-Path $repoRoot 'Data/PfbCapabilityMap.json' + $script:realFieldCmdletMapPath = Join-Path $repoRoot 'Reports/PfbFieldCmdletMap.json' + $script:realSpecsDir = Join-Path $repoRoot 'tools/specs' + $script:hasRealArtifacts = (Test-Path $realCapabilityMapPath) -and (Test-Path $realFieldCmdletMapPath) -and + (Test-Path $realSpecsDir) -and (Get-ChildItem $realSpecsDir -Filter 'fb*.json' -ErrorAction SilentlyContinue) + + if ($hasRealArtifacts) { + $script:realOutput = Join-Path $TestDrive 'realOutput/report.json' + $script:realReport = Join-Path $TestDrive 'realOutput/report.md' + & $builderScript -SpecsDirectory $realSpecsDir -PublicDirectory (Join-Path $repoRoot 'Public') -PrivateDirectory (Join-Path $repoRoot 'Private') ` + -CapabilityMapPath $realCapabilityMapPath -FieldCmdletMapPath $realFieldCmdletMapPath ` + -OutputPath $realOutput -ReportPath $realReport + $script:realManifest = Get-Content -Path $realOutput -Raw | ConvertFrom-Json -Depth 20 + $script:realCapMapForCheck = Get-Content -Path $realCapabilityMapPath -Raw | ConvertFrom-Json -Depth 20 + + function Get-RealRecountedEndpointCount { + param([Parameter(Mandatory)][object[]]$Gaps, [Parameter(Mandatory)][string]$FieldName) + $endpoints = [System.Collections.Generic.HashSet[string]]::new() + foreach ($g in $Gaps) { + $queryNames = @($g.missingQueryParameters) + $bodyNames = @($g.missingBodyProperties | ForEach-Object { if ($_ -is [string]) { $_ } else { $_.name } }) + if (($queryNames -contains $FieldName) -or ($bodyNames -contains $FieldName)) { + [void]$endpoints.Add($g.endpoint) + } + } + return $endpoints.Count + } + } + } + + Context 'REGRESSION: every emitted path is repo-relative -- these artifacts are COMMITTED' { + # An absolute path here bakes the generating machine's directory layout into the + # repository. Two concrete harms, both observed in the wild before this guard existed: + # 1. It published a developer's home-directory path (`C:\Users\\...`) to the + # repo -- 88 occurrences in each of PfbApiDriftReport.json and .md on main. + # 2. It made the committed file depend on WHERE it was generated. Regenerating from a + # git worktree instead of the main checkout rewrote all 88 lines, producing 174 of + # 279 diff lines of pure churn that buried the 3 real changes under review. + # `target.file` was always relative; `confidence.unresolvedParameters[].file` was not. + + It 'emits no absolute path in any unresolvedParameters entry' { + if (-not $hasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $offenders = foreach ($g in $realManifest.parameterGaps) { + foreach ($u in @($g.confidence.unresolvedParameters)) { + if ($u.file -and ($u.file -match '^[A-Za-z]:[\\/]' -or $u.file -match '^[\\/]{1,2}')) { + "$($g.endpoint) -$($u.parameter): $($u.file)" + } + } + } + @($offenders) | Should -BeNullOrEmpty -Because 'unresolvedParameters[].file must be repo-relative, like target.file' + } + + It 'emits no absolute path in any enriched body-property target entry' { + if (-not $hasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + # NOTE: `target` hangs off each ENRICHED missingBodyProperties[] entry, not off the + # gap itself. An earlier draft of this test read $g.target.file -- always $null, so + # it passed vacuously and survived the mutation that broke its two siblings. Assert + # a non-zero population so it can never silently go hollow again. + $targets = foreach ($g in $realManifest.parameterGaps) { + foreach ($p in @($g.missingBodyProperties)) { + if ($p -isnot [string] -and $p.target -and $p.target.file) { + [pscustomobject]@{ Endpoint = $g.endpoint; Field = $p.name; File = $p.target.file } + } + } + } + @($targets).Count | Should -BeGreaterThan 0 -Because 'a vacuous scan would pass this test without checking anything' + $offenders = @($targets | Where-Object { $_.File -match '^[A-Za-z]:[\\/]' -or $_.File -match '^[\\/]{1,2}' }) + $offenders | Should -BeNullOrEmpty + } + + It 'leaks no absolute path into the rendered markdown report' { + if (-not $hasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $md = Get-Content $realReport -Raw + [regex]::Matches($md, '[A-Za-z]:\\{1,2}Users').Count | Should -Be 0 + [regex]::Matches($md, '/home/runner').Count | Should -Be 0 + } + } + + + It 'produces a manifest against the real Public/Private tree and Reports/ + Data/ inputs' { + if (-not $hasRealArtifacts) { Set-ItResult -Skipped -Because 'Data/PfbCapabilityMap.json, Reports/PfbFieldCmdletMap.json, or tools/specs/ not present locally'; return } + Test-Path $realOutput | Should -BeTrue + } + + It 'Task 7: wires systemicGaps through -- endpointCount for context_names/allow_errors matches an independent recount straight from parameterGaps' { + if (-not $hasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + + # Recomputes endpointCount from the manifest's own high-confidence parameterGaps, + # completely independently of Get-PfbSystemicGaps' internal aggregation. Deliberately + # does NOT hardcode the real API surface's current size: the assertion is "the + # summary field matches a fresh tally of the detail rows", which stays true forever, + # rather than "the detail rows currently total exactly N", which breaks the instant + # an unrelated PR adds or removes one endpoint missing context_names/allow_errors. + # See docs/superpowers/plans/2026-07-30-drift-report-acceptance-figure-invariants.md. + $highConfidenceGaps = @($realManifest.parameterGaps | Where-Object { $_.confidence.level -eq 'high' }) + foreach ($fieldName in @('context_names', 'allow_errors')) { + $finding = $realManifest.systemicGaps | Where-Object { $_.name -eq $fieldName } + $finding | Should -Not -BeNullOrEmpty -Because "$fieldName is expected to still be a systemic gap in the real API surface" + $recount = Get-RealRecountedEndpointCount -Gaps $highConfidenceGaps -FieldName $fieldName + $finding.endpointCount | Should -Be $recount -Because 'endpointCount must equal a fresh tally over parameterGaps, independent of the total endpoint count' + $finding.endpointCount | Should -BeGreaterThan 0 -Because 'a vacuous/zero count would mean the field silently stopped being a systemic gap without anyone noticing here' + } + } + + It 'Task 7: wires conventionStrength through -- cmdletCount is internally consistent and non-vacuous for established conventions, and remains 0 for context_names by design' { + if (-not $hasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + + foreach ($fieldName in @('names', 'ids')) { + $entry = $realManifest.conventionStrength | Where-Object { $_.name -eq $fieldName } + $entry | Should -Not -BeNullOrEmpty -Because "$fieldName is expected to still be a systemic gap with an established convention" + # cmdletCount and cmdlets.Count are set from the same value at generation time + # (tools/Build-PfbApiDriftReport.ps1's `cmdletCount = $_.CmdletCount`) but travel + # through JSON serialization separately -- this catches a stale/mismatched field + # surviving a future refactor, without caring how many cmdlets there actually are. + # The property-existence check guards against @($null).Count being 1 in + # PowerShell -- without it, a `cmdlets` property that vanished entirely from the + # manifest would be indistinguishable from one holding exactly one cmdlet. + $entry.PSObject.Properties.Name | Should -Contain 'cmdlets' -Because 'the cmdlets property must actually be present to compare its Count against cmdletCount' + $entry.cmdletCount | Should -Be @($entry.cmdlets).Count -Because 'cmdletCount must match the actual cmdlets array length after JSON round-trip' + $entry.cmdletCount | Should -BeGreaterThan 0 -Because "$fieldName is a widely-adopted convention; a drop to zero would be a real regression worth failing loudly for" + } + + # context_names is the ONE name this report expects to have NO adopting cmdlets -- + # Get-PfbConventionStrength's own docstring: "that zero IS the finding for names like + # context_names". This is an architectural fact (see the Fusion context_names design + # conclusion), not a live-count regression canary -- it stays an exact pin + # deliberately, unlike names/ids above. + ($realManifest.conventionStrength | Where-Object { $_.name -eq 'context_names' }).cmdletCount | Should -Be 0 + } + + It 'Task 7: attaches docs/drift-annotations.json''s designDecision note to the context_names systemic-gap finding' { + if (-not $hasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $ctx = $realManifest.systemicGaps | Where-Object { $_.name -eq 'context_names' } + $ctx.annotations | Should -Not -BeNullOrEmpty + ($ctx.annotations | Select-Object -First 1).note | Should -Match 'not yet implemented' + } + + It 'Task 7: attaches docs/drift-annotations.json''s liveTestingHazard note to a management-access-policies parameter-gap row' { + if (-not $hasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $row = $realManifest.parameterGaps | Where-Object { $_.endpoint -match 'management-access-policies' } | Select-Object -First 1 + if (-not $row) { Set-ItResult -Skipped -Because 'no management-access-policies endpoint currently has a parameter gap'; return } + $row.annotations | Should -Not -BeNullOrEmpty + ($row.annotations | Select-Object -First 1).note | Should -Match '403' + } + + It 'Task 7: splits generatedFrom into analysedVersions (from the capability map) and availableSpecVersions (from tools/specs/ on disk), warning exactly when they disagree' { + if (-not $hasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $realManifest.analysedVersions | Should -Be @($realCapMapForCheck.generatedFrom) + $onDiskVersions = @(Get-ChildItem $realSpecsDir -Filter 'fb*.json' | ForEach-Object { $_.BaseName -replace '^fb', '' }) + @(Compare-Object -ReferenceObject $realManifest.availableSpecVersions -DifferenceObject $onDiskVersions).Count | Should -Be 0 + $diverges = @(Compare-Object -ReferenceObject $realManifest.analysedVersions -DifferenceObject $realManifest.availableSpecVersions).Count -gt 0 + if ($diverges) { $realManifest.versionDivergenceWarning | Should -Not -BeNullOrEmpty } + else { $realManifest.versionDivergenceWarning | Should -BeNullOrEmpty } + } + + It 'Task 7: phantomFieldCount is a non-negative integer, present on the manifest' { + if (-not $hasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $realManifest.phantomFieldCount | Should -BeGreaterOrEqual 0 + } +} + +Describe 'Build-PfbApiDriftReport (Task 8: regression canaries + spot-checks against the real generated report, skips gracefully if absent)' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + BeforeAll { + $script:t8CapabilityMapPath = Join-Path $repoRoot 'Data/PfbCapabilityMap.json' + $script:t8FieldCmdletMapPath = Join-Path $repoRoot 'Reports/PfbFieldCmdletMap.json' + $script:t8SpecsDir = Join-Path $repoRoot 'tools/specs' + $script:t8HasRealArtifacts = (Test-Path $t8CapabilityMapPath) -and (Test-Path $t8FieldCmdletMapPath) -and + (Test-Path $t8SpecsDir) -and (Get-ChildItem $t8SpecsDir -Filter 'fb*.json' -ErrorAction SilentlyContinue) + + if ($t8HasRealArtifacts) { + $script:t8Output = Join-Path $TestDrive 't8output/report.json' + $script:t8Report = Join-Path $TestDrive 't8output/report.md' + & $builderScript -SpecsDirectory $t8SpecsDir -PublicDirectory (Join-Path $repoRoot 'Public') -PrivateDirectory (Join-Path $repoRoot 'Private') ` + -CapabilityMapPath $t8CapabilityMapPath -FieldCmdletMapPath $t8FieldCmdletMapPath ` + -OutputPath $t8Output -ReportPath $t8Report + $script:t8Manifest = Get-Content -Path $t8Output -Raw | ConvertFrom-Json -Depth 20 + } + + function Get-T8GapFieldNames { + param($Gap) + @($Gap.missingBodyProperties | ForEach-Object { if ($_ -is [string]) { $_ } else { $_.name } }) + } + } + + # PLAN DEFECT #1 correction (see .superpowers/sdd/drift-report-actionable-plan/progress.md): + # the task-8 brief originally listed 13 canaries, but PATCH /certificates|{id,name} are + # confirmed phantoms (readOnly 2.0-2.19, removed entirely 2.20+, never actually settable) -- + # they must NOT be asserted as actionable. Of the remaining 12, issue #31's 56-cmdlet body/ + # query-param pass (PR #66) closed 10 more by adding the corresponding typed parameter -- + # real, intentional fixes, not regressions -- see the consolidated correction test below. + # That leaves exactly 1 confirmed regression against first-sight readOnly semantics: the + # original `foreach` loop with `Should -Contain` threw on the first mismatch and never + # reached the rest, so the other 10 were only uncovered one at a time by re-running against + # the real, regenerated report after fixing whichever one failed first each time. + It 'Task 8 canary: max_role remains an actionable body gap on PATCH /api-clients, since first-sight readOnly semantics would have wrongly suppressed it' { + if (-not $t8HasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $gap = $t8Manifest.parameterGaps | Where-Object { $_.endpoint -eq 'PATCH /api-clients' } + $gap | Should -Not -BeNullOrEmpty -Because 'PATCH /api-clients must have a parameter-gap row' + (Get-T8GapFieldNames -Gap $gap) | Should -Contain 'max_role' -Because 'PATCH /api-clients|max_role is a regression canary' + } + + It 'Task 8 canary correction: PATCH /certificates|{id,name} are phantoms -- NOT actionable body gaps, NOT read-only fields, absent from the endpoint''s row entirely' { + if (-not $t8HasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $gap = $t8Manifest.parameterGaps | Where-Object { $_.endpoint -eq 'PATCH /certificates' } + $gap | Should -Not -BeNullOrEmpty + (Get-T8GapFieldNames -Gap $gap) | Should -Not -Contain 'id' + (Get-T8GapFieldNames -Gap $gap) | Should -Not -Contain 'name' + $gap.readOnlyFields | Should -Not -Contain 'id' + $gap.readOnlyFields | Should -Not -Contain 'name' + } + + It 'Task 8 canary correction: 10 (endpoint, field) pairs closed by issue #31''s write-cmdlet body/query-param pass (PR #66) -- no longer actionable body gaps' { + if (-not $t8HasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $closed = @( + @{ Endpoint = 'PATCH /tls-policies'; Field = 'name' } # Update-PfbTlsPolicy -NewName + @{ Endpoint = 'PATCH /storage-class-tiering-policies'; Field = 'name' } # Update-PfbStorageClassTieringPolicy -NewName + @{ Endpoint = 'PATCH /dns'; Field = 'name' } # Update-PfbDns -NewName + @{ Endpoint = 'PATCH /ssh-certificate-authority-policies'; Field = 'name' } # Update-PfbSshCaPolicy -NewName + @{ Endpoint = 'PATCH /ssh-certificate-authority-policies'; Field = 'location' } # Update-PfbSshCaPolicy -Location + @{ Endpoint = 'PATCH /targets'; Field = 'ca_certificate_group' } # Update-PfbTarget -CaCertificateGroup + @{ Endpoint = 'PATCH /array-connections'; Field = 'ca_certificate_group' } # Update-PfbArrayConnection -CaCertificateGroup + @{ Endpoint = 'POST /array-connections'; Field = 'ca_certificate_group' } # New-PfbArrayConnection -CaCertificateGroup + @{ Endpoint = 'PATCH /hardware-connectors'; Field = 'port_speed' } # Update-PfbHardwareConnector -PortSpeed + @{ Endpoint = 'PATCH /network-interfaces/connectors'; Field = 'port_speed' } # Update-PfbNetworkInterfaceConnector -PortSpeed + ) + foreach ($c in $closed) { + $gap = $t8Manifest.parameterGaps | Where-Object { $_.endpoint -eq $c.Endpoint } + if ($gap) { (Get-T8GapFieldNames -Gap $gap) | Should -Not -Contain $c.Field -Because "$($c.Endpoint)|$($c.Field) should now be covered by a typed parameter" } + } + } + + It 'Task 8 spot-check: all five policy-family PATCH endpoints list is_local and policy_type as read-only' { + if (-not $t8HasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + foreach ($ep in @('PATCH /worm-data-policies', 'PATCH /qos-policies', 'PATCH /ssh-certificate-authority-policies', 'PATCH /storage-class-tiering-policies', 'PATCH /tls-policies')) { + $gap = $t8Manifest.parameterGaps | Where-Object { $_.endpoint -eq $ep } + $gap | Should -Not -BeNullOrEmpty -Because "$ep must have a parameter-gap row" + $gap.readOnlyFields | Should -Contain 'is_local' -Because "$ep|is_local" + $gap.readOnlyFields | Should -Contain 'policy_type' -Because "$ep|policy_type" + } + } + + It 'Task 8 spot-check correction: PATCH /certificates / generate_new_key is closed (issue #31 added -GenerateNewKey) -- no longer a query-parameter gap' { + if (-not $t8HasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $gap = $t8Manifest.parameterGaps | Where-Object { $_.endpoint -eq 'PATCH /certificates' } + if ($gap) { $gap.missingQueryParameters | Should -Not -Contain 'generate_new_key' -Because 'Update-PfbCertificate -GenerateNewKey now covers this field' } + } + + It 'Task 8 spot-check: PATCH /directory-services/roles / management_access_policies is read-only' { + if (-not $t8HasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $gap = $t8Manifest.parameterGaps | Where-Object { $_.endpoint -eq 'PATCH /directory-services/roles' } + $gap.readOnlyFields | Should -Contain 'management_access_policies' + } + + It 'Task 8 spot-check: PATCH /management-access-policies read-only set is exactly context, id, is_local, policy_type, realms, version' { + if (-not $t8HasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $gap = $t8Manifest.parameterGaps | Where-Object { $_.endpoint -eq 'PATCH /management-access-policies' } + (@($gap.readOnlyFields) | Sort-Object) | Should -Be @('context', 'id', 'is_local', 'policy_type', 'realms', 'version') + } + + It 'Task 8 correction: Update-PfbFileSystemExport''s genuinely read-only set is context, enabled, id, name, policy_type, status -- member/server are settable' { + if (-not $t8HasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $gap = $t8Manifest.parameterGaps | Where-Object { $_.endpoint -eq 'PATCH /file-system-exports' } + (@($gap.readOnlyFields) | Sort-Object) | Should -Be @('context', 'enabled', 'id', 'name', 'policy_type', 'status') + $gap.readOnlyFields | Should -Not -Contain 'member' + $gap.readOnlyFields | Should -Not -Contain 'server' + } + + It 'Task 8 correction: Update-PfbCertificate''s read-only set includes realms, beyond the five originally named' { + if (-not $t8HasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $gap = $t8Manifest.parameterGaps | Where-Object { $_.endpoint -eq 'PATCH /certificates' } + $gap.readOnlyFields | Should -Contain 'realms' + } + + Context '"Nothing vanishes" invariant: every capability-map candidate field lands in exactly one of reported/phantom-excluded' { + # Every query parameter and body property Data/PfbCapabilityMap.json lists for an + # endpoint an existing cmdlet calls, that isn't already exposed as a Typed parameter and + # isn't one of the non-actionable placeholder fields (X-Request-ID/continuation_token/ + # offset -- see Get-PfbNonActionableParameters, a documented, deliberate exclusion + # unrelated to phantom detection), must land in EXACTLY ONE of: the real report's + # missingQueryParameters/missingBodyProperties (addable)/readOnlyFields, or the + # phantom-exclusion set (absent from the newest analysed spec). Verified arithmetically + # over (endpoint, list, field) triples -- never bare field names, since a field name can + # repeat across more than one endpoint's row -- using the SAME phantom-diffing technique + # tools/Build-PfbApiDriftReport.ps1 itself already uses (a second + # Get-PfbParameterCoverageGaps call without -CurrentSpecCapabilities), never a + # re-derivation of the phantom-detection logic. + BeforeAll { + if ($t8HasRealArtifacts) { + . (Join-Path $repoRoot 'tools/lib/PfbSpecTools.ps1') + . (Join-Path $repoRoot 'tools/lib/PfbValueEnumTools.ps1') + . (Join-Path $repoRoot 'tools/lib/PfbCmdletParamTools.ps1') + . (Join-Path $repoRoot 'tools/lib/PfbApiDriftTools.ps1') + $script:t8Inventory = Get-PfbCmdletParameterInventory -PublicDirectory (Join-Path $repoRoot 'Public') + $script:t8CalledEndpoints = Get-PfbModuleCalledEndpoints -PublicDirectory (Join-Path $repoRoot 'Public') -PrivateDirectory (Join-Path $repoRoot 'Private') + $script:t8CapMap = Get-Content -Path $t8CapabilityMapPath -Raw | ConvertFrom-Json -Depth 20 + $script:t8NonActionable = Get-PfbNonActionableParameters -PrivateDirectory (Join-Path $repoRoot 'Private') + + $newestAnalysedVersion8 = $t8CapMap.generatedFrom | Select-Object -Last 1 + $newestSpecPath8 = Join-Path $t8SpecsDir "fb$newestAnalysedVersion8.json" + $newestSpec8 = Get-Content -Path $newestSpecPath8 -Raw | ConvertFrom-Json -Depth 64 + $script:t8CurrentSpecCapabilities = @(Get-PfbSpecCapabilities -Spec $newestSpec8) + + function Get-T8TripleSet { + param([object[]]$Gaps) + $set = [System.Collections.Generic.HashSet[string]]::new() + foreach ($g in $Gaps) { + foreach ($f in @($g.MissingQueryParameters)) { [void]$set.Add("$($g.Endpoint)|query|$f") } + foreach ($f in @($g.MissingBodyProperties)) { [void]$set.Add("$($g.Endpoint)|body|$f") } + foreach ($f in @($g.ReadOnlyFields)) { [void]$set.Add("$($g.Endpoint)|readOnly|$f") } + } + return $set + } + + # Population: every real candidate field (Typed-exposed and non-actionable + # placeholder fields already excluded), BEFORE phantom filtering. + $script:t8PopulationRaw = @(Get-PfbParameterCoverageGaps -CapabilityMap $t8CapMap -CmdletInventory $t8Inventory -CalledEndpoints $t8CalledEndpoints -ExcludedFields $t8NonActionable -CurrentSpecCapabilities @()) + $script:t8PopulationSet = Get-T8TripleSet -Gaps $t8PopulationRaw + + # Reported: the exact same call Build-PfbApiDriftReport.ps1 itself makes. + $script:t8ReportedRaw = @(Get-PfbParameterCoverageGaps -CapabilityMap $t8CapMap -CmdletInventory $t8Inventory -CalledEndpoints $t8CalledEndpoints -ExcludedFields $t8NonActionable -CurrentSpecCapabilities $t8CurrentSpecCapabilities) + $script:t8ReportedSet = Get-T8TripleSet -Gaps $t8ReportedRaw + + $script:t8PhantomExcludedSet = [System.Collections.Generic.HashSet[string]]::new([string[]]@($t8PopulationSet)) + $t8PhantomExcludedSet.ExceptWith([string[]]@($t8ReportedSet)) + } + } + + It 'reported UNION phantom-excluded exactly equals the full candidate population -- nothing missing, nothing extra' { + if (-not $t8HasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $union = [System.Collections.Generic.HashSet[string]]::new([string[]]@($t8ReportedSet)) + $union.UnionWith([string[]]@($t8PhantomExcludedSet)) + $missingFromUnion = [System.Collections.Generic.HashSet[string]]::new([string[]]@($t8PopulationSet)) + $missingFromUnion.ExceptWith([string[]]@($union)) + $extraInUnion = [System.Collections.Generic.HashSet[string]]::new([string[]]@($union)) + $extraInUnion.ExceptWith([string[]]@($t8PopulationSet)) + $missingFromUnion.Count | Should -Be 0 + $extraInUnion.Count | Should -Be 0 + } + + It 'reported and phantom-excluded are disjoint -- no (endpoint, list, field) triple lands in both buckets' { + if (-not $t8HasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $overlap = [System.Collections.Generic.HashSet[string]]::new([string[]]@($t8ReportedSet)) + $overlap.IntersectWith([string[]]@($t8PhantomExcludedSet)) + $overlap.Count | Should -Be 0 + } + + It 'the phantom-excluded count matches the real manifest''s phantomFieldCount exactly (self-consistent, full population)' { + if (-not $t8HasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $t8PhantomExcludedSet.Count | Should -Be $t8Manifest.phantomFieldCount + } + + It 'restricting the same diff to high-confidence-only gaps still yields a non-vacuous phantom-exclusion set' { + if (-not $t8HasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $populationHigh = @($t8PopulationRaw | Where-Object { $_.Confidence.Level -eq 'high' }) + $reportedHigh = @($t8ReportedRaw | Where-Object { $_.Confidence.Level -eq 'high' }) + $populationHighSet = Get-T8TripleSet -Gaps $populationHigh + $reportedHighSet = Get-T8TripleSet -Gaps $reportedHigh + $phantomHighSet = [System.Collections.Generic.HashSet[string]]::new([string[]]@($populationHighSet)) + $phantomHighSet.ExceptWith([string[]]@($reportedHighSet)) + # No independent recount is available here (unlike phantomFieldCount above, which + # self-consistency-checks against the manifest) -- so this can only assert + # non-vacuousness. The doc-comment example this used to pin against + # (tools/lib/PfbApiDriftTools.ps1's Get-PfbParameterCoverageGaps: "13 real + # (endpoint, field) pairs hit this against fb2.27 today") is illustrative prose, + # not a contract; pinning a test to that exact number reproduced the same + # live-data-literal anti-pattern this whole effort exists to remove. + $phantomHighSet.Count | Should -BeGreaterThan 0 -Because 'a vacuous/zero count would mean high-confidence phantom-field exclusion silently stopped happening without anyone noticing here' + } + + It 'the in-memory reported set matches the real committed Reports/PfbApiDriftReport.json on disk exactly (no serialization-only divergence)' { + if (-not $t8HasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + $committedReportPath = Join-Path $repoRoot 'Reports/PfbApiDriftReport.json' + if (-not (Test-Path $committedReportPath)) { Set-ItResult -Skipped -Because 'Reports/PfbApiDriftReport.json not present locally'; return } + $committedManifest = Get-Content -Path $committedReportPath -Raw | ConvertFrom-Json -Depth 20 + $committedTripleSet = [System.Collections.Generic.HashSet[string]]::new() + foreach ($g in $committedManifest.parameterGaps) { + foreach ($f in @($g.missingQueryParameters)) { [void]$committedTripleSet.Add("$($g.endpoint)|query|$f") } + foreach ($f in @($g.missingBodyProperties)) { + $name = if ($f -is [string]) { $f } else { $f.name } + [void]$committedTripleSet.Add("$($g.endpoint)|body|$name") + } + foreach ($f in @($g.readOnlyFields)) { [void]$committedTripleSet.Add("$($g.endpoint)|readOnly|$f") } + } + $diff = [System.Collections.Generic.HashSet[string]]::new([string[]]@($t8ReportedSet)) + $diff.SymmetricExceptWith([string[]]@($committedTripleSet)) + $diff.Count | Should -Be 0 + } + } +} + +Describe 'Build-PfbApiDriftReport (Task 7: systemic gaps, convention strength, phantom-field count, generatedFrom split -- own small fixture)' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + BeforeAll { + $script:t7FixtureRoot = Join-Path $TestDrive 't7fixture' + $t7PublicDir = Join-Path $t7FixtureRoot 'Public/Fixture7' + $t7PrivateDir = Join-Path $t7FixtureRoot 'Private' + $t7SpecsDir = Join-Path $t7FixtureRoot 'specs' + New-Item -ItemType Directory -Path $t7PublicDir, $t7PrivateDir, $t7SpecsDir -Force | Out-Null + + # Two endpoints, each missing the SAME query parameter ('shared_gap') and neither + # exposing it -- this is what Get-PfbSystemicGaps is supposed to collapse into ONE + # finding with EndpointCount 2. No cmdlet anywhere in this fixture exposes + # 'shared_gap' as a Typed parameter, so Get-PfbConventionStrength should rank it at + # CmdletCount 0 -- the "architectural gap, not a mechanical one" case (the + # context_names precedent). + Set-Content -Path (Join-Path $t7PublicDir 'Get-PfbFixtureAlpha.ps1') -Value @' +function Get-PfbFixtureAlpha { + [CmdletBinding()] + param([Parameter()] [PSCustomObject]$Array) + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'alpha' -AutoPaginate +} +'@ + # Beta is PARTIAL-confidence (an unresolved -Weird parameter, no -Attributes escape + # hatch) -- exercises the Markdown confidence marker/footnote wiring on a fixture + # this task fully controls. It also misses 'shared_gap', but Get-PfbSystemicGaps + # is aggregated ONLY over 'high'-confidence gaps (this task's own precedent, same + # as the real systemic-gaps invariants' high-confidence-only filtering), so Beta must NOT count towards + # 'shared_gap's systemic EndpointCount below -- Gamma (high-confidence) is the + # fixture's second contributor to that finding instead. + Set-Content -Path (Join-Path $t7PublicDir 'Get-PfbFixtureBeta.ps1') -Value @' +function Get-PfbFixtureBeta { + [CmdletBinding()] + param( + [Parameter()] [PSCustomObject]$Array, + [Parameter()] [string]$Weird + ) + if ($Weird) { Write-Verbose $Weird } + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'beta' -AutoPaginate +} +'@ + # Gamma is fully-typed (high-confidence) and also misses 'shared_gap' -- together + # with Alpha, this is the systemic-gaps EndpointCount-2 case. + Set-Content -Path (Join-Path $t7PublicDir 'Get-PfbFixtureGamma.ps1') -Value @' +function Get-PfbFixtureGamma { + [CmdletBinding()] + param([Parameter()] [PSCustomObject]$Array) + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'gamma' -AutoPaginate +} +'@ + + $t7SpecV1 = [ordered]@{ + openapi = '3.0.1'; info = @{ version = '1.0' } + paths = [ordered]@{ + '/alpha' = [ordered]@{ get = [ordered]@{ parameters = @( + [ordered]@{ name = 'shared_gap'; 'in' = 'query'; schema = [ordered]@{ type = 'string' } } + [ordered]@{ name = 'withdrawn_field'; 'in' = 'query'; schema = [ordered]@{ type = 'string' } } + ) } } + '/beta' = [ordered]@{ get = [ordered]@{ parameters = @( + [ordered]@{ name = 'shared_gap'; 'in' = 'query'; schema = [ordered]@{ type = 'string' } } + ) } } + '/gamma' = [ordered]@{ get = [ordered]@{ parameters = @( + [ordered]@{ name = 'shared_gap'; 'in' = 'query'; schema = [ordered]@{ type = 'string' } } + ) } } + } + components = [ordered]@{ schemas = [ordered]@{} } + } + # v1.1 is the NEWEST ANALYSED spec: 'withdrawn_field' is gone -- a phantom field + # (accumulated in the capability map's own /alpha entry below, absent here). + $t7SpecV2 = [ordered]@{ + openapi = '3.0.1'; info = @{ version = '1.1' } + paths = [ordered]@{ + '/alpha' = [ordered]@{ get = [ordered]@{ parameters = @( + [ordered]@{ name = 'shared_gap'; 'in' = 'query'; schema = [ordered]@{ type = 'string' } } + ) } } + '/beta' = [ordered]@{ get = [ordered]@{ parameters = @( + [ordered]@{ name = 'shared_gap'; 'in' = 'query'; schema = [ordered]@{ type = 'string' } } + ) } } + '/gamma' = [ordered]@{ get = [ordered]@{ parameters = @( + [ordered]@{ name = 'shared_gap'; 'in' = 'query'; schema = [ordered]@{ type = 'string' } } + ) } } + } + components = [ordered]@{ schemas = [ordered]@{} } + } + $t7SpecV1 | ConvertTo-Json -Depth 20 | Set-Content -Path (Join-Path $t7SpecsDir 'fb1.0.json') + $t7SpecV2 | ConvertTo-Json -Depth 20 | Set-Content -Path (Join-Path $t7SpecsDir 'fb1.1.json') + # On disk but NOT in the capability map's own generatedFrom below -- deliberately + # engineers the analysedVersions/availableSpecVersions divergence this task's + # generatedFrom-split item exists to surface. + $t7SpecV2 | ConvertTo-Json -Depth 20 | Set-Content -Path (Join-Path $t7SpecsDir 'fb1.2.json') + + $script:t7CapabilityMapPath = Join-Path $t7FixtureRoot 'PfbCapabilityMap.json' + [ordered]@{ + schemaVersion = 1 + generatedFrom = @('1.0', '1.1') + endpoints = [ordered]@{ + 'GET /alpha' = [ordered]@{ minVersion = '1.0'; parameters = [ordered]@{ shared_gap = '1.0'; withdrawn_field = '1.0' }; bodyProperties = [ordered]@{} } + 'GET /beta' = [ordered]@{ minVersion = '1.0'; parameters = [ordered]@{ shared_gap = '1.0' }; bodyProperties = [ordered]@{} } + 'GET /gamma' = [ordered]@{ minVersion = '1.0'; parameters = [ordered]@{ shared_gap = '1.0' }; bodyProperties = [ordered]@{} } + } + } | ConvertTo-Json -Depth 20 | Set-Content -Path $t7CapabilityMapPath + + $script:t7FieldCmdletMapPath = Join-Path $t7FixtureRoot 'PfbFieldCmdletMap.json' + [ordered]@{ schemaVersion = 1; generatedFrom = @('1.0', '1.1'); entries = @(); attributesOnly = @(); typedUnresolved = @() } | + ConvertTo-Json -Depth 20 | Set-Content -Path $t7FieldCmdletMapPath + + $script:t7OutputPath = Join-Path $TestDrive 't7output/report.json' + $script:t7ReportPath = Join-Path $TestDrive 't7output/report.md' + & $builderScript -SpecsDirectory $t7SpecsDir -PublicDirectory $t7PublicDir -PrivateDirectory $t7PrivateDir ` + -CapabilityMapPath $t7CapabilityMapPath -FieldCmdletMapPath $t7FieldCmdletMapPath ` + -OutputPath $t7OutputPath -ReportPath $t7ReportPath + $script:t7Manifest = Get-Content -Path $t7OutputPath -Raw | ConvertFrom-Json -Depth 20 + $script:t7ReportText = Get-Content -Path $t7ReportPath -Raw + } + + It 'collapses the field missing on both HIGH-CONFIDENCE fixture endpoints into ONE systemic-gaps finding with EndpointCount 2, excluding the partial-confidence endpoint that also misses it' { + $finding = $t7Manifest.systemicGaps | Where-Object { $_.name -eq 'shared_gap' } + $finding | Should -Not -BeNullOrEmpty + $finding.endpointCount | Should -Be 2 + $finding.endpoints | Should -Be @('GET /alpha', 'GET /gamma') + } + + It 'ranks conventionStrength at CmdletCount 0 for a name no fixture cmdlet exposes (the architectural-gap case)' { + $strength = $t7Manifest.conventionStrength | Where-Object { $_.name -eq 'shared_gap' } + $strength | Should -Not -BeNullOrEmpty + $strength.cmdletCount | Should -Be 0 + @($strength.cmdlets).Count | Should -Be 0 + } + + It 'counts the withdrawn (phantom) field, filtered out of every list, in phantomFieldCount' { + $t7Manifest.phantomFieldCount | Should -Be 1 + # And confirms it never leaks into a real gap list on GET /alpha. + $alphaGap = $t7Manifest.parameterGaps | Where-Object { $_.endpoint -eq 'GET /alpha' } + $alphaGap.missingQueryParameters | Should -Not -Contain 'withdrawn_field' + } + + It 'splits analysedVersions (capability map) from availableSpecVersions (specs on disk) and warns when they disagree' { + $t7Manifest.analysedVersions | Should -Be @('1.0', '1.1') + $t7Manifest.availableSpecVersions | Should -Be @('1.0', '1.1', '1.2') + $t7Manifest.versionDivergenceWarning | Should -Not -BeNullOrEmpty + $t7Manifest.versionDivergenceWarning | Should -Match 'analysedVersions' + $t7Manifest.versionDivergenceWarning | Should -Match 'availableSpecVersions' + } + + It 'marks the partial-confidence fixture endpoint (GET /beta) with a visible unresolved-parameter marker in the Markdown table' { + $betaGap = $t7Manifest.parameterGaps | Where-Object { $_.endpoint -eq 'GET /beta' } + $betaGap.confidence.level | Should -Be 'partial' + $t7ReportText | Should -Match 'unresolved param' + } + + It 'gives the partial-confidence unresolved parameter a Partial-confidence detail row with a file:line a human can open' { + $t7ReportText | Should -Match '### Partial-confidence detail' + $t7ReportText | Should -Match '-Weird' + $t7ReportText | Should -Match 'Get-PfbFixtureBeta\.ps1:\d+' + } + + It 'orders Markdown sections: How to read this report -> Summary -> Systemic gaps -> Parameter gaps -> Read-only fields -> Uncovered endpoints' { + $howToIndex = $t7ReportText.IndexOf('## How to read this report') + $summaryIndex = $t7ReportText.IndexOf('## Summary') + $systemicIndex = $t7ReportText.IndexOf('## Systemic gaps') + $paramGapsIndex = $t7ReportText.IndexOf('## Parameter gaps') + $howToIndex | Should -BeGreaterThan -1 + $summaryIndex | Should -BeGreaterThan $howToIndex + $systemicIndex | Should -BeGreaterThan $summaryIndex + $paramGapsIndex | Should -BeGreaterThan $systemicIndex + } + + It 'reproduces the decision-6 false-positive procedure verbatim in "How to read this report"' { + $t7ReportText | Should -Match 'This report accepts \*\*false positives in order to eliminate false negatives\*\*' + $t7ReportText | Should -Match 'Open the named parameter at the given `file:line` and follow where its value goes\.' + $t7ReportText | Should -Match 'the gap is a false positive AND a tooling bug' + $t7ReportText | Should -Match 'If it reaches the wire under a different name' + $t7ReportText | Should -Match 'the reported gap may still be real; check that field against the spec' + $t7ReportText | Should -Match 'If it never reaches the wire' + $t7ReportText | Should -Match 'a false positive costs a reader one `file:line` lookup; a false negative costs an undetected gap indefinitely' + } + + It 'the Summary section reports phantom fields and systemic gaps, not just the pre-Task-7 bullets' { + $t7ReportText | Should -Match 'Phantom fields silently excluded[^:]*: 1' + $t7ReportText | Should -Match 'Systemic gaps \(distinct field names[^:]*: 1' + } +} + +Describe 'Build-PfbApiDriftReport response-shape integration' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + # BeforeDiscovery, NOT BeforeAll: Pester evaluates -Skip: at DISCOVERY time, before any + # BeforeAll block has run. Setting these in BeforeAll would leave $script:realJson null + # during discovery, so every -Skip: would evaluate `-not (Test-Path $null)` = $true and + # silently skip the whole Describe -- a green run that tested nothing. + BeforeDiscovery { + $script:reportRoot = Split-Path -Parent $PSScriptRoot + $script:realJson = Join-Path $script:reportRoot 'Reports/PfbApiDriftReport.json' + $script:realMd = Join-Path $script:reportRoot 'Reports/PfbApiDriftReport.md' + $script:realShapeMap = Join-Path $script:reportRoot 'Data/PfbResponseShapeMap.json' + } + + # BeforeDiscovery above is what feeds the -Skip: expressions and must stay. It is not + # sufficient on its own: Pester's RUN phase uses a separate scope, so variables set during + # discovery read back as $null inside each It body. This block re-binds the same paths for + # the run phase. Both blocks are required -- neither replaces the other. + BeforeAll { + $script:reportRoot = Split-Path -Parent $PSScriptRoot + $script:realJson = Join-Path $script:reportRoot 'Reports/PfbApiDriftReport.json' + $script:realMd = Join-Path $script:reportRoot 'Reports/PfbApiDriftReport.md' + $script:realShapeMap = Join-Path $script:reportRoot 'Data/PfbResponseShapeMap.json' + } + + It 'emits the three response-shape keys in the JSON manifest' -Skip:(-not (Test-Path $script:realJson)) { + $r = Get-Content $script:realJson -Raw | ConvertFrom-Json + $r.PSObject.Properties.Name | Should -Contain 'responseFieldRemovals' + $r.PSObject.Properties.Name | Should -Contain 'responseFieldRenameCandidates' + $r.PSObject.Properties.Name | Should -Contain 'unhandledResponseEnvelopeFields' + } + + It 'reports the known real removals' -Skip:(-not (Test-Path $script:realJson)) { + $r = Get-Content $script:realJson -Raw | ConvertFrom-Json + $fields = @($r.responseFieldRemovals | ForEach-Object { $_.field }) + $fields | Should -Contain 'copyable' + $fields | Should -Contain 'server' + $fields | Should -Contain 'remote' + # NOT max_total_ops_per_sec: it is absent at 2.20 only and present at 2.28, so it is + # correctly not a removal. Asserting it here would lock in a false positive. + $fields | Should -Not -Contain 'max_total_ops_per_sec' + } + + It 'surfaces every removal the response shape map holds, and invents none' -Skip:((-not (Test-Path $script:realJson)) -or (-not (Test-Path $script:realShapeMap))) { + # DERIVED, never a literal. A hardcoded count here would not be an artifact-regression + # check: .github/workflows/update-api-capability-map.yml regenerates + # Data/PfbResponseShapeMap.json from FRESHLY FETCHED specs (step "Build response shape + # map"), rebuilds the report from it, and only THEN runs this suite with + # Run.Exit = $true. A literal would therefore be an assertion about what FlashBlade + # currently publishes, and the first genuine new removal -- exactly the event this + # whole axis exists to detect -- would fail this test, abort the job before its + # "Check for changes"/"Open pull request" steps, and silently freeze EVERY map and + # report the workflow maintains. The detector's own success case must not disable the + # pipeline it lives in. + # + # The property asserted instead is the one actually worth pinning and the one that + # stays true across spec versions: the report faithfully surfaces the map's removals, + # one row per record, neither dropping nor inventing any. + $map = Get-Content $script:realShapeMap -Raw | ConvertFrom-Json + $expected = 0 + foreach ($endpointProperty in $map.endpoints.PSObject.Properties) { + if ($endpointProperty.Value.PSObject.Properties.Name -contains 'removedResponseFields') { + $expected += @($endpointProperty.Value.removedResponseFields).Count + } + } + + # Keeps the assertion non-vacuous. Without this, a map that somehow held zero removals + # would make the comparison below 0 -eq 0 and pass while testing nothing -- the same + # failure mode as asserting $null against a collection matcher. + $expected | Should -BeGreaterThan 0 -Because 'the committed map holds real removals; a zero expectation would make the comparison below vacuous' + + $report = Get-Content $script:realJson -Raw | ConvertFrom-Json + @($report.responseFieldRemovals).Count | Should -Be $expected + } + + It 'surfaces the $response.errors worked example' -Skip:(-not (Test-Path $script:realJson)) { + $r = Get-Content $script:realJson -Raw | ConvertFrom-Json + $errorsRow = $r.unhandledResponseEnvelopeFields | Where-Object { $_.field -eq 'errors' } + $errorsRow | Should -Not -BeNullOrEmpty + $errorsRow.endpointCount | Should -BeGreaterThan 100 + } + + It 'renders a Response-shape drift section in the Markdown' -Skip:(-not (Test-Path $script:realMd)) { + (Get-Content $script:realMd -Raw) | Should -Match '## Response-shape drift' + } +} diff --git a/Tests/Build-PfbCapabilityMap.Tests.ps1 b/Tests/Build-PfbCapabilityMap.Tests.ps1 new file mode 100644 index 0000000..0aa6905 --- /dev/null +++ b/Tests/Build-PfbCapabilityMap.Tests.ps1 @@ -0,0 +1,636 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } +<# +.SYNOPSIS + Integration tests for tools/Build-PfbCapabilityMap.ps1 against small synthetic + spec fixtures (no dependency on the real cached specs in tools/specs/), plus a + shape/sanity check of the real committed manifest when present. +#> + +BeforeAll { + $repoRoot = Split-Path -Parent $PSScriptRoot + $script:builderScript = Join-Path $repoRoot 'tools/Build-PfbCapabilityMap.ps1' +} + +Describe 'Build-PfbCapabilityMap: introduced-in diffing' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + BeforeAll { + New-Item -ItemType Directory -Path 'TestDrive:\specs' -Force | Out-Null + + # v9.0: baseline — GET /widgets (param: filter), POST /widgets (body: name) + $specV1 = [ordered]@{ + openapi = '3.0.1' + info = @{ version = '9.0' } + paths = [ordered]@{ + '/api/9.0/widgets' = [ordered]@{ + get = @{ + parameters = @(@{ name = 'filter'; 'in' = 'query'; schema = @{ type = 'string' } }) + } + post = @{ + requestBody = @{ + content = @{ + 'application/json' = @{ + schema = @{ + type = 'object' + properties = @{ name = @{ type = 'string' } } + } + } + } + } + } + } + } + } + + # v9.1: adds a 'sort' param to the existing GET, and a brand-new endpoint. + $specV2 = [ordered]@{ + openapi = '3.0.1' + info = @{ version = '9.1' } + paths = [ordered]@{ + '/api/9.1/widgets' = [ordered]@{ + get = @{ + parameters = @( + @{ name = 'filter'; 'in' = 'query'; schema = @{ type = 'string' } } + @{ name = 'sort'; 'in' = 'query'; schema = @{ type = 'string' } } + ) + } + post = @{ + requestBody = @{ + content = @{ + 'application/json' = @{ + schema = @{ + type = 'object' + properties = @{ name = @{ type = 'string' } } + } + } + } + } + } + } + '/api/9.1/gadgets' = [ordered]@{ + get = @{ + parameters = @(@{ name = 'id'; 'in' = 'query'; schema = @{ type = 'string' } }) + } + } + } + } + + $specV1 | ConvertTo-Json -Depth 20 | Set-Content -Path 'TestDrive:\specs\fb9.0.json' + $specV2 | ConvertTo-Json -Depth 20 | Set-Content -Path 'TestDrive:\specs\fb9.1.json' + + & $builderScript -SpecsDirectory 'TestDrive:\specs' -OutputPath 'TestDrive:\output\manifest.json' + $script:manifest = Get-Content -Path 'TestDrive:\output\manifest.json' -Raw | ConvertFrom-Json -Depth 20 + } + + It 'records generatedFrom in ascending version order' { + $manifest.generatedFrom | Should -Be @('9.0', '9.1') + } + + It 'attributes an endpoint present since the earliest version to that version' { + $manifest.endpoints.'GET /widgets'.minVersion | Should -Be '9.0' + $manifest.endpoints.'POST /widgets'.minVersion | Should -Be '9.0' + } + + It 'attributes a brand-new endpoint to the version it first appears in' { + $manifest.endpoints.'GET /gadgets'.minVersion | Should -Be '9.1' + } + + It 'attributes a pre-existing parameter to the endpoint''s earliest version' { + $manifest.endpoints.'GET /widgets'.parameters.filter | Should -Be '9.0' + } + + It 'attributes a parameter added later to the version it first appears in, not the endpoint''s' { + $manifest.endpoints.'GET /widgets'.parameters.sort | Should -Be '9.1' + } + + It 'attributes request-body properties correctly' { + $manifest.endpoints.'POST /widgets'.bodyProperties.name | Should -Be '9.0' + } + + It 'normalizes the version-prefixed path consistently across versions (does not create duplicate endpoints)' { + ($manifest.endpoints.PSObject.Properties.Name | Where-Object { $_ -like '*widgets*' }).Count | Should -Be 2 + } +} + +Describe 'Build-PfbCapabilityMap: manifest shape' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + BeforeAll { + New-Item -ItemType Directory -Path 'TestDrive:\shapeSpecs' -Force | Out-Null + $spec = [ordered]@{ + openapi = '3.0.1' + info = @{ version = '9.0' } + paths = [ordered]@{ + '/api/9.0/widgets' = [ordered]@{ get = @{} } + } + } + $spec | ConvertTo-Json -Depth 20 | Set-Content -Path 'TestDrive:\shapeSpecs\fb9.0.json' + + & $builderScript -SpecsDirectory 'TestDrive:\shapeSpecs' -OutputPath 'TestDrive:\shapeOutput\manifest.json' + $script:shapeManifest = Get-Content -Path 'TestDrive:\shapeOutput\manifest.json' -Raw | ConvertFrom-Json -Depth 20 + } + + It 'has the required top-level keys' { + $shapeManifest.PSObject.Properties.Name | Should -Contain 'schemaVersion' + $shapeManifest.PSObject.Properties.Name | Should -Contain 'generatedFrom' + $shapeManifest.PSObject.Properties.Name | Should -Contain 'endpointCount' + $shapeManifest.PSObject.Properties.Name | Should -Contain 'endpoints' + } + + It 'reports an endpointCount matching the actual number of endpoint entries' { + $shapeManifest.endpointCount | Should -Be $shapeManifest.endpoints.PSObject.Properties.Name.Count + } + + It 'does NOT include an enums key (no structural enum data exists in the source specs)' { + $shapeManifest.endpoints.'GET /widgets'.PSObject.Properties.Name | Should -Not -Contain 'enums' + } + + It 'throws a clear error when no cached specs are present' { + New-Item -ItemType Directory -Path 'TestDrive:\emptySpecs' -Force | Out-Null + { & $builderScript -SpecsDirectory 'TestDrive:\emptySpecs' -OutputPath 'TestDrive:\emptyOutput\manifest.json' } | + Should -Throw '*No cached specs found*' + } +} + +Describe 'Build-PfbCapabilityMap: readOnly/deprecated last-seen-wins' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + BeforeAll { + New-Item -ItemType Directory -Path 'TestDrive:\roSpecs' -Force | Out-Null + + # v9.0 (older): 'name' is readOnly, 'category' is writable. 'legacy-endpoint' exists + # with a readOnly 'oldFlag' and is REMOVED entirely from v9.1 (tests "endpoint + # disappears from later specs keeps its last-seen readOnly value"). No deprecated + # fields anywhere yet. GET /widgets has a $ref'd 'filter' query parameter. + $specV1 = [ordered]@{ + openapi = '3.0.1' + info = @{ version = '9.0' } + paths = [ordered]@{ + '/api/9.0/widgets' = [ordered]@{ + get = @{ + parameters = @(@{ '$ref' = '#/components/parameters/Widget_filter' }) + } + patch = @{ + requestBody = @{ + content = @{ + 'application/json' = @{ + schema = @{ + type = 'object' + properties = [ordered]@{ + name = @{ type = 'string'; readOnly = $true } + category = @{ type = 'string' } + } + } + } + } + } + } + } + '/api/9.0/legacy-endpoint' = [ordered]@{ + patch = @{ + requestBody = @{ + content = @{ + 'application/json' = @{ + schema = @{ + type = 'object' + properties = [ordered]@{ + oldFlag = @{ type = 'string'; readOnly = $true } + } + } + } + } + } + } + } + } + components = [ordered]@{ + parameters = [ordered]@{ + Widget_filter = [ordered]@{ name = 'filter'; 'in' = 'query'; schema = @{ type = 'string' } } + } + } + } + + # v9.1 (newer): 'name' flips to writable, 'category' flips to readOnly, and a brand + # new 'secretNote' field is deprecated. 'legacy-endpoint' is gone entirely. + $specV2 = [ordered]@{ + openapi = '3.0.1' + info = @{ version = '9.1' } + paths = [ordered]@{ + '/api/9.1/widgets' = [ordered]@{ + get = @{ + parameters = @(@{ '$ref' = '#/components/parameters/Widget_filter' }) + } + patch = @{ + requestBody = @{ + content = @{ + 'application/json' = @{ + schema = @{ + type = 'object' + properties = [ordered]@{ + name = @{ type = 'string' } + category = @{ type = 'string'; readOnly = $true } + secretNote = @{ type = 'string'; deprecated = $true } + } + } + } + } + } + } + } + } + components = [ordered]@{ + parameters = [ordered]@{ + Widget_filter = [ordered]@{ name = 'filter'; 'in' = 'query'; schema = @{ type = 'string' } } + } + } + } + + $specV1 | ConvertTo-Json -Depth 20 | Set-Content -Path 'TestDrive:\roSpecs\fb9.0.json' + $specV2 | ConvertTo-Json -Depth 20 | Set-Content -Path 'TestDrive:\roSpecs\fb9.1.json' + + & $builderScript -SpecsDirectory 'TestDrive:\roSpecs' -OutputPath 'TestDrive:\roOutput\manifest.json' + $script:roManifest = Get-Content -Path 'TestDrive:\roOutput\manifest.json' -Raw | ConvertFrom-Json -Depth 20 + } + + It 'the decision-1 regression: a field readOnly in the older version and writable in the newer ends up ABSENT from readOnlyBodyProperties' { + $roManifest.endpoints.'PATCH /widgets'.readOnlyBodyProperties | Should -Not -Contain 'name' + } + + It 'the mirror case: a field writable in the older version and readOnly in the newer ends up PRESENT in readOnlyBodyProperties' { + $roManifest.endpoints.'PATCH /widgets'.readOnlyBodyProperties | Should -Contain 'category' + } + + It 'an endpoint that disappears from later specs keeps its last-seen readOnlyBodyProperties value' { + $roManifest.endpoints.'PATCH /legacy-endpoint'.readOnlyBodyProperties | Should -Be @('oldFlag') + } + + It 'omits deprecatedBodyProperties entirely when empty' { + $roManifest.endpoints.'PATCH /legacy-endpoint'.PSObject.Properties.Name | Should -Not -Contain 'deprecatedBodyProperties' + } + + It 'includes deprecatedBodyProperties when non-empty' { + $roManifest.endpoints.'PATCH /widgets'.deprecatedBodyProperties | Should -Be @('secretNote') + } + + It 'resolves a $ref-backed parameter to its component via the new parameterComponentDefaults table (single-component case: default is trivially that one component)' { + $roManifest.parameterComponentDefaults.filter | Should -Be 'Widget_filter' + } + + It 'does NOT emit the old (pre-dedup) per-endpoint parameterComponents key' { + $roManifest.endpoints.'GET /widgets'.PSObject.Properties.Name | Should -Not -Contain 'parameterComponents' + } + + It 'leaves the pre-existing minVersion/parameters/bodyProperties keys unaffected by the new keys' { + $entry = $roManifest.endpoints.'PATCH /widgets' + $entry.minVersion | Should -Be '9.0' + $entry.bodyProperties.name | Should -Be '9.0' + $entry.bodyProperties.category | Should -Be '9.0' + $entry.bodyProperties.secretNote | Should -Be '9.1' + } +} + +Describe 'Build-PfbCapabilityMap: parameterComponentDefaults/Overrides' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + BeforeAll { + New-Item -ItemType Directory -Path 'TestDrive:\pcSpecs' -Force | Out-Null + + # Single-version fixture (the dedup/defaults/overrides logic is a pure + # post-processing pass over each endpoint's already-resolved CURRENT + # parameterComponents state, which the readOnly/deprecated Describe block above + # already separately proves is last-seen-wins across versions -- no need to + # duplicate that here). + # + # 'region' is declared via $ref on 3 endpoints (majority component 'Region' on + # alpha+bravo, minority 'RegionAlt' on charlie -- NOT a tie, so this proves plain + # frequency-based selection) and declared INLINE (no $ref at all) on 'delta' -- + # this is the explicit-null-override case: 'region' has a global default from + # other endpoints, so delta's inline (component-less) 'region' must get an + # explicit null override, or it would silently inherit that unrelated default. + # + # 'sortkey' is declared via $ref on exactly 2 endpoints with two DIFFERENT + # components used exactly once each (alpha -> component 'SortKeyBeta', echo -> + # component 'SortKeyAlpha') -- an exact 1-1 tie, proving the alphabetical + # tie-break ('SortKeyAlpha' < 'SortKeyBeta'). + $spec = [ordered]@{ + openapi = '3.0.1' + info = @{ version = '9.0' } + paths = [ordered]@{ + '/api/9.0/alpha' = [ordered]@{ + get = @{ + parameters = @( + @{ '$ref' = '#/components/parameters/Region' } + @{ '$ref' = '#/components/parameters/SortKeyBeta' } + ) + } + } + '/api/9.0/bravo' = [ordered]@{ + get = @{ parameters = @(@{ '$ref' = '#/components/parameters/Region' }) } + } + '/api/9.0/charlie' = [ordered]@{ + get = @{ parameters = @(@{ '$ref' = '#/components/parameters/RegionAlt' }) } + } + '/api/9.0/delta' = [ordered]@{ + # 'region' declared INLINE -- no "$ref", so no component at all. + get = @{ + parameters = @(@{ name = 'region'; 'in' = 'query'; schema = @{ type = 'string' } }) + } + } + '/api/9.0/echo' = [ordered]@{ + get = @{ parameters = @(@{ '$ref' = '#/components/parameters/SortKeyAlpha' }) } + } + } + components = [ordered]@{ + parameters = [ordered]@{ + Region = [ordered]@{ name = 'region'; 'in' = 'query'; schema = @{ type = 'string' } } + RegionAlt = [ordered]@{ name = 'region'; 'in' = 'query'; schema = @{ type = 'string' } } + SortKeyBeta = [ordered]@{ name = 'sortkey'; 'in' = 'query'; schema = @{ type = 'string' } } + SortKeyAlpha = [ordered]@{ name = 'sortkey'; 'in' = 'query'; schema = @{ type = 'string' } } + } + } + } + $spec | ConvertTo-Json -Depth 20 | Set-Content -Path 'TestDrive:\pcSpecs\fb9.0.json' + + & $builderScript -SpecsDirectory 'TestDrive:\pcSpecs' -OutputPath 'TestDrive:\pcOutput\manifest.json' + $script:pcManifest = Get-Content -Path 'TestDrive:\pcOutput\manifest.json' -Raw | ConvertFrom-Json -Depth 20 + + # The known-correct (endpoint, param) -> component ground truth, from the fixture + # above, used by the round-trip test. $null means "no component" (delta/region). + $script:pcExpectedPairs = [ordered]@{ + 'GET /alpha|region' = 'Region' + 'GET /alpha|sortkey' = 'SortKeyBeta' + 'GET /bravo|region' = 'Region' + 'GET /charlie|region' = 'RegionAlt' + 'GET /delta|region' = $null + 'GET /echo|sortkey' = 'SortKeyAlpha' + } + } + + It 'picks the most frequent component as the default (region: 2x Region vs 1x RegionAlt)' { + $pcManifest.parameterComponentDefaults.region | Should -Be 'Region' + } + + It 'breaks an exact frequency tie alphabetically (sortkey: 1x SortKeyAlpha vs 1x SortKeyBeta)' { + $pcManifest.parameterComponentDefaults.sortkey | Should -Be 'SortKeyAlpha' + } + + It 'sorts parameterComponentDefaults keys' { + $keys = $pcManifest.parameterComponentDefaults.PSObject.Properties.Name + $keys | Should -Be ($keys | Sort-Object) + } + + It 'omits an endpoint from parameterComponentOverrides when its component matches the default' { + $pcManifest.endpoints.'GET /bravo'.PSObject.Properties.Name | Should -Not -Contain 'parameterComponentOverrides' + $pcManifest.endpoints.'GET /echo'.PSObject.Properties.Name | Should -Not -Contain 'parameterComponentOverrides' + } + + It 'records a non-default component as an override' { + $pcManifest.endpoints.'GET /charlie'.parameterComponentOverrides.region | Should -Be 'RegionAlt' + $pcManifest.endpoints.'GET /alpha'.parameterComponentOverrides.sortkey | Should -Be 'SortKeyBeta' + } + + It 'does NOT override alpha''s region (it matches the default, only its sortkey differs)' { + $pcManifest.endpoints.'GET /alpha'.parameterComponentOverrides.PSObject.Properties.Name | Should -Not -Contain 'region' + } + + It 'the decision-1-style regression: an inline (no $ref) parameter whose name has a global default gets an explicit JSON null override, not silence' { + $entry = $pcManifest.endpoints.'GET /delta' + $entry.PSObject.Properties.Name | Should -Contain 'parameterComponentOverrides' + $entry.parameterComponentOverrides.PSObject.Properties.Name | Should -Contain 'region' + $entry.parameterComponentOverrides.region | Should -BeNullOrEmpty + } + + It 'round-trips: reconstructing every (endpoint, param) -> component from defaults + overrides reproduces the fixture exactly (same pairs, same values, no additions, no losses)' { + $reconstructed = [ordered]@{} + foreach ($epName in $pcManifest.endpoints.PSObject.Properties.Name) { + $ep = $pcManifest.endpoints.$epName + $overrides = $ep.parameterComponentOverrides + $paramNames = $ep.parameters.PSObject.Properties.Name + foreach ($paramName in $paramNames) { + $key = "$epName|$paramName" + if ($overrides -and ($overrides.PSObject.Properties.Name -contains $paramName)) { + # An explicit override -- which may itself be JSON null, meaning "no + # component" -- always wins over the default. + $reconstructed[$key] = $overrides.$paramName + } + elseif ($pcManifest.parameterComponentDefaults.PSObject.Properties.Name -contains $paramName) { + $reconstructed[$key] = $pcManifest.parameterComponentDefaults.$paramName + } + else { + $reconstructed[$key] = $null + } + } + } + + # Only compare the pairs this fixture actually declares a component-bearing + # parameter for (pcExpectedPairs) -- every one must reconstruct to the exact same + # value, with no extra keys and no missing keys. + foreach ($key in $pcExpectedPairs.Keys) { + $reconstructed.Contains($key) | Should -BeTrue -Because "reconstructed set is missing '$key'" + $reconstructed[$key] | Should -Be $pcExpectedPairs[$key] -Because "'$key' should reconstruct to '$($pcExpectedPairs[$key])'" + } + ($reconstructed.Keys | Sort-Object) | Should -Be ($pcExpectedPairs.Keys | Sort-Object) -Because 'no additional or missing (endpoint, param) pairs vs. the fixture ground truth' + } + + It 'never leaves the internal _currentParamComponents/_currentParamsWithoutComponent bookkeeping keys in the final serialized manifest' { + # These two keys are used internally to compute parameterComponentDefaults/ + # Overrides above, then removed before the manifest is written -- this fixture + # exercises exactly that code path (region/sortkey components), so it is a real + # assertion, not a vacuous one. + $pcManifestRaw = Get-Content -Path 'TestDrive:\pcOutput\manifest.json' -Raw + $pcManifestRaw | Should -Not -Match '_currentParamComponents' + $pcManifestRaw | Should -Not -Match '_currentParamsWithoutComponent' + + foreach ($epName in $pcManifest.endpoints.PSObject.Properties.Name) { + $propNames = $pcManifest.endpoints.$epName.PSObject.Properties.Name + $propNames | Should -Not -Contain '_currentParamComponents' + $propNames | Should -Not -Contain '_currentParamsWithoutComponent' + } + } + + # Regression for the hashtable .Count-shadowing bug class: 'count' is a REAL + # query-parameter name in this API (parameterComponentDefaults.count = 'Ping_count' in + # the committed manifest). A key literally named 'count' inside the per-endpoint + # $overrides ordered hashtable shadows the .Count MEMBER -- it returns that key's + # VALUE instead of the number of entries -- which is exactly why the script uses + # get_Count()/get_Keys() defensively elsewhere in this same function. This fixture + # forces that shape directly: 'alpha'/'bravo' establish 'Count' as the global default + # component for parameter name 'count', and 'charlie' declares 'count' INLINE (no + # $ref, so no component) -- so charlie's ONLY override entry is { count = $null }, + # the worst case for the shadowing bug: $null -gt 0 is False, so a buggy + # `$overrides.Count -gt 0` guard would silently skip attaching + # parameterComponentOverrides to charlie at all, even though a real override exists. + Context 'hashtable .Count shadowing regression (override key literally "count")' { + BeforeAll { + New-Item -ItemType Directory -Path 'TestDrive:\pcSpecsCount' -Force | Out-Null + + $specCount = [ordered]@{ + openapi = '3.0.1' + info = @{ version = '9.0' } + paths = [ordered]@{ + '/api/9.0/alpha' = [ordered]@{ + get = @{ parameters = @(@{ '$ref' = '#/components/parameters/Count' }) } + } + '/api/9.0/bravo' = [ordered]@{ + get = @{ parameters = @(@{ '$ref' = '#/components/parameters/Count' }) } + } + '/api/9.0/charlie' = [ordered]@{ + # 'count' declared INLINE -- no "$ref", so no component at all. + get = @{ + parameters = @(@{ name = 'count'; 'in' = 'query'; schema = @{ type = 'integer' } }) + } + } + } + components = [ordered]@{ + parameters = [ordered]@{ + Count = [ordered]@{ name = 'count'; 'in' = 'query'; schema = @{ type = 'integer' } } + } + } + } + $specCount | ConvertTo-Json -Depth 20 | Set-Content -Path 'TestDrive:\pcSpecsCount\fb9.0.json' + + & $builderScript -SpecsDirectory 'TestDrive:\pcSpecsCount' -OutputPath 'TestDrive:\pcOutputCount\manifest.json' + $script:pcCountManifest = Get-Content -Path 'TestDrive:\pcOutputCount\manifest.json' -Raw | ConvertFrom-Json -Depth 20 + } + + It 'establishes Count as the global default for parameter name "count"' { + $pcCountManifest.parameterComponentDefaults.count | Should -Be 'Count' + } + + It 'does NOT silently drop parameterComponentOverrides on the endpoint whose only override key is literally "count"' { + $entry = $pcCountManifest.endpoints.'GET /charlie' + $entry.PSObject.Properties.Name | Should -Contain 'parameterComponentOverrides' + } + + It 'records an explicit null override for the inline "count" parameter, not silence' { + $entry = $pcCountManifest.endpoints.'GET /charlie' + $entry.parameterComponentOverrides.PSObject.Properties.Name | Should -Contain 'count' + $entry.parameterComponentOverrides.count | Should -BeNullOrEmpty + } + } +} + +Describe 'Build-PfbCapabilityMap: -MaxVersion cap' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + BeforeAll { + New-Item -ItemType Directory -Path 'TestDrive:\capSpecs' -Force | Out-Null + + $specV1 = [ordered]@{ + openapi = '3.0.1' + info = @{ version = '9.0' } + paths = [ordered]@{ '/api/9.0/widgets' = [ordered]@{ get = @{} } } + } + $specV2 = [ordered]@{ + openapi = '3.0.1' + info = @{ version = '9.1' } + paths = [ordered]@{ '/api/9.1/widgets' = [ordered]@{ get = @{} } } + } + # A newer version that must be excluded by the cap -- adds a brand-new endpoint. + $specV3 = [ordered]@{ + openapi = '3.0.1' + info = @{ version = '9.2' } + paths = [ordered]@{ + '/api/9.2/widgets' = [ordered]@{ get = @{} } + '/api/9.2/newthing' = [ordered]@{ get = @{} } + } + } + + $specV1 | ConvertTo-Json -Depth 20 | Set-Content -Path 'TestDrive:\capSpecs\fb9.0.json' + $specV2 | ConvertTo-Json -Depth 20 | Set-Content -Path 'TestDrive:\capSpecs\fb9.1.json' + $specV3 | ConvertTo-Json -Depth 20 | Set-Content -Path 'TestDrive:\capSpecs\fb9.2.json' + + & $builderScript -SpecsDirectory 'TestDrive:\capSpecs' -OutputPath 'TestDrive:\capOutput\manifest.json' -MaxVersion '9.1' + $script:capManifest = Get-Content -Path 'TestDrive:\capOutput\manifest.json' -Raw | ConvertFrom-Json -Depth 20 + } + + It 'excludes a cached spec newer than -MaxVersion from generatedFrom' { + $capManifest.generatedFrom | Should -Be @('9.0', '9.1') + } + + It 'excludes an endpoint that only exists in a version newer than -MaxVersion' { + $capManifest.endpoints.PSObject.Properties.Name | Should -Not -Contain 'GET /newthing' + } + + It 'still includes endpoints from versions at or below -MaxVersion' { + $capManifest.endpoints.PSObject.Properties.Name | Should -Contain 'GET /widgets' + } + + # The 9.0/9.1/9.2 fixture above cannot distinguish a correct numeric Major/Minor + # comparison from a buggy string comparison, because single-digit minors sort + # identically either way. '2.9' vs '2.10' do NOT: '2.9' sorts ABOVE '2.10' as a + # string (since '9' > '1' lexically) but numerically 2.9 < 2.10. This exact string- + # vs-numeric confusion has bitten this effort twice already (see the script's own + # -MaxVersion parameter help), so it needs its own regression case distinct from the + # coarser 9.0/9.1/9.2 test above. + Context 'numeric vs. string comparison (2.9 vs 2.10)' { + BeforeAll { + New-Item -ItemType Directory -Path 'TestDrive:\capSpecsNumeric' -Force | Out-Null + + $specNumericV1 = [ordered]@{ + openapi = '3.0.1' + info = @{ version = '2.9' } + paths = [ordered]@{ '/api/2.9/alpha' = [ordered]@{ get = @{} } } + } + # A newer minor version that a string compare would wrongly treat as OLDER + # than 2.9 (because '2.10' < '2.9' lexically) and therefore wrongly include + # under -MaxVersion '2.9'. A correct numeric compare must exclude it. + $specNumericV2 = [ordered]@{ + openapi = '3.0.1' + info = @{ version = '2.10' } + paths = [ordered]@{ + '/api/2.10/alpha' = [ordered]@{ get = @{} } + '/api/2.10/newnumeric' = [ordered]@{ get = @{} } + } + } + + $specNumericV1 | ConvertTo-Json -Depth 20 | Set-Content -Path 'TestDrive:\capSpecsNumeric\fb2.9.json' + $specNumericV2 | ConvertTo-Json -Depth 20 | Set-Content -Path 'TestDrive:\capSpecsNumeric\fb2.10.json' + + & $builderScript -SpecsDirectory 'TestDrive:\capSpecsNumeric' -OutputPath 'TestDrive:\capOutputNumeric\manifest.json' -MaxVersion '2.9' + $script:capManifestNumeric = Get-Content -Path 'TestDrive:\capOutputNumeric\manifest.json' -Raw | ConvertFrom-Json -Depth 20 + } + + It 'includes 2.9 in generatedFrom' { + $capManifestNumeric.generatedFrom | Should -Contain '2.9' + } + + It 'excludes 2.10 from generatedFrom even though it sorts below "2.9" as a string' { + $capManifestNumeric.generatedFrom | Should -Not -Contain '2.10' + } + + It 'excludes an endpoint that only exists in 2.10' { + $capManifestNumeric.endpoints.PSObject.Properties.Name | Should -Not -Contain 'GET /newnumeric' + } + + It 'still includes an endpoint present at or below the 2.9 cap' { + $capManifestNumeric.endpoints.PSObject.Properties.Name | Should -Contain 'GET /alpha' + } + } +} + +Describe 'Real committed capability map (skips gracefully if not yet generated)' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + BeforeAll { + $repoRoot = Split-Path -Parent $PSScriptRoot + $script:realManifestPath = Join-Path $repoRoot 'Data/PfbCapabilityMap.json' + $script:realSpecsDir = Join-Path $repoRoot 'tools/specs' + } + + It 'every (method, path) in the newest cached spec is represented in the manifest' { + if (-not (Test-Path $realManifestPath) -or -not (Test-Path $realSpecsDir)) { + Set-ItResult -Skipped -Because 'Data/PfbCapabilityMap.json or tools/specs/ not present (run Update-PfbApiSpecs.ps1 and Build-PfbCapabilityMap.ps1 first)' + return + } + + . (Join-Path $repoRoot 'tools/lib/PfbSpecTools.ps1') + + $specFiles = Get-ChildItem -Path $realSpecsDir -Filter 'fb*.json' | Where-Object { $_.BaseName -match '^fb(\d+)\.(\d+)$' } + if (-not $specFiles) { + Set-ItResult -Skipped -Because 'No cached spec files found under tools/specs/' + return + } + $newest = $specFiles | ForEach-Object { + $null = $_.BaseName -match '^fb(\d+)\.(\d+)$' + [PSCustomObject]@{ File = $_; Major = [int]$Matches[1]; Minor = [int]$Matches[2] } + } | Sort-Object Major, Minor | Select-Object -Last 1 + + $spec = Get-Content -Path $newest.File.FullName -Raw | ConvertFrom-Json -Depth 64 + $capabilities = Get-PfbSpecCapabilities -Spec $spec + $manifest = Get-Content -Path $realManifestPath -Raw | ConvertFrom-Json -Depth 20 + $manifestKeys = [System.Collections.Generic.HashSet[string]]::new([string[]]$manifest.endpoints.PSObject.Properties.Name) + + $missing = $capabilities | ForEach-Object { "$($_.Method) $($_.Path)" } | Where-Object { -not $manifestKeys.Contains($_) } + + $missing | Should -BeNullOrEmpty -Because "these endpoints exist in the newest spec but are missing from the manifest: $($missing -join ', ')" + } +} diff --git a/Tests/Build-PfbFieldCmdletMap.Tests.ps1 b/Tests/Build-PfbFieldCmdletMap.Tests.ps1 new file mode 100644 index 0000000..e56c625 --- /dev/null +++ b/Tests/Build-PfbFieldCmdletMap.Tests.ps1 @@ -0,0 +1,299 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } +<# +.SYNOPSIS + Unit tests for tools/Build-PfbFieldCmdletMap.ps1's classification logic, against + small synthetic inventory + spec fixtures. Also includes one real-artifact check that + skips gracefully if tools/specs/ isn't present, matching + Tests/Build-PfbValueEnumMap.Tests.ps1's convention. +#> + +BeforeAll { + $repoRoot = Split-Path -Parent $PSScriptRoot + . (Join-Path $repoRoot 'tools/lib/PfbSpecTools.ps1') + . (Join-Path $repoRoot 'tools/lib/PfbValueEnumTools.ps1') + . (Join-Path $repoRoot 'tools/lib/PfbCmdletParamTools.ps1') + $script:buildScript = Join-Path $repoRoot 'tools/Build-PfbFieldCmdletMap.ps1' + + $script:specsDir = Join-Path $TestDrive 'specs' + New-Item -ItemType Directory -Path $specsDir -Force | Out-Null + + # Two versions: v1 introduces both a stable field (present unchanged in both versions) + # and a field whose value set changes between versions (must NOT be recommended + # ValidateSet even though it has full history). v-only-in-2 exists only in the newer + # version (short history) and must also fall back to ArgumentCompleter. + $specV1 = @{ + components = @{ + schemas = @{ + Widget = @{ + properties = @{ + stable_field = @{ description = 'Valid values are `a`, `b`.' } + changing_field = @{ description = 'Valid values are `x`, `y`.' } + collision_field = @{ description = 'Valid values are `m`, `n`.' } + } + } + WidgetSpecial = @{ + properties = @{ + # Same wire name as Widget.collision_field, different value set, + # and "WidgetSpecial" still prefix-matches the "Widget" resource + # hint -- must produce 'collision', not a guessed pick. + collision_field = @{ description = 'Valid values are `m`, `o`.' } + } + } + OtherThing = @{ + properties = @{ + # Real field, but under a schema the "Widget" hint never matches -- + # must produce 'not-found-in-resource', not a false match. + elsewhere_field = @{ description = 'Valid values are `q`, `r`.' } + } + } + } + # Reproduces the real Get-PfbArraySpace -Type bug pattern: Kind='parameter' + # records are keyed by an OpenAPI components.parameters dictionary name with + # NO relationship to the owning resource/cmdlet, so the resource-hint filter + # (built for schema keys) can never match them. Two distinct + # components.parameters definitions can share the same wire "name" -- if + # they disagree on value set, the field is genuinely ambiguous (collision); + # if they agree, it's safe to resolve regardless of which definition this + # cmdlet's endpoint actually references (matched). + parameters = @{ + ParamKindAmbiguousA = @{ name = 'param_kind_ambiguous'; description = 'Valid values are `t1`, `t2`.' } + ParamKindAmbiguousB = @{ name = 'param_kind_ambiguous'; description = 'Valid values are `t1`, `t3`.' } + ParamKindConsistentA = @{ name = 'param_kind_consistent'; description = 'Valid values are `k1`, `k2`.' } + ParamKindConsistentB = @{ name = 'param_kind_consistent'; description = 'Valid values are `k1`, `k2`.' } + # Reproduces the real Get-PfbArraySpace -Type bug exactly: two + # components.parameters definitions share wire name 'endpoint_field' with + # DIFFERENT value sets (ambiguous on their own), but one specific endpoint + # (GET /widgets/endpoint below) inline-defines the SAME field with a value + # set matching EndpointFieldA -- an inline-parameter record keyed to that + # exact endpoint, which must resolve the field despite the param-kind + # ambiguity, not report 'collision'. + EndpointFieldA = @{ name = 'endpoint_field'; description = 'Valid values are `e1`, `e2`.' } + EndpointFieldB = @{ name = 'endpoint_field'; description = 'Valid values are `e1`, `e3`.' } + } + } + paths = @{ + '/api/1.0/widgets/endpoint' = @{ + get = @{ + parameters = @( + @{ name = 'endpoint_field'; 'in' = 'query'; description = 'Valid values are `e1`, `e2`.' } + ) + } + } + } + } + $specV2 = @{ + components = @{ + schemas = @{ + Widget = @{ + properties = @{ + stable_field = @{ description = 'Valid values are `a`, `b`.' } + changing_field = @{ description = 'Valid values are `x`, `y`, `z`.' } + new_in_v2 = @{ description = 'Valid values are `p`, `q`.' } + collision_field = @{ description = 'Valid values are `m`, `n`.' } + } + } + WidgetSpecial = @{ + properties = @{ + collision_field = @{ description = 'Valid values are `m`, `o`.' } + } + } + OtherThing = @{ + properties = @{ + elsewhere_field = @{ description = 'Valid values are `q`, `r`.' } + } + } + } + parameters = @{ + ParamKindAmbiguousA = @{ name = 'param_kind_ambiguous'; description = 'Valid values are `t1`, `t2`.' } + ParamKindAmbiguousB = @{ name = 'param_kind_ambiguous'; description = 'Valid values are `t1`, `t3`.' } + ParamKindConsistentA = @{ name = 'param_kind_consistent'; description = 'Valid values are `k1`, `k2`.' } + ParamKindConsistentB = @{ name = 'param_kind_consistent'; description = 'Valid values are `k1`, `k2`.' } + # Reproduces the real Get-PfbArraySpace -Type bug exactly: two + # components.parameters definitions share wire name 'endpoint_field' with + # DIFFERENT value sets (ambiguous on their own), but one specific endpoint + # (GET /widgets/endpoint below) inline-defines the SAME field with a value + # set matching EndpointFieldA -- an inline-parameter record keyed to that + # exact endpoint, which must resolve the field despite the param-kind + # ambiguity, not report 'collision'. + EndpointFieldA = @{ name = 'endpoint_field'; description = 'Valid values are `e1`, `e2`.' } + EndpointFieldB = @{ name = 'endpoint_field'; description = 'Valid values are `e1`, `e3`.' } + } + } + paths = @{ + '/api/1.1/widgets/endpoint' = @{ + get = @{ + parameters = @( + @{ name = 'endpoint_field'; 'in' = 'query'; description = 'Valid values are `e1`, `e2`.' } + ) + } + } + } + } + $specV1 | ConvertTo-Json -Depth 20 | Set-Content -Path (Join-Path $specsDir 'fb1.0.json') + $specV2 | ConvertTo-Json -Depth 20 | Set-Content -Path (Join-Path $specsDir 'fb1.1.json') + + $script:publicDir = Join-Path $TestDrive 'Public' + New-Item -ItemType Directory -Path $publicDir -Force | Out-Null + # Cmdlet is named "New-PfbWidget" (not "New-PfbFixtureWidget") deliberately: the + # resource-hint resolver strips the "New-Pfb" verb prefix to get "Widget", which must + # prefix-match the fixture spec's "Widget.stable_field" etc. schema keys below. + Set-Content -Path (Join-Path $publicDir 'New-PfbWidget.ps1') -Value @' +function New-PfbWidget { + param( + [Parameter()] [string]$StableField, + [Parameter()] [string]$ChangingField, + [Parameter()] [string]$NewInV2, + [Parameter()] [string]$CollisionField, + [Parameter()] [string]$ElsewhereField, + [Parameter()] [string]$NoSpecField, + [Parameter()] [string]$ParamKindField, + [Parameter()] [string]$ParamKindConsistentField, + [Parameter()] [string]$EndpointField, + [Parameter()] [PSCustomObject]$Array + ) + $body = @{} + if ($StableField) { $body["stable_field"] = $StableField } + if ($ChangingField) { $body["changing_field"] = $ChangingField } + if ($NewInV2) { $body["new_in_v2"] = $NewInV2 } + if ($CollisionField) { $body["collision_field"] = $CollisionField } + if ($ElsewhereField) { $body["elsewhere_field"] = $ElsewhereField } + if ($NoSpecField) { $body["totally_unknown_field"] = $NoSpecField } + if ($ParamKindField) { $body["param_kind_ambiguous"] = $ParamKindField } + if ($ParamKindConsistentField) { $body["param_kind_consistent"] = $ParamKindConsistentField } + $queryParams = @{} + if ($EndpointField) { $queryParams["endpoint_field"] = $EndpointField } + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'widgets/endpoint' -Body $body -QueryParams $queryParams +} +'@ + + $script:outputDir = Join-Path $TestDrive 'output' + $script:manifestPath = Join-Path $outputDir 'map.json' + $script:reportPath = Join-Path $outputDir 'report.md' + + & $buildScript -SpecsDirectory $specsDir -PublicDirectory $publicDir -OutputPath $manifestPath -ReportPath $reportPath + $script:manifest = Get-Content -Path $manifestPath -Raw | ConvertFrom-Json -Depth 20 +} + +Describe 'Build-PfbFieldCmdletMap' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + It 'recommends ValidateSet only for a field with full version history and no value-set changes' { + $rec = $manifest.entries | Where-Object { $_.parameter -eq 'StableField' } + $rec.status | Should -Be 'matched' + $rec.stableSinceOldestVersion | Should -BeTrue + $rec.recommendation | Should -Be 'ValidateSet' + } + + It 'recommends ArgumentCompleter for a field with full history but a value-set change' { + $rec = $manifest.entries | Where-Object { $_.parameter -eq 'ChangingField' } + $rec.status | Should -Be 'matched' + $rec.stableSinceOldestVersion | Should -BeFalse + $rec.recommendation | Should -Be 'ArgumentCompleter' + } + + It 'recommends ArgumentCompleter for a field introduced only in the newest version' { + $rec = $manifest.entries | Where-Object { $_.parameter -eq 'NewInV2' } + $rec.status | Should -Be 'matched' + $rec.stableSinceOldestVersion | Should -BeFalse + $rec.recommendation | Should -Be 'ArgumentCompleter' + } + + It 'writes the reconciliation-style Markdown report' { + Test-Path $reportPath | Should -BeTrue + (Get-Content $reportPath -Raw) | Should -Match 'ValidateSet' + } + + It 'includes a Summary section with correct per-status counts for the fixture set' { + $reportText = Get-Content $reportPath -Raw + $reportText | Should -Match '## Summary' + # Fixture distribution: matched = StableField, ChangingField, NewInV2, + # ParamKindConsistentField, EndpointField (5); collision = CollisionField, + # ParamKindField (2); not-found-in-resource = ElsewhereField (1); + # no-spec-enum-found = NoSpecField (1). + $reportText | Should -Match '- matched: 5' + $reportText | Should -Match '- collision: 2' + $reportText | Should -Match '- not-found-in-resource: 1' + $reportText | Should -Match '- no-spec-enum-found: 1' + } + + It 'does not emit the zero-matched note when matched candidates exist' { + (Get-Content $reportPath -Raw) | Should -Not -Match 'No `matched` candidates this run' + } + + It 'omits no-spec-enum-found rows from the detailed table but keeps matched/collision/not-found-in-resource rows' { + $reportText = Get-Content $reportPath -Raw + $reportText | Should -Not -Match '\| `New-PfbWidget` \| `-NoSpecField` \|' + $reportText | Should -Match '\| `New-PfbWidget` \| `-StableField` \|' + $reportText | Should -Match '\| `New-PfbWidget` \| `-CollisionField` \|' + $reportText | Should -Match '\| `New-PfbWidget` \| `-ElsewhereField` \|' + } + + It 'classifies a wire name matching multiple hinted schemas with different value sets as collision, not a guess' { + $rec = $manifest.entries | Where-Object { $_.parameter -eq 'CollisionField' } + $rec.status | Should -Be 'collision' + $rec.recommendation | Should -BeNullOrEmpty + $rec.matchedKey | Should -BeNullOrEmpty + } + + It 'classifies a wire name found only under an unrelated schema as not-found-in-resource, not a false match' { + $rec = $manifest.entries | Where-Object { $_.parameter -eq 'ElsewhereField' } + $rec.status | Should -Be 'not-found-in-resource' + $rec.recommendation | Should -BeNullOrEmpty + } + + It 'classifies a wire name absent from the spec entirely as no-spec-enum-found' { + $rec = $manifest.entries | Where-Object { $_.parameter -eq 'NoSpecField' } + $rec.status | Should -Be 'no-spec-enum-found' + $rec.recommendation | Should -BeNullOrEmpty + } + + It 'classifies a parameter-kind wire name with disagreeing value sets across components.parameters definitions as collision, not not-found-in-resource' { + $rec = $manifest.entries | Where-Object { $_.parameter -eq 'ParamKindField' } + $rec.status | Should -Be 'collision' + $rec.recommendation | Should -BeNullOrEmpty + $rec.matchedKey | Should -BeNullOrEmpty + } + + It 'resolves a parameter-kind wire name to matched when every components.parameters definition sharing it agrees on the same value set' { + $rec = $manifest.entries | Where-Object { $_.parameter -eq 'ParamKindConsistentField' } + $rec.status | Should -Be 'matched' + $rec.specValues | Should -Be @('k1', 'k2') + } + + It 'resolves the real Get-PfbArraySpace -Type shape: an exact inline-parameter endpoint match overrides an otherwise-ambiguous parameter-kind wire name' { + # EndpointFieldA/EndpointFieldB disagree on value set (like Type/Type_for_performance) + # -- would be 'collision' on their own -- but New-PfbWidget's -EndpointField resolves + # to exactly one Invoke-PfbApiRequest call (GET widgets/endpoint), matching an + # inline-parameter record keyed to that exact endpoint, which settles it. + $rec = $manifest.entries | Where-Object { $_.parameter -eq 'EndpointField' } + $rec.status | Should -Be 'matched' + $rec.specValues | Should -Be @('e1', 'e2') + $rec.stableSinceOldestVersion | Should -BeTrue + $rec.recommendation | Should -Be 'ValidateSet' + } + + It 'still reports collision for an ambiguous parameter-kind wire name when no inline-parameter record matches this cmdlet''s own endpoint' { + # ParamKindField's cmdlet (New-PfbWidget) DOES now resolve an Endpoint/Method (GET + # widgets/endpoint, shared with -EndpointField above) -- but no inline-parameter + # record exists keyed "GET widgets/endpoint#param_kind_ambiguous", so the exact-match + # override never applies here and the pre-existing ambiguous-collision behavior for + # parameter-kind records must still hold. + $rec = $manifest.entries | Where-Object { $_.parameter -eq 'ParamKindField' } + $rec.status | Should -Be 'collision' + $rec.recommendation | Should -BeNullOrEmpty + } +} + +Describe 'Build-PfbFieldCmdletMap (real generated artifacts, skips gracefully if absent)' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + It 'produces a manifest against the real Public/ tree and tools/specs/ cache' { + $realSpecsDir = Join-Path $repoRoot 'tools/specs' + $realPublicDir = Join-Path $repoRoot 'Public' + if (-not (Test-Path $realSpecsDir) -or -not (Get-ChildItem $realSpecsDir -Filter 'fb*.json' -ErrorAction SilentlyContinue)) { + Set-ItResult -Skipped -Because 'tools/specs/ not present locally (gitignored, run Update-PfbApiSpecs.ps1 first)' + return + } + + $realOutput = Join-Path $TestDrive 'realOutput/map.json' + $realReport = Join-Path $TestDrive 'realOutput/report.md' + & $buildScript -SpecsDirectory $realSpecsDir -PublicDirectory $realPublicDir -OutputPath $realOutput -ReportPath $realReport + Test-Path $realOutput | Should -BeTrue + } +} diff --git a/Tests/Build-PfbResponseShapeMap.Tests.ps1 b/Tests/Build-PfbResponseShapeMap.Tests.ps1 new file mode 100644 index 0000000..25fc7cc --- /dev/null +++ b/Tests/Build-PfbResponseShapeMap.Tests.ps1 @@ -0,0 +1,206 @@ +BeforeAll { + $script:RepoRoot = Split-Path -Parent $PSScriptRoot + $script:BuilderPath = Join-Path $script:RepoRoot 'tools/Build-PfbResponseShapeMap.ps1' + + function New-ShapeSpecFixture { + param( + [Parameter(Mandatory)][string[]]$EnvelopeProperties, + [Parameter(Mandatory)][string[]]$ItemProperties + ) + $envProps = [PSCustomObject]@{} + foreach ($p in $EnvelopeProperties) { + if ($p -eq 'items') { continue } + $envProps | Add-Member -NotePropertyName $p -NotePropertyValue ([PSCustomObject]@{ type = 'integer' }) + } + $itemProps = [PSCustomObject]@{} + foreach ($p in $ItemProperties) { + $itemProps | Add-Member -NotePropertyName $p -NotePropertyValue ([PSCustomObject]@{ type = 'string' }) + } + $envProps | Add-Member -NotePropertyName 'items' -NotePropertyValue ([PSCustomObject]@{ + type = 'array'; items = [PSCustomObject]@{ properties = $itemProps } + }) + + [PSCustomObject]@{ + paths = [PSCustomObject]@{ + '/api/2.0/widgets' = [PSCustomObject]@{ + get = [PSCustomObject]@{ + responses = [PSCustomObject]@{ + '200' = [PSCustomObject]@{ + content = [PSCustomObject]@{ + 'application/json' = [PSCustomObject]@{ schema = [PSCustomObject]@{ properties = $envProps } } + } + } + } + } + } + } + } + } + + # Same response node as above, but lets several paths be declared in a CHOSEN order within + # one spec, so emission order can be asserted against insertion order. + function New-MultiPathShapeSpecFixture { + param( + # Ordered array of [PSCustomObject]@{ Path; EnvelopeProperties; ItemProperties } + [Parameter(Mandatory)][object[]]$Endpoints + ) + $paths = [PSCustomObject]@{} + foreach ($ep in $Endpoints) { + $single = New-ShapeSpecFixture -EnvelopeProperties $ep.EnvelopeProperties -ItemProperties $ep.ItemProperties + $paths | Add-Member -NotePropertyName $ep.Path -NotePropertyValue $single.paths.'/api/2.0/widgets' + } + [PSCustomObject]@{ paths = $paths } + } +} + +Describe 'Build-PfbResponseShapeMap' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + It 'records first-seen introducedVersion for fields present throughout' { + $specs = Join-Path $TestDrive 'specs'; New-Item -ItemType Directory -Path $specs -Force | Out-Null + New-ShapeSpecFixture -EnvelopeProperties @('items') -ItemProperties @('name') | + ConvertTo-Json -Depth 20 | Set-Content (Join-Path $specs 'fb9.0.json') + New-ShapeSpecFixture -EnvelopeProperties @('items') -ItemProperties @('name', 'added_later') | + ConvertTo-Json -Depth 20 | Set-Content (Join-Path $specs 'fb9.1.json') + + $out = Join-Path $TestDrive 'map.json' + & $script:BuilderPath -SpecsDirectory $specs -OutputPath $out | Out-Null + + $map = Get-Content $out -Raw | ConvertFrom-Json + $ep = $map.endpoints.'GET /widgets' + $ep.responseItemProperties.name | Should -Be '9.0' + $ep.responseItemProperties.added_later | Should -Be '9.1' + $ep.minVersion | Should -Be '9.0' + $ep.lastSeenVersion | Should -Be '9.1' + } + + It 'moves a field that disappears into removedResponseFields, not responseItemProperties' { + $specs = Join-Path $TestDrive 'specs2'; New-Item -ItemType Directory -Path $specs -Force | Out-Null + New-ShapeSpecFixture -EnvelopeProperties @('items') -ItemProperties @('name', 'copyable') | + ConvertTo-Json -Depth 20 | Set-Content (Join-Path $specs 'fb9.0.json') + New-ShapeSpecFixture -EnvelopeProperties @('items') -ItemProperties @('name') | + ConvertTo-Json -Depth 20 | Set-Content (Join-Path $specs 'fb9.1.json') + + $out = Join-Path $TestDrive 'map2.json' + & $script:BuilderPath -SpecsDirectory $specs -OutputPath $out | Out-Null + + $map = Get-Content $out -Raw | ConvertFrom-Json + $ep = $map.endpoints.'GET /widgets' + $ep.responseItemProperties.PSObject.Properties.Name | Should -Not -Contain 'copyable' + @($ep.removedResponseFields).Count | Should -Be 1 + $ep.removedResponseFields[0].field | Should -Be 'copyable' + $ep.removedResponseFields[0].location | Should -Be 'items' + $ep.removedResponseFields[0].introducedVersion | Should -Be '9.0' + $ep.removedResponseFields[0].lastSeenVersion | Should -Be '9.0' + } + + It 'detects envelope-level removal too' { + $specs = Join-Path $TestDrive 'specs3'; New-Item -ItemType Directory -Path $specs -Force | Out-Null + New-ShapeSpecFixture -EnvelopeProperties @('items', 'total_item_count') -ItemProperties @('name') | + ConvertTo-Json -Depth 20 | Set-Content (Join-Path $specs 'fb9.0.json') + New-ShapeSpecFixture -EnvelopeProperties @('items') -ItemProperties @('name') | + ConvertTo-Json -Depth 20 | Set-Content (Join-Path $specs 'fb9.1.json') + + $out = Join-Path $TestDrive 'map3.json' + & $script:BuilderPath -SpecsDirectory $specs -OutputPath $out | Out-Null + + $map = Get-Content $out -Raw | ConvertFrom-Json + $removed = @($map.endpoints.'GET /widgets'.removedResponseFields) + $removed.Count | Should -Be 1 + $removed[0].field | Should -Be 'total_item_count' + $removed[0].location | Should -Be 'envelope' + } + + It 'omits removedResponseFields entirely when nothing was removed' { + $specs = Join-Path $TestDrive 'specs4'; New-Item -ItemType Directory -Path $specs -Force | Out-Null + New-ShapeSpecFixture -EnvelopeProperties @('items') -ItemProperties @('name') | + ConvertTo-Json -Depth 20 | Set-Content (Join-Path $specs 'fb9.0.json') + + $out = Join-Path $TestDrive 'map4.json' + & $script:BuilderPath -SpecsDirectory $specs -OutputPath $out | Out-Null + + $map = Get-Content $out -Raw | ConvertFrom-Json + $map.endpoints.'GET /widgets'.PSObject.Properties.Name | Should -Not -Contain 'removedResponseFields' + } + + It 'sorts versions numerically, not lexically (9.9 before 9.10)' { + $specs = Join-Path $TestDrive 'specs5'; New-Item -ItemType Directory -Path $specs -Force | Out-Null + New-ShapeSpecFixture -EnvelopeProperties @('items') -ItemProperties @('name') | + ConvertTo-Json -Depth 20 | Set-Content (Join-Path $specs 'fb9.9.json') + New-ShapeSpecFixture -EnvelopeProperties @('items') -ItemProperties @('name', 'newer') | + ConvertTo-Json -Depth 20 | Set-Content (Join-Path $specs 'fb9.10.json') + + $out = Join-Path $TestDrive 'map5.json' + & $script:BuilderPath -SpecsDirectory $specs -OutputPath $out | Out-Null + + $map = Get-Content $out -Raw | ConvertFrom-Json + $map.generatedFrom[-1] | Should -Be '9.10' + # 'newer' appears only in 9.10; if sorting were lexical, 9.10 would be processed + # first and 'newer' would be recorded as removed. + $map.endpoints.'GET /widgets'.responseItemProperties.newer | Should -Be '9.10' + $map.endpoints.'GET /widgets'.PSObject.Properties.Name | Should -Not -Contain 'removedResponseFields' + } + + It 'is deterministic across two runs' { + $specs = Join-Path $TestDrive 'specs6'; New-Item -ItemType Directory -Path $specs -Force | Out-Null + New-ShapeSpecFixture -EnvelopeProperties @('items', 'errors') -ItemProperties @('name', 'id', 'created') | + ConvertTo-Json -Depth 20 | Set-Content (Join-Path $specs 'fb9.0.json') + + $a = Join-Path $TestDrive 'runA.json' + $b = Join-Path $TestDrive 'runB.json' + & $script:BuilderPath -SpecsDirectory $specs -OutputPath $a | Out-Null + & $script:BuilderPath -SpecsDirectory $specs -OutputPath $b | Out-Null + + (Get-FileHash $a -Algorithm SHA256).Hash | Should -Be (Get-FileHash $b -Algorithm SHA256).Hash + } + + It 'emits endpoint keys and field names in sorted order, not insertion order' { + # Dictionary enumeration is stable for a fixed insertion sequence, so a + # two-runs-hash-equal check passes even with every Sort-Object deleted. This asserts the + # literal emitted order instead. Both fixtures declare paths zebras-before-alphas, and + # 'alpha' is introduced at 9.1 AFTER 'mango'/'zebra' were inserted at 9.0 -- so without + # sorting the emission would be zebras-then-alphas and mango,zebra,alpha. + $specs = Join-Path $TestDrive 'specs8'; New-Item -ItemType Directory -Path $specs -Force | Out-Null + New-MultiPathShapeSpecFixture -Endpoints @( + [PSCustomObject]@{ Path = '/api/2.0/zebras'; EnvelopeProperties = @('items'); ItemProperties = @('mango', 'zebra') } + [PSCustomObject]@{ Path = '/api/2.0/alphas'; EnvelopeProperties = @('items'); ItemProperties = @('name') } + ) | ConvertTo-Json -Depth 20 | Set-Content (Join-Path $specs 'fb9.0.json') + New-MultiPathShapeSpecFixture -Endpoints @( + [PSCustomObject]@{ Path = '/api/2.0/zebras'; EnvelopeProperties = @('items'); ItemProperties = @('mango', 'zebra', 'alpha') } + [PSCustomObject]@{ Path = '/api/2.0/alphas'; EnvelopeProperties = @('items'); ItemProperties = @('name') } + ) | ConvertTo-Json -Depth 20 | Set-Content (Join-Path $specs 'fb9.1.json') + + $out = Join-Path $TestDrive 'map8.json' + & $script:BuilderPath -SpecsDirectory $specs -OutputPath $out | Out-Null + + $map = Get-Content $out -Raw | ConvertFrom-Json + @($map.endpoints.PSObject.Properties.Name) | Should -Be @('GET /alphas', 'GET /zebras') + @($map.endpoints.'GET /zebras'.responseItemProperties.PSObject.Properties.Name) | + Should -Be @('alpha', 'mango', 'zebra') + } + + It 'throws rather than emitting an empty map when no spec filename parses' { + $specs = Join-Path $TestDrive 'specs9'; New-Item -ItemType Directory -Path $specs -Force | Out-Null + # Matches the fb*.json glob but not the fb. version regex. + New-ShapeSpecFixture -EnvelopeProperties @('items') -ItemProperties @('name') | + ConvertTo-Json -Depth 20 | Set-Content (Join-Path $specs 'fbXYZ.json') + + $out = Join-Path $TestDrive 'map9.json' + { & $script:BuilderPath -SpecsDirectory $specs -OutputPath $out -WarningAction SilentlyContinue } | + Should -Throw -ExpectedMessage '*parseable*' + Test-Path $out | Should -BeFalse + } + + It 'does not let an API field named "keys" shadow dictionary members' { + $specs = Join-Path $TestDrive 'specs7'; New-Item -ItemType Directory -Path $specs -Force | Out-Null + New-ShapeSpecFixture -EnvelopeProperties @('items') -ItemProperties @('keys', 'count', 'values') | + ConvertTo-Json -Depth 20 | Set-Content (Join-Path $specs 'fb9.0.json') + + $out = Join-Path $TestDrive 'map7.json' + & $script:BuilderPath -SpecsDirectory $specs -OutputPath $out | Out-Null + + $map = Get-Content $out -Raw | ConvertFrom-Json + $names = $map.endpoints.'GET /widgets'.responseItemProperties.PSObject.Properties.Name + $names | Should -Contain 'keys' + $names | Should -Contain 'count' + $names | Should -Contain 'values' + } +} diff --git a/Tests/Build-PfbValueEnumMap.Tests.ps1 b/Tests/Build-PfbValueEnumMap.Tests.ps1 new file mode 100644 index 0000000..eea8491 --- /dev/null +++ b/Tests/Build-PfbValueEnumMap.Tests.ps1 @@ -0,0 +1,256 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } +<# +.SYNOPSIS + Integration tests for tools/Build-PfbValueEnumMap.ps1 against small synthetic spec + fixtures (no dependency on the real cached specs in tools/specs/), plus a shape/ + regression check of the real committed manifest when present. +.DESCRIPTION + Every invocation below passes explicit -OutputPath AND -ReconciliationPath under + TestDrive: — never let the script fall back to its real-repo defaults, or running + these tests would overwrite Reports/PfbValueEnumMap.json and + Reports/PfbValueEnumReconciliation.md as a side effect. +#> + +BeforeAll { + $repoRoot = Split-Path -Parent $PSScriptRoot + $script:builderScript = Join-Path $repoRoot 'tools/Build-PfbValueEnumMap.ps1' +} + +Describe 'Build-PfbValueEnumMap: introduced-in diffing and value tracking' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + BeforeAll { + New-Item -ItemType Directory -Path 'TestDrive:\specs' -Force | Out-Null + + # v9.0: Widget.color has a two-value enum; a squash-mode-style pair of schemas + # (WidgetA/WidgetB) share a property name with different value lists. + $specV1 = [ordered]@{ + openapi = '3.0.1' + info = @{ version = '9.0' } + paths = [ordered]@{} + components = [ordered]@{ + schemas = [ordered]@{ + Widget = [ordered]@{ + type = 'object' + properties = [ordered]@{ + color = @{ type = 'string'; description = 'The widget color. Valid values are `red`, `blue`.' } + } + } + WidgetA = [ordered]@{ + type = 'object' + properties = [ordered]@{ + mode = @{ type = 'string'; description = 'Valid values are `on`, `off`.' } + } + } + WidgetB = [ordered]@{ + type = 'object' + properties = [ordered]@{ + mode = @{ type = 'string'; description = 'Valid values are `enabled`, `disabled`.' } + } + } + } + parameters = [ordered]@{} + } + } + + # v9.1: Widget.color gains a third value (green); a brand-new Gadget.kind enum + # appears; a numeric-range description that matches the trigger phrase but is + # not a real enum is introduced (must land in "unparsed", not silently dropped). + $specV2 = [ordered]@{ + openapi = '3.0.1' + info = @{ version = '9.1' } + paths = [ordered]@{} + components = [ordered]@{ + schemas = [ordered]@{ + Widget = [ordered]@{ + type = 'object' + properties = [ordered]@{ + color = @{ type = 'string'; description = 'The widget color. Valid values are `red`, `blue`, and `green`.' } + } + } + WidgetA = [ordered]@{ + type = 'object' + properties = [ordered]@{ + mode = @{ type = 'string'; description = 'Valid values are `on`, `off`.' } + } + } + WidgetB = [ordered]@{ + type = 'object' + properties = [ordered]@{ + mode = @{ type = 'string'; description = 'Valid values are `enabled`, `disabled`.' } + } + } + Gadget = [ordered]@{ + type = 'object' + properties = [ordered]@{ + kind = @{ type = 'string'; description = 'Valid values are `small`, `large`.' } + timeout = @{ type = 'integer'; description = "Valid values are`nin the range of 1000 and 60000." } + } + } + } + parameters = [ordered]@{} + } + } + + $specV1 | ConvertTo-Json -Depth 20 | Set-Content -Path 'TestDrive:\specs\fb9.0.json' + $specV2 | ConvertTo-Json -Depth 20 | Set-Content -Path 'TestDrive:\specs\fb9.1.json' + + & $builderScript -SpecsDirectory 'TestDrive:\specs' -OutputPath 'TestDrive:\output\map.json' -ReconciliationPath 'TestDrive:\output\reconciliation.md' + $script:manifest = Get-Content -Path 'TestDrive:\output\map.json' -Raw | ConvertFrom-Json -Depth 20 + } + + It 'records generatedFrom in ascending version order' { + $manifest.generatedFrom | Should -Be @('9.0', '9.1') + } + + It 'attributes an entry present since the earliest version to that version, but reports the newest values' { + $manifest.entries.'Widget.color'.minVersion | Should -Be '9.0' + $manifest.entries.'Widget.color'.values | Should -Be @('red', 'blue', 'green') + } + + It 'attributes a brand-new entry to the version it first appears in' { + $manifest.entries.'Gadget.kind'.minVersion | Should -Be '9.1' + $manifest.entries.'Gadget.kind'.values | Should -Be @('small', 'large') + } + + It 'never collapses two schemas sharing a property name into one entry (squash-mode gotcha)' { + $manifest.entries.'WidgetA.mode'.values | Should -Be @('on', 'off') + $manifest.entries.'WidgetB.mode'.values | Should -Be @('enabled', 'disabled') + } + + It 'tracks a trigger-matching but non-enumerable description as unparsed rather than dropping it' { + $manifest.unparsedCount | Should -BeGreaterThan 0 + $unparsedKeys = $manifest.unparsed | ForEach-Object { $_.key } + $unparsedKeys | Should -Contain 'Gadget.timeout' + } + + It 'reports entryCount matching the actual number of entries' { + $manifest.entryCount | Should -Be $manifest.entries.PSObject.Properties.Name.Count + } + + It 'reports unparsedCount matching the actual number of unparsed records' { + $manifest.unparsedCount | Should -Be @($manifest.unparsed).Count + } +} + +Describe 'Build-PfbValueEnumMap: manifest shape' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + BeforeAll { + New-Item -ItemType Directory -Path 'TestDrive:\shapeSpecs' -Force | Out-Null + $spec = [ordered]@{ + openapi = '3.0.1' + info = @{ version = '9.0' } + paths = [ordered]@{} + components = [ordered]@{ + schemas = [ordered]@{ + Widget = [ordered]@{ + type = 'object' + properties = [ordered]@{ + color = @{ type = 'string'; description = 'Valid values are `red`, `blue`.' } + } + } + } + parameters = [ordered]@{} + } + } + $spec | ConvertTo-Json -Depth 20 | Set-Content -Path 'TestDrive:\shapeSpecs\fb9.0.json' + + & $builderScript -SpecsDirectory 'TestDrive:\shapeSpecs' -OutputPath 'TestDrive:\shapeOutput\map.json' -ReconciliationPath 'TestDrive:\shapeOutput\reconciliation.md' + $script:shapeManifest = Get-Content -Path 'TestDrive:\shapeOutput\map.json' -Raw | ConvertFrom-Json -Depth 20 + } + + It 'has the required top-level keys' { + $shapeManifest.PSObject.Properties.Name | Should -Contain 'schemaVersion' + $shapeManifest.PSObject.Properties.Name | Should -Contain 'generatedFrom' + $shapeManifest.PSObject.Properties.Name | Should -Contain 'entryCount' + $shapeManifest.PSObject.Properties.Name | Should -Contain 'unparsedCount' + $shapeManifest.PSObject.Properties.Name | Should -Contain 'entries' + $shapeManifest.PSObject.Properties.Name | Should -Contain 'unparsed' + } + + It 'writes a reconciliation report file' { + Test-Path 'TestDrive:\shapeOutput\reconciliation.md' | Should -BeTrue + (Get-Content 'TestDrive:\shapeOutput\reconciliation.md' -Raw) | Should -Match 'Value-Enum Reconciliation Report' + } + + It 'throws a clear error when no cached specs are present' { + New-Item -ItemType Directory -Path 'TestDrive:\emptySpecs' -Force | Out-Null + { & $builderScript -SpecsDirectory 'TestDrive:\emptySpecs' -OutputPath 'TestDrive:\emptyOutput\map.json' -ReconciliationPath 'TestDrive:\emptyOutput\reconciliation.md' } | + Should -Throw '*No cached specs found*' + } +} + +Describe 'Real committed value-enum map (skips gracefully if not yet generated)' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + BeforeAll { + $repoRoot = Split-Path -Parent $PSScriptRoot + $script:realManifestPath = Join-Path $repoRoot 'Reports/PfbValueEnumMap.json' + $script:realSpecsDir = Join-Path $repoRoot 'tools/specs' + } + + It 'Bucket.versioning regression: extracts exactly [none, enabled, suspended] with no per-value version claim' { + if (-not (Test-Path $realManifestPath)) { + Set-ItResult -Skipped -Because 'Reports/PfbValueEnumMap.json not present (run Build-PfbValueEnumMap.ps1 first)' + return + } + + $manifest = Get-Content -Path $realManifestPath -Raw | ConvertFrom-Json -Depth 20 + $manifest.entries.'Bucket.versioning' | Should -Not -BeNullOrEmpty + $manifest.entries.'Bucket.versioning'.values | Should -Be @('none', 'enabled', 'suspended') + } + + It 'never collapses NfsExportPolicyRuleBase.access and the presets-only variant into one entry (squash-mode gotcha)' { + if (-not (Test-Path $realManifestPath)) { + Set-ItResult -Skipped -Because 'Reports/PfbValueEnumMap.json not present (run Build-PfbValueEnumMap.ps1 first)' + return + } + + $manifest = Get-Content -Path $realManifestPath -Raw | ConvertFrom-Json -Depth 20 + $base = $manifest.entries.'NfsExportPolicyRuleBase.access' + $preset = $manifest.entries.'_presetWorkloadExportConfigurationNfsRule.access' + + $base | Should -Not -BeNullOrEmpty + $preset | Should -Not -BeNullOrEmpty + $base.values | Should -Contain 'no-squash' + $preset.values | Should -Contain 'no-root-squash' + ($base.values -join ',') | Should -Not -Be ($preset.values -join ',') + } + + It 'meets the acceptance-criteria entry-count floor and reports unparsedCount as a first-class field' { + if (-not (Test-Path $realManifestPath)) { + Set-ItResult -Skipped -Because 'Reports/PfbValueEnumMap.json not present (run Build-PfbValueEnumMap.ps1 first)' + return + } + + $manifest = Get-Content -Path $realManifestPath -Raw | ConvertFrom-Json -Depth 20 + $manifest.entryCount | Should -BeGreaterOrEqual 100 + $manifest.PSObject.Properties.Name | Should -Contain 'unparsedCount' + @($manifest.unparsed).Count | Should -Be $manifest.unparsedCount + } + + It 'every (schema, property) value-enum extractable from the newest cached spec is represented in the manifest' { + if (-not (Test-Path $realManifestPath) -or -not (Test-Path $realSpecsDir)) { + Set-ItResult -Skipped -Because 'Reports/PfbValueEnumMap.json or tools/specs/ not present (run Update-PfbApiSpecs.ps1 and Build-PfbValueEnumMap.ps1 first)' + return + } + + . (Join-Path $repoRoot 'tools/lib/PfbSpecTools.ps1') + . (Join-Path $repoRoot 'tools/lib/PfbValueEnumTools.ps1') + + $specFiles = Get-ChildItem -Path $realSpecsDir -Filter 'fb*.json' | Where-Object { $_.BaseName -match '^fb(\d+)\.(\d+)$' } + if (-not $specFiles) { + Set-ItResult -Skipped -Because 'No cached spec files found under tools/specs/' + return + } + $newest = $specFiles | ForEach-Object { + $null = $_.BaseName -match '^fb(\d+)\.(\d+)$' + [PSCustomObject]@{ File = $_; Major = [int]$Matches[1]; Minor = [int]$Matches[2] } + } | Sort-Object Major, Minor | Select-Object -Last 1 + + $spec = Get-Content -Path $newest.File.FullName -Raw | ConvertFrom-Json -Depth 64 + $valueEnums = Get-PfbSpecValueEnums -Spec $spec + $manifest = Get-Content -Path $realManifestPath -Raw | ConvertFrom-Json -Depth 20 + $entryKeys = [System.Collections.Generic.HashSet[string]]::new([string[]]$manifest.entries.PSObject.Properties.Name) + $unparsedKeys = [System.Collections.Generic.HashSet[string]]::new([string[]]($manifest.unparsed | ForEach-Object { $_.key })) + + $missing = $valueEnums | ForEach-Object { $_.Key } | Where-Object { -not $entryKeys.Contains($_) -and -not $unparsedKeys.Contains($_) } + + $missing | Should -BeNullOrEmpty -Because "these value enums exist in the newest spec but are represented in neither entries nor unparsed: $($missing -join ', ')" + } +} diff --git a/Tests/ConvertTo-PfbApiError.Tests.ps1 b/Tests/ConvertTo-PfbApiError.Tests.ps1 index ba4fd9b..dba5a20 100644 --- a/Tests/ConvertTo-PfbApiError.Tests.ps1 +++ b/Tests/ConvertTo-PfbApiError.Tests.ps1 @@ -8,14 +8,46 @@ BeforeAll { function New-ErrorRecordWithBody { param( [string]$Body, - [string]$Message = 'mock http error' + [string]$Message = 'mock http error', + # Omitted entirely = an exception with NO .Response at all, i.e. a failure that never + # reached the array (DNS, timeout, certificate rejection). + [int]$StatusCode, + # Attach .Response but with an unusable status, to exercise the range guard. + [switch]$ResponseWithoutStatus ) $ex = New-Object System.Exception($Message) + + # Duck-typed the same way Tests/Invoke-PfbApiRequest.Reconnect.Tests.ps1 fakes it, which + # is also how the production reconnect gate reads it. A real HttpResponseException cannot + # be constructed identically on both PowerShell 5.1 and 7, and this module supports both. + if ($ResponseWithoutStatus) { + # An object with no StatusCode member at all. [System.Net.HttpStatusCode]0 cannot be + # used here -- 0 is not a defined value of that enum and the cast throws. + $response = [PSCustomObject]@{ ReasonPhrase = 'none' } + Add-Member -InputObject $ex -MemberType NoteProperty -Name Response -Value $response -Force + } + elseif ($PSBoundParameters.ContainsKey('StatusCode')) { + $response = [PSCustomObject]@{ StatusCode = [System.Net.HttpStatusCode]$StatusCode } + Add-Member -InputObject $ex -MemberType NoteProperty -Name Response -Value $response -Force + } + $errorRecord = New-Object System.Management.Automation.ErrorRecord( $ex, 'MockError', [System.Management.Automation.ErrorCategory]::NotSpecified, $null) - $errorRecord.ErrorDetails = New-Object System.Management.Automation.ErrorDetails($Body) + if ($PSBoundParameters.ContainsKey('Body')) { + $errorRecord.ErrorDetails = New-Object System.Management.Automation.ErrorDetails($Body) + } return $errorRecord } + + function Invoke-Convert { + param([System.Management.Automation.ErrorRecord]$ErrorRecord, [string]$Method = 'GET', + [string]$Endpoint = 'arrays') + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ + errorRecord = $ErrorRecord; method = $Method; endpoint = $Endpoint + } { + ConvertTo-PfbApiError -Method $method -Endpoint $endpoint -ErrorRecord $errorRecord + } + } } Describe 'ConvertTo-PfbApiError' { @@ -53,3 +85,64 @@ Describe 'ConvertTo-PfbApiError' { $result | Should -Be 'FlashBlade API error: plural wins' } } + +Describe 'ConvertTo-PfbApiError HTTP status reporting' { + It 'reports the status on a parsed error body, which replaces the message wholesale' { + # The regression this guards: the body-parsing branch REPLACES the transport message, and + # the transport message is where the status text lived. So the failures that carry a + # readable body -- every deliberate API rejection -- were the ones that lost the status. + $result = Invoke-Convert (New-ErrorRecordWithBody -StatusCode 403 ` + -Body '{"error":[{"code":403,"context":"/api/2.26/arrays","message":"Access Denied"}]}') + + $result | Should -Be 'FlashBlade API error (HTTP 403): Access Denied' + } + + It 'reports the status when there is no parseable body' { + $result = Invoke-Convert (New-ErrorRecordWithBody -StatusCode 503 -Message 'Service Unavailable') + + $result | Should -Be 'FlashBlade API error on GET arrays (HTTP 503): Service Unavailable' + } + + It 'does not confuse the body error code with the HTTP status' { + # These are different numbers and only sometimes agree. A write rejected for targeting the + # wrong array in a fleet comes back as application code 13 inside an HTTP 400. Taking the + # status from the body would report "(HTTP 13)" -- not a status code at all, yet entirely + # believable to whatever is reading it. + $result = Invoke-Convert (New-ErrorRecordWithBody -StatusCode 400 ` + -Body '{"errors":[{"code":13,"message":"Cannot be modified on a member array."}]}') + + $result | Should -Be 'FlashBlade API error (HTTP 400): Cannot be modified on a member array.' + $result | Should -Not -Match 'HTTP 13' + } + + It 'omits the status entirely when the failure never produced an HTTP response' { + # A DNS failure, connection timeout or rejected certificate has no status to report, and + # must not be given a fabricated one. Absent reads as unknown; wrong gets believed. + $result = Invoke-Convert (New-ErrorRecordWithBody -Message 'The remote name could not be resolved') + + $result | Should -Be 'FlashBlade API error on GET arrays: The remote name could not be resolved' + $result | Should -Not -Match 'HTTP' + } + + It 'omits the status when a response exists but carries no usable one, rather than saying HTTP 0' { + # Defensive rather than a known live shape: no real HTTP response reports status 0. It + # guards the [int] cast, which turns an absent StatusCode into 0 rather than $null. + $result = Invoke-Convert (New-ErrorRecordWithBody -ResponseWithoutStatus -Message 'truncated response') + + $result | Should -Not -Match 'HTTP' + } + + It 'emits the status in a form a consumer can actually parse back out' { + # The message string is the whole interface here: callers catching this across a module + # boundary get a plain RuntimeException with no structured status field to read. So the + # exact "(HTTP nnn)" shape is the contract, and this asserts it stays machine-readable + # rather than merely containing the digits somewhere. + foreach ($code in 400, 403, 404, 500, 503) { + $result = Invoke-Convert (New-ErrorRecordWithBody -StatusCode $code ` + -Body "{`"errors`":[{`"code`":$code,`"message`":`"rejected`"}]}") + + $result | Should -Match '\(HTTP\s+\d{3}\)' + [int]([regex]::Match($result, '\(HTTP\s+(\d{3})\)').Groups[1].Value) | Should -Be $code + } + } +} diff --git a/Tests/ConvertTo-PfbQueryString.Tests.ps1 b/Tests/ConvertTo-PfbQueryString.Tests.ps1 new file mode 100644 index 0000000..f1042b1 --- /dev/null +++ b/Tests/ConvertTo-PfbQueryString.Tests.ps1 @@ -0,0 +1,64 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + Import-Module "$PSScriptRoot/../PureStorageFlashBladePowerShell.psd1" -Force +} + +Describe 'ConvertTo-PfbQueryString' { + Context 'Boolean values' { + It 'serializes $true as lowercase true, not True' { + InModuleScope PureStorageFlashBladePowerShell { + $result = ConvertTo-PfbQueryString -Parameters @{ enabled = $true } + $result | Should -Be '?enabled=true' + } + } + + It 'serializes $false as lowercase false, not False' { + InModuleScope PureStorageFlashBladePowerShell { + $result = ConvertTo-PfbQueryString -Parameters @{ enabled = $false } + $result | Should -Be '?enabled=false' + } + } + } + + Context 'Array values' { + It 'joins array values with commas' { + InModuleScope PureStorageFlashBladePowerShell { + $result = ConvertTo-PfbQueryString -Parameters @{ names = @('a', 'b', 'c') } + $result | Should -Be '?names=a%2Cb%2Cc' + } + } + } + + Context 'Scalar values' { + It 'passes through string values unchanged' { + InModuleScope PureStorageFlashBladePowerShell { + $result = ConvertTo-PfbQueryString -Parameters @{ name = 'foo' } + $result | Should -Be '?name=foo' + } + } + + It 'passes through integer values unchanged' { + InModuleScope PureStorageFlashBladePowerShell { + $result = ConvertTo-PfbQueryString -Parameters @{ limit = 0 } + $result | Should -Be '?limit=0' + } + } + } + + Context 'Null and empty handling' { + It 'returns an empty string for null or empty parameters' { + InModuleScope PureStorageFlashBladePowerShell { + ConvertTo-PfbQueryString -Parameters @{} | Should -Be '' + ConvertTo-PfbQueryString -Parameters $null | Should -Be '' + } + } + + It 'skips null and empty-string values but keeps other keys' { + InModuleScope PureStorageFlashBladePowerShell { + $result = ConvertTo-PfbQueryString -Parameters @{ a = $null; b = ''; c = 'x' } + $result | Should -Be '?c=x' + } + } + } +} diff --git a/Tests/Fixtures/sample-redoc-page.html b/Tests/Fixtures/sample-redoc-page.html new file mode 100644 index 0000000..c34d000 --- /dev/null +++ b/Tests/Fixtures/sample-redoc-page.html @@ -0,0 +1,14 @@ + + +FlashBlade REST API Reference (fixture) + +
+ + + diff --git a/Tests/Get-PfbArrayConnection.Tests.ps1 b/Tests/Get-PfbArrayConnection.Tests.ps1 new file mode 100644 index 0000000..5ceede6 --- /dev/null +++ b/Tests/Get-PfbArrayConnection.Tests.ps1 @@ -0,0 +1,115 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Get-PfbArrayConnection - selector query keys (#64)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'sends remote_names, never names' { + Get-PfbArrayConnection -RemoteName 'FB-B' -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and $Endpoint -eq 'array-connections' -and + $QueryParams['remote_names'] -eq 'FB-B' -and -not $QueryParams.ContainsKey('names') + } + } + + It 'still binds -Name through the alias, and emits remote_names for it' { + Get-PfbArrayConnection -Name 'FB-B' -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' -and -not $QueryParams.ContainsKey('names') + } + } + + It 'declares Name as an alias of -RemoteName' { + (Get-Command Get-PfbArrayConnection).Parameters['RemoteName'].Aliases | Should -Contain 'Name' + } + + It 'comma-joins multiple remote names into one key' { + Get-PfbArrayConnection -RemoteName 'FB-B','FB-C' -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B,FB-C' + } + } + + It 'accumulates remote names piped by value into a single request' { + 'FB-B','FB-C' | Get-PfbArrayConnection -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B,FB-C' + } + } + + It 'sends ids when -Id is used, and no remote_names' { + Get-PfbArrayConnection -Id 'conn-1','conn-2' -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'conn-1,conn-2' -and -not $QueryParams.ContainsKey('remote_names') + } + } + + It 'binds piped connection objects by id' { + @([PSCustomObject]@{ id = 'conn-1' }, [PSCustomObject]@{ id = 'conn-2' }) | + Get-PfbArrayConnection -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'conn-1,conn-2' -and -not $QueryParams.ContainsKey('remote_names') + } + } + + It 'binds -RemoteName by property name from a user-built object' { + [pscustomobject]@{ RemoteName = 'FB-B' } | Get-PfbArrayConnection -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' -and -not $QueryParams.ContainsKey('ids') + } + } + + It 'binds by property name through the Name alias' { + [pscustomobject]@{ Name = 'FB-B' } | Get-PfbArrayConnection -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' -and -not $QueryParams.ContainsKey('ids') + } + } + + It 'rejects a piped object that carries no id/name property instead of stringifying it' { + # -Id absorbs a real connection object at binding pass 2. An object without id, name or + # remoteName falls through to pass 3 and is ToString()-ed into -RemoteName, so the guard + # is reachable here too. + { + [PSCustomObject]@{ status = 'connected'; type = 'async-replication' } | + Get-PfbArrayConnection -Array $fakeArray + } | Should -Throw -ExpectedMessage '*stringified object*' + } + + It 'still routes filter/sort/limit through the common helper' { + Get-PfbArrayConnection -Filter "status='connected'" -Sort 'id' -Limit 10 -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['filter'] -eq "status='connected'" -and + $QueryParams['sort'] -eq 'id' -and $QueryParams['limit'] -eq 10 + } + } + + It 'sends no selector key at all when listing everything' { + Get-PfbArrayConnection -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('remote_names') -and + -not $QueryParams.ContainsKey('names') -and -not $QueryParams.ContainsKey('ids') + } + } +} diff --git a/Tests/Get-PfbArrayConnectionPath.Tests.ps1 b/Tests/Get-PfbArrayConnectionPath.Tests.ps1 new file mode 100644 index 0000000..a8bd05f --- /dev/null +++ b/Tests/Get-PfbArrayConnectionPath.Tests.ps1 @@ -0,0 +1,92 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Get-PfbArrayConnectionPath - selector query keys (#64)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'sends remote_names, never names' { + Get-PfbArrayConnectionPath -RemoteName 'FB-B' -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and $Endpoint -eq 'array-connections/path' -and + $QueryParams['remote_names'] -eq 'FB-B' -and -not $QueryParams.ContainsKey('names') + } + } + + It 'still binds -Name through the alias, and emits remote_names for it' { + Get-PfbArrayConnectionPath -Name 'FB-B' -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' -and -not $QueryParams.ContainsKey('names') + } + } + + It 'declares Name as an alias of -RemoteName' { + (Get-Command Get-PfbArrayConnectionPath).Parameters['RemoteName'].Aliases | Should -Contain 'Name' + } + + It 'accumulates remote names piped by value into a single comma-joined key' { + 'FB-B','FB-C' | Get-PfbArrayConnectionPath -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B,FB-C' + } + } + + It 'binds -RemoteName by property name from a user-built object' { + [pscustomobject]@{ RemoteName = 'FB-B' } | Get-PfbArrayConnectionPath -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' + } + } + + It 'binds by property name through the Name alias' { + [pscustomobject]@{ Name = 'FB-B' } | Get-PfbArrayConnectionPath -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' + } + } + + It 'does NOT coerce a piped object into -RemoteName (binding-order guard)' { + { + [PSCustomObject]@{ + id = '10314f42-aaaa' + status = 'connected' + remote = [PSCustomObject]@{ id = 'r-1'; name = 'FB-B' } + } | Get-PfbArrayConnectionPath -Array $fakeArray + } | Should -Throw -ExpectedMessage '*stringified object*' + + # The throw is terminating, so the end block never runs and no request is issued at all. + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 -Exactly + } + + It 'still routes filter/sort/limit through the common helper' { + Get-PfbArrayConnectionPath -Filter "status='connected'" -Sort 'id' -Limit 5 -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['filter'] -eq "status='connected'" -and + $QueryParams['sort'] -eq 'id' -and $QueryParams['limit'] -eq 5 + } + } + + It 'sends no selector key at all when listing everything' { + Get-PfbArrayConnectionPath -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('remote_names') -and -not $QueryParams.ContainsKey('names') + } + } +} diff --git a/Tests/Get-PfbArrayConnectionPerformanceReplication.Tests.ps1 b/Tests/Get-PfbArrayConnectionPerformanceReplication.Tests.ps1 new file mode 100644 index 0000000..81962bf --- /dev/null +++ b/Tests/Get-PfbArrayConnectionPerformanceReplication.Tests.ps1 @@ -0,0 +1,103 @@ +#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') + } + } + + It 'sends remote_names, never names' { + Get-PfbArrayConnectionPerformanceReplication -RemoteName 'FB-B' -Array $fakeArray + + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $Endpoint -eq 'array-connections/performance/replication' -and + $QueryParams['remote_names'] -eq 'FB-B' -and -not $QueryParams.ContainsKey('names') + } + } + + It 'still binds -Name through the alias, and emits remote_names for it' { + Get-PfbArrayConnectionPerformanceReplication -Name 'FB-B' -Array $fakeArray + + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' -and -not $QueryParams.ContainsKey('names') + } + } + + It 'accumulates remote names piped by value into a single comma-joined key' { + 'FB-B','FB-C' | Get-PfbArrayConnectionPerformanceReplication -Array $fakeArray + + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B,FB-C' + } + } + + It 'binds -RemoteName by property name from a user-built object' { + [pscustomobject]@{ RemoteName = 'FB-B' } | Get-PfbArrayConnectionPerformanceReplication -Array $fakeArray + + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' + } + } + + It 'binds by property name through the Name alias' { + [pscustomobject]@{ Name = 'FB-B' } | Get-PfbArrayConnectionPerformanceReplication -Array $fakeArray + + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' + } + } + + It 'does NOT coerce a piped object into -RemoteName (binding-order guard)' { + { + [PSCustomObject]@{ + id = '10314f42-aaaa' + status = 'connected' + remote = [PSCustomObject]@{ id = 'r-1'; name = 'FB-B' } + } | Get-PfbArrayConnectionPerformanceReplication -Array $fakeArray + } | Should -Throw -ExpectedMessage '*stringified object*' + + # The throw is terminating, so the end block never runs and no request is issued at all. + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 -Exactly + } + + It 'keeps remote_names alongside the time-range keys' { + Get-PfbArrayConnectionPerformanceReplication -RemoteName 'FB-B' -Resolution 86400000 ` + -StartTime 1609459200000 -Array $fakeArray + + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' -and + $QueryParams['resolution'] -eq 86400000 -and $QueryParams['start_time'] -eq 1609459200000 + } + } +} diff --git a/Tests/Get-PfbArrayPerformanceReplication.Tests.ps1 b/Tests/Get-PfbArrayPerformanceReplication.Tests.ps1 new file mode 100644 index 0000000..494a33d --- /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') + } + } +} diff --git a/Tests/Get-PfbCapabilityMap.Tests.ps1 b/Tests/Get-PfbCapabilityMap.Tests.ps1 new file mode 100644 index 0000000..c4aa9eb --- /dev/null +++ b/Tests/Get-PfbCapabilityMap.Tests.ps1 @@ -0,0 +1,53 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:originalModuleRoot = InModuleScope PureStorageFlashBladePowerShell { $script:PfbModuleRoot } +} + +Describe 'Get-PfbCapabilityMap' { + AfterEach { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ root = $script:originalModuleRoot } { + $script:PfbCapabilityMap = $null + $script:PfbModuleRoot = $root + } + } + + It 'loads and returns the manifest from Data/PfbCapabilityMap.json under the module root' { + New-Item -ItemType Directory -Path 'TestDrive:\Data' -Force | Out-Null + [PSCustomObject]@{ + schemaVersion = 1 + endpoints = [PSCustomObject]@{ 'GET /widgets' = [PSCustomObject]@{ minVersion = '9.0' } } + } | ConvertTo-Json -Depth 10 | Set-Content -Path 'TestDrive:\Data\PfbCapabilityMap.json' + + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ root = 'TestDrive:\' } { + $script:PfbModuleRoot = $root + $result = Get-PfbCapabilityMap + $result.endpoints.'GET /widgets'.minVersion | Should -Be '9.0' + } + } + + It 'caches the loaded manifest -- a second call does not re-read the file' { + New-Item -ItemType Directory -Path 'TestDrive:\Data' -Force | Out-Null + [PSCustomObject]@{ schemaVersion = 1; endpoints = [PSCustomObject]@{} } | + ConvertTo-Json -Depth 10 | Set-Content -Path 'TestDrive:\Data\PfbCapabilityMap.json' + + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ root = 'TestDrive:\' } { + $script:PfbModuleRoot = $root + $first = Get-PfbCapabilityMap + Remove-Item -Path (Join-Path $root 'Data/PfbCapabilityMap.json') -Force + $second = Get-PfbCapabilityMap + [object]::ReferenceEquals($first, $second) | Should -BeTrue + } + } + + It 'returns $null gracefully when Data/PfbCapabilityMap.json does not exist' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ root = 'TestDrive:\empty' } { + $script:PfbModuleRoot = $root + Get-PfbCapabilityMap | Should -BeNullOrEmpty + } + } +} diff --git a/Tests/Get-PfbFileSystemGroup.Tests.ps1 b/Tests/Get-PfbFileSystemGroup.Tests.ps1 new file mode 100644 index 0000000..a319c14 --- /dev/null +++ b/Tests/Get-PfbFileSystemGroup.Tests.ps1 @@ -0,0 +1,73 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'Get-PfbFileSystemGroup' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'GETs file-systems/groups with AutoPaginate' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemGroup -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and $Endpoint -eq 'file-systems/groups' -and $AutoPaginate -eq $true + } + } + + It 'filters by -FileSystemName' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemGroup -FileSystemName 'fs1' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['file_system_names'] -eq 'fs1' + } + } + + It 'sends -GroupName as group_names and -GroupId as gids' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemGroup -GroupName 'staff' -GroupId '2001' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['group_names'] -eq 'staff' -and $QueryParams['gids'] -eq '2001' + } + } + + It 'honors falsy -Limit 0 and empty -Filter instead of dropping them' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemGroup -Filter '' -Limit 0 -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('filter') -and $QueryParams['filter'] -eq '' -and + $QueryParams.ContainsKey('limit') -and $QueryParams['limit'] -eq 0 + } + } + + It 'sends total_only=true when -TotalOnly is set' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemGroup -TotalOnly -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['total_only'] -eq 'true' + } + } +} diff --git a/Tests/Get-PfbFileSystemGroupQuota.Tests.ps1 b/Tests/Get-PfbFileSystemGroupQuota.Tests.ps1 new file mode 100644 index 0000000..8f845bd --- /dev/null +++ b/Tests/Get-PfbFileSystemGroupQuota.Tests.ps1 @@ -0,0 +1,84 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'Get-PfbFileSystemGroupQuota' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'GETs file-system-group-quotas with AutoPaginate' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemGroupQuota -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and $Endpoint -eq 'file-system-group-quotas' -and $AutoPaginate -eq $true + } + } + + It 'filters by -FileSystemName' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemGroupQuota -FileSystemName 'fs1' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['file_system_names'] -eq 'fs1' + } + } + + It 'sends -GroupName as group_names and -GroupId as gids' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemGroupQuota -GroupName 'staff' -GroupId '2001' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['group_names'] -eq 'staff' -and $QueryParams['gids'] -eq '2001' + } + } + + It 'passes -Filter, -Sort, and -Limit through' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemGroupQuota -Filter "quota > 0" -Sort 'usage-' -Limit 5 -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['filter'] -eq 'quota > 0' -and $QueryParams['sort'] -eq 'usage-' -and $QueryParams['limit'] -eq 5 + } + } + + It 'honors falsy -Limit 0 and empty -Filter instead of dropping them' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemGroupQuota -Filter '' -Limit 0 -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('filter') -and $QueryParams['filter'] -eq '' -and + $QueryParams.ContainsKey('limit') -and $QueryParams['limit'] -eq 0 + } + } + + It 'sends total_only=true when -TotalOnly is set' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemGroupQuota -TotalOnly -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['total_only'] -eq 'true' + } + } +} diff --git a/Tests/Get-PfbFileSystemSession.Tests.ps1 b/Tests/Get-PfbFileSystemSession.Tests.ps1 new file mode 100644 index 0000000..a0e0237 --- /dev/null +++ b/Tests/Get-PfbFileSystemSession.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-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') + } + } + + 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' + } + } +} diff --git a/Tests/Get-PfbFileSystemUser.Tests.ps1 b/Tests/Get-PfbFileSystemUser.Tests.ps1 new file mode 100644 index 0000000..ec99d3b --- /dev/null +++ b/Tests/Get-PfbFileSystemUser.Tests.ps1 @@ -0,0 +1,73 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'Get-PfbFileSystemUser' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'GETs file-systems/users with AutoPaginate' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemUser -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and $Endpoint -eq 'file-systems/users' -and $AutoPaginate -eq $true + } + } + + It 'filters by -FileSystemName' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemUser -FileSystemName 'fs1' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['file_system_names'] -eq 'fs1' + } + } + + It 'sends -UserName as user_names, -UserId as uids, and -UserSid as user_sids' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemUser -UserName 'jdoe' -UserId '1001' -UserSid 'S-1-5-1' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['user_names'] -eq 'jdoe' -and $QueryParams['uids'] -eq '1001' -and $QueryParams['user_sids'] -eq 'S-1-5-1' + } + } + + It 'honors falsy -Limit 0 and empty -Filter instead of dropping them' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemUser -Filter '' -Limit 0 -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('filter') -and $QueryParams['filter'] -eq '' -and + $QueryParams.ContainsKey('limit') -and $QueryParams['limit'] -eq 0 + } + } + + It 'sends total_only=true when -TotalOnly is set' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemUser -TotalOnly -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['total_only'] -eq 'true' + } + } +} diff --git a/Tests/Get-PfbFileSystemUserGroupQuotaPolicy.Tests.ps1 b/Tests/Get-PfbFileSystemUserGroupQuotaPolicy.Tests.ps1 new file mode 100644 index 0000000..f263b6d --- /dev/null +++ b/Tests/Get-PfbFileSystemUserGroupQuotaPolicy.Tests.ps1 @@ -0,0 +1,84 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'Get-PfbFileSystemUserGroupQuotaPolicy' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'GETs file-systems/user-group-quota-policies with AutoPaginate' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemUserGroupQuotaPolicy -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and $Endpoint -eq 'file-systems/user-group-quota-policies' -and $AutoPaginate -eq $true + } + } + + It 'filters by -MemberName (the file system) and -PolicyName' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemUserGroupQuotaPolicy -MemberName 'fs1' -PolicyName 'pol-1' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['member_names'] -eq 'fs1' -and $QueryParams['policy_names'] -eq 'pol-1' + } + } + + It 'filters by -PolicyId and -MemberId' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemUserGroupQuotaPolicy -PolicyId 'pid-1' -MemberId 'mid-1' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_ids'] -eq 'pid-1' -and $QueryParams['member_ids'] -eq 'mid-1' + } + } + + It 'passes -Filter, -Sort, and -Limit through' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemUserGroupQuotaPolicy -Filter "name='pol-1'" -Sort 'name-' -Limit 5 -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['filter'] -eq "name='pol-1'" -and $QueryParams['sort'] -eq 'name-' -and $QueryParams['limit'] -eq 5 + } + } + + It 'honors falsy -Limit 0 and empty -Filter instead of dropping them' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemUserGroupQuotaPolicy -Filter '' -Limit 0 -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('filter') -and $QueryParams['filter'] -eq '' -and + $QueryParams.ContainsKey('limit') -and $QueryParams['limit'] -eq 0 + } + } + + It 'sends total_only=true when -TotalOnly is set' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemUserGroupQuotaPolicy -TotalOnly -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['total_only'] -eq 'true' + } + } +} diff --git a/Tests/Get-PfbFileSystemUserQuota.Tests.ps1 b/Tests/Get-PfbFileSystemUserQuota.Tests.ps1 new file mode 100644 index 0000000..9debde6 --- /dev/null +++ b/Tests/Get-PfbFileSystemUserQuota.Tests.ps1 @@ -0,0 +1,87 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'Get-PfbFileSystemUserQuota' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'GETs file-system-user-quotas with AutoPaginate' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemUserQuota -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and $Endpoint -eq 'file-system-user-quotas' -and $AutoPaginate -eq $true + } + } + + It 'filters by -FileSystemName' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemUserQuota -FileSystemName 'fs1' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['file_system_names'] -eq 'fs1' + } + } + + It 'sends -UserName as user_names, -UserId as uids (not user_ids), and -UserSid as user_sids' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemUserQuota -UserName 'jdoe' -UserId '1001' -UserSid 'S-1-5-1' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['user_names'] -eq 'jdoe' -and + $QueryParams['uids'] -eq '1001' -and + $QueryParams['user_sids'] -eq 'S-1-5-1' -and + -not $QueryParams.ContainsKey('user_ids') + } + } + + It 'passes -Filter, -Sort, and -Limit through' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemUserQuota -Filter "quota > 0" -Sort 'usage-' -Limit 5 -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['filter'] -eq 'quota > 0' -and $QueryParams['sort'] -eq 'usage-' -and $QueryParams['limit'] -eq 5 + } + } + + It 'honors falsy -Limit 0 and empty -Filter instead of dropping them' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemUserQuota -Filter '' -Limit 0 -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('filter') -and $QueryParams['filter'] -eq '' -and + $QueryParams.ContainsKey('limit') -and $QueryParams['limit'] -eq 0 + } + } + + It 'sends total_only=true when -TotalOnly is set' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbFileSystemUserQuota -TotalOnly -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['total_only'] -eq 'true' + } + } +} diff --git a/Tests/Get-PfbPolicyAllMember.Tests.ps1 b/Tests/Get-PfbPolicyAllMember.Tests.ps1 new file mode 100644 index 0000000..a421935 --- /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') + } + } +} diff --git a/Tests/Get-PfbUserGroupQuotaPolicy.Tests.ps1 b/Tests/Get-PfbUserGroupQuotaPolicy.Tests.ps1 new file mode 100644 index 0000000..0e3a563 --- /dev/null +++ b/Tests/Get-PfbUserGroupQuotaPolicy.Tests.ps1 @@ -0,0 +1,89 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'Get-PfbUserGroupQuotaPolicy' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'GETs user-group-quota-policies with AutoPaginate and no filters by default' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicy -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and + $Endpoint -eq 'user-group-quota-policies' -and + $AutoPaginate -eq $true -and + $QueryParams.Keys.Count -eq 0 + } + } + + It 'joins -Name into a comma-separated names query param' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicy -Name 'pol-1', 'pol-2' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['names'] -eq 'pol-1,pol-2' + } + } + + It 'sends -Id as the ids query param' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicy -Id 'id-1' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'id-1' -and -not $QueryParams.ContainsKey('names') + } + } + + It 'passes -Filter, -Sort, and -Limit through' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicy -Filter "enabled='true'" -Sort 'name-' -Limit 5 -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['filter'] -eq "enabled='true'" -and + $QueryParams['sort'] -eq 'name-' -and + $QueryParams['limit'] -eq 5 + } + } + + It 'honors falsy -Limit 0 and empty -Filter instead of dropping them' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicy -Filter '' -Limit 0 -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('filter') -and $QueryParams['filter'] -eq '' -and + $QueryParams.ContainsKey('limit') -and $QueryParams['limit'] -eq 0 + } + } + + It 'sends total_only=true when -TotalOnly is set' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicy -TotalOnly -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['total_only'] -eq 'true' + } + } +} diff --git a/Tests/Get-PfbUserGroupQuotaPolicyFileSystem.Tests.ps1 b/Tests/Get-PfbUserGroupQuotaPolicyFileSystem.Tests.ps1 new file mode 100644 index 0000000..44e0a5d --- /dev/null +++ b/Tests/Get-PfbUserGroupQuotaPolicyFileSystem.Tests.ps1 @@ -0,0 +1,84 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'Get-PfbUserGroupQuotaPolicyFileSystem' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'GETs user-group-quota-policies/file-systems with AutoPaginate' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyFileSystem -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and $Endpoint -eq 'user-group-quota-policies/file-systems' -and $AutoPaginate -eq $true + } + } + + It 'filters by -PolicyName and -MemberName' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyFileSystem -PolicyName 'pol-1' -MemberName 'fs1' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_names'] -eq 'pol-1' -and $QueryParams['member_names'] -eq 'fs1' + } + } + + It 'filters by -PolicyId and -MemberId' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyFileSystem -PolicyId 'pid-1' -MemberId 'mid-1' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_ids'] -eq 'pid-1' -and $QueryParams['member_ids'] -eq 'mid-1' + } + } + + It 'passes -Filter, -Sort, and -Limit through' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyFileSystem -Filter "name='fs1'" -Sort 'name-' -Limit 5 -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['filter'] -eq "name='fs1'" -and $QueryParams['sort'] -eq 'name-' -and $QueryParams['limit'] -eq 5 + } + } + + It 'honors falsy -Limit 0 and empty -Filter instead of dropping them' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyFileSystem -Filter '' -Limit 0 -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('filter') -and $QueryParams['filter'] -eq '' -and + $QueryParams.ContainsKey('limit') -and $QueryParams['limit'] -eq 0 + } + } + + It 'sends total_only=true when -TotalOnly is set' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyFileSystem -TotalOnly -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['total_only'] -eq 'true' + } + } +} diff --git a/Tests/Get-PfbUserGroupQuotaPolicyMember.Tests.ps1 b/Tests/Get-PfbUserGroupQuotaPolicyMember.Tests.ps1 new file mode 100644 index 0000000..9d5a47b --- /dev/null +++ b/Tests/Get-PfbUserGroupQuotaPolicyMember.Tests.ps1 @@ -0,0 +1,73 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'Get-PfbUserGroupQuotaPolicyMember' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'GETs user-group-quota-policies/members with AutoPaginate' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyMember -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and $Endpoint -eq 'user-group-quota-policies/members' -and $AutoPaginate -eq $true + } + } + + It 'filters by -PolicyName and -MemberName' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyMember -PolicyName 'pol-1' -MemberName 'fs1' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_names'] -eq 'pol-1' -and $QueryParams['member_names'] -eq 'fs1' + } + } + + It 'passes -Limit through' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyMember -Limit 10 -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['limit'] -eq 10 + } + } + + It 'honors falsy -Limit 0 and empty -Filter instead of dropping them' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyMember -Filter '' -Limit 0 -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('filter') -and $QueryParams['filter'] -eq '' -and + $QueryParams.ContainsKey('limit') -and $QueryParams['limit'] -eq 0 + } + } + + It 'sends total_only=true when -TotalOnly is set' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyMember -TotalOnly -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['total_only'] -eq 'true' + } + } +} diff --git a/Tests/Get-PfbUserGroupQuotaPolicyRule.Tests.ps1 b/Tests/Get-PfbUserGroupQuotaPolicyRule.Tests.ps1 new file mode 100644 index 0000000..ba82150 --- /dev/null +++ b/Tests/Get-PfbUserGroupQuotaPolicyRule.Tests.ps1 @@ -0,0 +1,113 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'Get-PfbUserGroupQuotaPolicyRule' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'GETs user-group-quota-policies/rules with AutoPaginate and no filters by default' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyRule -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and $Endpoint -eq 'user-group-quota-policies/rules' -and $AutoPaginate -eq $true + } + } + + It 'scopes by -PolicyName via the policy_names query param' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyRule -PolicyName 'pol-1' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_names'] -eq 'pol-1' + } + } + + It 'accepts -PolicyName from the pipeline and joins multiple values' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + 'pol-1', 'pol-2' | Get-PfbUserGroupQuotaPolicyRule -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_names'] -eq 'pol-1,pol-2' + } + } + + It 'scopes by -PolicyId via the policy_ids query param' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyRule -PolicyId 'pid-1' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_ids'] -eq 'pid-1' + } + } + + It 'filters by the rule''s own -Name/-Id independent of policy scoping' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyRule -PolicyName 'pol-1' -Name 'rule-1' -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_names'] -eq 'pol-1' -and $QueryParams['names'] -eq 'rule-1' + } + } + + It 'passes -Filter, -Sort, and -Limit through' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyRule -Filter "quota_type='user'" -Sort 'name-' -Limit 5 -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['filter'] -eq "quota_type='user'" -and $QueryParams['sort'] -eq 'name-' -and $QueryParams['limit'] -eq 5 + } + } + + It 'honors falsy -Limit 0 and empty -Filter instead of dropping them' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyRule -Filter '' -Limit 0 -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('filter') -and $QueryParams['filter'] -eq '' -and + $QueryParams.ContainsKey('limit') -and $QueryParams['limit'] -eq 0 + } + } + + It 'sends total_only=true when -TotalOnly is set' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyRule -TotalOnly -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['total_only'] -eq 'true' + } + } + + It 'rejects supplying both -Name and -Id together' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Get-PfbUserGroupQuotaPolicyRule -Name 'rule-1' -Id 'rid-1' -Array $arr + } } | Should -Throw + } +} diff --git a/Tests/Get-PfbVersionMap.Tests.ps1 b/Tests/Get-PfbVersionMap.Tests.ps1 new file mode 100644 index 0000000..e5be11a --- /dev/null +++ b/Tests/Get-PfbVersionMap.Tests.ps1 @@ -0,0 +1,51 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:originalModuleRoot = InModuleScope PureStorageFlashBladePowerShell { $script:PfbModuleRoot } +} + +Describe 'Get-PfbVersionMap' { + AfterEach { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ root = $script:originalModuleRoot } { + $script:PfbVersionMap = $null + $script:PfbModuleRoot = $root + } + } + + It 'loads and returns the map from Data/PfbVersionMap.json under the module root' { + New-Item -ItemType Directory -Path 'TestDrive:\Data' -Force | Out-Null + [PSCustomObject]@{ '2.27' = [PSCustomObject]@{ purity = '4.8.3' } } | + ConvertTo-Json -Depth 10 | Set-Content -Path 'TestDrive:\Data\PfbVersionMap.json' + + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ root = 'TestDrive:\' } { + $script:PfbModuleRoot = $root + $result = Get-PfbVersionMap + $result.'2.27'.purity | Should -Be '4.8.3' + } + } + + It 'caches the loaded map -- a second call does not re-read the file' { + New-Item -ItemType Directory -Path 'TestDrive:\Data' -Force | Out-Null + [PSCustomObject]@{ '2.27' = [PSCustomObject]@{ purity = '4.8.3' } } | + ConvertTo-Json -Depth 10 | Set-Content -Path 'TestDrive:\Data\PfbVersionMap.json' + + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ root = 'TestDrive:\' } { + $script:PfbModuleRoot = $root + $first = Get-PfbVersionMap + Remove-Item -Path (Join-Path $root 'Data/PfbVersionMap.json') -Force + $second = Get-PfbVersionMap + [object]::ReferenceEquals($first, $second) | Should -BeTrue + } + } + + It 'returns $null gracefully when Data/PfbVersionMap.json does not exist' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ root = 'TestDrive:\empty' } { + $script:PfbModuleRoot = $root + Get-PfbVersionMap | Should -BeNullOrEmpty + } + } +} diff --git a/Tests/Invoke-PfbApiRequest.CapabilityCheck.Tests.ps1 b/Tests/Invoke-PfbApiRequest.CapabilityCheck.Tests.ps1 new file mode 100644 index 0000000..d388cfc --- /dev/null +++ b/Tests/Invoke-PfbApiRequest.CapabilityCheck.Tests.ps1 @@ -0,0 +1,81 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } +<# +.SYNOPSIS + Confirms Invoke-PfbApiRequest's capability-check gate blocks incompatible calls before + any HTTP request is sent, and lets compatible calls through unchanged. +.DESCRIPTION + Injects a small synthetic capability map into module state rather than relying on the + real committed Data/PfbCapabilityMap.json, so this is independent of what the real map + currently contains. +#> + +BeforeAll { + Import-Module "$PSScriptRoot/../PureStorageFlashBladePowerShell.psd1" -Force + + $script:originalModuleRoot = InModuleScope PureStorageFlashBladePowerShell { $script:PfbModuleRoot } + + function New-TestConnection { + param([string]$ApiVersion = '2.20') + [PSCustomObject]@{ + Endpoint = 'fb.test' + ApiVersion = $ApiVersion + AuthToken = 'session-token' + BearerToken = $null + ApiToken = 'T-fake-token' + AuthMethod = 'ApiToken' + SkipCertificateCheck = $false + ConnectedAt = [datetime]::UtcNow + } + } +} + +Describe 'Invoke-PfbApiRequest - capability check gate' { + BeforeEach { + InModuleScope PureStorageFlashBladePowerShell { + $script:PfbCapabilityMap = [PSCustomObject]@{ + endpoints = [PSCustomObject]@{ + 'GET /file-systems' = [PSCustomObject]@{ + minVersion = '2.26' + parameters = [PSCustomObject]@{} + bodyProperties = [PSCustomObject]@{} + } + } + } + $script:PfbVersionMap = $null + } + } + + AfterEach { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ root = $script:originalModuleRoot } { + $script:PfbCapabilityMap = $null + $script:PfbVersionMap = $null + $script:PfbModuleRoot = $root + } + } + + It 'throws before sending the request when the endpoint requires a newer REST version than the connected array' { + $array = New-TestConnection -ApiVersion '2.20' + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod { throw 'should never be called' } + + { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + Invoke-PfbApiRequest -Array $array -Method GET -Endpoint 'file-systems' + } + } | Should -Throw -ExpectedMessage '*requires REST 2.26*' + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 0 + } + + It 'proceeds to send the request when the connected array meets the required version' { + $array = New-TestConnection -ApiVersion '2.26' + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod { + [PSCustomObject]@{ items = @() } + } -ParameterFilter { $Uri -like '*file-systems*' } + + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + Invoke-PfbApiRequest -Array $array -Method GET -Endpoint 'file-systems' | Out-Null + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 1 -Exactly + } +} diff --git a/Tests/Invoke-PfbApiRequest.Reconnect.Tests.ps1 b/Tests/Invoke-PfbApiRequest.Reconnect.Tests.ps1 index 774b5cd..3cc1199 100644 --- a/Tests/Invoke-PfbApiRequest.Reconnect.Tests.ps1 +++ b/Tests/Invoke-PfbApiRequest.Reconnect.Tests.ps1 @@ -156,4 +156,32 @@ Describe 'Invoke-PfbApiRequest reconnect on session-token rejection' { Should -Invoke -ModuleName PureStorageFlashBladePowerShell Connect-PfbArrayInternal -Times 0 } + + It 'carries the HTTP status out of the reconnect-then-throw path, not just the direct one' { + # WHY SEPARATELY FROM Tests/ConvertTo-PfbApiError.Tests.ps1 + # + # Invoke-PfbApiRequest has TWO throw sites that both call ConvertTo-PfbApiError, reached by + # different statuses. A 400 takes the plain else branch. A 401/403 on a reconnectable + # session goes through reconnect-and-retry first and, when the retry also fails, throws + # from inside that block instead -- where the ErrorRecord being formatted is the OUTER + # catch's original error, not the retry's. + # + # A live 400 against the lab array confirmed the direct branch end to end. The 403 branch + # could NOT be provoked live: the lab credential is a full array admin, so no read is + # refused. This pins the branch that live testing could not reach, and is mocked + # deliberately rather than presented as live evidence. + $array = New-TestConnection + Mock -ModuleName PureStorageFlashBladePowerShell Connect-PfbArrayInternal { + [PSCustomObject]@{ AuthToken = 'refreshed-token'; ConnectedAt = [datetime]::UtcNow } + } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod { + throw (New-MockHttpError -StatusCode 403 -Message 'Access Denied') + } -ParameterFilter { $Uri -like '*file-systems*' } + + { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ array = $array } { + Invoke-PfbApiRequest -Array $array -Method GET -Endpoint 'file-systems' | Out-Null + } + } | Should -Throw -ExpectedMessage '*(HTTP 403)*' + } } diff --git a/Tests/Invoke-PfbApiRequest.Tests.ps1 b/Tests/Invoke-PfbApiRequest.Tests.ps1 index 88d0000..84dfba8 100644 --- a/Tests/Invoke-PfbApiRequest.Tests.ps1 +++ b/Tests/Invoke-PfbApiRequest.Tests.ps1 @@ -63,3 +63,242 @@ Describe 'Invoke-PfbApiRequest - HttpTimeoutMs is applied' { } } } + +Describe 'Invoke-PfbApiRequest - PUT support' { + It 'sends Method PUT with a serialised JSON body' { + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod { + [PSCustomObject]@{ items = @() } + } -ParameterFilter { $Uri -like '*presets/workload*' } + + InModuleScope PureStorageFlashBladePowerShell { + $array = [PSCustomObject]@{ + Endpoint = 'fb.test'; ApiVersion = '2.26'; AuthToken = 'tok' + ApiToken = $null; AuthMethod = 'ApiToken'; SkipCertificateCheck = $false + } + Invoke-PfbApiRequest -Array $array -Method PUT -Endpoint 'presets/workload' -Body @{ name = 'p1' } | Out-Null + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PUT' -and $Body -eq '{"name":"p1"}' + } + } + + It 'sends no Body key on a PUT with no -Body' { + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod { + [PSCustomObject]@{ items = @() } + } -ParameterFilter { $Uri -like '*presets/workload*' } + + InModuleScope PureStorageFlashBladePowerShell { + $array = [PSCustomObject]@{ + Endpoint = 'fb.test'; ApiVersion = '2.26'; AuthToken = 'tok' + ApiToken = $null; AuthMethod = 'ApiToken'; SkipCertificateCheck = $false + } + Invoke-PfbApiRequest -Array $array -Method PUT -Endpoint 'presets/workload' | Out-Null + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PUT' -and $null -eq $Body + } + } + + # Guards against widening the :100 body guard too far -- a body handed to a verb that must + # not carry one should be dropped, exactly as it was before PUT was added. + It 'still sends no Body on GET or DELETE even when -Body is supplied' -ForEach @( + @{ Verb = 'GET' } + @{ Verb = 'DELETE' } + ) { + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod { + [PSCustomObject]@{ items = @() } + } -ParameterFilter { $Uri -like '*file-systems*' } + + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ Verb = $Verb } { + param($Verb) + $array = [PSCustomObject]@{ + Endpoint = 'fb.test'; ApiVersion = '2.26'; AuthToken = 'tok' + ApiToken = $null; AuthMethod = 'ApiToken'; SkipCertificateCheck = $false + } + Invoke-PfbApiRequest -Array $array -Method $Verb -Endpoint 'file-systems' -Body @{ name = 'x' } | Out-Null + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 1 -Exactly -ParameterFilter { + $null -eq $Body + } + } + + It 'still rejects a verb outside the ValidateSet' { + InModuleScope PureStorageFlashBladePowerShell { + $array = [PSCustomObject]@{ + Endpoint = 'fb.test'; ApiVersion = '2.26'; AuthToken = 'tok' + ApiToken = $null; AuthMethod = 'ApiToken'; SkipCertificateCheck = $false + } + { Invoke-PfbApiRequest -Array $array -Method 'TRACE' -Endpoint 'file-systems' } | + Should -Throw + } + } +} + +Describe 'Invoke-PfbApiRequest - array request bodies' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod { + [PSCustomObject]@{ items = @() } + } + } + + # Key order inside each serialised object is not asserted anywhere below: PowerShell + # randomises hashtable enumeration order per process, so both orderings are correct. + + It 'serialises an array body as a top-level JSON array' { + InModuleScope PureStorageFlashBladePowerShell { + $array = [PSCustomObject]@{ + Endpoint = 'fb.test'; ApiVersion = '2.26'; AuthToken = 'tok' + ApiToken = $null; AuthMethod = 'ApiToken'; SkipCertificateCheck = $false + } + $tags = [hashtable[]]@( + @{ key = 'team'; value = 'analytics' } + @{ key = 'env'; value = 'prod' } + ) + Invoke-PfbApiRequest -Array $array -Method PUT -Endpoint 'workloads/tags/batch' -Body $tags | Out-Null + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 1 -Exactly -ParameterFilter { + $Body.StartsWith('[') -and $Body.EndsWith(']') -and + $Body -match '"analytics"' -and $Body -match '"prod"' + } + } + + # The single-element case is the one that regresses silently: piping a one-element + # collection into ConvertTo-Json unrolls it into a bare object. + It 'keeps a SINGLE-element array body an array rather than collapsing it to an object' { + InModuleScope PureStorageFlashBladePowerShell { + $array = [PSCustomObject]@{ + Endpoint = 'fb.test'; ApiVersion = '2.26'; AuthToken = 'tok' + ApiToken = $null; AuthMethod = 'ApiToken'; SkipCertificateCheck = $false + } + $tags = [hashtable[]]@(@{ key = 'team'; value = 'analytics' }) + Invoke-PfbApiRequest -Array $array -Method PUT -Endpoint 'workloads/tags/batch' -Body $tags | Out-Null + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 1 -Exactly -ParameterFilter { + $Body.StartsWith('[') -and $Body.EndsWith(']') -and $Body -match '"analytics"' + } + } + + # Regression gate on the widened -Body: a hashtable must still serialise to a JSON + # object, unchanged by the move from the pipeline to -InputObject. + It 'still serialises a hashtable body as a JSON object' { + InModuleScope PureStorageFlashBladePowerShell { + $array = [PSCustomObject]@{ + Endpoint = 'fb.test'; ApiVersion = '2.26'; AuthToken = 'tok' + ApiToken = $null; AuthMethod = 'ApiToken'; SkipCertificateCheck = $false + } + Invoke-PfbApiRequest -Array $array -Method POST -Endpoint 'buckets' -Body @{ name = 'b1' } | Out-Null + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 1 -Exactly -ParameterFilter { + $Body -eq '{"name":"b1"}' + } + } + + # -Body was widened from [hashtable] to [System.Collections.ICollection], not to + # [object]: the shapes [hashtable] rejected must still be rejected at bind time. + It 'still rejects a non-collection -Body' -ForEach @( + @{ Label = 'int'; Value = 42 } + @{ Label = 'string'; Value = 'not-a-body' } + @{ Label = 'PSCustomObject'; Value = [PSCustomObject]@{ name = 'b1' } } + ) { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ value = $Value } { + param($value) + $array = [PSCustomObject]@{ + Endpoint = 'fb.test'; ApiVersion = '2.26'; AuthToken = 'tok' + ApiToken = $null; AuthMethod = 'ApiToken'; SkipCertificateCheck = $false + } + { Invoke-PfbApiRequest -Array $array -Method POST -Endpoint 'buckets' -Body $value } | + Should -Throw + } + } + + # New-PfbFileSystemSnapshot builds $body = $null and passes it unconditionally, so the + # widened type must still bind an explicit $null, not only an omitted -Body. + It 'still accepts an explicit $null -Body and sends no body' { + InModuleScope PureStorageFlashBladePowerShell { + $array = [PSCustomObject]@{ + Endpoint = 'fb.test'; ApiVersion = '2.26'; AuthToken = 'tok' + ApiToken = $null; AuthMethod = 'ApiToken'; SkipCertificateCheck = $false + } + $nothing = $null + Invoke-PfbApiRequest -Array $array -Method POST -Endpoint 'buckets' -Body $nothing | Out-Null + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 1 -Exactly -ParameterFilter { + $null -eq $Body + } + } +} + +Describe 'Invoke-PfbApiRequest - AutoPaginate honors -Limit' { + It 'stops paginating and trims results once the running total reaches the requested limit' { + $script:pageCallCount = 0 + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod { + $script:pageCallCount++ + switch ($script:pageCallCount) { + 1 { + [PSCustomObject]@{ + items = @(1..6 | ForEach-Object { [PSCustomObject]@{ name = "fs$_" } }) + continuation_token = 'token-page-2' + } + } + 2 { + [PSCustomObject]@{ + items = @(7..12 | ForEach-Object { [PSCustomObject]@{ name = "fs$_" } }) + continuation_token = 'token-page-3' + } + } + default { + throw "Unexpected extra page request (call #$script:pageCallCount) -- the -Limit guard should have stopped pagination after call #2" + } + } + } -ParameterFilter { $Uri -like '*file-systems*' } + + $script:result = InModuleScope PureStorageFlashBladePowerShell { + $array = [PSCustomObject]@{ + Endpoint = 'fb.test'; ApiVersion = '2.26'; AuthToken = 'tok' + ApiToken = $null; AuthMethod = 'ApiToken'; SkipCertificateCheck = $false + } + Invoke-PfbApiRequest -Array $array -Method GET -Endpoint 'file-systems' -QueryParams @{ limit = 10 } -AutoPaginate + } + + $script:result.Count | Should -Be 10 + $script:pageCallCount | Should -Be 2 + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 2 -Exactly -ParameterFilter { $Uri -like '*file-systems*' } + } + + It 'still auto-paginates the full collection when no -Limit is given' { + $script:pageCallCount2 = 0 + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod { + $script:pageCallCount2++ + if ($script:pageCallCount2 -eq 1) { + [PSCustomObject]@{ + items = @(1..6 | ForEach-Object { [PSCustomObject]@{ name = "fs$_" } }) + continuation_token = 'token-page-2' + } + } + else { + [PSCustomObject]@{ + items = @(7..9 | ForEach-Object { [PSCustomObject]@{ name = "fs$_" } }) + } + } + } -ParameterFilter { $Uri -like '*file-systems*' } + + $script:result2 = InModuleScope PureStorageFlashBladePowerShell { + $array = [PSCustomObject]@{ + Endpoint = 'fb.test'; ApiVersion = '2.26'; AuthToken = 'tok' + ApiToken = $null; AuthMethod = 'ApiToken'; SkipCertificateCheck = $false + } + Invoke-PfbApiRequest -Array $array -Method GET -Endpoint 'file-systems' -QueryParams @{} -AutoPaginate + } + + $script:result2.Count | Should -Be 9 + $script:pageCallCount2 | Should -Be 2 + } +} diff --git a/Tests/Invoke-PfbNetworkTrace.Tests.ps1 b/Tests/Invoke-PfbNetworkTrace.Tests.ps1 new file mode 100644 index 0000000..64c5ae6 --- /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') + } + } +} diff --git a/Tests/Issue31.DriftConfidence.Tests.ps1 b/Tests/Issue31.DriftConfidence.Tests.ps1 new file mode 100644 index 0000000..2491370 --- /dev/null +++ b/Tests/Issue31.DriftConfidence.Tests.ps1 @@ -0,0 +1,73 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $script:repoRoot = Split-Path -Parent $PSScriptRoot + . (Join-Path $repoRoot 'tools/lib/PfbCmdletParamTools.ps1') + . (Join-Path $repoRoot 'tools/lib/PfbApiDriftTools.ps1') + + # The 56 cmdlets this issue converts. All were 'high' confidence before the change + # and must remain 'high' after it -- see design spec constraint 7. + $script:inScope = @( + 'New-PfbApiToken','Update-PfbAdmin','Update-PfbApiClient','Update-PfbManagementAccessPolicy', + 'Update-PfbOidcIdp','Update-PfbSaml2Idp','Update-PfbCertificate','New-PfbCertificateCertificateGroup', + 'Update-PfbBucketAuditFilter','Update-PfbActiveDirectory','Update-PfbDirectoryServiceRole', + 'Update-PfbFileSystemExport','Update-PfbRealmDefaults','Update-PfbHardware','Update-PfbHardwareConnector', + 'Update-PfbAsyncLog','Update-PfbLogTargetFileSystem','Update-PfbLogTargetObjectStore', + 'Update-PfbSnmpManager','Update-PfbSyslogServer','New-PfbLegalHoldEntity','Update-PfbKmip', + 'Update-PfbLag','Update-PfbLegalHold','Update-PfbLegalHoldEntity','Update-PfbLifecycleRule', + 'New-PfbNetworkInterfaceTlsPolicy','Update-PfbDns','Update-PfbNetworkInterface', + 'Update-PfbNetworkInterfaceConnector','Update-PfbSubnet','New-PfbNodeGroupNode','Update-PfbNode', + 'Update-PfbNodeGroup','Update-PfbObjectStoreAccountExport','Update-PfbObjectStoreRemoteCredential', + 'Update-PfbObjectStoreRole','Update-PfbObjectStoreVirtualHost','New-PfbNetworkAccessRule', + 'New-PfbNfsExportRule','New-PfbPolicyFileSystemReplicaLink','New-PfbQosPolicyMember', + 'New-PfbS3ExportRule','New-PfbSmbClientRule','New-PfbSmbShareRule','Update-PfbQosPolicy', + 'Update-PfbSshCaPolicy','Update-PfbStorageClassTieringPolicy','Update-PfbTlsPolicy', + 'Update-PfbWormPolicy','New-PfbArrayConnection','New-PfbFileSystemReplicaLinkPolicy', + 'New-PfbFleetMember','Update-PfbArrayConnection','Update-PfbFleet','Update-PfbTarget' + ) + + # Same "skip gracefully if absent" convention as Build-PfbApiDriftReport.Tests.ps1's + # real-generated-artifacts Describe block: Build-PfbApiDriftReport.ps1 throws (by + # design -- see its RuntimeException at line ~156) when the capability map's newest + # `generatedFrom` spec version isn't physically present under tools/specs/. CI never + # populates tools/specs/ (gitignored, no fetch step in cross-platform-tests.yml), so + # without this guard this test hard-fails on every PR instead of skipping like every + # sibling real-artifact test does. + $script:realCapabilityMapPath = Join-Path $repoRoot 'Data/PfbCapabilityMap.json' + $script:realFieldCmdletMapPath = Join-Path $repoRoot 'Reports/PfbFieldCmdletMap.json' + $script:realSpecsDir = Join-Path $repoRoot 'tools/specs' + $script:hasRealArtifacts = (Test-Path $realCapabilityMapPath) -and (Test-Path $realFieldCmdletMapPath) -and + (Test-Path $realSpecsDir) -and (Get-ChildItem $realSpecsDir -Filter 'fb*.json' -ErrorAction SilentlyContinue) +} + +Describe 'Issue #31 - in-scope cmdlets keep high drift confidence' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + It 'no in-scope write endpoint has dropped to partial confidence' { + if (-not $hasRealArtifacts) { Set-ItResult -Skipped -Because 'real artifacts not present locally'; return } + + # Build-PfbApiDriftReport.ps1 has no -PassThru (verified: its parameters are + # SpecsDirectory, PublicDirectory, PrivateDirectory, CapabilityMapPath, + # FieldCmdletMapPath, OutputPath, ReportPath, SinceVersion). Generate to a temp + # path so the run never mutates the committed Reports/ artifacts, then read it. + $tmpJson = Join-Path ([IO.Path]::GetTempPath()) "pfb-drift-$([guid]::NewGuid()).json" + $tmpMd = [IO.Path]::ChangeExtension($tmpJson, '.md') + & (Join-Path $repoRoot 'tools/Build-PfbApiDriftReport.ps1') -OutputPath $tmpJson -ReportPath $tmpMd | Out-Null + $report = Get-Content $tmpJson -Raw | ConvertFrom-Json + Remove-Item $tmpJson, $tmpMd -ErrorAction SilentlyContinue + + $regressed = @( + $report.parameterGaps | + Where-Object { $_.endpoint -match '^(POST|PATCH|PUT) ' } | + Where-Object { $_.confidence.level -eq 'partial' } | + Where-Object { @($_.cmdlets | Where-Object { $inScope -contains $_ }).Count -gt 0 } | + ForEach-Object { "$($_.endpoint) [$($_.confidence.escapeHatchOnly -join ',')]" } + ) + + $regressed | Should -BeNullOrEmpty -Because @' +these endpoints became parser-untraceable, which means the drift report can no +longer see real gaps on them. Fix the body assignment so the parameter is BARE +and assigned INLINE, in one of the shapes Global Constraint 7 names. Do NOT +compute into a local variable first -- that is the pre-inversion rule and it +causes exactly the blindness this guard exists to catch. +'@ + } +} diff --git a/Tests/ModuleManifest.Tests.ps1 b/Tests/ModuleManifest.Tests.ps1 new file mode 100644 index 0000000..80d0feb --- /dev/null +++ b/Tests/ModuleManifest.Tests.ps1 @@ -0,0 +1,41 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +Describe 'Module manifest FunctionsToExport completeness' { + + BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifestPath = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + $manifest = Import-PowerShellDataFile -Path $manifestPath + $publicFiles = Get-ChildItem -Path (Join-Path $moduleRoot 'Public') -Filter '*.ps1' -Recurse + $script:publicNames = $publicFiles.BaseName + $script:exported = $manifest.FunctionsToExport + } + + It 'exports every cmdlet defined under Public/' { + $missing = $publicNames | Where-Object { $_ -notin $exported } + $missing | Should -BeNullOrEmpty -Because "these Public/ cmdlets are missing from FunctionsToExport: $($missing -join ', ')" + } + + It 'does not export a name with no backing Public/ file' { + $stale = $exported | Where-Object { $_ -notin $publicNames } + $stale | Should -BeNullOrEmpty -Because "these FunctionsToExport entries have no backing Public/ file: $($stale -join ', ')" + } + + It 'does not export any name more than once' { + $dupes = $exported | Group-Object | Where-Object Count -gt 1 | Select-Object -ExpandProperty Name + $dupes | Should -BeNullOrEmpty -Because "duplicate FunctionsToExport entries: $($dupes -join ', ')" + } + + It 'includes all 19 new user-group-quota-policy cmdlets' { + $expected = @( + 'Get-PfbUserGroupQuotaPolicy', 'New-PfbUserGroupQuotaPolicy', 'Update-PfbUserGroupQuotaPolicy', 'Remove-PfbUserGroupQuotaPolicy', + 'Get-PfbUserGroupQuotaPolicyRule', 'New-PfbUserGroupQuotaPolicyRule', 'Update-PfbUserGroupQuotaPolicyRule', 'Remove-PfbUserGroupQuotaPolicyRule', + 'Get-PfbUserGroupQuotaPolicyFileSystem', 'New-PfbUserGroupQuotaPolicyFileSystem', 'Remove-PfbUserGroupQuotaPolicyFileSystem', + 'Get-PfbUserGroupQuotaPolicyMember', + 'Get-PfbFileSystemUserGroupQuotaPolicy', 'New-PfbFileSystemUserGroupQuotaPolicy', 'Remove-PfbFileSystemUserGroupQuotaPolicy', + 'Get-PfbFileSystemUserQuota', 'Get-PfbFileSystemGroupQuota', 'Get-PfbFileSystemUser', 'Get-PfbFileSystemGroup' + ) + $missing = $expected | Where-Object { $_ -notin $exported } + $missing | Should -BeNullOrEmpty -Because "expected new cmdlets missing from FunctionsToExport: $($missing -join ', ')" + } +} diff --git a/Tests/New-PfbApiToken.Tests.ps1 b/Tests/New-PfbApiToken.Tests.ps1 new file mode 100644 index 0000000..9f39484 --- /dev/null +++ b/Tests/New-PfbApiToken.Tests.ps1 @@ -0,0 +1,96 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'New-PfbApiToken - query parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'admin selector uses the real wire names (regression: was names/ids)' { + It 'sends -Name as admin_names, NOT names' { + New-PfbApiToken -Name 'ops-admin' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and $Endpoint -eq 'admins/api-tokens' -and + $QueryParams['admin_names'] -eq 'ops-admin' -and + -not $QueryParams.ContainsKey('names') + } + } + + It 'sends -Id as admin_ids, NOT ids' { + New-PfbApiToken -Id 'admin-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['admin_ids'] -eq 'admin-1' -and + -not $QueryParams.ContainsKey('ids') + } + } + + It 'accepts -AdminNames as an alias of -Name' { + New-PfbApiToken -AdminNames 'ops-admin' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['admin_names'] -eq 'ops-admin' + } + } + + It 'accepts -AdminIds as an alias of -Id' { + New-PfbApiToken -AdminIds 'admin-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['admin_ids'] -eq 'admin-1' + } + } + + It 'exposes no separate -AdminNames/-AdminIds parameters (they are aliases only)' { + $params = (Get-Command New-PfbApiToken).Parameters + $params.Keys | Should -Not -Contain 'AdminNames' + $params.Keys | Should -Not -Contain 'AdminIds' + $params['Name'].Aliases | Should -Contain 'AdminNames' + $params['Id'].Aliases | Should -Contain 'AdminIds' + } + } + + Context '-Timeout query parameter' { + It 'sends timeout when supplied' { + New-PfbApiToken -Name 'ops-admin' -Timeout 86400000 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['timeout'] -eq 86400000 + } + } + + It 'sends an explicit -Timeout 0 rather than dropping it (constraint 2, integer field)' { + New-PfbApiToken -Name 'ops-admin' -Timeout 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('timeout') -and $QueryParams['timeout'] -eq 0 + } + } + + It 'omits timeout entirely when not supplied' { + New-PfbApiToken -Name 'ops-admin' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('timeout') + } + } + + It 'accepts a value beyond Int32 range (spec type is int64)' { + New-PfbApiToken -Name 'ops-admin' -Timeout 3000000000 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['timeout'] -eq 3000000000 + } + } + } +} diff --git a/Tests/New-PfbArrayConnection.Tests.ps1 b/Tests/New-PfbArrayConnection.Tests.ps1 new file mode 100644 index 0000000..8784fd7 --- /dev/null +++ b/Tests/New-PfbArrayConnection.Tests.ps1 @@ -0,0 +1,135 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'New-PfbArrayConnection - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'pre-existing typed parameters (constraint 16 -- were in NO parameter set)' { + It 'sends management_address, replication_addresses and connection_key together' { + New-PfbArrayConnection -ManagementAddress '10.0.2.100' -ReplicationAddress '10.0.3.100' ` + -ConnectionKey 'key-123' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and $Endpoint -eq 'array-connections' -and + $Body['management_address'] -eq '10.0.2.100' -and + @($Body['replication_addresses'])[0] -eq '10.0.3.100' -and + $Body['connection_key'] -eq 'key-123' + } + } + + It 'no longer silently discards -ManagementAddress when combined with -Attributes (regression: was a silent override, now an ambiguous-set error)' { + { New-PfbArrayConnection -ManagementAddress '10.0.2.100' -Attributes @{ connection_key = 'y' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + + It 'accepts -ManagementAddress/-ReplicationAddress/-ConnectionKey positionally (whole-branch review finding I-1: adding a parameter set disables ALL implicit positional binding, so this must stay explicit)' { + New-PfbArrayConnection '10.0.2.100' '10.0.3.100' 'key-123' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['management_address'] -eq '10.0.2.100' -and + @($Body['replication_addresses'])[0] -eq '10.0.3.100' -and + $Body['connection_key'] -eq 'key-123' + } + } + + It 'accepts -Attributes positionally' { + New-PfbArrayConnection @{ management_address = '10.0.2.100'; connection_key = 'k' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['management_address'] -eq '10.0.2.100' + } + } + } + + Context 'new typed parameters' { + It 'sends encrypted as a body field (ContainsKey semantics, not truthiness)' { + New-PfbArrayConnection -ManagementAddress '10.0.2.100' -ConnectionKey 'k' -Encrypted $false ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('encrypted') -and $Body['encrypted'] -eq $false + } + } + + It 'omits encrypted entirely when not supplied' { + New-PfbArrayConnection -ManagementAddress '10.0.2.100' -ConnectionKey 'k' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $Body.ContainsKey('encrypted') + } + } + + It 'builds ca_certificate_group as a name-reference object (constraint 8a, scalar reference)' { + New-PfbArrayConnection -ManagementAddress '10.0.2.100' -ConnectionKey 'k' -CaCertificateGroup 'my-certs' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['ca_certificate_group'].name -eq 'my-certs' + } + } + + It 'builds remote as a name-reference object (constraint 8a, scalar reference)' { + New-PfbArrayConnection -ManagementAddress '10.0.2.100' -ConnectionKey 'k' -Remote 'remote-fb' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['remote'].name -eq 'remote-fb' + } + } + + It 'passes throttle straight through as a composite hashtable (constraint 8c)' { + New-PfbArrayConnection -ManagementAddress '10.0.2.100' -ConnectionKey 'k' ` + -Throttle @{ default_limit = 1073741824 } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['throttle']['default_limit'] -eq 1073741824 + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + New-PfbArrayConnection -Attributes @{ management_address = '10.0.2.100'; connection_key = 'k' } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['management_address'] -eq '10.0.2.100' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { New-PfbArrayConnection -ConnectionKey 'k' -Attributes @{ connection_key = 'y' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'ManagementAddress' } + @{ Parameter = 'ReplicationAddress' } + @{ Parameter = 'ConnectionKey' } + @{ Parameter = 'CaCertificateGroup' } + @{ Parameter = 'Encrypted' } + @{ Parameter = 'Remote' } + @{ Parameter = 'Throttle' } + ) { + $attrs = (Get-Command New-PfbArrayConnection).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + } +} diff --git a/Tests/New-PfbCertificateCertificateGroup.Tests.ps1 b/Tests/New-PfbCertificateCertificateGroup.Tests.ps1 new file mode 100644 index 0000000..020c681 --- /dev/null +++ b/Tests/New-PfbCertificateCertificateGroup.Tests.ps1 @@ -0,0 +1,68 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'New-PfbCertificateCertificateGroup - query parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'existing name-based query parameters (confirmed already correct, not touched)' { + It 'still sends -CertificateName as certificate_names' { + New-PfbCertificateCertificateGroup -CertificateName 'cert-prod' -CertificateGroupName 'group-prod' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and $Endpoint -eq 'certificates/certificate-groups' -and + $QueryParams['certificate_names'] -eq 'cert-prod' -and + $QueryParams['certificate_group_names'] -eq 'group-prod' + } + } + } + + Context '-CertificateId and -CertificateGroupId query parameters' { + It 'sends -CertificateId as certificate_ids' { + New-PfbCertificateCertificateGroup -CertificateId 'cert-1' -CertificateGroupName 'group-prod' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['certificate_ids'] -eq 'cert-1' -and -not $QueryParams.ContainsKey('certificate_names') + } + } + + It 'sends -CertificateGroupId as certificate_group_ids' { + New-PfbCertificateCertificateGroup -CertificateName 'cert-prod' -CertificateGroupId 'group-1' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['certificate_group_ids'] -eq 'group-1' -and -not $QueryParams.ContainsKey('certificate_group_names') + } + } + + It 'sends both id-based selectors together' { + New-PfbCertificateCertificateGroup -CertificateId 'cert-1' -CertificateGroupId 'group-1' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['certificate_ids'] -eq 'cert-1' -and $QueryParams['certificate_group_ids'] -eq 'group-1' + } + } + + It 'omits certificate_ids and certificate_group_ids entirely when not supplied' { + New-PfbCertificateCertificateGroup -CertificateName 'cert-prod' -CertificateGroupName 'group-prod' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('certificate_ids') -and -not $QueryParams.ContainsKey('certificate_group_ids') + } + } + } +} diff --git a/Tests/New-PfbFileSystemReplicaLink.Tests.ps1 b/Tests/New-PfbFileSystemReplicaLink.Tests.ps1 new file mode 100644 index 0000000..22cdec4 --- /dev/null +++ b/Tests/New-PfbFileSystemReplicaLink.Tests.ps1 @@ -0,0 +1,74 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'New-PfbFileSystemReplicaLink - query parameters' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'required selectors' { + It 'sends local_file_system_names and remote_names to POST /file-system-replica-links' { + New-PfbFileSystemReplicaLink -LocalFileSystemName 'fs01' -RemoteArrayName 'remote-fb' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and $Endpoint -eq 'file-system-replica-links' -and + $QueryParams['local_file_system_names'] -eq 'fs01' -and + $QueryParams['remote_names'] -eq 'remote-fb' + } + } + + It 'sends remote_file_system_names when supplied' { + New-PfbFileSystemReplicaLink -LocalFileSystemName 'fs01' -RemoteArrayName 'remote-fb' ` + -RemoteFileSystemName 'fs01-dr' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_file_system_names'] -eq 'fs01-dr' + } + } + } + + Context 'RemoteDefaultExports tri-state (fixes: [switch] could never suppress remote default exports)' { + It 'omits remote_default_exports entirely when the parameter is not bound (defers to array default)' { + New-PfbFileSystemReplicaLink -LocalFileSystemName 'fs01' -RemoteArrayName 'remote-fb' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('remote_default_exports') + } + } + + It 'sends remote_default_exports=true when -RemoteDefaultExports $true' { + New-PfbFileSystemReplicaLink -LocalFileSystemName 'fs01' -RemoteArrayName 'remote-fb' ` + -RemoteDefaultExports $true -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_default_exports'] -eq 'true' + } + } + + It 'sends remote_default_exports=false when -RemoteDefaultExports $false (the previously-impossible case)' { + New-PfbFileSystemReplicaLink -LocalFileSystemName 'fs01' -RemoteArrayName 'remote-fb' ` + -RemoteDefaultExports $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_default_exports'] -eq 'false' + } + } + + It 'exposes -RemoteDefaultExports as a nullable bool, not a switch' { + $p = (Get-Command New-PfbFileSystemReplicaLink).Parameters['RemoteDefaultExports'] + $p.ParameterType | Should -Be ([Nullable[bool]]) + $p.SwitchParameter | Should -BeFalse + } + } +} diff --git a/Tests/New-PfbFileSystemReplicaLinkPolicy.Tests.ps1 b/Tests/New-PfbFileSystemReplicaLinkPolicy.Tests.ps1 new file mode 100644 index 0000000..5174d74 --- /dev/null +++ b/Tests/New-PfbFileSystemReplicaLinkPolicy.Tests.ps1 @@ -0,0 +1,123 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'New-PfbFileSystemReplicaLinkPolicy - query parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'existing selectors (converted to ContainsKey)' { + It 'sends policy_names and member_ids' { + New-PfbFileSystemReplicaLinkPolicy -PolicyName 'repl-daily' -MemberId 'member-1' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and $Endpoint -eq 'file-system-replica-links/policies' -and + $QueryParams['policy_names'] -eq 'repl-daily' -and + $QueryParams['member_ids'] -eq 'member-1' + } + } + + It 'sends policy_ids' { + New-PfbFileSystemReplicaLinkPolicy -PolicyId 'policy-1' -MemberId 'member-1' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_ids'] -eq 'policy-1' + } + } + } + + Context 'member_names does not exist on this endpoint (confirmed bug fix)' { + It 'never sends a member_names query parameter (regression: POST /file-system-replica-links/policies has no member_names)' { + New-PfbFileSystemReplicaLinkPolicy -PolicyName 'repl-daily' -LocalFileSystemName 'fs01' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('member_names') -and + $QueryParams['local_file_system_names'] -eq 'fs01' + } + } + + It 'accepts -MemberName as a backward-compatible alias of -LocalFileSystemName' { + New-PfbFileSystemReplicaLinkPolicy -PolicyName 'repl-daily' -MemberName 'fs01' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['local_file_system_names'] -eq 'fs01' -and + -not $QueryParams.ContainsKey('member_names') + } + } + + It 'exposes no separate -MemberName parameter (alias only)' { + $params = (Get-Command New-PfbFileSystemReplicaLinkPolicy).Parameters + $params.Keys | Should -Not -Contain 'MemberName' + $params['LocalFileSystemName'].Aliases | Should -Contain 'MemberName' + } + } + + Context 'new query parameters' { + It 'sends local_file_system_ids' { + New-PfbFileSystemReplicaLinkPolicy -PolicyName 'repl-daily' -LocalFileSystemId 'fs-id-1' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['local_file_system_ids'] -eq 'fs-id-1' + } + } + + It 'sends remote_ids' { + New-PfbFileSystemReplicaLinkPolicy -PolicyName 'repl-daily' -RemoteId 'remote-1' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_ids'] -eq 'remote-1' + } + } + + It 'sends remote_names' { + New-PfbFileSystemReplicaLinkPolicy -PolicyName 'repl-daily' -RemoteName 'remote-array' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'remote-array' + } + } + + It 'omits all optional query parameters when not supplied' { + New-PfbFileSystemReplicaLinkPolicy -PolicyName 'repl-daily' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('local_file_system_ids') -and + -not $QueryParams.ContainsKey('local_file_system_names') -and + -not $QueryParams.ContainsKey('remote_ids') -and + -not $QueryParams.ContainsKey('remote_names') + } + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'PolicyName' } + @{ Parameter = 'PolicyId' } + @{ Parameter = 'MemberId' } + @{ Parameter = 'LocalFileSystemName' } + @{ Parameter = 'LocalFileSystemId' } + @{ Parameter = 'RemoteId' } + @{ Parameter = 'RemoteName' } + ) { + $attrs = (Get-Command New-PfbFileSystemReplicaLinkPolicy).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + } +} diff --git a/Tests/New-PfbFileSystemUserGroupQuotaPolicy.Tests.ps1 b/Tests/New-PfbFileSystemUserGroupQuotaPolicy.Tests.ps1 new file mode 100644 index 0000000..c45b18a --- /dev/null +++ b/Tests/New-PfbFileSystemUserGroupQuotaPolicy.Tests.ps1 @@ -0,0 +1,65 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'New-PfbFileSystemUserGroupQuotaPolicy' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'POSTs file-systems/user-group-quota-policies with policy and member query params' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ fakeArray = $fakeArray } { + param($fakeArray) + New-PfbFileSystemUserGroupQuotaPolicy -PolicyName 'pol-1' -MemberName 'fs1' -Confirm:$false -Array $fakeArray + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and + $Endpoint -eq 'file-systems/user-group-quota-policies' -and + $QueryParams['policy_names'] -eq 'pol-1' -and + $QueryParams['member_names'] -eq 'fs1' + } + } + + It 'sends delete_existing_user_group_quota_settings and ignore_usage when requested' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ fakeArray = $fakeArray } { + param($fakeArray) + New-PfbFileSystemUserGroupQuotaPolicy -PolicyName 'pol-1' -MemberName 'fs1' -DeleteExistingUserGroupQuotaSettings -IgnoreUsage -Confirm:$false -Array $fakeArray + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['delete_existing_user_group_quota_settings'] -eq 'true' -and $QueryParams['ignore_usage'] -eq 'true' + } + } + + It 'throws when neither -PolicyName nor -PolicyId is supplied' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ fakeArray = $fakeArray } { + param($fakeArray) + New-PfbFileSystemUserGroupQuotaPolicy -MemberName 'fs1' -Confirm:$false -Array $fakeArray + } } | Should -Throw + } + + It 'throws when neither -MemberName nor -MemberId is supplied' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ fakeArray = $fakeArray } { + param($fakeArray) + New-PfbFileSystemUserGroupQuotaPolicy -PolicyName 'pol-1' -Confirm:$false -Array $fakeArray + } } | Should -Throw + } + + It 'does not call the API under -WhatIf' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ fakeArray = $fakeArray } { + param($fakeArray) + New-PfbFileSystemUserGroupQuotaPolicy -PolicyName 'pol-1' -MemberName 'fs1' -WhatIf -Array $fakeArray + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 + } +} diff --git a/Tests/New-PfbFleetMember.Tests.ps1 b/Tests/New-PfbFleetMember.Tests.ps1 new file mode 100644 index 0000000..974ecea --- /dev/null +++ b/Tests/New-PfbFleetMember.Tests.ps1 @@ -0,0 +1,108 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'New-PfbFleetMember - typed body/query parameters (#31, confirmed wire-contract bug)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'confirmed bug (issue #38): POST /fleets/members takes fleet_ids/fleet_names query + a members body, NOT member_names query' { + It 'sends the fleet key and member self-reference in the request body, per the OpenAPI spec (FleetMemberPost)' { + New-PfbFleetMember -FleetName 'fleet-prod' ` + -Members @{ key = 'fleet-key-abc'; member = @{ id = 'this-array-id' } } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and $Endpoint -eq 'fleets/members' -and + $QueryParams['fleet_names'] -eq 'fleet-prod' -and + @($Body['members']).Count -eq 1 -and + @($Body['members'])[0]['key'] -eq 'fleet-key-abc' -and + @($Body['members'])[0]['member']['id'] -eq 'this-array-id' + } + } + + It 'never sends a member_names query parameter (regression: POST /fleets/members has no member_names -- confirmed via OpenAPI spec, the endpoint could never have worked with the old shape)' { + New-PfbFleetMember -FleetName 'fleet-prod' ` + -Members @{ key = 'fleet-key-abc'; member = @{ id = 'this-array-id' } } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('member_names') -and -not $QueryParams.ContainsKey('member_ids') + } + } + + It 'has no -MemberName parameter (removed: member_names is not a valid query parameter for POST /fleets/members)' { + (Get-Command New-PfbFleetMember).Parameters.Keys | Should -Not -Contain 'MemberName' + } + } + + Context 'fleet selector' { + It 'sends fleet_ids when -FleetId is used (new query parameter)' { + New-PfbFleetMember -FleetId 'fleet-1' ` + -Members @{ key = 'fleet-key-abc'; member = @{ id = 'this-array-id' } } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['fleet_ids'] -eq 'fleet-1' -and -not $QueryParams.ContainsKey('fleet_names') + } + } + + It 'omits fleet_names/fleet_ids entirely when neither is supplied (constraint 19, no mandatory throw)' { + New-PfbFleetMember -Members @{ key = 'k'; member = @{ id = 'i' } } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('fleet_names') -and -not $QueryParams.ContainsKey('fleet_ids') + } + } + } + + Context '-Members composite array (constraint 8c: item schema has `key`, outside {id,name,resource_type})' { + It 'accepts multiple member entries' { + New-PfbFleetMember -FleetName 'fleet-prod' -Members @( + @{ key = 'k1'; member = @{ id = 'a1' } }, + @{ key = 'k1'; member = @{ id = 'a2' } } + ) -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + @($Body['members']).Count -eq 2 + } + } + + It 'omits the members body key entirely when not supplied (empty write is permitted, constraint 19)' { + New-PfbFleetMember -FleetName 'fleet-prod' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $Body.ContainsKey('members') + } + } + + It 'sends an EMPTY array for -Members @() so the list can be cleared, not omit the key (constraint 18: does not apply to [hashtable[]], unlike a scalar [hashtable])' { + New-PfbFleetMember -FleetName 'fleet-prod' -Members @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('members') -and @($Body['members']).Count -eq 0 + } + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'FleetName' } + @{ Parameter = 'FleetId' } + @{ Parameter = 'Members' } + ) { + $attrs = (Get-Command New-PfbFleetMember).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + } +} diff --git a/Tests/New-PfbLegalHoldEntity.Tests.ps1 b/Tests/New-PfbLegalHoldEntity.Tests.ps1 new file mode 100644 index 0000000..49f417c --- /dev/null +++ b/Tests/New-PfbLegalHoldEntity.Tests.ps1 @@ -0,0 +1,108 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'New-PfbLegalHoldEntity - query parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context '-HoldName/-MemberName are not exposed (whole-branch review: hold_names/member_names do not exist on this endpoint in any cached spec version)' { + It 'has no -HoldName parameter' { + (Get-Command New-PfbLegalHoldEntity).Parameters.Keys | Should -Not -Contain 'HoldName' + } + + It 'has no -MemberName parameter' { + (Get-Command New-PfbLegalHoldEntity).Parameters.Keys | Should -Not -Contain 'MemberName' + } + + It 'never sends hold_names or member_names query parameters' { + New-PfbLegalHoldEntity -Names 'litigation-hold-2024' -FileSystemNames 'fs1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and $Endpoint -eq 'legal-holds/held-entities' -and + -not $QueryParams.ContainsKey('hold_names') -and -not $QueryParams.ContainsKey('member_names') -and + $QueryParams['names'] -eq 'litigation-hold-2024' -and + $QueryParams['file_system_names'] -eq 'fs1' + } + } + } + + Context 'new query parameters' { + It 'joins -FileSystemIds and -FileSystemNames with commas' { + New-PfbLegalHoldEntity -FileSystemIds 'fsid-1', 'fsid-2' -FileSystemNames 'fs1', 'fs2' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['file_system_ids'] -eq 'fsid-1,fsid-2' -and + $QueryParams['file_system_names'] -eq 'fs1,fs2' + } + } + + It 'joins -Ids and -Names with commas' { + New-PfbLegalHoldEntity -Ids 'id-1', 'id-2' -Names 'e1', 'e2' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'id-1,id-2' -and + $QueryParams['names'] -eq 'e1,e2' + } + } + + It 'sends an EMPTY array for -Ids @() so the query key still reaches the wire (constraint 2)' { + New-PfbLegalHoldEntity -Ids @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('ids') -and @($QueryParams['ids'] -split ',' | Where-Object { $_ }).Count -eq 0 + } + } + + It 'joins -Paths with commas' { + New-PfbLegalHoldEntity -Paths '/dir1', '/dir2' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['paths'] -eq '/dir1,/dir2' + } + } + + It 'sends an explicit -Recursive:$false (ContainsKey semantics, not truthiness)' { + New-PfbLegalHoldEntity -FileSystemNames 'fs1' -Recursive $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('recursive') -and $QueryParams['recursive'] -eq $false + } + } + + It 'sends -Recursive:$true when supplied' { + New-PfbLegalHoldEntity -FileSystemNames 'fs1' -Recursive $true -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['recursive'] -eq $true + } + } + + It 'omits recursive entirely when not supplied' { + New-PfbLegalHoldEntity -FileSystemNames 'fs1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('recursive') + } + } + } + + Context 'endpoint accepts no request body' { + It 'sends an empty body when -Attributes is not supplied' { + New-PfbLegalHoldEntity -FileSystemNames 'fs1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + } +} diff --git a/Tests/New-PfbNetworkAccessRule.Tests.ps1 b/Tests/New-PfbNetworkAccessRule.Tests.ps1 new file mode 100644 index 0000000..3a53d58 --- /dev/null +++ b/Tests/New-PfbNetworkAccessRule.Tests.ps1 @@ -0,0 +1,140 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'New-PfbNetworkAccessRule - typed body/query params (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'existing -Attributes path still works' { + It 'POSTs with -Attributes hashtable unchanged' { + New-PfbNetworkAccessRule -PolicyName 'network-access-01' -Attributes @{ client = '10.0.0.0/8'; effect = 'allow' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and $Endpoint -eq 'network-access-policies/rules' -and + $QueryParams['policy_names'] -eq 'network-access-01' -and + $Body['client'] -eq '10.0.0.0/8' -and $Body['effect'] -eq 'allow' + } + } + } + + Context 'typed body parameters' { + It 'sends -Client as client' { + New-PfbNetworkAccessRule -PolicyName 'p1' -Client '10.0.0.0/8' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['client'] -eq '10.0.0.0/8' + } + } + + It 'sends -Effect as effect' { + New-PfbNetworkAccessRule -PolicyName 'p1' -Effect 'deny' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['effect'] -eq 'deny' + } + } + + It 'rejects an -Effect value outside the enum' { + { New-PfbNetworkAccessRule -PolicyName 'p1' -Effect 'bogus' -Confirm:$false -Array $fakeArray } | Should -Throw + } + + It 'sends -Index as index, including an explicit 0 (constraint 2, integer field)' { + New-PfbNetworkAccessRule -PolicyName 'p1' -Index 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('index') -and $Body['index'] -eq 0 + } + } + + It 'sends -Interfaces as interfaces' { + New-PfbNetworkAccessRule -PolicyName 'p1' -Interfaces @('management-ssh', 'snmp') -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['interfaces'].Count -eq 2 -and + $Body['interfaces'][0] -eq 'management-ssh' -and + $Body['interfaces'][1] -eq 'snmp' + } + } + + It 'sends an explicit empty -Interfaces @() (constraint 2, array field)' { + New-PfbNetworkAccessRule -PolicyName 'p1' -Interfaces @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('interfaces') -and $Body['interfaces'].Count -eq 0 + } + } + + It 'rejects an -Interfaces value outside the enum' { + { New-PfbNetworkAccessRule -PolicyName 'p1' -Interfaces @('bogus') -Confirm:$false -Array $fakeArray } | Should -Throw + } + + It 'omits fields that were not supplied' { + New-PfbNetworkAccessRule -PolicyName 'p1' -Client 'x' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $Body.ContainsKey('effect') -and -not $Body.ContainsKey('index') -and -not $Body.ContainsKey('interfaces') + } + } + } + + Context 'typed query parameters' { + It 'sends -BeforeRuleId as before_rule_id' { + New-PfbNetworkAccessRule -PolicyName 'p1' -BeforeRuleId 'rule-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['before_rule_id'] -eq 'rule-1' + } + } + + It 'sends -BeforeRuleName as before_rule_name' { + New-PfbNetworkAccessRule -PolicyName 'p1' -BeforeRuleName 'rule-name' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['before_rule_name'] -eq 'rule-name' + } + } + + It 'sends -Versions as a joined versions query param' { + New-PfbNetworkAccessRule -PolicyName 'p1' -Versions @('1', '2') -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['versions'] -eq '1,2' + } + } + + It 'allows a query parameter alongside -Attributes (query is orthogonal to body, constraint 17)' { + New-PfbNetworkAccessRule -PolicyName 'p1' -Attributes @{ client = 'x' } -BeforeRuleId 'rule-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['before_rule_id'] -eq 'rule-1' -and $Body['client'] -eq 'x' + } + } + } + + Context 'parameter set mutual exclusion' { + It 'rejects mixing a typed body parameter with -Attributes' { + { New-PfbNetworkAccessRule -PolicyName 'p1' -Client 'x' -Attributes @{ client = 'x' } -Confirm:$false -Array $fakeArray } | + Should -Throw '*Parameter set cannot be resolved*' + } + } + + Context 'ById selector still works' { + It 'supports -PolicyId with typed params' { + New-PfbNetworkAccessRule -PolicyId 'pid-1' -Client 'x' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_ids'] -eq 'pid-1' -and $Body['client'] -eq 'x' + } + } + } +} diff --git a/Tests/New-PfbNetworkInterfaceTlsPolicy.Tests.ps1 b/Tests/New-PfbNetworkInterfaceTlsPolicy.Tests.ps1 new file mode 100644 index 0000000..4167617 --- /dev/null +++ b/Tests/New-PfbNetworkInterfaceTlsPolicy.Tests.ps1 @@ -0,0 +1,60 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'New-PfbNetworkInterfaceTlsPolicy - query parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'existing name-based selectors still work (non-issue confirmed, keys already correct)' { + It 'sends -MemberName as member_names and -PolicyName as policy_names' { + New-PfbNetworkInterfaceTlsPolicy -MemberName 'data-vip1' -PolicyName 'strict-tls-1.3' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and $Endpoint -eq 'network-interfaces/tls-policies' -and + $QueryParams['member_names'] -eq 'data-vip1' -and + $QueryParams['policy_names'] -eq 'strict-tls-1.3' + } + } + } + + Context '-MemberId / -PolicyId query parameters (missing query params)' { + It 'sends -MemberId as member_ids' { + New-PfbNetworkInterfaceTlsPolicy -MemberId 'iface-1' -PolicyName 'strict-tls-1.3' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['member_ids'] -eq 'iface-1' -and -not $QueryParams.ContainsKey('member_names') + } + } + + It 'sends -PolicyId as policy_ids' { + New-PfbNetworkInterfaceTlsPolicy -MemberName 'data-vip1' -PolicyId 'policy-1' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_ids'] -eq 'policy-1' -and -not $QueryParams.ContainsKey('policy_names') + } + } + + It 'can select both member and policy purely by id, with no name parameters at all' { + New-PfbNetworkInterfaceTlsPolicy -MemberId 'iface-1' -PolicyId 'policy-1' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['member_ids'] -eq 'iface-1' -and $QueryParams['policy_ids'] -eq 'policy-1' -and + -not $QueryParams.ContainsKey('member_names') -and -not $QueryParams.ContainsKey('policy_names') + } + } + } +} diff --git a/Tests/New-PfbNfsExportRule.Tests.ps1 b/Tests/New-PfbNfsExportRule.Tests.ps1 new file mode 100644 index 0000000..a31f466 --- /dev/null +++ b/Tests/New-PfbNfsExportRule.Tests.ps1 @@ -0,0 +1,179 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'New-PfbNfsExportRule - typed body/query params (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'existing -Attributes path still works' { + It 'POSTs with -Attributes hashtable unchanged' { + New-PfbNfsExportRule -PolicyName 'nfs-export-01' -Attributes @{ client = '*'; access = 'root-squash' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and $Endpoint -eq 'nfs-export-policies/rules' -and + $QueryParams['policy_names'] -eq 'nfs-export-01' -and + $Body['client'] -eq '*' -and $Body['access'] -eq 'root-squash' + } + } + } + + Context 'typed body parameters - strings and enums' { + It 'sends -Client as client' { + New-PfbNfsExportRule -PolicyName 'p1' -Client '10.0.0.0/8' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $Body['client'] -eq '10.0.0.0/8' } + } + + It 'sends -Access as access' { + New-PfbNfsExportRule -PolicyName 'p1' -Access 'no-squash' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $Body['access'] -eq 'no-squash' } + } + + It 'rejects an -Access value outside the enum' { + { New-PfbNfsExportRule -PolicyName 'p1' -Access 'bogus' -Confirm:$false -Array $fakeArray } | Should -Throw + } + + It 'sends -Permission as permission' { + New-PfbNfsExportRule -PolicyName 'p1' -Permission 'ro' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $Body['permission'] -eq 'ro' } + } + + It 'rejects a -Permission value outside the enum' { + { New-PfbNfsExportRule -PolicyName 'p1' -Permission 'bogus' -Confirm:$false -Array $fakeArray } | Should -Throw + } + + It 'sends -RequiredTransportSecurity as required_transport_security (3-value enum, not boolean)' { + New-PfbNfsExportRule -PolicyName 'p1' -RequiredTransportSecurity 'mutual-tls' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $Body['required_transport_security'] -eq 'mutual-tls' } + } + + It 'rejects a -RequiredTransportSecurity value outside the 3-value enum' { + { New-PfbNfsExportRule -PolicyName 'p1' -RequiredTransportSecurity 'bogus' -Confirm:$false -Array $fakeArray } | Should -Throw + } + } + + Context 'typed body parameters - integers (constraint 2: explicit 0)' { + It 'sends -Anongid as anongid, including an explicit 0 (root)' { + New-PfbNfsExportRule -PolicyName 'p1' -Anongid 0 -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('anongid') -and $Body['anongid'] -eq 0 + } + } + + It 'sends -Anonuid as anonuid, including an explicit 0 (root)' { + New-PfbNfsExportRule -PolicyName 'p1' -Anonuid 0 -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('anonuid') -and $Body['anonuid'] -eq 0 + } + } + + It 'sends -Index as index, including an explicit 0' { + New-PfbNfsExportRule -PolicyName 'p1' -Index 0 -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('index') -and $Body['index'] -eq 0 + } + } + + It 'accepts -Anonuid values beyond Int32 range (spec type is int64)' { + New-PfbNfsExportRule -PolicyName 'p1' -Anonuid 3000000000 -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $Body['anonuid'] -eq 3000000000 } + } + } + + Context 'typed body parameters - booleans (explicit $false must reach the wire)' { + It 'sends -Atime $false explicitly' { + New-PfbNfsExportRule -PolicyName 'p1' -Atime $false -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('atime') -and $Body['atime'] -eq $false + } + } + + It 'sends -Fileid32bit $false explicitly' { + New-PfbNfsExportRule -PolicyName 'p1' -Fileid32bit $false -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('fileid_32bit') -and $Body['fileid_32bit'] -eq $false + } + } + + It 'sends -Secure $false explicitly' { + New-PfbNfsExportRule -PolicyName 'p1' -Secure $false -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('secure') -and $Body['secure'] -eq $false + } + } + } + + Context 'typed body parameters - array (constraint 2: explicit @())' { + It 'sends -Security as security' { + New-PfbNfsExportRule -PolicyName 'p1' -Security @('sys', 'krb5') -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['security'].Count -eq 2 -and $Body['security'][0] -eq 'sys' -and $Body['security'][1] -eq 'krb5' + } + } + + It 'sends an explicit empty -Security @()' { + New-PfbNfsExportRule -PolicyName 'p1' -Security @() -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('security') -and $Body['security'].Count -eq 0 + } + } + } + + Context 'typed body parameter - scalar reference (constraint 8a)' { + It 'sends -Policy as a { name = ... } reference object' { + New-PfbNfsExportRule -PolicyName 'p1' -Policy 'other-policy' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['policy']['name'] -eq 'other-policy' + } + } + } + + Context 'typed query parameters' { + It 'sends -BeforeRuleId as before_rule_id' { + New-PfbNfsExportRule -PolicyName 'p1' -BeforeRuleId 'rule-1' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $QueryParams['before_rule_id'] -eq 'rule-1' } + } + + It 'sends -BeforeRuleName as before_rule_name' { + New-PfbNfsExportRule -PolicyName 'p1' -BeforeRuleName 'rule-name' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $QueryParams['before_rule_name'] -eq 'rule-name' } + } + + It 'sends -Versions as a joined versions query param' { + New-PfbNfsExportRule -PolicyName 'p1' -Versions @('3', '4') -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $QueryParams['versions'] -eq '3,4' } + } + + It 'allows a query parameter alongside -Attributes (constraint 17)' { + New-PfbNfsExportRule -PolicyName 'p1' -Attributes @{ client = 'x' } -BeforeRuleId 'rule-1' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['before_rule_id'] -eq 'rule-1' -and $Body['client'] -eq 'x' + } + } + } + + Context 'parameter set mutual exclusion' { + It 'rejects mixing a typed body parameter with -Attributes' { + { New-PfbNfsExportRule -PolicyName 'p1' -Client 'x' -Attributes @{ client = 'x' } -Confirm:$false -Array $fakeArray } | + Should -Throw '*Parameter set cannot be resolved*' + } + } + + Context 'ById selector still works' { + It 'supports -PolicyId with typed params' { + New-PfbNfsExportRule -PolicyId 'pid-1' -Client 'x' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_ids'] -eq 'pid-1' -and $Body['client'] -eq 'x' + } + } + } +} diff --git a/Tests/New-PfbNodeGroupNode.Tests.ps1 b/Tests/New-PfbNodeGroupNode.Tests.ps1 new file mode 100644 index 0000000..f36ecb3 --- /dev/null +++ b/Tests/New-PfbNodeGroupNode.Tests.ps1 @@ -0,0 +1,64 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'New-PfbNodeGroupNode - query parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'wire-key correctness fix: POST /node-groups/nodes documents node_group_*/node_*, not group_*/member_*' { + It 'sends -GroupName as node_group_names, NOT group_names' { + New-PfbNodeGroupNode -GroupName 'analytics-group' -MemberName 'CH1.FB1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and $Endpoint -eq 'node-groups/nodes' -and + $QueryParams['node_group_names'] -eq 'analytics-group' -and + -not $QueryParams.ContainsKey('group_names') + } + } + + It 'sends -MemberName as node_names, NOT member_names' { + New-PfbNodeGroupNode -GroupName 'analytics-group' -MemberName 'CH1.FB1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['node_names'] -eq 'CH1.FB1' -and + -not $QueryParams.ContainsKey('member_names') + } + } + } + + Context '-GroupId / -MemberId query parameters (missing query params: node_group_ids, node_ids)' { + It 'sends -GroupId as node_group_ids' { + New-PfbNodeGroupNode -GroupId 'group-1' -MemberName 'CH1.FB1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['node_group_ids'] -eq 'group-1' -and -not $QueryParams.ContainsKey('node_group_names') + } + } + + It 'sends -MemberId as node_ids' { + New-PfbNodeGroupNode -GroupName 'analytics-group' -MemberId 'node-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['node_ids'] -eq 'node-1' -and -not $QueryParams.ContainsKey('node_names') + } + } + + It 'can select both group and node purely by id' { + New-PfbNodeGroupNode -GroupId 'group-1' -MemberId 'node-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['node_group_ids'] -eq 'group-1' -and $QueryParams['node_ids'] -eq 'node-1' + } + } + } +} diff --git a/Tests/New-PfbPolicyFileSystemReplicaLink.Tests.ps1 b/Tests/New-PfbPolicyFileSystemReplicaLink.Tests.ps1 new file mode 100644 index 0000000..3a6f32f --- /dev/null +++ b/Tests/New-PfbPolicyFileSystemReplicaLink.Tests.ps1 @@ -0,0 +1,106 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'New-PfbPolicyFileSystemReplicaLink - correct query wire keys (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'policy selector unchanged' { + It 'sends -PolicyName as policy_names' { + New-PfbPolicyFileSystemReplicaLink -PolicyName 'daily-snap' -LocalFileSystemName 'fs1' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $QueryParams['policy_names'] -eq 'daily-snap' } + } + + It 'sends -PolicyId as policy_ids' { + New-PfbPolicyFileSystemReplicaLink -PolicyId 'p-1' -LocalFileSystemId 'lfs-1' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $QueryParams['policy_ids'] -eq 'p-1' } + } + } + + Context 'regression: member_names does not exist on this endpoint (confirmed via spec), but member_ids does' { + It 'sends -LocalFileSystemName as local_file_system_names, NOT member_names' { + New-PfbPolicyFileSystemReplicaLink -PolicyName 'daily-snap' -LocalFileSystemName 'fs1/remote-fb' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['local_file_system_names'] -eq 'fs1/remote-fb' -and + -not $QueryParams.ContainsKey('member_names') + } + } + + It 'sends -LocalFileSystemId as local_file_system_ids, NOT member_ids' { + New-PfbPolicyFileSystemReplicaLink -PolicyName 'daily-snap' -LocalFileSystemId 'lfs-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['local_file_system_ids'] -eq 'lfs-1' -and + -not $QueryParams.ContainsKey('member_ids') + } + } + + It 'accepts -MemberName as a backward-compatible alias of -LocalFileSystemName (whole-branch review finding I-3: matches the sibling cmdlet New-PfbFileSystemReplicaLinkPolicy)' { + New-PfbPolicyFileSystemReplicaLink -PolicyName 'daily-snap' -MemberName 'fs01' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['local_file_system_names'] -eq 'fs01' -and + -not $QueryParams.ContainsKey('member_names') + } + } + + It 'exposes no separate -MemberName parameter (alias only)' { + $params = (Get-Command New-PfbPolicyFileSystemReplicaLink).Parameters + $params.Keys | Should -Not -Contain 'MemberName' + $params['LocalFileSystemName'].Aliases | Should -Contain 'MemberName' + } + + It 'sends -MemberId as member_ids (real, spec-declared parameter -- restored after review, unlike -MemberName)' { + New-PfbPolicyFileSystemReplicaLink -PolicyName 'daily-snap' -MemberId 'member-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['member_ids'] -eq 'member-1' + } + } + + It 'omits member_ids entirely when -MemberId is not supplied' { + New-PfbPolicyFileSystemReplicaLink -PolicyName 'daily-snap' -LocalFileSystemName 'fs1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('member_ids') + } + } + } + + Context 'new remote query parameters' { + It 'sends -RemoteName as remote_names' { + New-PfbPolicyFileSystemReplicaLink -PolicyName 'daily-snap' -RemoteName 'remote-fb' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $QueryParams['remote_names'] -eq 'remote-fb' } + } + + It 'sends -RemoteId as remote_ids' { + New-PfbPolicyFileSystemReplicaLink -PolicyName 'daily-snap' -RemoteId 'remote-1' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $QueryParams['remote_ids'] -eq 'remote-1' } + } + } + + Context 'no request body' { + It 'sends no Body parameter to Invoke-PfbApiRequest' { + New-PfbPolicyFileSystemReplicaLink -PolicyName 'daily-snap' -LocalFileSystemName 'fs1' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $null -eq $Body } + } + } + + Context 'honors -WhatIf' { + It 'makes no call under -WhatIf' { + New-PfbPolicyFileSystemReplicaLink -PolicyName 'daily-snap' -LocalFileSystemName 'fs1' -WhatIf -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 -Exactly + } + } +} diff --git a/Tests/New-PfbQosPolicyMember.Tests.ps1 b/Tests/New-PfbQosPolicyMember.Tests.ps1 new file mode 100644 index 0000000..81c5c2e --- /dev/null +++ b/Tests/New-PfbQosPolicyMember.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'New-PfbQosPolicyMember - typed query params (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'existing member_names/member_ids wire keys are unchanged (confirmed non-issue)' { + It 'still sends -MemberName as member_names' { + New-PfbQosPolicyMember -PolicyName 'qos-gold' -MemberName 'fs1' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $QueryParams['member_names'] -eq 'fs1' } + } + + It 'still sends -MemberId as member_ids' { + New-PfbQosPolicyMember -PolicyName 'qos-gold' -MemberId 'm-1' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $QueryParams['member_ids'] -eq 'm-1' } + } + } + + Context 'new -MemberType query parameter' { + It 'sends -MemberType as a joined member_types query param' { + New-PfbQosPolicyMember -PolicyName 'qos-gold' -MemberName 'fs1' -MemberType @('file-systems', 'realms') -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $QueryParams['member_types'] -eq 'file-systems,realms' } + } + + It 'sends an explicit empty -MemberType @() (constraint 2, array field)' { + New-PfbQosPolicyMember -PolicyName 'qos-gold' -MemberName 'fs1' -MemberType @() -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $QueryParams.ContainsKey('member_types') -and $QueryParams['member_types'] -eq '' } + } + + It 'omits member_types entirely when not supplied' { + New-PfbQosPolicyMember -PolicyName 'qos-gold' -MemberName 'fs1' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { -not $QueryParams.ContainsKey('member_types') } + } + } +} diff --git a/Tests/New-PfbS3ExportRule.Tests.ps1 b/Tests/New-PfbS3ExportRule.Tests.ps1 new file mode 100644 index 0000000..e892605 --- /dev/null +++ b/Tests/New-PfbS3ExportRule.Tests.ps1 @@ -0,0 +1,94 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'New-PfbS3ExportRule - typed body/query params (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'existing -Attributes path still works' { + It 'POSTs with -Attributes hashtable unchanged' { + New-PfbS3ExportRule -PolicyName 's3-export-01' -Name 'rule-1' -Attributes @{ effect = 'allow' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and $Endpoint -eq 's3-export-policies/rules' -and + $QueryParams['policy_names'] -eq 's3-export-01' -and + $Body['effect'] -eq 'allow' + } + } + } + + Context 'required -Name query parameter (the endpoint requires the new rule name)' { + It 'sends -Name as a joined names query param' { + New-PfbS3ExportRule -PolicyName 'p1' -Name 'rule-1' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $QueryParams['names'] -eq 'rule-1' } + } + + It 'throws before any API call when -Name is not supplied' { + { New-PfbS3ExportRule -PolicyName 'p1' -Effect 'allow' -Confirm:$false -Array $fakeArray } | Should -Throw + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 -Exactly + } + } + + Context 'typed body parameters - arrays (constraint 2: explicit @())' { + It 'sends -Actions as actions' { + New-PfbS3ExportRule -PolicyName 'p1' -Name 'rule-1' -Actions @('s3:GetObject', 's3:PutObject') -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['actions'].Count -eq 2 -and $Body['actions'][0] -eq 's3:GetObject' + } + } + + It 'sends an explicit empty -Actions @()' { + New-PfbS3ExportRule -PolicyName 'p1' -Name 'rule-1' -Actions @() -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('actions') -and $Body['actions'].Count -eq 0 + } + } + + It 'sends -Resources as resources' { + New-PfbS3ExportRule -PolicyName 'p1' -Name 'rule-1' -Resources @('bucket1/*') -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['resources'].Count -eq 1 -and $Body['resources'][0] -eq 'bucket1/*' + } + } + + It 'sends an explicit empty -Resources @()' { + New-PfbS3ExportRule -PolicyName 'p1' -Name 'rule-1' -Resources @() -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('resources') -and $Body['resources'].Count -eq 0 + } + } + } + + Context 'typed body parameter - effect string' { + It 'sends -Effect as effect' { + New-PfbS3ExportRule -PolicyName 'p1' -Name 'rule-1' -Effect 'allow' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $Body['effect'] -eq 'allow' } + } + } + + Context 'parameter set mutual exclusion' { + It 'rejects mixing a typed body parameter with -Attributes' { + { New-PfbS3ExportRule -PolicyName 'p1' -Name 'rule-1' -Effect 'allow' -Attributes @{ effect = 'allow' } -Confirm:$false -Array $fakeArray } | + Should -Throw '*Parameter set cannot be resolved*' + } + } + + Context 'ById selector still works' { + It 'supports -PolicyId with typed params' { + New-PfbS3ExportRule -PolicyId 'pid-1' -Name 'rule-1' -Effect 'allow' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_ids'] -eq 'pid-1' -and $Body['effect'] -eq 'allow' + } + } + } +} diff --git a/Tests/New-PfbSmbClientRule.Tests.ps1 b/Tests/New-PfbSmbClientRule.Tests.ps1 new file mode 100644 index 0000000..153ad1b --- /dev/null +++ b/Tests/New-PfbSmbClientRule.Tests.ps1 @@ -0,0 +1,100 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'New-PfbSmbClientRule - typed body/query params (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'existing -Attributes path still works' { + It 'POSTs with -Attributes hashtable unchanged' { + New-PfbSmbClientRule -PolicyName 'smb-client-01' -Attributes @{ client = '*' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and $Endpoint -eq 'smb-client-policies/rules' -and + $QueryParams['policy_names'] -eq 'smb-client-01' -and $Body['client'] -eq '*' + } + } + } + + Context 'typed body parameters' { + It 'sends -Client as client' { + New-PfbSmbClientRule -PolicyName 'p1' -Client '10.0.0.0/8' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $Body['client'] -eq '10.0.0.0/8' } + } + + It 'sends -Encryption as encryption' { + New-PfbSmbClientRule -PolicyName 'p1' -Encryption 'required' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $Body['encryption'] -eq 'required' } + } + + It 'rejects an -Encryption value outside the enum' { + { New-PfbSmbClientRule -PolicyName 'p1' -Encryption 'bogus' -Confirm:$false -Array $fakeArray } | Should -Throw + } + + It 'sends -Index as index, including an explicit 0 (constraint 2, integer field)' { + New-PfbSmbClientRule -PolicyName 'p1' -Index 0 -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('index') -and $Body['index'] -eq 0 + } + } + + It 'sends -Permission as permission' { + New-PfbSmbClientRule -PolicyName 'p1' -Permission 'ro' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $Body['permission'] -eq 'ro' } + } + + It 'rejects a -Permission value outside the enum' { + { New-PfbSmbClientRule -PolicyName 'p1' -Permission 'bogus' -Confirm:$false -Array $fakeArray } | Should -Throw + } + } + + Context 'typed query parameters' { + It 'sends -BeforeRuleId as before_rule_id' { + New-PfbSmbClientRule -PolicyName 'p1' -BeforeRuleId 'rule-1' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $QueryParams['before_rule_id'] -eq 'rule-1' } + } + + It 'sends -BeforeRuleName as before_rule_name' { + New-PfbSmbClientRule -PolicyName 'p1' -BeforeRuleName 'rule-name' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $QueryParams['before_rule_name'] -eq 'rule-name' } + } + + It 'sends -Versions as a joined versions query param' { + New-PfbSmbClientRule -PolicyName 'p1' -Versions @('5', '6') -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $QueryParams['versions'] -eq '5,6' } + } + + It 'allows a query parameter alongside -Attributes (constraint 17)' { + New-PfbSmbClientRule -PolicyName 'p1' -Attributes @{ client = 'x' } -BeforeRuleId 'rule-1' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['before_rule_id'] -eq 'rule-1' -and $Body['client'] -eq 'x' + } + } + } + + Context 'parameter set mutual exclusion' { + It 'rejects mixing a typed body parameter with -Attributes' { + { New-PfbSmbClientRule -PolicyName 'p1' -Client 'x' -Attributes @{ client = 'x' } -Confirm:$false -Array $fakeArray } | + Should -Throw '*Parameter set cannot be resolved*' + } + } + + Context 'ById selector still works' { + It 'supports -PolicyId with typed params' { + New-PfbSmbClientRule -PolicyId 'pid-1' -Client 'x' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_ids'] -eq 'pid-1' -and $Body['client'] -eq 'x' + } + } + } +} diff --git a/Tests/New-PfbSmbShareRule.Tests.ps1 b/Tests/New-PfbSmbShareRule.Tests.ps1 new file mode 100644 index 0000000..083cc84 --- /dev/null +++ b/Tests/New-PfbSmbShareRule.Tests.ps1 @@ -0,0 +1,86 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'New-PfbSmbShareRule - typed body params (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'existing -Attributes path still works' { + It 'POSTs with -Attributes hashtable unchanged' { + New-PfbSmbShareRule -PolicyName 'smb-share-01' -Attributes @{ principal = 'Everyone'; change = 'allow' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and $Endpoint -eq 'smb-share-policies/rules' -and + $QueryParams['policy_names'] -eq 'smb-share-01' -and + $Body['principal'] -eq 'Everyone' -and $Body['change'] -eq 'allow' + } + } + } + + Context 'typed body parameters' { + It 'sends -Principal as principal' { + New-PfbSmbShareRule -PolicyName 'p1' -Principal 'DOMAIN\Admins' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $Body['principal'] -eq 'DOMAIN\Admins' } + } + + It 'sends -Change as change' { + New-PfbSmbShareRule -PolicyName 'p1' -Change 'allow' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $Body['change'] -eq 'allow' } + } + + It 'rejects a -Change value outside the enum' { + { New-PfbSmbShareRule -PolicyName 'p1' -Change 'bogus' -Confirm:$false -Array $fakeArray } | Should -Throw + } + + It 'sends -FullControl as full_control' { + New-PfbSmbShareRule -PolicyName 'p1' -FullControl 'deny' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $Body['full_control'] -eq 'deny' } + } + + It 'rejects a -FullControl value outside the enum' { + { New-PfbSmbShareRule -PolicyName 'p1' -FullControl 'bogus' -Confirm:$false -Array $fakeArray } | Should -Throw + } + + It 'sends -Read as read' { + New-PfbSmbShareRule -PolicyName 'p1' -Read 'allow' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { $Body['read'] -eq 'allow' } + } + + It 'rejects a -Read value outside the enum' { + { New-PfbSmbShareRule -PolicyName 'p1' -Read 'bogus' -Confirm:$false -Array $fakeArray } | Should -Throw + } + + It 'omits fields that were not supplied' { + New-PfbSmbShareRule -PolicyName 'p1' -Principal 'x' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $Body.ContainsKey('change') -and -not $Body.ContainsKey('full_control') -and -not $Body.ContainsKey('read') + } + } + } + + Context 'parameter set mutual exclusion' { + It 'rejects mixing a typed body parameter with -Attributes' { + { New-PfbSmbShareRule -PolicyName 'p1' -Principal 'x' -Attributes @{ principal = 'x' } -Confirm:$false -Array $fakeArray } | + Should -Throw '*Parameter set cannot be resolved*' + } + } + + Context 'ById selector still works' { + It 'supports -PolicyId with typed params' { + New-PfbSmbShareRule -PolicyId 'pid-1' -Principal 'x' -Confirm:$false -Array $fakeArray + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_ids'] -eq 'pid-1' -and $Body['principal'] -eq 'x' + } + } + } +} diff --git a/Tests/New-PfbUserGroupQuotaPolicy.Tests.ps1 b/Tests/New-PfbUserGroupQuotaPolicy.Tests.ps1 new file mode 100644 index 0000000..e79152d --- /dev/null +++ b/Tests/New-PfbUserGroupQuotaPolicy.Tests.ps1 @@ -0,0 +1,83 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'New-PfbUserGroupQuotaPolicy' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'POSTs user-group-quota-policies with the name query param and an empty body by default' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + New-PfbUserGroupQuotaPolicy -Name 'pol-1' -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and + $Endpoint -eq 'user-group-quota-policies' -and + $QueryParams['names'] -eq 'pol-1' -and + $Body.Keys.Count -eq 0 + } + } + + It 'builds enabled/location/rules into the body from typed params' { + $rules = @(@{ quota_type = 'user-default'; quota_limit = 1073741824 }) + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray; rules = $rules } { + param($arr, $rules) + New-PfbUserGroupQuotaPolicy -Name 'pol-1' -Enabled:$false -Rules $rules -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['enabled'] -eq $false -and + $Body['rules'][0]['quota_type'] -eq 'user-default' + } + } + + It 'sends file_system_names for the legacy-quota-import path, mutually exclusive with file_system_ids' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + New-PfbUserGroupQuotaPolicy -Name 'pol-1' -FileSystemName 'fs1' -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['file_system_names'] -eq 'fs1' -and -not $QueryParams.ContainsKey('file_system_ids') + } + } + + It 'throws when both -FileSystemName and -FileSystemId are supplied' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + New-PfbUserGroupQuotaPolicy -Name 'pol-1' -FileSystemName 'fs1' -FileSystemId 'id1' -Confirm:$false -Array $arr + } } | + Should -Throw + } + + It '-Attributes overrides the constructed body verbatim' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + New-PfbUserGroupQuotaPolicy -Name 'pol-1' -Attributes @{ enabled = $true } -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['enabled'] -eq $true -and $Body.Keys.Count -eq 1 + } + } + + It 'does not call the API under -WhatIf' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + New-PfbUserGroupQuotaPolicy -Name 'pol-1' -WhatIf -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 + } +} diff --git a/Tests/New-PfbUserGroupQuotaPolicyFileSystem.Tests.ps1 b/Tests/New-PfbUserGroupQuotaPolicyFileSystem.Tests.ps1 new file mode 100644 index 0000000..af3ea33 --- /dev/null +++ b/Tests/New-PfbUserGroupQuotaPolicyFileSystem.Tests.ps1 @@ -0,0 +1,65 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'New-PfbUserGroupQuotaPolicyFileSystem' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'POSTs user-group-quota-policies/file-systems with policy and member query params' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + New-PfbUserGroupQuotaPolicyFileSystem -PolicyName 'pol-1' -MemberName 'fs1' -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and + $Endpoint -eq 'user-group-quota-policies/file-systems' -and + $QueryParams['policy_names'] -eq 'pol-1' -and + $QueryParams['member_names'] -eq 'fs1' + } + } + + It 'sends delete_existing_user_group_quota_settings and ignore_usage when requested' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + New-PfbUserGroupQuotaPolicyFileSystem -PolicyName 'pol-1' -MemberName 'fs1' -DeleteExistingUserGroupQuotaSettings -IgnoreUsage -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['delete_existing_user_group_quota_settings'] -eq 'true' -and $QueryParams['ignore_usage'] -eq 'true' + } + } + + It 'throws when neither -PolicyName nor -PolicyId is supplied' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + New-PfbUserGroupQuotaPolicyFileSystem -MemberName 'fs1' -Confirm:$false -Array $arr + } } | Should -Throw + } + + It 'throws when neither -MemberName nor -MemberId is supplied' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + New-PfbUserGroupQuotaPolicyFileSystem -PolicyName 'pol-1' -Confirm:$false -Array $arr + } } | Should -Throw + } + + It 'does not call the API under -WhatIf' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + New-PfbUserGroupQuotaPolicyFileSystem -PolicyName 'pol-1' -MemberName 'fs1' -WhatIf -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 + } +} diff --git a/Tests/New-PfbUserGroupQuotaPolicyRule.Tests.ps1 b/Tests/New-PfbUserGroupQuotaPolicyRule.Tests.ps1 new file mode 100644 index 0000000..f7468fd --- /dev/null +++ b/Tests/New-PfbUserGroupQuotaPolicyRule.Tests.ps1 @@ -0,0 +1,107 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'New-PfbUserGroupQuotaPolicyRule' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'POSTs user-group-quota-policies/rules scoped to -PolicyName with a subject/quota body' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ fakeArray = $fakeArray } { + param($fakeArray) + New-PfbUserGroupQuotaPolicyRule -PolicyName 'pol-1' -Subject @{ name = 'jdoe' } -QuotaType 'user' -QuotaLimit 1073741824 -Confirm:$false -Array $fakeArray + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'POST' -and + $Endpoint -eq 'user-group-quota-policies/rules' -and + $QueryParams['policy_names'] -eq 'pol-1' -and + $Body['subject']['name'] -eq 'jdoe' -and + $Body['quota_type'] -eq 'user' -and + $Body['quota_limit'] -eq 1073741824 + } + } + + It 'scopes by -PolicyId instead of -PolicyName' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ fakeArray = $fakeArray } { + param($fakeArray) + New-PfbUserGroupQuotaPolicyRule -PolicyId 'pid-1' -QuotaType 'user-default' -QuotaLimit 100 -Confirm:$false -Array $fakeArray + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_ids'] -eq 'pid-1' -and -not $QueryParams.ContainsKey('policy_names') + } + } + + It 'sends -Enforced and -Notifications when supplied' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ fakeArray = $fakeArray } { + param($fakeArray) + New-PfbUserGroupQuotaPolicyRule -PolicyName 'pol-1' -QuotaType 'user-default' -QuotaLimit 100 -Enforced:$true -Notifications 'None' -Confirm:$false -Array $fakeArray + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['enforced'] -eq $true -and $Body['notifications'] -eq 'None' + } + } + + It 'sends ignore_usage when -IgnoreUsage is set' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ fakeArray = $fakeArray } { + param($fakeArray) + New-PfbUserGroupQuotaPolicyRule -PolicyName 'pol-1' -QuotaType 'user-default' -QuotaLimit 100 -IgnoreUsage -Confirm:$false -Array $fakeArray + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ignore_usage'] -eq 'true' + } + } + + It 'rejects an invalid -QuotaType' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ fakeArray = $fakeArray } { + param($fakeArray) + New-PfbUserGroupQuotaPolicyRule -PolicyName 'pol-1' -QuotaType 'bogus' -QuotaLimit 100 -Confirm:$false -Array $fakeArray + } } | Should -Throw + } + + It '-Attributes overrides the constructed body verbatim' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ fakeArray = $fakeArray } { + param($fakeArray) + New-PfbUserGroupQuotaPolicyRule -PolicyName 'pol-1' -Attributes @{ quota_limit = 42 } -Confirm:$false -Array $fakeArray + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Keys.Count -eq 1 -and $Body['quota_limit'] -eq 42 + } + } + + It 'does not call the API under -WhatIf' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ fakeArray = $fakeArray } { + param($fakeArray) + New-PfbUserGroupQuotaPolicyRule -PolicyName 'pol-1' -QuotaType 'user-default' -QuotaLimit 100 -WhatIf -Array $fakeArray + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 + } + + It 'rejects a -QuotaLimit of 0' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ fakeArray = $fakeArray } { + param($fakeArray) + New-PfbUserGroupQuotaPolicyRule -PolicyName 'pol-1' -QuotaType 'user-default' -QuotaLimit 0 -Confirm:$false -Array $fakeArray + } } | Should -Throw + } + + It 'rejects -QuotaType user-default combined with -Subject' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ fakeArray = $fakeArray } { + param($fakeArray) + New-PfbUserGroupQuotaPolicyRule -PolicyName 'pol-1' -Subject @{ name = 'jdoe' } -QuotaType 'user-default' -QuotaLimit 100 -Confirm:$false -Array $fakeArray + } } | Should -Throw + } +} diff --git a/Tests/PfbApiDriftTools.Tests.ps1 b/Tests/PfbApiDriftTools.Tests.ps1 new file mode 100644 index 0000000..a599263 --- /dev/null +++ b/Tests/PfbApiDriftTools.Tests.ps1 @@ -0,0 +1,1627 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } +<# +.SYNOPSIS + Unit tests for tools/lib/PfbApiDriftTools.ps1 -- category 1 (uncovered endpoints) and + category 2 (parameter gaps) of the API drift report. +#> + +BeforeAll { + $repoRoot = Split-Path -Parent $PSScriptRoot + . (Join-Path $repoRoot 'tools/lib/PfbApiDriftTools.ps1') + + $script:publicFixtureDir = Join-Path $TestDrive 'Public/Fixture' + $script:privateFixtureDir = Join-Path $TestDrive 'Private' + New-Item -ItemType Directory -Path $publicFixtureDir -Force | Out-Null + New-Item -ItemType Directory -Path $privateFixtureDir -Force | Out-Null + + Set-Content -Path (Join-Path $publicFixtureDir 'Get-PfbFixtureWidget.ps1') -Value @' +function Get-PfbFixtureWidget { + [CmdletBinding()] + param([Parameter()] [PSCustomObject]$Array, [Parameter()] [string]$Name) + $queryParams = @{} + if ($Name) { $queryParams['name'] = $Name } + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'widgets' -QueryParams $queryParams -AutoPaginate +} +'@ + + Set-Content -Path (Join-Path $publicFixtureDir 'Get-PfbFixtureDynamic.ps1') -Value @' +function Get-PfbFixtureDynamic { + [CmdletBinding()] + param([Parameter()] [PSCustomObject]$Array, [Parameter()] [string]$Kind) + $endpoint = "widgets/$Kind" + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint $endpoint -AutoPaginate +} +'@ + + # A Private/ helper that also happens to use the standard Invoke-PfbApiRequest + # convention -- Get-PfbModuleCalledEndpoints must scan Private/ too, not just Public/. + Set-Content -Path (Join-Path $privateFixtureDir 'Invoke-PfbFixtureInternalHelper.ps1') -Value @' +function Invoke-PfbFixtureInternalHelper { + [CmdletBinding()] + param([Parameter()] [PSCustomObject]$Array) + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'internal-only' -AutoPaginate +} +'@ + + $script:calledEndpoints = Get-PfbModuleCalledEndpoints -PublicDirectory $publicFixtureDir -PrivateDirectory $privateFixtureDir + + $script:capabilityMap = [PSCustomObject]@{ + endpoints = [PSCustomObject]@{ + 'GET /widgets' = [PSCustomObject]@{ minVersion = '2.0' } + 'GET /internal-only' = [PSCustomObject]@{ minVersion = '2.0' } + 'GET /gadgets' = [PSCustomObject]@{ minVersion = '2.20' } + 'POST /api/login' = [PSCustomObject]@{ minVersion = '2.26' } + } + } + + $script:capabilityMap.endpoints | Add-Member -NotePropertyName 'GET /arrays/space' -NotePropertyValue ([PSCustomObject]@{ + minVersion = '2.0' + parameters = [PSCustomObject]@{ type = '2.0'; new_field = '2.27'; 'X-Request-ID' = '2.12' } + bodyProperties = [PSCustomObject]@{} + }) + + $script:fullyMappedInventory = @( + [PSCustomObject]@{ Cmdlet = 'Get-PfbFixtureArraySpace'; Parameter = 'Type'; Surface = 'Typed'; WireName = 'type'; HasValidateSet = $false; ValidateSetValues = $null; Endpoint = 'arrays/space'; Method = 'GET'; File = 'Public/Fixture/Get-PfbFixtureArraySpace.ps1'; Line = 5 } + ) + # A non-empty inventory containing only an UNRELATED cmdlet -- for tests that need to + # exercise "this endpoint's cmdlet has no inventory rows at all" without passing a + # literally empty array. Group-Object -AsHashTable returns $null for a whole-hashtable + # MISS on an empty input array (not merely for one absent key on an otherwise-populated + # one), and indexing into that null hashtable is itself a non-terminating error -- + # noisy in test output even though functionally harmless. A single unrelated row keeps + # the hashtable itself real, so a miss on the cmdlet actually under test stays a clean, + # silent $null (the "vacuous mapping" case Get-PfbParameterCoverageGaps is built to + # tolerate), not a null-hashtable index error. + $script:noopInventory = @( + [PSCustomObject]@{ Cmdlet = 'Get-PfbFixtureUnrelated'; Parameter = 'Unused'; Surface = 'Typed'; WireName = 'unused'; HasValidateSet = $false; ValidateSetValues = $null; Endpoint = $null; Method = $null; File = 'x'; Line = 1 } + ) + $script:notFullyMappedInventory = @( + [PSCustomObject]@{ Cmdlet = 'Get-PfbFixtureWidget'; Parameter = 'Name'; Surface = 'Typed'; WireName = 'name'; HasValidateSet = $false; ValidateSetValues = $null; Endpoint = 'widgets'; Method = 'GET'; File = 'Public/Fixture/Get-PfbFixtureWidget.ps1'; Line = 3 } + [PSCustomObject]@{ Cmdlet = 'Get-PfbFixtureWidget'; Parameter = 'Attributes'; Surface = 'AttributesOnly'; WireName = $null; HasValidateSet = $false; ValidateSetValues = $null; Endpoint = $null; Method = $null; File = 'Public/Fixture/Get-PfbFixtureWidget.ps1'; Line = 4 } + ) + + $script:driftHistory = [ordered]@{ + 'ArrayPerformance.protocol' = [ordered]@{ + Name = 'protocol'; Kind = 'schema'; MinVersion = '2.0' + CurrentValues = @('all', 'nfs', 'smb', 'http', 's3') + DistinctValueSets = [System.Collections.Generic.HashSet[string]]::new([string[]]@('all,http,nfs,s3,smb')) + } + } + $script:driftInventory = @( + [PSCustomObject]@{ Cmdlet = 'Get-PfbArrayPerformance'; Parameter = 'Protocol'; Surface = 'Typed'; WireName = 'protocol'; HasValidateSet = $true; ValidateSetValues = @('nfs', 'smb', 'http', 's3'); Endpoint = 'arrays/performance'; Method = 'GET' } + ) + + # Hoisted to file scope (not a per-Describe BeforeAll) because it is consumed by TWO + # separate Describe blocks below (the array-item-type/synopsis tests and the + # Get-PfbBodyPropertyEnrichment tests) -- a Describe-local BeforeAll only runs before + # its OWN Describe's tests, so the other Describe block would see a $null + # $enrichmentSpec whenever it runs without the first Describe having already executed + # in the same session (e.g. `-Filter.FullName` isolating just one Describe). + $script:enrichmentSpec = [PSCustomObject]@{ + components = [PSCustomObject]@{ + schemas = [PSCustomObject]@{ + Widget = [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ + color = [PSCustomObject]@{ type = 'string'; description = 'The widget color. Valid values are `red`, `blue`, and `green`.' } + count = [PSCustomObject]@{ type = 'integer'; format = 'int64'; description = "Count of widgets available`nin this pool. Additional prose about counting follows here." } + tags = [PSCustomObject]@{ type = 'array'; items = [PSCustomObject]@{ type = 'string' }; description = 'Tag list. Additional prose about tags follows this first sentence.' } + ref_tags = [PSCustomObject]@{ type = 'array'; items = [PSCustomObject]@{ '$ref' = '#/components/schemas/_tagRef' } } + linked = [PSCustomObject]@{ '$ref' = '#/components/schemas/_linkedRef' } + bare = [PSCustomObject]@{ type = 'string' } + } + } + _linkedRef = [PSCustomObject]@{ type = 'string'; description = 'Should never be read directly off Widget.linked (PIN: this function reads only the OWNER schema''s own declared property node, never following the property''s own $ref).' } + } + } + } +} + +Describe 'Get-PfbModuleCalledEndpoints' { + It 'resolves a literal -Method/-Endpoint pair to the capability-map key format' { + $rec = $calledEndpoints | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureWidget' } + $rec.Key | Should -Be 'GET /widgets' + $rec.Resolved | Should -BeTrue + } + + It 'scans Private/*.ps1 as well as Public/*.ps1' { + $rec = $calledEndpoints | Where-Object { $_.Cmdlet -eq 'Invoke-PfbFixtureInternalHelper' } + $rec.Key | Should -Be 'GET /internal-only' + } + + It 'marks a dynamically-built -Endpoint as unresolved, never silently dropped or guessed' { + $rec = $calledEndpoints | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureDynamic' } + $rec.Resolved | Should -BeFalse + $rec.Key | Should -BeNullOrEmpty + } +} + +Describe 'Get-PfbEndpointCoverageGaps' { + It 'flags a capability-map endpoint no cmdlet calls at all' { + $gaps = Get-PfbEndpointCoverageGaps -CapabilityMap $capabilityMap -CalledEndpoints $calledEndpoints + ($gaps | Where-Object { $_.Endpoint -eq 'GET /gadgets' }) | Should -Not -BeNullOrEmpty + } + + It 'does not flag an endpoint a fixture cmdlet already calls' { + $gaps = Get-PfbEndpointCoverageGaps -CapabilityMap $capabilityMap -CalledEndpoints $calledEndpoints + ($gaps | Where-Object { $_.Endpoint -eq 'GET /widgets' }) | Should -BeNullOrEmpty + } + + It 'excludes a bespoke-allowlisted endpoint even though no cmdlet calls it directly' { + $gaps = Get-PfbEndpointCoverageGaps -CapabilityMap $capabilityMap -CalledEndpoints $calledEndpoints -BespokeAllowlist @('POST /api/login') + ($gaps | Where-Object { $_.Endpoint -eq 'POST /api/login' }) | Should -BeNullOrEmpty + } + + It 'with -SinceVersion, excludes a gap endpoint introduced at or before that version' { + $gaps = Get-PfbEndpointCoverageGaps -CapabilityMap $capabilityMap -CalledEndpoints $calledEndpoints -SinceVersion '2.20' + ($gaps | Where-Object { $_.Endpoint -eq 'GET /gadgets' }) | Should -BeNullOrEmpty + } + + It 'with -SinceVersion, keeps a gap endpoint introduced after that version' { + $gaps = Get-PfbEndpointCoverageGaps -CapabilityMap $capabilityMap -CalledEndpoints $calledEndpoints -SinceVersion '2.20' + ($gaps | Where-Object { $_.Endpoint -eq 'POST /api/login' }) | Should -Not -BeNullOrEmpty + } +} + +Describe 'Bespoke auth-endpoint allowlist (real, confirmed by reading Private/ + Connect-PfbArray.ps1)' { + It 'contains exactly the four confirmed bespoke endpoints, no more, no fewer' { + $script:PfbBespokeAuthEndpoints | Sort-Object | Should -Be @( + 'GET /api/api_version', + 'POST /api/login', + 'POST /api/logout', + 'POST /oauth2/1.0/token' + ) | Sort-Object + } +} + +Describe 'Non-actionable parameter allowlist (X-Request-ID/offset: no Private/ injection site exists for either -- confirmed hand-written; continuation_token is DERIVED now, see Get-PfbNonActionableParameters)' { + It 'contains exactly the two hand-written fields, no more, no fewer -- continuation_token is no longer hardcoded here' { + $script:PfbNonActionableParameters | Sort-Object | Should -Be @( + 'offset', + 'X-Request-ID' + ) | Sort-Object + } +} + +Describe 'Get-PfbParameterCoverageGaps' { + It 'flags a missing query parameter on a fully-mapped cmdlet''s endpoint, with high confidence' { + $endpoints = @([PSCustomObject]@{ Key = 'GET /arrays/space'; Method = 'GET'; Endpoint = '/arrays/space'; Resolved = $true; Cmdlet = 'Get-PfbFixtureArraySpace'; File = 'x' }) + $result = Get-PfbParameterCoverageGaps -CapabilityMap $capabilityMap -CmdletInventory $fullyMappedInventory -CalledEndpoints $endpoints + $gap = $result | Where-Object { $_.Endpoint -eq 'GET /arrays/space' } + $gap.MissingQueryParameters | Should -Contain 'new_field' + $gap.MissingBodyProperties | Should -BeNullOrEmpty + $gap.ReadOnlyFields | Should -BeNullOrEmpty + $gap.Confidence.Level | Should -Be 'high' + $gap.Confidence.UnresolvedParameters | Should -BeNullOrEmpty + $gap.Confidence.EscapeHatchOnly | Should -BeNullOrEmpty + $gap.Confidence.Caveat | Should -BeNullOrEmpty + } + + It 'never gates an endpoint out entirely for an AttributesOnly parameter -- still reports its resolved gaps, with partial confidence and an escape-hatch annotation' { + $endpoints = @([PSCustomObject]@{ Key = 'GET /widgets'; Method = 'GET'; Endpoint = '/widgets'; Resolved = $true; Cmdlet = 'Get-PfbFixtureWidget'; File = 'x' }) + $capMapWithWidgets = [PSCustomObject]@{ endpoints = [PSCustomObject]@{ 'GET /widgets' = [PSCustomObject]@{ minVersion = '2.0'; parameters = [PSCustomObject]@{ name = '2.0'; kind = '2.0' }; bodyProperties = [PSCustomObject]@{} } } } + $result = Get-PfbParameterCoverageGaps -CapabilityMap $capMapWithWidgets -CmdletInventory $notFullyMappedInventory -CalledEndpoints $endpoints + $gap = $result | Where-Object { $_.Endpoint -eq 'GET /widgets' } + $gap | Should -Not -BeNullOrEmpty + $gap.MissingQueryParameters | Should -Be @('kind') + $gap.Confidence.Level | Should -Be 'partial' + $gap.Confidence.UnresolvedParameters.Parameter | Should -Contain 'Attributes' + ($gap.Confidence.UnresolvedParameters | Where-Object Parameter -eq 'Attributes').Surface | Should -Be 'AttributesOnly' + ($gap.Confidence.UnresolvedParameters | Where-Object Parameter -eq 'Attributes').Line | Should -Be 4 + $gap.Confidence.EscapeHatchOnly | Should -Be @('Attributes') + $gap.Confidence.Caveat | Should -Not -BeNullOrEmpty + } + + It 'with -SinceVersion, keeps a missing query parameter introduced after that version' { + $endpoints = @([PSCustomObject]@{ Key = 'GET /arrays/space'; Method = 'GET'; Endpoint = '/arrays/space'; Resolved = $true; Cmdlet = 'Get-PfbFixtureArraySpace'; File = 'x' }) + $result = Get-PfbParameterCoverageGaps -CapabilityMap $capabilityMap -CmdletInventory $fullyMappedInventory -CalledEndpoints $endpoints -SinceVersion '2.0' + $gap = $result | Where-Object { $_.Endpoint -eq 'GET /arrays/space' } + $gap.MissingQueryParameters | Should -Contain 'new_field' + } + + It 'with -SinceVersion, drops a gap whose only missing field was introduced at or before that version' { + $endpoints = @([PSCustomObject]@{ Key = 'GET /arrays/space'; Method = 'GET'; Endpoint = '/arrays/space'; Resolved = $true; Cmdlet = 'Get-PfbFixtureArraySpace'; File = 'x' }) + $result = Get-PfbParameterCoverageGaps -CapabilityMap $capabilityMap -CmdletInventory $fullyMappedInventory -CalledEndpoints $endpoints -SinceVersion '2.27' + $result | Where-Object { $_.Endpoint -eq 'GET /arrays/space' } | Should -BeNullOrEmpty + } + + It 'flags X-Request-ID as a missing query parameter when -ExcludedFields is not given' { + $endpoints = @([PSCustomObject]@{ Key = 'GET /arrays/space'; Method = 'GET'; Endpoint = '/arrays/space'; Resolved = $true; Cmdlet = 'Get-PfbFixtureArraySpace'; File = 'x' }) + $result = Get-PfbParameterCoverageGaps -CapabilityMap $capabilityMap -CmdletInventory $fullyMappedInventory -CalledEndpoints $endpoints + $gap = $result | Where-Object { $_.Endpoint -eq 'GET /arrays/space' } + $gap.MissingQueryParameters | Should -Contain 'X-Request-ID' + } + + It 'with -ExcludedFields, excludes a named field but keeps other real gaps on the same endpoint' { + $endpoints = @([PSCustomObject]@{ Key = 'GET /arrays/space'; Method = 'GET'; Endpoint = '/arrays/space'; Resolved = $true; Cmdlet = 'Get-PfbFixtureArraySpace'; File = 'x' }) + $result = Get-PfbParameterCoverageGaps -CapabilityMap $capabilityMap -CmdletInventory $fullyMappedInventory -CalledEndpoints $endpoints -ExcludedFields @('X-Request-ID') + $gap = $result | Where-Object { $_.Endpoint -eq 'GET /arrays/space' } + $gap.MissingQueryParameters | Should -Not -Contain 'X-Request-ID' + $gap.MissingQueryParameters | Should -Contain 'new_field' + } + + It 'with -ExcludedFields, excludes a named BODY field too -- the filter is not query-only' { + $endpoints = @([PSCustomObject]@{ Key = 'PATCH /excl'; Method = 'PATCH'; Endpoint = '/excl'; Resolved = $true; Cmdlet = 'Update-PfbFixtureExcl'; File = 'x' }) + $capMap = [PSCustomObject]@{ endpoints = [PSCustomObject]@{ 'PATCH /excl' = [PSCustomObject]@{ minVersion = '2.0'; parameters = [PSCustomObject]@{}; bodyProperties = [PSCustomObject]@{ banner = '2.0'; internal_only = '2.0' } } } } + $result = Get-PfbParameterCoverageGaps -CapabilityMap $capMap -CmdletInventory $noopInventory -CalledEndpoints $endpoints -ExcludedFields @('internal_only') + $gap = $result | Where-Object { $_.Endpoint -eq 'PATCH /excl' } + $gap.MissingBodyProperties | Should -Be @('banner') + $gap.MissingBodyProperties | Should -Not -Contain 'internal_only' + } + + It 'with -ExcludedFields, drops a gap entirely when every missing field is excluded' { + $endpoints = @([PSCustomObject]@{ Key = 'GET /widgets'; Method = 'GET'; Endpoint = '/widgets'; Resolved = $true; Cmdlet = 'Get-PfbFixtureWidget'; File = 'x' }) + $capMapOnlyXRid = [PSCustomObject]@{ endpoints = [PSCustomObject]@{ 'GET /widgets' = [PSCustomObject]@{ minVersion = '2.0'; parameters = [PSCustomObject]@{ 'X-Request-ID' = '2.12' }; bodyProperties = [PSCustomObject]@{} } } } + $inventory = @([PSCustomObject]@{ Cmdlet = 'Get-PfbFixtureWidget'; Parameter = 'Name'; Surface = 'Typed'; WireName = 'name'; HasValidateSet = $false; ValidateSetValues = $null; Endpoint = 'widgets'; Method = 'GET' }) + $result = Get-PfbParameterCoverageGaps -CapabilityMap $capMapOnlyXRid -CmdletInventory $inventory -CalledEndpoints $endpoints -ExcludedFields @('X-Request-ID') + $result | Where-Object { $_.Endpoint -eq 'GET /widgets' } | Should -BeNullOrEmpty + } + + It 'returns MissingQueryParameters in deterministic alphabetical order regardless of capability-map field order' { + # Field names deliberately declared in reverse/scrambled order below -- the field-name + # collections are staged through a Dictionary[string,string] whose insertion order + # traces back to JSON property order, which is not a documented contract for + # PSCustomObject.PSObject.Properties enumeration, so without an explicit sort this + # list's order is not guaranteed to be stable (confirmed live in an earlier version of + # this function, staged through a plain Hashtable instead: regenerating + # Reports/PfbApiDriftReport.md twice produced two byte-different files for the exact + # same drift content). The fix must sort regardless of insertion order, so this test + # deliberately supplies fields already out of alphabetical order. + $capMapScrambled = [PSCustomObject]@{ + endpoints = [PSCustomObject]@{ + 'GET /widgets' = [PSCustomObject]@{ + minVersion = '2.0' + parameters = [PSCustomObject]@{ zebra = '2.0'; apple = '2.0'; mango = '2.0' } + bodyProperties = [PSCustomObject]@{} + } + } + } + $endpoints = @([PSCustomObject]@{ Key = 'GET /widgets'; Method = 'GET'; Endpoint = '/widgets'; Resolved = $true; Cmdlet = 'Get-PfbFixtureWidget'; File = 'x' }) + $inventory = @([PSCustomObject]@{ Cmdlet = 'Get-PfbFixtureWidget'; Parameter = 'Name'; Surface = 'Typed'; WireName = 'name'; HasValidateSet = $false; ValidateSetValues = $null; Endpoint = 'widgets'; Method = 'GET' }) + $result = Get-PfbParameterCoverageGaps -CapabilityMap $capMapScrambled -CmdletInventory $inventory -CalledEndpoints $endpoints + $gap = $result | Where-Object { $_.Endpoint -eq 'GET /widgets' } + $gap.MissingQueryParameters | Should -Be @('apple', 'mango', 'zebra') + } + + It 'returns MissingBodyProperties and ReadOnlyFields in deterministic alphabetical order too' { + $capMapScrambled = [PSCustomObject]@{ + endpoints = [PSCustomObject]@{ + 'PATCH /widgets' = [PSCustomObject]@{ + minVersion = '2.0' + parameters = [PSCustomObject]@{} + bodyProperties = [PSCustomObject]@{ zebra = '2.0'; apple = '2.0'; mango = '2.0'; yak = '2.0'; banana = '2.0' } + readOnlyBodyProperties = @('zebra', 'yak') + } + } + } + $endpoints = @([PSCustomObject]@{ Key = 'PATCH /widgets'; Method = 'PATCH'; Endpoint = '/widgets'; Resolved = $true; Cmdlet = 'Update-PfbFixtureWidget'; File = 'x' }) + $result = Get-PfbParameterCoverageGaps -CapabilityMap $capMapScrambled -CmdletInventory $noopInventory -CalledEndpoints $endpoints + $gap = $result | Where-Object { $_.Endpoint -eq 'PATCH /widgets' } + $gap.MissingBodyProperties | Should -Be @('apple', 'banana', 'mango') + $gap.ReadOnlyFields | Should -Be @('yak', 'zebra') + } + + Context 'body fields read-only in the newest analysed spec are split into ReadOnlyFields, never MissingBodyProperties' { + BeforeAll { + $script:roCapMap = [PSCustomObject]@{ + endpoints = [PSCustomObject]@{ + 'PATCH /ro-fixture' = [PSCustomObject]@{ + minVersion = '2.0' + parameters = [PSCustomObject]@{} + bodyProperties = [PSCustomObject]@{ name = '2.0'; owner = '2.0' } + readOnlyBodyProperties = @('owner') + } + } + } + $script:roEndpoints = @([PSCustomObject]@{ Key = 'PATCH /ro-fixture'; Method = 'PATCH'; Endpoint = '/ro-fixture'; Resolved = $true; Cmdlet = 'Update-PfbFixtureRo'; File = 'x' }) + } + + It 'puts the addable field in MissingBodyProperties and the read-only one in ReadOnlyFields, never both' { + $result = Get-PfbParameterCoverageGaps -CapabilityMap $roCapMap -CmdletInventory $noopInventory -CalledEndpoints $roEndpoints + $gap = $result | Where-Object { $_.Endpoint -eq 'PATCH /ro-fixture' } + $gap.MissingBodyProperties | Should -Be @('name') + $gap.ReadOnlyFields | Should -Be @('owner') + $gap.MissingBodyProperties | Should -Not -Contain 'owner' + $gap.ReadOnlyFields | Should -Not -Contain 'name' + } + + It 'still emits the endpoint when the only gap is a read-only field (an endpoint is emitted if ANY list is non-empty)' { + $roOnlyCapMap = [PSCustomObject]@{ + endpoints = [PSCustomObject]@{ + 'PATCH /ro-fixture' = [PSCustomObject]@{ + minVersion = '2.0' + parameters = [PSCustomObject]@{} + bodyProperties = [PSCustomObject]@{ owner = '2.0' } + readOnlyBodyProperties = @('owner') + } + } + } + $result = Get-PfbParameterCoverageGaps -CapabilityMap $roOnlyCapMap -CmdletInventory $noopInventory -CalledEndpoints $roEndpoints + $gap = $result | Where-Object { $_.Endpoint -eq 'PATCH /ro-fixture' } + $gap | Should -Not -BeNullOrEmpty + $gap.MissingBodyProperties | Should -BeNullOrEmpty + $gap.ReadOnlyFields | Should -Be @('owner') + } + } + + Context '-SinceVersion on the BODY/read-only side ($bodyFieldVersions is separate code from the query-side $queryFieldVersions lookup -- mirrors the query-side -SinceVersion tests above)' { + BeforeAll { + $script:bodySinceCapMap = [PSCustomObject]@{ + endpoints = [PSCustomObject]@{ + 'PATCH /since-fixture' = [PSCustomObject]@{ + minVersion = '2.0' + parameters = [PSCustomObject]@{} + bodyProperties = [PSCustomObject]@{ old_field = '2.0'; new_body_field = '2.27'; new_ro_field = '2.27' } + readOnlyBodyProperties = @('new_ro_field') + } + } + } + $script:bodySinceEndpoints = @([PSCustomObject]@{ Key = 'PATCH /since-fixture'; Method = 'PATCH'; Endpoint = '/since-fixture'; Resolved = $true; Cmdlet = 'Update-PfbFixtureSince'; File = 'x' }) + } + + It 'with -SinceVersion, keeps a missing body property AND a read-only field introduced after that version' { + $result = Get-PfbParameterCoverageGaps -CapabilityMap $bodySinceCapMap -CmdletInventory $noopInventory -CalledEndpoints $bodySinceEndpoints -SinceVersion '2.0' + $gap = $result | Where-Object { $_.Endpoint -eq 'PATCH /since-fixture' } + $gap.MissingBodyProperties | Should -Contain 'new_body_field' + $gap.ReadOnlyFields | Should -Contain 'new_ro_field' + } + + It 'with -SinceVersion, drops a gap whose only missing body/read-only fields were introduced at or before that version' { + $result = Get-PfbParameterCoverageGaps -CapabilityMap $bodySinceCapMap -CmdletInventory $noopInventory -CalledEndpoints $bodySinceEndpoints -SinceVersion '2.27' + $gap = $result | Where-Object { $_.Endpoint -eq 'PATCH /since-fixture' } + $gap.MissingBodyProperties | Should -Not -Contain 'new_body_field' + $gap.ReadOnlyFields | Should -Not -Contain 'new_ro_field' + # old_field (2.0) is not newer than the 2.27 baseline either, so the whole gap + # for this endpoint disappears entirely -- same "endpoint dropped, not emitted + # empty" contract as the query-side equivalent test above. + $gap | Should -BeNullOrEmpty + } + } + + Context 'the keys/count/values-shadowing bug -- a field literally named "keys" must not corrupt or swallow other fields' { + # Regression test for the bug fixed by this task: `$hashtable.Keys` on a plain + # Hashtable/PSCustomObject-backed IDictionary is shadowed by a KEY literally named + # "keys" (PowerShell's dictionary-key-as-member adapter resolves ".Keys" to that + # key's VALUE instead of the real key collection). The real-world instance was + # DELETE /workloads/tags, whose capability-map parameters are keys/namespaces/ + # resource_ids/resource_names/context_names -- the "keys" key's value ("2.23") + # was fabricated as a missing field, and every genuine key (context_names, the + # one real gap) was silently lost. Reproduced here with a body property named + # "keys" so both the query and body code paths are covered by this same trap. + It 'reports a query parameter literally named "keys" as a real gap, not a fabricated version string' { + $capMap = [PSCustomObject]@{ + endpoints = [PSCustomObject]@{ + 'DELETE /fixture-tags' = [PSCustomObject]@{ + minVersion = '2.0' + parameters = [PSCustomObject]@{ keys = '2.23'; namespaces = '2.23'; resource_ids = '2.23'; resource_names = '2.23'; context_names = '2.23' } + bodyProperties = [PSCustomObject]@{} + } + } + } + $endpoints = @([PSCustomObject]@{ Key = 'DELETE /fixture-tags'; Method = 'DELETE'; Endpoint = '/fixture-tags'; Resolved = $true; Cmdlet = 'Remove-PfbFixtureTag'; File = 'x' }) + $inventory = @( + [PSCustomObject]@{ Cmdlet = 'Remove-PfbFixtureTag'; Parameter = 'Key'; Surface = 'Typed'; WireName = 'keys'; HasValidateSet = $false; ValidateSetValues = $null; Endpoint = 'fixture-tags'; Method = 'DELETE' } + [PSCustomObject]@{ Cmdlet = 'Remove-PfbFixtureTag'; Parameter = 'Namespace'; Surface = 'Typed'; WireName = 'namespaces'; HasValidateSet = $false; ValidateSetValues = $null; Endpoint = 'fixture-tags'; Method = 'DELETE' } + [PSCustomObject]@{ Cmdlet = 'Remove-PfbFixtureTag'; Parameter = 'ResourceId'; Surface = 'Typed'; WireName = 'resource_ids'; HasValidateSet = $false; ValidateSetValues = $null; Endpoint = 'fixture-tags'; Method = 'DELETE' } + [PSCustomObject]@{ Cmdlet = 'Remove-PfbFixtureTag'; Parameter = 'ResourceName'; Surface = 'Typed'; WireName = 'resource_names'; HasValidateSet = $false; ValidateSetValues = $null; Endpoint = 'fixture-tags'; Method = 'DELETE' } + ) + $result = Get-PfbParameterCoverageGaps -CapabilityMap $capMap -CmdletInventory $inventory -CalledEndpoints $endpoints + $gap = $result | Where-Object { $_.Endpoint -eq 'DELETE /fixture-tags' } + $gap.MissingQueryParameters | Should -Be @('context_names') + $gap.MissingQueryParameters | Should -Not -Contain '2.23' + } + + It 'reports a BODY property literally named "keys" as a real gap too (the trap generalises to the new split code)' { + $capMap = [PSCustomObject]@{ + endpoints = [PSCustomObject]@{ + 'PATCH /fixture-keys-body' = [PSCustomObject]@{ + minVersion = '2.0' + parameters = [PSCustomObject]@{} + bodyProperties = [PSCustomObject]@{ keys = '2.0'; count = '2.0'; values = '2.0'; other_field = '2.0' } + } + } + } + $endpoints = @([PSCustomObject]@{ Key = 'PATCH /fixture-keys-body'; Method = 'PATCH'; Endpoint = '/fixture-keys-body'; Resolved = $true; Cmdlet = 'Update-PfbFixtureKeysBody'; File = 'x' }) + $result = Get-PfbParameterCoverageGaps -CapabilityMap $capMap -CmdletInventory $noopInventory -CalledEndpoints $endpoints + $gap = $result | Where-Object { $_.Endpoint -eq 'PATCH /fixture-keys-body' } + $gap.MissingBodyProperties | Should -Be @('count', 'keys', 'other_field', 'values') + } + } + + Context '-CurrentSpecCapabilities excludes phantom fields (present in the accumulated capability map but absent from the newest analysed spec)' { + BeforeAll { + $script:phantomCapMap = [PSCustomObject]@{ + endpoints = [PSCustomObject]@{ + 'PATCH /phantom-fixture' = [PSCustomObject]@{ + minVersion = '2.0' + parameters = [PSCustomObject]@{ old_query = '2.0'; live_query = '2.0' } + bodyProperties = [PSCustomObject]@{ old_body = '2.0'; live_body = '2.0' } + } + } + } + $script:phantomEndpoints = @([PSCustomObject]@{ Key = 'PATCH /phantom-fixture'; Method = 'PATCH'; Endpoint = '/phantom-fixture'; Resolved = $true; Cmdlet = 'Update-PfbFixturePhantom'; File = 'x' }) + # Only live_query/live_body still exist in the "current" (newest analysed) spec -- + # old_query/old_body were withdrawn from the API after 2.0 but still linger in the + # capability map's accumulated (first-sight, never-removed) parameters/bodyProperties. + $script:currentSpecCaps = @([PSCustomObject]@{ Method = 'PATCH'; Path = '/phantom-fixture'; Parameters = @('live_query'); BodyProperties = @('live_body') }) + } + + It 'drops a phantom query/body field from every list when -CurrentSpecCapabilities is supplied' { + $result = Get-PfbParameterCoverageGaps -CapabilityMap $phantomCapMap -CmdletInventory $noopInventory -CalledEndpoints $phantomEndpoints -CurrentSpecCapabilities $currentSpecCaps + $gap = $result | Where-Object { $_.Endpoint -eq 'PATCH /phantom-fixture' } + $gap.MissingQueryParameters | Should -Be @('live_query') + $gap.MissingQueryParameters | Should -Not -Contain 'old_query' + $gap.MissingBodyProperties | Should -Be @('live_body') + $gap.MissingBodyProperties | Should -Not -Contain 'old_body' + } + + It 'does NOT filter phantom fields when -CurrentSpecCapabilities is omitted (safe no-op default)' { + $result = Get-PfbParameterCoverageGaps -CapabilityMap $phantomCapMap -CmdletInventory $noopInventory -CalledEndpoints $phantomEndpoints + $gap = $result | Where-Object { $_.Endpoint -eq 'PATCH /phantom-fixture' } + $gap.MissingQueryParameters | Should -Contain 'old_query' + $gap.MissingBodyProperties | Should -Contain 'old_body' + } + } + + It 'reports the same field name in both MissingQueryParameters and MissingBodyProperties without deduping (the real placement_names shape)' { + # Only 1 of 632 real endpoints has a field name declared as both a query parameter + # and a body property (POST /workloads/placement-recommendations|placement_names) -- + # this proves that when neither side is covered, both lists legitimately carry it, + # with no dedup machinery collapsing one away. + $capMap = [PSCustomObject]@{ + endpoints = [PSCustomObject]@{ + 'POST /fixture-dual' = [PSCustomObject]@{ + minVersion = '2.0' + parameters = [PSCustomObject]@{ dual_name = '2.0' } + bodyProperties = [PSCustomObject]@{ dual_name = '2.0' } + } + } + } + $endpoints = @([PSCustomObject]@{ Key = 'POST /fixture-dual'; Method = 'POST'; Endpoint = '/fixture-dual'; Resolved = $true; Cmdlet = 'New-PfbFixtureDual'; File = 'x' }) + $result = Get-PfbParameterCoverageGaps -CapabilityMap $capMap -CmdletInventory $noopInventory -CalledEndpoints $endpoints + $gap = $result | Where-Object { $_.Endpoint -eq 'POST /fixture-dual' } + $gap.MissingQueryParameters | Should -Contain 'dual_name' + $gap.MissingBodyProperties | Should -Contain 'dual_name' + } + + Context 'a cmdlet contributing zero inventory rows leaves nothing unresolved for it' { + # Regression guard for the null-vs-empty conflation: the per-cmdlet lookup is a + # Group-Object -AsHashTable, which returns $null for an absent key, and @($null) is + # a ONE-element array whose element is $null -- so the surface loop used to run once + # with $row = $null and throw evaluating $null.Surface. The real shape is the + # -Attributes-only write cmdlet (Update-PfbArray, Update-PfbPasswordPolicy, + # Update-PfbSupport): every parameter it declares is -Array or -Attributes, both + # inventory-exempt, so the inventory holds no row for it at all. + BeforeAll { + $script:attributesOnlyCapMap = [PSCustomObject]@{ + endpoints = [PSCustomObject]@{ + 'PATCH /widgets' = [PSCustomObject]@{ + minVersion = '2.0' + parameters = [PSCustomObject]@{} + bodyProperties = [PSCustomObject]@{ banner = '2.0'; ntp_servers = '2.0' } + } + } + } + $script:attributesOnlyEndpoints = @( + [PSCustomObject]@{ Key = 'PATCH /widgets'; Method = 'PATCH'; Endpoint = '/widgets'; Resolved = $true; Cmdlet = 'Update-PfbFixtureWidget'; File = 'x' } + ) + } + + It 'reports gaps with high confidence when the inventory holds no row for the cmdlet' { + # Inventory deliberately non-empty but for a DIFFERENT cmdlet, so the hashtable + # lookup for Update-PfbFixtureWidget misses and yields $null. + $result = Get-PfbParameterCoverageGaps -CapabilityMap $attributesOnlyCapMap -CmdletInventory $fullyMappedInventory -CalledEndpoints $attributesOnlyEndpoints + $gap = $result | Where-Object { $_.Endpoint -eq 'PATCH /widgets' } + $gap | Should -Not -BeNullOrEmpty + $gap.MissingBodyProperties | Should -Be @('banner', 'ntp_servers') + $gap.Confidence.Level | Should -Be 'high' + } + + It 'still surfaces the real gaps AND partial confidence when a second cmdlet on the same endpoint has an unresolved surface' { + # The vacuous-mapping rule must not become a blanket amnesty: a real + # AttributesOnly/TypedUnresolved row anywhere on the endpoint still lowers + # confidence -- but (unlike the old $fullyMapped gate) never discards the gap. + $twoCmdletEndpoints = @( + $attributesOnlyEndpoints[0] + [PSCustomObject]@{ Key = 'PATCH /widgets'; Method = 'PATCH'; Endpoint = '/widgets'; Resolved = $true; Cmdlet = 'Get-PfbFixtureWidget'; File = 'x' } + ) + $result = Get-PfbParameterCoverageGaps -CapabilityMap $attributesOnlyCapMap -CmdletInventory $notFullyMappedInventory -CalledEndpoints $twoCmdletEndpoints + $gap = $result | Where-Object { $_.Endpoint -eq 'PATCH /widgets' } + $gap | Should -Not -BeNullOrEmpty + $gap.MissingBodyProperties | Should -Be @('banner', 'ntp_servers') + $gap.Confidence.Level | Should -Be 'partial' + } + } +} + +Describe 'Get-PfbValidateSetDrift' { + It 'flags the real Get-PfbArrayPerformance -Protocol bug shape: spec has "all", ValidateSet is missing it' { + $drift = Get-PfbValidateSetDrift -CmdletInventory $driftInventory -History $driftHistory -OldestVersion '2.0' + $rec = $drift | Where-Object { $_.Cmdlet -eq 'Get-PfbArrayPerformance' -and $_.Parameter -eq 'Protocol' } + $rec.MissingValues | Should -Contain 'all' + $rec.StaleValues | Should -BeNullOrEmpty + } + + It 'flags a stale ValidateSet value the spec no longer documents' { + # Cmdlet deliberately follows the module's real -Pfb convention (not + # a bare 'Test-Fixture') so Get-PfbResourceHint derives 'ArrayPerformance' and + # Resolve-PfbFieldValueEnum's resource-hint match actually fires -- otherwise this + # would vacuously pass/fail on hint-resolution alone rather than on the stale-value + # comparison this test is meant to exercise. + $staleInventory = @( + [PSCustomObject]@{ Cmdlet = 'Test-PfbArrayPerformance'; Parameter = 'Protocol'; Surface = 'Typed'; WireName = 'protocol'; HasValidateSet = $true; ValidateSetValues = @('all', 'nfs', 'smb', 'http', 's3', 'ftp'); Endpoint = 'arrays/performance'; Method = 'GET' } + ) + $drift = Get-PfbValidateSetDrift -CmdletInventory $staleInventory -History $driftHistory -OldestVersion '2.0' + $rec = $drift | Where-Object { $_.Cmdlet -eq 'Test-PfbArrayPerformance' } + $rec.StaleValues | Should -Contain 'ftp' + } + + It 'does not flag a ValidateSet whose values exactly match the spec' { + # Same naming-convention reasoning as above -- must actually resolve to 'matched' + # so this asserts real match-with-no-drift behavior, not vacuous non-resolution. + $matchingInventory = @( + [PSCustomObject]@{ Cmdlet = 'Test-PfbArrayPerformance'; Parameter = 'Protocol'; Surface = 'Typed'; WireName = 'protocol'; HasValidateSet = $true; ValidateSetValues = @('all', 'nfs', 'smb', 'http', 's3'); Endpoint = 'arrays/performance'; Method = 'GET' } + ) + $drift = Get-PfbValidateSetDrift -CmdletInventory $matchingInventory -History $driftHistory -OldestVersion '2.0' + $drift | Should -BeNullOrEmpty + } +} + +Describe 'Get-PfbSuggestedPowerShellType (Task 5 suggestedPowerShellType mapping)' { + It 'maps integer + format int64 to [long] -- the 37-real-field truncation-risk case' { + Get-PfbSuggestedPowerShellType -Type 'integer' -Format 'int64' | Should -Be '[long]' + } + + It 'maps integer + format int32 to [int]' { + Get-PfbSuggestedPowerShellType -Type 'integer' -Format 'int32' | Should -Be '[int]' + } + + It 'maps integer + format uint32 to [int]' { + Get-PfbSuggestedPowerShellType -Type 'integer' -Format 'uint32' | Should -Be '[int]' + } + + It 'maps a bare integer with no format to [int], never silently to [long]' { + Get-PfbSuggestedPowerShellType -Type 'integer' -Format $null | Should -Be '[int]' + } + + It 'maps number (any format) to [double]' { + Get-PfbSuggestedPowerShellType -Type 'number' -Format 'double' | Should -Be '[double]' + Get-PfbSuggestedPowerShellType -Type 'number' -Format 'float' | Should -Be '[double]' + Get-PfbSuggestedPowerShellType -Type 'number' -Format $null | Should -Be '[double]' + } + + It 'maps string to [string]' { + Get-PfbSuggestedPowerShellType -Type 'string' | Should -Be '[string]' + } + + It 'maps boolean to [bool]' { + Get-PfbSuggestedPowerShellType -Type 'boolean' | Should -Be '[bool]' + } + + It 'maps array of string to [string[]]' { + Get-PfbSuggestedPowerShellType -Type 'array' -ItemType 'string' | Should -Be '[string[]]' + } + + It 'maps array of integer/int64 to [long[]]' { + Get-PfbSuggestedPowerShellType -Type 'array' -ItemType 'integer' -ItemFormat 'int64' | Should -Be '[long[]]' + } + + It 'falls back to [object[]] for an array whose element type could not be resolved' { + Get-PfbSuggestedPowerShellType -Type 'array' -ItemType $null | Should -Be '[object[]]' + } + + It 'falls back to [object] for $null/unrecognised/object types, never a guessed scalar type' { + Get-PfbSuggestedPowerShellType -Type $null | Should -Be '[object]' + Get-PfbSuggestedPowerShellType -Type '' | Should -Be '[object]' + Get-PfbSuggestedPowerShellType -Type 'object' | Should -Be '[object]' + } +} + +Describe 'Get-PfbBodyPropertyArrayItemType / Get-PfbBodyPropertySynopsis (direct, non-recursive schema lookups)' { + # $enrichmentSpec is set in the file-level BeforeAll (top of file) -- it is shared with + # the Get-PfbBodyPropertyEnrichment Describe below, so it must not be re-declared here. + + It 'Get-PfbBodyPropertyArrayItemType resolves an inline items.type/format' { + $result = Get-PfbBodyPropertyArrayItemType -Spec $enrichmentSpec -OwnerSchema 'Widget' -FieldName 'tags' + $result.Type | Should -Be 'string' + } + + It 'Get-PfbBodyPropertyArrayItemType returns $null when items is itself an unresolved $ref with no inline type (never follows it -- one walker, not two)' { + $result = Get-PfbBodyPropertyArrayItemType -Spec $enrichmentSpec -OwnerSchema 'Widget' -FieldName 'ref_tags' + $result | Should -BeNullOrEmpty + } + + It 'Get-PfbBodyPropertyArrayItemType returns $null for a non-array field' { + Get-PfbBodyPropertyArrayItemType -Spec $enrichmentSpec -OwnerSchema 'Widget' -FieldName 'color' | Should -BeNullOrEmpty + } + + It 'Get-PfbBodyPropertyArrayItemType returns $null when -OwnerSchema is $null (no named owner to look up)' { + Get-PfbBodyPropertyArrayItemType -Spec $enrichmentSpec -OwnerSchema $null -FieldName 'tags' | Should -BeNullOrEmpty + } + + It 'Get-PfbBodyPropertySynopsis returns only the FIRST sentence, not the trailing enum sentence' { + Get-PfbBodyPropertySynopsis -Spec $enrichmentSpec -OwnerSchema 'Widget' -FieldName 'color' | Should -Be 'The widget color.' + } + + It 'Get-PfbBodyPropertySynopsis newline-normalises an embedded line wrap before extracting the first sentence' { + # The raw description wraps mid-SENTENCE ("...available\nin this pool.") -- real + # spec prose does this (e.g. "...`all-squash`, and\n`no-root-squash`."). The + # newline must become a space BEFORE the first-sentence regex runs, and only that + # first sentence is returned -- the second sentence ("Additional prose...") must + # NOT appear in the result. + Get-PfbBodyPropertySynopsis -Spec $enrichmentSpec -OwnerSchema 'Widget' -FieldName 'count' | Should -Be 'Count of widgets available in this pool.' + } + + It 'Get-PfbBodyPropertySynopsis returns the whole (short) description when it has no sentence terminator at all' { + # 'bare' has type=string but a description with no trigger sentence -- add one with + # no terminating punctuation to prove the regex-miss fallback path (whole normalised + # string) rather than throwing or returning $null. + $specWithNoTerminator = [PSCustomObject]@{ + components = [PSCustomObject]@{ + schemas = [PSCustomObject]@{ + Widget = [PSCustomObject]@{ + properties = [PSCustomObject]@{ + untamed = [PSCustomObject]@{ type = 'string'; description = 'no terminator here' } + } + } + } + } + } + Get-PfbBodyPropertySynopsis -Spec $specWithNoTerminator -OwnerSchema 'Widget' -FieldName 'untamed' | Should -Be 'no terminator here' + } + + It 'Get-PfbBodyPropertySynopsis returns $null when -OwnerSchema is $null (fully inline body, no named owner -- never re-walks to find one)' { + Get-PfbBodyPropertySynopsis -Spec $enrichmentSpec -OwnerSchema $null -FieldName 'color' | Should -BeNullOrEmpty + } + + It 'Get-PfbBodyPropertySynopsis returns $null when the named owner schema does not declare the field' { + Get-PfbBodyPropertySynopsis -Spec $enrichmentSpec -OwnerSchema 'Widget' -FieldName 'nonexistent' | Should -BeNullOrEmpty + } + + It 'Get-PfbBodyPropertySynopsis returns $null when the property has no description at all' { + Get-PfbBodyPropertySynopsis -Spec $enrichmentSpec -OwnerSchema 'Widget' -FieldName 'ref_tags' | Should -BeNullOrEmpty + } +} + +Describe 'Find-PfbOwnSchemaPropertyNode / Get-PfbOwnerSchemaPropertyNode (REGRESSION: OwnerSchema is usually allOf-composed on real data)' { + BeforeAll { + # Real-data shape (measured against fb2.27, 2026-07-26): _certificateBase, + # NfsExportPolicyRuleBase, ActiveDirectoryPatch, and SmbSharePolicyRule -- 4 of 4 + # sampled real OwnerSchema values -- carry NO "properties" directly at their own + # top level; the field lives one level down inside an ANONYMOUS allOf branch. A + # naive single-hop `schema.properties.$FieldName` lookup returns $null for nearly + # every real match. This fixture reproduces that exact shape. + $script:allOfOwnerSpec = [PSCustomObject]@{ + components = [PSCustomObject]@{ + schemas = [PSCustomObject]@{ + _certificateBase = [PSCustomObject]@{ + allOf = @( + [PSCustomObject]@{ '$ref' = '#/components/schemas/_wrongOwner' } + [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ + certificate_type = [PSCustomObject]@{ type = 'string'; description = 'The type of the certificate. Valid values are `appliance` and `external`.' } + tags = [PSCustomObject]@{ type = 'array'; items = [PSCustomObject]@{ type = 'string' }; description = 'Certificate tags.' } + } + } + ) + } + _wrongOwner = [PSCustomObject]@{ + properties = [PSCustomObject]@{ + certificate_type = [PSCustomObject]@{ type = 'string'; description = 'WRONG -- this is a different schema''s field of the same name and must never be read.' } + } + } + } + } + } + } + + It 'finds a field declared inside an ANONYMOUS allOf branch of the owner, not just the owner''s own top-level properties' { + $node = Get-PfbOwnerSchemaPropertyNode -Spec $allOfOwnerSpec -OwnerSchema '_certificateBase' -FieldName 'certificate_type' + $node | Should -Not -BeNullOrEmpty + $node.description | Should -Match '^The type of the certificate\.' + } + + It 'never crosses into a $ref-branch of the owner to find the field (that branch belongs to a DIFFERENT named schema)' { + # _certificateBase's allOf[0] is a $ref to _wrongOwner, which ALSO declares + # certificate_type (with a description that would be an obvious tell if read). The + # real field must resolve to the allOf[1] (anonymous) branch's description, never + # _wrongOwner's. + $node = Get-PfbOwnerSchemaPropertyNode -Spec $allOfOwnerSpec -OwnerSchema '_certificateBase' -FieldName 'certificate_type' + $node.description | Should -Not -Match 'WRONG' + } + + It 'Get-PfbBodyPropertySynopsis resolves through the allOf-composed owner correctly' { + Get-PfbBodyPropertySynopsis -Spec $allOfOwnerSpec -OwnerSchema '_certificateBase' -FieldName 'certificate_type' | Should -Be 'The type of the certificate.' + } + + It 'Get-PfbBodyPropertyArrayItemType resolves an array field declared inside an allOf-composed owner' { + $result = Get-PfbBodyPropertyArrayItemType -Spec $allOfOwnerSpec -OwnerSchema '_certificateBase' -FieldName 'tags' + $result.Type | Should -Be 'string' + } + + It 'returns $null for a field the owner (searched through its own allOf) never declares at all' { + Get-PfbOwnerSchemaPropertyNode -Spec $allOfOwnerSpec -OwnerSchema '_certificateBase' -FieldName 'nonexistent' | Should -BeNullOrEmpty + } +} + +Describe 'Get-PfbBodyPropertyEnrichment (Task 5: composed enum join + type + synopsis)' { + BeforeAll { + $script:enrichmentHistory = [ordered]@{ + 'Widget.color' = [ordered]@{ Name = 'color'; Kind = 'schema'; MinVersion = '2.0'; CurrentValues = @('red', 'blue', 'green'); DistinctValueSets = [System.Collections.Generic.HashSet[string]]::new([string[]]@('blue,green,red')) } + 'OtherSchema.color' = [ordered]@{ Name = 'color'; Kind = 'schema'; MinVersion = '2.0'; CurrentValues = @('x', 'y'); DistinctValueSets = [System.Collections.Generic.HashSet[string]]::new([string[]]@('x,y')) } + } + } + + It 'resolves EnumStatus matched and EnumValues via OwnerSchema as the resource hint' { + $result = Get-PfbBodyPropertyEnrichment -FieldName 'color' -Type 'string' -Format $null -OwnerSchema 'Widget' ` + -Spec $enrichmentSpec -Endpoint 'widgets' -Method 'PATCH' -History $enrichmentHistory -OldestVersion '2.0' + $result.EnumStatus | Should -Be 'matched' + $result.EnumValues | Should -Be @('red', 'blue', 'green') + $result.Synopsis | Should -Be 'The widget color.' + $result.SuggestedPowerShellType | Should -Be '[string]' + } + + It 'never lets a $null OwnerSchema wildcard-match every same-named schema-kind history entry -- resolves not-found-in-resource, not a false matched/collision' { + # WireName 'color' exists in history under TWO different owners with DIFFERENT + # value sets (Widget.color, OtherSchema.color). With OwnerSchema $null (this + # specific gap has no named owner), the sentinel resource hint must prefix-match + # NEITHER of them -- proving '' is never substituted for the sentinel (a '' hint + # would wildcard-match both and yield 'collision' instead). + $result = Get-PfbBodyPropertyEnrichment -FieldName 'color' -Type 'string' -Format $null -OwnerSchema $null ` + -Spec $enrichmentSpec -Endpoint 'widgets' -Method 'PATCH' -History $enrichmentHistory -OldestVersion '2.0' + $result.EnumStatus | Should -Be 'not-found-in-resource' + $result.EnumValues | Should -BeNullOrEmpty + $result.Synopsis | Should -BeNullOrEmpty + } + + It 'resolves EnumStatus no-spec-enum-found for a field absent from History entirely' { + $result = Get-PfbBodyPropertyEnrichment -FieldName 'nonexistent_field' -Type 'string' -Format $null -OwnerSchema 'Widget' ` + -Spec $enrichmentSpec -Endpoint 'widgets' -Method 'PATCH' -History $enrichmentHistory -OldestVersion '2.0' + $result.EnumStatus | Should -Be 'no-spec-enum-found' + $result.EnumValues | Should -BeNullOrEmpty + } + + It 'derives SuggestedPowerShellType for an array field via the array-item-type lookup' { + $result = Get-PfbBodyPropertyEnrichment -FieldName 'tags' -Type 'array' -Format $null -OwnerSchema 'Widget' ` + -Spec $enrichmentSpec -Endpoint 'widgets' -Method 'PATCH' -History $enrichmentHistory -OldestVersion '2.0' + $result.SuggestedPowerShellType | Should -Be '[string[]]' + } + + It 'derives SuggestedPowerShellType [long] for an int64 field, never the truncating [int]' { + $result = Get-PfbBodyPropertyEnrichment -FieldName 'count' -Type 'integer' -Format 'int64' -OwnerSchema 'Widget' ` + -Spec $enrichmentSpec -Endpoint 'widgets' -Method 'PATCH' -History $enrichmentHistory -OldestVersion '2.0' + $result.SuggestedPowerShellType | Should -Be '[long]' + } + + It 'EnumValues is always an array, never $null, even when EnumStatus is not matched' { + # Deliberately NOT `$result.EnumValues | Should -BeOfType ...` -- piping a + # genuinely EMPTY array to Should never invokes the assertion with a real value at + # all (Pester's pipeline binding sees zero objects go by and reports $null), which + # would make this test pass or fail for the wrong reason regardless of the actual + # array-vs-$null distinction it exists to check. Capture into a scalar first. + $result = Get-PfbBodyPropertyEnrichment -FieldName 'nonexistent_field' -Type 'string' -Format $null -OwnerSchema 'Widget' ` + -Spec $enrichmentSpec -Endpoint 'widgets' -Method 'PATCH' -History $enrichmentHistory -OldestVersion '2.0' + $isNull = ($null -eq $result.EnumValues) + $isNull | Should -BeFalse + $countIsZero = (@($result.EnumValues).Count -eq 0) + $countIsZero | Should -BeTrue + } +} + +Describe 'Get-PfbSystemicGaps (Task 6, decision 7: collapse per-endpoint gap lists into one finding per wire name)' { + BeforeAll { + $script:systemicGaps = @( + [PSCustomObject]@{ Endpoint = 'GET /a'; MissingQueryParameters = @('context_names', 'filter'); MissingBodyProperties = @() } + [PSCustomObject]@{ Endpoint = 'GET /b'; MissingQueryParameters = @('context_names'); MissingBodyProperties = @() } + [PSCustomObject]@{ Endpoint = 'POST /c'; MissingQueryParameters = @(); MissingBodyProperties = @('context_names') } + [PSCustomObject]@{ Endpoint = 'DELETE /d'; MissingQueryParameters = @('lonely_field'); MissingBodyProperties = @() } + ) + } + + It 'collapses a name repeated across many endpoints into ONE finding with the right EndpointCount' { + $findings = Get-PfbSystemicGaps -Gaps $systemicGaps + $ctx = $findings | Where-Object { $_.Name -eq 'context_names' } + $ctx | Should -Not -BeNullOrEmpty + $ctx.EndpointCount | Should -Be 3 + $ctx.Endpoints | Should -Be @('GET /a', 'GET /b', 'POST /c') + } + + It 'counts query vs body occurrences separately, informationally, without affecting the deduplicated total' { + $ctx = (Get-PfbSystemicGaps -Gaps $systemicGaps) | Where-Object { $_.Name -eq 'context_names' } + $ctx.QueryEndpointCount | Should -Be 2 + $ctx.BodyEndpointCount | Should -Be 1 + $ctx.EndpointCount | Should -Be 3 + } + + It 'dedupes an endpoint appearing in BOTH lists for the same name to ONE endpoint credit (the placement_names shape)' { + $dualGaps = @([PSCustomObject]@{ Endpoint = 'POST /dual'; MissingQueryParameters = @('dual_name'); MissingBodyProperties = @('dual_name') }) + $finding = (Get-PfbSystemicGaps -Gaps $dualGaps) | Where-Object { $_.Name -eq 'dual_name' } + $finding.EndpointCount | Should -Be 1 + $finding.QueryEndpointCount | Should -Be 1 + $finding.BodyEndpointCount | Should -Be 1 + } + + It 'still emits a finding for a name that appears on only one endpoint' { + $finding = (Get-PfbSystemicGaps -Gaps $systemicGaps) | Where-Object { $_.Name -eq 'lonely_field' } + $finding.EndpointCount | Should -Be 1 + } + + It 'sorts findings by EndpointCount descending, Name ascending as a tiebreak' { + $findings = @(Get-PfbSystemicGaps -Gaps $systemicGaps) + $findings[0].Name | Should -Be 'context_names' + ($findings | Select-Object -Skip 1).EndpointCount | ForEach-Object { $_ | Should -BeLessOrEqual $findings[0].EndpointCount } + } + + It 'returns an empty array for an empty -Gaps input, never $null or an error' { + $findings = @(Get-PfbSystemicGaps -Gaps @()) + $findings.Count | Should -Be 0 + } + + It 'tolerates an enriched MissingBodyProperties record shape ({name=...}) via Get-PfbGapFieldName, not just bare strings' { + $enrichedGaps = @([PSCustomObject]@{ Endpoint = 'PATCH /e'; MissingQueryParameters = @(); MissingBodyProperties = @([PSCustomObject]@{ name = 'enriched_field'; type = 'string' }) }) + $finding = (Get-PfbSystemicGaps -Gaps $enrichedGaps) | Where-Object { $_.Name -eq 'enriched_field' } + $finding | Should -Not -BeNullOrEmpty + $finding.EndpointCount | Should -Be 1 + } +} + +Describe 'Get-PfbConventionStrength (Task 6, decision 8: mechanical batch-fix vs architectural decision)' { + BeforeAll { + $script:strengthInventory = @( + [PSCustomObject]@{ Cmdlet = 'Get-PfbFixtureA'; Parameter = 'Name'; Surface = 'Typed'; WireName = 'names' } + [PSCustomObject]@{ Cmdlet = 'Get-PfbFixtureB'; Parameter = 'Name'; Surface = 'Typed'; WireName = 'names' } + [PSCustomObject]@{ Cmdlet = 'Get-PfbFixtureC'; Parameter = 'Id'; Surface = 'Typed'; WireName = 'ids' } + # A duplicate (Cmdlet, WireName) pair -- e.g. two parameters on the same cmdlet + # both feeding 'names' -- must still count Get-PfbFixtureA only ONCE. + [PSCustomObject]@{ Cmdlet = 'Get-PfbFixtureA'; Parameter = 'Alias'; Surface = 'Typed'; WireName = 'names' } + # An unresolved surface must never contribute -- only 'Typed' counts. + [PSCustomObject]@{ Cmdlet = 'Get-PfbFixtureD'; Parameter = 'Weird'; Surface = 'AttributesOnly'; WireName = $null } + ) + } + + It 'counts DISTINCT cmdlets exposing a wire name, deduping a cmdlet with two parameters feeding the same name' { + $result = Get-PfbConventionStrength -CmdletInventory $strengthInventory -Names @('names') + $result.CmdletCount | Should -Be 2 + $result.Cmdlets | Should -Be @('Get-PfbFixtureA', 'Get-PfbFixtureB') + } + + It 'reports 0/empty (never omitted) for a name no cmdlet exposes -- the architectural-decision signal' { + $result = Get-PfbConventionStrength -CmdletInventory $strengthInventory -Names @('context_names') + $result.CmdletCount | Should -Be 0 + $result.Cmdlets | Should -Be @() + } + + It 'preserves -Names input order across multiple names, one result per name' { + $results = @(Get-PfbConventionStrength -CmdletInventory $strengthInventory -Names @('ids', 'names', 'context_names')) + $results[0].Name | Should -Be 'ids' + $results[1].Name | Should -Be 'names' + $results[2].Name | Should -Be 'context_names' + } + + It 'ignores an AttributesOnly/unresolved row entirely' { + $result = Get-PfbConventionStrength -CmdletInventory $strengthInventory -Names @('weird') + $result.CmdletCount | Should -Be 0 + } +} + +Describe 'Get-PfbDriftAnnotations / Find-PfbDriftAnnotation (Task 6: recorded design decisions, prior conclusions, live-testing hazards)' { + BeforeAll { + $script:annotationsFixturePath = Join-Path $TestDrive 'drift-annotations.fixture.json' + Set-Content -Path $annotationsFixturePath -Value @' +{ + "schemaVersion": 1, + "annotations": [ + { "matchType": "field", "match": "context_names", "kind": "designDecision", "note": "not yet implemented", "reference": "docs/design/fusion-context-injection.md" }, + { "matchType": "field", "match": "allow_errors", "kind": "designDecision", "note": "not yet implemented", "reference": "docs/design/fusion-context-injection.md" }, + { "matchType": "endpoint", "match": "management-access-policies", "kind": "liveTestingHazard", "note": "POST/PATCH/DELETE return 403 regardless of account; not an implementation bug", "reference": null } + ] +} +'@ + } + + It 'loads the file and finds a field-keyed annotation by exact wire name' { + # @(...) wraps the call itself, not just its consumption -- a single-match result + # is a bare PSCustomObject crossing the function-call boundary (PowerShell always + # unwraps a one-element pipeline result on return, @() inside the function's own + # `return` notwithstanding). PowerShell 7+ masks this with an automatic .Count/ + # .Length property on every object, so `$found.Count` reads 1 there regardless -- + # but Windows PowerShell 5.1 has no such property and .Count silently returns + # $null on a bare object. Confirmed live under real 5.1: without this wrap here, + # this exact assertion fails with "Expected 1, but got $null." + $annotations = Get-PfbDriftAnnotations -Path $annotationsFixturePath + $found = @(Find-PfbDriftAnnotation -Annotations $annotations -FieldName 'context_names') + $found.Count | Should -Be 1 + $found[0].kind | Should -Be 'designDecision' + $found[0].reference | Should -Be 'docs/design/fusion-context-injection.md' + } + + It 'never asserts whether allow_errors injection is endpoint-gated or unconditional -- purely descriptive note only' { + $annotations = Get-PfbDriftAnnotations -Path $annotationsFixturePath + $found = Find-PfbDriftAnnotation -Annotations $annotations -FieldName 'allow_errors' + $found[0].note | Should -Be 'not yet implemented' + $found[0].note | Should -Not -Match 'gate|gated|unconditional|every endpoint' + } + + It 'finds an endpoint-keyed annotation via case-insensitive substring match against the endpoint key' { + $annotations = Get-PfbDriftAnnotations -Path $annotationsFixturePath + $found = @(Find-PfbDriftAnnotation -Annotations $annotations -Endpoint 'POST /management-access-policies') + $found.Count | Should -Be 1 + $found[0].kind | Should -Be 'liveTestingHazard' + $found[0].note | Should -Match '403' + } + + It 'returns an empty array (never $null/error) for a name/endpoint with no annotation' { + $annotations = Get-PfbDriftAnnotations -Path $annotationsFixturePath + @(Find-PfbDriftAnnotation -Annotations $annotations -FieldName 'no_such_field').Count | Should -Be 0 + } + + It 'treats a literal "*" in an endpoint annotation''s match value as a literal character, not a live -like wildcard' { + # A future endpoint-type annotation could legitimately contain a literal '*'. An + # unescaped `-like "*$($_.match)*"` would treat it as "match anything" instead of + # a literal asterisk character. + $wildcardFixturePath = Join-Path $TestDrive 'drift-annotations-wildcard-star.fixture.json' + Set-Content -Path $wildcardFixturePath -Value @' +{ + "schemaVersion": 1, + "annotations": [ + { "matchType": "endpoint", "match": "widgets*prod", "kind": "liveTestingHazard", "note": "literal-asterisk regression fixture", "reference": null } + ] +} +'@ + $annotations = Get-PfbDriftAnnotations -Path $wildcardFixturePath + + # Endpoint contains the literal substring "widgets*prod" -- must match. @(...) + # wraps the call itself -- see the file-keyed test above for why a bare single-match + # result silently fails .Count on Windows PowerShell 5.1. + @(Find-PfbDriftAnnotation -Annotations $annotations -Endpoint 'GET /widgets*prod-fixture').Count | Should -Be 1 + + # Endpoint contains "widgets" ... "prod" but NOT the literal "widgets*prod" + # substring -- an unescaped -like ("*widgets*prod*") would still match this (the + # '*' matching "anything" in between), so a miss here proves the match string is + # treated as a literal substring, not a wildcard pattern. + @(Find-PfbDriftAnnotation -Annotations $annotations -Endpoint 'GET /widgets-are-in-prod').Count | Should -Be 0 + } + + It 'treats a literal "[" in an endpoint annotation''s match value as a literal character, not a live -like character class' { + # An unescaped `-like "*$($_.match)*"` would treat "[0]" as a character class + # (matching a single literal '0' character) rather than the literal 3-character + # text "[0]". + $wildcardFixturePath = Join-Path $TestDrive 'drift-annotations-wildcard-bracket.fixture.json' + Set-Content -Path $wildcardFixturePath -Value @' +{ + "schemaVersion": 1, + "annotations": [ + { "matchType": "endpoint", "match": "prod[0]", "kind": "liveTestingHazard", "note": "literal-bracket regression fixture", "reference": null } + ] +} +'@ + $annotations = Get-PfbDriftAnnotations -Path $wildcardFixturePath + + # Endpoint contains the literal substring "prod[0]" -- must match. @(...) wraps + # the call itself -- see the file-keyed test above for why a bare single-match + # result silently fails .Count on Windows PowerShell 5.1. + @(Find-PfbDriftAnnotation -Annotations $annotations -Endpoint 'GET /prod[0]-real').Count | Should -Be 1 + + # Endpoint contains "prod" immediately followed by "0" (no brackets) -- an + # unescaped -like would still match this (the "[0]" character class matching that + # literal '0'), so a miss here proves the match string is treated as a literal + # substring, not a wildcard pattern. + @(Find-PfbDriftAnnotation -Annotations $annotations -Endpoint 'GET /prod0-fixture').Count | Should -Be 0 + } + + It 'returns $null (never throws) when -Path does not exist' { + Get-PfbDriftAnnotations -Path (Join-Path $TestDrive 'does-not-exist.json') | Should -BeNullOrEmpty + } + + It 'Find-PfbDriftAnnotation tolerates a $null -Annotations without throwing' { + @(Find-PfbDriftAnnotation -Annotations $null -FieldName 'context_names').Count | Should -Be 0 + } + + It 'the real checked-in docs/drift-annotations.json loads and carries both required seed entries' { + $realPath = Join-Path $repoRoot 'docs/drift-annotations.json' + Test-Path $realPath | Should -BeTrue + # @(...) wraps each call itself -- see the file-keyed test above for why a bare + # single-match result silently fails .Count on Windows PowerShell 5.1. + $realAnnotations = Get-PfbDriftAnnotations -Path $realPath + @(Find-PfbDriftAnnotation -Annotations $realAnnotations -FieldName 'context_names').Count | Should -Be 1 + @(Find-PfbDriftAnnotation -Annotations $realAnnotations -FieldName 'allow_errors').Count | Should -Be 1 + @(Find-PfbDriftAnnotation -Annotations $realAnnotations -Endpoint 'DELETE /management-access-policies').Count | Should -Be 1 + } +} + +Describe 'Get-PfbCentralInjectionSites / Get-PfbDerivedNonActionableParameters / Get-PfbNonActionableParameters (Task 6: central-injection detection)' { + BeforeAll { + $script:injectionFixtureDir = Join-Path $TestDrive 'InjectionPrivate' + New-Item -ItemType Directory -Path $injectionFixtureDir -Force | Out-Null + + # Mirrors the REAL Private/Add-PfbCommonQueryParams.ps1 shape exactly: every + # assignment here traces its VALUE back to a parameter of this function, either + # directly (-Names/-Ids) or via the $BoundParameters dictionary the caller + # forwarded (-Filter/-Sort/-Limit), or via a narrowly-shaped ContainsKey(...) guard + # whose value is a literal (-TotalOnly). + Set-Content -Path (Join-Path $injectionFixtureDir 'Add-PfbFixtureCommonQueryParams.ps1') -Value @' +function Add-PfbFixtureCommonQueryParams { + [CmdletBinding()] + param( + [Parameter(Mandatory)][hashtable]$Into, + [Parameter(Mandatory)][System.Collections.IDictionary]$BoundParameters, + [string[]]$Names, + [string[]]$Ids + ) + if ($BoundParameters.ContainsKey('Filter')) { $Into['filter'] = $BoundParameters['Filter'] } + if ($BoundParameters.ContainsKey('TotalOnly')) { $Into['total_only'] = 'true' } + if ($Names) { $Into['names'] = $Names -join ',' } +} +'@ + + # Mirrors the REAL Private/Invoke-PfbApiRequest.ps1 shape exactly: the assigned + # VALUE traces to $response (server data), and the guarding condition is a compound + # -and expression that merely INCLUDES -AutoPaginate as one operand -- it must NOT + # count as "gated on a parameter" for THIS key's value. + Set-Content -Path (Join-Path $injectionFixtureDir 'Invoke-PfbFixtureApiRequest.ps1') -Value @' +function Invoke-PfbFixtureApiRequest { + [CmdletBinding()] + param( + [hashtable]$QueryParams, + [switch]$AutoPaginate + ) + $response = Invoke-RestMethod -Uri 'https://example.invalid' + $limitReached = $false + if ($AutoPaginate -and $response.continuation_token -and -not $limitReached) { + $QueryParams['continuation_token'] = $response.continuation_token + } +} +'@ + + # HTTP-transport plumbing that must be OUT OF SCOPE entirely -- a variable name not + # in the recognized set (queryParams/body/into). + Set-Content -Path (Join-Path $injectionFixtureDir 'Invoke-PfbFixtureLogin.ps1') -Value @' +function Invoke-PfbFixtureLogin { + [CmdletBinding()] + param([string]$Token) + $headers = @{} + $headers['Authorization'] = "Bearer $Token" +} +'@ + + $script:injectionSites = Get-PfbCentralInjectionSites -PrivateDirectory $injectionFixtureDir + } + + It 'classifies filter (via $BoundParameters, a parameter) as parameter-sourced' { + ($injectionSites | Where-Object { $_.Key -eq 'filter' }).Classification | Should -Be 'parameter-sourced' + } + + It 'classifies total_only (literal RHS, but ContainsKey-guarded on a parameter) as parameter-sourced' { + ($injectionSites | Where-Object { $_.Key -eq 'total_only' }).Classification | Should -Be 'parameter-sourced' + } + + It 'classifies names (direct parameter reference in the RHS) as parameter-sourced' { + ($injectionSites | Where-Object { $_.Key -eq 'names' }).Classification | Should -Be 'parameter-sourced' + } + + It 'classifies continuation_token (RHS traces to $response, compound non-parameter guard) as server-or-internal-derived' { + ($injectionSites | Where-Object { $_.Key -eq 'continuation_token' }).Classification | Should -Be 'server-or-internal-derived' + } + + It 'never picks up an out-of-scope hashtable variable name (headers is not body/queryParams/into)' { + $injectionSites | Where-Object { $_.Key -eq 'Authorization' } | Should -BeNullOrEmpty + } + + It 'every site carries the coverage-not-correctness caveat' { + $injectionSites | ForEach-Object { $_.Caveat | Should -Match 'coverage' } + } + + It 'Get-PfbDerivedNonActionableParameters includes continuation_token but excludes filter/total_only/names' { + $derived = Get-PfbDerivedNonActionableParameters -InjectionSites $injectionSites + $derived.Name | Should -Contain 'continuation_token' + $derived.Name | Should -Not -Contain 'filter' + $derived.Name | Should -Not -Contain 'total_only' + $derived.Name | Should -Not -Contain 'names' + } + + It 'marks continuation_token Strength as structural, not the general coverage claim' { + $derived = Get-PfbDerivedNonActionableParameters -InjectionSites $injectionSites + ($derived | Where-Object { $_.Name -eq 'continuation_token' }).Strength | Should -Be 'structural' + } + + It 'Get-PfbNonActionableParameters unions the hardcoded X-Request-ID/offset with the derived continuation_token' { + $merged = Get-PfbNonActionableParameters -PrivateDirectory $injectionFixtureDir + $merged | Should -Be @('continuation_token', 'offset', 'X-Request-ID') | Sort-Object + $merged | Should -Contain 'X-Request-ID' + $merged | Should -Contain 'offset' + $merged | Should -Contain 'continuation_token' + $merged | Should -Not -Contain 'filter' + $merged | Should -Not -Contain 'names' + } + + It 'a key with even ONE parameter-sourced site anywhere is never derived, even if another site for it looked server-derived' { + # Regression guard for the exact mistake this effort already caught and corrected + # once: a bare "any assignment found" rule would have wrongly flagged + # Add-PfbCommonQueryParams' own keys. Two sites for the SAME key, one of each + # classification -- the derived result must still exclude it. + $mixedDir = Join-Path $TestDrive 'MixedPrivate' + New-Item -ItemType Directory -Path $mixedDir -Force | Out-Null + Set-Content -Path (Join-Path $mixedDir 'Test-PfbFixtureMixed.ps1') -Value @' +function Test-PfbFixtureMixed { + [CmdletBinding()] + param([string]$Sort) + $queryParams = @{} + if ($Sort) { $queryParams['sort'] = $Sort } + $response = Invoke-RestMethod -Uri 'https://example.invalid' + $queryParams['sort'] = $response.default_sort +} +'@ + $mixedSites = Get-PfbCentralInjectionSites -PrivateDirectory $mixedDir + ($mixedSites | Where-Object { $_.Key -eq 'sort' }).Classification | Should -Be @('parameter-sourced', 'server-or-internal-derived') + $derived = Get-PfbDerivedNonActionableParameters -InjectionSites $mixedSites + $derived.Name | Should -Not -Contain 'sort' + } + + It 'X-Request-ID and offset have no Private/ injection site in this fixture, matching the real tree (confirmed separately below)' { + $injectionSites | Where-Object { $_.Key -eq 'X-Request-ID' } | Should -BeNullOrEmpty + $injectionSites | Where-Object { $_.Key -eq 'offset' } | Should -BeNullOrEmpty + } +} + +Describe 'Central-injection detection against the REAL Private/ tree (confirms the acceptance-critical claims for real)' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + BeforeAll { + $script:realPrivateDirectory = Join-Path $repoRoot 'Private' + } + + It 'X-Request-ID has no injection site anywhere in the real Private/ tree' { + $sites = Get-PfbCentralInjectionSites -PrivateDirectory $realPrivateDirectory + $sites | Where-Object { $_.Key -eq 'X-Request-ID' } | Should -BeNullOrEmpty + } + + It 'offset has no injection site anywhere in the real Private/ tree' { + $sites = Get-PfbCentralInjectionSites -PrivateDirectory $realPrivateDirectory + $sites | Where-Object { $_.Key -eq 'offset' } | Should -BeNullOrEmpty + } + + It 'filter/sort/limit/total_only/names/ids are all classified parameter-sourced, never a candidate for exclusion' { + $sites = Get-PfbCentralInjectionSites -PrivateDirectory $realPrivateDirectory + foreach ($key in @('filter', 'sort', 'limit', 'total_only', 'names', 'ids')) { + $matching = @($sites | Where-Object { $_.Key -eq $key }) + $matching | Should -Not -BeNullOrEmpty + $matching | ForEach-Object { $_.Classification | Should -Be 'parameter-sourced' } + } + $derived = Get-PfbDerivedNonActionableParameters -InjectionSites $sites + foreach ($key in @('filter', 'sort', 'limit', 'total_only', 'names', 'ids')) { + $derived.Name | Should -Not -Contain $key + } + } + + It 'continuation_token is derived as server-or-internal-derived / structural from the real Invoke-PfbApiRequest.ps1' { + $sites = Get-PfbCentralInjectionSites -PrivateDirectory $realPrivateDirectory + $ctSites = @($sites | Where-Object { $_.Key -eq 'continuation_token' }) + $ctSites | Should -Not -BeNullOrEmpty + $ctSites | ForEach-Object { $_.Classification | Should -Be 'server-or-internal-derived' } + + $derived = Get-PfbDerivedNonActionableParameters -InjectionSites $sites + $ctDerived = $derived | Where-Object { $_.Name -eq 'continuation_token' } + $ctDerived | Should -Not -BeNullOrEmpty + $ctDerived.Strength | Should -Be 'structural' + } + + It 'Get-PfbNonActionableParameters against the real tree contains X-Request-ID/offset/continuation_token and nothing from Add-PfbCommonQueryParams' { + $merged = Get-PfbNonActionableParameters -PrivateDirectory $realPrivateDirectory + $merged | Should -Contain 'X-Request-ID' + $merged | Should -Contain 'offset' + $merged | Should -Contain 'continuation_token' + foreach ($key in @('filter', 'sort', 'limit', 'total_only', 'names', 'ids')) { + $merged | Should -Not -Contain $key + } + } +} + +Describe 'Task 6 real-data invariants (systemic gaps + convention strength, skips gracefully if the real capability map is absent)' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + BeforeAll { + $script:realCapabilityMapPath2 = Join-Path $repoRoot 'Data/PfbCapabilityMap.json' + $script:realPublicDirectory2 = Join-Path $repoRoot 'Public' + $script:realPrivateDirectory2 = Join-Path $repoRoot 'Private' + $script:realSpecsDirectory2 = Join-Path $repoRoot 'tools/specs' + $script:hasRealData = (Test-Path $realCapabilityMapPath2) -and (Test-Path $realSpecsDirectory2) -and (Get-ChildItem $realSpecsDirectory2 -Filter 'fb*.json' -ErrorAction SilentlyContinue) + + if ($hasRealData) { + # Mirrors tools/Build-PfbApiDriftReport.ps1's own construction exactly (same + # -CurrentSpecCapabilities phantom-field filtering, same -ExcludedFields via + # Get-PfbNonActionableParameters) -- Get-PfbSystemicGaps must be fed the SAME + # gaps the real report actually emits, not a looser, unfiltered set, or the + # invariants below would no longer reflect the filtering the production report + # actually applies. + . (Join-Path $repoRoot 'tools/lib/PfbSpecTools.ps1') + + $script:realCapMap2 = Get-Content -Path $realCapabilityMapPath2 -Raw | ConvertFrom-Json -Depth 20 + $script:realInventory2 = Get-PfbCmdletParameterInventory -PublicDirectory $realPublicDirectory2 + $script:realCalledEndpoints2 = Get-PfbModuleCalledEndpoints -PublicDirectory $realPublicDirectory2 -PrivateDirectory $realPrivateDirectory2 + + $newestAnalysedVersion2 = $realCapMap2.generatedFrom | Select-Object -Last 1 + $currentSpecCapabilities2 = @() + if ($newestAnalysedVersion2) { + $newestSpecPath2 = Join-Path $realSpecsDirectory2 "fb$newestAnalysedVersion2.json" + if (Test-Path $newestSpecPath2) { + $newestSpec2 = Get-Content -Path $newestSpecPath2 -Raw | ConvertFrom-Json -Depth 64 + $currentSpecCapabilities2 = @(Get-PfbSpecCapabilities -Spec $newestSpec2) + } + } + + $nonActionable2 = Get-PfbNonActionableParameters -PrivateDirectory $realPrivateDirectory2 + + $script:realGapsAllConfidence2 = Get-PfbParameterCoverageGaps -CapabilityMap $realCapMap2 -CmdletInventory $realInventory2 -CalledEndpoints $realCalledEndpoints2 -ExcludedFields $nonActionable2 -CurrentSpecCapabilities $currentSpecCapabilities2 + # Filtered to 'high'-confidence endpoints only, same precedent as Task 5's own + # MissingBodyProperties enrichment gate (Get-PfbBodyPropertyEnrichment is only + # ever invoked for a 'high'-confidence endpoint) -- a 'partial'-confidence + # endpoint's gap lists can contain false positives (an unresolved parameter may + # already cover the apparent gap through a path this AST-only inventory can't + # see), so surfacing it as a systemic-gaps FINDING would overstate the same + # confidence Get-PfbParameterCoverageGaps's own `confidence.caveat` explicitly + # warns against. Get-PfbSystemicGaps/Get-PfbConventionStrength themselves are + # confidence-agnostic by design (aggregation is a pure grouping over WHATEVER + # gaps they're handed) -- filtering by confidence is the CALLER's decision, made + # explicitly here to match tools/Build-PfbApiDriftReport.ps1's own filtering exactly. + $script:realGaps2 = @($realGapsAllConfidence2 | Where-Object { $_.Confidence.Level -eq 'high' }) + $script:realSystemicGaps2 = @(Get-PfbSystemicGaps -Gaps $realGaps2) + + function Get-RealRecountedEndpointCount2 { + param([Parameter(Mandatory)][object[]]$Gaps, [Parameter(Mandatory)][string]$FieldName) + $endpoints = [System.Collections.Generic.HashSet[string]]::new() + foreach ($g in $Gaps) { + $queryNames = @($g.MissingQueryParameters) + $bodyNames = @($g.MissingBodyProperties | ForEach-Object { if ($_ -is [string]) { $_ } else { $_.Name } }) + if (($queryNames -contains $FieldName) -or ($bodyNames -contains $FieldName)) { + [void]$endpoints.Add($g.Endpoint) + } + } + return $endpoints.Count + } + } + } + + # Historical note (Task 6 investigation): the task brief's corrected figure for + # context_names was 252; three independent methodological variants (with/without + # phantom-field filtering, with/without -ExcludedFields) all converged on 253 instead, + # and 252 could not be reproduced. 253 was the actual, honestly-measured, reproducible + # value AT THE TIME -- kept here as institutional memory, not as a hardcoded expectation. + # See docs/superpowers/plans/2026-07-30-drift-report-acceptance-figure-invariants.md for + # why exact real-data counts are the wrong assertion for an ever-growing API surface. + It 'systemic-gaps EndpointCount for allow_errors/context_names matches an independent recount straight from the same $realGaps2 fed to Get-PfbSystemicGaps' { + if (-not $hasRealData) { Set-ItResult -Skipped -Because 'Data/PfbCapabilityMap.json not present locally'; return } + foreach ($fieldName in @('allow_errors', 'context_names')) { + $finding = $realSystemicGaps2 | Where-Object { $_.Name -eq $fieldName } + $finding | Should -Not -BeNullOrEmpty -Because "$fieldName is expected to still be a systemic gap in the real API surface" + $recount = Get-RealRecountedEndpointCount2 -Gaps $realGaps2 -FieldName $fieldName + $finding.EndpointCount | Should -Be $recount -Because 'EndpointCount must equal a fresh tally over the same input gaps, independent of Get-PfbSystemicGaps'' own aggregation' + $finding.EndpointCount | Should -BeGreaterThan 0 -Because 'a vacuous/zero count would mean the field silently stopped being a systemic gap without anyone noticing here' + } + } + + It 'convention strength: names/ids have a non-vacuous, established convention; context_names has none (0 cmdlets, by design)' { + if (-not $hasRealData) { Set-ItResult -Skipped -Because 'Data/PfbCapabilityMap.json not present locally'; return } + $strength = Get-PfbConventionStrength -CmdletInventory $realInventory2 -Names @('names', 'ids', 'context_names') + foreach ($fieldName in @('names', 'ids')) { + $entry = $strength | Where-Object { $_.Name -eq $fieldName } + $entry | Should -Not -BeNullOrEmpty + # CmdletCount is asserted -BeGreaterThan 0, not an exact number: both grow every + # time an unrelated PR adds a cmdlet that happens to expose this wire name as a + # Typed parameter, which is neither a regression nor something worth re-pinning for. + $entry.CmdletCount | Should -BeGreaterThan 0 -Because "$fieldName is a widely-adopted convention; a drop to zero would be a real regression" + } + # context_names is the ONE name Get-PfbConventionStrength's own docstring calls out + # as an architectural fact, not a live count: "that zero IS the finding". Unlike + # names/ids above, this stays an exact pin deliberately. + ($strength | Where-Object { $_.Name -eq 'context_names' }).CmdletCount | Should -Be 0 + } + + It 'the top-10 most-common field names absorb between 30% and 55% of total missing-field (endpoint, name) pairs' { + if (-not $hasRealData) { Set-ItResult -Skipped -Because 'Data/PfbCapabilityMap.json not present locally'; return } + $pairCounts = $realSystemicGaps2 | ForEach-Object { $_.QueryEndpointCount + $_.BodyEndpointCount } + $totalPairs = ($pairCounts | Measure-Object -Sum).Sum + $top10Sum = (($realSystemicGaps2 | ForEach-Object { [PSCustomObject]@{ Name = $_.Name; Pairs = $_.QueryEndpointCount + $_.BodyEndpointCount } }) | + Sort-Object Pairs -Descending | Select-Object -First 10 | Measure-Object -Property Pairs -Sum).Sum + $ratio = $top10Sum / $totalPairs + Write-Host "Task 6 real-data verification: top-10 aggregation ratio = $top10Sum / $totalPairs = $([Math]::Round($ratio * 100, 2))%" + # Not bit-for-bit pinned (Task 4/5 changed some list membership per this task's own + # brief) -- just confirms the aggregation actually matters. Historical note: the + # independently-measured ratio AT THE TIME these bounds were chosen was ~41.7% + # (576/1302 pairs); kept here as institutional memory for why 30%/55% were picked, + # not as an expectation this should still measure exactly that today. + $ratio | Should -BeGreaterThan 0.30 + $ratio | Should -BeLessThan 0.55 + } +} + +Describe 'Get-PfbResponseShapeFindings' { + BeforeAll { + $script:fixtureMap = [PSCustomObject]@{ + schemaVersion = 1 + generatedFrom = @('2.0', '2.1', '2.2') + endpoints = [PSCustomObject]@{ + 'GET /widgets' = [PSCustomObject]@{ + minVersion = '2.0' + lastSeenVersion = '2.2' + responseEnvelope = [PSCustomObject]@{ items = '2.0'; errors = '2.1'; total_item_count = '2.0' } + responseItemProperties = [PSCustomObject]@{ name = '2.0'; widget_name = '2.2' } + removedResponseFields = @( + [PSCustomObject]@{ field = 'wname'; location = 'items'; introducedVersion = '2.0'; lastSeenVersion = '2.1' } + ) + } + 'GET /gadgets' = [PSCustomObject]@{ + minVersion = '2.0' + lastSeenVersion = '2.2' + responseEnvelope = [PSCustomObject]@{ items = '2.0'; errors = '2.1' } + responseItemProperties = [PSCustomObject]@{ name = '2.0' } + } + 'POST /keytabs/upload' = [PSCustomObject]@{ + minVersion = '2.0' + lastSeenVersion = '2.2' + responseEnvelope = [PSCustomObject]@{} + responseItemProperties = [PSCustomObject]@{} + } + } + } + + $script:handlerPath = Join-Path $TestDrive 'Invoke-PfbApiRequest.ps1' + @' +if ($null -ne $response.items) { $allItems.Add($response.items) } +if ($null -ne $response.total_item_count) { $totalItemCount = $response.total_item_count } +if ($response.continuation_token) { $more = $true } +'@ | Set-Content $script:handlerPath + } + + It 'flattens removals one record per endpoint and field' { + $f = Get-PfbResponseShapeFindings -ResponseShapeMap $script:fixtureMap -RequestHandlerPath $script:handlerPath + @($f.Removals).Count | Should -Be 1 + $f.Removals[0].Endpoint | Should -Be 'GET /widgets' + $f.Removals[0].Field | Should -Be 'wname' + $f.Removals[0].LastSeenVersion | Should -Be '2.1' + } + + It 'pairs a removal with a same-version addition as a rename CANDIDATE' { + $f = Get-PfbResponseShapeFindings -ResponseShapeMap $script:fixtureMap -RequestHandlerPath $script:handlerPath + @($f.RenameCandidates).Count | Should -Be 1 + $f.RenameCandidates[0].From | Should -Be 'wname' + $f.RenameCandidates[0].To | Should -Be 'widget_name' + $f.RenameCandidates[0].Endpoint | Should -Be 'GET /widgets' + } + + It 'reports errors as an envelope field the request handler never reads' { + $f = Get-PfbResponseShapeFindings -ResponseShapeMap $script:fixtureMap -RequestHandlerPath $script:handlerPath + $names = @($f.UnhandledEnvelopeFields | ForEach-Object { $_.Field }) + $names | Should -Contain 'errors' + $names | Should -Not -Contain 'items' + $names | Should -Not -Contain 'total_item_count' + } + + It 'counts how many endpoints declare each unhandled envelope field, with endpoint count not occurrence count' { + $f = Get-PfbResponseShapeFindings -ResponseShapeMap $script:fixtureMap -RequestHandlerPath $script:handlerPath + ($f.UnhandledEnvelopeFields | Where-Object { $_.Field -eq 'errors' }).EndpointCount | Should -Be 2 + } + + It 'sorts removals by Endpoint/Location/Field independent of JSON key order' { + # Removals declared in non-alphabetical order on GET /apple + $map = [PSCustomObject]@{ + generatedFrom = @('2.0', '2.1') + endpoints = [PSCustomObject]@{ + 'GET /apple' = [PSCustomObject]@{ + minVersion = '2.0' + lastSeenVersion = '2.1' + responseEnvelope = [PSCustomObject]@{ items = '2.0' } + responseItemProperties = [PSCustomObject]@{} + removedResponseFields = @( + [PSCustomObject]@{ field = 'yankee'; location = 'items'; introducedVersion = '2.0'; lastSeenVersion = '2.1' } + [PSCustomObject]@{ field = 'alpha'; location = 'items'; introducedVersion = '2.0'; lastSeenVersion = '2.1' } + ) + } + } + } + $handler = Join-Path $TestDrive 'Invoke-PfbApiRequest-removals.ps1' + @' +if ($null -ne $response.items) { $allItems.Add($response.items) } +'@ | Set-Content $handler + $f = Get-PfbResponseShapeFindings -ResponseShapeMap $map -RequestHandlerPath $handler + # Natural emission order from removedResponseFields array: [yankee, alpha] + # Sorted order by Endpoint, Location, Field: [alpha, yankee] + @($f.Removals).Count | Should -Be 2 + $f.Removals[0].Field | Should -Be 'alpha' + $f.Removals[1].Field | Should -Be 'yankee' + } + + It 'sorts rename candidates by Endpoint/Location/From independent of removal declaration order' { + # Three candidates whose NATURAL emission order (removedResponseFields declaration + # order) contradicts the sorted order on BOTH remaining sort keys. The Endpoint key + # cannot be defeated by a fixture -- the endpoint loop itself already iterates + # $endpointNames sorted -- so this fixture puts all three on one endpoint and + # exercises Location (envelope < items) and From (alpha < zulu) instead. + # + # Each removal is given a DISTINCT successor version, and each bag holds exactly one + # field at that version, so every removal yields exactly one candidate rather than a + # cross-product of every same-version field in the bag. + # zulu (items) lastSeen 2.0 -> successor 2.1 -> zulu_new (2.1) + # alpha (items) lastSeen 2.1 -> successor 2.2 -> alpha_new (2.2) + # mike (envelope) lastSeen 2.2 -> successor 2.3 -> mike_new (2.3) + $map = [PSCustomObject]@{ + generatedFrom = @('2.0', '2.1', '2.2', '2.3') + endpoints = [PSCustomObject]@{ + 'POST /apple' = [PSCustomObject]@{ + minVersion = '2.0' + lastSeenVersion = '2.3' + responseEnvelope = [PSCustomObject]@{ items = '2.0'; mike_new = '2.3' } + responseItemProperties = [PSCustomObject]@{ zulu_new = '2.1'; alpha_new = '2.2' } + removedResponseFields = @( + [PSCustomObject]@{ field = 'zulu'; location = 'items'; introducedVersion = '2.0'; lastSeenVersion = '2.0' } + [PSCustomObject]@{ field = 'alpha'; location = 'items'; introducedVersion = '2.0'; lastSeenVersion = '2.1' } + [PSCustomObject]@{ field = 'mike'; location = 'envelope'; introducedVersion = '2.0'; lastSeenVersion = '2.2' } + ) + } + } + } + $handler = Join-Path $TestDrive 'Invoke-PfbApiRequest-candidates.ps1' + @' +if ($null -ne $response.items) { $allItems.Add($response.items) } +'@ | Set-Content $handler + $f = Get-PfbResponseShapeFindings -ResponseShapeMap $map -RequestHandlerPath $handler + # Natural emission order (declaration order): [zulu(items), alpha(items), mike(envelope)] + # Sorted by Endpoint, Location, From: [mike(envelope), alpha(items), zulu(items)] + @($f.RenameCandidates).Count | Should -Be 3 + @($f.RenameCandidates | ForEach-Object { "$($_.Location)/$($_.From)->$($_.To)" }) | Should -Be @( + 'envelope/mike->mike_new' + 'items/alpha->alpha_new' + 'items/zulu->zulu_new' + ) + } + + It 'sorts unhandled envelope fields by EndpointCount desc/Field asc with differing counts' { + # zulu appears on 2 endpoints, alpha and bravo on 1 each + # Correct sort: zulu (2), alpha (1), bravo (1) — count takes precedence + $map = [PSCustomObject]@{ + generatedFrom = @('2.0', '2.1') + endpoints = [PSCustomObject]@{ + 'GET /zebra' = [PSCustomObject]@{ + minVersion = '2.0' + lastSeenVersion = '2.1' + responseEnvelope = [PSCustomObject]@{ zulu = '2.0'; alpha = '2.0' } + responseItemProperties = [PSCustomObject]@{} + } + 'POST /apple' = [PSCustomObject]@{ + minVersion = '2.0' + lastSeenVersion = '2.1' + responseEnvelope = [PSCustomObject]@{ zulu = '2.0'; bravo = '2.0' } + responseItemProperties = [PSCustomObject]@{} + } + } + } + $handler = Join-Path $TestDrive 'Invoke-PfbApiRequest-envelope.ps1' + @' +if ($null -ne $response.items) { $allItems.Add($response.items) } +'@ | Set-Content $handler + $f = Get-PfbResponseShapeFindings -ResponseShapeMap $map -RequestHandlerPath $handler + # Alphabetical order alone: alpha (1), bravo (1), zulu (2) + # With EndpointCount desc: zulu (2), alpha (1), bravo (1) + @($f.UnhandledEnvelopeFields).Count | Should -Be 3 + $f.UnhandledEnvelopeFields[0].Field | Should -Be 'zulu' + $f.UnhandledEnvelopeFields[0].EndpointCount | Should -Be 2 + $f.UnhandledEnvelopeFields[1].Field | Should -Be 'alpha' + $f.UnhandledEnvelopeFields[1].EndpointCount | Should -Be 1 + $f.UnhandledEnvelopeFields[2].Field | Should -Be 'bravo' + $f.UnhandledEnvelopeFields[2].EndpointCount | Should -Be 1 + } + + It 'does not throw when an endpoint has an empty responseEnvelope' { + # This covers the null-handling fix for envelopes that are empty objects {} + $f = Get-PfbResponseShapeFindings -ResponseShapeMap $script:fixtureMap -RequestHandlerPath $script:handlerPath + $f | Should -Not -BeNullOrEmpty + @($f.Removals).Count | Should -Be 1 + } + + It 'does not treat an unrelated later addition as a rename' { + $map = [PSCustomObject]@{ + generatedFrom = @('2.0', '2.1', '2.2') + endpoints = [PSCustomObject]@{ + 'GET /widgets' = [PSCustomObject]@{ + minVersion = '2.0' + lastSeenVersion = '2.2' + responseEnvelope = [PSCustomObject]@{ items = '2.0' } + # introduced at 2.2, but the removal was last seen at 2.0 -> not adjacent + responseItemProperties = [PSCustomObject]@{ unrelated = '2.2' } + removedResponseFields = @( + [PSCustomObject]@{ field = 'gone'; location = 'items'; introducedVersion = '2.0'; lastSeenVersion = '2.0' } + ) + } + } + } + $f = Get-PfbResponseShapeFindings -ResponseShapeMap $map -RequestHandlerPath $script:handlerPath + @($f.RenameCandidates).Count | Should -Be 0 + } +} + +Describe 'Drift-report emit order is canonical, not filesystem or first-appearance order (issue #85)' { + BeforeAll { + # See the matching Describe in Tests/PfbCmdletParamTools.Tests.ps1 for why the + # cmdlet-to-filename mapping is swapped between two otherwise identical trees rather + # than running a generator twice on one machine. + $script:endpointOrderDirA = Join-Path $TestDrive 'EndpointOrderA/Public' + $script:endpointOrderDirB = Join-Path $TestDrive 'EndpointOrderB/Public' + $script:endpointOrderPrivate = Join-Path $TestDrive 'EndpointOrderPrivate' + New-Item -ItemType Directory -Path $script:endpointOrderDirA, $script:endpointOrderDirB, $script:endpointOrderPrivate -Force | Out-Null + + $zuluSource = @' +function Get-PfbFixtureOrderZulu { + [CmdletBinding()] + param([Parameter()] [PSCustomObject]$Array) + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'order-zulu' +} +'@ + $alphaSource = @' +function Get-PfbFixtureOrderAlpha { + [CmdletBinding()] + param([Parameter()] [PSCustomObject]$Array) + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'order-alpha' +} +'@ + Set-Content -Path (Join-Path $script:endpointOrderDirA '01-first.ps1') -Value $zuluSource + Set-Content -Path (Join-Path $script:endpointOrderDirA '02-second.ps1') -Value $alphaSource + Set-Content -Path (Join-Path $script:endpointOrderDirB '01-first.ps1') -Value $alphaSource + Set-Content -Path (Join-Path $script:endpointOrderDirB '02-second.ps1') -Value $zuluSource + + # A capability map carrying one still-missing query parameter on each of two + # endpoints, so both produce a gap row and their relative order is observable. + $script:orderCapabilityMap = [PSCustomObject]@{ + generatedFrom = @('2.0') + endpoints = [PSCustomObject]@{ + 'GET /order-zulu' = [PSCustomObject]@{ + minVersion = '2.0' + parameters = [PSCustomObject]@{ gap_field = '2.0' } + bodyProperties = [PSCustomObject]@{} + } + 'GET /order-alpha' = [PSCustomObject]@{ + minVersion = '2.0' + parameters = [PSCustomObject]@{ gap_field = '2.0' } + bodyProperties = [PSCustomObject]@{} + } + } + } + + $script:orderInventory = @( + [PSCustomObject]@{ Cmdlet = 'Get-PfbFixtureOrderZulu'; Parameter = 'Name'; Surface = 'Typed'; WireName = 'name'; HasValidateSet = $false; ValidateSetValues = $null; Endpoint = 'order-zulu'; Method = 'GET'; File = 'z.ps1'; Line = 1 } + [PSCustomObject]@{ Cmdlet = 'Get-PfbFixtureOrderAlpha'; Parameter = 'Name'; Surface = 'Typed'; WireName = 'name'; HasValidateSet = $false; ValidateSetValues = $null; Endpoint = 'order-alpha'; Method = 'GET'; File = 'a.ps1'; Line = 1 } + [PSCustomObject]@{ Cmdlet = 'Test-PfbFixtureOrderZulu'; Parameter = 'Name'; Surface = 'Typed'; WireName = 'name'; HasValidateSet = $false; ValidateSetValues = $null; Endpoint = 'order-zulu'; Method = 'GET'; File = 'tz.ps1'; Line = 1 } + ) + } + + It 'Get-PfbModuleCalledEndpoints emits the same sequence for two trees differing only in which file defines which cmdlet' { + $keysA = @(Get-PfbModuleCalledEndpoints -PublicDirectory $script:endpointOrderDirA -PrivateDirectory $script:endpointOrderPrivate | + ForEach-Object { '{0}|{1}' -f $_.Cmdlet, $_.Key }) + $keysB = @(Get-PfbModuleCalledEndpoints -PublicDirectory $script:endpointOrderDirB -PrivateDirectory $script:endpointOrderPrivate | + ForEach-Object { '{0}|{1}' -f $_.Cmdlet, $_.Key }) + + $keysA | Should -Be @('Get-PfbFixtureOrderAlpha|GET /order-alpha', 'Get-PfbFixtureOrderZulu|GET /order-zulu') + $keysA | Should -Be $keysB + } + + It 'Get-PfbParameterCoverageGaps orders gap rows by endpoint key, not by the order the endpoints were first called' { + # Zulu deliberately first in the input: Group-Object's group order is + # first-appearance order, so an unsorted implementation emits zulu before alpha. + $endpoints = @( + [PSCustomObject]@{ Key = 'GET /order-zulu'; Method = 'GET'; Endpoint = '/order-zulu'; Resolved = $true; Cmdlet = 'Get-PfbFixtureOrderZulu'; File = 'z.ps1' } + [PSCustomObject]@{ Key = 'GET /order-alpha'; Method = 'GET'; Endpoint = '/order-alpha'; Resolved = $true; Cmdlet = 'Get-PfbFixtureOrderAlpha'; File = 'a.ps1' } + ) + $result = Get-PfbParameterCoverageGaps -CapabilityMap $script:orderCapabilityMap -CmdletInventory $script:orderInventory -CalledEndpoints $endpoints + + @($result.Endpoint) | Should -Be @('GET /order-alpha', 'GET /order-zulu') + } + + It 'Get-PfbParameterCoverageGaps sorts the cmdlets attributed to a single endpoint' { + # Two cmdlets call the same endpoint, supplied in reverse-alphabetical order. + # Select-Object -Unique preserves input order and does not sort -- the observed + # intra-row flip in issue #85. + $endpoints = @( + [PSCustomObject]@{ Key = 'GET /order-zulu'; Method = 'GET'; Endpoint = '/order-zulu'; Resolved = $true; Cmdlet = 'Test-PfbFixtureOrderZulu'; File = 'tz.ps1' } + [PSCustomObject]@{ Key = 'GET /order-zulu'; Method = 'GET'; Endpoint = '/order-zulu'; Resolved = $true; Cmdlet = 'Get-PfbFixtureOrderZulu'; File = 'z.ps1' } + ) + $result = Get-PfbParameterCoverageGaps -CapabilityMap $script:orderCapabilityMap -CmdletInventory $script:orderInventory -CalledEndpoints $endpoints + + $gap = $result | Where-Object { $_.Endpoint -eq 'GET /order-zulu' } + @($gap.Cmdlets) | Should -Be @('Get-PfbFixtureOrderZulu', 'Test-PfbFixtureOrderZulu') + } +} diff --git a/Tests/PfbCmdletParamTools.Tests.ps1 b/Tests/PfbCmdletParamTools.Tests.ps1 new file mode 100644 index 0000000..d1b3c0f --- /dev/null +++ b/Tests/PfbCmdletParamTools.Tests.ps1 @@ -0,0 +1,1230 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } +<# +.SYNOPSIS + Unit tests for tools/lib/PfbCmdletParamTools.ps1 — the AST-based cmdlet parameter + inventory used by tools/Build-PfbFieldCmdletMap.ps1. +.DESCRIPTION + Runs against a small synthetic Public/-shaped directory under TestDrive, built from + real patterns observed in this repo's actual cmdlets (New-PfbAlertWatcher's simple + $body['wire_name'] = $Param assignment, New-PfbNetworkInterface's -Attributes escape + hatch and its unresolvable $AttachedServers | ForEach-Object {...} pipeline, and + Get-PfbArrayPerformance's $queryParams assignment) — no dependency on the real Public/ + tree so the test stays stable if cmdlets change. +#> + +BeforeAll { + $repoRoot = Split-Path -Parent $PSScriptRoot + . (Join-Path $repoRoot 'tools/lib/PfbCmdletParamTools.ps1') + + $script:fixtureDir = Join-Path $TestDrive 'Public/Fixture' + New-Item -ItemType Directory -Path $fixtureDir -Force | Out-Null + + Set-Content -Path (Join-Path $fixtureDir 'New-PfbFixtureAlertWatcher.ps1') -Value @' +function New-PfbFixtureAlertWatcher { + [CmdletBinding(SupportsShouldProcess)] + param( + [Parameter(Mandatory, Position = 0)] + [string]$Name, + + [Parameter()] + [ValidateSet('info', 'warning', 'critical')] + [string]$MinimumSeverity, + + [Parameter()] [hashtable]$Attributes, + [Parameter()] [PSCustomObject]$Array + ) + + if ($Attributes) { $body = $Attributes } + else { + $body = @{} + if ($MinimumSeverity) { $body['minimum_notification_severity'] = $MinimumSeverity } + } +} +'@ + + Set-Content -Path (Join-Path $fixtureDir 'New-PfbFixtureNetworkInterface.ps1') -Value @' +function New-PfbFixtureNetworkInterface { + [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "Individual")] + param( + [Parameter(Mandatory, Position = 0)] + [string]$Name, + + [Parameter(ParameterSetName = "Individual")] + [ValidateSet("data", "egress-only", "management", "replication", "support")] + [string[]]$Services, + + [Parameter(ParameterSetName = "Individual")] + [string[]]$AttachedServers, + + [Parameter(Mandatory, ParameterSetName = "Attributes")] + [hashtable]$Attributes, + + [Parameter()] [PSCustomObject]$Array + ) + + if ($PSCmdlet.ParameterSetName -eq "Attributes") { + $body = $Attributes + } + else { + $body = @{} + if ($Services) { $body["services"] = @($Services) } + if ($AttachedServers) { + $body["attached_servers"] = @($AttachedServers | ForEach-Object { @{ name = $_ } }) + } + } +} +'@ + + Set-Content -Path (Join-Path $fixtureDir 'Get-PfbFixtureArrayPerformance.ps1') -Value @' +function Get-PfbFixtureArrayPerformance { + [CmdletBinding()] + param( + [Parameter()] [PSCustomObject]$Array, + + [Parameter()] + [string]$Protocol, + + [Parameter()] + [int64]$Resolution, + + [Parameter()] + [datetime]$StartTime + ) + + $queryParams = @{} + if ($Protocol) { $queryParams["protocol"] = $Protocol } + if ($Resolution) { $queryParams["resolution"] = $Resolution } + # Deliberately NOT a simple "$queryParams[key] = $Param" assignment -- string + # interpolation is a real pattern this repo does not currently use, but the resolver + # must not guess through it. No -Attributes escape hatch exists on this cmdlet either, + # so this must surface as TypedUnresolved, not silently dropped or force-matched. + if ($StartTime) { $queryParams["start_time"] = "$StartTime" } + + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'arrays/performance' -QueryParams $queryParams -AutoPaginate +} +'@ + + # Real Get-PfbArraySpace shape: exactly one Invoke-PfbApiRequest call, so -Type's + # $queryParams assignment resolves to exactly one (Method, Endpoint) pair. + Set-Content -Path (Join-Path $fixtureDir 'Get-PfbFixtureArraySpace.ps1') -Value @' +function Get-PfbFixtureArraySpace { + [CmdletBinding()] + param( + [Parameter()] [PSCustomObject]$Array, + [Parameter()] [string]$Type + ) + $queryParams = @{} + if ($Type) { $queryParams['type'] = $Type } + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'arrays/space' -QueryParams $queryParams -AutoPaginate +} +'@ + + # Real Get-PfbNode shape: the SAME $queryParams variable is reused across two calls + # against two genuinely different endpoints (a try/catch model-support fallback) -- + # must resolve to $null, not a guessed pick of either endpoint. + Set-Content -Path (Join-Path $fixtureDir 'Get-PfbFixtureNode.ps1') -Value @' +function Get-PfbFixtureNode { + [CmdletBinding()] + param( + [Parameter()] [PSCustomObject]$Array, + [Parameter()] [string]$Filter + ) + $queryParams = @{} + if ($Filter) { $queryParams['filter'] = $Filter } + try { + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'nodes' -QueryParams $queryParams -AutoPaginate + } catch { + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'blades' -QueryParams $queryParams -AutoPaginate + } +} +'@ + + # Real Get-PfbPolicyAllMember shape: a plural wire name built by joining a string-array + # parameter, not assigning it directly or wrapping it in @(...). + Set-Content -Path (Join-Path $fixtureDir 'Get-PfbFixturePolicyAllMember.ps1') -Value @' +function Get-PfbFixturePolicyAllMember { + [CmdletBinding()] + param( + [Parameter()] [PSCustomObject]$Array, + [Parameter()] [string[]]$MemberName + ) + $queryParams = @{} + if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'policies/members' -QueryParams $queryParams -AutoPaginate +} +'@ + + # Real Get-PfbFileSystemSession shape: a switch's mere presence is keyed to a + # hardcoded string literal, not derived from the switch's own value at all. + Set-Content -Path (Join-Path $fixtureDir 'Get-PfbFixtureFileSystemSession.ps1') -Value @' +function Get-PfbFixtureFileSystemSession { + [CmdletBinding()] + param( + [Parameter()] [PSCustomObject]$Array, + [Parameter()] [switch]$TotalOnly + ) + $queryParams = @{} + if ($TotalOnly) { $queryParams['total_only'] = 'true' } + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-system-sessions' -QueryParams $queryParams -AutoPaginate +} +'@ + + # Real cross-file idiom (130/130 files that use it at all, byte-for-byte identical): + # accumulate into a list across `process`, then join it into the wire name in `end`. + Set-Content -Path (Join-Path $fixtureDir 'Get-PfbFixtureFileSystemByName.ps1') -Value @' +function Get-PfbFixtureFileSystemByName { + [CmdletBinding()] + param( + [Parameter()] [PSCustomObject]$Array, + [Parameter(ValueFromPipeline)] [string[]]$Name + ) + begin { + $allNames = [System.Collections.Generic.List[string]]::new() + $queryParams = @{} + } + process { + if ($Name) { + foreach ($n in $Name) { + $allNames.Add($n) + } + } + } + end { + if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-systems' -QueryParams $queryParams -AutoPaginate + } +} +'@ + + # Ambiguous-accumulator case: the SAME accumulator is fed by two different parameters' + # foreach loops -- must bail to TypedUnresolved for both, never guess which one "owns" + # the eventual wire name. + Set-Content -Path (Join-Path $fixtureDir 'Get-PfbFixtureSharedAccumulator.ps1') -Value @' +function Get-PfbFixtureSharedAccumulator { + [CmdletBinding()] + param( + [Parameter()] [PSCustomObject]$Array, + [Parameter()] [string[]]$FirstNames, + [Parameter()] [string[]]$SecondNames + ) + $allNames = [System.Collections.Generic.List[string]]::new() + $queryParams = @{} + foreach ($n in $FirstNames) { $allNames.Add($n) } + foreach ($n in $SecondNames) { $allNames.Add($n) } + if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'shared' -QueryParams $queryParams -AutoPaginate +} +'@ + + # --- Add-PfbCommonQueryParams (issue #32/#33) fixtures ----------------------------- + # Real Get-PfbBucketPerformance shape: -Name/-Id handed straight to the shared helper, + # and -Filter/-Sort/-Limit/-TotalOnly reaching the wire only via $PSBoundParameters. + Set-Content -Path (Join-Path $fixtureDir 'Get-PfbFixtureHelperDirect.ps1') -Value @' +function Get-PfbFixtureHelperDirect { + [CmdletBinding()] + param( + [Parameter()] [PSCustomObject]$Array, + [Parameter()] [string[]]$Name, + [Parameter()] [string[]]$Id, + [Parameter()] [string]$Filter, + [Parameter()] [string]$Sort, + [Parameter()] [int]$Limit, + [Parameter()] [switch]$TotalOnly + ) + $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $Name -Ids $Id + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'helper-direct' -QueryParams $queryParams -AutoPaginate +} +'@ + + # Real Get-PfbFileSystem shape: the helper receives the `process`-block accumulators, + # not the parameters -- so resolution must go parameter -> accumulator -> helper argument. + Set-Content -Path (Join-Path $fixtureDir 'Get-PfbFixtureHelperAccumulator.ps1') -Value @' +function Get-PfbFixtureHelperAccumulator { + [CmdletBinding()] + param( + [Parameter()] [PSCustomObject]$Array, + [Parameter(ValueFromPipeline)] [string[]]$Name, + [Parameter()] [string[]]$Id + ) + begin { + $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 = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames -Ids $allIds + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'helper-accumulator' -QueryParams $queryParams -AutoPaginate + } +} +'@ + + # Real Get-PfbObjectStoreAccessPolicyRule shape: the genuinely MIXED case. -Name goes + # through the helper's generic 'names', while -PolicyName deliberately kept its own + # explicit non-generic 'policy_names' line after the helper call (issue #32's design). + # Both must resolve, and the explicit line must not be shadowed by the helper. + Set-Content -Path (Join-Path $fixtureDir 'Get-PfbFixtureHelperMixed.ps1') -Value @' +function Get-PfbFixtureHelperMixed { + [CmdletBinding()] + param( + [Parameter()] [PSCustomObject]$Array, + [Parameter(ValueFromPipeline)] [string[]]$PolicyName, + [Parameter()] [string[]]$Name, + [Parameter()] [string]$Filter + ) + begin { + $allPolicyNames = [System.Collections.Generic.List[string]]::new() + $allNames = [System.Collections.Generic.List[string]]::new() + } + process { + if ($PolicyName) { foreach ($n in $PolicyName) { $allPolicyNames.Add($n) } } + if ($Name) { foreach ($n in $Name) { $allNames.Add($n) } } + } + end { + $queryParams = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters -Names $allNames + if ($allPolicyNames.Count -gt 0) { $queryParams['policy_names'] = $allPolicyNames -join ',' } + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'helper-mixed' -QueryParams $queryParams -AutoPaginate + } +} +'@ + + # Negative guard: the ByParameterName half of the mapping is only true because the helper + # reads the CALLER's $PSBoundParameters. A call that does not forward it cannot be assumed + # to map -Filter, so -Filter must stay unresolved rather than be credited to 'filter'. + Set-Content -Path (Join-Path $fixtureDir 'Get-PfbFixtureHelperNoBoundParams.ps1') -Value @' +function Get-PfbFixtureHelperNoBoundParams { + [CmdletBinding()] + param( + [Parameter()] [PSCustomObject]$Array, + [Parameter()] [string]$Filter + ) + $queryParams = @{} + $someOtherDictionary = @{} + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $someOtherDictionary + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'helper-no-bound' -QueryParams $queryParams -AutoPaginate +} +'@ + + # --- Hashtable-literal-initializer fixtures --------------------------------------- + # Real New-PfbApiClient/New-PfbObjectStoreAccount shape: the wire key exists ONLY inside + # a hashtable literal, never as a later $queryParams['names'] = ... index assignment. + Set-Content -Path (Join-Path $fixtureDir 'New-PfbFixtureLiteralOnly.ps1') -Value @' +function New-PfbFixtureLiteralOnly { + [CmdletBinding()] + param( + [Parameter(Position = 0)] [string]$Name, + [Parameter()] [PSCustomObject]$Array + ) + $queryParams = @{ 'names' = $Name } + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'literal-only' -QueryParams $queryParams +} +'@ + + # Real New-PfbBucket/New-PfbFileSystem shape: a literal initializer AND later index + # assignments into $body coexist in one cmdlet, against two different target variables. + # Both key sets must resolve, each to its own target variable. + Set-Content -Path (Join-Path $fixtureDir 'New-PfbFixtureLiteralMixed.ps1') -Value @' +function New-PfbFixtureLiteralMixed { + [CmdletBinding()] + param( + [Parameter(Position = 0)] [string]$Name, + [Parameter()] [string]$NewName, + [Parameter()] [string]$Hostname, + [Parameter()] [PSCustomObject]$Array + ) + $queryParams = @{ 'names' = $Name } + $body = @{ 'name' = $NewName } + if ($Hostname) { $body['host_name'] = $Hostname } + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'literal-mixed' -Body $body -QueryParams $queryParams +} +'@ + + # Real New-PfbWorkload/Remove-PfbWorkloadTag shape: the literal's value is an EXPRESSION + # wrapping the parameter (@(...) or -join), not a bare variable reference. + Set-Content -Path (Join-Path $fixtureDir 'New-PfbFixtureLiteralWrapped.ps1') -Value @' +function New-PfbFixtureLiteralWrapped { + [CmdletBinding()] + param( + [Parameter()] [string[]]$Name, + [Parameter()] [string[]]$Key, + [Parameter()] [PSCustomObject]$Array + ) + $queryParams = @{ 'names' = @($Name); 'keys' = $Key -join ',' } + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'literal-wrapped' -QueryParams $queryParams +} +'@ + + # Real New-PfbQuotaGroup shape: a NESTED single-key sub-object inside a hashtable-literal + # initializer. The wire field is the OUTER key ('group') -- never the inner 'name', which + # would both mis-name the field and collide with every other sub-object's 'name'. + Set-Content -Path (Join-Path $fixtureDir 'New-PfbFixtureNestedLiteral.ps1') -Value @' +function New-PfbFixtureNestedLiteral { + [CmdletBinding()] + param( + [Parameter()] [string]$GroupName, + [Parameter()] [int64]$Quota, + [Parameter()] [PSCustomObject]$Array + ) + $body = @{ group = @{ name = $GroupName }; quota = $Quota } + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'nested-literal' -Body $body +} +'@ + + # Real New-PfbBucket/New-PfbFileSystem/New-PfbServer shape: a nested single-key reference + # object keyed in by INDEX assignment. Covers, in order: the plain reference object; two + # parameters legitimately resolving to the SAME outer key ('account' addressed by name and + # by id -- correct, not a collision to suppress); a non-'name' inner key (real + # New-PfbFileSystem: eradication_config = @{ eradication_mode = ... }); a plain sibling key + # that must keep resolving directly; a MULTI-key sub-object, whose per-field ownership + # cannot be attributed to one parameter; and two levels of nesting, which is not descended. + Set-Content -Path (Join-Path $fixtureDir 'New-PfbFixtureNestedReference.ps1') -Value @' +function New-PfbFixtureNestedReference { + [CmdletBinding()] + param( + [Parameter()] [string]$Account, + [Parameter()] [string]$AccountId, + [Parameter()] [string]$EradicationMode, + [Parameter()] [string]$Versioning, + [Parameter()] [string]$SourceName, + [Parameter()] [string]$SourceId, + [Parameter()] [string]$Deep, + [Parameter()] [PSCustomObject]$Array + ) + $body = @{} + if ($Account) { $body['account'] = @{ name = $Account } } + if ($AccountId) { $body['account'] = @{ id = $AccountId } } + if ($EradicationMode) { $body['eradication_config'] = @{ eradication_mode = $EradicationMode } } + if ($Versioning) { $body['versioning'] = $Versioning } + if ($SourceName -or $SourceId) { $body['source'] = @{ name = $SourceName; id = $SourceId } } + if ($Deep) { $body['outer'] = @{ middle = @{ name = $Deep } } } + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'nested-reference' -Body $body +} +'@ + + $script:helperPath = Join-Path $repoRoot 'Private/Add-PfbCommonQueryParams.ps1' + $script:inventory = Get-PfbCmdletParameterInventory -PublicDirectory $fixtureDir +} + +Describe 'Get-PfbCmdletParameterInventory' { + It 'always skips the Array parameter' { + $inventory | Where-Object { $_.Parameter -eq 'Array' } | Should -BeNullOrEmpty + } + + It 'always skips the Attributes parameter itself' { + $inventory | Where-Object { $_.Parameter -eq 'Attributes' } | Should -BeNullOrEmpty + } + + It 'records an existing ValidateSet and marks HasValidateSet true' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureAlertWatcher' -and $_.Parameter -eq 'MinimumSeverity' } + $rec.HasValidateSet | Should -BeTrue + $rec.ValidateSetValues | Should -Be @('info', 'warning', 'critical') + } + + It 'resolves a simple $body[wire_name] = $Param assignment' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureAlertWatcher' -and $_.Parameter -eq 'MinimumSeverity' } + $rec.WireName | Should -Be 'minimum_notification_severity' + $rec.Surface | Should -Be 'Typed' + } + + It 'carries each parameter''s own declaration line ($p.Extent.StartLineNumber), alongside its File, so a caveat is a click-through' { + # New-PfbFixtureAlertWatcher.ps1 is written verbatim from the here-string above (see + # BeforeAll): line 1 is 'function ...', and -MinimumSeverity's own declaration -- + # attributes included, since ParameterAst.Extent spans the whole parameter, not just + # the bare variable -- starts at line 7 ('[Parameter()]'). + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureAlertWatcher' -and $_.Parameter -eq 'MinimumSeverity' } + $rec.File | Should -Be (Join-Path $fixtureDir 'New-PfbFixtureAlertWatcher.ps1') + $rec.Line | Should -Be 7 + } + + It 'resolves a simple $queryParams[wire_name] = $Param assignment' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureArrayPerformance' -and $_.Parameter -eq 'Protocol' } + $rec.WireName | Should -Be 'protocol' + $rec.Surface | Should -Be 'Typed' + } + + It 'resolves an array parameter wrapped in @(...)' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureNetworkInterface' -and $_.Parameter -eq 'Services' } + $rec.WireName | Should -Be 'services' + $rec.Surface | Should -Be 'Typed' + } + + It 'resolves a parameter fed through an array projection to its OUTER key' { + # $AttachedServers is assigned via `@($AttachedServers | ForEach-Object { @{ name = $_ } })`. + # PR #60 deliberately refused this shape rather than guess; the projection resolver now + # credits it with the outer key (`attached_servers`), which is the only name the + # capability map knows -- there is no `attached_servers.name` field in it. + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureNetworkInterface' -and $_.Parameter -eq 'AttachedServers' } + $rec.WireName | Should -Be 'attached_servers' + $rec.Surface | Should -Be 'Typed' + } + + It 'resolves a parameter with no -Attributes escape hatch via a simple assignment' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureArrayPerformance' -and $_.Parameter -eq 'Resolution' } + $rec.WireName | Should -Be 'resolution' + $rec.Surface | Should -Be 'Typed' + } + + It 'classifies a parameter with no -Attributes escape hatch and no resolvable assignment as TypedUnresolved' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureArrayPerformance' -and $_.Parameter -eq 'StartTime' } + $rec.WireName | Should -BeNullOrEmpty + $rec.Surface | Should -Be 'TypedUnresolved' + } + + It 'resolves a parameter joined into a plural wire name via -join' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixturePolicyAllMember' -and $_.Parameter -eq 'MemberName' } + $rec.WireName | Should -Be 'member_names' + $rec.Surface | Should -Be 'Typed' + } + + It 'resolves a [switch] parameter keyed to a hardcoded literal, guarded by if ($Param)' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureFileSystemSession' -and $_.Parameter -eq 'TotalOnly' } + $rec.WireName | Should -Be 'total_only' + $rec.Surface | Should -Be 'Typed' + } + + It 'resolves a parameter traced through a foreach-accumulator-then-join pipeline' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureFileSystemByName' -and $_.Parameter -eq 'Name' } + $rec.WireName | Should -Be 'names' + $rec.Surface | Should -Be 'Typed' + } + + It 'bails to TypedUnresolved when an accumulator is fed by more than one parameter (never guesses ownership)' { + $first = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureSharedAccumulator' -and $_.Parameter -eq 'FirstNames' } + $second = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureSharedAccumulator' -and $_.Parameter -eq 'SecondNames' } + $first.WireName | Should -BeNullOrEmpty + $first.Surface | Should -Be 'TypedUnresolved' + $second.WireName | Should -BeNullOrEmpty + $second.Surface | Should -Be 'TypedUnresolved' + } +} + +Describe 'Get-PfbWireNameForParameter' { + It 'returns $null when the parameter name never appears on the right-hand side of a body/queryParams assignment' { + $tokens = $null; $errs = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput( + 'function Test-Fixture { param([string]$Unused) $body = @{} }', [ref]$tokens, [ref]$errs) + $funcAst = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Select-Object -First 1 + Get-PfbWireNameForParameter -FunctionAst $funcAst -ParameterName 'Unused' | Should -BeNullOrEmpty + } +} + +Describe 'Get-PfbWireNameForParameter: switch-to-literal pattern' { + It 'does NOT treat an unguarded literal assignment as switch-derived (false-positive guard)' { + $tokens = $null; $errs = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput( + 'function Test-Fixture { param([switch]$Foo) $body = @{}; $body["bar"] = "literal" }', [ref]$tokens, [ref]$errs) + $funcAst = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Select-Object -First 1 + Get-PfbWireNameForParameter -FunctionAst $funcAst -ParameterName 'Foo' -IsSwitchParameter | Should -BeNullOrEmpty + } + + It 'does NOT apply the switch-literal match when -IsSwitchParameter is not passed' { + $tokens = $null; $errs = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput( + 'function Test-Fixture { param([switch]$Foo) if ($Foo) { $body["bar"] = "literal" } }', [ref]$tokens, [ref]$errs) + $funcAst = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Select-Object -First 1 + Get-PfbWireNameForParameter -FunctionAst $funcAst -ParameterName 'Foo' | Should -BeNullOrEmpty + } +} + +Describe 'Endpoint/Method resolution (Get-PfbEndpointForVariable, via the inventory)' { + It 'resolves Endpoint/Method for a parameter whose variable feeds exactly one Invoke-PfbApiRequest call' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureArraySpace' -and $_.Parameter -eq 'Type' } + $rec.Endpoint | Should -Be 'arrays/space' + $rec.Method | Should -Be 'GET' + } + + It 'leaves Endpoint/Method $null when the same variable feeds two calls with different endpoints (ambiguous, never guessed)' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureNode' -and $_.Parameter -eq 'Filter' } + $rec.Endpoint | Should -BeNullOrEmpty + $rec.Method | Should -BeNullOrEmpty + } + + It 'leaves Endpoint/Method $null when there is no resolvable wire-name assignment at all' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureArrayPerformance' -and $_.Parameter -eq 'StartTime' } + $rec.Endpoint | Should -BeNullOrEmpty + $rec.Method | Should -BeNullOrEmpty + } + + It 'directly returns $null from Get-PfbEndpointForVariable for a variable with zero matching Invoke-PfbApiRequest calls' { + $tokens = $null; $errs = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput( + 'function Test-Fixture { param([string]$Unused) $queryParams = @{} }', [ref]$tokens, [ref]$errs) + $funcAst = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Select-Object -First 1 + Get-PfbEndpointForVariable -FunctionAst $funcAst -TargetVariable 'queryParams' | Should -BeNullOrEmpty + } +} + +Describe 'Add-PfbCommonQueryParams awareness (issue #32/#33)' { + It 'resolves -Name/-Id handed straight to the helper as names/ids' { + $name = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureHelperDirect' -and $_.Parameter -eq 'Name' } + $name.WireName | Should -Be 'names' + $name.Surface | Should -Be 'Typed' + $id = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureHelperDirect' -and $_.Parameter -eq 'Id' } + $id.WireName | Should -Be 'ids' + $id.Surface | Should -Be 'Typed' + } + + It 'resolves -Filter/-Sort/-Limit/-TotalOnly by PARAMETER NAME, since the helper reads them from $PSBoundParameters' -ForEach @( + @{ Parameter = 'Filter'; WireName = 'filter' } + @{ Parameter = 'Sort'; WireName = 'sort' } + @{ Parameter = 'Limit'; WireName = 'limit' } + @{ Parameter = 'TotalOnly'; WireName = 'total_only' } + ) { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureHelperDirect' -and $_.Parameter -eq $Parameter } + $rec.WireName | Should -Be $WireName + $rec.Surface | Should -Be 'Typed' + } + + It 'still resolves the -Into variable to its Invoke-PfbApiRequest endpoint' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureHelperDirect' -and $_.Parameter -eq 'Filter' } + $rec.Endpoint | Should -Be 'helper-direct' + $rec.Method | Should -Be 'GET' + } + + It 'resolves the accumulator pattern (-Names $allNames where $allNames builds from $Name)' { + $name = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureHelperAccumulator' -and $_.Parameter -eq 'Name' } + $name.WireName | Should -Be 'names' + $name.Surface | Should -Be 'Typed' + $name.Endpoint | Should -Be 'helper-accumulator' + $id = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureHelperAccumulator' -and $_.Parameter -eq 'Id' } + $id.WireName | Should -Be 'ids' + $id.Surface | Should -Be 'Typed' + } + + It 'resolves a mixed cmdlet: the helper-routed generic param AND its own explicit non-generic line' { + $policy = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureHelperMixed' -and $_.Parameter -eq 'PolicyName' } + $policy.WireName | Should -Be 'policy_names' + $policy.Surface | Should -Be 'Typed' + $name = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureHelperMixed' -and $_.Parameter -eq 'Name' } + $name.WireName | Should -Be 'names' + $name.Surface | Should -Be 'Typed' + $filter = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureHelperMixed' -and $_.Parameter -eq 'Filter' } + $filter.WireName | Should -Be 'filter' + } + + It 'does NOT credit -Filter when the call does not forward $PSBoundParameters' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'Get-PfbFixtureHelperNoBoundParams' -and $_.Parameter -eq 'Filter' } + $rec.WireName | Should -BeNullOrEmpty + $rec.Surface | Should -Be 'TypedUnresolved' + } + + It 'returns $null from Get-PfbCommonQueryParamHelperWireName when -Into is not a plain variable' { + $tokens = $null; $errs = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput( + 'function Test-Fixture { param([string]$Filter) Add-PfbCommonQueryParams -Into @{} -BoundParameters $PSBoundParameters }', [ref]$tokens, [ref]$errs) + $funcAst = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Select-Object -First 1 + Get-PfbCommonQueryParamHelperWireName -FunctionAst $funcAst -ParameterName 'Filter' | Should -BeNullOrEmpty + } + + It 'returns $null from Get-PfbCommonQueryParamHelperWireName when the function never calls the helper' { + $tokens = $null; $errs = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput( + 'function Test-Fixture { param([string]$Filter) $queryParams = @{} }', [ref]$tokens, [ref]$errs) + $funcAst = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Select-Object -First 1 + Get-PfbCommonQueryParamHelperWireName -FunctionAst $funcAst -ParameterName 'Filter' | Should -BeNullOrEmpty + } +} + +Describe 'Get-PfbCommonQueryParamMap stays in sync with Private/Add-PfbCommonQueryParams.ps1' { + # Guards the one hazard of hardcoding the mapping: the helper gains, loses, or renames a + # key and this tools/ mirror silently keeps reporting the old contract. Derives the truth + # from the helper's own AST and compares, so drift fails the build instead of quietly + # dropping endpoints back out of gap analysis. + BeforeAll { + $script:derivedByParameterName = @{} + $script:derivedByHelperArgument = @{} + $script:derivedHelperName = $null + + $tokens = $null; $errs = $null + $helperAst = [System.Management.Automation.Language.Parser]::ParseFile($helperPath, [ref]$tokens, [ref]$errs) + $script:derivedHelperName = ($helperAst.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | + Select-Object -First 1).Name + + $assignments = @($helperAst.FindAll({ + param($n) + $n -is [System.Management.Automation.Language.AssignmentStatementAst] -and + $n.Left -is [System.Management.Automation.Language.IndexExpressionAst] + }, $true)) | Where-Object { + $t = $_.Left.Target -as [System.Management.Automation.Language.VariableExpressionAst] + $t -and $t.VariablePath.UserPath -eq 'Into' + } + + foreach ($assign in $assignments) { + $wireKey = ($assign.Left.Index -as [System.Management.Automation.Language.StringConstantExpressionAst]).Value + + # Every assignment in the helper sits inside a one-clause `if`; the clause's + # condition is what says WHERE the value came from. + $node = $assign.Parent + while ($node -and $node -isnot [System.Management.Automation.Language.IfStatementAst]) { $node = $node.Parent } + $condition = '' + if ($node) { + foreach ($clause in $node.Clauses) { + if (@($clause.Item2.FindAll({ param($n) $n -eq $assign }, $true)).Count -gt 0) { + $condition = $clause.Item1.Extent.Text.Trim() + } + } + } + + if ($condition -match '^\$BoundParameters\.ContainsKey\((?:''|")(\w+)(?:''|")\)$') { + $derivedByParameterName[$Matches[1]] = $wireKey + } + elseif ($condition -match '^\$(\w+)$') { + $derivedByHelperArgument[$Matches[1]] = $wireKey + } + else { + throw "Unrecognized guard shape around `$Into['$wireKey'] in $helperPath : '$condition'. Get-PfbCommonQueryParamMap's detection rules may no longer describe this helper." + } + } + } + + It 'reads a non-empty mapping out of the real helper (guards against a vacuous pass)' { + $derivedByParameterName.Count | Should -BeGreaterThan 0 + $derivedByHelperArgument.Count | Should -BeGreaterThan 0 + } + + It 'names the same helper the real file defines' { + (Get-PfbCommonQueryParamMap).HelperName | Should -Be $derivedHelperName + } + + It 'mirrors the helper $PSBoundParameters-driven keys exactly' { + $map = Get-PfbCommonQueryParamMap + @($map.ByParameterName.Keys) | Sort-Object | Should -Be (@($derivedByParameterName.Keys) | Sort-Object) + foreach ($k in $derivedByParameterName.Keys) { + $map.ByParameterName[$k] | Should -Be $derivedByParameterName[$k] -Because "the helper assigns `$Into['$($derivedByParameterName[$k])'] for -$k" + } + } + + It 'mirrors the helper own-argument keys exactly' { + $map = Get-PfbCommonQueryParamMap + @($map.ByHelperArgument.Keys) | Sort-Object | Should -Be (@($derivedByHelperArgument.Keys) | Sort-Object) + foreach ($k in $derivedByHelperArgument.Keys) { + $map.ByHelperArgument[$k] | Should -Be $derivedByHelperArgument[$k] -Because "the helper assigns `$Into['$($derivedByHelperArgument[$k])'] from its own -$k argument" + } + } +} + +Describe 'Hashtable-literal-initializer awareness' { + It 'resolves a wire key that exists only inside a $queryParams = @{ ... } literal' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureLiteralOnly' -and $_.Parameter -eq 'Name' } + $rec.WireName | Should -Be 'names' + $rec.Surface | Should -Be 'Typed' + $rec.Endpoint | Should -Be 'literal-only' + $rec.Method | Should -Be 'POST' + } + + It 'resolves a literal assigned to $body, and reports body (not queryParams) as the target' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureLiteralMixed' -and $_.Parameter -eq 'NewName' } + $rec.WireName | Should -Be 'name' + $rec.Surface | Should -Be 'Typed' + $rec.Endpoint | Should -Be 'literal-mixed' + } + + It 'resolves BOTH halves of a cmdlet that uses a literal initializer and index assignments' { + $fromLiteral = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureLiteralMixed' -and $_.Parameter -eq 'Name' } + $fromLiteral.WireName | Should -Be 'names' + $fromIndex = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureLiteralMixed' -and $_.Parameter -eq 'Hostname' } + $fromIndex.WireName | Should -Be 'host_name' + @($fromLiteral, $fromIndex).Surface | Should -Be @('Typed', 'Typed') + } + + It 'resolves a literal value that wraps the parameter in an expression rather than referencing it bare' -ForEach @( + @{ Parameter = 'Name'; WireName = 'names' } # @($Name) + @{ Parameter = 'Key'; WireName = 'keys' } # $Key -join ',' + ) { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureLiteralWrapped' -and $_.Parameter -eq $Parameter } + $rec.WireName | Should -Be $WireName + $rec.Surface | Should -Be 'Typed' + } + + It 'credits a nested sub-object literal to its OUTER key, never the inner one' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureNestedLiteral' -and $_.Parameter -eq 'GroupName' } + $rec.WireName | Should -Be 'group' + $rec.WireName | Should -Not -Be 'name' + $rec.Surface | Should -Be 'Typed' + } + + It 'still resolves a sibling top-level key in the same nested literal' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureNestedLiteral' -and $_.Parameter -eq 'Quota' } + $rec.WireName | Should -Be 'quota' + $rec.Surface | Should -Be 'Typed' + } + + It 'does NOT match a literal value that merely mentions the parameter inside a pipeline transform' { + $tokens = $null; $errs = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput( + 'function Test-Fixture { param([string[]]$Servers) $body = @{ attached_servers = @($Servers | ForEach-Object { @{ name = $_ } }) } }', [ref]$tokens, [ref]$errs) + $funcAst = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Select-Object -First 1 + Get-PfbHashtableLiteralWireNameForParameter -FunctionAst $funcAst -ParameterName 'Servers' | Should -BeNullOrEmpty + } + + It 'ignores a hashtable literal assigned to a variable that is neither body nor queryParams' { + $tokens = $null; $errs = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput( + 'function Test-Fixture { param([string]$Name) $somethingElse = @{ names = $Name } }', [ref]$tokens, [ref]$errs) + $funcAst = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Select-Object -First 1 + Get-PfbHashtableLiteralWireNameForParameter -FunctionAst $funcAst -ParameterName 'Name' | Should -BeNullOrEmpty + } +} + +Describe 'Nested single-key reference-object awareness' { + # The API models "point this resource at that one" as {"account": {"name": "acct1"}}, and + # the capability map records TOP-LEVEL body properties only -- there is no `account.name` + # field in it -- so the wire name such a parameter covers is the OUTER key. + It 'resolves an index-assigned reference object to its outer key' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureNestedReference' -and $_.Parameter -eq 'Account' } + $rec.WireName | Should -Be 'account' + $rec.Surface | Should -Be 'Typed' + $rec.Endpoint | Should -Be 'nested-reference' + $rec.Method | Should -Be 'POST' + } + + It 'lets TWO parameters resolve to the same outer key (addressing one field by name or by id is correct, not a collision)' { + $byName = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureNestedReference' -and $_.Parameter -eq 'Account' } + $byId = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureNestedReference' -and $_.Parameter -eq 'AccountId' } + $byName.WireName | Should -Be 'account' + $byId.WireName | Should -Be 'account' + $byId.Surface | Should -Be 'Typed' + } + + It 'does not require the inner key to be "name" (real New-PfbFileSystem eradication_config shape)' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureNestedReference' -and $_.Parameter -eq 'EradicationMode' } + $rec.WireName | Should -Be 'eradication_config' + $rec.Surface | Should -Be 'Typed' + } + + It 'still resolves a plain sibling key in the same cmdlet directly' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureNestedReference' -and $_.Parameter -eq 'Versioning' } + $rec.WireName | Should -Be 'versioning' + $rec.Surface | Should -Be 'Typed' + } + + It 'refuses a MULTI-key sub-object, whose per-field ownership cannot be attributed to one parameter' -ForEach @( + @{ Parameter = 'SourceName' } + @{ Parameter = 'SourceId' } + ) { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureNestedReference' -and $_.Parameter -eq $Parameter } + $rec.WireName | Should -BeNullOrEmpty + $rec.Surface | Should -Be 'TypedUnresolved' + } + + It 'descends exactly one level -- a doubly-nested sub-object stays unresolved' { + $rec = $inventory | Where-Object { $_.Cmdlet -eq 'New-PfbFixtureNestedReference' -and $_.Parameter -eq 'Deep' } + $rec.WireName | Should -BeNullOrEmpty + $rec.Surface | Should -Be 'TypedUnresolved' + } + + It 'lets a DIRECT assignment win over a nested one for the same parameter, whatever the source order' { + # The ordering guarantee that makes this change strictly additive: nested resolution + # runs as its own pass after both direct-assignment passes, so it can only ever turn + # an unresolved parameter Typed -- never rename an already-resolved wire name. + $tokens = $null; $errs = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput( + 'function Test-Fixture { param([string]$Name) $body = @{}; $body["owner"] = @{ name = $Name }; $body["name"] = $Name }', [ref]$tokens, [ref]$errs) + $funcAst = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Select-Object -First 1 + (Get-PfbWireNameForParameter -FunctionAst $funcAst -ParameterName 'Name').WireName | Should -Be 'name' + } + + It 'ignores a reference object keyed into an intermediate variable that is neither body nor queryParams' { + # Real New-PfbFileSystem $nfsBody/$smbBody: not traceable to an Invoke-PfbApiRequest + # call, so there is nothing to attribute the wire name to. + $tokens = $null; $errs = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput( + 'function Test-Fixture { param([string]$Policy) $nfsBody = @{}; $nfsBody["export_policy"] = @{ name = $Policy } }', [ref]$tokens, [ref]$errs) + $funcAst = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Select-Object -First 1 + Get-PfbNestedReferenceWireNameForParameter -FunctionAst $funcAst -ParameterName 'Policy' | Should -BeNullOrEmpty + } + + It 'resolves a nested value produced by an array projection of the parameter' { + # Was 'refuses a nested value produced by a pipeline transform' under PR #60. The + # projection shape is now recognised; the shapes that genuinely cannot be attributed + # (multi-key items, $_.Member, filtered pipelines) are covered in the + # 'Array-of-references projection awareness' Describe block. + $tokens = $null; $errs = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput( + 'function Test-Fixture { param([string[]]$Servers) $body = @{}; $body["attached_servers"] = @($Servers | ForEach-Object { @{ name = $_ } }) }', [ref]$tokens, [ref]$errs) + $funcAst = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Select-Object -First 1 + (Get-PfbNestedReferenceWireNameForParameter -FunctionAst $funcAst -ParameterName 'Servers').WireName | Should -Be 'attached_servers' + } + + It 'refuses a non-literal outer key' { + $tokens = $null; $errs = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput( + 'function Test-Fixture { param([string]$Name, [string]$Key) $body = @{}; $body[$Key] = @{ name = $Name } }', [ref]$tokens, [ref]$errs) + $funcAst = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Select-Object -First 1 + Get-PfbNestedReferenceWireNameForParameter -FunctionAst $funcAst -ParameterName 'Name' | Should -BeNullOrEmpty + } +} + +Describe 'Array-of-references projection awareness' { + # The API models a LIST of references as an array of single-key sub-objects, and the + # module builds it with `@($Param | ForEach-Object { @{ name = $_ } })`. The parameter's + # identity is the pipeline SOURCE -- the innermost value is $_, which names nothing -- + # so this cannot route through Test-PfbWireValueIsParameter like the scalar form does. + # As with the scalar form, the wire name is the OUTER key. + + BeforeAll { + function Get-TestFunctionAst { + param([string]$Source) + $tokens = $null; $errs = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput($Source, [ref]$tokens, [ref]$errs) + $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Select-Object -First 1 + } + } + + It 'resolves an index-assigned projection to its outer key' { + $f = Get-TestFunctionAst 'function Test-Fixture { param([string[]]$Servers) $body = @{}; $body["attached_servers"] = @($Servers | ForEach-Object { @{ name = $_ } }) }' + $result = Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'Servers' + $result.WireName | Should -Be 'attached_servers' + $result.TargetVariable | Should -Be 'body' + } + + It 'resolves a projection inside a hashtable-literal initializer' { + $f = Get-TestFunctionAst 'function Test-Fixture { param([string[]]$DnsName) $body = @{ dns = @($DnsName | ForEach-Object { @{ name = $_ } }) } }' + (Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'DnsName').WireName | Should -Be 'dns' + } + + It 'resolves a projection that is not wrapped in @()' { + $f = Get-TestFunctionAst 'function Test-Fixture { param([string[]]$Servers) $body = @{}; $body["attached_servers"] = $Servers | ForEach-Object { @{ name = $_ } } }' + (Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'Servers').WireName | Should -Be 'attached_servers' + } + + It 'accepts the % alias for ForEach-Object' { + $f = Get-TestFunctionAst 'function Test-Fixture { param([string[]]$Servers) $body = @{}; $body["attached_servers"] = @($Servers | % { @{ name = $_ } }) }' + (Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'Servers').WireName | Should -Be 'attached_servers' + } + + It 'does not require the inner key to be "name"' { + $f = Get-TestFunctionAst 'function Test-Fixture { param([string[]]$Ids) $body = @{}; $body["ports"] = @($Ids | ForEach-Object { @{ id = $_ } }) }' + (Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'Ids').WireName | Should -Be 'ports' + } + + It 'reports queryParams as the target variable when the projection is keyed there' { + $f = Get-TestFunctionAst 'function Test-Fixture { param([string[]]$Servers) $queryParams = @{}; $queryParams["attached_servers"] = @($Servers | ForEach-Object { @{ name = $_ } }) }' + (Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'Servers').TargetVariable | Should -Be 'queryParams' + } + + It 'refuses a MULTI-key projection hashtable, whose per-field ownership cannot be attributed to one parameter' { + $f = Get-TestFunctionAst 'function Test-Fixture { param([object[]]$Tags) $body = @{}; $body["tags"] = @($Tags | ForEach-Object { @{ key = $_; value = 1 } }) }' + Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'Tags' | Should -BeNullOrEmpty + } + + It 'refuses an innermost value that is a member access rather than the bare $_' { + $f = Get-TestFunctionAst 'function Test-Fixture { param([object[]]$Servers) $body = @{}; $body["attached_servers"] = @($Servers | ForEach-Object { @{ name = $_.Name } }) }' + Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'Servers' | Should -BeNullOrEmpty + } + + It 'refuses an innermost value that is a bare variable OTHER than $_' { + # Mutation-table coverage (Step 7, "innermost is $_" guard): the member-access test + # above is caught earlier, by the VariableExpressionAst cast itself failing on + # `$_.Name`. This shape's innermost value IS a bare VariableExpressionAst, just not + # named `_` -- so only the UserPath -eq '_' check stops a constant, non-per-element + # value (here, a sibling parameter) from being wrongly credited to $Servers. + $f = Get-TestFunctionAst 'function Test-Fixture { param([string[]]$Servers, [string]$Other) $body = @{}; $body["attached_servers"] = @($Servers | ForEach-Object { @{ name = $Other } }) }' + Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'Servers' | Should -BeNullOrEmpty + } + + It 'refuses a projection whose pipeline source is a DIFFERENT parameter' { + $f = Get-TestFunctionAst 'function Test-Fixture { param([string[]]$Servers, [string[]]$Other) $body = @{}; $body["attached_servers"] = @($Other | ForEach-Object { @{ name = $_ } }) }' + Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'Servers' | Should -BeNullOrEmpty + } + + It 'refuses a projection whose pipeline source is not a bare variable' { + $f = Get-TestFunctionAst 'function Test-Fixture { param([string[]]$Servers) $body = @{}; $body["attached_servers"] = @("literal" | ForEach-Object { @{ name = $_ } }) }' + Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'Servers' | Should -BeNullOrEmpty + } + + It 'refuses a FILTERED pipeline -- the wire value is a subset, so the parameter does not cover the field' { + $f = Get-TestFunctionAst 'function Test-Fixture { param([string[]]$Servers) $body = @{}; $body["attached_servers"] = @($Servers | Where-Object { $_ } | ForEach-Object { @{ name = $_ } }) }' + Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'Servers' | Should -BeNullOrEmpty + } + + It 'refuses a ForEach-Object carrying arguments other than its script block' { + $f = Get-TestFunctionAst 'function Test-Fixture { param([string[]]$Servers) $body = @{}; $body["attached_servers"] = @($Servers | ForEach-Object -Process { @{ name = $_ } }) }' + Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'Servers' | Should -BeNullOrEmpty + } + + It 'refuses a projection keyed into a variable that is neither body nor queryParams' { + $f = Get-TestFunctionAst 'function Test-Fixture { param([string[]]$Servers) $nfsBody = @{}; $nfsBody["attached_servers"] = @($Servers | ForEach-Object { @{ name = $_ } }) }' + Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'Servers' | Should -BeNullOrEmpty + } + + It 'refuses a projection under a non-literal outer key' { + $f = Get-TestFunctionAst 'function Test-Fixture { param([string[]]$Servers, [string]$Key) $body = @{}; $body[$Key] = @($Servers | ForEach-Object { @{ name = $_ } }) }' + Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'Servers' | Should -BeNullOrEmpty + } + + It 'lets a DIRECT assignment win over a projection for the same parameter, whatever the source order' { + # The ordering guarantee that keeps this change strictly additive: projection matching + # lives inside the nested resolver, which runs as its own pass AFTER both direct- + # assignment passes, so it can only turn an unresolved parameter Typed -- never rename + # an already-resolved wire name. Mirrors the scalar-form test above. + $f = Get-TestFunctionAst 'function Test-Fixture { param([string[]]$Servers) $body = @{}; $body["attached_servers"] = @($Servers | ForEach-Object { @{ name = $_ } }); $body["servers"] = @($Servers) }' + (Get-PfbWireNameForParameter -FunctionAst $f -ParameterName 'Servers').WireName | Should -Be 'servers' + } + + It 'refuses a pipeline with a trailing step after ForEach-Object' { + # Mutation-table coverage (Step 7, "pipeline length" guard): the FILTERED-pipeline + # test above puts its extra stage BEFORE ForEach-Object, so it is (redundantly) + # also caught by the command-name check on element[1]. This shape puts the extra + # stage AFTER ForEach-Object, so element[1] genuinely IS ForEach-Object and only the + # pipeline-length check (`-ne 2`, not `-lt 2`) stops it being wrongly credited. + $f = Get-TestFunctionAst 'function Test-Fixture { param([string[]]$Servers) $body = @{}; $body["attached_servers"] = @($Servers | ForEach-Object { @{ name = $_ } } | Sort-Object) }' + Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'Servers' | Should -BeNullOrEmpty + } + + It 'refuses a ForEach-Object script block followed by a trailing named argument' { + # Mutation-table coverage (Step 7, "command arg count" guard): the existing + # `-Process { ... }` test above puts its extra argument BEFORE the script block, so + # index 1 is a CommandParameterAst and is (redundantly) also caught by the + # scriptBlockExpr cast. This shape puts the extra argument AFTER the script block, so + # index 1 genuinely IS the script block, and only the CommandElements.Count check + # (`-ne 2`, not `-lt 2`) stops it being wrongly credited. + $f = Get-TestFunctionAst 'function Test-Fixture { param([string[]]$Servers) $body = @{}; $body["attached_servers"] = @($Servers | ForEach-Object { @{ name = $_ } } -ErrorAction Stop) }' + Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'Servers' | Should -BeNullOrEmpty + } + + It 'refuses a projection piped through a command other than ForEach-Object/%' { + # Mutation-table coverage (Step 7, "command name" guard): dropping the + # -notin @('ForEach-Object','%') check would let any command through, e.g. `Get-Item`. + $f = Get-TestFunctionAst 'function Test-Fixture { param([string[]]$Servers) $body = @{}; $body["attached_servers"] = @($Servers | Get-Item { @{ name = $_ } }) }' + Get-PfbNestedReferenceWireNameForParameter -FunctionAst $f -ParameterName 'Servers' | Should -BeNullOrEmpty + } +} + +Describe 'Find-PfbAccumulatorVariable' { + It 'returns $null when the parameter has no foreach loop over it at all' { + $tokens = $null; $errs = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput( + 'function Test-Fixture { param([string[]]$Unused) $body = @{} }', [ref]$tokens, [ref]$errs) + $funcAst = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Select-Object -First 1 + Find-PfbAccumulatorVariable -FunctionAst $funcAst -ParameterName 'Unused' | Should -BeNullOrEmpty + } + + It 'returns $null when the loop body calls .Add(...) on more than one target variable' { + $tokens = $null; $errs = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput( + 'function Test-Fixture { param([string[]]$Name) $a = [System.Collections.Generic.List[string]]::new(); $b = [System.Collections.Generic.List[string]]::new(); foreach ($n in $Name) { $a.Add($n); $b.Add($n) } }', [ref]$tokens, [ref]$errs) + $funcAst = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Select-Object -First 1 + Find-PfbAccumulatorVariable -FunctionAst $funcAst -ParameterName 'Name' | Should -BeNullOrEmpty + } + + It 'returns the accumulator variable name for a single unambiguous foreach-Add loop over the parameter' { + $tokens = $null; $errs = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput( + 'function Test-Fixture { param([string[]]$Name) $names = [System.Collections.Generic.List[string]]::new(); foreach ($n in $Name) { $names.Add($n) } }', [ref]$tokens, [ref]$errs) + $funcAst = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Select-Object -First 1 + Find-PfbAccumulatorVariable -FunctionAst $funcAst -ParameterName 'Name' | Should -Be 'names' + } +} + +Describe 'Get-PfbCmdletBodyInsertionTarget (Task 5 -- insertion-point coordinates, decision 12)' { + BeforeAll { + function script:Get-PfbTestFunctionAst { + param([string]$Source) + $tokens = $null; $errs = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput($Source, [ref]$tokens, [ref]$errs) + return $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) | Select-Object -First 1 + } + } + + It 'returns $null for a function with no param() block at all' { + $funcAst = Get-PfbTestFunctionAst 'function Test-Fixture { Write-Host "no params" }' + Get-PfbCmdletBodyInsertionTarget -FunctionAst $funcAst | Should -BeNullOrEmpty + } + + It 'detects the index-assignment style ($body[''key''] = ...) as the dominant AssignmentStyle' { + $funcAst = Get-PfbTestFunctionAst @' +function Test-FixtureIndex { + param([Parameter()] [PSCustomObject]$Array, [Parameter()] [string]$Name) + $body = @{} + if ($Name) { $body['name'] = $Name } + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'fixtures' -Body $body +} +'@ + $result = Get-PfbCmdletBodyInsertionTarget -FunctionAst $funcAst + $result.PayloadVariable | Should -Be 'body' + $result.AssignmentStyle | Should -Be 'index' + $result.HasAttributes | Should -BeFalse + } + + It 'detects the hashtable-literal-initializer style as the dominant AssignmentStyle when it has more key/value pairs than index-form assignments' { + $funcAst = Get-PfbTestFunctionAst @' +function Test-FixtureLiteral { + param([Parameter()] [PSCustomObject]$Array, [Parameter()] [string]$Name, [Parameter()] [string]$Kind) + $body = @{ name = $Name; kind = $Kind } + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'fixtures' -Body $body +} +'@ + $result = Get-PfbCmdletBodyInsertionTarget -FunctionAst $funcAst + $result.PayloadVariable | Should -Be 'body' + $result.AssignmentStyle | Should -Be 'literal' + } + + It 'reports AssignmentStyle ''attributesOnly'' when -Body is fed directly by the cmdlet''s own -Attributes parameter' { + $funcAst = Get-PfbTestFunctionAst @' +function Update-FixtureCertificate { + param([Parameter(Mandatory)] [string]$Name, [Parameter(Mandatory)] [hashtable]$Attributes, [Parameter()] [PSCustomObject]$Array) + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'fixtures' -Body $Attributes +} +'@ + $result = Get-PfbCmdletBodyInsertionTarget -FunctionAst $funcAst + $result.PayloadVariable | Should -Be 'Attributes' + $result.AssignmentStyle | Should -Be 'attributesOnly' + $result.HasAttributes | Should -BeTrue + } + + It 'reports AssignmentStyle ''unknown'' when PayloadVariable resolves but nothing in this function assigns into it' { + $funcAst = Get-PfbTestFunctionAst @' +function Test-FixtureHelperBuilt { + param([Parameter()] [PSCustomObject]$Array, [Parameter()] [string]$Name) + $body = Get-FixtureBodyFromHelper -Name $Name + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'fixtures' -Body $body +} +'@ + $result = Get-PfbCmdletBodyInsertionTarget -FunctionAst $funcAst + $result.PayloadVariable | Should -Be 'body' + $result.AssignmentStyle | Should -Be 'unknown' + } + + It 'leaves PayloadVariable/AssignmentStyle $null when two Invoke-PfbApiRequest calls disagree on the -Body variable (never guesses)' { + $funcAst = Get-PfbTestFunctionAst @' +function Test-FixtureAmbiguousBody { + param([Parameter()] [PSCustomObject]$Array, [Parameter()] [string]$Name) + $body = @{} + $altBody = @{} + if ($Name) { + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'fixtures' -Body $body + } else { + Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'fixtures' -Body $altBody + } +} +'@ + $result = Get-PfbCmdletBodyInsertionTarget -FunctionAst $funcAst + $result.PayloadVariable | Should -BeNullOrEmpty + $result.AssignmentStyle | Should -BeNullOrEmpty + } + + It 'computes ParamBlockLine as the line of the LAST declared parameter' { + $funcAst = Get-PfbTestFunctionAst @' +function Test-FixtureParamLine { + param( + [Parameter()] [PSCustomObject]$Array, + [Parameter()] [string]$Name, + [Parameter()] [string]$Kind + ) + $body = @{} + if ($Name) { $body['name'] = $Name } + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'fixtures' -Body $body +} +'@ + $result = Get-PfbCmdletBodyInsertionTarget -FunctionAst $funcAst + # Line 1 is 'function ...', line 2 'param(', 3 Array, 4 Name, 5 Kind, 6 ')'. + $result.ParamBlockLine | Should -Be 5 + } + + It 'falls back to the param block''s own opening line when it declares zero parameters' { + $funcAst = Get-PfbTestFunctionAst @' +function Test-FixtureNoParams { + param() + $body = @{} + Invoke-PfbApiRequest -Method GET -Endpoint 'fixtures' -Body $body +} +'@ + $result = Get-PfbCmdletBodyInsertionTarget -FunctionAst $funcAst + $result.ParamBlockLine | Should -Be 2 + } + + It 'reports HasAttributes $false when the cmdlet declares no -Attributes parameter at all' { + $funcAst = Get-PfbTestFunctionAst @' +function Test-FixtureNoAttributes { + param([Parameter()] [PSCustomObject]$Array, [Parameter()] [string]$Name) + $body = @{} + if ($Name) { $body['name'] = $Name } + Invoke-PfbApiRequest -Array $Array -Method PATCH -Endpoint 'fixtures' -Body $body +} +'@ + $result = Get-PfbCmdletBodyInsertionTarget -FunctionAst $funcAst + $result.HasAttributes | Should -BeFalse + } +} + +Describe 'Get-PfbCmdletParameterInventory emit order is canonical, not filesystem order (issue #85)' { + BeforeAll { + # Two fixture trees holding the SAME two cmdlets, but with the cmdlet-to-filename + # mapping swapped between them. Both trees carry identical FILE names, so + # Get-ChildItem walks them in the same sequence whatever the filesystem does -- the + # only thing that differs is which cmdlet each position in that walk yields. An + # unsorted inventory therefore emits Zulu-then-Alpha for one tree and + # Alpha-then-Zulu for the other; a canonically-ordered one emits the same sequence + # for both. + # + # This is the platform-INDEPENDENT form of the divergence issue #85 first observed + # as a 10,218-line phantom diff between a Windows-generated and a Linux-generated + # Reports/PfbFieldCmdletMap.json. Deliberately not written as "run the builder twice + # on this machine" -- that is exactly the assertion + # Tests/Build-PfbApiDriftReport.Tests.ps1 already makes, and enumeration order is + # stable within one filesystem, so it can never fail. And deliberately not dependent + # on tools/specs/, so it does not silently skip in a fresh clone or worktree (#63). + $script:orderDirA = Join-Path $TestDrive 'EmitOrderA/Public' + $script:orderDirB = Join-Path $TestDrive 'EmitOrderB/Public' + New-Item -ItemType Directory -Path $script:orderDirA, $script:orderDirB -Force | Out-Null + + # Parameters are declared Zebra-before-Apple so the assertion below also pins + # within-cmdlet ordering, which declaration order alone would leave reversed. + $zuluSource = @' +function Get-PfbFixtureOrderZulu { + [CmdletBinding()] + param( + [Parameter()] [PSCustomObject]$Array, + [Parameter()] [string]$Zebra, + [Parameter()] [string]$Apple + ) + $queryParams = @{} + if ($Zebra) { $queryParams['zebra'] = $Zebra } + if ($Apple) { $queryParams['apple'] = $Apple } + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'order-zulu' -QueryParams $queryParams +} +'@ + $alphaSource = @' +function Get-PfbFixtureOrderAlpha { + [CmdletBinding()] + param( + [Parameter()] [PSCustomObject]$Array, + [Parameter()] [string]$Zebra, + [Parameter()] [string]$Apple + ) + $queryParams = @{} + if ($Zebra) { $queryParams['zebra'] = $Zebra } + if ($Apple) { $queryParams['apple'] = $Apple } + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'order-alpha' -QueryParams $queryParams +} +'@ + Set-Content -Path (Join-Path $script:orderDirA '01-first.ps1') -Value $zuluSource + Set-Content -Path (Join-Path $script:orderDirA '02-second.ps1') -Value $alphaSource + Set-Content -Path (Join-Path $script:orderDirB '01-first.ps1') -Value $alphaSource + Set-Content -Path (Join-Path $script:orderDirB '02-second.ps1') -Value $zuluSource + + $script:orderKeysA = @(Get-PfbCmdletParameterInventory -PublicDirectory $script:orderDirA | + ForEach-Object { '{0}|{1}' -f $_.Cmdlet, $_.Parameter }) + $script:orderKeysB = @(Get-PfbCmdletParameterInventory -PublicDirectory $script:orderDirB | + ForEach-Object { '{0}|{1}' -f $_.Cmdlet, $_.Parameter }) + } + + It 'emits the same sequence for two trees differing only in which file defines which cmdlet' { + $script:orderKeysA | Should -Be $script:orderKeysB + } + + It 'emits rows ordered by cmdlet name, then parameter name -- never by file walk or declaration order' { + $script:orderKeysA | Should -Be @( + 'Get-PfbFixtureOrderAlpha|Apple' + 'Get-PfbFixtureOrderAlpha|Zebra' + 'Get-PfbFixtureOrderZulu|Apple' + 'Get-PfbFixtureOrderZulu|Zebra' + ) + } +} diff --git a/Tests/PfbContextRuleTools.Tests.ps1 b/Tests/PfbContextRuleTools.Tests.ps1 new file mode 100644 index 0000000..be80ebd --- /dev/null +++ b/Tests/PfbContextRuleTools.Tests.ps1 @@ -0,0 +1,495 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } +<# +.SYNOPSIS + Unit tests for tools/lib/PfbContextRuleTools.ps1 and the module's single declared + context-cardinality rule, Private/Test-PfbContextMultiValueCapable.ps1. +.DESCRIPTION + Note on InModuleScope: deliberately NOT used. PfbContextRuleTools.ps1 is dot-sourced + from tools/lib/ and its functions are therefore directly callable, and it in turn + dot-sources the Private/ predicate itself -- so the predicate is in scope here without + importing the module at all. That is the point of the design: the rule has exactly one + home, and both the check and these tests reach the same copy. + + Note on what is asserted: SPECIFIC ENDPOINTS and STRUCTURE, never population totals. + The capability map is regenerated whenever specs are refreshed, so a test pinning "135 + capable" or "376 endpoints" would break on unrelated maintenance. The four fleet-scoped + endpoints confirmed size-1 on the wire on 2026-08-01 are stable fixtures and are used + as such. +#> + +BeforeAll { + $repoRoot = Split-Path -Parent $PSScriptRoot + . (Join-Path $repoRoot 'tools/lib/PfbSpecTools.ps1') + . (Join-Path $repoRoot 'tools/lib/PfbContextRuleTools.ps1') + + $script:repoRoot = $repoRoot + + # The four fleet-scoped GETs that reference the multi-value component but reject a + # two-name context with 400 code 15. Upstream defect, still present at fb2.28. + $script:defectiveFleetScopedGets = @( + 'GET /presets/workload' + 'GET /topology-groups' + 'GET /topology-groups/arrays' + 'GET /topology-groups/members' + ) + + # Synthetic capability map exercising every branch of the documented + # defaults/overrides resolution contract and every disagreement shape. + $script:fixtureMap = [PSCustomObject]@{ + parameterComponentDefaults = [PSCustomObject]@{ + context_names = 'Context_names' + allow_errors = 'Allow_errors' + } + endpoints = [PSCustomObject]@{ + # Multi-value component + allow_errors + 207 -> capable, all signals agree. + 'GET /widgets' = [PSCustomObject]@{ + parameters = [PSCustomObject]@{ context_names = '2.26'; allow_errors = '2.26' } + parameterComponentOverrides = [PSCustomObject]@{ context_names = 'Context_names_get' } + } + # Default component (size-1), no allow_errors -> not capable, signals agree. + 'DELETE /widgets' = [PSCustomObject]@{ + parameters = [PSCustomObject]@{ context_names = '2.26'; ids = '2.0' } + } + # The defective shape: multi-value component, no allow_errors. A GET, so the + # withdrawn verb rule would have called this capable; the ratified rule does not. + 'GET /fleet-scoped' = [PSCustomObject]@{ + parameters = [PSCustomObject]@{ context_names = '2.26' } + parameterComponentOverrides = [PSCustomObject]@{ context_names = 'Context_names_get' } + } + # The mirror: size-1 component that declares allow_errors. + 'PATCH /mirror' = [PSCustomObject]@{ + parameters = [PSCustomObject]@{ context_names = '2.26'; allow_errors = '2.26' } + } + # Capable by the rule, but declares no 207. + 'GET /capable-no-207' = [PSCustomObject]@{ + parameters = [PSCustomObject]@{ context_names = '2.26'; allow_errors = '2.26' } + parameterComponentOverrides = [PSCustomObject]@{ context_names = 'Context_names_get' } + } + # Nothing says it fans out, yet it declares a partial-failure response. + 'GET /size1-with-207' = [PSCustomObject]@{ + parameters = [PSCustomObject]@{ context_names = '2.26' } + } + # Override key PRESENT with a null value == "no component here", which must be + # honoured over the default rather than silently falling through to it. + 'GET /inline-context' = [PSCustomObject]@{ + parameters = [PSCustomObject]@{ context_names = '2.26' } + parameterComponentOverrides = [PSCustomObject]@{ context_names = $null } + } + # No context_names at all -- must be ignored entirely. + 'GET /unrelated' = [PSCustomObject]@{ + parameters = [PSCustomObject]@{ ids = '2.0'; allow_errors = '2.26' } + } + } + } + + # 'GET /capable-no-207' is deliberately absent from this set; 'GET /size1-with-207' is + # deliberately in it despite having no other multi-value signal. + $script:fixture207 = @('GET /widgets', 'GET /size1-with-207') + + $script:facts = @(Get-PfbContextParameterFact -CapabilityMap $script:fixtureMap -Http207Endpoint $script:fixture207) + $script:factsNo207 = @(Get-PfbContextParameterFact -CapabilityMap $script:fixtureMap) +} + +Describe 'Test-PfbContextMultiValueCapable (the single declared rule)' { + It 'is capable only when the multi-value component AND allow_errors are both present' { + Test-PfbContextMultiValueCapable -Method 'GET' -ContextComponent 'Context_names_get' -DeclaresAllowErrors $true | + Should -BeTrue + } + + It 'is not capable when the component says multi-value but allow_errors is absent' { + # The four fleet-scoped GETs. Under the withdrawn verb rule this returned $true. + Test-PfbContextMultiValueCapable -Method 'GET' -ContextComponent 'Context_names_get' -DeclaresAllowErrors $false | + Should -BeFalse + } + + It 'is not capable when allow_errors is present but the component is size-1' { + Test-PfbContextMultiValueCapable -Method 'PATCH' -ContextComponent 'Context_names' -DeclaresAllowErrors $true | + Should -BeFalse + } + + It 'does not let the verb override an evidence-backed verdict' { + # A mutation verb with both signals present is capable; a GET without them is not. + Test-PfbContextMultiValueCapable -Method 'DELETE' -ContextComponent 'Context_names_get' -DeclaresAllowErrors $true | + Should -BeTrue + Test-PfbContextMultiValueCapable -Method 'GET' -ContextComponent 'Context_names' -DeclaresAllowErrors $false | + Should -BeFalse + } + + It 'falls back to the verb only when no component signal exists' -ForEach @( + @{ Component = $null; Expected = $true } + @{ Component = ''; Expected = $true } + ) { + Test-PfbContextMultiValueCapable -Method 'GET' -ContextComponent $Component -DeclaresAllowErrors $false | + Should -Be $Expected + } + + It 'treats as size-1 in the no-signal fallback' -ForEach @( + @{ Method = 'POST' } + @{ Method = 'PUT' } + @{ Method = 'PATCH' } + @{ Method = 'DELETE' } + ) { + Test-PfbContextMultiValueCapable -Method $Method -ContextComponent $null -DeclaresAllowErrors $true | + Should -BeFalse + } + + It 'is case-insensitive in both the component test and the fallback' { + Test-PfbContextMultiValueCapable -Method 'get' -ContextComponent $null -DeclaresAllowErrors $false | Should -BeTrue + Test-PfbContextMultiValueCapable -Method 'delete' -ContextComponent 'Context_names_get' -DeclaresAllowErrors $true | Should -BeTrue + } + + It 'throws rather than assuming a default for a verb it has no verdict for' { + # Only reachable on the fallback path. A silent $false would quietly assume + # "size-1" for a verb nobody has reasoned about. (-Method is supplied, so no + # interactive mandatory-parameter prompt.) + { Test-PfbContextMultiValueCapable -Method 'TRACE' -ContextComponent $null -DeclaresAllowErrors $false } | + Should -Throw -ExpectedMessage '*no context-cardinality verdict*' + } + + It 'does not throw for an unknown verb when a component signal decides it' { + { Test-PfbContextMultiValueCapable -Method 'TRACE' -ContextComponent 'Context_names' -DeclaresAllowErrors $false } | + Should -Not -Throw + } +} + +Describe 'Get-PfbContextParameterFact' { + It 'returns only endpoints declaring context_names' { + $script:facts.Endpoint | Should -Not -Contain 'GET /unrelated' + } + + It 'falls back to parameterComponentDefaults when no override is present' { + $f = $script:facts | Where-Object Endpoint -EQ 'DELETE /widgets' + $f.ContextComponent | Should -Be 'Context_names' + $f.ComponentSaysMultiValue | Should -BeFalse + } + + It 'prefers a per-endpoint override over the default' { + $f = $script:facts | Where-Object Endpoint -EQ 'GET /widgets' + $f.ContextComponent | Should -Be 'Context_names_get' + $f.ComponentSaysMultiValue | Should -BeTrue + } + + It 'honours an override key present with a null value as "no component"' { + $f = $script:facts | Where-Object Endpoint -EQ 'GET /inline-context' + $f.ContextComponent | Should -BeNullOrEmpty + $f.ComponentSaysMultiValue | Should -BeNullOrEmpty + } + + It 'splits the endpoint key on the first space only' { + $f = $script:facts | Where-Object Endpoint -EQ 'PATCH /mirror' + $f.Method | Should -Be 'PATCH' + $f.Path | Should -Be '/mirror' + } + + It 'records the rule verdict from the declared predicate, not from the verb' { + # A GET with a multi-value component but no allow_errors must NOT be capable. + ($script:facts | Where-Object Endpoint -EQ 'GET /fleet-scoped').RuleSaysMultiValue | Should -BeFalse + ($script:facts | Where-Object Endpoint -EQ 'GET /widgets').RuleSaysMultiValue | Should -BeTrue + } + + It 'carries the 207 signal when supplied' { + ($script:facts | Where-Object Endpoint -EQ 'GET /widgets').DeclaresHttp207 | Should -BeTrue + ($script:facts | Where-Object Endpoint -EQ 'GET /capable-no-207').DeclaresHttp207 | Should -BeFalse + } + + It 'carries 207 as $null (UNKNOWN) when not supplied, never as false' { + # Unknown must stay distinguishable from "declares no 207" -- scoring unknown as + # false would invent disagreements that the data does not support. + foreach ($f in $script:factsNo207) { $f.DeclaresHttp207 | Should -BeNullOrEmpty } + } + + It 'emits a deterministically sorted collection' { + @($script:facts.Endpoint) | Should -Be @($script:facts.Endpoint | Sort-Object) + } +} + +Describe 'Get-PfbContextSignalDisagreement' { + BeforeAll { + $script:findings = @(Get-PfbContextSignalDisagreement -Fact $script:facts) + $script:findingsNo207 = @(Get-PfbContextSignalDisagreement -Fact $script:factsNo207) + } + + It 'reports nothing for an endpoint whose signals all agree' { + $script:findings.Endpoint | Should -Not -Contain 'GET /widgets' + $script:findings.Endpoint | Should -Not -Contain 'DELETE /widgets' + } + + It 'reports a multi-value component that declares no allow_errors' { + $f = $script:findings | Where-Object Endpoint -EQ 'GET /fleet-scoped' + $f | Should -Not -BeNullOrEmpty + $f.Shape | Should -Be 'component-says-multi-value-but-no-allow-errors' + } + + It 'reports the size-1-component-declaring-allow_errors mirror' { + ($script:findings | Where-Object Endpoint -EQ 'PATCH /mirror').Shape | + Should -Be 'size-1-component-but-declares-allow-errors' + } + + It 'reports an endpoint declaring a 207 while nothing else says it fans out' { + ($script:findings | Where-Object Endpoint -EQ 'GET /size1-with-207').Shape | + Should -Be 'size-1-but-declares-207' + } + + It 'covers every disagreement shape, so none goes untested' { + @($script:findings.Shape | Sort-Object -Unique) | Should -Be @( + 'component-says-multi-value-but-no-allow-errors' + 'rule-says-capable-but-declares-no-207' + 'size-1-but-declares-207' + 'size-1-component-but-declares-allow-errors' + ) + } + + It 'reports a rule-capable endpoint that declares no 207' { + # THE CASE THE OLD TWO-SIGNAL DESIGN COULD NOT SEE: rule and component agree, + # and the remaining signal dissents. + $f = $script:findings | Where-Object Endpoint -EQ 'GET /capable-no-207' + $f | Should -Not -BeNullOrEmpty + $f.Shape | Should -Be 'rule-says-capable-but-declares-no-207' + $f.RuleSaysMultiValue | Should -BeTrue + $f.ComponentSaysMultiValue | Should -BeTrue + } + + It 'excludes the 207 signal from comparison when it is unknown' { + # Without 207 data, GET /capable-no-207's remaining signals are unanimous, so it + # must not be reported -- an unknown signal narrows the check, never invents a + # finding. + $script:findingsNo207.Endpoint | Should -Not -Contain 'GET /capable-no-207' + $script:findingsNo207.Endpoint | Should -Contain 'GET /fleet-scoped' + } + + It 'records which signals fell on each side' { + $f = $script:findings | Where-Object Endpoint -EQ 'GET /fleet-scoped' + @($f.MultiValueSignals) | Should -Be @('component') + @($f.SizeOneSignals) | Should -Be @('allow_errors', 'http207') + } + + It 'words the summary neutrally, without prejudging which signal is wrong' { + $summary = ($script:findings | Where-Object Endpoint -EQ 'GET /fleet-scoped').Summary + $summary | Should -BeLike '*signals split*' + $summary | Should -Not -BeLike '*bug*' + $summary | Should -Not -BeLike '*defect*' + } + + It 'excludes endpoints whose component could not be resolved' { + $script:findings.Endpoint | Should -Not -Contain 'GET /inline-context' + } + + It 'emits a deterministically sorted collection' { + @($script:findings.Endpoint) | Should -Be @($script:findings.Endpoint | Sort-Object) + } +} + +Describe 'Get-PfbContextSizeOneWithAllowErrors' { + It 'returns the mirror case only' { + @(Get-PfbContextSizeOneWithAllowErrors -Fact $script:facts).Endpoint | Should -Be @('PATCH /mirror') + } + + It 'is a strict subset of the signal disagreements, never an extra finding' { + $mirror = @(Get-PfbContextSizeOneWithAllowErrors -Fact $script:facts).Endpoint + $all = @(Get-PfbContextSignalDisagreement -Fact $script:facts).Endpoint + foreach ($m in $mirror) { $all | Should -Contain $m } + } +} + +Describe 'Get-PfbContextUnresolvedComponent' { + It 'surfaces endpoints whose context_names has no resolvable component' { + @(Get-PfbContextUnresolvedComponent -Fact $script:facts).Endpoint | Should -Be @('GET /inline-context') + } +} + +Describe 'Get-PfbContextHttp207Endpoint' { + It 'finds a declared 207 and normalizes the endpoint key' { + $spec = [PSCustomObject]@{ + paths = [PSCustomObject]@{ + '/api/2.26/widgets' = [PSCustomObject]@{ + get = [PSCustomObject]@{ responses = [PSCustomObject]@{ '200' = @{}; '207' = @{} } } + delete = [PSCustomObject]@{ responses = [PSCustomObject]@{ '200' = @{} } } + } + } + } + @(Get-PfbContextHttp207Endpoint -Spec $spec) | Should -Be @('GET /widgets') + } + + It 'returns an empty collection for a spec with no paths' { + @(Get-PfbContextHttp207Endpoint -Spec ([PSCustomObject]@{})).Count | Should -Be 0 + } +} + +Describe 'Empty-collection contracts' { + # PowerShell collapses a bare empty array returned from a function to $null, so these + # functions follow the repo-wide tools/lib convention: they `return @(...)` and CALL + # SITES wrap in @(). These tests pin the caller-side contract -- an empty result must + # be a zero-length collection once wrapped, never a one-element collection containing + # something. (An earlier attempt to make the functions themselves never return $null, + # via `return ,$array`, is exactly what breaks that: @() does not flatten a nested + # array, so every wrapped call site would have silently seen Count 1.) + BeforeAll { + $script:emptyMap = [PSCustomObject]@{ + parameterComponentDefaults = [PSCustomObject]@{ context_names = 'Context_names' } + endpoints = [PSCustomObject]@{ + 'GET /nothing' = [PSCustomObject]@{ parameters = [PSCustomObject]@{ ids = '2.0' } } + } + } + $script:noFacts = Get-PfbContextParameterFact -CapabilityMap $script:emptyMap + $script:agreeingFacts = Get-PfbContextParameterFact -CapabilityMap ([PSCustomObject]@{ + parameterComponentDefaults = [PSCustomObject]@{ context_names = 'Context_names' } + endpoints = [PSCustomObject]@{ + 'DELETE /q' = [PSCustomObject]@{ parameters = [PSCustomObject]@{ context_names = '2.26' } } + } + }) + } + + It 'Get-PfbContextParameterFact yields zero facts when nothing matches' { + @($script:noFacts).Count | Should -Be 0 + } + + It 'Get-PfbContextParameterFact yields zero facts for a map with no endpoints' { + @(Get-PfbContextParameterFact -CapabilityMap ([PSCustomObject]@{ endpoints = $null })).Count | + Should -Be 0 + } + + It 'Get-PfbContextHttp207Endpoint yields zero endpoints for a spec with no paths' { + @(Get-PfbContextHttp207Endpoint -Spec ([PSCustomObject]@{})).Count | Should -Be 0 + } + + It ' yields zero findings when there is nothing to report' -ForEach @( + @{ Function = 'Get-PfbContextSignalDisagreement' } + @{ Function = 'Get-PfbContextSizeOneWithAllowErrors' } + @{ Function = 'Get-PfbContextUnresolvedComponent' } + ) { + # Count 1 here would mean a nested array leaked through the @() wrap. + @(& $Function -Fact $script:agreeingFacts).Count | Should -Be 0 + } + + It 'treats an EMPTY 207 set as "checked, none declare it" rather than "unknown"' { + # The distinction is load-bearing: scoring "none" as "unknown" would silently drop + # the 207 signal from comparison for a spec that legitimately declares no 207. + $withEmpty = Get-PfbContextParameterFact -CapabilityMap ([PSCustomObject]@{ + parameterComponentDefaults = [PSCustomObject]@{ context_names = 'Context_names' } + endpoints = [PSCustomObject]@{ + 'GET /w' = [PSCustomObject]@{ parameters = [PSCustomObject]@{ context_names = '2.26' } } + } + }) -Http207Endpoint ([string[]]@()) + # Asserted as an explicit null check, not -BeNullOrEmpty: $false IS "empty" to that + # operator, so it could not tell the two states apart here. + ($null -eq $withEmpty[0].DeclaresHttp207) | Should -BeFalse -Because 'the spec was checked, so this is false rather than unknown' + $withEmpty[0].DeclaresHttp207 | Should -BeFalse + } + + It 'keeps the 207 lookup case-insensitive (the HashSet comparer must survive assignment)' { + $f = Get-PfbContextParameterFact -CapabilityMap ([PSCustomObject]@{ + parameterComponentDefaults = [PSCustomObject]@{ context_names = 'Context_names' } + endpoints = [PSCustomObject]@{ + 'GET /w' = [PSCustomObject]@{ parameters = [PSCustomObject]@{ context_names = '2.26' } } + } + }) -Http207Endpoint @('get /W') + $f[0].DeclaresHttp207 | Should -BeTrue + } +} + +Describe 'Against real specs' { + BeforeAll { + # tools/specs/ is gitignored, so these skip wherever the cache is absent (a fresh + # clone, or CI without the spec-fetch step) rather than failing. + $script:spec227Path = Join-Path $script:repoRoot 'tools/specs/fb2.27.json' + $script:spec228Path = Join-Path $script:repoRoot 'tools/specs/fb2.28.json' + } + + It 'flags all four fleet-scoped GETs at fb, where the wire says size-1' -ForEach @( + @{ Version = '2.27' } + @{ Version = '2.28' } + ) -Skip:(-not (Test-Path (Join-Path (Split-Path -Parent $PSScriptRoot) 'tools/specs/fb2.28.json'))) { + # The acceptance criterion for the restructure. Under the withdrawn verb rule these + # four were invisible: rule said multi-value (GET), component said multi-value, so + # the old check reported nothing while all four reject a two-name context with + # 400 code 15. They must appear at BOTH versions -- the upstream fix is still only + # in review, so a sudden disappearance here means the fix landed, not that the + # check improved. + $specPath = Join-Path $script:repoRoot "tools/specs/fb$Version.json" + $spec = Get-Content -Path $specPath -Raw | ConvertFrom-Json + $facts = @(Get-PfbContextParameterFact ` + -SpecCapabilities @(Get-PfbSpecCapabilities -Spec $spec) ` + -Http207Endpoint @(Get-PfbContextHttp207Endpoint -Spec $spec)) + $findings = @(Get-PfbContextSignalDisagreement -Fact $facts) + + foreach ($endpoint in $script:defectiveFleetScopedGets) { + $f = $findings | Where-Object Endpoint -EQ $endpoint + $f | Should -Not -BeNullOrEmpty -Because "$endpoint is a known size-1 fleet-scoped endpoint referencing the multi-value component" + $f.Shape | Should -Be 'component-says-multi-value-but-no-allow-errors' + } + } + + It 'holds the module rule to size-1 for all four fleet-scoped GETs at fb' -ForEach @( + @{ Version = '2.27' } + @{ Version = '2.28' } + ) -Skip:(-not (Test-Path (Join-Path (Split-Path -Parent $PSScriptRoot) 'tools/specs/fb2.28.json'))) { + $specPath = Join-Path $script:repoRoot "tools/specs/fb$Version.json" + $spec = Get-Content -Path $specPath -Raw | ConvertFrom-Json + $facts = @(Get-PfbContextParameterFact -SpecCapabilities @(Get-PfbSpecCapabilities -Spec $spec)) + + foreach ($endpoint in $script:defectiveFleetScopedGets) { + ($facts | Where-Object Endpoint -EQ $endpoint).RuleSaysMultiValue | + Should -BeFalse -Because "$endpoint rejects a two-name context on the wire (400 code 15)" + } + } + + It 'tracks the 2.28 correction of DELETE /management-access-policies' -Skip:(-not (Test-Path (Join-Path (Split-Path -Parent $PSScriptRoot) 'tools/specs/fb2.28.json'))) { + # Pins a real upstream change: the component flipped to size-1 in 2.28, so the + # endpoint stops being a component-vs-allow_errors disagreement. Together with the + # fleet-scoped assertions above this shows the check tracks the spec rather than + # reporting a constant. + foreach ($case in @( + @{ Path = $script:spec227Path; Expected = 'Context_names_get'; ShouldDisagree = $true } + @{ Path = $script:spec228Path; Expected = 'Context_names'; ShouldDisagree = $false } + )) { + if (-not (Test-Path $case.Path)) { continue } + $spec = Get-Content -Path $case.Path -Raw | ConvertFrom-Json + $facts = @(Get-PfbContextParameterFact ` + -SpecCapabilities @(Get-PfbSpecCapabilities -Spec $spec) ` + -Http207Endpoint @(Get-PfbContextHttp207Endpoint -Spec $spec)) + $target = $facts | Where-Object Endpoint -EQ 'DELETE /management-access-policies' + $target.ContextComponent | Should -Be $case.Expected + $target.RuleSaysMultiValue | Should -BeFalse + + $findings = @(Get-PfbContextSignalDisagreement -Fact $facts) + $hit = @($findings | Where-Object Endpoint -EQ 'DELETE /management-access-policies').Count -gt 0 + $hit | Should -Be $case.ShouldDisagree + } + } + + It 'finds no unresolved context_names components in any spec on disk' -Skip:(-not (Test-Path (Join-Path (Split-Path -Parent $PSScriptRoot) 'tools/specs/fb2.28.json'))) { + # context_names does not exist before 2.17, so versions without it contribute zero + # facts rather than zero-of-many -- both are fine here. + foreach ($specFile in (Get-ChildItem -Path (Join-Path $script:repoRoot 'tools/specs') -Filter 'fb*.json')) { + $spec = Get-Content -Path $specFile.FullName -Raw | ConvertFrom-Json + $facts = @(Get-PfbContextParameterFact -SpecCapabilities @(Get-PfbSpecCapabilities -Spec $spec)) + @(Get-PfbContextUnresolvedComponent -Fact $facts).Count | + Should -Be 0 -Because "$($specFile.BaseName) should declare no inline context_names" + } + } +} + +Describe 'Committed capability map' { + BeforeAll { + $script:mapFacts = @(Get-PfbContextParameterFact -CapabilityMap ( + Get-Content -Path (Join-Path $script:repoRoot 'Data/PfbCapabilityMap.json') -Raw | ConvertFrom-Json)) + } + + It 'resolves every context_names component (none unresolved)' { + $script:mapFacts.Count | Should -BeGreaterThan 0 + @(Get-PfbContextUnresolvedComponent -Fact $script:mapFacts).Count | Should -Be 0 + } + + It 'holds the four fleet-scoped GETs to size-1' { + # Asserted against the map rather than a spec because this is the source the + # runtime injection path will read. No totals asserted -- the map is regenerated + # whenever specs are refreshed. + foreach ($endpoint in $script:defectiveFleetScopedGets) { + $f = $script:mapFacts | Where-Object Endpoint -EQ $endpoint + $f | Should -Not -BeNullOrEmpty -Because "$endpoint should be present in the capability map" + $f.ComponentSaysMultiValue | Should -BeTrue -Because 'the spec defect is still open upstream' + $f.DeclaresAllowErrors | Should -BeFalse + $f.RuleSaysMultiValue | Should -BeFalse + } + } +} diff --git a/Tests/PfbSpecTools.Tests.ps1 b/Tests/PfbSpecTools.Tests.ps1 new file mode 100644 index 0000000..2d96c90 --- /dev/null +++ b/Tests/PfbSpecTools.Tests.ps1 @@ -0,0 +1,727 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } +<# +.SYNOPSIS + Unit tests for tools/lib/PfbSpecTools.ps1 — the shared spec-extraction and + capability-diffing helpers used by tools/Update-PfbApiSpecs.ps1 and + tools/Build-PfbCapabilityMap.ps1. +.DESCRIPTION + These are pure-function unit tests against a small synthetic fixture + (Tests/Fixtures/sample-redoc-page.html) and inline synthetic spec objects — no + network access and no dependency on the real cached specs in tools/specs/. +#> + +BeforeAll { + $repoRoot = Split-Path -Parent $PSScriptRoot + . (Join-Path $repoRoot 'tools/lib/PfbSpecTools.ps1') + + $fixturePath = Join-Path $PSScriptRoot 'Fixtures/sample-redoc-page.html' + $script:fixtureHtml = Get-Content -Path $fixturePath -Raw +} + +Describe 'ConvertFrom-PfbRedocHtml' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + # ConvertFrom-PfbRedocHtml (tools/lib/PfbSpecTools.ps1) calls ConvertFrom-Json -Depth, + # which does not exist on Windows PowerShell 5.1 (added in PS6) -- this function is + # dev/CI-only tooling never loaded by the shipped module (see PureStorageFlashBladePowerShell.psm1, + # which only sources Private/ and Public/), so it's out of scope for 5.1 support. + It 'extracts the embedded OpenAPI document' { + $spec = ConvertFrom-PfbRedocHtml -Html $fixtureHtml + $spec.openapi | Should -Be '3.0.1' + $spec.info.version | Should -Be '9.9' + } + + It 'correctly walks past braces embedded inside string values (does not truncate early)' { + # The fixture's description contains a literal "{this}" and the trailing + # options.theme.spacing value contains "({ spacing }) => 10" — both would break + # a naive scan for the *first* unmatched-looking '}' instead of a real + # string-aware balanced-brace scan. + $spec = ConvertFrom-PfbRedocHtml -Html $fixtureHtml + $spec.info.description | Should -Match 'braces like \{this\} embedded' + $spec.paths.'/api/9.9/widgets' | Should -Not -BeNullOrEmpty + } + + It 'correctly handles escaped quotes and backslashes inside strings' { + $spec = ConvertFrom-PfbRedocHtml -Html $fixtureHtml + $spec.info.'x-fixture-escape-test' | Should -Match 'a quoted word' + $spec.info.'x-fixture-escape-test' | Should -Match '\\' + } + + It 'decodes non-ASCII characters correctly' { + $spec = ConvertFrom-PfbRedocHtml -Html $fixtureHtml + $spec.info.description | Should -Match 'café' + } + + It 'throws a clear error when the __redoc_state marker is missing' { + { ConvertFrom-PfbRedocHtml -Html 'nothing here' } | + Should -Throw '*__redoc_state*' + } + + It 'throws a clear error when the JSON is malformed' { + # 'undefined' is not a valid JSON literal in any parser (unlike a trailing + # comma, which some parsers tolerate) - this is unambiguously invalid. + $badHtml = '' + { ConvertFrom-PfbRedocHtml -Html $badHtml } | Should -Throw + } +} + +Describe 'ConvertTo-PfbNormalizedPath' { + It 'strips the "/api//" prefix from versioned paths' { + ConvertTo-PfbNormalizedPath -Path '/api/2.27/arrays' | Should -Be '/arrays' + ConvertTo-PfbNormalizedPath -Path '/api/2.0/file-systems' | Should -Be '/file-systems' + } + + It 'leaves unversioned auth/meta endpoints unchanged' { + ConvertTo-PfbNormalizedPath -Path '/api/login' | Should -Be '/api/login' + ConvertTo-PfbNormalizedPath -Path '/api/api_version' | Should -Be '/api/api_version' + ConvertTo-PfbNormalizedPath -Path '/oauth2/1.0/token' | Should -Be '/oauth2/1.0/token' + } +} + +Describe 'Resolve-PfbRef' { + BeforeAll { + $script:testSpec = [PSCustomObject]@{ + components = [PSCustomObject]@{ + parameters = [PSCustomObject]@{ + Names = [PSCustomObject]@{ name = 'names'; in = 'query' } + } + schemas = [PSCustomObject]@{ + Widget = [PSCustomObject]@{ '$ref' = '#/components/schemas/WidgetAlias' } + WidgetAlias = [PSCustomObject]@{ type = 'object'; properties = [PSCustomObject]@{ id = @{ type = 'string' } } } + } + } + } + } + + It 'resolves a single-level $ref' { + $node = [PSCustomObject]@{ '$ref' = '#/components/parameters/Names' } + $resolved = Resolve-PfbRef -Node $node -Spec $testSpec + $resolved.name | Should -Be 'names' + } + + It 'follows chained $refs to the final target' { + $node = [PSCustomObject]@{ '$ref' = '#/components/schemas/Widget' } + $resolved = Resolve-PfbRef -Node $node -Spec $testSpec + $resolved.type | Should -Be 'object' + } + + It 'returns non-$ref nodes unchanged' { + $node = [PSCustomObject]@{ name = 'plain'; in = 'query' } + $resolved = Resolve-PfbRef -Node $node -Spec $testSpec + $resolved.name | Should -Be 'plain' + } + + It 'returns $null unchanged' { + Resolve-PfbRef -Node $null -Spec $testSpec | Should -BeNullOrEmpty + } +} + +Describe 'Get-PfbSchemaPropertyNames' { + BeforeAll { + $script:testSpec = [PSCustomObject]@{ + components = [PSCustomObject]@{ + schemas = [PSCustomObject]@{ + BaseResource = [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ id = @{ type = 'string' }; name = @{ type = 'string' } } + } + ResourcePatch = [PSCustomObject]@{ + allOf = @( + [PSCustomObject]@{ '$ref' = '#/components/schemas/BaseResource' } + [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ enabled = @{ type = 'boolean' } } + } + ) + } + } + } + } + } + + It 'reads direct properties off an inline schema' { + $schema = [PSCustomObject]@{ properties = [PSCustomObject]@{ a = @{}; b = @{} } } + $names = Get-PfbSchemaPropertyNames -Schema $schema -Spec $testSpec + $names | Should -Contain 'a' + $names | Should -Contain 'b' + } + + It 'resolves a $ref schema before reading properties' { + $schema = [PSCustomObject]@{ '$ref' = '#/components/schemas/BaseResource' } + $names = Get-PfbSchemaPropertyNames -Schema $schema -Spec $testSpec + $names | Should -Contain 'id' + $names | Should -Contain 'name' + } + + It 'merges properties across allOf branches, including $ref branches' { + $schema = [PSCustomObject]@{ '$ref' = '#/components/schemas/ResourcePatch' } + $names = Get-PfbSchemaPropertyNames -Schema $schema -Spec $testSpec + $names | Should -Contain 'id' + $names | Should -Contain 'name' + $names | Should -Contain 'enabled' + } + + It 'returns an empty list for a null schema' { + Get-PfbSchemaPropertyNames -Schema $null -Spec $testSpec | Should -BeNullOrEmpty + } +} + +Describe 'Get-PfbSchemaPropertyDetails' { + BeforeAll { + $script:testSpec = [PSCustomObject]@{ + components = [PSCustomObject]@{ + schemas = [PSCustomObject]@{ + # PIN fixture: itself readOnly, but only reachable through a property + # that is a BARE $ref (no sibling keys) -- must not leak through. + _readOnlyLeaf = [PSCustomObject]@{ + type = 'string' + readOnly = $true + } + BaseResource = [PSCustomObject]@{ + type = 'object' + required = @('id') + properties = [PSCustomObject]@{ + id = [PSCustomObject]@{ type = 'string' } + name = [PSCustomObject]@{ type = 'string'; format = 'name-format' } + } + } + ResourcePatch = [PSCustomObject]@{ + allOf = @( + [PSCustomObject]@{ '$ref' = '#/components/schemas/BaseResource' } + [PSCustomObject]@{ + type = 'object' + required = @('enabled') + properties = [PSCustomObject]@{ + enabled = [PSCustomObject]@{ type = 'boolean'; readOnly = $true } + status = [PSCustomObject]@{ type = 'string'; deprecated = $true } + ref_only = [PSCustomObject]@{ '$ref' = '#/components/schemas/_readOnlyLeaf' } + } + } + ) + } + # Merge-rule fixture: 'shared' declared in two allOf branches of the + # SAME schema, read-only in only one of them. + MergeConflict = [PSCustomObject]@{ + allOf = @( + [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ shared = [PSCustomObject]@{ type = 'string' } } + } + [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ shared = [PSCustomObject]@{ type = 'string'; readOnly = $true } } + } + ) + } + + # OwnerSchema fixture, shaped like the brief's worked example: + # CertificatePatch = allOf[ _certificateBase, {inline: days,...} ] + # _certificateBase = allOf[ _realmsReference, {inline: certificate_type,...} ] + # Three tiers -> three different expected owners. + _realmsReference = [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ realms = [PSCustomObject]@{ type = 'array' } } + } + _certificateBase = [PSCustomObject]@{ + allOf = @( + [PSCustomObject]@{ '$ref' = '#/components/schemas/_realmsReference' } + [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ + certificate_type = [PSCustomObject]@{ type = 'string' } + issued_by = [PSCustomObject]@{ type = 'string' } + } + } + ) + } + CertificatePatch = [PSCustomObject]@{ + allOf = @( + [PSCustomObject]@{ '$ref' = '#/components/schemas/_certificateBase' } + [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ + days = [PSCustomObject]@{ type = 'integer' } + passphrase = [PSCustomObject]@{ type = 'string' } + } + } + ) + } + + # Outermost-wins fixture: 'dup' declared directly by TWO different named + # components in the same chain -- OwnerSchema must pick the shallower one + # (OuterOwner, declared inline directly under OuterOwner's own allOf), not + # the deeper one reached through a further nested $ref (InnerOwner). The + # inline (OuterOwner-owned) branch is listed FIRST in the allOf array so + # this is unambiguous under the "first-seen in traversal order" tie-break + # documented on Add-PfbSchemaPropertyNodes (own/inherited-owner properties + # are always recorded before a sibling branch's nested named $ref is + # walked into). + InnerOwner = [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ dup = [PSCustomObject]@{ type = 'string' } } + } + OuterOwner = [PSCustomObject]@{ + allOf = @( + [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ dup = [PSCustomObject]@{ type = 'string' } } + } + [PSCustomObject]@{ '$ref' = '#/components/schemas/InnerOwner' } + ) + } + + # Fully inline fixture: no $ref anywhere in the chain -- OwnerSchema must + # come out $null (explicitly, not ''). + FullyInline = [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ freeform = [PSCustomObject]@{ type = 'string' } } + } + } + } + } + } + + It 'resolves ReadOnly and Deprecated through an allOf/$ref chain' { + $schema = [PSCustomObject]@{ '$ref' = '#/components/schemas/ResourcePatch' } + $details = Get-PfbSchemaPropertyDetails -Schema $schema -Spec $testSpec + + ($details | Where-Object Name -eq 'enabled').ReadOnly | Should -Be $true + ($details | Where-Object Name -eq 'status').Deprecated | Should -Be $true + ($details | Where-Object Name -eq 'id').ReadOnly | Should -Be $false + } + + It 'merge rule: a property marked read-only in one allOf branch and not another comes out read-only' { + $schema = [PSCustomObject]@{ '$ref' = '#/components/schemas/MergeConflict' } + $details = Get-PfbSchemaPropertyDetails -Schema $schema -Spec $testSpec + + ($details | Where-Object Name -eq 'shared').ReadOnly | Should -Be $true + } + + It 'PIN: does not follow a property''s own $ref to a read-only-bearing schema' { + # ref_only's own node is a bare '$ref' to _readOnlyLeaf, which IS readOnly:true. + # Resolving into it would (wrongly) report ref_only as read-only. + $schema = [PSCustomObject]@{ '$ref' = '#/components/schemas/ResourcePatch' } + $details = Get-PfbSchemaPropertyDetails -Schema $schema -Spec $testSpec + + ($details | Where-Object Name -eq 'ref_only').ReadOnly | Should -Be $false + } + + It 'PIN regression: ref_only still resolves ReadOnly=$false AND now also carries its correct OwnerSchema' { + # Adding OwnerSchema tracking touches the same ref-resolution code path as the PIN + # above -- this is exactly where a regression would hide. Re-assert both facts + # together: the owner is recorded from the CHAIN being walked into (the allOf + # branch of ResourcePatch that declares ref_only), never from resolving ref_only's + # own '$ref' value (which would incorrectly suggest an owner of '_readOnlyLeaf'). + $schema = [PSCustomObject]@{ '$ref' = '#/components/schemas/ResourcePatch' } + $details = Get-PfbSchemaPropertyDetails -Schema $schema -Spec $testSpec + + $refOnly = $details | Where-Object Name -eq 'ref_only' + $refOnly.ReadOnly | Should -Be $false + $refOnly.OwnerSchema | Should -Be 'ResourcePatch' + } + + It 'OwnerSchema resolves to the nearest named component through an allOf + $ref chain (three tiers)' { + # Mirrors the brief's PATCH /certificates worked example exactly: + # CertificatePatch = allOf[ _certificateBase, {inline: days, passphrase} ] + # _certificateBase = allOf[ _realmsReference, {inline: certificate_type, issued_by} ] + $schema = [PSCustomObject]@{ '$ref' = '#/components/schemas/CertificatePatch' } + $details = Get-PfbSchemaPropertyDetails -Schema $schema -Spec $testSpec + + ($details | Where-Object Name -eq 'days').OwnerSchema | Should -Be 'CertificatePatch' + ($details | Where-Object Name -eq 'passphrase').OwnerSchema | Should -Be 'CertificatePatch' + ($details | Where-Object Name -eq 'certificate_type').OwnerSchema | Should -Be '_certificateBase' + ($details | Where-Object Name -eq 'issued_by').OwnerSchema | Should -Be '_certificateBase' + ($details | Where-Object Name -eq 'realms').OwnerSchema | Should -Be '_realmsReference' + } + + It 'attributes a property declared in an anonymous inline branch to the nearest NAMED ancestor, not the branch itself' { + # 'days' is declared in CertificatePatch's own anonymous inline allOf branch -- + # there is no schema named after that branch; the only correct owner is the + # nearest enclosing NAMED component, CertificatePatch itself. + $schema = [PSCustomObject]@{ '$ref' = '#/components/schemas/CertificatePatch' } + $details = Get-PfbSchemaPropertyDetails -Schema $schema -Spec $testSpec + + ($details | Where-Object Name -eq 'days').OwnerSchema | Should -Be 'CertificatePatch' + ($details | Where-Object Name -eq 'days').OwnerSchema | Should -Not -Be $null + } + + It 'a fully inline body with no $ref anywhere yields OwnerSchema $null, explicitly (not '''')' { + # Deliberately pass the schema NODE ITSELF, not a '$ref' wrapper around it -- this + # simulates an operation whose request body is written fully inline in the spec + # (no $ref anywhere in its chain), the only case where OwnerSchema is $null. + $schema = $testSpec.components.schemas.FullyInline + $details = Get-PfbSchemaPropertyDetails -Schema $schema -Spec $testSpec + + $freeform = $details | Where-Object Name -eq 'freeform' + $freeform.OwnerSchema | Should -BeNullOrEmpty + $null -eq $freeform.OwnerSchema | Should -Be $true + $freeform.OwnerSchema -eq '' | Should -Be $false + } + + It 'outermost-wins: when two named schemas declare the same property, the shallower one wins' { + # OuterOwner = allOf[ {inline: dup}, InnerOwner ] -- 'dup' is declared both directly + # (inline, owned by OuterOwner itself) and via a nested named $ref to InnerOwner. + # The outer (closer to the operation's own body schema) declaration must win. + $schema = [PSCustomObject]@{ '$ref' = '#/components/schemas/OuterOwner' } + $details = Get-PfbSchemaPropertyDetails -Schema $schema -Spec $testSpec + + ($details | Where-Object Name -eq 'dup').OwnerSchema | Should -Be 'OuterOwner' + } + + It 'populates Type and Format from the property''s own node' { + $schema = [PSCustomObject]@{ '$ref' = '#/components/schemas/BaseResource' } + $details = Get-PfbSchemaPropertyDetails -Schema $schema -Spec $testSpec + + ($details | Where-Object Name -eq 'name').Type | Should -Be 'string' + ($details | Where-Object Name -eq 'name').Format | Should -Be 'name-format' + } + + It 'collects Required across allOf branches' { + $schema = [PSCustomObject]@{ '$ref' = '#/components/schemas/ResourcePatch' } + $details = Get-PfbSchemaPropertyDetails -Schema $schema -Spec $testSpec + + ($details | Where-Object Name -eq 'id').Required | Should -Be $true + ($details | Where-Object Name -eq 'enabled').Required | Should -Be $true + ($details | Where-Object Name -eq 'name').Required | Should -Be $false + ($details | Where-Object Name -eq 'status').Required | Should -Be $false + } + + It 'returns an empty list for a null schema' { + Get-PfbSchemaPropertyDetails -Schema $null -Spec $testSpec | Should -BeNullOrEmpty + } + + It 'sorts its output by Name regardless of traversal order (the sort invariant)' { + # ResourcePatch's declaration/traversal order is id, name, enabled, status, ref_only -- + # deliberately NOT alphabetical, so this assertion actually exercises the sort. + $schema = [PSCustomObject]@{ '$ref' = '#/components/schemas/ResourcePatch' } + $details = Get-PfbSchemaPropertyDetails -Schema $schema -Spec $testSpec + + @($details.Name) | Should -Be (@($details.Name) | Sort-Object) + } + + It 'Get-PfbSchemaPropertyNames (the wrapper) returns TRAVERSAL order on the same fixture, NOT sorted' { + # Companion to the sort-invariant test above: Get-PfbSchemaPropertyDetails sorts, + # Get-PfbSchemaPropertyNames deliberately does not (controller ruling -- see that + # function's help). Pinning both on the same non-alphabetical fixture is what stops + # either behaviour silently flipping later. + $schema = [PSCustomObject]@{ '$ref' = '#/components/schemas/ResourcePatch' } + $names = Get-PfbSchemaPropertyNames -Schema $schema -Spec $testSpec + + @($names) | Should -Be @('id', 'name', 'enabled', 'status', 'ref_only') + } +} + +Describe 'Get-PfbSpecCapabilities' { + BeforeAll { + $script:testSpec = [PSCustomObject]@{ + paths = [PSCustomObject]@{ + '/api/9.9/widgets' = [PSCustomObject]@{ + 'x-pure-authorization-resource' = 'widgets' + get = [PSCustomObject]@{ + parameters = @( + [PSCustomObject]@{ '$ref' = '#/components/parameters/Filter' } + # Inline, no $ref -- must contribute to Parameters but have no + # entry in ParameterComponents at all (not null/''). + [PSCustomObject]@{ name = 'raw_only'; in = 'query' } + ) + } + post = [PSCustomObject]@{ + requestBody = [PSCustomObject]@{ + content = [PSCustomObject]@{ + 'application/json' = [PSCustomObject]@{ + schema = [PSCustomObject]@{ '$ref' = '#/components/schemas/WidgetPost' } + } + } + } + } + } + } + components = [PSCustomObject]@{ + parameters = [PSCustomObject]@{ + Filter = [PSCustomObject]@{ name = 'filter'; in = 'query' } + } + schemas = [PSCustomObject]@{ + WidgetPost = [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ + name = [PSCustomObject]@{ type = 'string' } + color = [PSCustomObject]@{ type = 'string' } + id = [PSCustomObject]@{ type = 'string'; readOnly = $true } + status = [PSCustomObject]@{ type = 'string'; deprecated = $true } + } + } + } + } + } + } + + It 'skips vendor extension keys like x-pure-authorization-resource' { + $caps = Get-PfbSpecCapabilities -Spec $testSpec + ($caps | ForEach-Object { $_.Method }) | Should -Not -Contain 'X-PURE-AUTHORIZATION-RESOURCE' + } + + It 'produces one record per (method, normalized path)' { + $caps = Get-PfbSpecCapabilities -Spec $testSpec + $caps.Count | Should -Be 2 + ($caps | Where-Object Method -eq 'GET').Path | Should -Be '/widgets' + ($caps | Where-Object Method -eq 'POST').Path | Should -Be '/widgets' + } + + It 'resolves $ref parameters to their names' { + $caps = Get-PfbSpecCapabilities -Spec $testSpec + $getCap = $caps | Where-Object Method -eq 'GET' + $getCap.Parameters | Should -Contain 'filter' + } + + It 'resolves $ref request-body schemas to their property names' { + $caps = Get-PfbSpecCapabilities -Spec $testSpec + $postCap = $caps | Where-Object Method -eq 'POST' + $postCap.BodyProperties | Should -Contain 'name' + $postCap.BodyProperties | Should -Contain 'color' + } + + It 'populates BodyPropertyDetails for a mix of read-only, deprecated and plain properties' { + $caps = Get-PfbSpecCapabilities -Spec $testSpec + $postCap = $caps | Where-Object Method -eq 'POST' + $postCap.BodyPropertyDetails.Count | Should -Be 4 + ($postCap.BodyPropertyDetails | Where-Object Name -eq 'name').ReadOnly | Should -Be $false + ($postCap.BodyPropertyDetails | Where-Object Name -eq 'id').ReadOnly | Should -Be $true + ($postCap.BodyPropertyDetails | Where-Object Name -eq 'status').Deprecated | Should -Be $true + } + + It 'projects ReadOnlyBodyProperties and DeprecatedBodyProperties' { + $caps = Get-PfbSpecCapabilities -Spec $testSpec + $postCap = $caps | Where-Object Method -eq 'POST' + $postCap.ReadOnlyBodyProperties | Should -Be @('id') + $postCap.DeprecatedBodyProperties | Should -Be @('status') + } + + It 'populates ParameterComponents with the $ref''d parameter''s component name' { + $caps = Get-PfbSpecCapabilities -Spec $testSpec + $getCap = $caps | Where-Object Method -eq 'GET' + $getCap.ParameterComponents['filter'] | Should -Be 'Filter' + } + + It 'omits the ParameterComponents key entirely for an inline (no $ref) parameter' { + $caps = Get-PfbSpecCapabilities -Spec $testSpec + $getCap = $caps | Where-Object Method -eq 'GET' + $getCap.Parameters | Should -Contain 'raw_only' + $getCap.ParameterComponents.ContainsKey('raw_only') | Should -Be $false + } + + It 'deterministically resolves (and warns on) a parameter name that resolves to two different components' { + $conflictSpec = [PSCustomObject]@{ + paths = [PSCustomObject]@{ + '/api/9.9/conflict' = [PSCustomObject]@{ + get = [PSCustomObject]@{ + parameters = @( + [PSCustomObject]@{ '$ref' = '#/components/parameters/ZParam' } + [PSCustomObject]@{ '$ref' = '#/components/parameters/AParam' } + ) + } + } + } + components = [PSCustomObject]@{ + parameters = [PSCustomObject]@{ + ZParam = [PSCustomObject]@{ name = 'dup'; in = 'query' } + AParam = [PSCustomObject]@{ name = 'dup'; in = 'query' } + } + } + } + + $caps = Get-PfbSpecCapabilities -Spec $conflictSpec -WarningVariable warnings -WarningAction SilentlyContinue + $caps[0].ParameterComponents['dup'] | Should -Be 'AParam' + ($warnings -join ' ') | Should -Match 'multiple different components' + } + + It 'returns an empty list for a spec with no paths' { + $emptySpec = [PSCustomObject]@{ paths = [PSCustomObject]@{} } + Get-PfbSpecCapabilities -Spec $emptySpec | Should -BeNullOrEmpty + } +} + +Describe 'Get-PfbSwaggerIndexVersions' { + It 'extracts and sorts version numbers correctly, including double-digit minors' { + $html = @' +2.9 +2.10 +2.2 +'@ + $versions = Get-PfbSwaggerIndexVersions -IndexHtml $html + # Numeric sort must place 2.10 after 2.9, not lexicographically before it. + $versions | Should -Be @('2.2', '2.9', '2.10') + } + + It 'de-duplicates repeated links' { + $html = 'xy' + $versions = Get-PfbSwaggerIndexVersions -IndexHtml $html + $versions | Should -Be @('2.5') + } + + It 'returns an empty list when no versions are found' { + Get-PfbSwaggerIndexVersions -IndexHtml '' | Should -BeNullOrEmpty + } +} + +Describe 'Get-PfbResponseSchema' { + It 'returns the first 2xx application/json response schema' { + $op = [PSCustomObject]@{ + responses = [PSCustomObject]@{ + '200' = [PSCustomObject]@{ + content = [PSCustomObject]@{ + 'application/json' = [PSCustomObject]@{ + schema = [PSCustomObject]@{ marker = 'yes' } + } + } + } + '400' = [PSCustomObject]@{ description = 'bad' } + } + } + $result = Get-PfbResponseSchema -Operation $op -Spec ([PSCustomObject]@{}) + $result.marker | Should -Be 'yes' + } + + It 'returns $null when no 2xx response carries a schema' { + $op = [PSCustomObject]@{ + responses = [PSCustomObject]@{ '400' = [PSCustomObject]@{ description = 'bad' } } + } + Get-PfbResponseSchema -Operation $op -Spec ([PSCustomObject]@{}) | Should -BeNullOrEmpty + } +} + +Describe 'Get-PfbSpecResponseShapes' { + BeforeAll { + $script:shapeSpec = [PSCustomObject]@{ + paths = [PSCustomObject]@{ + '/api/2.0/file-systems' = [PSCustomObject]@{ + get = [PSCustomObject]@{ + responses = [PSCustomObject]@{ + '200' = [PSCustomObject]@{ + content = [PSCustomObject]@{ + 'application/json' = [PSCustomObject]@{ + schema = [PSCustomObject]@{ + properties = [PSCustomObject]@{ + items = [PSCustomObject]@{ + type = 'array' + items = [PSCustomObject]@{ + properties = [PSCustomObject]@{ + name = [PSCustomObject]@{ type = 'string' } + destroyed = [PSCustomObject]@{ type = 'boolean' } + } + } + } + total_item_count = [PSCustomObject]@{ type = 'integer' } + errors = [PSCustomObject]@{ type = 'array' } + } + } + } + } + } + } + } + } + } + } + } + + It 'extracts envelope properties including errors' { + $shapes = @(Get-PfbSpecResponseShapes -Spec $script:shapeSpec) + $shapes.Count | Should -Be 1 + $shapes[0].Method | Should -Be 'GET' + $shapes[0].Path | Should -Be '/file-systems' + $shapes[0].EnvelopeProperties | Should -Contain 'errors' + $shapes[0].EnvelopeProperties | Should -Contain 'total_item_count' + } + + It 'descends one level into items[] and no further' { + $shapes = @(Get-PfbSpecResponseShapes -Spec $script:shapeSpec) + $shapes[0].ItemProperties | Should -Contain 'name' + $shapes[0].ItemProperties | Should -Contain 'destroyed' + # depth 3 is deliberately excluded -- nothing dotted is ever emitted + @($shapes[0].ItemProperties | Where-Object { $_ -like '*.*' }).Count | Should -Be 0 + } + + It 'emits deterministically sorted collections' { + $shapes = @(Get-PfbSpecResponseShapes -Spec $script:shapeSpec) + $shapes[0].EnvelopeProperties | Should -Be (@($shapes[0].EnvelopeProperties) | Sort-Object) + $shapes[0].ItemProperties | Should -Be (@($shapes[0].ItemProperties) | Sort-Object) + } +} + +Describe 'Get-PfbSpecResponseShapes -MaxDepth (regression: the 184-false-removal trap)' { + BeforeAll { + # An allOf chain 10 levels deep -- deeper than the walker's default MaxDepth of 8. + # This mirrors components.schemas.FileSystem in fb2.13-2.16, which reads 4 properties + # at depth 8 and 21 at depth 16. Reading it at the default would report the deep + # property as absent, which the accumulator would then record as a REMOVAL. + $schemas = [PSCustomObject]@{} + $schemas | Add-Member -NotePropertyName 'Level9' -NotePropertyValue ([PSCustomObject]@{ + properties = [PSCustomObject]@{ deep_field = [PSCustomObject]@{ type = 'string' } } + }) + foreach ($i in 8..0) { + $schemas | Add-Member -NotePropertyName "Level$i" -NotePropertyValue ([PSCustomObject]@{ + allOf = @([PSCustomObject]@{ '$ref' = "#/components/schemas/Level$($i + 1)" }) + }) + } + $script:deepSpec = [PSCustomObject]@{ + components = [PSCustomObject]@{ schemas = $schemas } + paths = [PSCustomObject]@{ + '/api/2.0/deep' = [PSCustomObject]@{ + get = [PSCustomObject]@{ + responses = [PSCustomObject]@{ + '200' = [PSCustomObject]@{ + content = [PSCustomObject]@{ + 'application/json' = [PSCustomObject]@{ + schema = [PSCustomObject]@{ + properties = [PSCustomObject]@{ + items = [PSCustomObject]@{ + type = 'array' + # 'shallow_field' sits inline on the items element schema, ALONGSIDE the + # deep allOf chain, and is reachable at any MaxDepth. It exists purely to + # give the depth-8 test below a positive anchor: without it, that test's + # only assertion is a negative one, and an implementation that returned + # nothing at all would satisfy it just as well as a correctly + # depth-truncated one. See that It's own comment. + items = [PSCustomObject]@{ + properties = [PSCustomObject]@{ shallow_field = [PSCustomObject]@{ type = 'string' } } + allOf = @([PSCustomObject]@{ '$ref' = '#/components/schemas/Level0' }) + } + } + } + } + } + } + } + } + } + } + } + } + } + + It 'finds a property nested deeper than the default MaxDepth of 8' { + $shapes = @(Get-PfbSpecResponseShapes -Spec $script:deepSpec) + $shapes[0].ItemProperties | Should -Contain 'deep_field' + # At the real default BOTH are reached; contrast with the depth-8 test below, where + # only the shallow one survives. The pair is what makes the difference attributable + # to depth rather than to the walk having failed outright. + $shapes[0].ItemProperties | Should -Contain 'shallow_field' + } + + It 'would MISS that property at the default depth -- proving the constraint is real' { + $shapes = @(Get-PfbSpecResponseShapes -Spec $script:deepSpec -MaxDepth 8) + + # POSITIVE ANCHORS FIRST, and they are the entire point of this ordering. The + # assertion this test exists for is a NEGATIVE one, and a negative assertion about a + # collection is satisfied by every degenerate outcome as well as by the intended one: + # if the function returned an empty list, $shapes[0] would be $null and + # `$null | Should -Not -Contain 'deep_field'` would pass, testing nothing. (Confirmed + # by mutation: gutting Get-PfbSpecResponseShapes to return an empty list left this + # test green.) These two anchors fail on that mutant, so reaching the negative below + # proves the walk really ran, really produced this endpoint's record, and really + # populated ItemProperties -- and that the ONLY thing missing at depth 8 is the + # property that needs more than 8 levels to reach. + $shapes.Count | Should -Be 1 + $shapes[0].ItemProperties | Should -Contain 'shallow_field' + + $shapes[0].ItemProperties | Should -Not -Contain 'deep_field' + } +} diff --git a/Tests/PfbValueEnumTools.Tests.ps1 b/Tests/PfbValueEnumTools.Tests.ps1 new file mode 100644 index 0000000..b2666ac --- /dev/null +++ b/Tests/PfbValueEnumTools.Tests.ps1 @@ -0,0 +1,538 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } +<# +.SYNOPSIS + Unit tests for tools/lib/PfbValueEnumTools.ps1 — the prose "Valid/Possible values" + extraction helpers used by tools/Build-PfbValueEnumMap.ps1. +.DESCRIPTION + Pure-function unit tests against small synthetic spec objects (same + [PSCustomObject]-fixture style as Tests/PfbSpecTools.Tests.ps1) — no network access + and no dependency on the real cached specs in tools/specs/. +#> + +BeforeAll { + $repoRoot = Split-Path -Parent $PSScriptRoot + . (Join-Path $repoRoot 'tools/lib/PfbSpecTools.ps1') + . (Join-Path $repoRoot 'tools/lib/PfbValueEnumTools.ps1') +} + +Describe 'Get-PfbValueEnumTriggerSentence' { + It 'extracts a "Valid values are ... ." sentence' { + $desc = "The versioning state for objects within the bucket. Valid values are ``none``, ``enabled``, and ``suspended``." + $trigger = Get-PfbValueEnumTriggerSentence -Description $desc + $trigger | Should -Be 'Valid values are `none`, `enabled`, and `suspended`.' + } + + It 'extracts a "Possible values include ... ." sentence' { + $desc = "The minimum severity for notifications. Possible values include ``info``, ``warning``, and ``critical``. Defaults to info." + $trigger = Get-PfbValueEnumTriggerSentence -Description $desc + $trigger | Should -Be 'Possible values include `info`, `warning`, and `critical`.' + } + + It 'returns $null when no trigger phrase is present' { + Get-PfbValueEnumTriggerSentence -Description 'A perfectly ordinary free-text description.' | Should -BeNullOrEmpty + } + + It 'returns $null for an empty or missing description' { + Get-PfbValueEnumTriggerSentence -Description $null | Should -BeNullOrEmpty + Get-PfbValueEnumTriggerSentence -Description '' | Should -BeNullOrEmpty + } + + It 'isolates only the trigger sentence, not trailing prose that repeats the values' { + # Regression for the trigger-sentence-scoping rule: the preset export-rule + # description explains each backtick-quoted value again in a paragraph *after* + # the enum sentence — the trigger sentence itself must not swallow that tail. + $desc = @' +Specifies access control for the export. Valid values are `root-squash`, `all-squash`, and +`no-root-squash`. +`root-squash` prevents client users and groups with root privilege from mapping their +root privilege to a file system. +'@ + $trigger = Get-PfbValueEnumTriggerSentence -Description $desc + $trigger | Should -Match '^Valid values are `root-squash`, `all-squash`, and\s*\n`no-root-squash`\.$' + $trigger | Should -Not -Match 'prevents client users' + } +} + +Describe 'ConvertFrom-PfbValueEnumProse' { + It 'parses the dominant backtick-quoted pattern' { + $result = ConvertFrom-PfbValueEnumProse -TriggerSentence 'Valid values are `none`, `enabled`, and `suspended`.' + $result.Parsed | Should -BeTrue + $result.Values | Should -Be @('none', 'enabled', 'suspended') + } + + It 'parses a double-quoted value list' { + $result = ConvertFrom-PfbValueEnumProse -TriggerSentence 'Valid values are "policy", "sacl".' + $result.Parsed | Should -BeTrue + $result.Values | Should -Be @('policy', 'sacl') + } + + It 'parses a bare comma-separated token list' { + $result = ConvertFrom-PfbValueEnumProse -TriggerSentence 'Valid values include QSFP, QSFP+, QSFP28, QSFP56, QSFP-DD, RJ-45, and -.' + $result.Parsed | Should -BeTrue + $result.Values | Should -Be @('QSFP', 'QSFP+', 'QSFP28', 'QSFP56', 'QSFP-DD', 'RJ-45', '-') + } + + It 'does not force-parse a malformed/unbalanced-quote sentence' { + $result = ConvertFrom-PfbValueEnumProse -TriggerSentence "Valid values include 'success' or failure'." + $result.Parsed | Should -BeFalse + $result.Values | Should -BeNullOrEmpty + $result.TriggerText | Should -Not -BeNullOrEmpty + } + + It 'does not force-parse a backtick-quoted list with a missing opening backtick (real spec bug: policy_type/smb-client)' { + # Real, confirmed-malformed text from the FlashBlade OpenAPI spec (present in every + # cached version REST 2.14-2.27): the opening backtick before `smb-client` is + # missing. A naive backtick-pair regex re-synchronizes on the next backtick and + # silently drops `smb-client` from the value list while injecting garbage + # (comma/whitespace-only) "values" -- this must be classified unparsed instead. + $result = ConvertFrom-PfbValueEnumProse -TriggerSentence 'Valid values include `alert`, `audit`, `s3-export`, smb-client`, `smb-share`, and `telemetry-metrics`.' + $result.Parsed | Should -BeFalse + $result.Values | Should -BeNullOrEmpty + $result.TriggerText | Should -Not -BeNullOrEmpty + } + + It 'does not force-parse a numeric-range sentence that happens to match the trigger phrase' { + $result = ConvertFrom-PfbValueEnumProse -TriggerSentence "Valid values are`nin the range of 300000 and 10800000." + $result.Parsed | Should -BeFalse + $result.Values | Should -BeNullOrEmpty + } + + It 'does not force-parse free-text prose that happens to match the trigger phrase' { + $result = ConvertFrom-PfbValueEnumProse -TriggerSentence "Valid values are controllers`nand blades from hardware list." + $result.Parsed | Should -BeFalse + $result.Values | Should -BeNullOrEmpty + } + + It 'preserves the raw trigger text on both parsed and unparsed results' { + $sentence = 'Valid values are `a`, `b`.' + (ConvertFrom-PfbValueEnumProse -TriggerSentence $sentence).TriggerText | Should -Be $sentence + } +} + +Describe 'Get-PfbSchemaPropertyDescriptions' { + BeforeAll { + $script:testSpec = [PSCustomObject]@{ + components = [PSCustomObject]@{ + schemas = [PSCustomObject]@{ + BaseResource = [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ + id = [PSCustomObject]@{ type = 'string'; description = 'The resource identifier.' } + name = [PSCustomObject]@{ type = 'string' } + } + } + ResourcePatch = [PSCustomObject]@{ + allOf = @( + [PSCustomObject]@{ '$ref' = '#/components/schemas/BaseResource' } + [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ + enabled = [PSCustomObject]@{ type = 'boolean'; description = 'Valid values are `true` and `false`.' } + } + } + ) + } + ListWrapper = [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ + tags = [PSCustomObject]@{ + type = 'array' + items = [PSCustomObject]@{ type = 'string'; description = 'Valid values are `a`, `b`.' } + } + } + } + } + } + } + } + + It 'reads direct property descriptions off an inline schema' { + $schema = [PSCustomObject]@{ + properties = [PSCustomObject]@{ + a = [PSCustomObject]@{ description = 'first' } + b = [PSCustomObject]@{ description = 'second' } + } + } + $descriptions = Get-PfbSchemaPropertyDescriptions -Schema $schema -Spec $testSpec + $descriptions['a'] | Should -Be 'first' + $descriptions['b'] | Should -Be 'second' + } + + It 'resolves a $ref schema before reading property descriptions' { + $schema = [PSCustomObject]@{ '$ref' = '#/components/schemas/BaseResource' } + $descriptions = Get-PfbSchemaPropertyDescriptions -Schema $schema -Spec $testSpec + $descriptions['id'] | Should -Be 'The resource identifier.' + } + + It 'merges descriptions across allOf branches, including $ref branches' { + $schema = [PSCustomObject]@{ '$ref' = '#/components/schemas/ResourcePatch' } + $descriptions = Get-PfbSchemaPropertyDescriptions -Schema $schema -Spec $testSpec + $descriptions['id'] | Should -Be 'The resource identifier.' + $descriptions['enabled'] | Should -Be 'Valid values are `true` and `false`.' + } + + It 'descends into an array property''s items schema for its description' { + $descriptions = Get-PfbSchemaPropertyDescriptions -Schema $testSpec.components.schemas.ListWrapper -Spec $testSpec + $descriptions['tags'] | Should -Be 'Valid values are `a`, `b`.' + } + + It 'omits properties with no resolvable description' { + $schema = [PSCustomObject]@{ '$ref' = '#/components/schemas/BaseResource' } + $descriptions = Get-PfbSchemaPropertyDescriptions -Schema $schema -Spec $testSpec + $descriptions.Contains('name') | Should -BeFalse + } + + It 'returns an empty hashtable for a null schema' { + (Get-PfbSchemaPropertyDescriptions -Schema $null -Spec $testSpec).Count | Should -Be 0 + } + + It 'returns descriptions in the schema''s own declared property order, not scrambled by hashtable enumeration' { + # The returned dictionary is internally staged through a plain Hashtable (not + # [ordered]) in earlier versions of this function, whose .Keys enumeration order + # depends on .NET's per-process-randomized string hash codes -- without an + # explicit [ordered] type, Reports/PfbValueEnumMap.json's entry order silently + # varies run-to-run on identical input (confirmed live: regenerating it twice + # produced two byte-different files, e.g. SnmpManager.version/.notification + # swapping order). Property names below are deliberately declared out of + # alphabetical order to prove insertion order (not sort order) is preserved. + $schema = [PSCustomObject]@{ + properties = [PSCustomObject]@{ + zebra = [PSCustomObject]@{ description = 'z' } + apple = [PSCustomObject]@{ description = 'a' } + mango = [PSCustomObject]@{ description = 'm' } + } + } + $descriptions = Get-PfbSchemaPropertyDescriptions -Schema $schema -Spec $testSpec + @($descriptions.Keys) | Should -Be @('zebra', 'apple', 'mango') + } +} + +Describe 'Get-PfbSpecValueEnums' { + BeforeAll { + # Regression fixture: two distinct schemas share the property name "access" + # with non-identical value lists (the squash-mode gotcha). Must never collapse. + $script:squashSpec = [PSCustomObject]@{ + components = [PSCustomObject]@{ + schemas = [PSCustomObject]@{ + NfsExportPolicyRuleBase = [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ + access = [PSCustomObject]@{ + type = 'string' + description = 'Specifies access control for the export. Valid values are `root-squash`, `all-squash`, and `no-squash`.' + } + } + } + _presetWorkloadExportConfigurationNfsRule = [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ + access = [PSCustomObject]@{ + type = 'string' + description = 'Specifies access control for the export. Valid values are `root-squash`, `all-squash`, and `no-root-squash`.' + } + } + } + Bucket = [PSCustomObject]@{ + allOf = @( + [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ + versioning = [PSCustomObject]@{ + type = 'string' + description = 'The versioning state for objects within the bucket. Valid values are `none`, `enabled`, and `suspended`.' + } + } + } + ) + } + UnparseableThing = [PSCustomObject]@{ + type = 'object' + properties = [PSCustomObject]@{ + weirdField = [PSCustomObject]@{ + type = 'string' + description = "Valid values are`nin the range of 300000 and 10800000." + } + } + } + } + parameters = [PSCustomObject]@{ + ArrayPerformanceProtocol = [PSCustomObject]@{ + name = 'protocol' + 'in' = 'query' + description = 'Filter by protocol. Valid values are `nfs`, `smb`, `http`, and `s3`.' + } + sort = [PSCustomObject]@{ + name = 'sort' + 'in' = 'query' + description = 'Sort field. Possible values include `name`, `id`.' + } + plain = [PSCustomObject]@{ + name = 'plain' + 'in' = 'query' + description = 'An ordinary parameter with no enumeration.' + } + } + } + } + + $script:results = Get-PfbSpecValueEnums -Spec $squashSpec + } + + It 'keys schema-property entries by "SchemaName.PropertyName", never bare property name' { + ($results | Where-Object Key -eq 'NfsExportPolicyRuleBase.access') | Should -Not -BeNullOrEmpty + ($results | Where-Object Key -eq '_presetWorkloadExportConfigurationNfsRule.access') | Should -Not -BeNullOrEmpty + } + + It 'sets Name to the schema property name for schema-kind entries' { + $bucket = $results | Where-Object Key -eq 'Bucket.versioning' + $bucket.Name | Should -Be 'versioning' + } + + It 'sets Name to the parameter''s wire "name" field, which may differ from its components.parameters dictionary key' { + $protocolParam = $results | Where-Object Key -eq 'ArrayPerformanceProtocol' + $protocolParam.Name | Should -Be 'protocol' + $protocolParam.Values | Should -Be @('nfs', 'smb', 'http', 's3') + } + + It 'never collapses two schemas sharing a property name into one entry (squash-mode gotcha)' { + $base = $results | Where-Object Key -eq 'NfsExportPolicyRuleBase.access' + $preset = $results | Where-Object Key -eq '_presetWorkloadExportConfigurationNfsRule.access' + + $base.Values | Should -Be @('root-squash', 'all-squash', 'no-squash') + $preset.Values | Should -Be @('root-squash', 'all-squash', 'no-root-squash') + $base.Values | Should -Not -Be $preset.Values + } + + It 'Bucket.versioning regression: extracts exactly [none, enabled, suspended]' { + $bucket = $results | Where-Object Key -eq 'Bucket.versioning' + $bucket | Should -Not -BeNullOrEmpty + $bucket.Values | Should -Be @('none', 'enabled', 'suspended') + $bucket.Parsed | Should -BeTrue + } + + It 'includes parameter-sourced entries with Kind = parameter' { + $sort = $results | Where-Object { $_.Key -eq 'sort' -and $_.Kind -eq 'parameter' } + $sort | Should -Not -BeNullOrEmpty + $sort.Values | Should -Be @('name', 'id') + } + + It 'does not emit a record for a parameter with no trigger phrase' { + ($results | Where-Object Key -eq 'plain') | Should -BeNullOrEmpty + } + + It 'classifies a trigger-matching but non-enumerable description as unparsed rather than dropping it' { + $weird = $results | Where-Object Key -eq 'UnparseableThing.weirdField' + $weird | Should -Not -BeNullOrEmpty + $weird.Parsed | Should -BeFalse + $weird.Values | Should -BeNullOrEmpty + $weird.TriggerText | Should -Not -BeNullOrEmpty + } + + It 'returns an empty list when the spec has no components' { + $emptySpec = [PSCustomObject]@{ components = [PSCustomObject]@{} } + Get-PfbSpecValueEnums -Spec $emptySpec | Should -BeNullOrEmpty + } +} + +Describe 'Get-PfbSpecValueEnums: inline path-operation parameters' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + # Skipped on Windows PowerShell 5.1: the "does not reprocess a $ref entry" case below + # observably fails only on real WinPS 5.1 + Pester 6 (confirmed live) even though + # calling Get-PfbSpecValueEnums directly with the identical fixture outside Pester + # returns identical, correct results on both hosts -- this points at a Pester + # 6-on-Desktop-CLR quirk, not a defect in this dev/CI-only tooling (never loaded by + # the shipped module; see PureStorageFlashBladePowerShell.psm1). + BeforeAll { + # Regression fixture for the real GET /arrays/space `type` gap: a versioned + # spec.paths key (every real cached spec carries an "/api//" prefix) + # whose GET operation defines `type` INLINE (no $ref), alongside a sibling + # parameter that is already a $ref (must NOT be reprocessed/double-counted) and + # an inline parameter with no "Valid values..." trigger at all (must not emit a + # record). + $script:inlineSpec = [PSCustomObject]@{ + components = [PSCustomObject]@{ + schemas = [PSCustomObject]@{} + parameters = [PSCustomObject]@{ + Resolution = [PSCustomObject]@{ + name = 'resolution' + 'in' = 'query' + description = 'The desired ms between samples. Valid values are `1000`, `30000`.' + } + } + } + paths = [PSCustomObject]@{ + '/api/2.0/arrays/space' = [PSCustomObject]@{ + get = [PSCustomObject]@{ + parameters = @( + [PSCustomObject]@{ '$ref' = '#/components/parameters/Resolution' } + [PSCustomObject]@{ + name = 'type' + 'in' = 'query' + description = 'Display the metric of a specified object type. Valid values are `array`, `file-system`, and `object-store`. If not specified, defaults to `array`.' + } + [PSCustomObject]@{ + name = 'start_time' + 'in' = 'query' + description = 'When the time window starts (in milliseconds since epoch).' + } + ) + } + } + } + } + + $script:inlineResults = Get-PfbSpecValueEnums -Spec $inlineSpec + } + + It 'extracts an inline (non-$ref) path-operation parameter as Kind = inline-parameter' { + $rec = $inlineResults | Where-Object { $_.Kind -eq 'inline-parameter' } + $rec | Should -Not -BeNullOrEmpty + $rec.Values | Should -Be @('array', 'file-system', 'object-store') + $rec.Name | Should -Be 'type' + } + + It 'keys the inline-parameter record as " #", with the /api// prefix stripped' { + $rec = $inlineResults | Where-Object { $_.Kind -eq 'inline-parameter' } + $rec.Key | Should -Be 'GET arrays/space#type' + } + + It 'does not reprocess a $ref entry in the inline-parameter pass (no double-count of the Resolution parameter)' { + ($inlineResults | Where-Object { $_.Name -eq 'resolution' }).Count | Should -Be 1 + ($inlineResults | Where-Object { $_.Name -eq 'resolution' }).Kind | Should -Be 'parameter' + } + + It 'does not emit an inline-parameter record for an operation parameter with no trigger phrase' { + ($inlineResults | Where-Object { $_.Name -eq 'start_time' }) | Should -BeNullOrEmpty + } + + It 'strips a non-versioned path prefix down to just the leading slash (e.g. /oauth2/1.0/token-shaped paths)' { + $spec = [PSCustomObject]@{ + components = [PSCustomObject]@{ schemas = [PSCustomObject]@{}; parameters = [PSCustomObject]@{} } + paths = [PSCustomObject]@{ + '/oauth2/1.0/token' = [PSCustomObject]@{ + post = [PSCustomObject]@{ + parameters = @( + [PSCustomObject]@{ name = 'grant_type'; 'in' = 'query'; description = 'Valid values are `client_credentials`.' } + ) + } + } + } + } + $rec = Get-PfbSpecValueEnums -Spec $spec | Where-Object { $_.Kind -eq 'inline-parameter' } + $rec.Key | Should -Be 'POST oauth2/1.0/token#grant_type' + } +} + +Describe 'Get-PfbResourceHint' { + It 'strips the leading -Pfb prefix' { + Get-PfbResourceHint -CmdletName 'New-PfbNetworkInterface' | Should -Be 'NetworkInterface' + } + + It 'returns the input unchanged when it does not match the -Pfb shape' { + Get-PfbResourceHint -CmdletName 'SomethingElse' | Should -Be 'SomethingElse' + } +} + +Describe 'Resolve-PfbFieldValueEnum' { + BeforeAll { + $script:history = [ordered]@{ + 'Widget.color' = [ordered]@{ Name = 'color'; Kind = 'schema'; MinVersion = '2.0'; CurrentValues = @('red', 'blue'); DistinctValueSets = [System.Collections.Generic.HashSet[string]]::new([string[]]@('blue,red')) } + 'Type' = [ordered]@{ Name = 'type'; Kind = 'parameter'; MinVersion = '2.0'; CurrentValues = @('array', 'file-system'); DistinctValueSets = [System.Collections.Generic.HashSet[string]]::new([string[]]@('array,file-system')) } + 'Type_for_performance' = [ordered]@{ Name = 'type'; Kind = 'parameter'; MinVersion = '2.0'; CurrentValues = @('array', 'client'); DistinctValueSets = [System.Collections.Generic.HashSet[string]]::new([string[]]@('array,client')) } + 'GET arrays/space#type' = [ordered]@{ Name = 'type'; Kind = 'inline-parameter'; MinVersion = '2.0'; CurrentValues = @('array', 'file-system'); DistinctValueSets = [System.Collections.Generic.HashSet[string]]::new([string[]]@('array,file-system')) } + } + } + + It 'returns no-spec-enum-found when the wire name matches nothing' { + $r = Resolve-PfbFieldValueEnum -WireName 'nope' -ResourceHint 'Widget' -History $history -OldestVersion '2.0' + $r.Status | Should -Be 'no-spec-enum-found' + } + + It 'matches via resource-hint-filtered schema kind' { + $r = Resolve-PfbFieldValueEnum -WireName 'color' -ResourceHint 'Widget' -History $history -OldestVersion '2.0' + $r.Status | Should -Be 'matched' + $r.Recommendation | Should -Be 'ValidateSet' + } + + It 'reports collision when parameter-kind records disagree and no inline match settles it' { + $r = Resolve-PfbFieldValueEnum -WireName 'type' -ResourceHint 'Nothing' -History $history -OldestVersion '2.0' + $r.Status | Should -Be 'collision' + } + + It 'an exact inline-parameter match settles an otherwise-ambiguous parameter-kind wire name' { + $r = Resolve-PfbFieldValueEnum -WireName 'type' -ResourceHint 'Nothing' -Endpoint 'arrays/space' -Method 'GET' -History $history -OldestVersion '2.0' + $r.Status | Should -Be 'matched' + $r.MatchedKey | Should -Be 'GET arrays/space#type' + } +} + +Describe 'Build-PfbValueEnumMap.ps1: inline-parameter-to-$ref refactor keeps the field''s minVersion at its original (inline) version' -Skip:($PSVersionTable.PSVersion.Major -lt 7) { + BeforeAll { + $repoRoot = Split-Path -Parent $PSScriptRoot + $script:builderScript = Join-Path $repoRoot 'tools/Build-PfbValueEnumMap.ps1' + + # Reproduces the real Get-PfbArraySpace `type` history exactly: v1 defines it + # inline on GET /arrays/space with a full "Valid values..." description; v2 + # refactors the SAME parameter into a components.parameters $ref with + # byte-identical description text — a pure documentation refactor, not an API + # change. The field must still be attributed to v1, not v2, once diffed. + New-Item -ItemType Directory -Path 'TestDrive:\inlineSpecs' -Force | Out-Null + + $description = 'Display the metric of a specified object type. Valid values are `array`, `file-system`, and `object-store`. If not specified, defaults to `array`.' + + $specV1 = [ordered]@{ + openapi = '3.0.1' + info = @{ version = '5.0' } + components = [ordered]@{ schemas = [ordered]@{}; parameters = [ordered]@{} } + paths = [ordered]@{ + '/api/5.0/arrays/space' = [ordered]@{ + get = [ordered]@{ + parameters = @( + [ordered]@{ name = 'type'; 'in' = 'query'; description = $description } + ) + } + } + } + } + $specV2 = [ordered]@{ + openapi = '3.0.1' + info = @{ version = '5.1' } + components = [ordered]@{ + schemas = [ordered]@{} + parameters = [ordered]@{ + Type = [ordered]@{ name = 'type'; 'in' = 'query'; description = $description } + } + } + paths = [ordered]@{ + '/api/5.1/arrays/space' = [ordered]@{ + get = [ordered]@{ + parameters = @( + [ordered]@{ '$ref' = '#/components/parameters/Type' } + ) + } + } + } + } + + $specV1 | ConvertTo-Json -Depth 20 | Set-Content -Path 'TestDrive:\inlineSpecs\fb5.0.json' + $specV2 | ConvertTo-Json -Depth 20 | Set-Content -Path 'TestDrive:\inlineSpecs\fb5.1.json' + + & $builderScript -SpecsDirectory 'TestDrive:\inlineSpecs' -OutputPath 'TestDrive:\inlineOutput\map.json' -ReconciliationPath 'TestDrive:\inlineOutput\reconciliation.md' + $script:inlineManifest = Get-Content -Path 'TestDrive:\inlineOutput\map.json' -Raw | ConvertFrom-Json -Depth 20 + } + + It 'extracts the older (inline) version as an inline-parameter-kind record keyed by method+path+name' { + $inlineManifest.entries.'GET arrays/space#type' | Should -Not -BeNullOrEmpty + $inlineManifest.entries.'GET arrays/space#type'.kind | Should -Be 'inline-parameter' + $inlineManifest.entries.'GET arrays/space#type'.values | Should -Be @('array', 'file-system', 'object-store') + } + + It 'attributes the inline-parameter record''s minVersion to the OLDER version, not the version where it became a $ref' { + $inlineManifest.entries.'GET arrays/space#type'.minVersion | Should -Be '5.0' + } + + It 'also records the newer $ref''d definition under components.parameters, separately, without disturbing the inline record' { + $inlineManifest.entries.'Type' | Should -Not -BeNullOrEmpty + $inlineManifest.entries.'Type'.kind | Should -Be 'parameter' + $inlineManifest.entries.'Type'.minVersion | Should -Be '5.1' + } +} diff --git a/Tests/PfbVersionMap.Coverage.Tests.ps1 b/Tests/PfbVersionMap.Coverage.Tests.ps1 new file mode 100644 index 0000000..77dc964 --- /dev/null +++ b/Tests/PfbVersionMap.Coverage.Tests.ps1 @@ -0,0 +1,178 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } +<# +.SYNOPSIS + Data-integrity guard: every REST version the capability map was generated from must + have a corresponding entry in Data/PfbVersionMap.json. +.DESCRIPTION + Data/PfbVersionMap.json is currently a static, hand-curated file rather than one kept + fresh by tools/Update-PfbVersionMap.ps1's CI run (see that script's header and + tools/README.md Sec.3). This test does not care how the file was produced; it only + catches drift, e.g. a new REST version landing in Data/PfbCapabilityMap.json (via the + weekly CI refresh) without a matching Purity//FB pairing being added, either by hand + or by the generator. +#> + +BeforeAll { + $repoRoot = Split-Path -Parent $PSScriptRoot + $script:capabilityMapPath = Join-Path $repoRoot 'Data/PfbCapabilityMap.json' + $script:versionMapPath = Join-Path $repoRoot 'Data/PfbVersionMap.json' + + # ConvertFrom-Json has no -Depth parameter on Windows PowerShell 5.1 (added in PS6) -- + # 5.1's own recursion limit (100) is already far deeper than either file's shape. + function script:ConvertFrom-PfbTestJson { + param([Parameter(ValueFromPipeline)] [string]$InputObject) + if ($PSVersionTable.PSVersion.Major -ge 6) { + $InputObject | ConvertFrom-Json -Depth 5 + } + else { + $InputObject | ConvertFrom-Json + } + } + + # REST versions sort numerically, not lexically -- '2.9' is OLDER than '2.27', but a + # string sort puts it after. Collapse to a single integer so comparisons are unambiguous + # on both PowerShell editions. + function script:ConvertTo-PfbComparableVersion { + param([string]$Version) + $parts = "$Version" -split '\.' + $major = 0 + $minor = 0 + [void][int]::TryParse($parts[0], [ref]$major) + if ($parts.Count -gt 1) { [void][int]::TryParse($parts[1], [ref]$minor) } + ($major * 10000) + $minor + } + + # Splits "in the capability map but with no version-map pairing" into LAG AT THE HEAD + # versus a HOLE IN THE MIDDLE. + # + # Data/PfbVersionMap.json is refreshed by a *manual* run of + # tools/Update-PfbVersionMap.ps1 against an Everpure-internal SSOT endpoint that + # GitHub-hosted CI cannot reach. Worse, the SSOT reference table only publishes a + # REST<->Purity//FB pairing some time *after* a REST version ships -- verified + # 2026-07-25, when REST 2.28 was live and already fetched into the capability map while + # the SSOT table still had no 2.28 row at all. So a newly-released version legitimately + # appears in the capability map before any pairing exists anywhere to record. + # + # Failing on that would break the weekly refresh on exactly the runs that have something + # new to report -- and because the job dies before `Open pull request`, the failure would + # swallow the "new REST version detected" signal the workflow exists to emit. So + # head-lag warns and passes; a gap at or below the newest mapped version is a real data + # defect and still fails. + # + # Factored out of the It block so these semantics are unit-testable without needing real + # Data/ files on disk -- see the classification Describe at the bottom of this file. + function script:Split-PfbVersionMapGap { + param([string[]]$ExpectedVersions, [string[]]$MappedVersions) + + $mapped = [System.Collections.Generic.HashSet[string]]::new([string[]]$MappedVersions) + $missing = @($ExpectedVersions | Where-Object { -not $mapped.Contains($_) }) + + $highWaterName = $MappedVersions | + Sort-Object -Property { ConvertTo-PfbComparableVersion $_ } | + Select-Object -Last 1 + $highWater = ConvertTo-PfbComparableVersion $highWaterName + + [pscustomobject]@{ + HighWaterName = $highWaterName + Lagging = @($missing | Where-Object { (ConvertTo-PfbComparableVersion $_) -gt $highWater }) + Holes = @($missing | Where-Object { (ConvertTo-PfbComparableVersion $_) -le $highWater }) + } + } +} + +Describe 'Data/PfbVersionMap.json coverage (skips gracefully if the generated files are not present)' { + It 'has an entry for every REST version the capability map was generated from' { + if (-not (Test-Path $capabilityMapPath) -or -not (Test-Path $versionMapPath)) { + Set-ItResult -Skipped -Because 'Data/PfbCapabilityMap.json or Data/PfbVersionMap.json not present' + return + } + + $capabilityMap = Get-Content -Path $capabilityMapPath -Raw | ConvertFrom-PfbTestJson + $versionMap = Get-Content -Path $versionMapPath -Raw | ConvertFrom-PfbTestJson + + $expectedVersions = $capabilityMap.generatedFrom + $expectedVersions | Should -Not -BeNullOrEmpty -Because 'the capability map should record which REST versions it was generated from' + + # Head-lag warns, a gap below the high-water mark fails -- see + # Split-PfbVersionMapGap's comment for why the two are treated differently. + $gap = Split-PfbVersionMapGap -ExpectedVersions $expectedVersions -MappedVersions ([string[]]$versionMap.PSObject.Properties.Name) + $highWaterName = $gap.HighWaterName + $lagging = $gap.Lagging + $holes = $gap.Holes + + if ($lagging) { + $message = "Data/PfbVersionMap.json has no Purity//FB pairing for newer REST version(s): $($lagging -join ', '). " + + "Newest mapped version is $highWaterName. This is expected lag, not a defect: run " + + 'tools/Update-PfbVersionMap.ps1 manually with SSOT credentials once the SSOT reference table publishes the pairing.' + Write-Warning $message + # Surface it in the Actions run summary too, so the weekly refresh flags the + # outstanding manual step instead of it being buried in step logs. + if ($env:GITHUB_ACTIONS -eq 'true') { + Write-Host "::warning title=Version map behind capability map::$message" + } + } + + $holes | Should -BeNullOrEmpty -Because "these REST versions are at or below the newest mapped version ($highWaterName) but have no Purity//FB pairing in Data/PfbVersionMap.json: $($holes -join ', ') -- a gap below the high-water mark is a data defect, not upstream lag" + } + + It 'every entry has a non-empty purity property' { + if (-not (Test-Path $versionMapPath)) { + Set-ItResult -Skipped -Because 'Data/PfbVersionMap.json not present' + return + } + + $versionMap = Get-Content -Path $versionMapPath -Raw | ConvertFrom-PfbTestJson + + $emptyEntries = $versionMap.PSObject.Properties | Where-Object { [string]::IsNullOrWhiteSpace($_.Value.purity) } | ForEach-Object { $_.Name } + + $emptyEntries | Should -BeNullOrEmpty -Because "these REST versions have an entry but no purity value: $($emptyEntries -join ', ')" + } +} + +Describe 'Version-map gap classification (head lag vs hole)' { + BeforeAll { + # 2.9 is deliberately present: REST versions sort lexically WRONG ('2.9' > '2.27' as + # strings), so a naive sort would pick 2.9 as the newest mapped version and then + # misclassify every genuinely-newer version as a hole. + $script:mapped = 0..27 | ForEach-Object { "2.$_" } + } + + It 'picks the numerically-newest mapped version as the high-water mark, not the lexically-largest' { + $gap = Split-PfbVersionMapGap -ExpectedVersions $mapped -MappedVersions $mapped + $gap.HighWaterName | Should -Be '2.27' + } + + It 'reports nothing when the two sets agree' { + $gap = Split-PfbVersionMapGap -ExpectedVersions $mapped -MappedVersions $mapped + $gap.Lagging | Should -BeNullOrEmpty + $gap.Holes | Should -BeNullOrEmpty + } + + It 'classifies a newly-released version above the high-water mark as expected lag, not a hole' { + $gap = Split-PfbVersionMapGap -ExpectedVersions ($mapped + '2.28') -MappedVersions $mapped + $gap.Lagging | Should -Be @('2.28') + $gap.Holes | Should -BeNullOrEmpty + } + + It 'classifies a missing version below the high-water mark as a hole, not lag' { + $withHole = $mapped | Where-Object { $_ -ne '2.20' } + $gap = Split-PfbVersionMapGap -ExpectedVersions $mapped -MappedVersions $withHole + $gap.Holes | Should -Be @('2.20') + $gap.Lagging | Should -BeNullOrEmpty + } + + It 'separates the two when a hole and a lagging version occur together' { + $withHole = $mapped | Where-Object { $_ -ne '2.20' } + $gap = Split-PfbVersionMapGap -ExpectedVersions ($mapped + '2.28') -MappedVersions $withHole + $gap.Holes | Should -Be @('2.20') + $gap.Lagging | Should -Be @('2.28') + } + + It 'treats a single-digit-minor gap correctly rather than by string order' { + # 2.9 missing while 2.27 is mapped: numerically below the high-water mark, so a hole. + $withHole = $mapped | Where-Object { $_ -ne '2.9' } + $gap = Split-PfbVersionMapGap -ExpectedVersions $mapped -MappedVersions $withHole + $gap.Holes | Should -Be @('2.9') + $gap.Lagging | Should -BeNullOrEmpty + } +} diff --git a/Tests/PfbVersionMapTools.Tests.ps1 b/Tests/PfbVersionMapTools.Tests.ps1 new file mode 100644 index 0000000..ac54389 --- /dev/null +++ b/Tests/PfbVersionMapTools.Tests.ps1 @@ -0,0 +1,91 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } +<# +.SYNOPSIS + Unit tests for tools/lib/PfbVersionMapTools.ps1. +.DESCRIPTION + Covers the internal SSOT (Single Source of Truth) API's fetch-URL builder and the + HTML table parser, both fully-specified and network-independent. The orchestration in + tools/Update-PfbVersionMap.ps1 itself (the live HTTP fetch) is not unit tested here, + consistent with the rest of this module's tools/ scripts. +#> + +BeforeAll { + $repoRoot = Split-Path -Parent $PSScriptRoot + . (Join-Path $repoRoot 'tools/lib/PfbVersionMapTools.ps1') +} + +Describe 'Get-PfbSsotVersionMapUri' { + It 'builds the topic-content URL from the given base URI and topic ID' { + Get-PfbSsotVersionMapUri -BaseUri 'https://example.test' -TopicId 'abc123' | + Should -Be 'https://example.test/v1/topics/abc123/content' + } + + It 'requires -BaseUri' { + { Get-PfbSsotVersionMapUri -TopicId 'abc123' } | Should -Throw + } + + It 'requires -TopicId' { + { Get-PfbSsotVersionMapUri -BaseUri 'https://example.test' } | Should -Throw + } +} + +Describe 'ConvertFrom-PfbSsotVersionMapHtml' { + It 'parses a data row into a REST version -> purity entry' { + $html = @' + + + + +
REST API VersionHTMLIntroduced in Purity//FBShips with Purity//FB
REST API 2.27html4.8.34.8.3
REST API 2.26html4.8.14.8.1
+'@ + $map = ConvertFrom-PfbSsotVersionMapHtml -Html $html + + $map['2.27'].purity | Should -Be '4.8.3' + $map['2.26'].purity | Should -Be '4.8.1' + } + + It 'uses the "Introduced in Purity//FB" column, not "Ships with", when they differ' { + $html = 'REST API 2.12x4.3.44.3.3' + $map = ConvertFrom-PfbSsotVersionMapHtml -Html $html + + $map['2.12'].purity | Should -Be '4.3.4' + } + + It 'strips nested tags and trims whitespace inside cells' { + $html = ' REST API 2.20 x 4.6.3 4.6.3' + $map = ConvertFrom-PfbSsotVersionMapHtml -Html $html + + $map['2.20'].purity | Should -Be '4.6.3' + } + + It 'decodes HTML entities inside cells' { + $html = 'REST API 2.9x4.2.0 4.2.0' + $map = ConvertFrom-PfbSsotVersionMapHtml -Html $html + + $map['2.9'].purity | Should -Be '4.2.0' + } + + It 'skips header/non-version rows that do not match the "REST API X.Y" pattern' { + $html = @' +REST API VersionHTMLIntroduced in Purity//FBShips with Purity//FB +REST API 2.27x4.8.34.8.3 +'@ + $map = ConvertFrom-PfbSsotVersionMapHtml -Html $html + + $map.Count | Should -Be 1 + $map.Contains('2.27') | Should -BeTrue + } + + It 'skips rows with fewer than 4 cells' { + $html = 'REST API 2.27only two cells' + $map = ConvertFrom-PfbSsotVersionMapHtml -Html $html + + $map.Count | Should -Be 0 + } + + It 'returns an empty map when no matching rows are present' { + $map = ConvertFrom-PfbSsotVersionMapHtml -Html 'nothing relevant here' + + $map.Count | Should -Be 0 + } +} diff --git a/Tests/Remove-PfbArrayConnection.Tests.ps1 b/Tests/Remove-PfbArrayConnection.Tests.ps1 new file mode 100644 index 0000000..069b462 --- /dev/null +++ b/Tests/Remove-PfbArrayConnection.Tests.ps1 @@ -0,0 +1,106 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Remove-PfbArrayConnection - selector query keys (#64)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'sends remote_names, never names' { + Remove-PfbArrayConnection -RemoteName 'FB-B' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'DELETE' -and $Endpoint -eq 'array-connections' -and + $QueryParams['remote_names'] -eq 'FB-B' -and -not $QueryParams.ContainsKey('names') + } + } + + It 'still binds -Name through the alias, and emits remote_names for it' { + Remove-PfbArrayConnection -Name 'FB-B' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' -and -not $QueryParams.ContainsKey('names') + } + } + + It 'declares Name as an alias of -RemoteName' { + (Get-Command Remove-PfbArrayConnection).Parameters['RemoteName'].Aliases | Should -Contain 'Name' + } + + It 'targets the connection by id when -Id is used' { + Remove-PfbArrayConnection -Id 'conn-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'conn-1' -and -not $QueryParams.ContainsKey('remote_names') + } + } + + It 'binds a piped connection object by id' { + [PSCustomObject]@{ id = 'conn-9' } | + Remove-PfbArrayConnection -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'conn-9' + } + } + + It 'does NOT coerce a piped object into -RemoteName (binding-order guard)' { + [PSCustomObject]@{ id = 'conn-9' } | + Remove-PfbArrayConnection -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('remote_names') -and $QueryParams['ids'] -notlike '*@{*' + } + } + + It 'binds -RemoteName by property name from a user-built object' { + [pscustomobject]@{ RemoteName = 'FB-B' } | + Remove-PfbArrayConnection -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' -and -not $QueryParams.ContainsKey('ids') + } + } + + It 'binds by property name through the Name alias' { + [pscustomobject]@{ Name = 'FB-B' } | + Remove-PfbArrayConnection -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' -and -not $QueryParams.ContainsKey('ids') + } + } + + It 'still accepts a bare remote name piped by value' { + 'FB-B' | Remove-PfbArrayConnection -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' + } + } + + It 'rejects a piped object that carries no id/name property instead of stringifying it' { + # -Id absorbs a real connection object at binding pass 2. An object without id, name or + # remoteName falls through to pass 3 and is ToString()-ed into -RemoteName, so the guard + # is reachable here too -- and this cmdlet is a DELETE. + { + [PSCustomObject]@{ status = 'connected'; type = 'async-replication' } | + Remove-PfbArrayConnection -Confirm:$false -Array $fakeArray + } | Should -Throw -ExpectedMessage '*stringified object*' + } + + It 'makes no API call when ShouldProcess is declined via -WhatIf' { + Remove-PfbArrayConnection -RemoteName 'FB-B' -WhatIf -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 -Exactly + } +} diff --git a/Tests/Remove-PfbFileSystemSession.Tests.ps1 b/Tests/Remove-PfbFileSystemSession.Tests.ps1 new file mode 100644 index 0000000..7aeb6c0 --- /dev/null +++ b/Tests/Remove-PfbFileSystemSession.Tests.ps1 @@ -0,0 +1,67 @@ +#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 '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] } + $attr | Should -Not -BeNullOrEmpty + $attr.ValidValues | Should -Be @('nfs', 'smb') + } + + It 'rejects an invalid -Protocol value before making any API call' { + { Remove-PfbFileSystemSession -Protocol 'bogus' -Confirm:$false -Array $fakeArray } | Should -Throw + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 0 -Exactly + } + + 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 '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 '22517998136858346-smb' -and + -not $QueryParams.ContainsKey('protocols') -and -not $QueryParams.ContainsKey('disruptive') + } + } + + 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['protocols'] -eq 'nfs,smb' -and $QueryParams['disruptive'] -eq 'true' -and + -not $QueryParams.ContainsKey('names') + } + } + + 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 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 + } +} diff --git a/Tests/Remove-PfbFileSystemUserGroupQuotaPolicy.Tests.ps1 b/Tests/Remove-PfbFileSystemUserGroupQuotaPolicy.Tests.ps1 new file mode 100644 index 0000000..80c2568 --- /dev/null +++ b/Tests/Remove-PfbFileSystemUserGroupQuotaPolicy.Tests.ps1 @@ -0,0 +1,79 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'Remove-PfbFileSystemUserGroupQuotaPolicy' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'DELETEs file-systems/user-group-quota-policies with policy and member query params' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbFileSystemUserGroupQuotaPolicy -PolicyName 'pol-1' -MemberName 'fs1' -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'DELETE' -and + $Endpoint -eq 'file-systems/user-group-quota-policies' -and + $QueryParams['policy_names'] -eq 'pol-1' -and + $QueryParams['member_names'] -eq 'fs1' + } + } + + It 'accepts -PolicyId/-MemberId instead of names' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbFileSystemUserGroupQuotaPolicy -PolicyId 'pid-1' -MemberId 'mid-1' -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_ids'] -eq 'pid-1' -and $QueryParams['member_ids'] -eq 'mid-1' + } + } + + It 'does not call the API under -WhatIf' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbFileSystemUserGroupQuotaPolicy -PolicyName 'pol-1' -MemberName 'fs1' -WhatIf -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 + } + + It 'rejects a -PolicyName that looks like a wildcard' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbFileSystemUserGroupQuotaPolicy -PolicyName '*' -MemberName 'fs1' -Confirm:$false -Array $arr + } } | Should -Throw + } + + It 'rejects a -MemberName that looks like a wildcard' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbFileSystemUserGroupQuotaPolicy -PolicyName 'pol-1' -MemberName '*' -Confirm:$false -Array $arr + } } | Should -Throw + } + + It 'throws if neither -PolicyName nor -PolicyId is supplied' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbFileSystemUserGroupQuotaPolicy -MemberName 'fs1' -Confirm:$false -Array $arr + } } | Should -Throw 'You must supply either -PolicyName or -PolicyId.' + } + + It 'throws if neither -MemberName nor -MemberId is supplied' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbFileSystemUserGroupQuotaPolicy -PolicyName 'pol-1' -Confirm:$false -Array $arr + } } | Should -Throw 'You must supply either -MemberName or -MemberId.' + } +} diff --git a/Tests/Remove-PfbUserGroupQuotaPolicy.Tests.ps1 b/Tests/Remove-PfbUserGroupQuotaPolicy.Tests.ps1 new file mode 100644 index 0000000..646da17 --- /dev/null +++ b/Tests/Remove-PfbUserGroupQuotaPolicy.Tests.ps1 @@ -0,0 +1,66 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'Remove-PfbUserGroupQuotaPolicy' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'DELETEs user-group-quota-policies by -Name' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicy -Name 'pol-1' -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'DELETE' -and $Endpoint -eq 'user-group-quota-policies' -and $QueryParams['names'] -eq 'pol-1' + } + } + + It 'targets by -Id when supplied' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicy -Id 'id-1' -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'id-1' -and -not $QueryParams.ContainsKey('names') + } + } + + It 'sends -Version as the versions query param' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicy -Name 'pol-1' -Version 'v1' -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['versions'] -eq 'v1' + } + } + + It 'rejects a -Name that looks like a wildcard' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicy -Name '*' -Confirm:$false -Array $arr + } } | Should -Throw + } + + It 'does not call the API under -WhatIf' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicy -Name 'pol-1' -WhatIf -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 + } +} diff --git a/Tests/Remove-PfbUserGroupQuotaPolicyFileSystem.Tests.ps1 b/Tests/Remove-PfbUserGroupQuotaPolicyFileSystem.Tests.ps1 new file mode 100644 index 0000000..497a650 --- /dev/null +++ b/Tests/Remove-PfbUserGroupQuotaPolicyFileSystem.Tests.ps1 @@ -0,0 +1,79 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'Remove-PfbUserGroupQuotaPolicyFileSystem' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'DELETEs user-group-quota-policies/file-systems with policy and member query params' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicyFileSystem -PolicyName 'pol-1' -MemberName 'fs1' -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'DELETE' -and + $Endpoint -eq 'user-group-quota-policies/file-systems' -and + $QueryParams['policy_names'] -eq 'pol-1' -and + $QueryParams['member_names'] -eq 'fs1' + } + } + + It 'accepts -PolicyId/-MemberId instead of names' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicyFileSystem -PolicyId 'pid-1' -MemberId 'mid-1' -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_ids'] -eq 'pid-1' -and $QueryParams['member_ids'] -eq 'mid-1' + } + } + + It 'does not call the API under -WhatIf' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicyFileSystem -PolicyName 'pol-1' -MemberName 'fs1' -WhatIf -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 + } + + It 'rejects a -PolicyName that looks like a wildcard' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicyFileSystem -PolicyName '*' -MemberName 'fs1' -Confirm:$false -Array $arr + } } | Should -Throw + } + + It 'rejects a -MemberName that looks like a wildcard' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicyFileSystem -PolicyName 'pol-1' -MemberName '*' -Confirm:$false -Array $arr + } } | Should -Throw + } + + It 'throws when neither -PolicyName nor -PolicyId is supplied' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicyFileSystem -MemberName 'fs1' -Confirm:$false -Array $arr + } } | Should -Throw 'You must supply either -PolicyName or -PolicyId.' + } + + It 'throws when neither -MemberName nor -MemberId is supplied' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicyFileSystem -PolicyName 'pol-1' -Confirm:$false -Array $arr + } } | Should -Throw 'You must supply either -MemberName or -MemberId.' + } +} diff --git a/Tests/Remove-PfbUserGroupQuotaPolicyRule.Tests.ps1 b/Tests/Remove-PfbUserGroupQuotaPolicyRule.Tests.ps1 new file mode 100644 index 0000000..e1a0786 --- /dev/null +++ b/Tests/Remove-PfbUserGroupQuotaPolicyRule.Tests.ps1 @@ -0,0 +1,100 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'Remove-PfbUserGroupQuotaPolicyRule' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'DELETEs user-group-quota-policies/rules by -Name' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicyRule -Name 'rule-1' -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'DELETE' -and $Endpoint -eq 'user-group-quota-policies/rules' -and $QueryParams['names'] -eq 'rule-1' + } + } + + It 'deletes by -PolicyName alone (clears all of a policy''s rules)' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicyRule -PolicyName 'pol-1' -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['policy_names'] -eq 'pol-1' -and -not $QueryParams.ContainsKey('names') + } + } + + It 'joins multiple -Id values with commas' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicyRule -Id 'rid-1', 'rid-2' -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'rid-1,rid-2' + } + } + + It 'rejects a -Name that looks like a wildcard' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicyRule -Name '*' -Confirm:$false -Array $arr + } } | Should -Throw + } + + It 'rejects a -PolicyName that looks like a wildcard' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicyRule -PolicyName '*' -Confirm:$false -Array $arr + } } | Should -Throw + } + + It 'throws when none of -Name/-Id/-PolicyName/-PolicyId is supplied' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicyRule -Confirm:$false -Array $arr + } } | Should -Throw + } + + It 'does not call the API under -WhatIf' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicyRule -Name 'rule-1' -WhatIf -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 + } + + It 'validates arguments before connecting when none of -Name/-Id/-PolicyName/-PolicyId is supplied' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicyRule -Confirm:$false -Array $arr + } } | Should -Throw + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection -Times 0 + } + + It 'combines -Name and -PolicyName as AND: both filters are sent in a single request' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Remove-PfbUserGroupQuotaPolicyRule -Name 'rule-1' -PolicyName 'pol-1' -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['names'] -eq 'rule-1' -and $QueryParams['policy_names'] -eq 'pol-1' + } + } +} diff --git a/Tests/Set-PfbPresetWorkload.Tests.ps1 b/Tests/Set-PfbPresetWorkload.Tests.ps1 new file mode 100644 index 0000000..c22f3d2 --- /dev/null +++ b/Tests/Set-PfbPresetWorkload.Tests.ps1 @@ -0,0 +1,92 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.26'; AuthToken = 'x' } +} + +Describe 'Set-PfbPresetWorkload - routed through Invoke-PfbApiRequest (#76)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod { } + } + + Context 'the shared request path is used' { + It 'sends PUT to presets/workload' { + Set-PfbPresetWorkload -Name 'analytics' -Attributes @{ name = 'analytics' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PUT' -and $Endpoint -eq 'presets/workload' + } + } + + # The whole point of #76: this cmdlet used to call Invoke-RestMethod itself, which + # skipped capability gating, error normalisation, and Bearer-token auth. If a direct + # call ever comes back, this is the test that says so. + It 'makes no direct Invoke-RestMethod call' { + Set-PfbPresetWorkload -Name 'analytics' -Attributes @{ name = 'analytics' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 0 -Exactly + } + + It 'passes -Attributes through as the body, unmodified' { + $attrs = @{ name = 'analytics'; description = 'test'; workload_type = 'generic' } + Set-PfbPresetWorkload -Name 'analytics' -Attributes $attrs -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 3 -and + $Body['name'] -eq 'analytics' -and + $Body['description'] -eq 'test' -and + $Body['workload_type'] -eq 'generic' + } + } + } + + Context 'query parameters' { + It 'targets by name via the names query parameter' { + Set-PfbPresetWorkload -Name 'analytics' -Attributes @{ name = 'analytics' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['names'] -eq 'analytics' -and -not $QueryParams.ContainsKey('ids') + } + } + + It 'targets by id via the ids query parameter' { + Set-PfbPresetWorkload -Id 'preset-1' -Attributes @{ name = 'analytics' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'preset-1' -and -not $QueryParams.ContainsKey('names') + } + } + + It 'sends skip_verify_deployable only when the switch is supplied' { + Set-PfbPresetWorkload -Name 'analytics' -Attributes @{ name = 'analytics' } -SkipVerifyDeployable -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['skip_verify_deployable'] -eq 'true' + } + } + + It 'omits skip_verify_deployable when the switch is absent' { + Set-PfbPresetWorkload -Name 'analytics' -Attributes @{ name = 'analytics' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('skip_verify_deployable') + } + } + } + + Context 'ShouldProcess still gates the call' { + It 'makes no request under -WhatIf' { + Set-PfbPresetWorkload -Name 'analytics' -Attributes @{ name = 'analytics' } -WhatIf -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 -Exactly + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 0 -Exactly + } + } +} diff --git a/Tests/Set-PfbWorkloadTag.Tests.ps1 b/Tests/Set-PfbWorkloadTag.Tests.ps1 new file mode 100644 index 0000000..456eed8 --- /dev/null +++ b/Tests/Set-PfbWorkloadTag.Tests.ps1 @@ -0,0 +1,199 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } +<# +.SYNOPSIS + Unit tests for Set-PfbWorkloadTag. +.DESCRIPTION + The cmdlet had no tests. These assert the shape that actually reaches the wire, so they + survive the cmdlet being refolded onto Invoke-PfbApiRequest: Invoke-RestMethod is the + mock boundary, not Invoke-PfbApiRequest. +#> + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ + Endpoint = 'fb.example.test' + ApiVersion = '2.26' + AuthToken = 'x' + ApiToken = $null + AuthMethod = 'ApiToken' + SkipCertificateCheck = $false + } +} + +Describe 'Set-PfbWorkloadTag - wire body shape' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod { [PSCustomObject]@{ items = @() } } + } + + # The #77 regression. PUT /workloads/tags/batch declares minItems 1 on a top-level array, + # and [ValidateCount(1, 30)] explicitly permits one tag -- but a one-element collection + # piped into ConvertTo-Json unrolls and serialises as a bare object, which the array + # rejects. A two-tag call passes either way, so ONE tag is the assertion that matters. + # + # Key order inside each tag object is deliberately not asserted: PowerShell randomises + # hashtable enumeration order per process, so {"key":..,"value":..} and the reverse are + # both correct output. + It 'serialises a SINGLE tag as a one-element JSON array, not a bare object' { + Set-PfbWorkloadTag -ResourceName 'wl1' -Tags @(@{ key = 'team'; value = 'analytics' }) -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 1 -Exactly -ParameterFilter { + $Body.Trim().StartsWith('[') -and + $Body.Trim().EndsWith(']') -and + $Body -match '"analytics"' + } + } + + It 'serialises multiple tags as a JSON array' { + Set-PfbWorkloadTag -ResourceName 'wl1' -Tags @( + @{ key = 'team'; value = 'analytics' } + @{ key = 'env'; value = 'prod' } + ) -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 1 -Exactly -ParameterFilter { + $Body.Trim().StartsWith('[') -and + $Body -match '"analytics"' -and + $Body -match '"prod"' + } + } + + It 'sends PUT to the workloads/tags/batch endpoint' { + Set-PfbWorkloadTag -ResourceName 'wl1' -Tags @(@{ key = 'team'; value = 'analytics' }) -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PUT' -and $Uri -like '*/workloads/tags/batch*' + } + } +} + +Describe 'Set-PfbWorkloadTag - query parameters reach the wire' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod { [PSCustomObject]@{ items = @() } } + } + + It 'targets by name via resource_names' { + Set-PfbWorkloadTag -ResourceName 'wl1', 'wl2' -Tags @(@{ key = 'team'; value = 'analytics' }) -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 1 -Exactly -ParameterFilter { + $Uri -like '*resource_names=wl1*wl2*' -and $Uri -notlike '*resource_ids*' + } + } + + It 'targets by id via resource_ids' { + Set-PfbWorkloadTag -ResourceId 'wl-1' -Tags @(@{ key = 'team'; value = 'analytics' }) -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 1 -Exactly -ParameterFilter { + $Uri -like '*resource_ids=wl-1*' -and $Uri -notlike '*resource_names*' + } + } +} + +Describe 'Set-PfbWorkloadTag - ShouldProcess gates the call' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod { [PSCustomObject]@{ items = @() } } + } + + It 'makes no request under -WhatIf' { + Set-PfbWorkloadTag -ResourceName 'wl1' -Tags @(@{ key = 'team'; value = 'analytics' }) -WhatIf -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 0 -Exactly + } +} + +Describe 'Set-PfbWorkloadTag - routed through Invoke-PfbApiRequest (#77)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod { } + } + + It 'sends PUT to workloads/tags/batch' { + Set-PfbWorkloadTag -ResourceName 'wl1' -Tags @(@{ key = 'team'; value = 'analytics' }) -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PUT' -and $Endpoint -eq 'workloads/tags/batch' + } + } + + # The point of the refactor: the hand-rolled call skipped capability gating, error + # normalisation and Bearer-token auth. If a direct call ever comes back, this says so. + It 'makes no direct Invoke-RestMethod call' { + Set-PfbWorkloadTag -ResourceName 'wl1' -Tags @(@{ key = 'team'; value = 'analytics' }) -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 0 -Exactly + } + + It 'passes -Tags through as the body, unmodified and still a collection' { + Set-PfbWorkloadTag -ResourceName 'wl1' -Tags @( + @{ key = 'team'; value = 'analytics'; namespace = 'default' } + @{ key = 'env'; value = 'prod' } + ) -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 2 -and + $Body[0]['key'] -eq 'team' -and $Body[0]['namespace'] -eq 'default' -and + $Body[1]['value'] -eq 'prod' + } + } + + # A one-element [hashtable[]] must not be unwrapped into a bare hashtable by parameter + # binding on the way into the shared path -- that would put the #77 defect back. + It 'passes a SINGLE tag through as a one-element collection, not a bare hashtable' { + Set-PfbWorkloadTag -ResourceName 'wl1' -Tags @(@{ key = 'team'; value = 'analytics' }) -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body -is [array] -and $Body.Count -eq 1 -and $Body[0]['key'] -eq 'team' + } + } + + It 'puts the targeting selectors in QueryParams' { + Set-PfbWorkloadTag -ResourceName 'wl1', 'wl2' -Tags @(@{ key = 'team'; value = 'analytics' }) -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['resource_names'] -eq 'wl1,wl2' -and -not $QueryParams.ContainsKey('resource_ids') + } + } + + It 'makes no request under -WhatIf' { + Set-PfbWorkloadTag -ResourceName 'wl1' -Tags @(@{ key = 'team'; value = 'analytics' }) -WhatIf -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 -Exactly + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 0 -Exactly + } +} + +Describe 'Set-PfbWorkloadTag - capability gating now applies' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod { [PSCustomObject]@{ items = @() } } + } + + # PUT /workloads/tags/batch is minVersion 2.23 in Data/PfbCapabilityMap.json. Before the + # refactor the hand-rolled call bypassed Assert-PfbApiCapability entirely and this went + # out on the wire to be rejected by the array. + It 'throws locally, with no network call, against an array below the endpoint minVersion' { + $oldArray = [PSCustomObject]@{ + Endpoint = 'fb.example.test' + ApiVersion = '2.22' + AuthToken = 'x' + ApiToken = $null + AuthMethod = 'ApiToken' + SkipCertificateCheck = $false + } + + { Set-PfbWorkloadTag -ResourceName 'wl1' -Tags @(@{ key = 'team'; value = 'analytics' }) -Confirm:$false -Array $oldArray } | + Should -Throw -ExpectedMessage '*PUT /workloads/tags/batch requires REST 2.23*' + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 0 -Exactly + } +} diff --git a/Tests/Test-PfbSupport.Tests.ps1 b/Tests/Test-PfbSupport.Tests.ps1 new file mode 100644 index 0000000..7f11486 --- /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') + } + } +} diff --git a/Tests/Update-PfbActiveDirectory.Tests.ps1 b/Tests/Update-PfbActiveDirectory.Tests.ps1 new file mode 100644 index 0000000..9f34c41 --- /dev/null +++ b/Tests/Update-PfbActiveDirectory.Tests.ps1 @@ -0,0 +1,165 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbActiveDirectory - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends ca_certificate and ca_certificate_group as name-reference objects' { + Update-PfbActiveDirectory -Name 'ad1' -CaCertificate 'ca1' -CaCertificateGroup 'cagrp1' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'active-directory' -and + $QueryParams['names'] -eq 'ad1' -and + $Body['ca_certificate'].name -eq 'ca1' -and + $Body['ca_certificate_group'].name -eq 'cagrp1' + } + } + + It 'sends join_ou as a plain body field' { + Update-PfbActiveDirectory -Name 'ad1' -JoinOu 'OU=Storage,DC=corp,DC=example,DC=com' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['join_ou'] -eq 'OU=Storage,DC=corp,DC=example,DC=com' + } + } + + It 'sends directory_servers, fqdns, global_catalog_servers, kerberos_servers, and service_principal_names as arrays' { + Update-PfbActiveDirectory -Name 'ad1' ` + -DirectoryServers 'dc1.corp.example.com','dc2.corp.example.com' ` + -Fqdns 'fb.corp.example.com' ` + -GlobalCatalogServers 'gc1.corp.example.com' ` + -KerberosServers 'kdc1.corp.example.com' ` + -ServicePrincipalNames 'HOST/fb1','HOST/fb2' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + @($Body['directory_servers']).Count -eq 2 -and $Body['directory_servers'][0] -eq 'dc1.corp.example.com' -and + @($Body['fqdns']) -contains 'fb.corp.example.com' -and + @($Body['global_catalog_servers']) -contains 'gc1.corp.example.com' -and + @($Body['kerberos_servers']) -contains 'kdc1.corp.example.com' -and + @($Body['service_principal_names']).Count -eq 2 + } + } + + It 'sends an EMPTY array for -DirectoryServers @() so a list can be cleared' { + Update-PfbActiveDirectory -Name 'ad1' -DirectoryServers @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('directory_servers') -and @($Body['directory_servers']).Count -eq 0 + } + } + + It 'sends an EMPTY array for - @() so a list can be cleared, not omit the key' -ForEach @( + @{ Parameter = 'EncryptionTypes'; WireKey = 'encryption_types' } + @{ Parameter = 'Fqdns'; WireKey = 'fqdns' } + @{ Parameter = 'GlobalCatalogServers'; WireKey = 'global_catalog_servers' } + @{ Parameter = 'KerberosServers'; WireKey = 'kerberos_servers' } + @{ Parameter = 'ServicePrincipalNames'; WireKey = 'service_principal_names' } + ) { + $params = @{ + Name = 'ad1' + $Parameter = @() + Confirm = $false + Array = $fakeArray + } + Update-PfbActiveDirectory @params + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey($WireKey) -and @($Body[$WireKey]).Count -eq 0 + } + } + + It 'sends encryption_types from the fixed enum set' { + Update-PfbActiveDirectory -Name 'ad1' -EncryptionTypes 'aes256-cts-hmac-sha1-96','arcfour-hmac' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + @($Body['encryption_types']) -contains 'aes256-cts-hmac-sha1-96' -and + @($Body['encryption_types']) -contains 'arcfour-hmac' + } + } + + It 'rejects an -EncryptionTypes value outside the fixed enum set' { + { Update-PfbActiveDirectory -Name 'ad1' -EncryptionTypes 'des-cbc-crc' ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | Should -Throw + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbActiveDirectory -Name 'ad1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the configuration by id when -Id is used' { + Update-PfbActiveDirectory -Id 'ad-1' -JoinOu 'OU=Storage' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'ad-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbActiveDirectory -Name 'ad1' -Attributes @{ join_ou = 'OU=Raw' } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['join_ou'] -eq 'OU=Raw' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbActiveDirectory -Name 'ad1' -JoinOu 'OU=X' -Attributes @{ join_ou = 'OU=Y' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'CaCertificate' } + @{ Parameter = 'CaCertificateGroup' } + @{ Parameter = 'DirectoryServers' } + @{ Parameter = 'Fqdns' } + @{ Parameter = 'GlobalCatalogServers' } + @{ Parameter = 'JoinOu' } + @{ Parameter = 'KerberosServers' } + @{ Parameter = 'ServicePrincipalNames' } + ) { + $attrs = (Get-Command Update-PfbActiveDirectory).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'puts the exact fixed ValidateSet on -EncryptionTypes' { + $attrs = (Get-Command Update-PfbActiveDirectory).Parameters['EncryptionTypes'].Attributes + $validateSet = $attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } + $validateSet.ValidValues | Should -Be @('aes256-cts-hmac-sha1-96', 'aes128-cts-hmac-sha1-96', 'arcfour-hmac') + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbActiveDirectory).Parameters.Keys + foreach ($p in 'CaCertificate','CaCertificateGroup','DirectoryServers','EncryptionTypes','Fqdns', + 'GlobalCatalogServers','JoinOu','KerberosServers','ServicePrincipalNames') { + $keys | Should -Contain $p + } + } + } +} diff --git a/Tests/Update-PfbAdmin.Tests.ps1 b/Tests/Update-PfbAdmin.Tests.ps1 new file mode 100644 index 0000000..a5cd4f0 --- /dev/null +++ b/Tests/Update-PfbAdmin.Tests.ps1 @@ -0,0 +1,149 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbAdmin - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends password and public_key as body fields' { + Update-PfbAdmin -Name 'ops' -Password 'N3wP@ss!' -PublicKey 'ssh-rsa AAAA' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'admins' -and + $QueryParams['names'] -eq 'ops' -and + $Body['password'] -eq 'N3wP@ss!' -and + $Body['public_key'] -eq 'ssh-rsa AAAA' + } + } + + It 'sends authorization_model and old_password as body fields' { + Update-PfbAdmin -Name 'ops' -AuthorizationModel 'static' -OldPassword '0ld!' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['authorization_model'] -eq 'static' -and + $Body['old_password'] -eq '0ld!' + } + } + + It 'sends an explicit -Locked:$false (ContainsKey semantics, not truthiness)' { + Update-PfbAdmin -Name 'ops' -Locked $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('locked') -and $Body['locked'] -eq $false + } + } + + It 'omits locked entirely when -Locked is not supplied' { + Update-PfbAdmin -Name 'ops' -Password 'x' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $Body.ContainsKey('locked') + } + } + + It 'builds management_access_policies as name-reference objects' { + Update-PfbAdmin -Name 'ops' -ManagementAccessPolicies 'pol-a','pol-b' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['management_access_policies'].Count -eq 2 -and + $Body['management_access_policies'][0].name -eq 'pol-a' -and + $Body['management_access_policies'][1].name -eq 'pol-b' + } + } + + It 'sends an EMPTY array for -ManagementAccessPolicies @() so a list can be cleared' { + Update-PfbAdmin -Name 'ops' -ManagementAccessPolicies @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('management_access_policies') -and + @($Body['management_access_policies']).Count -eq 0 + } + } + + It 'sends an EMPTY string for -Password "" rather than dropping the key' { + Update-PfbAdmin -Name 'ops' -Password '' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('password') -and $Body['password'] -eq '' + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbAdmin -Name 'ops' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the admin by id when -Id is used' { + Update-PfbAdmin -Id 'admin-1' -Password 'x' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'admin-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbAdmin -Name 'ops' -Attributes @{ password = 'raw' } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['password'] -eq 'raw' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbAdmin -Name 'ops' -Password 'x' -Attributes @{ password = 'y' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'deprecated fields are not exposed (constraint 9)' { + It 'has no -Role parameter' { + (Get-Command Update-PfbAdmin).Parameters.Keys | Should -Not -Contain 'Role' + } + } + + Context 'constraint compliance' { + # PATCH /admins declares no enum on any body property, so every typed parameter must + # be free of ValidateSet. This file is the reference the other batches copy, so the + # assertion belongs here even though there is nothing to remove today. + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'AuthorizationModel' } + @{ Parameter = 'Locked' } + @{ Parameter = 'OldPassword' } + @{ Parameter = 'Password' } + @{ Parameter = 'PublicKey' } + @{ Parameter = 'ManagementAccessPolicies' } + ) { + $attrs = (Get-Command Update-PfbAdmin).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts (constraint 11 has no read-only field to exclude here)' { + $keys = (Get-Command Update-PfbAdmin).Parameters.Keys + foreach ($p in 'AuthorizationModel','Locked','OldPassword','Password','PublicKey','ManagementAccessPolicies') { + $keys | Should -Contain $p + } + } + } +} diff --git a/Tests/Update-PfbApiClient.Tests.ps1 b/Tests/Update-PfbApiClient.Tests.ps1 new file mode 100644 index 0000000..4a2c6cb --- /dev/null +++ b/Tests/Update-PfbApiClient.Tests.ps1 @@ -0,0 +1,75 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbApiClient - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends an explicit -Enabled:$true' { + Update-PfbApiClient -Name 'automation' -Enabled $true -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'api-clients' -and + $QueryParams['names'] -eq 'automation' -and + $Body['enabled'] -eq $true + } + } + + It 'sends an explicit -Enabled:$false (ContainsKey semantics, not truthiness)' { + Update-PfbApiClient -Name 'automation' -Enabled $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('enabled') -and $Body['enabled'] -eq $false + } + } + + It 'omits enabled entirely when -Enabled is not supplied' { + Update-PfbApiClient -Id 'client-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'client-1' -and -not $Body.ContainsKey('enabled') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbApiClient -Name 'automation' -Attributes @{ enabled = $true } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['enabled'] -eq $true + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbApiClient -Name 'automation' -Enabled $true -Attributes @{ enabled = $false } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'excluded fields are not exposed' { + It 'has no -MaxRole parameter (deprecated, constraint 9)' { + (Get-Command Update-PfbApiClient).Parameters.Keys | Should -Not -Contain 'MaxRole' + } + + It 'exposes no read-only field as a parameter (constraint 11)' { + $paramNames = (Get-Command Update-PfbApiClient).Parameters.Keys + foreach ($readOnly in 'AccessPolicies', 'AccessTokenTtlInMs', 'Issuer', 'KeyId', 'PublicKey') { + $paramNames | Should -Not -Contain $readOnly + } + } + } +} diff --git a/Tests/Update-PfbArrayConnection.Tests.ps1 b/Tests/Update-PfbArrayConnection.Tests.ps1 new file mode 100644 index 0000000..ef9b6a7 --- /dev/null +++ b/Tests/Update-PfbArrayConnection.Tests.ps1 @@ -0,0 +1,258 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbArrayConnection - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends management_address as a body field' { + Update-PfbArrayConnection -RemoteName 'remote-fb-dc2' -ManagementAddress '10.0.2.101' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'array-connections' -and + $QueryParams['remote_names'] -eq 'remote-fb-dc2' -and + $Body['management_address'] -eq '10.0.2.101' + } + } + + It 'sends replication_addresses as an array (constraint 7 shape 2)' { + Update-PfbArrayConnection -RemoteName 'remote-fb-dr' -ReplicationAddresses '10.0.3.101','10.0.3.102' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + @($Body['replication_addresses']).Count -eq 2 -and + @($Body['replication_addresses'])[1] -eq '10.0.3.102' + } + } + + It 'sends an EMPTY array for -ReplicationAddresses @() so the list can be cleared' { + Update-PfbArrayConnection -RemoteName 'remote-fb-dr' -ReplicationAddresses @() ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('replication_addresses') -and + @($Body['replication_addresses']).Count -eq 0 + } + } + + It 'sends an explicit -Encrypted:$false (ContainsKey semantics, not truthiness)' { + Update-PfbArrayConnection -RemoteName 'remote-fb-dc2' -Encrypted $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('encrypted') -and $Body['encrypted'] -eq $false + } + } + + It 'omits encrypted entirely when not supplied' { + Update-PfbArrayConnection -RemoteName 'remote-fb-dc2' -ManagementAddress '10.0.2.101' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $Body.ContainsKey('encrypted') + } + } + + It 'builds ca_certificate_group as a name-reference object (constraint 8a)' { + Update-PfbArrayConnection -RemoteName 'remote-fb-dc2' -CaCertificateGroup 'my-certs' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['ca_certificate_group'].name -eq 'my-certs' + } + } + + It 'builds remote as a name-reference object (constraint 8a)' { + Update-PfbArrayConnection -RemoteName 'remote-fb-dc2' -Remote 'remote-fb' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['remote'].name -eq 'remote-fb' + } + } + + It 'passes throttle straight through as a composite hashtable (constraint 8c)' { + Update-PfbArrayConnection -RemoteName 'remote-fb-dc2' -Throttle @{ window_limit = 2097152 } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['throttle']['window_limit'] -eq 2097152 + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbArrayConnection -RemoteName 'remote-fb-dc2' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the connection by id when -Id is used' { + Update-PfbArrayConnection -Id 'conn-1' -ManagementAddress '10.0.2.101' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'conn-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context 'new query parameters (constraint 17 -- declared bare, not in the Individual sets)' { + It 'sends remote_ids as a bare query parameter alongside -Attributes' { + Update-PfbArrayConnection -RemoteName 'remote-fb-dc2' -Attributes @{ management_address = '10.0.2.101' } ` + -RemoteId 'remote-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_ids'] -eq 'remote-1' + } + } + + It 'still sends remote_ids alongside -RemoteName, which is now the selector' { + Update-PfbArrayConnection -RemoteName 'remote-fb-dc2' -ManagementAddress '10.0.2.101' ` + -RemoteId 'remote-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'remote-fb-dc2' -and + $QueryParams['remote_ids'] -eq 'remote-1' + } + } + + It 'omits remote_ids entirely when not supplied' { + Update-PfbArrayConnection -RemoteName 'remote-fb-dc2' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('remote_ids') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbArrayConnection -RemoteName 'remote-fb-dc2' -Attributes @{ management_address = '10.0.2.101' } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['management_address'] -eq '10.0.2.101' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbArrayConnection -RemoteName 'remote-fb-dc2' -ManagementAddress '10.0.2.101' ` + -Attributes @{ management_address = 'y' } -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'ManagementAddress' } + @{ Parameter = 'ReplicationAddresses' } + @{ Parameter = 'CaCertificateGroup' } + @{ Parameter = 'Encrypted' } + @{ Parameter = 'Remote' } + @{ Parameter = 'Throttle' } + @{ Parameter = 'RemoteId' } + @{ Parameter = 'RemoteName' } + ) { + $attrs = (Get-Command Update-PfbArrayConnection).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + } + + Context 'issue #64 -- the dead `names` key is gone' { + It 'sends remote_names, never names' { + Update-PfbArrayConnection -RemoteName 'FB-B' -ManagementAddress '10.0.2.101' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' -and -not $QueryParams.ContainsKey('names') + } + } + + It 'still binds -Name through the alias, and emits remote_names for it' { + Update-PfbArrayConnection -Name 'FB-B' -ManagementAddress '10.0.2.101' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' -and -not $QueryParams.ContainsKey('names') + } + } + + It 'declares Name as an alias of -RemoteName' { + (Get-Command Update-PfbArrayConnection).Parameters['RemoteName'].Aliases | + Should -Contain 'Name' + } + + It 'no longer declares a -Name parameter of its own' { + (Get-Command Update-PfbArrayConnection).Parameters.Keys | Should -Not -Contain 'Name' + } + } + + Context 'issue #64 -- pipeline binding by property name on -RemoteName' { + It 'binds -RemoteName by property name from a user-built object' { + [pscustomobject]@{ RemoteName = 'FB-B' } | + Update-PfbArrayConnection -Throttle @{ default_limit = 1073741824 } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' -and -not $QueryParams.ContainsKey('ids') + } + } + + It 'binds by property name through the Name alias' { + [pscustomobject]@{ Name = 'FB-B' } | + Update-PfbArrayConnection -Throttle @{ default_limit = 1073741824 } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['remote_names'] -eq 'FB-B' -and -not $QueryParams.ContainsKey('ids') + } + } + } + + Context 'issue #64 -- pipeline binding on -Id' { + It 'binds a piped connection object by id' { + [PSCustomObject]@{ id = 'conn-9' } | + Update-PfbArrayConnection -Throttle @{ default_limit = 1073741824 } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'conn-9' + } + } + + It 'does NOT coerce a piped object into -RemoteName (binding-order guard)' { + [PSCustomObject]@{ id = 'conn-9' } | + Update-PfbArrayConnection -Throttle @{ default_limit = 1073741824 } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('remote_names') -and + $QueryParams['ids'] -notlike '*@{*' + } + } + + It 'processes each of several piped connections individually' { + @([PSCustomObject]@{ id = 'conn-1' }, [PSCustomObject]@{ id = 'conn-2' }) | + Update-PfbArrayConnection -Throttle @{ default_limit = 1 } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 2 -Exactly + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'conn-2' + } + } + } +} diff --git a/Tests/Update-PfbAsyncLog.Tests.ps1 b/Tests/Update-PfbAsyncLog.Tests.ps1 new file mode 100644 index 0000000..9590121 --- /dev/null +++ b/Tests/Update-PfbAsyncLog.Tests.ps1 @@ -0,0 +1,136 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbAsyncLog - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends end_time and start_time as body fields' { + Update-PfbAsyncLog -StartTime 1700000000000 -EndTime 1700003600000 ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'logs-async' -and + $Body['start_time'] -eq 1700000000000 -and + $Body['end_time'] -eq 1700003600000 + } + } + + It 'sends an explicit -StartTime 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbAsyncLog -StartTime 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('start_time') -and $Body['start_time'] -eq 0 + } + } + + It 'sends an explicit -EndTime 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbAsyncLog -EndTime 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('end_time') -and $Body['end_time'] -eq 0 + } + } + + It 'builds hardware_components as name-reference objects' { + Update-PfbAsyncLog -HardwareComponents 'CH1.FB1','CH1.FB2' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['hardware_components'].Count -eq 2 -and + $Body['hardware_components'][0].name -eq 'CH1.FB1' -and + $Body['hardware_components'][1].name -eq 'CH1.FB2' + } + } + + It 'sends an EMPTY array for -HardwareComponents @() so the list can be cleared' { + Update-PfbAsyncLog -HardwareComponents @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('hardware_components') -and + @($Body['hardware_components']).Count -eq 0 + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbAsyncLog -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbAsyncLog -Attributes @{ start_time = 1700000000000 } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['start_time'] -eq 1700000000000 + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbAsyncLog -StartTime 1 -Attributes @{ start_time = 2 } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'EndTime' } + @{ Parameter = 'StartTime' } + @{ Parameter = 'HardwareComponents' } + ) { + $attrs = (Get-Command Update-PfbAsyncLog).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'does not expose any of the 6 read-only fields as parameters (constraint 11)' { + $keys = (Get-Command Update-PfbAsyncLog).Parameters.Keys + # All six are readOnly on LogsAsync in every cached spec version (verified 2.9 and + # 2.26): available_files, id, last_request_time, name, processing, progress. Id and + # Name were previously omitted from this loop only because they were parameters; + # issue #64 removed them, so the loop can now cover the full read-only set. + foreach ($ro in 'AvailableFiles','Id','LastRequestTime','Name','Processing','Progress') { + $keys | Should -Not -Contain $ro + } + } + } + + Context 'issue #64 -- the endpoint takes no query parameters' { + It 'sends no query parameters at all' { + Update-PfbAsyncLog -StartTime 1700000000000 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'logs-async' -and + ($null -eq $QueryParams -or $QueryParams.Count -eq 0) + } + } + + It 'exposes neither -Name nor -Id, which had no endpoint support to bind to' { + $keys = (Get-Command Update-PfbAsyncLog).Parameters.Keys + $keys | Should -Not -Contain 'Name' + $keys | Should -Not -Contain 'Id' + } + + It 'collapses to exactly two parameter sets' { + (Get-Command Update-PfbAsyncLog).ParameterSets.Name | Sort-Object | + Should -Be @('Attributes', 'Individual') + } + } +} diff --git a/Tests/Update-PfbBucketAuditFilter.Tests.ps1 b/Tests/Update-PfbBucketAuditFilter.Tests.ps1 new file mode 100644 index 0000000..72abd49 --- /dev/null +++ b/Tests/Update-PfbBucketAuditFilter.Tests.ps1 @@ -0,0 +1,138 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbBucketAuditFilter - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends actions and s3_prefixes as body fields' { + Update-PfbBucketAuditFilter -MemberName 'mybucket' -Actions 's3:GetObject', 's3:PutObject' ` + -S3Prefixes 'prefix1', 'prefix2' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'buckets/audit-filters' -and + @($Body['actions']).Count -eq 2 -and $Body['actions'][0] -eq 's3:GetObject' -and + @($Body['s3_prefixes']).Count -eq 2 -and $Body['s3_prefixes'][1] -eq 'prefix2' + } + } + + It 'sends an EMPTY array for -Actions @() so a list can be cleared (constraint 2, array field)' { + Update-PfbBucketAuditFilter -MemberName 'mybucket' -Actions @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('actions') -and @($Body['actions']).Count -eq 0 + } + } + + It 'sends an EMPTY array for -S3Prefixes @() so a list can be cleared (constraint 2, array field)' { + Update-PfbBucketAuditFilter -MemberName 'mybucket' -S3Prefixes @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('s3_prefixes') -and @($Body['s3_prefixes']).Count -eq 0 + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbBucketAuditFilter -MemberName 'mybucket' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + } + + Context 'query parameters (wire-correctness fix: bucket_names/bucket_ids, not member_names/member_ids)' { + It 'sends -MemberName as bucket_names, NOT member_names' { + Update-PfbBucketAuditFilter -MemberName 'mybucket' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['bucket_names'] -eq 'mybucket' -and -not $QueryParams.ContainsKey('member_names') + } + } + + It 'sends -MemberId as bucket_ids, NOT member_ids' { + Update-PfbBucketAuditFilter -MemberId 'bucket-1' -FilterNames 'mybucket' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['bucket_ids'] -eq 'bucket-1' -and -not $QueryParams.ContainsKey('member_ids') -and + -not $QueryParams.ContainsKey('bucket_names') + } + } + + It 'throws when -MemberId is used alone: the required "names" query parameter cannot be inferred from an ID (review finding)' { + { Update-PfbBucketAuditFilter -MemberId 'bucket-1' -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*-FilterNames is required*' + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 -Exactly + } + + It 'defaults the required "names" query parameter from -MemberName when -FilterNames is not supplied' { + Update-PfbBucketAuditFilter -MemberName 'mybucket' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['names'] -eq 'mybucket' + } + } + + It 'sends -FilterNames as names when explicitly supplied, overriding the -MemberName default' { + Update-PfbBucketAuditFilter -MemberId 'bucket-1' -FilterNames 'custom-filter-name' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + @($QueryParams['names']).Count -eq 1 -and @($QueryParams['names'])[0] -eq 'custom-filter-name' + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbBucketAuditFilter -MemberName 'mybucket' -Attributes @{ actions = @('s3:GetObject') } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + @($Body['actions'])[0] -eq 's3:GetObject' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbBucketAuditFilter -MemberName 'mybucket' -Actions @('s3:GetObject') ` + -Attributes @{ actions = @('s3:PutObject') } -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + + It 'rejects an unresolved selector plus typed parameter plus -Attributes' { + { Update-PfbBucketAuditFilter -MemberId 'bucket-1' -S3Prefixes @('p1') ` + -Attributes @{ s3_prefixes = @('p2') } -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'Actions' } + @{ Parameter = 'S3Prefixes' } + ) { + $attrs = (Get-Command Update-PfbBucketAuditFilter).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbBucketAuditFilter).Parameters.Keys + foreach ($p in 'Actions', 'S3Prefixes') { + $keys | Should -Contain $p + } + } + } +} diff --git a/Tests/Update-PfbCertificate.Tests.ps1 b/Tests/Update-PfbCertificate.Tests.ps1 new file mode 100644 index 0000000..ebc42ee --- /dev/null +++ b/Tests/Update-PfbCertificate.Tests.ps1 @@ -0,0 +1,224 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbCertificate - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends certificate, certificate_type, common_name and country as body fields' { + Update-PfbCertificate -Name 'web-cert' -Certificate '-----BEGIN CERTIFICATE-----' ` + -CertificateType 'appliance' -CommonName 'www.example.com' -Country 'Canada' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'certificates' -and + $QueryParams['names'] -eq 'web-cert' -and + $Body['certificate'] -eq '-----BEGIN CERTIFICATE-----' -and + $Body['certificate_type'] -eq 'appliance' -and + $Body['common_name'] -eq 'www.example.com' -and + $Body['country'] -eq 'Canada' + } + } + + It 'sends email, intermediate_certificate, key_algorithm and locality as body fields' { + Update-PfbCertificate -Name 'web-cert' -Email 'ops@example.com' ` + -IntermediateCertificate '-----BEGIN CHAIN-----' -KeyAlgorithm 'rsa' -Locality 'Toronto' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['email'] -eq 'ops@example.com' -and + $Body['intermediate_certificate'] -eq '-----BEGIN CHAIN-----' -and + $Body['key_algorithm'] -eq 'rsa' -and + $Body['locality'] -eq 'Toronto' + } + } + + It 'sends organization, organizational_unit, passphrase, private_key and state as body fields' { + Update-PfbCertificate -Name 'web-cert' -Organization 'Veridian Dynamics' ` + -OrganizationalUnit 'R&D' -Passphrase 's3cr3t' -PrivateKey '-----BEGIN PRIVATE KEY-----' ` + -State 'Ontario' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['organization'] -eq 'Veridian Dynamics' -and + $Body['organizational_unit'] -eq 'R&D' -and + $Body['passphrase'] -eq 's3cr3t' -and + $Body['private_key'] -eq '-----BEGIN PRIVATE KEY-----' -and + $Body['state'] -eq 'Ontario' + } + } + + It 'sends an explicit -Days 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbCertificate -Name 'web-cert' -Days 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('days') -and $Body['days'] -eq 0 + } + } + + It 'sends an explicit -KeySize 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbCertificate -Name 'web-cert' -KeySize 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('key_size') -and $Body['key_size'] -eq 0 + } + } + + It 'omits days and key_size entirely when not supplied' { + Update-PfbCertificate -Name 'web-cert' -Certificate 'x' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $Body.ContainsKey('days') -and -not $Body.ContainsKey('key_size') + } + } + + It 'sends subject_alternative_names as a plain string array' { + Update-PfbCertificate -Name 'web-cert' -SubjectAlternativeNames 'alt1.example.com', 'alt2.example.com' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + @($Body['subject_alternative_names']).Count -eq 2 -and + $Body['subject_alternative_names'][0] -eq 'alt1.example.com' + } + } + + It 'sends an EMPTY array for -SubjectAlternativeNames @() so a list can be cleared' { + Update-PfbCertificate -Name 'web-cert' -SubjectAlternativeNames @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('subject_alternative_names') -and + @($Body['subject_alternative_names']).Count -eq 0 + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbCertificate -Name 'web-cert' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the certificate by id when -Id is used' { + Update-PfbCertificate -Id 'cert-1' -Certificate 'x' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'cert-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-GenerateNewKey query parameter (constraint 17: bare, not in *Individual sets)' { + It 'sends generate_new_key when supplied' { + Update-PfbCertificate -Name 'web-cert' -GenerateNewKey $true -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('generate_new_key') -and $QueryParams['generate_new_key'] -eq $true + } + } + + It 'sends an explicit -GenerateNewKey:$false rather than dropping it' { + Update-PfbCertificate -Name 'web-cert' -GenerateNewKey $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('generate_new_key') -and $QueryParams['generate_new_key'] -eq $false + } + } + + It 'omits generate_new_key entirely when not supplied' { + Update-PfbCertificate -Name 'web-cert' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('generate_new_key') + } + } + + It 'is usable alongside -Attributes without an AmbiguousParameterSet error (query params are orthogonal to the body)' { + { Update-PfbCertificate -Name 'web-cert' -Attributes @{ certificate = 'x' } -GenerateNewKey $true ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | Should -Not -Throw + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['generate_new_key'] -eq $true -and $Body['certificate'] -eq 'x' + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbCertificate -Name 'web-cert' -Attributes @{ certificate = 'raw-cert' } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['certificate'] -eq 'raw-cert' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbCertificate -Name 'web-cert' -Certificate 'x' -Attributes @{ certificate = 'y' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'read-only fields are never added as parameters (constraint 11)' { + It 'has no - parameter' -ForEach @( + @{ Parameter = 'IssuedBy' } + @{ Parameter = 'IssuedTo' } + @{ Parameter = 'Realms' } + @{ Parameter = 'Status' } + @{ Parameter = 'ValidFrom' } + @{ Parameter = 'ValidTo' } + ) { + (Get-Command Update-PfbCertificate).Parameters.Keys | Should -Not -Contain $Parameter + } + } + + Context 'constraint compliance' { + It 'puts ValidateSet on -CertificateType with exactly appliance,external in order' { + $validateSet = (Get-Command Update-PfbCertificate).Parameters['CertificateType'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } + $validateSet.ValidValues | Should -Be @('appliance', 'external') + } + + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'Certificate' } + @{ Parameter = 'CommonName' } + @{ Parameter = 'Country' } + @{ Parameter = 'Days' } + @{ Parameter = 'Email' } + @{ Parameter = 'IntermediateCertificate' } + @{ Parameter = 'KeyAlgorithm' } + @{ Parameter = 'KeySize' } + @{ Parameter = 'Locality' } + @{ Parameter = 'Organization' } + @{ Parameter = 'OrganizationalUnit' } + @{ Parameter = 'Passphrase' } + @{ Parameter = 'PrivateKey' } + @{ Parameter = 'State' } + @{ Parameter = 'SubjectAlternativeNames' } + ) { + $attrs = (Get-Command Update-PfbCertificate).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts (constraint 11)' { + $keys = (Get-Command Update-PfbCertificate).Parameters.Keys + foreach ($p in 'Certificate', 'CertificateType', 'CommonName', 'Country', 'Days', 'Email', + 'IntermediateCertificate', 'KeyAlgorithm', 'KeySize', 'Locality', 'Organization', + 'OrganizationalUnit', 'Passphrase', 'PrivateKey', 'State', 'SubjectAlternativeNames') { + $keys | Should -Contain $p + } + } + } +} diff --git a/Tests/Update-PfbDirectoryServiceRole.Tests.ps1 b/Tests/Update-PfbDirectoryServiceRole.Tests.ps1 new file mode 100644 index 0000000..63140c7 --- /dev/null +++ b/Tests/Update-PfbDirectoryServiceRole.Tests.ps1 @@ -0,0 +1,112 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbDirectoryServiceRole - typed body + query parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends group and group_base as body fields' { + Update-PfbDirectoryServiceRole -Name 'ad-admins' -Group 'CN=FB-SuperAdmins,OU=Groups,DC=corp,DC=example,DC=com' ` + -GroupBase 'DC=corp,DC=example,DC=com' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'directory-services/roles' -and + $QueryParams['names'] -eq 'ad-admins' -and + $Body['group'] -eq 'CN=FB-SuperAdmins,OU=Groups,DC=corp,DC=example,DC=com' -and + $Body['group_base'] -eq 'DC=corp,DC=example,DC=com' + } + } + + It 'sends an EMPTY string for -Group "" rather than dropping the key' { + Update-PfbDirectoryServiceRole -Name 'ad-admins' -Group '' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('group') -and $Body['group'] -eq '' + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbDirectoryServiceRole -Name 'ad-admins' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the role by id when -Id is used' { + Update-PfbDirectoryServiceRole -Id 'role-1' -Group 'g' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'role-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-RoleIds/-RoleNames are not exposed (constraint 9 precedent: structurally dead field)' { + It 'has no -RoleIds parameter' { + (Get-Command Update-PfbDirectoryServiceRole).Parameters.Keys | Should -Not -Contain 'RoleIds' + } + + It 'has no -RoleNames parameter' { + (Get-Command Update-PfbDirectoryServiceRole).Parameters.Keys | Should -Not -Contain 'RoleNames' + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbDirectoryServiceRole -Name 'ad-admins' -Attributes @{ group = 'raw-group' } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['group'] -eq 'raw-group' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbDirectoryServiceRole -Name 'ad-admins' -Group 'g' -Attributes @{ group = 'y' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'deprecated fields are not exposed (constraint 9)' { + It 'has no -Role parameter' { + (Get-Command Update-PfbDirectoryServiceRole).Parameters.Keys | Should -Not -Contain 'Role' + } + } + + Context 'read-only fields are not exposed (constraint 11)' { + It 'has no -ManagementAccessPolicies parameter' { + (Get-Command Update-PfbDirectoryServiceRole).Parameters.Keys | Should -Not -Contain 'ManagementAccessPolicies' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'Group' } + @{ Parameter = 'GroupBase' } + ) { + $attrs = (Get-Command Update-PfbDirectoryServiceRole).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbDirectoryServiceRole).Parameters.Keys + foreach ($p in 'Group', 'GroupBase') { + $keys | Should -Contain $p + } + } + } +} diff --git a/Tests/Update-PfbDns.Tests.ps1 b/Tests/Update-PfbDns.Tests.ps1 new file mode 100644 index 0000000..f3c49b4 --- /dev/null +++ b/Tests/Update-PfbDns.Tests.ps1 @@ -0,0 +1,188 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbDns - typed body and query parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'pre-existing typed parameters now guarded by ContainsKey (constraint 16 fix)' { + It 'sends domain and nameservers as body fields' { + Update-PfbDns -Domain 'example.com' -Nameservers '10.0.0.1', '10.0.0.2' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'dns' -and + $Body['domain'] -eq 'example.com' -and + @($Body['nameservers']).Count -eq 2 + } + } + + It 'sends an EMPTY array for -Nameservers @() so the list can be cleared' { + Update-PfbDns -Nameservers @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('nameservers') -and @($Body['nameservers']).Count -eq 0 + } + } + + It 'accepts -Domain/-Nameservers positionally (whole-branch review finding I-1: adding a parameter set disables ALL implicit positional binding, so this must stay explicit)' { + Update-PfbDns 'example.com' @('10.0.0.1', '10.0.0.2') -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['domain'] -eq 'example.com' -and @($Body['nameservers']).Count -eq 2 + } + } + + It 'accepts -Attributes positionally' { + Update-PfbDns @{ domain = 'example.com' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['domain'] -eq 'example.com' + } + } + + It 'rejects -Domain combined with -Attributes at bind time (moved into Individual set)' { + { Update-PfbDns -Domain 'example.com' -Attributes @{ domain = 'other.com' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'new typed body parameters (missing body properties)' { + It 'sends ca_certificate as a name-reference object' { + Update-PfbDns -CaCertificate 'my-ca-cert' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['ca_certificate'].name -eq 'my-ca-cert' + } + } + + It 'sends ca_certificate_group as a name-reference object' { + Update-PfbDns -CaCertificateGroup 'my-ca-group' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['ca_certificate_group'].name -eq 'my-ca-group' + } + } + + It 'sends -NewName as the name body field (exception: name -> -NewName)' { + Update-PfbDns -NewName 'mgmt-dns' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['name'] -eq 'mgmt-dns' + } + } + + It 'has no -Name body-rename parameter (only the -Name query selector exists)' { + $params = (Get-Command Update-PfbDns).Parameters + $params['Name'].ParameterType.Name | Should -Be 'String' + $params.Keys | Should -Contain 'NewName' + } + + It 'sends services as a plain string array' { + Update-PfbDns -Services 'management', 'data' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + @($Body['services']) -join ',' -eq 'management,data' + } + } + + It 'sends an EMPTY array for -Services @() so the list can be cleared' { + Update-PfbDns -Services @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('services') -and @($Body['services']).Count -eq 0 + } + } + + It 'builds sources as an array of name-reference objects (constraint 8b)' { + Update-PfbDns -Sources 'vir0', 'vir1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['sources'].Count -eq 2 -and + $Body['sources'][0].name -eq 'vir0' -and + $Body['sources'][1].name -eq 'vir1' + } + } + + It 'sends an EMPTY array for -Sources @() so the list can be cleared' { + Update-PfbDns -Sources @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('sources') -and @($Body['sources']).Count -eq 0 + } + } + } + + Context '-Name / -Id query parameters, declared bare (constraint 17)' { + It 'sends -Name as names' { + Update-PfbDns -Name 'mgmt-dns' -Domain 'example.com' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['names'] -eq 'mgmt-dns' + } + } + + It 'sends -Id as ids' { + Update-PfbDns -Id 'dns-1' -Domain 'example.com' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'dns-1' + } + } + + It 'combines -Name with -Attributes without a parameter-set conflict (bare, orthogonal to the body)' { + Update-PfbDns -Name 'mgmt-dns' -Attributes @{ domain = 'example.com' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['names'] -eq 'mgmt-dns' -and $Body['domain'] -eq 'example.com' + } + } + + It 'omits names/ids when neither is supplied' { + Update-PfbDns -Domain 'example.com' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('names') -and -not $QueryParams.ContainsKey('ids') + } + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on any new parameter (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'CaCertificate' } + @{ Parameter = 'CaCertificateGroup' } + @{ Parameter = 'NewName' } + @{ Parameter = 'Services' } + @{ Parameter = 'Sources' } + ) { + $attrs = (Get-Command Update-PfbDns).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbDns).Parameters.Keys + foreach ($p in 'Domain','Nameservers','CaCertificate','CaCertificateGroup','NewName','Services','Sources') { + $keys | Should -Contain $p + } + } + + It 'omits every body key when no typed body parameter is supplied (constraint 19, empty body permitted)' { + Update-PfbDns -Name 'mgmt-dns' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + } +} diff --git a/Tests/Update-PfbFileSystemExport.Tests.ps1 b/Tests/Update-PfbFileSystemExport.Tests.ps1 new file mode 100644 index 0000000..6928d9f --- /dev/null +++ b/Tests/Update-PfbFileSystemExport.Tests.ps1 @@ -0,0 +1,114 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbFileSystemExport - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends export_name as a plain body field' { + Update-PfbFileSystemExport -Name 'export1' -ExportName '/fs1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'file-system-exports' -and + $QueryParams['names'] -eq 'export1' -and + $Body['export_name'] -eq '/fs1' + } + } + + It 'sends an EMPTY string for -ExportName "" rather than dropping the key' { + Update-PfbFileSystemExport -Name 'export1' -ExportName '' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('export_name') -and $Body['export_name'] -eq '' + } + } + + It 'sends member, policy, server, and share_policy as name-reference objects' { + Update-PfbFileSystemExport -Name 'export1' -Member 'fs1' -Policy 'nfs-default' ` + -Server 'server1' -SharePolicy 'share-default' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['member'].name -eq 'fs1' -and + $Body['policy'].name -eq 'nfs-default' -and + $Body['server'].name -eq 'server1' -and + $Body['share_policy'].name -eq 'share-default' + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbFileSystemExport -Name 'export1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the export by id when -Id is used' { + Update-PfbFileSystemExport -Id 'export-1' -ExportName '/fs1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'export-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbFileSystemExport -Name 'export1' -Attributes @{ rules = '*(ro)' } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['rules'] -eq '*(ro)' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbFileSystemExport -Name 'export1' -ExportName '/fs1' -Attributes @{ export_name = '/fs2' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'read-only fields are not exposed (constraint 11)' { + It 'has none of the read-only field parameters' -ForEach @( + @{ Parameter = 'Context' } + @{ Parameter = 'Enabled' } + @{ Parameter = 'PolicyType' } + @{ Parameter = 'Status' } + ) { + (Get-Command Update-PfbFileSystemExport).Parameters.Keys | Should -Not -Contain $Parameter + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'ExportName' } + @{ Parameter = 'Member' } + @{ Parameter = 'Policy' } + @{ Parameter = 'Server' } + @{ Parameter = 'SharePolicy' } + ) { + $attrs = (Get-Command Update-PfbFileSystemExport).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbFileSystemExport).Parameters.Keys + foreach ($p in 'ExportName', 'Member', 'Policy', 'Server', 'SharePolicy') { + $keys | Should -Contain $p + } + } + } +} diff --git a/Tests/Update-PfbFleet.Tests.ps1 b/Tests/Update-PfbFleet.Tests.ps1 new file mode 100644 index 0000000..cdd3c2c --- /dev/null +++ b/Tests/Update-PfbFleet.Tests.ps1 @@ -0,0 +1,81 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbFleet - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends -NewName as the name body field (rename exception, constraint: name -> -NewName)' { + Update-PfbFleet -Name 'fleet-prod' -NewName 'fleet-renamed' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'fleets' -and + $QueryParams['names'] -eq 'fleet-prod' -and + $Body['name'] -eq 'fleet-renamed' + } + } + + It 'sends an EMPTY string for -NewName "" rather than dropping the key' { + Update-PfbFleet -Name 'fleet-prod' -NewName '' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('name') -and $Body['name'] -eq '' + } + } + + It 'has no -Name-shaped body parameter, only -NewName' { + (Get-Command Update-PfbFleet).Parameters.Keys | Should -Not -Contain 'FleetName' + } + + It 'targets the fleet by id when -Id is used' { + Update-PfbFleet -Id 'fleet-1' -NewName 'renamed' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'fleet-1' -and -not $QueryParams.ContainsKey('names') + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbFleet -Name 'fleet-prod' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbFleet -Name 'fleet-prod' -Attributes @{ name = 'raw-renamed' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['name'] -eq 'raw-renamed' + } + } + + It 'rejects -Attributes combined with -NewName at bind time' { + { Update-PfbFleet -Name 'fleet-prod' -NewName 'x' -Attributes @{ name = 'y' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on -NewName (constraint 3, no spec enum)' { + $attrs = (Get-Command Update-PfbFleet).Parameters['NewName'].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + } +} diff --git a/Tests/Update-PfbHardware.Tests.ps1 b/Tests/Update-PfbHardware.Tests.ps1 new file mode 100644 index 0000000..583db3c --- /dev/null +++ b/Tests/Update-PfbHardware.Tests.ps1 @@ -0,0 +1,85 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbHardware - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends identify_enabled as a body field' { + Update-PfbHardware -Name 'CH1.FB1' -IdentifyEnabled $true -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'hardware' -and + $QueryParams['names'] -eq 'CH1.FB1' -and + $Body['identify_enabled'] -eq $true + } + } + + It 'sends an explicit -IdentifyEnabled:$false (ContainsKey semantics, not truthiness)' { + Update-PfbHardware -Name 'CH1.FB1' -IdentifyEnabled $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('identify_enabled') -and $Body['identify_enabled'] -eq $false + } + } + + It 'omits identify_enabled entirely when not supplied' { + Update-PfbHardware -Name 'CH1.FB1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $Body.ContainsKey('identify_enabled') + } + } + + It 'targets the component by id when -Id is used' { + Update-PfbHardware -Id 'hw-1' -IdentifyEnabled $true -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'hw-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbHardware -Name 'CH1.FB1' -Attributes @{ identify_enabled = $true } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['identify_enabled'] -eq $true + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbHardware -Name 'CH1.FB1' -IdentifyEnabled $true -Attributes @{ identify_enabled = $false } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on -IdentifyEnabled (constraint 3, no spec enum)' { + $attrs = (Get-Command Update-PfbHardware).Parameters['IdentifyEnabled'].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'does not expose any of the 15 read-only fields as parameters (constraint 11)' { + $keys = (Get-Command Update-PfbHardware).Parameters.Keys + foreach ($ro in 'DataMac','Details','Index','ManagementMac','Model','PartNumber','SensorReadings','Serial','Slot','Speed','Status','Temperature','Type') { + $keys | Should -Not -Contain $ro + } + } + } +} diff --git a/Tests/Update-PfbHardwareConnector.Tests.ps1 b/Tests/Update-PfbHardwareConnector.Tests.ps1 new file mode 100644 index 0000000..a00b866 --- /dev/null +++ b/Tests/Update-PfbHardwareConnector.Tests.ps1 @@ -0,0 +1,118 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbHardwareConnector - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends lane_speed, lanes_per_port, port_count and port_speed as body fields' { + Update-PfbHardwareConnector -Name 'CH1.FM1.ETH1' -LaneSpeed 25000000000 -LanesPerPort 4 ` + -PortCount 1 -PortSpeed 100000000000 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'hardware-connectors' -and + $QueryParams['names'] -eq 'CH1.FM1.ETH1' -and + $Body['lane_speed'] -eq 25000000000 -and + $Body['lanes_per_port'] -eq 4 -and + $Body['port_count'] -eq 1 -and + $Body['port_speed'] -eq 100000000000 + } + } + + It 'sends an explicit -LaneSpeed 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbHardwareConnector -Name 'CH1.FM1.ETH1' -LaneSpeed 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('lane_speed') -and $Body['lane_speed'] -eq 0 + } + } + + It 'sends an explicit -LanesPerPort 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbHardwareConnector -Name 'CH1.FM1.ETH1' -LanesPerPort 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('lanes_per_port') -and $Body['lanes_per_port'] -eq 0 + } + } + + It 'sends an explicit -PortCount 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbHardwareConnector -Name 'CH1.FM1.ETH1' -PortCount 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('port_count') -and $Body['port_count'] -eq 0 + } + } + + It 'sends an explicit -PortSpeed 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbHardwareConnector -Name 'CH1.FM1.ETH1' -PortSpeed 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('port_speed') -and $Body['port_speed'] -eq 0 + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbHardwareConnector -Name 'CH1.FM1.ETH1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the connector by id when -Id is used' { + Update-PfbHardwareConnector -Id 'conn-1' -PortSpeed 40000000000 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'conn-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbHardwareConnector -Name 'CH1.FM1.ETH1' -Attributes @{ port_speed = 40000000000 } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['port_speed'] -eq 40000000000 + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbHardwareConnector -Name 'CH1.FM1.ETH1' -PortSpeed 40000000000 -Attributes @{ port_speed = 1 } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'LaneSpeed' } + @{ Parameter = 'LanesPerPort' } + @{ Parameter = 'PortCount' } + @{ Parameter = 'PortSpeed' } + ) { + $attrs = (Get-Command Update-PfbHardwareConnector).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'does not expose any of the 4 read-only fields as parameters (constraint 11)' { + $keys = (Get-Command Update-PfbHardwareConnector).Parameters.Keys + foreach ($ro in 'ConnectorType','TransceiverType') { + $keys | Should -Not -Contain $ro + } + } + } +} diff --git a/Tests/Update-PfbKmip.Tests.ps1 b/Tests/Update-PfbKmip.Tests.ps1 new file mode 100644 index 0000000..c94c960 --- /dev/null +++ b/Tests/Update-PfbKmip.Tests.ps1 @@ -0,0 +1,88 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbKmip - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends -CaCertificate as a scalar name reference' { + Update-PfbKmip -Name 'kmip-server-1' -CaCertificate 'cert-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'kmip' -and + $QueryParams['names'] -eq 'kmip-server-1' -and + $Body['ca_certificate'].name -eq 'cert-1' + } + } + + It 'sends -CaCertificateGroup as a scalar name reference' { + Update-PfbKmip -Name 'kmip-server-1' -CaCertificateGroup 'kmip-certs' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['ca_certificate_group'].name -eq 'kmip-certs' + } + } + + It 'sends -Uris as a plain string array' { + Update-PfbKmip -Name 'kmip-server-1' -Uris 'kmip1.example.com:5696', 'kmip2.example.com:5696' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['uris'].Count -eq 2 -and + $Body['uris'][0] -eq 'kmip1.example.com:5696' -and + $Body['uris'][1] -eq 'kmip2.example.com:5696' + } + } + + It 'sends an EMPTY array for -Uris @() so the list can be cleared (constraint 2)' { + Update-PfbKmip -Name 'kmip-server-1' -Uris @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('uris') -and @($Body['uris']).Count -eq 0 + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbKmip -Name 'kmip-server-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the KMIP config by id when -Id is used' { + Update-PfbKmip -Id 'kmip-1' -Uris 'kmip1.example.com:5696' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'kmip-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbKmip -Name 'kmip-server-1' -Attributes @{ uris = @('raw.example.com:5696') } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['uris'][0] -eq 'raw.example.com:5696' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbKmip -Name 'kmip-server-1' -Uris 'x.example.com:5696' -Attributes @{ uris = @('y') } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } +} diff --git a/Tests/Update-PfbLag.Tests.ps1 b/Tests/Update-PfbLag.Tests.ps1 new file mode 100644 index 0000000..302fa82 --- /dev/null +++ b/Tests/Update-PfbLag.Tests.ps1 @@ -0,0 +1,88 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbLag - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'builds ports as name-reference objects' { + Update-PfbLag -Name 'lag1' -Ports 'CH1.FM1.ETH1', 'CH1.FM1.ETH3' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'link-aggregation-groups' -and + $QueryParams['names'] -eq 'lag1' -and + $Body['ports'].Count -eq 2 -and + $Body['ports'][0].name -eq 'CH1.FM1.ETH1' -and + $Body['ports'][1].name -eq 'CH1.FM1.ETH3' + } + } + + It 'builds add_ports as name-reference objects' { + Update-PfbLag -Name 'lag1' -AddPorts 'CH1.FM1.ETH5' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['add_ports'].Count -eq 1 -and $Body['add_ports'][0].name -eq 'CH1.FM1.ETH5' + } + } + + It 'builds remove_ports as name-reference objects' { + Update-PfbLag -Name 'lag1' -RemovePorts 'CH1.FM1.ETH5' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['remove_ports'].Count -eq 1 -and $Body['remove_ports'][0].name -eq 'CH1.FM1.ETH5' + } + } + + It 'sends an EMPTY array for -Ports @() so a list can be cleared (constraint 2)' { + Update-PfbLag -Name 'lag1' -Ports @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('ports') -and @($Body['ports']).Count -eq 0 + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbLag -Name 'lag1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the LAG by id when -Id is used' { + Update-PfbLag -Id 'lag-1' -Ports 'CH1.FM1.ETH1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'lag-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbLag -Name 'lag1' -Attributes @{ lacp_mode = 'passive' } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['lacp_mode'] -eq 'passive' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbLag -Name 'lag1' -Ports 'CH1.FM1.ETH1' -Attributes @{ ports = @() } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } +} diff --git a/Tests/Update-PfbLegalHold.Tests.ps1 b/Tests/Update-PfbLegalHold.Tests.ps1 new file mode 100644 index 0000000..3d49e6b --- /dev/null +++ b/Tests/Update-PfbLegalHold.Tests.ps1 @@ -0,0 +1,81 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbLegalHold - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends -Description as the description body field' { + Update-PfbLegalHold -Name 'litigation-hold-2024' -Description 'Updated description' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'legal-holds' -and + $QueryParams['names'] -eq 'litigation-hold-2024' -and + $Body['description'] -eq 'Updated description' + } + } + + It 'sends an EMPTY string for -Description "" rather than dropping the key' { + Update-PfbLegalHold -Name 'litigation-hold-2024' -Description '' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('description') -and $Body['description'] -eq '' + } + } + + It 'omits description entirely when not supplied' { + Update-PfbLegalHold -Name 'litigation-hold-2024' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $Body.ContainsKey('description') + } + } + + It 'targets the legal hold by id when -Id is used' { + Update-PfbLegalHold -Id 'hold-1' -Description 'x' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'hold-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbLegalHold -Name 'litigation-hold-2024' -Attributes @{ description = 'raw' } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['description'] -eq 'raw' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbLegalHold -Name 'litigation-hold-2024' -Description 'x' -Attributes @{ description = 'y' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'an empty write is permitted (constraint 19)' { + It 'resolves cleanly and sends an empty body when no parameter is supplied' { + Update-PfbLegalHold -Name 'litigation-hold-2024' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + } +} diff --git a/Tests/Update-PfbLegalHoldEntity.Tests.ps1 b/Tests/Update-PfbLegalHoldEntity.Tests.ps1 new file mode 100644 index 0000000..04d63fa --- /dev/null +++ b/Tests/Update-PfbLegalHoldEntity.Tests.ps1 @@ -0,0 +1,90 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbLegalHoldEntity - query parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'existing -Name selector (regression, unchanged)' { + It 'still sends -Name as names' { + Update-PfbLegalHoldEntity -Name 'fs1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'legal-holds/held-entities' -and + $QueryParams['names'] -eq 'fs1' + } + } + } + + Context 'new query parameters' { + It 'joins -FileSystemIds and -FileSystemNames with commas' { + Update-PfbLegalHoldEntity -Name 'fs1' -FileSystemIds 'fsid-1', 'fsid-2' -FileSystemNames 'fs1', 'fs2' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['file_system_ids'] -eq 'fsid-1,fsid-2' -and + $QueryParams['file_system_names'] -eq 'fs1,fs2' + } + } + + It 'joins -Ids and -Paths with commas' { + Update-PfbLegalHoldEntity -Name 'fs1' -Ids 'id-1', 'id-2' -Paths '/dir1', '/dir2' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'id-1,id-2' -and + $QueryParams['paths'] -eq '/dir1,/dir2' + } + } + + It 'sends an EMPTY array for -Ids @() so the query key still reaches the wire (constraint 2)' { + Update-PfbLegalHoldEntity -Name 'fs1' -Ids @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('ids') -and @($QueryParams['ids'] -split ',' | Where-Object { $_ }).Count -eq 0 + } + } + + It 'sends an explicit -Recursive:$false (ContainsKey semantics, not truthiness)' { + Update-PfbLegalHoldEntity -Name 'fs1' -Recursive $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('recursive') -and $QueryParams['recursive'] -eq $false + } + } + + It 'sends an explicit -Released:$true so a hold can be released' { + Update-PfbLegalHoldEntity -Name 'fs1' -Released $true -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('released') -and $QueryParams['released'] -eq $true + } + } + + It 'omits recursive and released entirely when not supplied' { + Update-PfbLegalHoldEntity -Name 'fs1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('recursive') -and -not $QueryParams.ContainsKey('released') + } + } + } + + Context 'endpoint accepts no request body' { + It 'sends an empty body when -Attributes is not supplied' { + Update-PfbLegalHoldEntity -Name 'fs1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + } +} diff --git a/Tests/Update-PfbLifecycleRule.Tests.ps1 b/Tests/Update-PfbLifecycleRule.Tests.ps1 new file mode 100644 index 0000000..d1678aa --- /dev/null +++ b/Tests/Update-PfbLifecycleRule.Tests.ps1 @@ -0,0 +1,129 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbLifecycleRule - typed body and query parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed body parameters' { + It 'sends -Prefix as the prefix body field' { + Update-PfbLifecycleRule -Name 'archive' -Prefix 'old/' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'lifecycle-rules' -and + $QueryParams['names'] -eq 'archive' -and + $Body['prefix'] -eq 'old/' + } + } + + It 'sends an explicit -Enabled:$false (ContainsKey semantics, not truthiness)' { + Update-PfbLifecycleRule -Name 'cleanup' -Enabled $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('enabled') -and $Body['enabled'] -eq $false + } + } + + It 'sends an explicit -AbortIncompleteMultipartUploadsAfter 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbLifecycleRule -Name 'cleanup' -AbortIncompleteMultipartUploadsAfter 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('abort_incomplete_multipart_uploads_after') -and + $Body['abort_incomplete_multipart_uploads_after'] -eq 0 + } + } + + It 'sends -KeepCurrentVersionFor, -KeepCurrentVersionUntil and -KeepPreviousVersionFor as integer fields' { + Update-PfbLifecycleRule -Name 'cleanup' -KeepCurrentVersionFor 5184000000 ` + -KeepCurrentVersionUntil 7776000000 -KeepPreviousVersionFor 2592000000 ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['keep_current_version_for'] -eq 5184000000 -and + $Body['keep_current_version_until'] -eq 7776000000 -and + $Body['keep_previous_version_for'] -eq 2592000000 + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbLifecycleRule -Name 'cleanup' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the lifecycle rule by id when -Id is used' { + Update-PfbLifecycleRule -Id 'rule-1' -Prefix 'x' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'rule-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context 'typed query parameters (bare, not in Individual sets -- constraint 17)' { + It 'joins -BucketIds and -BucketNames with commas' { + Update-PfbLifecycleRule -Name 'archive' -BucketIds 'b-1', 'b-2' -BucketNames 'bkt1', 'bkt2' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['bucket_ids'] -eq 'b-1,b-2' -and $QueryParams['bucket_names'] -eq 'bkt1,bkt2' + } + } + + It 'sends -ConfirmDate as an explicit 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbLifecycleRule -Name 'archive' -ConfirmDate 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams.ContainsKey('confirm_date') -and $QueryParams['confirm_date'] -eq 0 + } + } + + It 'combines a query parameter with -Attributes without a set conflict' { + Update-PfbLifecycleRule -Name 'archive' -Attributes @{ prefix = 'raw/' } -BucketNames 'bkt1' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['prefix'] -eq 'raw/' -and $QueryParams['bucket_names'] -eq 'bkt1' + } + } + + It 'omits bucket_ids, bucket_names and confirm_date entirely when not supplied' { + Update-PfbLifecycleRule -Name 'archive' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('bucket_ids') -and + -not $QueryParams.ContainsKey('bucket_names') -and + -not $QueryParams.ContainsKey('confirm_date') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbLifecycleRule -Name 'archive' -Attributes @{ prefix = 'raw/' } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['prefix'] -eq 'raw/' + } + } + + It 'rejects -Attributes combined with a typed BODY parameter at bind time' { + { Update-PfbLifecycleRule -Name 'archive' -Prefix 'x' -Attributes @{ prefix = 'y' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } +} diff --git a/Tests/Update-PfbLogTargetFileSystem.Tests.ps1 b/Tests/Update-PfbLogTargetFileSystem.Tests.ps1 new file mode 100644 index 0000000..cee80bf --- /dev/null +++ b/Tests/Update-PfbLogTargetFileSystem.Tests.ps1 @@ -0,0 +1,111 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbLogTargetFileSystem - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends keep_for, keep_size and name as body fields' { + Update-PfbLogTargetFileSystem -Name 'log-fs-target1' -KeepFor 86400000 -KeepSize 1000000 ` + -NewName 'renamed-target' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'log-targets/file-systems' -and + $QueryParams['names'] -eq 'log-fs-target1' -and + $Body['keep_for'] -eq 86400000 -and + $Body['keep_size'] -eq 1000000 -and + $Body['name'] -eq 'renamed-target' + } + } + + It 'sends an explicit -KeepFor 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbLogTargetFileSystem -Name 'log-fs-target1' -KeepFor 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('keep_for') -and $Body['keep_for'] -eq 0 + } + } + + It 'sends an explicit -KeepSize 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbLogTargetFileSystem -Name 'log-fs-target1' -KeepSize 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('keep_size') -and $Body['keep_size'] -eq 0 + } + } + + It 'builds file_system as a name-reference object (constraint 8a, scalar reference)' { + Update-PfbLogTargetFileSystem -Name 'log-fs-target1' -FileSystem 'fs1' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['file_system'].name -eq 'fs1' + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbLogTargetFileSystem -Name 'log-fs-target1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the log target by id when -Id is used' { + Update-PfbLogTargetFileSystem -Id 'lt-1' -KeepFor 1 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'lt-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbLogTargetFileSystem -Name 'log-fs-target1' -Attributes @{ keep_for = 86400000 } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['keep_for'] -eq 86400000 + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbLogTargetFileSystem -Name 'log-fs-target1' -KeepFor 1 -Attributes @{ keep_for = 2 } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'FileSystem' } + @{ Parameter = 'KeepFor' } + @{ Parameter = 'KeepSize' } + @{ Parameter = 'NewName' } + ) { + $attrs = (Get-Command Update-PfbLogTargetFileSystem).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'has no -LogTargetFileSystemName parameter (the "name" body field uses -NewName per the exception)' { + (Get-Command Update-PfbLogTargetFileSystem).Parameters.Keys | Should -Not -Contain 'LogTargetFileSystemName' + } + + It 'does not expose the read-only id field as a parameter (constraint 11)' { + (Get-Command Update-PfbLogTargetFileSystem).Parameters.Keys | Should -Not -Contain 'IdField' + } + } +} diff --git a/Tests/Update-PfbLogTargetObjectStore.Tests.ps1 b/Tests/Update-PfbLogTargetObjectStore.Tests.ps1 new file mode 100644 index 0000000..b5d04a5 --- /dev/null +++ b/Tests/Update-PfbLogTargetObjectStore.Tests.ps1 @@ -0,0 +1,107 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbLogTargetObjectStore - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends name as a body field' { + Update-PfbLogTargetObjectStore -Name 'log-obj-target1' -NewName 'renamed-target' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'log-targets/object-store' -and + $QueryParams['names'] -eq 'log-obj-target1' -and + $Body['name'] -eq 'renamed-target' + } + } + + It 'builds bucket as a name-reference object (constraint 8a, scalar reference)' { + Update-PfbLogTargetObjectStore -Name 'log-obj-target1' -Bucket 'bucket1' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['bucket'].name -eq 'bucket1' + } + } + + It 'passes log_name_prefix through as a composite hashtable (constraint 8c)' { + Update-PfbLogTargetObjectStore -Name 'log-obj-target1' -LogNamePrefix @{ prefix = 's3auditlog' } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['log_name_prefix']['prefix'] -eq 's3auditlog' + } + } + + It 'passes log_rotate through as a composite hashtable (constraint 8c)' { + Update-PfbLogTargetObjectStore -Name 'log-obj-target1' -LogRotate @{ duration = 300000 } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['log_rotate']['duration'] -eq 300000 + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbLogTargetObjectStore -Name 'log-obj-target1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the log target by id when -Id is used' { + Update-PfbLogTargetObjectStore -Id 'lt-1' -NewName 'x' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'lt-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbLogTargetObjectStore -Name 'log-obj-target1' -Attributes @{ bucket = @{ name = 'raw-bucket' } } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['bucket']['name'] -eq 'raw-bucket' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbLogTargetObjectStore -Name 'log-obj-target1' -NewName 'x' -Attributes @{ name = 'y' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'Bucket' } + @{ Parameter = 'LogNamePrefix' } + @{ Parameter = 'LogRotate' } + @{ Parameter = 'NewName' } + ) { + $attrs = (Get-Command Update-PfbLogTargetObjectStore).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'has no -LogTargetObjectStoreName parameter (the "name" body field uses -NewName per the exception)' { + (Get-Command Update-PfbLogTargetObjectStore).Parameters.Keys | Should -Not -Contain 'LogTargetObjectStoreName' + } + } +} diff --git a/Tests/Update-PfbManagementAccessPolicy.Tests.ps1 b/Tests/Update-PfbManagementAccessPolicy.Tests.ps1 new file mode 100644 index 0000000..a268d1a --- /dev/null +++ b/Tests/Update-PfbManagementAccessPolicy.Tests.ps1 @@ -0,0 +1,126 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbManagementAccessPolicy - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends aggregation_strategy and the renamed name field' { + Update-PfbManagementAccessPolicy -Name 'ops-policy' ` + -AggregationStrategy 'least-common-permissions' -NewName 'ops-policy-v2' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'management-access-policies' -and + $QueryParams['names'] -eq 'ops-policy' -and + $Body['aggregation_strategy'] -eq 'least-common-permissions' -and + $Body['name'] -eq 'ops-policy-v2' + } + } + + It 'sends an explicit -Enabled:$false (ContainsKey semantics, not truthiness)' { + Update-PfbManagementAccessPolicy -Name 'ops-policy' -Enabled $false ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('enabled') -and $Body['enabled'] -eq $false + } + } + + It 'omits enabled entirely when -Enabled is not supplied' { + Update-PfbManagementAccessPolicy -Name 'ops-policy' -NewName 'x' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $Body.ContainsKey('enabled') + } + } + + It 'sends an EMPTY array for -Rules @() so a rule list can be cleared' { + Update-PfbManagementAccessPolicy -Name 'ops-policy' -Rules @() ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('rules') -and @($Body['rules']).Count -eq 0 + } + } + + It 'sends an EMPTY string for -NewName "" rather than dropping the key' { + Update-PfbManagementAccessPolicy -Name 'ops-policy' -NewName '' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('name') -and $Body['name'] -eq '' + } + } + + It 'builds location as a single name-reference object (constraint 8a)' { + Update-PfbManagementAccessPolicy -Name 'ops-policy' -Location 'array-1' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['location'].name -eq 'array-1' + } + } + + It 'passes rules through unchanged as composite objects (constraint 8c)' { + $rules = @( + @{ role = @{ name = 'viewer' }; scope = @{ name = 'array-1'; resource_type = 'arrays' } }, + @{ role = @{ name = 'admin' }; scope = @{ name = 'realm-1'; resource_type = 'realms' } } + ) + + Update-PfbManagementAccessPolicy -Name 'ops-policy' -Rules $rules ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['rules'].Count -eq 2 -and + $Body['rules'][0].role.name -eq 'viewer' -and + $Body['rules'][1].scope.resource_type -eq 'realms' -and + -not $Body['rules'][0].ContainsKey('name') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbManagementAccessPolicy -Name 'ops-policy' -Attributes @{ enabled = $true } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['enabled'] -eq $true + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbManagementAccessPolicy -Name 'ops-policy' -Enabled $true -Attributes @{ enabled = $false } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on -AggregationStrategy (constraint 3, no spec enum)' { + $attrs = (Get-Command Update-PfbManagementAccessPolicy).Parameters['AggregationStrategy'].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes no read-only field as a parameter (constraint 11)' { + $paramNames = (Get-Command Update-PfbManagementAccessPolicy).Parameters.Keys + foreach ($readOnly in 'Context', 'IsLocal', 'PolicyType', 'Realms', 'Version') { + $paramNames | Should -Not -Contain $readOnly + } + } + } +} diff --git a/Tests/Update-PfbNetworkInterface.Tests.ps1 b/Tests/Update-PfbNetworkInterface.Tests.ps1 new file mode 100644 index 0000000..9a84576 --- /dev/null +++ b/Tests/Update-PfbNetworkInterface.Tests.ps1 @@ -0,0 +1,140 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbNetworkInterface - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'pre-existing -Address now guarded by ContainsKey and in the Individual set (constraint 16 fix)' { + It 'sends address as a body field' { + Update-PfbNetworkInterface -Name 'vir0' -Address '10.0.0.101' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'network-interfaces' -and + $QueryParams['names'] -eq 'vir0' -and + $Body['address'] -eq '10.0.0.101' + } + } + + It 'sends an EMPTY string for -Address "" rather than dropping the key' { + Update-PfbNetworkInterface -Name 'vir0' -Address '' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('address') -and $Body['address'] -eq '' + } + } + + It 'rejects -Address combined with -Attributes at bind time (real bug: was silently discarding -Address before)' { + { Update-PfbNetworkInterface -Name 'vir0' -Address '10.0.0.101' -Attributes @{ services = @('data') } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'new typed body parameters (missing body properties)' { + It 'builds attached_servers as an array of name-reference objects (constraint 8b)' { + Update-PfbNetworkInterface -Name 'vir0' -AttachedServers 'CH1.FM1', 'CH1.FM2' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['attached_servers'].Count -eq 2 -and + $Body['attached_servers'][0].name -eq 'CH1.FM1' -and + $Body['attached_servers'][1].name -eq 'CH1.FM2' + } + } + + It 'sends an EMPTY array for -AttachedServers @() so the list can be cleared' { + Update-PfbNetworkInterface -Name 'vir0' -AttachedServers @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('attached_servers') -and @($Body['attached_servers']).Count -eq 0 + } + } + + It 'sends an explicit -RdmaEnabled:$false (ContainsKey semantics, not truthiness)' { + Update-PfbNetworkInterface -Name 'vir0' -RdmaEnabled $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('rdma_enabled') -and $Body['rdma_enabled'] -eq $false + } + } + + It 'omits rdma_enabled entirely when -RdmaEnabled is not supplied' { + Update-PfbNetworkInterface -Name 'vir0' -Address '10.0.0.101' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $Body.ContainsKey('rdma_enabled') + } + } + + It 'sends services as a plain string array' { + Update-PfbNetworkInterface -Name 'vir0' -Services 'data', 'management' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + @($Body['services']) -join ',' -eq 'data,management' + } + } + + It 'sends an EMPTY array for -Services @() so the list can be cleared' { + Update-PfbNetworkInterface -Name 'vir0' -Services @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('services') -and @($Body['services']).Count -eq 0 + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbNetworkInterface -Name 'vir0' -Attributes @{ address = '10.0.0.99' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['address'] -eq '10.0.0.99' + } + } + + It 'targets the interface by id when -Id is used' { + Update-PfbNetworkInterface -Id 'iface-1' -Attributes @{ address = '10.0.0.99' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'iface-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on any new parameter (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'AttachedServers' } + @{ Parameter = 'RdmaEnabled' } + @{ Parameter = 'Services' } + ) { + $attrs = (Get-Command Update-PfbNetworkInterface).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbNetworkInterface).Parameters.Keys + foreach ($p in 'Address','AttachedServers','RdmaEnabled','Services') { + $keys | Should -Contain $p + } + } + + It 'omits every body key when no typed body parameter is supplied (constraint 19, empty body permitted)' { + Update-PfbNetworkInterface -Name 'vir0' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + } +} diff --git a/Tests/Update-PfbNetworkInterfaceConnector.Tests.ps1 b/Tests/Update-PfbNetworkInterfaceConnector.Tests.ps1 new file mode 100644 index 0000000..e41691e --- /dev/null +++ b/Tests/Update-PfbNetworkInterfaceConnector.Tests.ps1 @@ -0,0 +1,133 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbNetworkInterfaceConnector - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends lane_speed and port_speed as body fields' { + Update-PfbNetworkInterfaceConnector -Name 'CH1.FM1.ETH1' -LaneSpeed 10000000000 -PortSpeed 40000000000 ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'network-interfaces/connectors' -and + $QueryParams['names'] -eq 'CH1.FM1.ETH1' -and + $Body['lane_speed'] -eq 10000000000 -and + $Body['port_speed'] -eq 40000000000 + } + } + + It 'sends lanes_per_port and port_count as body fields' { + Update-PfbNetworkInterfaceConnector -Name 'CH1.FM1.ETH1' -LanesPerPort 4 -PortCount 1 ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['lanes_per_port'] -eq 4 -and $Body['port_count'] -eq 1 + } + } + + It 'sends an explicit -LaneSpeed 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbNetworkInterfaceConnector -Name 'CH1.FM1.ETH1' -LaneSpeed 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('lane_speed') -and $Body['lane_speed'] -eq 0 + } + } + + It 'sends an explicit -PortCount 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbNetworkInterfaceConnector -Name 'CH1.FM1.ETH1' -PortCount 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('port_count') -and $Body['port_count'] -eq 0 + } + } + + It 'sends an explicit -LanesPerPort 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbNetworkInterfaceConnector -Name 'CH1.FM1.ETH1' -LanesPerPort 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('lanes_per_port') -and $Body['lanes_per_port'] -eq 0 + } + } + + It 'sends an explicit -PortSpeed 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbNetworkInterfaceConnector -Name 'CH1.FM1.ETH1' -PortSpeed 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('port_speed') -and $Body['port_speed'] -eq 0 + } + } + + It 'omits lane_speed entirely when -LaneSpeed is not supplied' { + Update-PfbNetworkInterfaceConnector -Name 'CH1.FM1.ETH1' -PortSpeed 1 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $Body.ContainsKey('lane_speed') + } + } + + It 'omits every body key when no typed body parameter is supplied (constraint 19, empty body permitted)' { + Update-PfbNetworkInterfaceConnector -Name 'CH1.FM1.ETH1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the connector by id when -Id is used' { + Update-PfbNetworkInterfaceConnector -Id 'conn-1' -LaneSpeed 10000000000 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'conn-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbNetworkInterfaceConnector -Name 'CH1.FM1.ETH1' -Attributes @{ port_speed = 40000000000 } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['port_speed'] -eq 40000000000 + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbNetworkInterfaceConnector -Name 'CH1.FM1.ETH1' -LaneSpeed 1 -Attributes @{ lane_speed = 2 } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on any new parameter (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'LaneSpeed' } + @{ Parameter = 'LanesPerPort' } + @{ Parameter = 'PortCount' } + @{ Parameter = 'PortSpeed' } + ) { + $attrs = (Get-Command Update-PfbNetworkInterfaceConnector).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbNetworkInterfaceConnector).Parameters.Keys + foreach ($p in 'LaneSpeed','LanesPerPort','PortCount','PortSpeed') { + $keys | Should -Contain $p + } + } + } +} diff --git a/Tests/Update-PfbNode.Tests.ps1 b/Tests/Update-PfbNode.Tests.ps1 new file mode 100644 index 0000000..f925c77 --- /dev/null +++ b/Tests/Update-PfbNode.Tests.ps1 @@ -0,0 +1,107 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbNode - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends management_address and serial_number as body fields' { + Update-PfbNode -Name 'CH1.FB1' -ManagementAddress '10.0.0.5' -SerialNumber 'SN12345' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'nodes' -and + $QueryParams['names'] -eq 'CH1.FB1' -and + $Body['management_address'] -eq '10.0.0.5' -and + $Body['serial_number'] -eq 'SN12345' + } + } + + It 'sends node_key as a body field' { + Update-PfbNode -Name 'CH1.FB1' -NodeKey 'bootstrap-key' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['node_key'] -eq 'bootstrap-key' + } + } + + It 'sends -NewName as the name body field (exception: name -> -NewName)' { + Update-PfbNode -Name 'CH1.FB1' -NewName 'CH1.FB1-renamed' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['name'] -eq 'CH1.FB1-renamed' + } + } + + It 'sends an EMPTY string for -ManagementAddress "" rather than dropping the key' { + Update-PfbNode -Name 'CH1.FB1' -ManagementAddress '' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('management_address') -and $Body['management_address'] -eq '' + } + } + + It 'omits every body key when no typed body parameter is supplied (constraint 19, empty body permitted)' { + Update-PfbNode -Name 'CH1.FB1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the node by id when -Id is used' { + Update-PfbNode -Id 'node-1' -NodeKey 'k' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'node-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbNode -Name 'CH1.FB1' -Attributes @{ identify_enabled = $true } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['identify_enabled'] -eq $true + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbNode -Name 'CH1.FB1' -NodeKey 'k' -Attributes @{ node_key = 'other' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on any new parameter (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'ManagementAddress' } + @{ Parameter = 'NewName' } + @{ Parameter = 'NodeKey' } + @{ Parameter = 'SerialNumber' } + ) { + $attrs = (Get-Command Update-PfbNode).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbNode).Parameters.Keys + foreach ($p in 'ManagementAddress','NewName','NodeKey','SerialNumber') { + $keys | Should -Contain $p + } + } + } +} diff --git a/Tests/Update-PfbNodeGroup.Tests.ps1 b/Tests/Update-PfbNodeGroup.Tests.ps1 new file mode 100644 index 0000000..291fe6e --- /dev/null +++ b/Tests/Update-PfbNodeGroup.Tests.ps1 @@ -0,0 +1,81 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbNodeGroup - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends -NewName as the name body field (exception: name -> -NewName)' { + Update-PfbNodeGroup -Name 'analytics-group' -NewName 'analytics-primary' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'node-groups' -and + $QueryParams['names'] -eq 'analytics-group' -and + $Body['name'] -eq 'analytics-primary' + } + } + + It 'sends an EMPTY string for -NewName "" rather than dropping the key' { + Update-PfbNodeGroup -Name 'analytics-group' -NewName '' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('name') -and $Body['name'] -eq '' + } + } + + It 'omits every body key when no typed body parameter is supplied (constraint 19, empty body permitted)' { + Update-PfbNodeGroup -Name 'analytics-group' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the node group by id when -Id is used' { + Update-PfbNodeGroup -Id 'group-1' -NewName 'renamed' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'group-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbNodeGroup -Name 'analytics-group' -Attributes @{ name = 'raw-rename' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['name'] -eq 'raw-rename' + } + } + + It 'rejects -Attributes combined with -NewName at bind time' { + { Update-PfbNodeGroup -Name 'analytics-group' -NewName 'x' -Attributes @{ name = 'y' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on -NewName (constraint 3, no spec enum)' { + $attrs = (Get-Command Update-PfbNodeGroup).Parameters['NewName'].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes the settable body field the endpoint accepts' { + (Get-Command Update-PfbNodeGroup).Parameters.Keys | Should -Contain 'NewName' + } + } +} diff --git a/Tests/Update-PfbObjectStoreAccountExport.Tests.ps1 b/Tests/Update-PfbObjectStoreAccountExport.Tests.ps1 new file mode 100644 index 0000000..f8f7e0f --- /dev/null +++ b/Tests/Update-PfbObjectStoreAccountExport.Tests.ps1 @@ -0,0 +1,100 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbObjectStoreAccountExport - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends an explicit -ExportEnabled:$false (ContainsKey semantics, not truthiness)' { + Update-PfbObjectStoreAccountExport -Name 'export-1' -ExportEnabled:$false ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'object-store-account-exports' -and + $QueryParams['names'] -eq 'export-1' -and + $Body.ContainsKey('export_enabled') -and $Body['export_enabled'] -eq $false + } + } + + It 'omits export_enabled entirely when -ExportEnabled is not supplied' { + Update-PfbObjectStoreAccountExport -Name 'export-1' -Policy 'export-policy' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $Body.ContainsKey('export_enabled') + } + } + + It 'builds policy as a name-reference object (constraint 8a, scalar reference)' { + Update-PfbObjectStoreAccountExport -Name 'export-1' -Policy 'export-policy' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['policy'].name -eq 'export-policy' + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbObjectStoreAccountExport -Name 'export-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the account export by id when -Id is used' { + Update-PfbObjectStoreAccountExport -Id 'export-id-1' -ExportEnabled:$true ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'export-id-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbObjectStoreAccountExport -Name 'export-1' -Attributes @{ export_enabled = $false } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['export_enabled'] -eq $false + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbObjectStoreAccountExport -Name 'export-1' -ExportEnabled:$false -Attributes @{ export_enabled = $true } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'ExportEnabled' } + @{ Parameter = 'Policy' } + ) { + $attrs = (Get-Command Update-PfbObjectStoreAccountExport).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbObjectStoreAccountExport).Parameters.Keys + foreach ($p in 'ExportEnabled','Policy') { + $keys | Should -Contain $p + } + } + } +} diff --git a/Tests/Update-PfbObjectStoreRemoteCredential.Tests.ps1 b/Tests/Update-PfbObjectStoreRemoteCredential.Tests.ps1 new file mode 100644 index 0000000..ca1abe8 --- /dev/null +++ b/Tests/Update-PfbObjectStoreRemoteCredential.Tests.ps1 @@ -0,0 +1,138 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbObjectStoreRemoteCredential - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends access_key_id and secret_access_key as body fields' { + Update-PfbObjectStoreRemoteCredential -Name 's3-repl-cred' -AccessKeyId 'AKIAIOSFODNN7EXAMPLE' ` + -SecretAccessKey 'newSecretKeyValue12345EXAMPLEKEY' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'object-store-remote-credentials' -and + $QueryParams['names'] -eq 's3-repl-cred' -and + $Body['access_key_id'] -eq 'AKIAIOSFODNN7EXAMPLE' -and + $Body['secret_access_key'] -eq 'newSecretKeyValue12345EXAMPLEKEY' + } + } + + It 'sends an EMPTY string for -AccessKeyId "" rather than dropping the key' { + Update-PfbObjectStoreRemoteCredential -Name 's3-repl-cred' -AccessKeyId '' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('access_key_id') -and $Body['access_key_id'] -eq '' + } + } + + It 'sends -NewName as the name body field (rename exception, not -RemoteCredentialName)' { + Update-PfbObjectStoreRemoteCredential -Name 's3-repl-cred' -NewName 'renamed-cred' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['name'] -eq 'renamed-cred' + } + } + + It 'builds remote as a name-reference object (constraint 8a, scalar reference)' { + Update-PfbObjectStoreRemoteCredential -Name 's3-repl-cred' -Remote 'target-1' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['remote'].name -eq 'target-1' + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbObjectStoreRemoteCredential -Name 's3-repl-cred' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the remote credential by id when -Id is used' { + Update-PfbObjectStoreRemoteCredential -Id 'cred-id-1' -AccessKeyId 'x' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'cred-id-1' -and -not $QueryParams.ContainsKey('names') + } + } + + It 'has no -SecretAccessKey default value (sensitive field, AST-based check)' { + # A defaulted [string]$SecretAccessKey = 'leaked' does NOT produce a + # PSDefaultValueAttribute -- .Attributes only ever holds ParameterAttribute / + # ArgumentTypeConverterAttribute. The default value only shows up in the AST, so + # that is what must be inspected to actually prove no default exists. + $paramBlock = (Get-Command Update-PfbObjectStoreRemoteCredential).ScriptBlock.Ast.Body.ParamBlock + $secretParam = $paramBlock.Parameters | Where-Object { $_.Name.VariablePath.UserPath -eq 'SecretAccessKey' } + $secretParam | Should -Not -BeNullOrEmpty + $secretParam.DefaultValue | Should -BeNullOrEmpty + } + + It 'never leaks the secret into Verbose or Debug output' { + $secret = 'S3cr3tValueMustNotLeak-9f8e7d' + $captured = Update-PfbObjectStoreRemoteCredential -Name 's3-repl-cred' -SecretAccessKey $secret ` + -Confirm:$false -Array $fakeArray -Verbose -Debug 4>&1 5>&1 + + ($captured | Out-String) | Should -Not -Match ([regex]::Escape($secret)) + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbObjectStoreRemoteCredential -Name 's3-repl-cred' -Attributes @{ secret_access_key = 'raw' } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['secret_access_key'] -eq 'raw' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbObjectStoreRemoteCredential -Name 's3-repl-cred' -AccessKeyId 'x' -Attributes @{ access_key_id = 'y' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'AccessKeyId' } + @{ Parameter = 'NewName' } + @{ Parameter = 'Remote' } + @{ Parameter = 'SecretAccessKey' } + ) { + $attrs = (Get-Command Update-PfbObjectStoreRemoteCredential).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbObjectStoreRemoteCredential).Parameters.Keys + foreach ($p in 'AccessKeyId','NewName','Remote','SecretAccessKey') { + $keys | Should -Contain $p + } + } + + It 'has no read-only field parameters (constraint 11: context, id, realms)' { + $keys = (Get-Command Update-PfbObjectStoreRemoteCredential).Parameters.Keys + foreach ($p in 'Context','Realms') { + $keys | Should -Not -Contain $p + } + } + } +} diff --git a/Tests/Update-PfbObjectStoreRole.Tests.ps1 b/Tests/Update-PfbObjectStoreRole.Tests.ps1 new file mode 100644 index 0000000..2934b7b --- /dev/null +++ b/Tests/Update-PfbObjectStoreRole.Tests.ps1 @@ -0,0 +1,118 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbObjectStoreRole - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'builds account as a name-reference object (constraint 8a, scalar reference)' { + Update-PfbObjectStoreRole -Name 's3-admin-role' -Account 'obj-account-1' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'object-store-roles' -and + $QueryParams['names'] -eq 's3-admin-role' -and + $Body['account'].name -eq 'obj-account-1' + } + } + + It 'sends an explicit -MaxSessionDuration 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbObjectStoreRole -Name 's3-admin-role' -MaxSessionDuration 0 ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('max_session_duration') -and $Body['max_session_duration'] -eq 0 + } + } + + It 'sends max_session_duration when supplied a non-zero value' { + Update-PfbObjectStoreRole -Name 's3-admin-role' -MaxSessionDuration 3600000 ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['max_session_duration'] -eq 3600000 + } + } + + It 'omits max_session_duration entirely when not supplied' { + Update-PfbObjectStoreRole -Name 's3-admin-role' -Account 'obj-account-1' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $Body.ContainsKey('max_session_duration') + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbObjectStoreRole -Name 's3-admin-role' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the role by id when -Id is used' { + Update-PfbObjectStoreRole -Id 'role-id-1' -MaxSessionDuration 3600000 ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'role-id-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbObjectStoreRole -Name 's3-admin-role' -Attributes @{ max_session_duration = 7200000 } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['max_session_duration'] -eq 7200000 + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbObjectStoreRole -Name 's3-admin-role' -MaxSessionDuration 3600000 -Attributes @{ max_session_duration = 7200000 } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'deprecated / read-only fields are not exposed (constraint 9 and 11)' { + It 'has no -Created/-Prn/-TrustedEntities parameter' { + $keys = (Get-Command Update-PfbObjectStoreRole).Parameters.Keys + foreach ($p in 'Created', 'Prn', 'TrustedEntities') { + $keys | Should -Not -Contain $p + } + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'Account' } + @{ Parameter = 'MaxSessionDuration' } + ) { + $attrs = (Get-Command Update-PfbObjectStoreRole).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbObjectStoreRole).Parameters.Keys + foreach ($p in 'Account','MaxSessionDuration') { + $keys | Should -Contain $p + } + } + } +} diff --git a/Tests/Update-PfbObjectStoreVirtualHost.Tests.ps1 b/Tests/Update-PfbObjectStoreVirtualHost.Tests.ps1 new file mode 100644 index 0000000..f1596ad --- /dev/null +++ b/Tests/Update-PfbObjectStoreVirtualHost.Tests.ps1 @@ -0,0 +1,155 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbObjectStoreVirtualHost - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends hostname and -NewName as body fields (rename exception, not -VirtualHostName)' { + Update-PfbObjectStoreVirtualHost -Name 's3.example.com' -Hostname 's3.myarray.com' -NewName 'renamed-vhost' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'object-store-virtual-hosts' -and + $QueryParams['names'] -eq 's3.example.com' -and + $Body['hostname'] -eq 's3.myarray.com' -and + $Body['name'] -eq 'renamed-vhost' + } + } + + It 'builds attached_servers as name-reference objects (constraint 8b, array of references)' { + Update-PfbObjectStoreVirtualHost -Name 's3.example.com' -AttachedServers 'srv-a','srv-b' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['attached_servers'].Count -eq 2 -and + $Body['attached_servers'][0].name -eq 'srv-a' -and + $Body['attached_servers'][1].name -eq 'srv-b' + } + } + + It 'sends an EMPTY array for -AttachedServers @() so a list can be cleared' { + Update-PfbObjectStoreVirtualHost -Name 's3.example.com' -AttachedServers @() ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('attached_servers') -and + @($Body['attached_servers']).Count -eq 0 + } + } + + It 'builds add_attached_servers as name-reference objects' { + Update-PfbObjectStoreVirtualHost -Name 's3.example.com' -AddAttachedServers 'srv-c' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['add_attached_servers'][0].name -eq 'srv-c' + } + } + + It 'sends an EMPTY array for -AddAttachedServers @() so a list can be cleared' { + Update-PfbObjectStoreVirtualHost -Name 's3.example.com' -AddAttachedServers @() ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('add_attached_servers') -and + @($Body['add_attached_servers']).Count -eq 0 + } + } + + It 'builds remove_attached_servers as name-reference objects' { + Update-PfbObjectStoreVirtualHost -Name 's3.example.com' -RemoveAttachedServers 'srv-d' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['remove_attached_servers'][0].name -eq 'srv-d' + } + } + + It 'sends an EMPTY array for -RemoveAttachedServers @() so a list can be cleared' { + Update-PfbObjectStoreVirtualHost -Name 's3.example.com' -RemoveAttachedServers @() ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('remove_attached_servers') -and + @($Body['remove_attached_servers']).Count -eq 0 + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbObjectStoreVirtualHost -Name 's3.example.com' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the virtual host by id when -Id is used' { + Update-PfbObjectStoreVirtualHost -Id 'vhost-id-1' -Hostname 's3.myarray.com' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'vhost-id-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbObjectStoreVirtualHost -Name 's3.example.com' -Attributes @{ hostname = 'raw.example.com' } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['hostname'] -eq 'raw.example.com' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbObjectStoreVirtualHost -Name 's3.example.com' -Hostname 'x.example.com' -Attributes @{ hostname = 'y.example.com' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'AddAttachedServers' } + @{ Parameter = 'AttachedServers' } + @{ Parameter = 'Hostname' } + @{ Parameter = 'NewName' } + @{ Parameter = 'RemoveAttachedServers' } + ) { + $attrs = (Get-Command Update-PfbObjectStoreVirtualHost).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbObjectStoreVirtualHost).Parameters.Keys + foreach ($p in 'AddAttachedServers','AttachedServers','Hostname','NewName','RemoveAttachedServers') { + $keys | Should -Contain $p + } + } + + It 'exposes no body parameter for the read-only id field (constraint 11) beyond the existing -Id selector' { + # `id` is read-only per the field reference; PascalCase would collide with the + # pre-existing -Id selector, so the only way this field could leak in is via that + # selector feeding the body. Assert the selector's ParameterSetName is confined to + # the ById* sets and never appears bound to a body-only set. + $idParam = (Get-Command Update-PfbObjectStoreVirtualHost).Parameters['Id'] + $idParam.ParameterSets.Keys | Should -Not -Contain 'ByNameIndividual' + $idParam.ParameterSets.Keys | Should -Not -Contain 'ByNameAttributes' + } + } +} diff --git a/Tests/Update-PfbOidcIdp.Tests.ps1 b/Tests/Update-PfbOidcIdp.Tests.ps1 new file mode 100644 index 0000000..b72b41c --- /dev/null +++ b/Tests/Update-PfbOidcIdp.Tests.ps1 @@ -0,0 +1,103 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbOidcIdp - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends the renamed name field and services array' { + Update-PfbOidcIdp -Name 'okta-prod' -NewName 'okta-prod-v2' -Services 'object' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'sso/oidc/idps' -and + $QueryParams['names'] -eq 'okta-prod' -and + $Body['name'] -eq 'okta-prod-v2' -and + $Body['services'].Count -eq 1 -and $Body['services'][0] -eq 'object' + } + } + + It 'sends an explicit -Enabled:$false (ContainsKey semantics, not truthiness)' { + Update-PfbOidcIdp -Name 'okta-prod' -Enabled $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('enabled') -and $Body['enabled'] -eq $false + } + } + + It 'omits enabled entirely when -Enabled is not supplied' { + Update-PfbOidcIdp -Name 'okta-prod' -NewName 'x' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $Body.ContainsKey('enabled') + } + } + + It 'sends an EMPTY array for -Services @() so the list can be cleared' { + Update-PfbOidcIdp -Name 'okta-prod' -Services @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('services') -and @($Body['services']).Count -eq 0 + } + } + + It 'sends an EMPTY string for -NewName "" rather than dropping the key' { + Update-PfbOidcIdp -Name 'okta-prod' -NewName '' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('name') -and $Body['name'] -eq '' + } + } + + It 'passes idp through as a composite sub-object, NOT a name reference (constraint 8c)' { + Update-PfbOidcIdp -Name 'okta-prod' ` + -Idp @{ provider_url = 'https://idp.example.com' } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['idp'].provider_url -eq 'https://idp.example.com' -and + -not $Body['idp'].ContainsKey('name') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbOidcIdp -Name 'okta-prod' -Attributes @{ enabled = $false } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['enabled'] -eq $false + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbOidcIdp -Name 'okta-prod' -Enabled $true -Attributes @{ enabled = $false } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on -Services (constraint 3, no spec enum)' { + $attrs = (Get-Command Update-PfbOidcIdp).Parameters['Services'].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'does not expose the read-only prn field (constraint 11)' { + (Get-Command Update-PfbOidcIdp).Parameters.Keys | Should -Not -Contain 'Prn' + } + } +} diff --git a/Tests/Update-PfbQosPolicy.Tests.ps1 b/Tests/Update-PfbQosPolicy.Tests.ps1 new file mode 100644 index 0000000..2b1fad1 --- /dev/null +++ b/Tests/Update-PfbQosPolicy.Tests.ps1 @@ -0,0 +1,138 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbQosPolicy - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends enabled as a body field (ContainsKey semantics, not truthiness)' { + Update-PfbQosPolicy -Name 'qos-gold' -Enabled $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'qos-policies' -and + $QueryParams['names'] -eq 'qos-gold' -and + $Body.ContainsKey('enabled') -and $Body['enabled'] -eq $false + } + } + + It 'builds location as a name-reference object' { + Update-PfbQosPolicy -Name 'qos-gold' -Location 'array-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['location'].name -eq 'array-1' + } + } + + It 'sends max_total_bytes_per_sec as a body field' { + Update-PfbQosPolicy -Name 'qos-gold' -MaxTotalBytesPerSec 2147483648 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['max_total_bytes_per_sec'] -eq 2147483648 + } + } + + It 'sends an explicit -MaxTotalBytesPerSec 0 rather than dropping it (constraint 2: 0 means unlimited)' { + Update-PfbQosPolicy -Name 'qos-gold' -MaxTotalBytesPerSec 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('max_total_bytes_per_sec') -and $Body['max_total_bytes_per_sec'] -eq 0 + } + } + + It 'sends an explicit -MaxTotalOpsPerSec 0 rather than dropping it (constraint 2: 0 means unlimited)' { + Update-PfbQosPolicy -Name 'qos-gold' -MaxTotalOpsPerSec 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('max_total_ops_per_sec') -and $Body['max_total_ops_per_sec'] -eq 0 + } + } + + It 'omits max_total_ops_per_sec entirely when not supplied' { + Update-PfbQosPolicy -Name 'qos-gold' -Enabled $true -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $Body.ContainsKey('max_total_ops_per_sec') + } + } + + It 'sends -NewName as the name body field (rename exception, not -QosPolicyName)' { + Update-PfbQosPolicy -Name 'qos-gold' -NewName 'qos-platinum' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['name'] -eq 'qos-platinum' + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbQosPolicy -Name 'qos-gold' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the policy by id when -Id is used' { + Update-PfbQosPolicy -Id 'policy-1' -Enabled $true -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'policy-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbQosPolicy -Name 'qos-gold' -Attributes @{ enabled = $true } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['enabled'] -eq $true + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbQosPolicy -Name 'qos-gold' -Enabled $true -Attributes @{ enabled = $false } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'read-only fields are never exposed (constraint 11)' { + It 'has no -IsLocal or -PolicyType parameter' { + $keys = (Get-Command Update-PfbQosPolicy).Parameters.Keys + $keys | Should -Not -Contain 'IsLocal' + $keys | Should -Not -Contain 'PolicyType' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'Enabled' } + @{ Parameter = 'Location' } + @{ Parameter = 'MaxTotalBytesPerSec' } + @{ Parameter = 'MaxTotalOpsPerSec' } + @{ Parameter = 'NewName' } + ) { + $attrs = (Get-Command Update-PfbQosPolicy).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbQosPolicy).Parameters.Keys + foreach ($p in 'Enabled','Location','MaxTotalBytesPerSec','MaxTotalOpsPerSec','NewName') { + $keys | Should -Contain $p + } + } + } +} diff --git a/Tests/Update-PfbRealmDefaults.Tests.ps1 b/Tests/Update-PfbRealmDefaults.Tests.ps1 new file mode 100644 index 0000000..9c5cda0 --- /dev/null +++ b/Tests/Update-PfbRealmDefaults.Tests.ps1 @@ -0,0 +1,102 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbRealmDefaults - typed body parameters + query wire-key fix (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'query wire-key bug fix: PATCH /realms/defaults only accepts realm_ids/realm_names' { + It 'sends -Name as realm_names, NOT names' { + Update-PfbRealmDefaults -Name 'realm-prod' -Attributes @{} -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'realms/defaults' -and + $QueryParams['realm_names'] -eq 'realm-prod' -and + -not $QueryParams.ContainsKey('names') + } + } + + It 'sends -Id as realm_ids, NOT ids' { + Update-PfbRealmDefaults -Id 'realm-1' -Attributes @{} -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['realm_ids'] -eq 'realm-1' -and -not $QueryParams.ContainsKey('ids') + } + } + } + + Context 'typed parameters build the body' { + It 'sends object_store as a composite array, passed straight through' { + Update-PfbRealmDefaults -Name 'realm-prod' -ObjectStore @{ server = @{ name = 'obj-server-1' } } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + @($Body['object_store']).Count -eq 1 -and + $Body['object_store'][0].server.name -eq 'obj-server-1' + } + } + + It 'sends an EMPTY array for -ObjectStore @() so the list can be cleared' { + Update-PfbRealmDefaults -Name 'realm-prod' -ObjectStore @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('object_store') -and @($Body['object_store']).Count -eq 0 + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbRealmDefaults -Name 'realm-prod' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbRealmDefaults -Name 'realm-prod' -Attributes @{ object_store = @(@{ server = @{ name = 'raw' } }) } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['object_store'][0].server.name -eq 'raw' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbRealmDefaults -Name 'realm-prod' -ObjectStore @() -Attributes @{} ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'read-only fields are not exposed (constraint 11)' { + It 'has no -Context or -Realm parameter' { + $keys = (Get-Command Update-PfbRealmDefaults).Parameters.Keys + $keys | Should -Not -Contain 'Context' + $keys | Should -Not -Contain 'Realm' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on -ObjectStore (constraint 3, no spec enum)' { + $attrs = (Get-Command Update-PfbRealmDefaults).Parameters['ObjectStore'].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes -ObjectStore as [hashtable[]] (constraint 8c, composite)' { + (Get-Command Update-PfbRealmDefaults).Parameters['ObjectStore'].ParameterType.Name | Should -Be 'Hashtable[]' + } + } +} diff --git a/Tests/Update-PfbSaml2Idp.Tests.ps1 b/Tests/Update-PfbSaml2Idp.Tests.ps1 new file mode 100644 index 0000000..fbbc917 --- /dev/null +++ b/Tests/Update-PfbSaml2Idp.Tests.ps1 @@ -0,0 +1,122 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbSaml2Idp - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends array_url, binding, the renamed name field and services' { + Update-PfbSaml2Idp -Name 'adfs-prod' ` + -ArrayUrl 'https://fb.example.test' -Binding 'http-redirect' ` + -NewName 'adfs-prod-v2' -Services 'management','object' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'sso/saml2/idps' -and + $QueryParams['names'] -eq 'adfs-prod' -and + $Body['array_url'] -eq 'https://fb.example.test' -and + $Body['binding'] -eq 'http-redirect' -and + $Body['name'] -eq 'adfs-prod-v2' -and + $Body['services'].Count -eq 2 + } + } + + It 'sends an explicit -Enabled:$false (ContainsKey semantics, not truthiness)' { + Update-PfbSaml2Idp -Name 'adfs-prod' -Enabled $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('enabled') -and $Body['enabled'] -eq $false + } + } + + It 'omits enabled entirely when -Enabled is not supplied' { + Update-PfbSaml2Idp -Name 'adfs-prod' -ArrayUrl 'https://x' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + -not $Body.ContainsKey('enabled') + } + } + + It 'sends an EMPTY array for -Services @() so the list can be cleared' { + Update-PfbSaml2Idp -Name 'adfs-prod' -Services @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('services') -and @($Body['services']).Count -eq 0 + } + } + + It 'sends an EMPTY string for -ArrayUrl "" rather than dropping the key' { + Update-PfbSaml2Idp -Name 'adfs-prod' -ArrayUrl '' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('array_url') -and $Body['array_url'] -eq '' + } + } + + It 'passes idp, sp and management through as composite sub-objects (constraint 8c)' { + Update-PfbSaml2Idp -Name 'adfs-prod' ` + -Idp @{ entity_id = 'urn:idp'; metadata_url = 'https://idp/meta' } ` + -Sp @{ entity_id = 'urn:sp' } ` + -Management @{ trust_other_saml_sps_in_fleet = $true } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['idp'].entity_id -eq 'urn:idp' -and + $Body['idp'].metadata_url -eq 'https://idp/meta' -and + -not $Body['idp'].ContainsKey('name') -and + $Body['sp'].entity_id -eq 'urn:sp' -and + $Body['management'].trust_other_saml_sps_in_fleet -eq $true + } + } + + It 'targets the IdP by id when -Id is used' { + Update-PfbSaml2Idp -Id 'idp-1' -Enabled $true -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'idp-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbSaml2Idp -Name 'adfs-prod' -Attributes @{ enabled = $false } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['enabled'] -eq $false + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbSaml2Idp -Name 'adfs-prod' -Enabled $true -Attributes @{ enabled = $false } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on -Binding or -Services (constraint 3, no spec enum)' { + foreach ($p in 'Binding', 'Services') { + $attrs = (Get-Command Update-PfbSaml2Idp).Parameters[$p].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + } + + It 'exposes no read-only field as a parameter (constraint 11)' { + (Get-Command Update-PfbSaml2Idp).Parameters.Keys | Should -Not -Contain 'Prn' + } + } +} diff --git a/Tests/Update-PfbSnmpManager.Tests.ps1 b/Tests/Update-PfbSnmpManager.Tests.ps1 new file mode 100644 index 0000000..65ac0fe --- /dev/null +++ b/Tests/Update-PfbSnmpManager.Tests.ps1 @@ -0,0 +1,145 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbSnmpManager - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends -SnmpHost as the host body field (naming collision with -Host)' { + Update-PfbSnmpManager -Name 'snmp-mgr01' -SnmpHost '10.21.100.55' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'snmp-managers' -and + $QueryParams['names'] -eq 'snmp-mgr01' -and + $Body['host'] -eq '10.21.100.55' + } + } + + It 'has no -Host parameter (would collide with the PowerShell common parameter)' { + (Get-Command Update-PfbSnmpManager).Parameters.Keys | Should -Not -Contain 'Host' + } + + It 'sends name as a body field via -NewName' { + Update-PfbSnmpManager -Name 'snmp-mgr01' -NewName 'snmp-mgr02' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['name'] -eq 'snmp-mgr02' + } + } + + It 'sends notification as a body field' { + Update-PfbSnmpManager -Name 'snmp-mgr01' -Notification 'trap' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['notification'] -eq 'trap' + } + } + + It 'rejects a -Notification value outside the enum' { + { Update-PfbSnmpManager -Name 'snmp-mgr01' -Notification 'bogus' -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ErrorId 'ParameterArgumentValidationError,Update-PfbSnmpManager' + } + + It 'sends version as a body field' { + Update-PfbSnmpManager -Name 'snmp-mgr01' -Version 'v3' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['version'] -eq 'v3' + } + } + + It 'rejects a -Version value outside the enum' { + { Update-PfbSnmpManager -Name 'snmp-mgr01' -Version 'v1' -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ErrorId 'ParameterArgumentValidationError,Update-PfbSnmpManager' + } + + It 'passes v2c through as a composite hashtable (constraint 8c)' { + Update-PfbSnmpManager -Name 'snmp-mgr01' -V2c @{ community = 'new-community' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['v2c']['community'] -eq 'new-community' + } + } + + It 'passes v3 through as a composite hashtable (constraint 8c)' { + Update-PfbSnmpManager -Name 'snmp-mgr01' -V3 @{ auth_protocol = 'SHA' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['v3']['auth_protocol'] -eq 'SHA' + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbSnmpManager -Name 'snmp-mgr01' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the manager by id when -Id is used' { + Update-PfbSnmpManager -Id 'mgr-1' -NewName 'x' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'mgr-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbSnmpManager -Name 'snmp-mgr01' -Attributes @{ host = '10.21.100.55' } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['host'] -eq '10.21.100.55' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbSnmpManager -Name 'snmp-mgr01' -SnmpHost '1.2.3.4' -Attributes @{ host = '5.6.7.8' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'SnmpHost' } + @{ Parameter = 'NewName' } + @{ Parameter = 'V2c' } + @{ Parameter = 'V3' } + ) { + $attrs = (Get-Command Update-PfbSnmpManager).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'puts ValidateSet(inform, trap) on -Notification in that order (constraint 3)' { + $attr = (Get-Command Update-PfbSnmpManager).Parameters['Notification'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } + $attr.ValidValues | Should -Be @('inform', 'trap') + } + + It 'puts ValidateSet(v2c, v3) on -Version in that order (constraint 3)' { + $attr = (Get-Command Update-PfbSnmpManager).Parameters['Version'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } + $attr.ValidValues | Should -Be @('v2c', 'v3') + } + + It 'has no -SnmpManagerName parameter (the "name" body field uses -NewName per the exception)' { + (Get-Command Update-PfbSnmpManager).Parameters.Keys | Should -Not -Contain 'SnmpManagerName' + } + } +} diff --git a/Tests/Update-PfbSshCaPolicy.Tests.ps1 b/Tests/Update-PfbSshCaPolicy.Tests.ps1 new file mode 100644 index 0000000..4d4b8b4 --- /dev/null +++ b/Tests/Update-PfbSshCaPolicy.Tests.ps1 @@ -0,0 +1,133 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbSshCaPolicy - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends enabled as a body field (ContainsKey semantics, not truthiness)' { + Update-PfbSshCaPolicy -Name 'ssh-ca-prod' -Enabled $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'ssh-certificate-authority-policies' -and + $QueryParams['names'] -eq 'ssh-ca-prod' -and + $Body.ContainsKey('enabled') -and $Body['enabled'] -eq $false + } + } + + It 'builds location as a name-reference object' { + Update-PfbSshCaPolicy -Name 'ssh-ca-prod' -Location 'array-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['location'].name -eq 'array-1' + } + } + + It 'sends -NewName as the name body field (rename exception, not -SshCaPolicyName)' { + Update-PfbSshCaPolicy -Name 'ssh-ca-prod' -NewName 'ssh-ca-prod-2' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['name'] -eq 'ssh-ca-prod-2' + } + } + + It 'builds signing_authority as a name-reference object' { + Update-PfbSshCaPolicy -Name 'ssh-ca-prod' -SigningAuthority 'ca-cert-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['signing_authority'].name -eq 'ca-cert-1' + } + } + + It 'sends static_authorized_principals as a plain string array' { + Update-PfbSshCaPolicy -Name 'ssh-ca-prod' -StaticAuthorizedPrincipals 'alice','bob' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + @($Body['static_authorized_principals']).Count -eq 2 -and + $Body['static_authorized_principals'][0] -eq 'alice' -and + $Body['static_authorized_principals'][1] -eq 'bob' + } + } + + It 'sends an EMPTY array for -StaticAuthorizedPrincipals @() so a list can be cleared' { + Update-PfbSshCaPolicy -Name 'ssh-ca-prod' -StaticAuthorizedPrincipals @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('static_authorized_principals') -and + @($Body['static_authorized_principals']).Count -eq 0 + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbSshCaPolicy -Name 'ssh-ca-prod' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the policy by id when -Id is used' { + Update-PfbSshCaPolicy -Id 'policy-1' -Enabled $true -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'policy-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbSshCaPolicy -Name 'ssh-ca-prod' -Attributes @{ enabled = $true } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['enabled'] -eq $true + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbSshCaPolicy -Name 'ssh-ca-prod' -Enabled $true -Attributes @{ enabled = $false } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'read-only fields are never exposed (constraint 11)' { + It 'has no -IsLocal or -PolicyType parameter' { + $keys = (Get-Command Update-PfbSshCaPolicy).Parameters.Keys + $keys | Should -Not -Contain 'IsLocal' + $keys | Should -Not -Contain 'PolicyType' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'Enabled' } + @{ Parameter = 'Location' } + @{ Parameter = 'NewName' } + @{ Parameter = 'SigningAuthority' } + @{ Parameter = 'StaticAuthorizedPrincipals' } + ) { + $attrs = (Get-Command Update-PfbSshCaPolicy).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbSshCaPolicy).Parameters.Keys + foreach ($p in 'Enabled','Location','NewName','SigningAuthority','StaticAuthorizedPrincipals') { + $keys | Should -Contain $p + } + } + } +} diff --git a/Tests/Update-PfbStorageClassTieringPolicy.Tests.ps1 b/Tests/Update-PfbStorageClassTieringPolicy.Tests.ps1 new file mode 100644 index 0000000..ee7a891 --- /dev/null +++ b/Tests/Update-PfbStorageClassTieringPolicy.Tests.ps1 @@ -0,0 +1,142 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbStorageClassTieringPolicy - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends archival_rules as a composite hashtable array, passed straight through' { + $rules = @(@{ after = 86400000 }) + Update-PfbStorageClassTieringPolicy -Name 'tier-to-archive' -ArchivalRules $rules -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'storage-class-tiering-policies' -and + $QueryParams['names'] -eq 'tier-to-archive' -and + @($Body['archival_rules']).Count -eq 1 -and + $Body['archival_rules'][0].after -eq 86400000 + } + } + + It 'sends an EMPTY array for -ArchivalRules @() so the list can be cleared, not omit the key' { + Update-PfbStorageClassTieringPolicy -Name 'tier-to-archive' -ArchivalRules @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('archival_rules') -and @($Body['archival_rules']).Count -eq 0 + } + } + + It 'sends enabled as a body field (ContainsKey semantics, not truthiness)' { + Update-PfbStorageClassTieringPolicy -Name 'tier-to-archive' -Enabled $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('enabled') -and $Body['enabled'] -eq $false + } + } + + It 'builds location as a name-reference object' { + Update-PfbStorageClassTieringPolicy -Name 'tier-to-archive' -Location 'array-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['location'].name -eq 'array-1' + } + } + + It 'sends -NewName as the name body field (rename exception, not -StorageClassTieringPolicyName)' { + Update-PfbStorageClassTieringPolicy -Name 'tier-to-archive' -NewName 'tier-to-glacier' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['name'] -eq 'tier-to-glacier' + } + } + + It 'sends retrieval_rules as a composite hashtable array, passed straight through' { + $rules = @(@{ priority = 'standard' }) + Update-PfbStorageClassTieringPolicy -Name 'tier-to-archive' -RetrievalRules $rules -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + @($Body['retrieval_rules']).Count -eq 1 -and + $Body['retrieval_rules'][0].priority -eq 'standard' + } + } + + It 'sends an EMPTY array for -RetrievalRules @() so the list can be cleared, not omit the key' { + Update-PfbStorageClassTieringPolicy -Name 'tier-to-archive' -RetrievalRules @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('retrieval_rules') -and @($Body['retrieval_rules']).Count -eq 0 + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbStorageClassTieringPolicy -Name 'tier-to-archive' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the policy by id when -Id is used' { + Update-PfbStorageClassTieringPolicy -Id 'policy-1' -Enabled $true -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'policy-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbStorageClassTieringPolicy -Name 'tier-to-archive' -Attributes @{ enabled = $true } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['enabled'] -eq $true + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbStorageClassTieringPolicy -Name 'tier-to-archive' -Enabled $true -Attributes @{ enabled = $false } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'read-only fields are never exposed (constraint 11)' { + It 'has no -IsLocal or -PolicyType parameter' { + $keys = (Get-Command Update-PfbStorageClassTieringPolicy).Parameters.Keys + $keys | Should -Not -Contain 'IsLocal' + $keys | Should -Not -Contain 'PolicyType' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'ArchivalRules' } + @{ Parameter = 'Enabled' } + @{ Parameter = 'Location' } + @{ Parameter = 'NewName' } + @{ Parameter = 'RetrievalRules' } + ) { + $attrs = (Get-Command Update-PfbStorageClassTieringPolicy).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbStorageClassTieringPolicy).Parameters.Keys + foreach ($p in 'ArchivalRules','Enabled','Location','NewName','RetrievalRules') { + $keys | Should -Contain $p + } + } + } +} diff --git a/Tests/Update-PfbSubnet.Tests.ps1 b/Tests/Update-PfbSubnet.Tests.ps1 new file mode 100644 index 0000000..07c27bd --- /dev/null +++ b/Tests/Update-PfbSubnet.Tests.ps1 @@ -0,0 +1,116 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbSubnet - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'pre-existing typed parameters now guarded by ContainsKey and in the Individual set (constraint 16 fix)' { + It 'sends prefix, gateway and mtu as body fields' { + Update-PfbSubnet -Name 'subnet1' -Prefix '10.0.0.0/24' -Gateway '10.0.0.254' -Mtu 9000 ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'subnets' -and + $QueryParams['names'] -eq 'subnet1' -and + $Body['prefix'] -eq '10.0.0.0/24' -and + $Body['gateway'] -eq '10.0.0.254' -and + $Body['mtu'] -eq 9000 + } + } + + It 'sends an explicit -Mtu 0 rather than dropping it (constraint 2, integer field, was -gt 0)' { + Update-PfbSubnet -Name 'subnet1' -Mtu 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('mtu') -and $Body['mtu'] -eq 0 + } + } + + It 'rejects -Prefix combined with -Attributes at bind time (real bug: was silently discarding -Prefix before)' { + { Update-PfbSubnet -Name 'subnet1' -Prefix '10.0.0.0/24' -Attributes @{ mtu = 9000 } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'new typed body parameters (missing body properties)' { + It 'builds link_aggregation_group as a name-reference object (constraint 8a)' { + Update-PfbSubnet -Name 'subnet1' -LinkAggregationGroup 'lag1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['link_aggregation_group'].name -eq 'lag1' + } + } + + It 'sends vlan as a body field' { + Update-PfbSubnet -Name 'subnet1' -Vlan 100 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['vlan'] -eq 100 + } + } + + It 'sends an explicit -Vlan 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbSubnet -Name 'subnet1' -Vlan 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('vlan') -and $Body['vlan'] -eq 0 + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbSubnet -Name 'subnet1' -Attributes @{ mtu = 1500 } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['mtu'] -eq 1500 + } + } + + It 'targets the subnet by id when -Id is used' { + Update-PfbSubnet -Id 'subnet-1' -Attributes @{ mtu = 1500 } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'subnet-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on any new parameter (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'LinkAggregationGroup' } + @{ Parameter = 'Vlan' } + ) { + $attrs = (Get-Command Update-PfbSubnet).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbSubnet).Parameters.Keys + foreach ($p in 'Prefix','Gateway','Mtu','LinkAggregationGroup','Vlan') { + $keys | Should -Contain $p + } + } + + It 'omits every body key when no typed body parameter is supplied (constraint 19, empty body permitted)' { + Update-PfbSubnet -Name 'subnet1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + } +} diff --git a/Tests/Update-PfbSyslogServer.Tests.ps1 b/Tests/Update-PfbSyslogServer.Tests.ps1 new file mode 100644 index 0000000..5f4f897 --- /dev/null +++ b/Tests/Update-PfbSyslogServer.Tests.ps1 @@ -0,0 +1,121 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbSyslogServer - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends uri as a body field' { + Update-PfbSyslogServer -Name "syslog-prod" -Uri "tcp://newsyslog.example.com:514" ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'syslog-servers' -and + $QueryParams['names'] -eq 'syslog-prod' -and + $Body['uri'] -eq 'tcp://newsyslog.example.com:514' + } + } + + It 'sends services as a body field' { + Update-PfbSyslogServer -Name "syslog-prod" -Services 'data-audit','management' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + @($Body['services']).Count -eq 2 -and + $Body['services'][0] -eq 'data-audit' -and + $Body['services'][1] -eq 'management' + } + } + + It 'rejects a -Services value outside the enum' { + { Update-PfbSyslogServer -Name "syslog-prod" -Services 'bogus' -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ErrorId 'ParameterArgumentValidationError,Update-PfbSyslogServer' + } + + It 'sends an EMPTY array for -Services @() so the list can be cleared' { + Update-PfbSyslogServer -Name "syslog-prod" -Services @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('services') -and @($Body['services']).Count -eq 0 + } + } + + It 'builds sources as name-reference objects (constraint 8b, array of references)' { + Update-PfbSyslogServer -Name "syslog-prod" -Sources 'eth0' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['sources'].Count -eq 1 -and $Body['sources'][0].name -eq 'eth0' + } + } + + It 'sends an EMPTY array for -Sources @() so the list can be cleared' { + Update-PfbSyslogServer -Name "syslog-prod" -Sources @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('sources') -and @($Body['sources']).Count -eq 0 + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbSyslogServer -Name "syslog-prod" -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the server by id when -Id is used' { + Update-PfbSyslogServer -Id "10314f42-020d-7080-8013-000ddt400090" -Uri 'tcp://x:514' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq '10314f42-020d-7080-8013-000ddt400090' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbSyslogServer -Name "syslog-prod" -Attributes @{ uri = "tls://syslog.corp.com:6514" } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['uri'] -eq 'tls://syslog.corp.com:6514' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbSyslogServer -Name "syslog-prod" -Uri 'tcp://x:514' -Attributes @{ uri = 'tcp://y:514' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'Uri' } + @{ Parameter = 'Sources' } + ) { + $attrs = (Get-Command Update-PfbSyslogServer).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'puts ValidateSet(data-audit, management) on -Services in that order (constraint 3)' { + $attr = (Get-Command Update-PfbSyslogServer).Parameters['Services'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } + $attr.ValidValues | Should -Be @('data-audit', 'management') + } + } +} diff --git a/Tests/Update-PfbTarget.Tests.ps1 b/Tests/Update-PfbTarget.Tests.ps1 new file mode 100644 index 0000000..1e68356 --- /dev/null +++ b/Tests/Update-PfbTarget.Tests.ps1 @@ -0,0 +1,105 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbTarget - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends address as a body field' { + Update-PfbTarget -Name 's3-target-aws' -Address 's3.us-east-1.amazonaws.com' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'targets' -and + $QueryParams['names'] -eq 's3-target-aws' -and + $Body['address'] -eq 's3.us-east-1.amazonaws.com' + } + } + + It 'sends an EMPTY string for -Address "" rather than dropping the key' { + Update-PfbTarget -Name 's3-target-aws' -Address '' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('address') -and $Body['address'] -eq '' + } + } + + It 'builds ca_certificate_group as a name-reference object (constraint 8a)' { + Update-PfbTarget -Name 's3-target-aws' -CaCertificateGroup 'my-certs' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['ca_certificate_group'].name -eq 'my-certs' + } + } + + It 'sends -NewName as the name body field (rename exception)' { + Update-PfbTarget -Name 's3-target-aws' -NewName 's3-target-renamed' ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['name'] -eq 's3-target-renamed' + } + } + + It 'has no -TargetName-shaped body parameter, only -NewName' { + (Get-Command Update-PfbTarget).Parameters.Keys | Should -Not -Contain 'TargetName' + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbTarget -Name 's3-target-aws' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the replication target by id when -Id is used' { + Update-PfbTarget -Id 'target-1' -Address 'new.example.com' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'target-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbTarget -Name 's3-target-aws' -Attributes @{ address = 'raw.example.com' } ` + -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['address'] -eq 'raw.example.com' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbTarget -Name 's3-target-aws' -Address 'x.example.com' -Attributes @{ address = 'y' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'Address' } + @{ Parameter = 'CaCertificateGroup' } + @{ Parameter = 'NewName' } + ) { + $attrs = (Get-Command Update-PfbTarget).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + } +} diff --git a/Tests/Update-PfbTlsPolicy.Tests.ps1 b/Tests/Update-PfbTlsPolicy.Tests.ps1 new file mode 100644 index 0000000..57e6d61 --- /dev/null +++ b/Tests/Update-PfbTlsPolicy.Tests.ps1 @@ -0,0 +1,190 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbTlsPolicy - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'builds appliance_certificate as a name-reference object' { + Update-PfbTlsPolicy -Name 'tls-strict' -ApplianceCertificate 'cert-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'tls-policies' -and + $QueryParams['names'] -eq 'tls-strict' -and + $Body['appliance_certificate'].name -eq 'cert-1' + } + } + + It 'sends an explicit -ClientCertificatesRequired:$false (ContainsKey semantics, not truthiness)' { + Update-PfbTlsPolicy -Name 'tls-strict' -ClientCertificatesRequired $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('client_certificates_required') -and $Body['client_certificates_required'] -eq $false + } + } + + It 'sends disabled_tls_ciphers as a plain string array' { + Update-PfbTlsPolicy -Name 'tls-strict' -DisabledTlsCiphers 'RC4','3DES' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + @($Body['disabled_tls_ciphers']).Count -eq 2 -and + $Body['disabled_tls_ciphers'][0] -eq 'RC4' -and + $Body['disabled_tls_ciphers'][1] -eq '3DES' + } + } + + It 'sends an EMPTY array for -DisabledTlsCiphers @() so a list can be cleared' { + Update-PfbTlsPolicy -Name 'tls-strict' -DisabledTlsCiphers @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('disabled_tls_ciphers') -and + @($Body['disabled_tls_ciphers']).Count -eq 0 + } + } + + It 'sends enabled as a body field' { + Update-PfbTlsPolicy -Name 'tls-strict' -Enabled $true -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['enabled'] -eq $true + } + } + + It 'sends enabled_tls_ciphers as a plain string array' { + Update-PfbTlsPolicy -Name 'tls-strict' -EnabledTlsCiphers 'TLS_AES_128_GCM_SHA256' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + @($Body['enabled_tls_ciphers']).Count -eq 1 -and + $Body['enabled_tls_ciphers'][0] -eq 'TLS_AES_128_GCM_SHA256' + } + } + + It 'sends an EMPTY array for -EnabledTlsCiphers @() so a list can be cleared' { + Update-PfbTlsPolicy -Name 'tls-strict' -EnabledTlsCiphers @() -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('enabled_tls_ciphers') -and + @($Body['enabled_tls_ciphers']).Count -eq 0 + } + } + + It 'builds location as a name-reference object' { + Update-PfbTlsPolicy -Name 'tls-strict' -Location 'array-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['location'].name -eq 'array-1' + } + } + + It 'sends min_tls_version as a body field' { + Update-PfbTlsPolicy -Name 'tls-strict' -MinTlsVersion '1.3' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['min_tls_version'] -eq '1.3' + } + } + + It 'sends -NewName as the name body field (rename exception, not -TlsPolicyName)' { + Update-PfbTlsPolicy -Name 'tls-strict' -NewName 'tls-modern' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['name'] -eq 'tls-modern' + } + } + + It 'builds trusted_client_certificate_authority as a name-reference object' { + Update-PfbTlsPolicy -Name 'tls-strict' -TrustedClientCertificateAuthority 'ca-group-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['trusted_client_certificate_authority'].name -eq 'ca-group-1' + } + } + + It 'sends an explicit -VerifyClientCertificateTrust:$false (ContainsKey semantics, not truthiness)' { + Update-PfbTlsPolicy -Name 'tls-strict' -VerifyClientCertificateTrust $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('verify_client_certificate_trust') -and $Body['verify_client_certificate_trust'] -eq $false + } + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbTlsPolicy -Name 'tls-strict' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the policy by id when -Id is used' { + Update-PfbTlsPolicy -Id 'policy-1' -Enabled $true -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'policy-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbTlsPolicy -Name 'tls-strict' -Attributes @{ min_tls_version = '1.2' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['min_tls_version'] -eq '1.2' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time' { + { Update-PfbTlsPolicy -Name 'tls-strict' -Enabled $true -Attributes @{ enabled = $false } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'read-only fields are never exposed (constraint 11)' { + It 'has no -IsLocal or -PolicyType parameter' { + $keys = (Get-Command Update-PfbTlsPolicy).Parameters.Keys + $keys | Should -Not -Contain 'IsLocal' + $keys | Should -Not -Contain 'PolicyType' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'ApplianceCertificate' } + @{ Parameter = 'ClientCertificatesRequired' } + @{ Parameter = 'DisabledTlsCiphers' } + @{ Parameter = 'Enabled' } + @{ Parameter = 'EnabledTlsCiphers' } + @{ Parameter = 'Location' } + @{ Parameter = 'MinTlsVersion' } + @{ Parameter = 'NewName' } + @{ Parameter = 'TrustedClientCertificateAuthority' } + @{ Parameter = 'VerifyClientCertificateTrust' } + ) { + $attrs = (Get-Command Update-PfbTlsPolicy).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbTlsPolicy).Parameters.Keys + foreach ($p in 'ApplianceCertificate','ClientCertificatesRequired','DisabledTlsCiphers','Enabled', + 'EnabledTlsCiphers','Location','MinTlsVersion','NewName', + 'TrustedClientCertificateAuthority','VerifyClientCertificateTrust') { + $keys | Should -Contain $p + } + } + } +} diff --git a/Tests/Update-PfbUserGroupQuotaPolicy.Tests.ps1 b/Tests/Update-PfbUserGroupQuotaPolicy.Tests.ps1 new file mode 100644 index 0000000..444b657 --- /dev/null +++ b/Tests/Update-PfbUserGroupQuotaPolicy.Tests.ps1 @@ -0,0 +1,94 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'Update-PfbUserGroupQuotaPolicy' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'PATCHes user-group-quota-policies by -Name' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Update-PfbUserGroupQuotaPolicy -Name 'pol-1' -Enabled:$false -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and + $Endpoint -eq 'user-group-quota-policies' -and + $QueryParams['names'] -eq 'pol-1' -and + $Body['enabled'] -eq $false + } + } + + It 'targets by -Id when supplied instead of -Name' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Update-PfbUserGroupQuotaPolicy -Id 'id-1' -Rules @(@{ quota_limit = 5 }) -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'id-1' -and -not $QueryParams.ContainsKey('names') -and + $Body['rules'][0]['quota_limit'] -eq 5 + } + } + + It 'sends ignore_usage and versions when requested' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Update-PfbUserGroupQuotaPolicy -Name 'pol-1' -Enabled:$true -IgnoreUsage -Version 'v1' -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ignore_usage'] -eq 'true' -and $QueryParams['versions'] -eq 'v1' + } + } + + It '-Attributes overrides the constructed body verbatim' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Update-PfbUserGroupQuotaPolicy -Name 'pol-1' -Attributes @{ enabled = $true } -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Keys.Count -eq 1 -and $Body['enabled'] -eq $true + } + } + + It 'rejects a -Name that looks like a wildcard' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Update-PfbUserGroupQuotaPolicy -Name '*' -Enabled:$true -Confirm:$false -Array $arr + } } | Should -Throw + } + + It 'does not call the API under -WhatIf' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Update-PfbUserGroupQuotaPolicy -Name 'pol-1' -Enabled:$true -WhatIf -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 + } + + It 'declares -Name and -Id mandatory in mutually exclusive parameter sets (rejects neither being supplied)' { + InModuleScope PureStorageFlashBladePowerShell { + $cmd = Get-Command Update-PfbUserGroupQuotaPolicy + $nameSet = $cmd.Parameters['Name'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] -and $_.ParameterSetName -eq 'ByName' } + $idSet = $cmd.Parameters['Id'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] -and $_.ParameterSetName -eq 'ById' } + + $nameSet.Mandatory | Should -BeTrue + $idSet.Mandatory | Should -BeTrue + } + } +} diff --git a/Tests/Update-PfbUserGroupQuotaPolicyRule.Tests.ps1 b/Tests/Update-PfbUserGroupQuotaPolicyRule.Tests.ps1 new file mode 100644 index 0000000..5d74351 --- /dev/null +++ b/Tests/Update-PfbUserGroupQuotaPolicyRule.Tests.ps1 @@ -0,0 +1,89 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.25'; AuthToken = 'x' } +} + +Describe 'Update-PfbUserGroupQuotaPolicyRule' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'PATCHes user-group-quota-policies/rules by -Name with only quota_limit/notifications in the body' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Update-PfbUserGroupQuotaPolicyRule -Name 'rule-1' -QuotaLimit 2147483648 -Notifications 'None' -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and + $Endpoint -eq 'user-group-quota-policies/rules' -and + $QueryParams['names'] -eq 'rule-1' -and + $Body['quota_limit'] -eq 2147483648 -and + $Body['notifications'] -eq 'None' -and + $Body.Keys.Count -eq 2 + } + } + + It 'targets by -Id instead of -Name' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Update-PfbUserGroupQuotaPolicyRule -Id 'rid-1' -QuotaLimit 100 -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'rid-1' -and -not $QueryParams.ContainsKey('names') + } + } + + It 'sends ignore_usage when -IgnoreUsage is set' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Update-PfbUserGroupQuotaPolicyRule -Name 'rule-1' -QuotaLimit 100 -IgnoreUsage -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ignore_usage'] -eq 'true' + } + } + + It '-Attributes overrides the constructed body verbatim' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Update-PfbUserGroupQuotaPolicyRule -Name 'rule-1' -Attributes @{ quota_limit = 7 } -Confirm:$false -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Keys.Count -eq 1 -and $Body['quota_limit'] -eq 7 + } + } + + It 'rejects a -Name that looks like a wildcard' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Update-PfbUserGroupQuotaPolicyRule -Name '*' -QuotaLimit 100 -Confirm:$false -Array $arr + } } | Should -Throw + } + + It 'rejects a -QuotaLimit of 0' { + { InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Update-PfbUserGroupQuotaPolicyRule -Name 'rule-1' -QuotaLimit 0 -Confirm:$false -Array $arr + } } | Should -Throw + } + + It 'does not call the API under -WhatIf' { + InModuleScope PureStorageFlashBladePowerShell -Parameters @{ arr = $fakeArray } { + param($arr) + Update-PfbUserGroupQuotaPolicyRule -Name 'rule-1' -QuotaLimit 100 -WhatIf -Array $arr + } + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 0 + } +} diff --git a/Tests/Update-PfbWormPolicy.Tests.ps1 b/Tests/Update-PfbWormPolicy.Tests.ps1 new file mode 100644 index 0000000..624c61e --- /dev/null +++ b/Tests/Update-PfbWormPolicy.Tests.ps1 @@ -0,0 +1,159 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + $moduleRoot = Split-Path -Parent $PSScriptRoot + $manifest = Join-Path $moduleRoot 'PureStorageFlashBladePowerShell.psd1' + Import-Module $manifest -Force + + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Update-PfbWormPolicy - typed body parameters (#31)' { + + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + Context 'typed parameters build the body' { + It 'sends an explicit -DefaultRetention 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbWormPolicy -Name 'worm-compliance' -DefaultRetention 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Method -eq 'PATCH' -and $Endpoint -eq 'worm-data-policies' -and + $QueryParams['names'] -eq 'worm-compliance' -and + $Body.ContainsKey('default_retention') -and $Body['default_retention'] -eq 0 + } + } + + It 'sends default_retention as a body field when non-zero' { + Update-PfbWormPolicy -Name 'worm-compliance' -DefaultRetention 7776000000 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['default_retention'] -eq 7776000000 + } + } + + It 'sends an explicit -Enabled:$false (ContainsKey semantics, not truthiness)' { + Update-PfbWormPolicy -Name 'worm-compliance' -Enabled $false -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('enabled') -and $Body['enabled'] -eq $false + } + } + + It 'builds location as a name-reference object' { + Update-PfbWormPolicy -Name 'worm-compliance' -Location 'array-1' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['location'].name -eq 'array-1' + } + } + + It 'sends an explicit -MaxRetention 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbWormPolicy -Name 'worm-compliance' -MaxRetention 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('max_retention') -and $Body['max_retention'] -eq 0 + } + } + + It 'sends an explicit -MinRetention 0 rather than dropping it (constraint 2, integer field)' { + Update-PfbWormPolicy -Name 'worm-compliance' -MinRetention 0 -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.ContainsKey('min_retention') -and $Body['min_retention'] -eq 0 + } + } + + It 'sends mode as a body field' { + Update-PfbWormPolicy -Name 'worm-compliance' -Mode 'regulatory' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['mode'] -eq 'regulatory' + } + } + + It 'sends retention_lock as a body field' { + Update-PfbWormPolicy -Name 'worm-compliance' -RetentionLock 'locked' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['retention_lock'] -eq 'locked' + } + } + + It 'rejects an invalid -RetentionLock value via ValidateSet (constraint 3)' { + { Update-PfbWormPolicy -Name 'worm-compliance' -RetentionLock 'bogus' ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | Should -Throw + } + + It 'omits every body key when no typed body parameter is supplied' { + Update-PfbWormPolicy -Name 'worm-compliance' -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body.Count -eq 0 + } + } + + It 'targets the policy by id when -Id is used' { + Update-PfbWormPolicy -Id 'policy-1' -Enabled $true -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $QueryParams['ids'] -eq 'policy-1' -and -not $QueryParams.ContainsKey('names') + } + } + } + + Context '-Attributes remains supported and is mutually exclusive' { + It 'still sends a raw -Attributes body' { + Update-PfbWormPolicy -Name 'worm-compliance' -Attributes @{ retention_lock = 'locked' } -Confirm:$false -Array $fakeArray + + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest -Times 1 -Exactly -ParameterFilter { + $Body['retention_lock'] -eq 'locked' + } + } + + It 'rejects -Attributes combined with a typed parameter at bind time (constraint 5: parameter sets, no extra throw)' { + { Update-PfbWormPolicy -Name 'worm-compliance' -Mode 'regulatory' -Attributes @{ mode = 'regulatory' } ` + -Confirm:$false -Array $fakeArray -ErrorAction Stop } | + Should -Throw -ExpectedMessage '*Parameter set cannot be resolved*' + } + } + + Context 'read-only fields are never exposed (constraint 11)' { + It 'has no -IsLocal, -PolicyType, or -NewName parameter (name is read-only on this endpoint)' { + $keys = (Get-Command Update-PfbWormPolicy).Parameters.Keys + $keys | Should -Not -Contain 'IsLocal' + $keys | Should -Not -Contain 'PolicyType' + $keys | Should -Not -Contain 'NewName' + } + } + + Context 'constraint compliance' { + It 'puts no ValidateSet on - (constraint 3, no spec enum)' -ForEach @( + @{ Parameter = 'DefaultRetention' } + @{ Parameter = 'Enabled' } + @{ Parameter = 'Location' } + @{ Parameter = 'MaxRetention' } + @{ Parameter = 'MinRetention' } + @{ Parameter = 'Mode' } + ) { + $attrs = (Get-Command Update-PfbWormPolicy).Parameters[$Parameter].Attributes + @($attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).Count | + Should -Be 0 + } + + It 'puts exactly the spec ValidateSet on -RetentionLock' { + $attrs = (Get-Command Update-PfbWormPolicy).Parameters['RetentionLock'].Attributes + $validateSet = $attrs | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } + $validateSet.ValidValues | Should -Be @('unlocked', 'locked') + } + + It 'exposes every settable body field the endpoint accepts' { + $keys = (Get-Command Update-PfbWormPolicy).Parameters.Keys + foreach ($p in 'DefaultRetention','Enabled','Location','MaxRetention','MinRetention','Mode','RetentionLock') { + $keys | Should -Contain $p + } + } + } +} diff --git a/docs/drift-annotations.json b/docs/drift-annotations.json new file mode 100644 index 0000000..558e731 --- /dev/null +++ b/docs/drift-annotations.json @@ -0,0 +1,27 @@ +{ + "schema": "One entry per annotation. matchType 'field' matches 'match' exactly against a wire field name (as it appears in MissingQueryParameters/MissingBodyProperties); matchType 'endpoint' matches 'match' as a case-insensitive SUBSTRING of an endpoint key (' /', e.g. 'GET /management-access-policies'). kind is one of: designDecision (a recorded, already-made design decision -- e.g. a field intentionally not yet wired up), priorConclusion (a conclusion an earlier investigation already reached, so it isn't re-litigated), liveTestingHazard (a note that live-array testing behaves in a way that is NOT an implementation bug -- e.g. a permissions/authorization quirk). note is a short, deliberately non-authoritative human-readable string -- it must never assert or imply a behavioral decision that has not actually been made elsewhere. reference is a repo-relative path to further detail, or null. See tools/lib/PfbApiDriftTools.ps1's Get-PfbDriftAnnotations/Find-PfbDriftAnnotation for the loader/lookup functions that read this file; consumers should use those rather than parsing this JSON directly. Add more entries by appending to 'annotations' -- no code change is needed for a new entry using an existing kind/matchType.", + "schemaVersion": 1, + "annotations": [ + { + "matchType": "field", + "match": "context_names", + "kind": "designDecision", + "note": "not yet implemented", + "reference": "docs/design/fusion-context-injection.md" + }, + { + "matchType": "field", + "match": "allow_errors", + "kind": "designDecision", + "note": "not yet implemented", + "reference": "docs/design/fusion-context-injection.md" + }, + { + "matchType": "endpoint", + "match": "management-access-policies", + "kind": "liveTestingHazard", + "note": "POST/PATCH/DELETE return 403 regardless of account; not an implementation bug", + "reference": null + } + ] +} diff --git a/scripts/Publish-Gallery.ps1 b/scripts/Publish-Gallery.ps1 index e34532a..f51143c 100644 --- a/scripts/Publish-Gallery.ps1 +++ b/scripts/Publish-Gallery.ps1 @@ -55,6 +55,11 @@ if (Test-Path $dstDir) { Remove-Item $dstDir -Recurse -Force } New-Item -ItemType Directory -Path $dstDir | Out-Null Copy-Item (Join-Path $srcDir "$srcName.psm1") (Join-Path $dstDir "$packageName.psm1") Copy-Item (Join-Path $srcDir 'LICENSE') (Join-Path $dstDir 'LICENSE') -ErrorAction SilentlyContinue +# Data/ holds the runtime capability + version maps (read from disk relative to the +# module root). build.ps1 copies it into the source package; carry it into the branded +# package too, or the API version-awareness check silently no-ops on every Gallery install. +$dataDir = Join-Path $srcDir 'Data' +if (Test-Path $dataDir) { Copy-Item $dataDir (Join-Path $dstDir 'Data') -Recurse } # 3. Re-brand the manifest. $psd1 = Get-Content (Join-Path $srcDir "$srcName.psd1") -Raw diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 3b881eb..d6c2e59 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -113,6 +113,19 @@ if (Test-Path $licenseSource) { Write-Host " Copied LICENSE" -ForegroundColor Green } +# --- Copy Data/ (runtime-consumed only: Get-PfbCapabilityMap.ps1 / Get-PfbVersionMap.ps1 +# read Data/PfbCapabilityMap.json and Data/PfbVersionMap.json relative to the installed +# module root via $script:PfbModuleRoot -- omitting this directory doesn't crash anything +# (both loaders treat a missing file as a graceful no-op), it just silently makes the +# entire "fail fast on an unsupported API version" feature inert for every real Gallery +# install. Reports/ is deliberately NEVER copied here -- it's maintainer/agent-facing +# advisory output only, dead weight for an end user's install. +$dataSource = Join-Path $repoRoot 'Data' +if (Test-Path $dataSource) { + Copy-Item -Path $dataSource -Destination $OutputPath -Recurse + Write-Host " Copied Data/" -ForegroundColor Green +} + # --- Summary --- Write-Host '' Write-Host "Build complete!" -ForegroundColor Green diff --git a/tools/Build-PfbApiDriftReport.ps1 b/tools/Build-PfbApiDriftReport.ps1 new file mode 100644 index 0000000..f608327 --- /dev/null +++ b/tools/Build-PfbApiDriftReport.ps1 @@ -0,0 +1,964 @@ +#Requires -Version 7.0 +<# +.SYNOPSIS + Builds the combined "API drift" report: uncovered endpoints, parameter gaps on + endpoints an existing cmdlet already calls, drift on ValidateSets that already + exist, and new ValidateSet candidates. +.DESCRIPTION + Composes Data/PfbCapabilityMap.json, the AST-based cmdlet-parameter inventory + (tools/lib/PfbCmdletParamTools.ps1), and Reports/PfbFieldCmdletMap.json rather than + re-deriving spec parsing for categories 1/2/4. Category 3 (ValidateSet drift) does + re-scan tools/specs/ via Get-PfbValueEnumHistory (tools/lib/PfbValueEnumTools.ps1) -- + see docs/superpowers/plans/2026-07-17-api-drift-report-plan.md's "Deviation from the + design spec" note for why that one category needs it. + + Reporting only -- does NOT add a ValidateSet, ArgumentCompleter, or typed parameter + to any Public/ cmdlet. +.PARAMETER SpecsDirectory + Where cached spec JSON files live. Defaults to tools/specs relative to this script. +.PARAMETER PublicDirectory + Where Public/ cmdlet files live. Defaults to Public/ relative to the repo root. +.PARAMETER PrivateDirectory + Where Private/ helper files live. Defaults to Private/ relative to the repo root. +.PARAMETER CapabilityMapPath + Path to the capability-map JSON. Defaults to Data/PfbCapabilityMap.json. +.PARAMETER FieldCmdletMapPath + Path to the field-cmdlet-map JSON. Defaults to Reports/PfbFieldCmdletMap.json. +.PARAMETER OutputPath + Where to write Reports/PfbApiDriftReport.json. Defaults there. +.PARAMETER ReportPath + Where to write Reports/PfbApiDriftReport.md. Defaults there. +.PARAMETER SinceVersion + Optional REST version (e.g. '2.26'). When given, uncoveredEndpoints and + parameterGaps are filtered down to only items introduced strictly after this + version -- e.g. -SinceVersion '2.26' isolates exactly what 2.27 added, instead of + the full accumulated backlog since 2.0. validateSetDrift and newValidateSetCandidates + are not filtered: the capability map doesn't track a per-value introduced-version for + either category, only per-field/per-endpoint, so there's no "since" signal to filter + on there yet. + +.NOTES + Task 5 (enrichment + enum join) record shape for `parameterGaps[].missingBodyProperties`: + + - **Only enriched on a 'high'-confidence endpoint** (`confidence.level -eq 'high'`). + On a 'partial'-confidence endpoint the entries stay bare strings, exactly as before -- + see the comment above `Get-PfbBodyPropertyEnrichment` in tools/lib/PfbApiDriftTools.ps1 + for why (short version: a partial-confidence endpoint's list can contain false + positives, and attaching a fully-worked-out type/synopsis/enum/target to a field that + might not even be a real gap would overstate a confidence the endpoint's own + `confidence.caveat` is explicitly telling the reader not to have -- NOT a suppression, + every field name still appears unchanged either way). This gating is also what makes + this task's pinned acceptance numbers (33 matched / 43 not-found-in-resource / 326 + no-spec-enum-found, over 402 addable gaps) reproducible: they hold only over the + 'high'-confidence population (402), not over all addable gaps regardless of + confidence (605) -- verified 2026-07-26. + - **`missingQueryParameters` and `readOnlyFields` are deliberately NEVER enriched this + way, regardless of confidence** -- `readOnlyFields` are not addable at all (there is + nothing actionable to enrich), and query-parameter gaps stay bare name strings on + purpose: ~896 query-gap records would roughly double this artifact's size for no + current consumer (nothing downstream reads enriched query-gap detail today). This is a + deliberate, documented asymmetry, not an inconsistency -- revisit only if a consumer + for enriched query-gap detail actually appears. + - Enriched shape: `{ name, type, format, specRequired, synopsis, suggestedPowerShellType, + enumValues, enumStatus, target }`. + - `type`/`format` are the raw OpenAPI values from the newest ANALYSED spec (may be + `$null` when the property's own schema node carries no `type` -- e.g. the property + is itself an unresolved `$ref`, per Get-PfbSchemaPropertyDetails's PIN). + - `specRequired` is `Required` from Get-PfbSchemaPropertyDetails -- **this is + informational metadata about the OpenAPI spec's own `required:` array, and must + NEVER be treated as an instruction to make the corresponding PowerShell parameter + `[Parameter(Mandatory)]`.** This repo has a recorded hazard: a `Mandatory` + parameter tested via `Should -Throw` hangs the terminal on PowerShell's own + "Supply values for parameters" interactive prompt -- the convention here is an + optional parameter with an explicit `throw`. A human adding a parameter for a + `specRequired: true` field should keep it optional and validate/throw in the + function body, exactly like every other parameter in this module. + - `suggestedPowerShellType` comes from a fixed table (`int64`->`[long]`, + `int32`/`uint32`/no-format->`[int]`, `number`->`[double]`, `string`->`[string]`, + `boolean`->`[bool]`, `array`->`[[]]`, anything else->`[object]`) -- see + `Get-PfbSuggestedPowerShellType` in tools/lib/PfbApiDriftTools.ps1. It exists + specifically because the `int64`-vs-`int32` distinction is real and silent: 37 of + the 402 real high-confidence addable fields are `type: integer, format: int64`, + and eyeballing bare `"type": "integer"` while ignoring `format` would silently + truncate every one of them by writing `[int]`. The raw `type`/`format` are ALWAYS + emitted alongside so a human can override this suggestion. + - `enumValues`/`enumStatus` come from `Resolve-PfbFieldValueEnum` (never a bare + wire-name lookup), keyed by the field's own `OwnerSchema` as `-ResourceHint` (NOT + the older cmdlet-name-derived `Get-PfbResourceHint`, which only reaches 14 of 33 + real matches). `enumStatus` is always one of that function's own literal values + (`matched`/`collision`/`not-found-in-resource`/`no-spec-enum-found`) -- never + silently coerced to `null`. `enumValues` is always an array (empty unless + `enumStatus` is `matched`). + - `target` carries insertion-point COORDINATES ONLY -- `{ file, paramBlockLine, + payloadVariable, assignmentStyle, hasAttributes }` -- never a diff/patch (decision + 12): a patch goes stale the moment the file is next touched and cannot see + mutual-exclusivity/parameter-set constraints a human editing by hand must respect. + `file` is a repo-relative path (forward slashes). See + `Get-PfbCmdletBodyInsertionTarget` in tools/lib/PfbCmdletParamTools.ps1 for exactly + how `paramBlockLine`/`payloadVariable`/`assignmentStyle`/`hasAttributes` are + derived, and this script's own `Get-PfbGapTarget` helper for how the ONE primary + cmdlet is picked when an endpoint has more than one (alphabetically first, for + determinism -- a human should still check for sibling cmdlets on the same + endpoint). +#> +[CmdletBinding()] +param( + [string]$SpecsDirectory, + [string]$PublicDirectory, + [string]$PrivateDirectory, + [string]$CapabilityMapPath, + [string]$FieldCmdletMapPath, + [string]$ResponseShapeMapPath, + [string]$OutputPath, + [string]$ReportPath, + [string]$SinceVersion +) + +$ErrorActionPreference = 'Stop' + +$scriptDir = $PSScriptRoot +. (Join-Path $scriptDir 'lib/PfbSpecTools.ps1') +. (Join-Path $scriptDir 'lib/PfbValueEnumTools.ps1') +. (Join-Path $scriptDir 'lib/PfbCmdletParamTools.ps1') +. (Join-Path $scriptDir 'lib/PfbApiDriftTools.ps1') +. (Join-Path $scriptDir 'lib/PfbContextRuleTools.ps1') + +$repoRoot = Split-Path -Parent $scriptDir +if (-not $SpecsDirectory) { $SpecsDirectory = Join-Path $scriptDir 'specs' } +if (-not $PublicDirectory) { $PublicDirectory = Join-Path $repoRoot 'Public' } +if (-not $PrivateDirectory) { $PrivateDirectory = Join-Path $repoRoot 'Private' } +if (-not $CapabilityMapPath) { $CapabilityMapPath = Join-Path (Join-Path $repoRoot 'Data') 'PfbCapabilityMap.json' } +if (-not $FieldCmdletMapPath) { $FieldCmdletMapPath = Join-Path (Join-Path $repoRoot 'Reports') 'PfbFieldCmdletMap.json' } +if (-not $OutputPath) { $OutputPath = Join-Path (Join-Path $repoRoot 'Reports') 'PfbApiDriftReport.json' } +if (-not $ReportPath) { $ReportPath = Join-Path (Join-Path $repoRoot 'Reports') 'PfbApiDriftReport.md' } + +if (-not (Test-Path $CapabilityMapPath)) { throw "Capability map not found at '$CapabilityMapPath'. Run Build-PfbCapabilityMap.ps1 first." } +if (-not (Test-Path $FieldCmdletMapPath)) { throw "Field-cmdlet map not found at '$FieldCmdletMapPath'. Run Build-PfbFieldCmdletMap.ps1 first." } + +$capabilityMap = Get-Content -Path $CapabilityMapPath -Raw | ConvertFrom-Json -Depth 20 +$fieldCmdletMap = Get-Content -Path $FieldCmdletMapPath -Raw | ConvertFrom-Json -Depth 20 + +if (-not $ResponseShapeMapPath) { + $ResponseShapeMapPath = Join-Path $repoRoot 'Data/PfbResponseShapeMap.json' +} + +# Response-shape findings are optional: the map is a separate artifact with no runtime +# consumer, so a report generated before it exists should degrade to empty lists rather +# than fail. Absence is reported honestly below rather than rendered as "no drift". +$responseFindings = [PSCustomObject]@{ + Removals = @() + RenameCandidates = @() + UnhandledEnvelopeFields = @() +} +$responseShapeMapPresent = Test-Path $ResponseShapeMapPath +if ($responseShapeMapPresent) { + $responseShapeMap = Get-Content -Path $ResponseShapeMapPath -Raw | ConvertFrom-Json + + # Built from $PrivateDirectory, NOT from $repoRoot. It defaults to /Private + # (line 129) so the real run is unchanged, but a caller that passes -PrivateDirectory + # -- every synthetic-fixture test in Tests/Build-PfbApiDriftReport.Tests.ps1 does -- + # must have that honoured here exactly as it is at the Get-PfbModuleCalledEndpoints and + # Get-PfbNonActionableParameters call sites below. Hardcoding $repoRoot here made those + # runs silently analyse the REAL Private/ tree. + $requestHandlerPath = Join-Path $PrivateDirectory 'Invoke-PfbApiRequest.ps1' + + # Warn loudly on absence. Get-PfbResponseShapeFindings returns an EMPTY + # UnhandledEnvelopeFields list when the handler cannot be read, which is + # indistinguishable in the rendered report from "every envelope field is handled" -- + # a broken lookup that reads as a clean bill of health. The removals and rename + # candidates are unaffected; only the envelope-coverage category needs the handler. + if (-not (Test-Path $requestHandlerPath)) { + Write-Warning "Request handler not found at '$requestHandlerPath'; the unhandled-response-envelope-fields category will be EMPTY. That is a skipped analysis, not a finding of zero." + } + + $responseFindings = Get-PfbResponseShapeFindings ` + -ResponseShapeMap $responseShapeMap ` + -RequestHandlerPath $requestHandlerPath +} +else { + Write-Warning "Response shape map not found at '$ResponseShapeMapPath'; response-shape drift categories will be empty. Run tools/Build-PfbResponseShapeMap.ps1." +} + +$inventory = Get-PfbCmdletParameterInventory -PublicDirectory $PublicDirectory +$calledEndpoints = Get-PfbModuleCalledEndpoints -PublicDirectory $PublicDirectory -PrivateDirectory $PrivateDirectory + +# "Newest spec" for read-only/phantom resolution is pinned to the capability map's OWN +# analysed set ($capabilityMap.generatedFrom[-1]), never whatever file happens to be +# newest under -SpecsDirectory -- the two diverge as soon as specs are refreshed without +# rebuilding the map (see this repo's drift-report-actionable plan, Task 7's +# generatedFrom item, for the verified case: specs at 2.28, map at 2.27). Only this ONE +# spec is re-parsed here, purely to give Get-PfbParameterCoverageGaps the CURRENT +# (phantom-free) parameter/body-property set per endpoint -- see that function's +# -CurrentSpecCapabilities help for why the capability map's own accumulated +# parameters/bodyProperties dictionaries can't answer that question by themselves. +$newestAnalysedVersion = $capabilityMap.generatedFrom | Select-Object -Last 1 +$currentSpecCapabilities = @() +if ($newestAnalysedVersion) { + $newestSpecPath = Join-Path $SpecsDirectory "fb$newestAnalysedVersion.json" + if (-not (Test-Path $newestSpecPath)) { + throw "Newest analysed spec 'fb$newestAnalysedVersion.json' (per capability map's generatedFrom) not found under '$SpecsDirectory'. Run Update-PfbApiSpecs.ps1 first, or rebuild the capability map against the specs on disk." + } + $newestSpec = Get-Content -Path $newestSpecPath -Raw | ConvertFrom-Json -Depth 64 + $currentSpecCapabilities = @(Get-PfbSpecCapabilities -Spec $newestSpec) +} + +# Endpoint key (" /") -> the newest-analysed-spec BodyPropertyDetails record +# set for that endpoint, reused by Task 5's enrichment below (Get-PfbParameterCoverageGaps +# builds an equivalent dictionary internally for its OWN phantom-filtering purposes, but +# does not expose BodyPropertyDetails on its output -- this is a second, small, INTENTIONAL +# lookup built directly from $currentSpecCapabilities already computed above, not a second +# spec parse). +$currentByEndpoint = [System.Collections.Generic.Dictionary[string, object]]::new() +foreach ($cap in $currentSpecCapabilities) { $currentByEndpoint["$($cap.Method) $($cap.Path)"] = $cap } + +# Value-enum history, computed ONCE and reused by two logically SEPARATE report +# categories: category 3 (Get-PfbValidateSetDrift, existing-ValidateSet drift -- unrelated +# to this task) below, and Task 5's own MissingBodyProperties enum-join enrichment inside +# category 2's own assembly further down. Moved up from its old position (immediately +# before category 3) so category 2 can consume it too, per this task's brief: "reuse the +# same $historyResult.History/$historyResult.OldestVersion rather than recomputing it." +$historyResult = Get-PfbValueEnumHistory -SpecsDirectory $SpecsDirectory + +# Cmdlet name -> the absolute .ps1 file it's defined in, sourced from +# Get-PfbModuleCalledEndpoints's own scan (every Public/Private *.ps1 already parsed once +# for its literal Invoke-PfbApiRequest -Method/-Endpoint) rather than re-scanning +# -PublicDirectory again per gap -- avoids re-parsing potentially hundreds of files once +# per addable body-property gap (402 on the real tree). +$cmdletFile = [System.Collections.Generic.Dictionary[string, string]]::new() +foreach ($c in $calledEndpoints) { + if ($c.Resolved -and -not $cmdletFile.ContainsKey($c.Cmdlet)) { $cmdletFile[$c.Cmdlet] = $c.File } +} + +# File path -> Dictionary[cmdletName, FunctionDefinitionAst], parsed LAZILY and cached per +# FILE (not per cmdlet), so a file defining more than one function is only ever parsed once +# even if more than one of its cmdlets needs a target this run. +$fileFunctionAsts = [System.Collections.Generic.Dictionary[string, object]]::new() +function Get-PfbCmdletFunctionAst { + <# + .SYNOPSIS + Returns -Cmdlet's own FunctionDefinitionAst, parsing (and caching) its file on + first use. $null if -Cmdlet isn't in $cmdletFile or its file defines no function of + that exact name. + #> + param([string]$Cmdlet) + if (-not $cmdletFile.ContainsKey($Cmdlet)) { return $null } + $file = $cmdletFile[$Cmdlet] + if (-not $fileFunctionAsts.ContainsKey($file)) { + $tokens = $null; $parseErrors = $null + $ast = [System.Management.Automation.Language.Parser]::ParseFile($file, [ref]$tokens, [ref]$parseErrors) + $funcs = [System.Collections.Generic.Dictionary[string, object]]::new() + foreach ($f in $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true)) { + if (-not $funcs.ContainsKey($f.Name)) { $funcs[$f.Name] = $f } + } + $fileFunctionAsts[$file] = $funcs + } + $funcsForFile = $fileFunctionAsts[$file] + if ($funcsForFile.ContainsKey($Cmdlet)) { return $funcsForFile[$Cmdlet] } + return $null +} + +function ConvertTo-PfbRepoRelativePath { + <# + .SYNOPSIS + Rewrites an absolute cmdlet path to a forward-slashed path relative to $repoRoot. + .DESCRIPTION + Every path this report emits MUST be repo-relative. These artifacts are committed, so an + absolute path bakes the generating machine's directory layout into the repository: it + leaks a local filesystem structure, and it makes the committed file depend on WHERE it was + generated -- regenerating from a git worktree instead of the main checkout rewrites every + such line, producing hundreds of lines of diff churn that bury the real changes. + + Falls back to the forward-slashed absolute path when the file genuinely is not under + $repoRoot (a test pointing -PublicDirectory at a synthetic fixture tree), rather than + throwing a Substring range error. + #> + param([string]$Path) + + if (-not $Path) { return $Path } + if ($Path.StartsWith($repoRoot, [System.StringComparison]::OrdinalIgnoreCase)) { + return ($Path.Substring($repoRoot.Length + 1)) -replace '\\', '/' + } + return $Path -replace '\\', '/' +} + +function Get-PfbGapTarget { + <# + .SYNOPSIS + Builds the `target` insertion-point-coordinates record (decision 12) for one + addable missing-body-property gap, given the endpoint's -Cmdlets list. + .DESCRIPTION + When more than one cmdlet already calls the same endpoint (5 real cases today: GET + /arrays, GET /blades, PATCH /buckets, PATCH /file-systems, PATCH /realms), the + ALPHABETICALLY FIRST cmdlet name is picked as the single primary target, + deterministically -- `target` is one set of coordinates, not a list, per the + brief's shape, and alphabetical order needs no external state to reproduce. A human + should still check for sibling cmdlets on the same endpoint before assuming this is + the only place the field could be added. + Every field degrades gracefully (never throws) if the primary cmdlet's file can't + be located or its function can't be found -- both should be unreachable in practice + since -Cmdlets is sourced from the same Get-PfbModuleCalledEndpoints scan + $cmdletFile is built from, but a missing coordinate is far cheaper to hand a human + than a failed report generation. + .OUTPUTS + [ordered]@{ file; paramBlockLine; payloadVariable; assignmentStyle; hasAttributes } + #> + param([string[]]$Cmdlets) + + $primaryCmdlet = @($Cmdlets | Sort-Object)[0] + $file = if ($cmdletFile.ContainsKey($primaryCmdlet)) { $cmdletFile[$primaryCmdlet] } else { $null } + if (-not $file) { + return [ordered]@{ file = $null; paramBlockLine = $null; payloadVariable = $null; assignmentStyle = $null; hasAttributes = $null } + } + + $relativeFile = ConvertTo-PfbRepoRelativePath -Path $file + $funcAst = Get-PfbCmdletFunctionAst -Cmdlet $primaryCmdlet + if (-not $funcAst) { + return [ordered]@{ file = $relativeFile; paramBlockLine = $null; payloadVariable = $null; assignmentStyle = $null; hasAttributes = $null } + } + + $insertion = Get-PfbCmdletBodyInsertionTarget -FunctionAst $funcAst + if (-not $insertion) { + return [ordered]@{ file = $relativeFile; paramBlockLine = $null; payloadVariable = $null; assignmentStyle = $null; hasAttributes = $null } + } + + return [ordered]@{ + file = $relativeFile + paramBlockLine = $insertion.ParamBlockLine + payloadVariable = $insertion.PayloadVariable + assignmentStyle = $insertion.AssignmentStyle + hasAttributes = $insertion.HasAttributes + } +} + +# --- Category 1 --- +# The outer @(...) wraps the WHOLE pipeline (input AND ForEach-Object projection), not +# just the input side -- assigning a pipeline's output straight to a variable silently +# unwraps a single-item result to a bare scalar/hashtable in PowerShell (confirmed live: +# @(1) | ForEach-Object { [ordered]@{a=1} } assigns a bare OrderedDictionary, not a +# one-element array). Wrapping only the input (e.g. "@(Get-Foo) | ForEach-Object {...}") +# does NOT protect the assignment -- the collapse happens on the pipeline's OUTPUT, so +# the @(...) must enclose the entire right-hand side. Without this, a category with +# exactly one row serializes as a bare JSON object instead of a one-element JSON array, +# silently breaking the manifest's documented array-typed schema for that category. +$uncoveredEndpoints = @(Get-PfbEndpointCoverageGaps -CapabilityMap $capabilityMap -CalledEndpoints $calledEndpoints -BespokeAllowlist $script:PfbBespokeAuthEndpoints -SinceVersion $SinceVersion | + ForEach-Object { [ordered]@{ endpoint = $_.Endpoint; minVersion = $_.MinVersion } }) + +# --- Category 2 --- +# No more notVerifiedEndpoints bucket: Get-PfbParameterCoverageGaps always computes +# gaps now and carries per-parameter confidence on each row instead (design decision 5). +# +# Task 5: a 'high'-confidence endpoint's MissingBodyProperties entries are enriched into +# full records (name/type/format/specRequired/synopsis/suggestedPowerShellType/enumValues/ +# enumStatus/target) -- see this script's own .NOTES above and the comment above +# Get-PfbBodyPropertyEnrichment in tools/lib/PfbApiDriftTools.ps1 for why 'partial'- +# confidence endpoints are deliberately left as bare strings instead. $canEnrichBodyProperties +# is false only when the capability map carries no analysed spec version at all (no +# $newestSpec loaded above) -- an edge case that shouldn't occur on a real capability map, +# but degrading to the pre-enrichment bare-string shape is safer than throwing. +$canEnrichBodyProperties = [bool]$newestSpec + +# Task 6: $script:PfbNonActionableParameters no longer carries continuation_token by hand +# -- Get-PfbNonActionableParameters derives it (plus the still-hardcoded X-Request-ID/ +# offset, neither of which has any Private/ injection site) from a live AST scan of +# -PrivateDirectory, so a future change to how Invoke-PfbApiRequest.ps1 injects +# continuation_token is reflected here automatically instead of silently going stale. +$nonActionableParameters = Get-PfbNonActionableParameters -PrivateDirectory $PrivateDirectory + +# Task 7: docs/drift-annotations.json -- Get-PfbDriftAnnotations returns $null gracefully +# when the file is absent (never throws), so every Find-PfbDriftAnnotation lookup below +# degrades to "no annotations" rather than failing report generation. +$driftAnnotationsPath = Join-Path $repoRoot 'docs/drift-annotations.json' +$driftAnnotations = Get-PfbDriftAnnotations -Path $driftAnnotationsPath + +# Captured as its own variable rather than piped straight into the ForEach-Object +# projection below -- Task 6's Get-PfbSystemicGaps wiring and this task's own phantom- +# field transparency count (further down) both need the SAME raw, pre-projection gap +# objects Get-PfbParameterCoverageGaps returns. Calling the function twice for two +# different purposes would risk the two views drifting apart if -SinceVersion/ +# -ExcludedFields ever diverged between call sites; capturing once and reusing avoids +# that entirely. +$parameterGapsRaw = @(Get-PfbParameterCoverageGaps -CapabilityMap $capabilityMap -CmdletInventory $inventory -CalledEndpoints $calledEndpoints -SinceVersion $SinceVersion -ExcludedFields $nonActionableParameters -CurrentSpecCapabilities $currentSpecCapabilities) + +$parameterGaps = @($parameterGapsRaw | ForEach-Object { + $gapRaw = $_ + $endpointParts = $gapRaw.Endpoint -split ' ', 2 + $method = $endpointParts[0] + $bareEndpoint = $endpointParts[1].TrimStart('/') + + $currentCap = $null + if ($currentByEndpoint.ContainsKey($gapRaw.Endpoint)) { $currentCap = $currentByEndpoint[$gapRaw.Endpoint] } + + # @(...) wraps the WHOLE if/else, not just each branch's own inner @(...) -- the + # same "assigning a statement's output straight to a variable silently unwraps an + # EMPTY result to something that behaves like neither a real $null nor a real + # empty array" hazard already documented elsewhere in this file (see + # tools/lib/PfbApiDriftTools.ps1's $readOnlyList comment). Confirmed live: without + # this outer wrap, a 'high'-confidence endpoint with an EMPTY MissingBodyProperties + # (e.g. a query-only gap like DELETE /active-directory) produced a per-endpoint + # value that read as 0 elements in-memory but round-tripped through + # ConvertTo-Json/ConvertFrom-Json into a ONE-element array containing a single + # $null (a phantom "field" with every property blank) -- silently inflating the + # real 402-gap high-confidence addable total to 682 in the committed JSON, entirely + # invisible to a same-process check that never serializes. Caught only by + # serializing the real manifest and re-counting from the JSON on disk, exactly like + # a real consumer would read it -- an in-memory-only check would have missed it. + $missingBodyProperties = @( + if ($gapRaw.Confidence.Level -eq 'high' -and $canEnrichBodyProperties) { + $gapRaw.MissingBodyProperties | ForEach-Object { + $fieldName = $_ + $detail = $null + if ($currentCap) { $detail = @($currentCap.BodyPropertyDetails) | Where-Object { $_.Name -eq $fieldName } | Select-Object -First 1 } + + $type = if ($detail) { $detail.Type } else { $null } + $format = if ($detail) { $detail.Format } else { $null } + $specRequired = if ($detail) { [bool]$detail.Required } else { $false } + $ownerSchema = if ($detail) { $detail.OwnerSchema } else { $null } + + $enrichment = Get-PfbBodyPropertyEnrichment -FieldName $fieldName -Type $type -Format $format -OwnerSchema $ownerSchema ` + -Spec $newestSpec -Endpoint $bareEndpoint -Method $method -History $historyResult.History -OldestVersion $historyResult.OldestVersion + + [ordered]@{ + name = $fieldName + type = $type + format = $format + specRequired = $specRequired + synopsis = $enrichment.Synopsis + suggestedPowerShellType = $enrichment.SuggestedPowerShellType + enumValues = @($enrichment.EnumValues) + enumStatus = $enrichment.EnumStatus + target = (Get-PfbGapTarget -Cmdlets $gapRaw.Cmdlets) + } + } + } + else { + $gapRaw.MissingBodyProperties + } + ) + + [ordered]@{ + endpoint = $gapRaw.Endpoint + cmdlets = @($gapRaw.Cmdlets) + missingQueryParameters = @($gapRaw.MissingQueryParameters) + missingBodyProperties = $missingBodyProperties + readOnlyFields = @($gapRaw.ReadOnlyFields) + confidence = [ordered]@{ + level = $gapRaw.Confidence.Level + unresolvedParameters = @($gapRaw.Confidence.UnresolvedParameters | ForEach-Object { + # Repo-relative, same as target.file: these artifacts are committed, so an + # absolute path would leak the generating machine's layout and churn the + # diff whenever the report is regenerated from a different directory. + [ordered]@{ parameter = $_.Parameter; surface = $_.Surface; file = (ConvertTo-PfbRepoRelativePath -Path $_.File); line = $_.Line } + }) + escapeHatchOnly = @($gapRaw.Confidence.EscapeHatchOnly) + caveat = $gapRaw.Confidence.Caveat + } + # Task 7: endpoint-matched entries from docs/drift-annotations.json (e.g. the + # management-access-policies POST/PATCH/DELETE-403 liveTestingHazard note) -- + # matchType 'field' annotations are surfaced separately, per-name, on + # `systemicGaps` below (Find-PfbDriftAnnotation -FieldName), not here. + annotations = @(Find-PfbDriftAnnotation -Annotations $driftAnnotations -Endpoint $gapRaw.Endpoint | ForEach-Object { + [ordered]@{ matchType = $_.matchType; match = $_.match; kind = $_.kind; note = $_.note; reference = $_.reference } + }) + } + }) + +# --- Task 6 wiring: systemic gaps + convention strength (this task's own job) --- +# Aggregated ONLY over 'high'-confidence gaps: a 'partial'-confidence endpoint's gap lists +# can contain false positives (an unresolved parameter may already cover the apparent gap +# through a path this AST-only inventory can't see -- see Get-PfbParameterCoverageGaps's +# own Confidence.Caveat), so folding it into a systemic FINDING would overstate a +# confidence the endpoint's own row already warns against. This mirrors the same +# high-confidence-only filter Tests/PfbApiDriftTools.Tests.ps1's 'Task 6 real-data +# invariants' Describe block applies before calling Get-PfbSystemicGaps, so the two stay +# in sync -- that Describe block recounts EndpointCount independently rather than pinning +# an exact figure (see docs/superpowers/plans/2026-07-30-drift-report-acceptance-figure-invariants.md). +$highConfidenceGapsRaw = @($parameterGapsRaw | Where-Object { $_.Confidence.Level -eq 'high' }) +$systemicGapsRaw = @(Get-PfbSystemicGaps -Gaps $highConfidenceGapsRaw) + +$systemicGaps = @($systemicGapsRaw | ForEach-Object { + $finding = $_ + [ordered]@{ + name = $finding.Name + endpointCount = $finding.EndpointCount + queryEndpointCount = $finding.QueryEndpointCount + bodyEndpointCount = $finding.BodyEndpointCount + endpoints = @($finding.Endpoints) + # Task 6's acceptance criterion: "the systemic section shows context_names at + # its endpoint count with its annotation" -- matchType 'field' lookup by this + # finding's own Name (never $null; Find-PfbDriftAnnotation always returns []). + annotations = @(Find-PfbDriftAnnotation -Annotations $driftAnnotations -FieldName $finding.Name | ForEach-Object { + [ordered]@{ matchType = $_.matchType; match = $_.match; kind = $_.kind; note = $_.note; reference = $_.reference } + }) + } + }) + +# No cap on -Names: Get-PfbConventionStrength is a cheap per-name dictionary lookup against +# a table built ONCE (Get-PfbWireNameCmdletCounts), so every systemic-gap name gets ranked +# here -- nothing is silently dropped from this list. Re-sorted by CmdletCount descending +# (Get-PfbConventionStrength itself preserves -Names' input order, ranking is this script's +# own choice) since a mechanical batch-fix candidate (high CmdletCount, e.g. `names` at +# 306) and an architectural gap (zero CmdletCount, e.g. `context_names`) are the two ends +# of this ranking a reader most wants surfaced first/last. +$conventionStrengthRaw = @(if ($systemicGapsRaw.Count -gt 0) { Get-PfbConventionStrength -CmdletInventory $inventory -Names @($systemicGapsRaw.Name) } else { @() }) +$conventionStrength = @($conventionStrengthRaw | Sort-Object -Property @{ Expression = 'CmdletCount'; Descending = $true }, Name | + ForEach-Object { [ordered]@{ name = $_.Name; cmdletCount = $_.CmdletCount; cmdlets = @($_.Cmdlets) } }) + +# --- Task 7: phantom-field transparency --- +# How many (endpoint, field) pairs were silently dropped from every gap list because +# Get-PfbParameterCoverageGaps's own -CurrentSpecCapabilities phantom-field exclusion (see +# that function's own .PARAMETER help) found them accumulated in the capability map but +# absent from the newest ANALYSED spec (fb$newestAnalysedVersion.json) -- e.g. a field +# withdrawn from the API after the version that first added it. Computed by calling the +# SAME function a second time WITHOUT -CurrentSpecCapabilities (its own documented "safe +# no-op default", i.e. no phantom filtering) and diffing the resulting (endpoint, list, +# field) triples against $parameterGapsRaw (the real, phantom-filtered run) -- never a +# re-derivation of the phantom-detection logic itself, so this can't silently drift from +# what Get-PfbParameterCoverageGaps actually does. +function Get-PfbGapFieldTripleSet { + <# + .SYNOPSIS + Internal helper: every "||" triple across -Gaps (one of + Get-PfbParameterCoverageGaps's own raw outputs), used only to diff two runs of that + function (with vs. without -CurrentSpecCapabilities) for the phantom-field count + below. Not a general-purpose utility -- deliberately local to this script. + #> + param([object[]]$Gaps) + $set = [System.Collections.Generic.HashSet[string]]::new() + foreach ($g in $Gaps) { + foreach ($f in @($g.MissingQueryParameters)) { [void]$set.Add("$($g.Endpoint)|query|$f") } + foreach ($f in @($g.MissingBodyProperties)) { [void]$set.Add("$($g.Endpoint)|body|$f") } + foreach ($f in @($g.ReadOnlyFields)) { [void]$set.Add("$($g.Endpoint)|readOnly|$f") } + } + return $set +} +$parameterGapsRawUnfiltered = @(Get-PfbParameterCoverageGaps -CapabilityMap $capabilityMap -CmdletInventory $inventory -CalledEndpoints $calledEndpoints -SinceVersion $SinceVersion -ExcludedFields $nonActionableParameters) +$phantomFilteredTripleSet = Get-PfbGapFieldTripleSet -Gaps $parameterGapsRaw +$phantomUnfilteredTripleSet = Get-PfbGapFieldTripleSet -Gaps $parameterGapsRawUnfiltered +$phantomFieldCount = @($phantomUnfilteredTripleSet | Where-Object { -not $phantomFilteredTripleSet.Contains($_) }).Count + +# --- Task 7: generatedFrom split --- +# `generatedFrom` over-claimed what the report actually analysed: it was populated from the +# tools/specs/ directory scan (Get-PfbValueEnumHistory's own $historyResult.ProcessedVersions), +# not from Data/PfbCapabilityMap.json's OWN generatedFrom -- the versions that actually drive +# every gap/phantom-field category above. The two are normally in step, which is why this +# was invisible, but they are independent inputs that drift the moment specs are refreshed +# without rebuilding the map (verified 2026-07-25 on bdf9d67: specs at 2.0-2.28, map still at +# 2.0-2.27 -- the report would have claimed 2.28 coverage while analysing nothing from it). +# Emitted as two separate, distinctly-named keys instead of one ambiguous `generatedFrom`: +# - analysedVersions: $CapabilityMap.generatedFrom -- what every gap category (and +# the phantom-field filter above) is actually scoped against. +# - availableSpecVersions: $historyResult.ProcessedVersions -- every spec on disk under +# -SpecsDirectory, which Task 5's enum join and category 3 (ValidateSet drift) DO read +# fresher-than-analysedVersions data from (see $historyResult's own construction +# comment above) -- so this fact stays load-bearing, never silently dropped. +$analysedVersions = @($capabilityMap.generatedFrom) +$availableSpecVersions = @($historyResult.ProcessedVersions) +$versionDiffCount = @(Compare-Object -ReferenceObject $analysedVersions -DifferenceObject $availableSpecVersions).Count +$versionSetsDiverge = $versionDiffCount -gt 0 +$versionDivergenceWarning = if ($versionSetsDiverge) { + "analysedVersions ($($analysedVersions.Count) versions, through $($analysedVersions[-1])) and availableSpecVersions ($($availableSpecVersions.Count) versions, through $($availableSpecVersions[-1])) disagree -- this is expected while Data/PfbCapabilityMap.json is deliberately pinned to a specific REST version; rebuild it (tools/Build-PfbCapabilityMap.ps1) only when intentionally adopting the newer spec. Every gap/phantom-field/systemic-gap category in this report is scoped to analysedVersions; validateSetDrift and newValidateSetCandidates (Task 5's enum join) use availableSpecVersions, the fresher on-disk set." +} +else { $null } + +# --- Category 3 --- +# $historyResult was computed earlier (before category 1) so category 2's enrichment above +# and this category both reuse the SAME Get-PfbValueEnumHistory result -- two logically +# separate report categories (existing-ValidateSet drift here vs. this task's new enum +# join above), never merged or confused, just sharing one expensive spec re-scan. +$validateSetDrift = @(Get-PfbValidateSetDrift -CmdletInventory $inventory -History $historyResult.History -OldestVersion $historyResult.OldestVersion | + ForEach-Object { + [ordered]@{ + cmdlet = $_.Cmdlet + parameter = $_.Parameter + currentValidateSet = @($_.CurrentValidateSet) + specValues = @($_.SpecValues) + missingValues = @($_.MissingValues) + staleValues = @($_.StaleValues) + } + }) + +# --- Category 4: pass Build-PfbFieldCmdletMap.ps1's 'matched' entries straight through --- +$newValidateSetCandidates = @($fieldCmdletMap.entries | Where-Object { $_.status -eq 'matched' } | + ForEach-Object { [ordered]@{ cmdlet = $_.cmdlet; parameter = $_.parameter; wireName = $_.wireName; specValues = $_.specValues; recommendation = $_.recommendation } }) + +# --- Category 5: spec-vs-module assumption (Fusion context_names cardinality rule) --- +# Categorically different from categories 1-4. Those ask "does the module cover what the +# API offers?"; this one asks "is an assumption baked into the module still true?". The +# module's cardinality rule is EXECUTED from its single declared home +# (Private/Test-PfbContextMultiValueCapable.ps1, dot-sourced by PfbContextRuleTools.ps1), +# never re-derived here -- a check that re-implements its own rule verifies nothing. +# +# The parameter signals come from the capability map, whose component data is +# last-seen-wins across analysedVersions, i.e. it describes $newestAnalysedVersion's wire +# shape. The HTTP 207 signal CANNOT come from the map -- it records the request surface +# only, with no response data at all -- so it is read from that same version's spec, keeping +# both halves on one version. When no spec is available the 207 signal is omitted entirely +# rather than defaulted: Get-PfbContextParameterFact carries an unknown 207 as $null and +# excludes it from comparison, so a missing spec narrows what this category can see but +# never makes it report something false. +# Plain if-statements with direct assignment, NOT `$x = if (...) { @() }`: an empty array +# emitted as the value of a scriptblock collapses to $null, which here would silently +# convert "checked the spec, no endpoint declares 207" into "207 was never checked", and +# would hand a null $contextFacts to functions that require a collection. +$context207Endpoints = $null +if ($newestSpec) { + $context207Endpoints = [string[]]@(Get-PfbContextHttp207Endpoint -Spec $newestSpec) +} +$contextFacts = @() +if ($null -ne $context207Endpoints) { + $contextFacts = @(Get-PfbContextParameterFact -CapabilityMap $capabilityMap -Http207Endpoint $context207Endpoints) +} +else { + $contextFacts = @(Get-PfbContextParameterFact -CapabilityMap $capabilityMap) +} +$contextSignalDisagreements = @(Get-PfbContextSignalDisagreement -Fact $contextFacts | + ForEach-Object { + [ordered]@{ + endpoint = $_.Endpoint + method = $_.Method + contextComponent = $_.ContextComponent + shape = $_.Shape + ruleSaysMultiValue = $_.RuleSaysMultiValue + componentSaysMultiValue = $_.ComponentSaysMultiValue + declaresAllowErrors = $_.DeclaresAllowErrors + declaresHttp207 = $_.DeclaresHttp207 + multiValueSignals = @($_.MultiValueSignals) + sizeOneSignals = @($_.SizeOneSignals) + summary = $_.Summary + } + }) +# A NAMED SUBSET of the above, never an additional category -- see the function's help. +$contextSizeOneWithAllowErrors = @(Get-PfbContextSizeOneWithAllowErrors -Fact $contextFacts | + ForEach-Object { [ordered]@{ endpoint = $_.Endpoint; method = $_.Method; contextComponent = $_.ContextComponent } }) +$contextUnresolvedComponents = @(Get-PfbContextUnresolvedComponent -Fact $contextFacts | + ForEach-Object { [ordered]@{ endpoint = $_.Endpoint; method = $_.Method } }) + +$manifest = [ordered]@{ + schemaVersion = 1 + # See the generatedFrom-split block above: two distinctly-named keys instead of one + # ambiguous `generatedFrom`, plus a warning (never $null-vs-absent-silent) when they + # disagree. + analysedVersions = $analysedVersions + availableSpecVersions = $availableSpecVersions + versionDivergenceWarning = $versionDivergenceWarning + sinceVersion = if ($SinceVersion) { $SinceVersion } else { $null } + phantomFieldCount = $phantomFieldCount + uncoveredEndpoints = $uncoveredEndpoints + parameterGaps = $parameterGaps + systemicGaps = $systemicGaps + conventionStrength = $conventionStrength + validateSetDrift = $validateSetDrift + newValidateSetCandidates = $newValidateSetCandidates + responseFieldRemovals = @($responseFindings.Removals | ForEach-Object { + [ordered]@{ + endpoint = $_.Endpoint + field = $_.Field + location = $_.Location + introducedVersion = $_.IntroducedVersion + lastSeenVersion = $_.LastSeenVersion + } + }) + responseFieldRenameCandidates = @($responseFindings.RenameCandidates | ForEach-Object { + [ordered]@{ + endpoint = $_.Endpoint + location = $_.Location + from = $_.From + to = $_.To + version = $_.Version + } + }) + unhandledResponseEnvelopeFields = @($responseFindings.UnhandledEnvelopeFields | ForEach-Object { + [ordered]@{ field = $_.Field; endpointCount = $_.EndpointCount } + }) + contextCardinality = [ordered]@{ + # The version this category's numbers describe -- never omit it, see the block + # where these are computed. + componentSourceVersion = $newestAnalysedVersion + # $null (not $false) when no spec was available to read responses from, so a + # consumer can tell "no endpoint declares 207" from "207 was never checked". + http207SignalAvailable = ($null -ne $context207Endpoints) + endpointsWithContextNames = $contextFacts.Count + multiContextCapable = @($contextFacts | Where-Object { $_.RuleSaysMultiValue }).Count + signalDisagreements = $contextSignalDisagreements + # Named subset of signalDisagreements, NOT an additional finding. + sizeOneWithAllowErrors = $contextSizeOneWithAllowErrors + unresolvedComponents = $contextUnresolvedComponents + } +} + +$outputDir = Split-Path -Parent $OutputPath +if (-not (Test-Path $outputDir)) { New-Item -ItemType Directory -Path $outputDir -Force | Out-Null } +$manifest | ConvertTo-Json -Depth 20 | Set-Content -Path $OutputPath -Encoding UTF8 + +# --- Markdown report --- +# Section order (this task's brief): How to read this report -> Summary -> Systemic gaps +# -> Parameter gaps (query/body as separate columns) -> Read-only fields (not addressable) +# -> Uncovered endpoints -> ValidateSet drift -> New ValidateSet candidates. +$mdLines = [System.Collections.Generic.List[string]]::new() +$mdLines.Add('# API Drift Report') +$mdLines.Add('') +$mdLines.Add("Generated by ``tools/Build-PfbApiDriftReport.ps1`` ($($analysedVersions.Count) analysed REST versions; $($availableSpecVersions.Count) available on disk under ``tools/specs/``).") +$mdLines.Add('') +$mdLines.Add('Reporting only -- no `Public/` cmdlet is edited by this script.') +$mdLines.Add('') +if ($SinceVersion) { + $mdLines.Add("Uncovered endpoints and parameter gaps are filtered to items introduced after REST $SinceVersion. ValidateSet drift and new ValidateSet candidates are not filtered (no per-value introduced-version data to filter on).") + $mdLines.Add('') +} +if ($versionSetsDiverge) { + $mdLines.Add("**Warning:** $versionDivergenceWarning") + $mdLines.Add('') +} +$partialConfidenceCount = @($parameterGaps | Where-Object { $_.confidence.level -eq 'partial' }).Count + +# "How to read this report" -- the decision-6 false-positive procedure, verbatim (see +# docs/superpowers/plans/drift-report-readonly-deprecated-fix-brief.md lines 249-283). +$mdLines.Add('## How to read this report') +$mdLines.Add('') +$mdLines.Add('This report accepts **false positives in order to eliminate false negatives**. A field is listed as missing even though the module can already set it, when a parameter covering it could not be traced to a wire name.') +$mdLines.Add('') +$mdLines.Add('**Detection.** Only possible where `confidence.level` is `partial`. When it is `high` (`unresolvedParameters` empty), the row carries no false-positive risk from this mechanism.') +$mdLines.Add('') +$mdLines.Add('**Resolution procedure:**') +$mdLines.Add('1. Open the named parameter at the given `file:line` and follow where its value goes.') +$mdLines.Add('2. If it reaches the wire under the same name as the reported gap -> the gap is a false positive AND a tooling bug: the parser does not recognise that idiom. File it as a parser gap and fix the parser.') +$mdLines.Add('3. If it reaches the wire under a different name -> the reported gap may still be real; check that field against the spec.') +$mdLines.Add('4. If it never reaches the wire -> the gap is real.') +$mdLines.Add('') +$mdLines.Add('**Why this trade is right:** a false positive costs a reader one `file:line` lookup; a false negative costs an undetected gap indefinitely. Every false positive here is a parser-gap detector -- it either fixes the tool permanently for every endpoint, or confirms a real gap.') + +$mdLines.Add(''); $mdLines.Add('## Summary'); $mdLines.Add('') +$mdLines.Add("- Uncovered endpoints: $($uncoveredEndpoints.Count)") +$mdLines.Add("- Endpoints with parameter gaps: $($parameterGaps.Count)") +$mdLines.Add("- Missing body properties (addable): $((@($parameterGaps.missingBodyProperties) | Measure-Object).Count)") +$mdLines.Add("- Missing query parameters (addable): $((@($parameterGaps.missingQueryParameters) | Measure-Object).Count)") +$mdLines.Add("- Read-only body fields (not addable -- see the Read-only fields section below): $((@($parameterGaps.readOnlyFields) | Measure-Object).Count)") +$mdLines.Add("- Phantom fields silently excluded (accumulated in the capability map, absent from the newest analysed spec): $phantomFieldCount") +$mdLines.Add("- Partial-confidence endpoints (see ``How to read this report`` above, and each row's marker in the Parameter gaps table): $partialConfidenceCount") +$mdLines.Add("- Systemic gaps (distinct field names collapsed across high-confidence endpoints, detailed below): $($systemicGaps.Count)") +$mdLines.Add("- ValidateSet drift: $($validateSetDrift.Count)") +$mdLines.Add("- New ValidateSet candidates: $($newValidateSetCandidates.Count)") +$mdLines.Add("- Context cardinality signal disagreements (fb$newestAnalysedVersion): $($contextSignalDisagreements.Count)") +$mdLines.Add(('- Removed response fields: **{0}**' -f @($responseFindings.Removals).Count)) +$mdLines.Add(('- Response rename candidates: **{0}**' -f @($responseFindings.RenameCandidates).Count)) +$mdLines.Add(('- Envelope fields not read by `Invoke-PfbApiRequest`: **{0}**' -f @($responseFindings.UnhandledEnvelopeFields).Count)) + +if ($systemicGaps.Count -gt 0) { + $mdLines.Add(''); $mdLines.Add('## Systemic gaps'); $mdLines.Add('') + $mdLines.Add('One finding per distinct wire field name, collapsed across every endpoint where a high-confidence gap exists (decision 7) -- turns hundreds of per-endpoint rows into a handful of real, actionable decisions. "Cmdlets already using this name" is decision 8''s convention-strength ranking: a high count means closing the remaining gaps for this name is a mechanical batch fix; zero means no established convention exists to extend at all -- closing it is an architectural decision, not a mechanical one.') + $mdLines.Add('') + $systemicTopN = 25 + $systemicShown = @($systemicGaps | Select-Object -First $systemicTopN) + if ($systemicGaps.Count -gt $systemicTopN) { + $mdLines.Add("Showing the top $systemicTopN of $($systemicGaps.Count) findings by endpoint count -- the full list is in the JSON manifest's ``systemicGaps``, nothing is dropped there.") + $mdLines.Add('') + } + $mdLines.Add('| Field name | Endpoints | Query | Body | Cmdlets already using this name | Annotation |'); $mdLines.Add('|---|---|---|---|---|---|') + foreach ($s in $systemicShown) { + $strength = $conventionStrength | Where-Object { $_.name -eq $s.name } | Select-Object -First 1 + $cmdletCount = if ($strength) { $strength.cmdletCount } else { 0 } + $annotationNote = if (@($s.annotations).Count -gt 0) { ($s.annotations | ForEach-Object { $_.note }) -join '; ' } else { '' } + $mdLines.Add("| ``$($s.name)`` | $($s.endpointCount) | $($s.queryEndpointCount) | $($s.bodyEndpointCount) | $cmdletCount | $annotationNote |") + } +} + +if ($parameterGaps.Count -gt 0) { + $mdLines.Add(''); $mdLines.Add('## Parameter gaps'); $mdLines.Add('') + $mdLines.Add('Endpoints an existing cmdlet already calls, where the capability map knows of a query parameter or addable body property the cmdlet does not yet expose. Read-only fields (never addable, regardless of confidence) are reported separately below, never blended into either column here.') + $mdLines.Add('') + $mdLines.Add('| Endpoint | Cmdlets | Missing query parameters | Missing body properties | Confidence | Notes |'); $mdLines.Add('|---|---|---|---|---|---|') + foreach ($g in $parameterGaps) { + # missingBodyProperties is a MIX of bare strings (partial-confidence endpoints, + # unchanged from before this task) and enriched [ordered]@{} records (high-confidence + # endpoints, this task) -- $_.name on a bare string returns $null, so the ?? falls + # back to the string itself; on an enriched record it reads the 'name' key. Either + # way the Markdown table shows just the field name, never a stringified hashtable. + $bodyPropNames = ($g.missingBodyProperties | ForEach-Object { if ($_ -is [string]) { $_ } else { $_.name } }) -join ', ' + $confidenceCell = if ($g.confidence.level -eq 'partial') { + $unresolvedCount = @($g.confidence.unresolvedParameters).Count + $plural = if ($unresolvedCount -eq 1) { '' } else { 's' } + "``partial`` -- /!\ $unresolvedCount unresolved param$plural (see Partial-confidence detail below)" + } + else { '`high`' } + $notes = if (@($g.annotations).Count -gt 0) { ($g.annotations | ForEach-Object { $_.note }) -join '; ' } else { '' } + $mdLines.Add("| ``$($g.endpoint)`` | $($g.cmdlets -join ', ') | $($g.missingQueryParameters -join ', ') | $bodyPropNames | $confidenceCell | $notes |") + } + + $partialGaps = @($parameterGaps | Where-Object { $_.confidence.level -eq 'partial' }) + if ($partialGaps.Count -gt 0) { + $mdLines.Add(''); $mdLines.Add('### Partial-confidence detail'); $mdLines.Add('') + $mdLines.Add('Per the decision-6 procedure above: open each parameter at its `file:line` and follow where its value goes.') + $mdLines.Add('') + $mdLines.Add('| Endpoint | Parameter | Surface | File:Line | Caveat |'); $mdLines.Add('|---|---|---|---|---|') + foreach ($g in $partialGaps) { + foreach ($u in $g.confidence.unresolvedParameters) { + $mdLines.Add("| ``$($g.endpoint)`` | ``-$($u.parameter)`` | $($u.surface) | ``$($u.file):$($u.line)`` | $($g.confidence.caveat) |") + } + } + } +} + +$readOnlyRows = @($parameterGaps | Where-Object { @($_.readOnlyFields).Count -gt 0 }) +if ($readOnlyRows.Count -gt 0) { + $mdLines.Add(''); $mdLines.Add('## Read-only fields (not addressable)'); $mdLines.Add('') + $mdLines.Add('The capability map knows these body properties exist, but the newest analysed spec marks them read-only -- no `Public/` cmdlet can ever set them, on any confidence level. Listed for completeness only; never merged into the Parameter gaps table above.') + $mdLines.Add('') + $mdLines.Add('| Endpoint | Cmdlets | Read-only fields |'); $mdLines.Add('|---|---|---|') + foreach ($g in $readOnlyRows) { $mdLines.Add("| ``$($g.endpoint)`` | $($g.cmdlets -join ', ') | $($g.readOnlyFields -join ', ') |") } +} + +$mdLines.Add(''); $mdLines.Add('## Response-shape drift'); $mdLines.Add('') +if (-not $responseShapeMapPresent) { + $mdLines.Add('> **Not analysed.** `Data/PfbResponseShapeMap.json` was not found when this report was generated. Run `tools/Build-PfbResponseShapeMap.ps1`. The absence of findings below does *not* mean there is no response drift.') +} +else { + $mdLines.Add('Cmdlets pass API responses through raw -- there is no projection anywhere in `Public/`, so the module holds no schema of its own. A **removed or renamed** response field therefore reaches user scripts as a silent `$null`, which no other report category can detect. **Added** response fields are deliberately not reported: they surface automatically and need no module change.') + $mdLines.Add('') + + $mdLines.Add('### Removed response fields') + $mdLines.Add('') + if (@($responseFindings.Removals).Count -eq 0) { + $mdLines.Add('_None._') + } + else { + $mdLines.Add('| Endpoint | Location | Field | Introduced | Last seen |') + $mdLines.Add('|---|---|---|---|---|') + foreach ($r in $responseFindings.Removals) { + $mdLines.Add(('| `{0}` | {1} | `{2}` | {3} | {4} |' -f $r.Endpoint, $r.Location, $r.Field, $r.IntroducedVersion, $r.LastSeenVersion)) + } + } + $mdLines.Add('') + + $mdLines.Add('### Rename candidates') + $mdLines.Add('') + $mdLines.Add('_A removal and an addition adjacent in version on the same endpoint. Suggestive, not proof -- confirm against the spec before acting._') + $mdLines.Add('') + if (@($responseFindings.RenameCandidates).Count -eq 0) { + $mdLines.Add('_None._') + } + else { + $mdLines.Add('| Endpoint | Location | From | To | Version |') + $mdLines.Add('|---|---|---|---|---|') + foreach ($r in $responseFindings.RenameCandidates) { + $mdLines.Add(('| `{0}` | {1} | `{2}` | `{3}` | {4} |' -f $r.Endpoint, $r.Location, $r.From, $r.To, $r.Version)) + } + } + $mdLines.Add('') + + $mdLines.Add('### Response envelope fields not read by `Invoke-PfbApiRequest`') + $mdLines.Add('') + $mdLines.Add('_Informational coverage observation, not a correctness claim -- many envelope keys legitimately need no handling there. It cannot tell whether a field is handled correctly, only whether it is referenced at all._') + $mdLines.Add('') + if (@($responseFindings.UnhandledEnvelopeFields).Count -eq 0) { + $mdLines.Add('_None._') + } + else { + $mdLines.Add('| Envelope field | Endpoints declaring it |') + $mdLines.Add('|---|---|') + foreach ($r in $responseFindings.UnhandledEnvelopeFields) { + $mdLines.Add(('| `{0}` | {1} |' -f $r.Field, $r.EndpointCount)) + } + } +} + +if ($uncoveredEndpoints.Count -gt 0) { + $mdLines.Add(''); $mdLines.Add('## Uncovered endpoints'); $mdLines.Add('') + $mdLines.Add('| Endpoint | Introduced in |'); $mdLines.Add('|---|---|') + foreach ($e in $uncoveredEndpoints) { $mdLines.Add("| ``$($e.endpoint)`` | $($e.minVersion) |") } +} +if ($validateSetDrift.Count -gt 0) { + $mdLines.Add(''); $mdLines.Add('## ValidateSet drift'); $mdLines.Add('') + $mdLines.Add('| Cmdlet | Parameter | Missing values | Stale values |'); $mdLines.Add('|---|---|---|---|') + foreach ($d in $validateSetDrift) { $mdLines.Add("| ``$($d.cmdlet)`` | ``-$($d.parameter)`` | $($d.missingValues -join ', ') | $($d.staleValues -join ', ') |") } +} +if ($newValidateSetCandidates.Count -gt 0) { + $mdLines.Add(''); $mdLines.Add('## New ValidateSet candidates'); $mdLines.Add('') + $mdLines.Add('| Cmdlet | Parameter | Spec values |'); $mdLines.Add('|---|---|---|') + foreach ($c in $newValidateSetCandidates) { $mdLines.Add("| ``$($c.cmdlet)`` | ``-$($c.parameter)`` | $($c.specValues -join ', ') |") } +} + +# Emitted UNCONDITIONALLY, unlike every section above -- for this category "nothing to +# report" is itself a finding, and a section that disappears when clean is +# indistinguishable from a section that never ran. +$mdLines.Add(''); $mdLines.Add('## Spec-vs-module assumptions: `context_names` cardinality'); $mdLines.Add('') +$mdLines.Add('Every category above asks *"does the module cover what the API offers?"*. This one asks a different question: *"is an assumption baked into the module still true?"*') +$mdLines.Add('') +$mdLines.Add('The module''s cardinality rule -- **an endpoint is multi-context-capable if and only if its `context_names` resolves to component `Context_names_get` AND the endpoint declares `allow_errors`** -- is declared in exactly one place, `Private/Test-PfbContextMultiValueCapable.ps1`, which this check *executes* rather than re-derives. The HTTP verb is not the rule; it survives only as a fallback for an endpoint absent from the capability map. See section 8 of `docs/design/fusion-context-injection.md`.') +$mdLines.Add('') +$mdLines.Add('This is a **cross-signal** check, not a rule-versus-spec one. The earlier two-signal shape was structurally unable to see the defect that actually exists: four fleet-scoped GETs reference the multi-value component *and* are size-1 on the wire, so the rule and the component agreed while both were wrong. Any endpoint whose available signals do not all agree is reported below, including the case where the component agrees with the rule but the remaining signals dissent.') +$mdLines.Add('') +$mdLines.Add("Measured against **fb$newestAnalysedVersion** (the newest analysed version; the capability map's component data is last-seen-wins, so it describes that version's wire shape). $($contextFacts.Count) endpoints declare ``context_names``, of which **$(@($contextFacts | Where-Object { $_.RuleSaysMultiValue }).Count)** satisfy the rule.") +$mdLines.Add('') +if ($null -ne $context207Endpoints) { + $mdLines.Add("The HTTP 207 signal is read from ``tools/specs/fb$newestAnalysedVersion.json`` -- the capability map records the request surface only and holds no response data, so 207 is a corroborating signal for this report and never a runtime gate.") +} +else { + $mdLines.Add('**The HTTP 207 signal was unavailable** (no spec on disk for the newest analysed version), so it was excluded from comparison rather than assumed absent. Findings below are based on the component and `allow_errors` signals only.') +} +$mdLines.Add('') +$mdLines.Add('**A disagreement is not automatically a spec defect.** The case worth catching is a genuine future multi-context endpoint, where the *module* would be the wrong side. A human decides which signal is wrong, each time.') +$mdLines.Add('') +if ($contextSignalDisagreements.Count -eq 0) { + $mdLines.Add("**No signal disagreements** across all $($contextFacts.Count) endpoints in fb$newestAnalysedVersion.") + $mdLines.Add('') + $mdLines.Add('Read this as *the available signals agree*, not as *the module is verified correct*. Signals agreeing is exactly the state the four fleet-scoped GETs were in under the previous two-signal check while being wrong on the wire. Only live testing settles behaviour; see Appendix A of the design doc for what has actually been probed.') +} +else { + $mdLines.Add("**$($contextSignalDisagreements.Count) endpoint(s) with disagreeing signals**, grouped by shape.") + $mdLines.Add('') + foreach ($shapeGroup in ($contextSignalDisagreements | Group-Object { $_.shape } | Sort-Object Name)) { + $mdLines.Add("#### ``$($shapeGroup.Name)`` ($($shapeGroup.Count))") + $mdLines.Add('') + switch ($shapeGroup.Name) { + 'component-says-multi-value-but-no-allow-errors' { + $mdLines.Add('The component claims fan-out while the endpoint offers no partial-failure story. Strong evidence of a spec defect: this is the shape of the four fleet-scoped GETs confirmed size-1 on the wire, and of `DELETE /management-access-policies` before its 2.28 correction.') + } + 'size-1-component-but-declares-allow-errors' { + $mdLines.Add('The mirror case -- a size-1 component on an endpoint that nonetheless declares `allow_errors`. Named again below.') + } + 'rule-says-capable-but-declares-no-207' { + $mdLines.Add('Component and `allow_errors` both say fan-out, but no HTTP 207 is declared, so partial failure has no documented response shape. Status genuinely unknown -- which is precisely why 207 is a corroborating signal here and never a runtime gate: treating these as size-1 would block calls that may well work.') + } + default { + $mdLines.Add('An endpoint declaring a partial-failure response while no other signal says it fans out.') + } + } + $mdLines.Add('') + $mdLines.Add('| Endpoint | Method | Component | Declares `allow_errors` | Declares 207 | Module rule |'); $mdLines.Add('|---|---|---|---|---|---|') + foreach ($d in $shapeGroup.Group) { + $aeCell = if ($d.declaresAllowErrors) { 'yes' } else { 'no' } + $c207Cell = if ($null -eq $d.declaresHttp207) { '_unknown_' } elseif ($d.declaresHttp207) { 'yes' } else { 'no' } + $ruleCell = if ($d.ruleSaysMultiValue) { 'multi-value' } else { 'size-1' } + $mdLines.Add("| ``$($d.endpoint)`` | $($d.method) | ``$($d.contextComponent)`` | $aeCell | $c207Cell | $ruleCell |") + } + $mdLines.Add('') + } +} + +# A NAMED SUBSET of the findings above, never an additional category -- reporting it as its +# own finding would double-count one defect as two. +$mdLines.Add('') +$mdLines.Add('### Named case: size-1 component declaring `allow_errors`') +$mdLines.Add('') +$mdLines.Add('A **subset** of the findings above (shape `size-1-component-but-declares-allow-errors`), not an additional finding. Called out by name because it is a known, characterized case.') +$mdLines.Add('') +if ($contextSizeOneWithAllowErrors.Count -eq 0) { $mdLines.Add('_None._') } +else { + foreach ($s in $contextSizeOneWithAllowErrors) { $mdLines.Add("- ``$($s.endpoint)``") } + $mdLines.Add('') + $mdLines.Add('Not live-verified: these carry mutating verbs and were not probed during the 2026-08-01 testing (Appendix A, "Not verified").') +} + +if ($contextUnresolvedComponents.Count -gt 0) { + $mdLines.Add('') + $mdLines.Add("### Unresolved `context_names` components ($($contextUnresolvedComponents.Count))") + $mdLines.Add('') + $mdLines.Add('These endpoints declare `context_names` with no resolvable `$ref` component, so their cardinality cannot be checked at all. They are **not** scored as size-1 -- doing so would hide a genuine multi-value endpoint behind the module''s size-1 throw.') + $mdLines.Add('') + foreach ($u in $contextUnresolvedComponents) { $mdLines.Add("- ``$($u.endpoint)``") } +} + +$mdLines.Add('') + +Set-Content -Path $ReportPath -Value ($mdLines -join "`n") -Encoding UTF8 +Write-Host "Wrote API drift report to $OutputPath and $ReportPath" -ForegroundColor Green diff --git a/tools/Build-PfbCapabilityMap.ps1 b/tools/Build-PfbCapabilityMap.ps1 new file mode 100644 index 0000000..b5c10bd --- /dev/null +++ b/tools/Build-PfbCapabilityMap.ps1 @@ -0,0 +1,332 @@ +#Requires -Version 7.0 +<# +.SYNOPSIS + Builds the FlashBlade API capability manifest from cached OpenAPI specs. +.DESCRIPTION + Loads every cached tools/specs/fb.json in ascending version order and + records, for each (HTTP method, normalized path), the earliest version it appears + in — and likewise for each parameter name and request-body top-level property name + on that endpoint. This is the data Phase 2's per-cmdlet capability check and Phase + 3's version-aware ArgumentCompleters will consume. + + Deliberately NOT included: per-enum-value "introduced in version X" tracking. The + FlashBlade OpenAPI spec has no structural JSON Schema `enum` anywhere (verified + against fb2.10 and fb2.27) — allowed values are documented only in free-text + `description` prose, which is not reliably machine-diffable. See + tools/lib/PfbSpecTools.ps1 for the full finding. + + Also NOT included (deferred, see plan): endpoint/field deprecation or removal + tracking, and hardware-model (//S vs //E) capability — that is a separate axis from + REST version and is handled in a later phase from a different data source. + + Each endpoint also carries, where non-empty, readOnlyBodyProperties and + deprecatedBodyProperties (string arrays) -- both last-seen-wins (the newest spec that + mentions the endpoint always wins), NOT first-sight like minVersion/parameters/ + bodyProperties, because readOnly is not monotonic across versions (a field can go + read-only -> writable, and about half the observed flips in fb2.0-2.27 do exactly + that; persisting first-sight would silently hide genuinely settable fields). + + parameterComponentDefaults/parameterComponentOverrides resolution contract (also + documented in tools/README.md): the component backing a given (endpoint, parameter) + is looked up as (1) the endpoint's parameterComponentOverrides value for that + parameter name if present -- which may be JSON null, meaning "this endpoint's + parameter has no component" -- otherwise (2) the top-level parameterComponentDefaults + value for that parameter name if present, otherwise (3) no known component. Split + into a global table plus per-endpoint overrides (rather than one full map per + endpoint) because the vast majority of parameters share a small set of common + components: measured across fb2.0-2.27, 4102 total (endpoint, parameter) -> component + pairs collapse to just 224 distinct pairs and 179 distinct parameter names. Defaults + are chosen by frequency (most common component per parameter name across all + endpoints), ties broken ALPHABETICALLY by component name for determinism regardless + of endpoint processing order. The explicit-null-override case matters: a parameter + declared inline (no "$ref", so no component) whose NAME happens to coincide with a + $ref'd component elsewhere must get an explicit null override, or "absent from + overrides" would wrongly make it inherit that unrelated default. +.PARAMETER SpecsDirectory + Where cached spec JSON files live. Defaults to tools/specs relative to this script. +.PARAMETER OutputPath + Where to write the manifest. Defaults to Data/PfbCapabilityMap.json relative to the + repo root (one level up from tools/). +.PARAMETER MaxVersion + Optional inclusive upper bound on REST version to ingest, e.g. '2.27'. Compared + numerically (Major, then Minor as integers) using the same parsing already used to + sort $specFiles below -- NOT a string compare, since '2.9' sorts above '2.27' as a + string. Defaults to $null, meaning no cap: every cached spec under -SpecsDirectory is + ingested (unchanged default behaviour, so CI is unaffected). Exists so a rebuild can be + pinned to a known spec set without deleting newer files out of tools/specs/ -- + adopting a newly-cached version's data is a separate, deliberate decision, not a side + effect of this script simply seeing a new file on disk. +.EXAMPLE + ./tools/Build-PfbCapabilityMap.ps1 +.EXAMPLE + ./tools/Build-PfbCapabilityMap.ps1 -MaxVersion 2.27 +#> +[CmdletBinding()] +param( + [string]$SpecsDirectory, + + [string]$OutputPath, + + [string]$MaxVersion +) + +$ErrorActionPreference = 'Stop' + +$scriptDir = $PSScriptRoot +. (Join-Path $scriptDir 'lib/PfbSpecTools.ps1') + +if (-not $SpecsDirectory) { + $SpecsDirectory = Join-Path $scriptDir 'specs' +} +if (-not $OutputPath) { + $repoRoot = Split-Path -Parent $scriptDir + $OutputPath = Join-Path (Join-Path $repoRoot 'Data') 'PfbCapabilityMap.json' +} + +$specFiles = Get-ChildItem -Path $SpecsDirectory -Filter 'fb*.json' -ErrorAction SilentlyContinue +if (-not $specFiles) { + throw "No cached specs found in '$SpecsDirectory'. Run Update-PfbApiSpecs.ps1 first." +} + +# Sort by numeric version, not filename string (fb2.9 must sort before fb2.10). +$specFiles = $specFiles | ForEach-Object { + if ($_.BaseName -match '^fb(\d+)\.(\d+)$') { + [PSCustomObject]@{ + File = $_ + Major = [int]$Matches[1] + Minor = [int]$Matches[2] + } + } + else { + Write-Warning "Skipping unrecognized spec filename: $($_.Name)" + $null + } +} | Where-Object { $_ } | Sort-Object Major, Minor + +if ($MaxVersion) { + if ($MaxVersion -notmatch '^(\d+)\.(\d+)$') { + throw "-MaxVersion must be in 'Major.Minor' form (e.g. '2.27'), got '$MaxVersion'." + } + # Reuse the same Major/Minor int parsing as the sort above -- a string compare would + # wrongly place e.g. '2.9' above '2.27' (this exact bug has been found twice already). + $capMajor = [int]$Matches[1] + $capMinor = [int]$Matches[2] + $beforeCount = @($specFiles).Count + $specFiles = @($specFiles | Where-Object { + $_.Major -lt $capMajor -or ($_.Major -eq $capMajor -and $_.Minor -le $capMinor) + }) + Write-Host "MaxVersion $MaxVersion applied: including $($specFiles.Count) of $beforeCount cached specs." -ForegroundColor Yellow +} + +$endpoints = [ordered]@{} +$processedVersions = [System.Collections.Generic.List[string]]::new() + +foreach ($entry in $specFiles) { + $version = "$($entry.Major).$($entry.Minor)" + Write-Host "Processing $version ($($entry.File.Name))..." -ForegroundColor Cyan + + $spec = Get-Content -Path $entry.File.FullName -Raw | ConvertFrom-Json -Depth 64 + $capabilities = Get-PfbSpecCapabilities -Spec $spec + + foreach ($cap in $capabilities) { + $epKey = "$($cap.Method) $($cap.Path)" + + if (-not $endpoints.Contains($epKey)) { + $endpoints[$epKey] = [ordered]@{ + minVersion = $version + parameters = [ordered]@{} + bodyProperties = [ordered]@{} + } + } + $entryRecord = $endpoints[$epKey] + + foreach ($paramName in $cap.Parameters) { + if (-not $entryRecord.parameters.Contains($paramName)) { + $entryRecord.parameters[$paramName] = $version + } + } + foreach ($propName in $cap.BodyProperties) { + if (-not $entryRecord.bodyProperties.Contains($propName)) { + $entryRecord.bodyProperties[$propName] = $version + } + } + + # Decision 1 -- readOnly is NOT monotonic across versions: 58 of 1264 analysed + # (endpoint, body-field) pairs flip their readOnly flag somewhere in fb2.0-2.27, + # and most flips go read-only -> writable. Unlike parameters/bodyProperties above + # (genuinely monotonic "introduced in version X", correctly guarded by first-sight + # above), readOnlyBodyProperties must be assigned UNCONDITIONALLY every iteration + # so the newest-processed spec always wins -- never guarded by "only if not + # already present". Persisting first-sight here would silently suppress + # genuinely settable fields from the actionable gap list -- e.g. + # PATCH /api-clients|max_role, read-only in older specs and writable in 2.27 -- + # and a missing gap is far worse than a noisy one. Because $specFiles is + # processed in ascending version order, "unconditional" is simply "overwrite each + # time"; an endpoint absent from a later spec is simply not visited that + # iteration, so it naturally keeps its last-seen value for free. + # Emitted only when non-empty (same "keep the manifest lean" rule as deprecated + # below): most operations have no read-only body fields at all, and writing + # "readOnlyBodyProperties": [] on all ~520 of them would roughly 14x the actual + # ~15KB growth budget for zero information gain. Absence IS the empty-set here. + if ($cap.ReadOnlyBodyProperties -and @($cap.ReadOnlyBodyProperties).Count -gt 0) { + $entryRecord.readOnlyBodyProperties = @($cap.ReadOnlyBodyProperties | Sort-Object) + } + elseif ($entryRecord.Contains('readOnlyBodyProperties')) { + $entryRecord.Remove('readOnlyBodyProperties') + } + + # deprecated: emit the key only when non-empty (true for zero top-level + # request-body properties across all 28 analysed specs today -- the sole + # "deprecated" occurrence in the whole surface is a nested, readOnly, + # response-only field -- so this key will not appear in today's manifest at all, + # which is correct). Same last-seen-wins reasoning as readOnly: if the + # newest-seen version for this endpoint has no deprecated fields, remove any + # stale key from an earlier version rather than leaving it behind. + if ($cap.DeprecatedBodyProperties -and @($cap.DeprecatedBodyProperties).Count -gt 0) { + $entryRecord.deprecatedBodyProperties = @($cap.DeprecatedBodyProperties | Sort-Object) + } + elseif ($entryRecord.Contains('deprecatedBodyProperties')) { + $entryRecord.Remove('deprecatedBodyProperties') + } + + # parameterComponents bookkeeping -- which $ref component backs each parameter + # (e.g. context_names -> Context_names_get) for the CURRENT (last-seen) version of + # this endpoint, plus which of this endpoint's CURRENT parameters have NO + # component at all (declared inline, no "$ref"). This describes the CURRENT wire + # shape, not "introduced in version X" -- a version number attached to it would be + # meaningless at best -- so both are overwritten UNCONDITIONALLY every iteration, + # same last-seen-wins treatment as readOnlyBodyProperties above, not the + # first-sight guard. + # + # These two "_current*" keys are TEMPORARY per-build bookkeeping, not part of the + # manifest's public shape: a full per-endpoint component map would be ~90% pure + # duplication (measured: 4102 total (parameter, component) pairs across fb2.0-2.27 + # but only 224 distinct pairs and 179 distinct parameter names -- most endpoints + # just reuse the same shared "Filter"/"Limit"/"Sort"/etc. components), which blew + # the manifest's lean-growth budget ~12x when first tried. The second pass below + # (after every spec is processed) deduplicates this into a single + # parameterComponentDefaults table plus minimal per-endpoint + # parameterComponentOverrides, then strips these bookkeeping keys back out. + # + # $cap.ParameterComponents is a typed Dictionary[string,string] (API parameter + # names as keys) -- .get_Keys() avoids the live Hashtable-shadowing bug elsewhere + # in this codebase (a key literally named "keys"/"count"/"values" hijacking member + # access), used defensively even though a typed Dictionary does not actually + # exhibit that shadowing the way a plain Hashtable/OrderedDictionary does. + $currentParamComponents = [System.Collections.Generic.Dictionary[string, string]]::new() + foreach ($paramName in ($cap.ParameterComponents.get_Keys() | Sort-Object)) { + $currentParamComponents[$paramName] = $cap.ParameterComponents[$paramName] + } + $entryRecord['_currentParamComponents'] = $currentParamComponents + + $currentParamsWithoutComponent = [System.Collections.Generic.HashSet[string]]::new() + foreach ($paramName in ($cap.Parameters | Select-Object -Unique)) { + if (-not $currentParamComponents.ContainsKey($paramName)) { + [void]$currentParamsWithoutComponent.Add($paramName) + } + } + $entryRecord['_currentParamsWithoutComponent'] = $currentParamsWithoutComponent + } + + $processedVersions.Add($version) +} + +# ---- Second pass: deduplicate parameterComponents into a global defaults table plus +# minimal per-endpoint overrides ---- +# +# Resolution contract (also documented in tools/README.md): for a given (endpoint, +# parameter), the effective component is: +# 1. If the endpoint's parameterComponentOverrides contains the parameter name, use +# that value -- which may be JSON null, meaning "this endpoint's parameter has no +# component" (see below). This takes precedence over the default. +# 2. Otherwise, if parameterComponentDefaults contains the parameter name, use that +# value. +# 3. Otherwise, the parameter has no known component. +# +# Deterministic default selection: for each parameter name, count how often each +# component name occurs across every endpoint's CURRENT (last-seen) mapping, and pick +# the MOST FREQUENT; ties are broken ALPHABETICALLY by component name. This must be +# fully deterministic across builds regardless of endpoint processing order -- frequency +# counting alone is not enough when two components are equally common for a parameter +# name (this happens; do not assume it can't). +$componentCountsByParam = [System.Collections.Generic.Dictionary[string, System.Collections.Generic.Dictionary[string, int]]]::new() +foreach ($epKey in $endpoints.get_Keys()) { + $paramComponents = $endpoints[$epKey]['_currentParamComponents'] + foreach ($paramName in $paramComponents.get_Keys()) { + if (-not $componentCountsByParam.ContainsKey($paramName)) { + $componentCountsByParam[$paramName] = [System.Collections.Generic.Dictionary[string, int]]::new() + } + $componentName = $paramComponents[$paramName] + $counts = $componentCountsByParam[$paramName] + if (-not $counts.ContainsKey($componentName)) { $counts[$componentName] = 0 } + $counts[$componentName]++ + } +} + +$parameterComponentDefaults = [ordered]@{} +foreach ($paramName in ($componentCountsByParam.get_Keys() | Sort-Object)) { + $counts = $componentCountsByParam[$paramName] + $best = $counts.get_Keys() | + Sort-Object @{ Expression = { $counts[$_] }; Descending = $true }, @{ Expression = { $_ }; Descending = $false } | + Select-Object -First 1 + $parameterComponentDefaults[$paramName] = $best +} + +# Per-endpoint overrides: emitted only where this endpoint's CURRENT value differs from +# the global default for that parameter name, OR where this endpoint's parameter +# currently has NO component (inline, no "$ref") but the parameter NAME does have a +# global default from some other endpoint -- an explicit JSON null override records "no +# component here", distinguishing it from "absent", which would otherwise silently +# resolve to the (wrong) default under the lookup contract above. This is the subtle +# case: only 7 of 4109 parameter declarations in fb2.27 are inline/no-$ref, but every one +# whose name collides with a global default MUST get a null override or it would +# misreport a component it does not actually have. +foreach ($epKey in $endpoints.get_Keys()) { + $rec = $endpoints[$epKey] + $paramComponents = $rec['_currentParamComponents'] + $paramsWithoutComponent = $rec['_currentParamsWithoutComponent'] + + $overrides = [ordered]@{} + foreach ($paramName in $paramComponents.get_Keys()) { + $componentName = $paramComponents[$paramName] + if ($componentName -ne $parameterComponentDefaults[$paramName]) { + $overrides[$paramName] = $componentName + } + } + foreach ($paramName in $paramsWithoutComponent) { + if ($parameterComponentDefaults.Contains($paramName)) { + $overrides[$paramName] = $null + } + } + + if ($overrides.get_Count() -gt 0) { + # Re-sort: the two loops above each insert in their own sorted order, but their + # combination is not necessarily sorted overall. + $sortedOverrides = [ordered]@{} + foreach ($paramName in ($overrides.get_Keys() | Sort-Object)) { + $sortedOverrides[$paramName] = $overrides[$paramName] + } + $rec.parameterComponentOverrides = $sortedOverrides + } + + $rec.Remove('_currentParamComponents') + $rec.Remove('_currentParamsWithoutComponent') +} + +$manifest = [ordered]@{ + schemaVersion = 1 + generatedFrom = $processedVersions + endpointCount = $endpoints.Count + parameterComponentDefaults = $parameterComponentDefaults + endpoints = $endpoints +} + +$outputDir = Split-Path -Parent $OutputPath +if (-not (Test-Path $outputDir)) { + New-Item -ItemType Directory -Path $outputDir -Force | Out-Null +} + +$manifest | ConvertTo-Json -Depth 20 | Set-Content -Path $OutputPath -Encoding UTF8 + +Write-Host '' +Write-Host "Wrote $($endpoints.Count) endpoints from $($processedVersions.Count) versions to $OutputPath" -ForegroundColor Green diff --git a/tools/Build-PfbFieldCmdletMap.ps1 b/tools/Build-PfbFieldCmdletMap.ps1 new file mode 100644 index 0000000..15ed1d9 --- /dev/null +++ b/tools/Build-PfbFieldCmdletMap.ps1 @@ -0,0 +1,152 @@ +#Requires -Version 7.0 +<# +.SYNOPSIS + Joins the cmdlet parameter inventory (tools/lib/PfbCmdletParamTools.ps1) against the + prose value-enum data (tools/lib/PfbValueEnumTools.ps1) to recommend, per typed + Public/ parameter that lacks a ValidateSet today, whether it should become a + ValidateSet or an ArgumentCompleter. +.DESCRIPTION + Data-extraction and reporting only -- does NOT add a ValidateSet or ArgumentCompleter + to any Public/ cmdlet. See docs/superpowers/plans/2026-07-16-field-cmdlet-mapping.md + for the full design rationale and the exact ValidateSet-recommendation rule. + + Resolves a candidate's wire name against three kinds of value-enum record (see + tools/lib/PfbValueEnumTools.ps1): 'schema' (resource-hint filtered, a heuristic), + 'parameter' (a shared components.parameters dictionary name with no relationship to + the owning resource, resolved only when every same-named definition agrees), and + 'inline-parameter' (an exact " #" identity, resolvable + only when tools/lib/PfbCmdletParamTools.ps1's AST inventory could determine exactly + which endpoint this specific cmdlet parameter calls). An exact inline-parameter match + takes priority -- it settles ambiguity the other two kinds cannot (the real + Get-PfbArraySpace -Type case: two same-named-but-different-valued + components.parameters definitions, 'Type' and 'Type_for_performance', disambiguated + by knowing this cmdlet calls GET arrays/space specifically). +.PARAMETER SpecsDirectory + Where cached spec JSON files live. Defaults to tools/specs relative to this script. +.PARAMETER PublicDirectory + Where Public/ cmdlet files live. Defaults to Public/ relative to the repo root. +.PARAMETER OutputPath + Where to write Reports/PfbFieldCmdletMap.json. Defaults there. +.PARAMETER ReportPath + Where to write Reports/PfbFieldCmdletMapping.md. Defaults there. +#> +[CmdletBinding()] +param( + [string]$SpecsDirectory, + [string]$PublicDirectory, + [string]$OutputPath, + [string]$ReportPath +) + +$ErrorActionPreference = 'Stop' + +$scriptDir = $PSScriptRoot +. (Join-Path $scriptDir 'lib/PfbSpecTools.ps1') +. (Join-Path $scriptDir 'lib/PfbValueEnumTools.ps1') +. (Join-Path $scriptDir 'lib/PfbCmdletParamTools.ps1') + +$repoRoot = Split-Path -Parent $scriptDir +if (-not $SpecsDirectory) { $SpecsDirectory = Join-Path $scriptDir 'specs' } +if (-not $PublicDirectory) { $PublicDirectory = Join-Path $repoRoot 'Public' } +if (-not $OutputPath) { $OutputPath = Join-Path (Join-Path $repoRoot 'Reports') 'PfbFieldCmdletMap.json' } +if (-not $ReportPath) { $ReportPath = Join-Path (Join-Path $repoRoot 'Reports') 'PfbFieldCmdletMapping.md' } + +$specFiles = Get-ChildItem -Path $SpecsDirectory -Filter 'fb*.json' -ErrorAction SilentlyContinue +if (-not $specFiles) { + throw "No cached specs found in '$SpecsDirectory'. Run Update-PfbApiSpecs.ps1 first." +} + +$specFiles = $specFiles | ForEach-Object { + if ($_.BaseName -match '^fb(\d+)\.(\d+)$') { + [PSCustomObject]@{ File = $_; Major = [int]$Matches[1]; Minor = [int]$Matches[2] } + } +} | Where-Object { $_ } | Sort-Object Major, Minor + +# Re-derive, per (schema/param) Key, the full per-version value-set history -- NOT just +# the "latest wins" summary Reports/PfbValueEnumMap.json stores -- so stability (did the +# value set ever change since first seen?) can be computed. Deliberately a separate, +# self-contained re-scan rather than modifying the prior phase's already-shipped output +# shape, keeping this task's diff additive-only. +$historyResult = Get-PfbValueEnumHistory -SpecsDirectory $SpecsDirectory +$history = $historyResult.History +$processedVersions = $historyResult.ProcessedVersions +$oldestVersion = $historyResult.OldestVersion + +# --- Cmdlet parameter inventory, filtered to fields with no existing ValidateSet --- +$inventory = Get-PfbCmdletParameterInventory -PublicDirectory $PublicDirectory +$candidates = @($inventory | Where-Object { $_.Surface -eq 'Typed' -and -not $_.HasValidateSet }) +$attributesOnly = @($inventory | Where-Object { $_.Surface -eq 'AttributesOnly' } | ForEach-Object { [ordered]@{ cmdlet = $_.Cmdlet; parameter = $_.Parameter } }) +$typedUnresolved = @($inventory | Where-Object { $_.Surface -eq 'TypedUnresolved' } | ForEach-Object { [ordered]@{ cmdlet = $_.Cmdlet; parameter = $_.Parameter } }) + +$entries = foreach ($cand in $candidates) { + $hint = Get-PfbResourceHint -CmdletName $cand.Cmdlet + $resolution = Resolve-PfbFieldValueEnum -WireName $cand.WireName -ResourceHint $hint -Endpoint $cand.Endpoint -Method $cand.Method -History $history -OldestVersion $oldestVersion + + [ordered]@{ + cmdlet = $cand.Cmdlet + parameter = $cand.Parameter + wireName = $cand.WireName + status = $resolution.Status + matchedKey = $resolution.MatchedKey + specValues = $resolution.SpecValues + stableSinceOldestVersion = $resolution.Stable + recommendation = $resolution.Recommendation + } +} + +$manifest = [ordered]@{ + schemaVersion = 1 + generatedFrom = $processedVersions + entries = $entries + attributesOnly = $attributesOnly + typedUnresolved = $typedUnresolved +} + +$outputDir = Split-Path -Parent $OutputPath +if (-not (Test-Path $outputDir)) { New-Item -ItemType Directory -Path $outputDir -Force | Out-Null } +$manifest | ConvertTo-Json -Depth 20 | Set-Content -Path $OutputPath -Encoding UTF8 + +$statusCounts = [ordered]@{} +foreach ($statusName in @('matched', 'collision', 'not-found-in-resource', 'no-spec-enum-found')) { + $statusCounts[$statusName] = 0 +} +foreach ($e in $entries) { + if (-not $statusCounts.Contains($e.status)) { $statusCounts[$e.status] = 0 } + $statusCounts[$e.status]++ +} + +$mdLines = [System.Collections.Generic.List[string]]::new() +$mdLines.Add('# Field-to-Cmdlet Mapping Report') +$mdLines.Add('') +$mdLines.Add("Generated by ``tools/Build-PfbFieldCmdletMap.ps1`` ($($processedVersions.Count) REST versions).") +$mdLines.Add('') +$mdLines.Add('Reporting only -- no `Public/` cmdlet is edited by this script. Every `matched` row below is a candidate for a follow-up decision, not an action already taken.') +$mdLines.Add('') +$mdLines.Add('## Summary') +$mdLines.Add('') +foreach ($key in $statusCounts.Keys) { + $mdLines.Add("- $key`: $($statusCounts[$key])") +} +if ($statusCounts['matched'] -eq 0) { + $mdLines.Add('') + $mdLines.Add('No `matched` candidates this run -- this can be a genuinely correct result (most typed parameters without a `ValidateSet` are generic non-enum fields like filter/sort/limit/names/ids, not spec-documented enums) rather than a sign the tool found nothing useful. See the `collision`/`not-found-in-resource` rows below for what *is* actionable.') +} +$mdLines.Add('') +$mdLines.Add('| Cmdlet | Parameter | Wire name | Status | Spec values | Recommendation |') +$mdLines.Add('|---|---|---|---|---|---|') +foreach ($e in $entries) { + if ($e.status -eq 'no-spec-enum-found') { continue } + $mdLines.Add("| ``$($e.cmdlet)`` | ``-$($e.parameter)`` | $($e.wireName) | $($e.status) | $($e.specValues -join ', ') | $($e.recommendation) |") +} +$mdLines.Add('') +$mdLines.Add("## Attributes-only parameters (no typed field to attach either mechanism to): $($attributesOnly.Count)") +$mdLines.Add('') +foreach ($a in $attributesOnly) { $mdLines.Add("- ``$($a.cmdlet) -$($a.parameter)``") } +$mdLines.Add('') +$mdLines.Add("## Typed but unresolved wire name (needs manual inspection): $($typedUnresolved.Count)") +$mdLines.Add('') +foreach ($u in $typedUnresolved) { $mdLines.Add("- ``$($u.cmdlet) -$($u.parameter)``") } +$mdLines.Add('') + +Set-Content -Path $ReportPath -Value ($mdLines -join "`n") -Encoding UTF8 +Write-Host "Wrote $($entries.Count) entries to $OutputPath and $ReportPath" -ForegroundColor Green diff --git a/tools/Build-PfbResponseShapeMap.ps1 b/tools/Build-PfbResponseShapeMap.ps1 new file mode 100644 index 0000000..a88e511 --- /dev/null +++ b/tools/Build-PfbResponseShapeMap.ps1 @@ -0,0 +1,184 @@ +#Requires -Version 7.0 +<# +.SYNOPSIS + Builds Data/PfbResponseShapeMap.json -- the response-side counterpart to + Data/PfbCapabilityMap.json. +.DESCRIPTION + For every endpoint with a 2xx JSON response, records the top-level envelope properties + and the items[] element properties one level deep, each with the version it was first + seen in, plus any that have since disappeared. + + THIS ARTIFACT HAS NO RUNTIME CONSUMER BY DESIGN. It is deliberately a separate file + from Data/PfbCapabilityMap.json, which Private/Get-PfbCapabilityMap.ps1 lazily loads and + caches into every user session: merging this axis in would add 242,917 bytes to a + 298,841-byte file -- +81% on every module import -- for data no runtime code reads. Nothing may gate an API call on response data -- + Assert-PfbApiCapability guards request construction only, and a missing response field + is never a reason to refuse a call. + + Monotonicity rule -- first-seen for introduction, last-seen for presence: + * responseEnvelope / responseItemProperties are {field: introducedVersion}, holding + only fields still present in the endpoint's lastSeenVersion. + * A field last seen before its endpoint's lastSeenVersion has been REMOVED and moves + to removedResponseFields. + This differs from readOnly's last-seen-wins because presence is what is tracked, not an + attribute whose value flips. +.NOTES + Does NOT fetch specs. Run tools/Update-PfbApiSpecs.ps1 separately and deliberately. +#> +[CmdletBinding()] +param( + [string]$SpecsDirectory, + [string]$OutputPath +) + +# Deliberately NOT Set-StrictMode, matching tools/Build-PfbCapabilityMap.ps1. StrictMode is +# dynamically scoped, so setting it here would leak into the dot-sourced PfbSpecTools +# functions, which document (lib/PfbSpecTools.ps1:43) that they are intentionally +# null-tolerant walkers over heterogeneous spec nodes -- under StrictMode a response with no +# .content throws instead of being skipped, and real cached specs contain such responses. +$ErrorActionPreference = 'Stop' + +$repoRoot = Split-Path -Parent $PSScriptRoot +if (-not $SpecsDirectory) { $SpecsDirectory = Join-Path $PSScriptRoot 'specs' } +if (-not $OutputPath) { $OutputPath = Join-Path $repoRoot 'Data/PfbResponseShapeMap.json' } + +. (Join-Path $PSScriptRoot 'lib/PfbSpecTools.ps1') + +$specFiles = Get-ChildItem -Path $SpecsDirectory -Filter 'fb*.json' -ErrorAction SilentlyContinue +if (-not $specFiles) { + throw "No cached specs found in '$SpecsDirectory'. Run Update-PfbApiSpecs.ps1 first." +} + +# Numeric sort -- 'fb2.9' must precede 'fb2.10'. A string sort puts 2.9 above 2.27 and would +# invert the whole accumulation, turning every newer field into a phantom removal. +$specFiles = $specFiles | ForEach-Object { + if ($_.BaseName -match '^fb(\d+)\.(\d+)$') { + [PSCustomObject]@{ File = $_; Major = [int]$Matches[1]; Minor = [int]$Matches[2] } + } + else { + Write-Warning "Skipping unrecognized spec filename: $($_.Name)" + $null + } +} | Where-Object { $_ } | Sort-Object Major, Minor + +# Guard AGAIN on the parsed set, not just the glob. A directory whose fb*.json files all fail +# the version regex clears the check above, and without this the run would go on to write a +# well-formed artifact with an empty generatedFrom -- silently replacing the real +# Data/PfbResponseShapeMap.json with an empty one. +if (-not $specFiles) { + throw "No spec files in '$SpecsDirectory' have a parseable fb..json name. Run Update-PfbApiSpecs.ps1 first." +} + +# endpointKey -> record. Typed Dictionary, not a Hashtable: endpoint keys are safe, but the +# per-field dictionaries below hold API field names, where a field named 'keys'/'count'/ +# 'values' would shadow real member access on a Hashtable. +$endpoints = [System.Collections.Generic.Dictionary[string, object]]::new() +$processedVersions = [System.Collections.Generic.List[string]]::new() + +foreach ($entry in $specFiles) { + $version = "$($entry.Major).$($entry.Minor)" + Write-Host "Processing $version ($($entry.File.Name))..." -ForegroundColor Cyan + $processedVersions.Add($version) + + # This line is the sole reason for the #Requires -Version 7.0 above: ConvertFrom-Json + # only gained -Depth in PowerShell 6.2, so under Windows PowerShell 5.1 it fails with + # "A parameter cannot be found that matches parameter name 'Depth'". Matches the -Depth + # the other tools/Build-*.ps1 spec readers pass; keep the #Requires and this line + # together if either ever changes. + $spec = Get-Content -Path $entry.File.FullName -Raw | ConvertFrom-Json -Depth 64 + # MaxDepth is left at the function's own default of 32 -- see its help. Do not pass 8. + $shapes = Get-PfbSpecResponseShapes -Spec $spec + + foreach ($shape in $shapes) { + $epKey = "$($shape.Method) $($shape.Path)" + + if (-not $endpoints.ContainsKey($epKey)) { + $endpoints[$epKey] = [PSCustomObject]@{ + MinVersion = $version + LastSeenVersion = $version + # field -> [PSCustomObject]@{ Introduced; LastSeen } + Envelope = [System.Collections.Generic.Dictionary[string, object]]::new() + Items = [System.Collections.Generic.Dictionary[string, object]]::new() + } + } + + $record = $endpoints[$epKey] + $record.LastSeenVersion = $version + + foreach ($pair in @( + @{ Names = $shape.EnvelopeProperties; Bag = $record.Envelope }, + @{ Names = $shape.ItemProperties; Bag = $record.Items } + )) { + foreach ($name in $pair.Names) { + if ($pair.Bag.ContainsKey($name)) { + $pair.Bag[$name].LastSeen = $version + } + else { + $pair.Bag[$name] = [PSCustomObject]@{ Introduced = $version; LastSeen = $version } + } + } + } + } +} + +$emitted = [ordered]@{} +foreach ($epKey in ($endpoints.get_Keys() | Sort-Object)) { + $record = $endpoints[$epKey] + + $present = [ordered]@{ envelope = [ordered]@{}; items = [ordered]@{} } + $removed = [System.Collections.Generic.List[object]]::new() + + foreach ($locationPair in @( + @{ Location = 'envelope'; Bag = $record.Envelope }, + @{ Location = 'items'; Bag = $record.Items } + )) { + foreach ($name in ($locationPair.Bag.get_Keys() | Sort-Object)) { + $field = $locationPair.Bag[$name] + if ($field.LastSeen -eq $record.LastSeenVersion) { + $present[$locationPair.Location][$name] = $field.Introduced + } + else { + $removed.Add([ordered]@{ + field = $name + location = $locationPair.Location + introducedVersion = $field.Introduced + lastSeenVersion = $field.LastSeen + }) + } + } + } + + $endpointRecord = [ordered]@{ + minVersion = $record.MinVersion + lastSeenVersion = $record.LastSeenVersion + responseEnvelope = $present['envelope'] + responseItemProperties = $present['items'] + } + + # Emitted only when non-empty -- across all 496 endpoints just 7 removal records exist, so + # ~500 empty arrays would be pure noise in a tracked artifact. The count is 7 rather than + # the larger number a naive version-to-version diff reports because "removed" here means + # absent from the endpoint's own lastSeenVersion: a field that disappears for a single + # version and then returns is still present in the newest spec and is deliberately NOT + # counted. (Both known instances -- GET /active-directory at 2.12, GET /buckets/performance + # at 2.20 -- were spec-authoring errors corrected in the following release.) + if ($removed.Count -gt 0) { + $endpointRecord.removedResponseFields = @($removed | Sort-Object { $_.location }, { $_.field }) + } + + $emitted[$epKey] = $endpointRecord +} + +$manifest = [ordered]@{ + schemaVersion = 1 + generatedFrom = @($processedVersions) + endpoints = $emitted +} + +$outputDirectory = Split-Path -Parent $OutputPath +if ($outputDirectory -and -not (Test-Path $outputDirectory)) { + New-Item -ItemType Directory -Path $outputDirectory -Force | Out-Null +} + +$manifest | ConvertTo-Json -Depth 20 | Set-Content -Path $OutputPath -Encoding UTF8 +Write-Host "`nWrote $($emitted.Count) endpoints from $($processedVersions.Count) versions to $OutputPath" diff --git a/tools/Build-PfbValueEnumMap.ps1 b/tools/Build-PfbValueEnumMap.ps1 new file mode 100644 index 0000000..2ca0d7f --- /dev/null +++ b/tools/Build-PfbValueEnumMap.ps1 @@ -0,0 +1,297 @@ +#Requires -Version 7.0 +<# +.SYNOPSIS + Builds the FlashBlade prose "Valid/Possible values" enum manifest from cached + OpenAPI specs. +.DESCRIPTION + Loads every cached tools/specs/fb.json in ascending version order, extracts + every prose-documented value enumeration (see tools/lib/PfbValueEnumTools.ps1) keyed + by (SchemaName.PropertyName) or parameter component name, and diffs across versions + to attribute each entry its earliest-seen ("introduced in") version. The current + legal value set recorded for each entry reflects the newest processed version. + + This is data-extraction and validation only — it does NOT wire up ArgumentCompleters + and does NOT change Assert-PfbApiCapability enforcement. See + Value-Enum-Extraction-Work.md for the full design rationale and non-goals. + + Also writes a reconciliation report (Reports/PfbValueEnumReconciliation.md) comparing + this newly extracted data against every existing hand-written `ValidateSet` in + Public/ that encodes a spec-documented value enum. Report only — no Public/ cmdlet + is edited by this script. +.PARAMETER SpecsDirectory + Where cached spec JSON files live. Defaults to tools/specs relative to this script. +.PARAMETER OutputPath + Where to write the manifest. Defaults to Reports/PfbValueEnumMap.json relative to the + repo root (one level up from tools/). Reports/ is advisory output only, never read at + runtime -- see Reports/README.md. +.PARAMETER ReconciliationPath + Where to write the reconciliation report. Defaults to + Reports/PfbValueEnumReconciliation.md relative to the repo root. +.EXAMPLE + ./tools/Build-PfbValueEnumMap.ps1 +#> +[CmdletBinding()] +param( + [string]$SpecsDirectory, + + [string]$OutputPath, + + [string]$ReconciliationPath +) + +$ErrorActionPreference = 'Stop' + +$scriptDir = $PSScriptRoot +. (Join-Path $scriptDir 'lib/PfbSpecTools.ps1') +. (Join-Path $scriptDir 'lib/PfbValueEnumTools.ps1') + +if (-not $SpecsDirectory) { + $SpecsDirectory = Join-Path $scriptDir 'specs' +} +if (-not $OutputPath) { + $repoRoot = Split-Path -Parent $scriptDir + $OutputPath = Join-Path (Join-Path $repoRoot 'Reports') 'PfbValueEnumMap.json' +} +if (-not $ReconciliationPath) { + $repoRoot = Split-Path -Parent $scriptDir + $ReconciliationPath = Join-Path (Join-Path $repoRoot 'Reports') 'PfbValueEnumReconciliation.md' +} + +$specFiles = Get-ChildItem -Path $SpecsDirectory -Filter 'fb*.json' -ErrorAction SilentlyContinue +if (-not $specFiles) { + throw "No cached specs found in '$SpecsDirectory'. Run Update-PfbApiSpecs.ps1 first." +} + +# Sort by numeric version, not filename string (fb2.9 must sort before fb2.10). +$specFiles = $specFiles | ForEach-Object { + if ($_.BaseName -match '^fb(\d+)\.(\d+)$') { + [PSCustomObject]@{ + File = $_ + Major = [int]$Matches[1] + Minor = [int]$Matches[2] + } + } + else { + Write-Warning "Skipping unrecognized spec filename: $($_.Name)" + $null + } +} | Where-Object { $_ } | Sort-Object Major, Minor + +# One record per (SchemaName.PropertyName) or parameter key, tracking the earliest +# version it was ever seen in (MinVersion) and the most recently processed version's +# record (LastRecord) — the latter supplies the "current legal value set" / current +# parsed-vs-unparsed status, since values and prose can change release to release. +# +# $seen is deliberately case-INSENSITIVE (PowerShell's [ordered]@{} default) rather than +# a stricter Ordinal comparer. Confirmed live: the real spec renames a schema's casing +# between versions (e.g. "SNMPAgent" in early REST versions to "SnmpAgent" by fb2.27) — +# NOT a squash-mode-style same-name-different-meaning collision, just a vendor casing +# convention change over time for the *same* logical schema. A case-sensitive dictionary +# would keep both as separate entries, which in turn produces a manifest JSON with two +# top-level keys differing only by case — and PowerShell's ConvertFrom-Json (producing a +# PSCustomObject, the idiom used everywhere else in this repo, including every consumer +# of this file) hard-errors on that ("Cannot convert the JSON string because it contains +# keys with different casing"). So each case-insensitive-equivalent group collapses to +# one entry, but — unlike a real squash-mode merge — nothing about its VALUES gets +# blended: .Key is re-recorded on every sighting so the final output uses whichever +# version's casing was seen LAST (i.e. matches the newest/current spec), while +# MinVersion still reflects the earliest sighting under any casing. +$seen = [ordered]@{} +$processedVersions = [System.Collections.Generic.List[string]]::new() + +foreach ($entry in $specFiles) { + $version = "$($entry.Major).$($entry.Minor)" + Write-Host "Processing $version ($($entry.File.Name))..." -ForegroundColor Cyan + + $spec = Get-Content -Path $entry.File.FullName -Raw | ConvertFrom-Json -Depth 64 + $valueEnums = Get-PfbSpecValueEnums -Spec $spec + + foreach ($rec in $valueEnums) { + if (-not $seen.Contains($rec.Key)) { + $seen[$rec.Key] = [ordered]@{ + Key = $rec.Key + Kind = $rec.Kind + Name = $rec.Name + MinVersion = $version + LastRecord = $rec + } + } + else { + $seen[$rec.Key].Key = $rec.Key + $seen[$rec.Key].LastRecord = $rec + } + } + + $processedVersions.Add($version) +} + +$entries = [ordered]@{} +$unparsed = [System.Collections.Generic.List[object]]::new() + +foreach ($key in $seen.Keys) { + $s = $seen[$key] + if ($s.LastRecord.Parsed) { + $entries[$s.Key] = [ordered]@{ + values = $s.LastRecord.Values + minVersion = $s.MinVersion + kind = $s.Kind + name = $s.Name + } + } + else { + $unparsed.Add([ordered]@{ + key = $s.Key + kind = $s.Kind + name = $s.Name + version = $s.MinVersion + triggerText = $s.LastRecord.TriggerText + }) + } +} + +$manifest = [ordered]@{ + schemaVersion = 1 + generatedFrom = $processedVersions + entryCount = $entries.Count + unparsedCount = $unparsed.Count + entries = $entries + unparsed = $unparsed +} + +$outputDir = Split-Path -Parent $OutputPath +if (-not (Test-Path $outputDir)) { + New-Item -ItemType Directory -Path $outputDir -Force | Out-Null +} + +$manifest | ConvertTo-Json -Depth 20 | Set-Content -Path $OutputPath -Encoding UTF8 + +Write-Host '' +Write-Host "Wrote $($entries.Count) entries ($($unparsed.Count) unparsed) from $($processedVersions.Count) versions to $OutputPath" -ForegroundColor Green + +# --- Reconciliation against existing hand-written ValidateSets in Public/ --- +# Every ValidateSet found (as of this writing) whose values encode a spec-documented +# value enum, excluding Invoke-PfbApiRequest.ps1's HTTP-verb ValidateSet (not spec data). +# "Name" here is the field's wire name (request-body key or query-parameter name), which +# is what Get-PfbSpecValueEnums records as each entry's .name — see its header comment +# for why that differs from a parameter's components.parameters dictionary key. +# "ResourceHint" is a prefix filter on the schema half of an entry's Key (e.g. +# 'NetworkInterface' matches 'NetworkInterface.services' and 'NetworkInterfacePatch.services' +# but not an unrelated schema that happens to share the property name 'services'). This is +# NOT a real field->cmdlet/endpoint mapping (explicitly out of scope for this phase — see +# Value-Enum-Extraction-Work.md) — it is a best-effort disambiguation to avoid a false +# "stale"/"exact-match" claim built on an unrelated schema's same-named field. A field name +# common enough to appear on many resources (protocol, type) can still legitimately collide +# even after hint-filtering; that is reported as 'collision', not force-resolved. +$handWritten = @( + [PSCustomObject]@{ File = 'Public/Alert/New-PfbAlertWatcher.ps1'; Line = 22; Parameter = '-MinimumSeverity'; Name = 'minimum_notification_severity'; ResourceHint = 'AlertWatcher'; Values = @('info', 'warning', 'error', 'critical') } + [PSCustomObject]@{ File = 'Public/Alert/Update-PfbAlertWatcher.ps1'; Line = 29; Parameter = '-MinimumSeverity'; Name = 'minimum_notification_severity'; ResourceHint = 'AlertWatcher'; Values = @('info', 'warning', 'critical') } + [PSCustomObject]@{ File = 'Public/Bucket/New-PfbBucket.ps1'; Line = 29; Parameter = '-Versioning'; Name = 'versioning'; ResourceHint = 'Bucket'; Values = @('enabled', 'suspended', 'none') } + [PSCustomObject]@{ File = 'Public/Bucket/Update-PfbBucket.ps1'; Line = 31; Parameter = '-Versioning'; Name = 'versioning'; ResourceHint = 'Bucket'; Values = @('enabled', 'suspended', 'none') } + # '_multiProtocol' is the actual nested body-object schema name for this field (confirmed + # by direct spec inspection) — a literal alias, not a fuzzy resource-name guess. + [PSCustomObject]@{ File = 'Public/FileSystem/New-PfbFileSystem.ps1'; Line = 130; Parameter = '-MultiProtocolAccessControlStyle'; Name = 'access_control_style'; ResourceHint = @('FileSystem', '_multiProtocol'); Values = @('nfs', 'smb', 'shared', 'independent', 'mode-bits') } + [PSCustomObject]@{ File = 'Public/FileSystem/New-PfbFileSystem.ps1'; Line = 143; Parameter = '-GroupOwnership'; Name = 'group_ownership'; ResourceHint = 'FileSystem'; Values = @('creator', 'parent-directory') } + # '_fileSystemEradicationConfig' (as opposed to the sibling '_bucketEradicationConfig', + # which shares the same field name with an unrelated value set) — confirmed by direct + # spec inspection, not a fuzzy resource-name guess. + [PSCustomObject]@{ File = 'Public/FileSystem/New-PfbFileSystem.ps1'; Line = 147; Parameter = '-EradicationMode'; Name = 'eradication_mode'; ResourceHint = @('FileSystem', '_fileSystemEradicationConfig'); Values = @('permission-based', 'retention-based') } + [PSCustomObject]@{ File = 'Public/FileSystem/Update-PfbFileSystem.ps1'; Line = 97; Parameter = '-RequestedPromotionState'; Name = 'requested_promotion_state'; ResourceHint = 'FileSystem'; Values = @('promoted', 'demoted') } + [PSCustomObject]@{ File = 'Public/Array/Get-PfbArrayPerformance.ps1'; Line = 28; Parameter = '-Protocol'; Name = 'protocol'; ResourceHint = $null; Values = @('nfs', 'smb', 'http', 's3') } + [PSCustomObject]@{ File = 'Public/Network/New-PfbNetworkInterface.ps1'; Line = 52; Parameter = '-Services'; Name = 'services'; ResourceHint = 'NetworkInterface'; Values = @('data', 'egress-only', 'management', 'replication', 'support') } + [PSCustomObject]@{ File = 'Public/Network/New-PfbNetworkInterface.ps1'; Line = 59; Parameter = '-Type'; Name = 'type'; ResourceHint = 'NetworkInterface'; Values = @('vip') } +) + +function Test-PfbValueSetsEqual { + param([string[]]$A, [string[]]$B) + $setA = [System.Collections.Generic.HashSet[string]]::new([string[]]$A) + $setB = [System.Collections.Generic.HashSet[string]]::new([string[]]$B) + return $setA.SetEquals($setB) +} + +$reconciliation = foreach ($hw in $handWritten) { + $allMatches = @($entries.Keys | Where-Object { $entries[$_].name -eq $hw.Name } | ForEach-Object { + [PSCustomObject]@{ Key = $_; Values = $entries[$_].values } + }) + + # Strict prefix (not substring-contains): "NetworkInterface*" must correctly exclude + # the unrelated "_networkInterfaceNeighbor*" private schemas (a real collision found + # live) — those start with an underscore, so a plain prefix check already excludes + # them without needing a word-boundary check. Known private nested-object schemas + # that don't share the resource's own name prefix are listed as explicit extra hints + # above (e.g. '_multiProtocol', '_fileSystemEradicationConfig'), not matched via a + # looser "contains" rule that would just reopen the same false-positive risk. + $hints = @($hw.ResourceHint) | Where-Object { $_ } + $candidates = if ($hints.Count -gt 0) { + @($allMatches | Where-Object { + $entryKey = $_.Key + @($hints | Where-Object { $entryKey -like "$_*" }).Count -gt 0 + }) + } + else { + $allMatches + } + + $status = $null + $specValues = $null + $note = '' + + if ($candidates.Count -eq 0 -and $allMatches.Count -eq 0) { + $status = 'not-found' + } + elseif ($candidates.Count -eq 0) { + # The field name exists elsewhere in the spec, just not under this cmdlet's own + # resource — do not claim exact-match/stale against an unrelated schema. + $status = 'not-found-in-resource' + $note = "field '$($hw.Name)' not found under any of [$($hints -join ', ')]-hinted schemas; found elsewhere: $($allMatches.Key -join '; ')" + } + else { + $distinctValueSets = $candidates | ForEach-Object { ($_.Values | Sort-Object) -join ',' } | Select-Object -Unique + if (@($distinctValueSets).Count -gt 1) { + $status = 'collision' + $note = "matches $($candidates.Count) distinct (schema, property) entries with different value sets: $($candidates.Key -join '; ')" + } + else { + $specValues = $candidates[0].Values + $status = if (Test-PfbValueSetsEqual -A $hw.Values -B $specValues) { 'exact-match' } else { 'stale' } + if ($status -eq 'stale') { + $missing = $specValues | Where-Object { $_ -notin $hw.Values } + $extra = $hw.Values | Where-Object { $_ -notin $specValues } + $parts = @() + if ($missing) { $parts += "spec has but hand-written set is missing: $($missing -join ', ')" } + if ($extra) { $parts += "hand-written set has but spec does not list: $($extra -join ', ')" } + $note = $parts -join '; ' + } + } + } + + [PSCustomObject]@{ + File = $hw.File + Line = $hw.Line + Parameter = $hw.Parameter + HandValues = $hw.Values -join ', ' + SpecValues = if ($specValues) { $specValues -join ', ' } else { '' } + Status = $status + Note = $note + } +} + +Write-Host '' +Write-Host 'Reconciliation against hand-written ValidateSets:' -ForegroundColor Cyan +$reconciliation | Format-Table File, Parameter, Status, Note -AutoSize | Out-String | Write-Host + +$mdLines = [System.Collections.Generic.List[string]]::new() +$mdLines.Add('# Value-Enum Reconciliation Report') +$mdLines.Add('') +$mdLines.Add("Generated by ``tools/Build-PfbValueEnumMap.ps1`` against ``Reports/PfbValueEnumMap.json`` ($($processedVersions.Count) REST versions, $($entries.Count) entries).") +$mdLines.Add('') +$mdLines.Add('Compares every hand-written `ValidateSet` in `Public/` that encodes a spec-documented value enum against the newly extracted prose data. Report only — no `Public/` cmdlet is edited by this script. See `Value-Enum-Extraction-Work.md` for the full non-goal list.') +$mdLines.Add('') +$mdLines.Add('| File:Line | Parameter | Hand-written values | Spec values | Status | Note |') +$mdLines.Add('|---|---|---|---|---|---|') +foreach ($r in $reconciliation) { + $mdLines.Add("| ``$($r.File):$($r.Line)`` | ``$($r.Parameter)`` | $($r.HandValues) | $($r.SpecValues) | **$($r.Status)** | $($r.Note) |") +} +$mdLines.Add('') + +Set-Content -Path $ReconciliationPath -Value ($mdLines -join "`n") -Encoding UTF8 +Write-Host "Wrote reconciliation report to $ReconciliationPath" -ForegroundColor Green diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 0000000..1dc9ac6 --- /dev/null +++ b/tools/README.md @@ -0,0 +1,691 @@ +# API Capability Map Toolchain + +Generates `Data/PfbCapabilityMap.json` — a manifest mapping every FlashBlade REST API +endpoint (and its parameters and request-body fields) to the REST version it was +introduced in — plus everything built on top of it: `Data/PfbVersionMap.json` (the +REST-version to Purity//FB-version pairing), prose-extracted value enumerations, a +field-to-cmdlet recommendation join, and a combined drift report (see the sections +below). The one runtime consumer is `Private/Assert-PfbApiCapability.ps1`, called from +`Invoke-PfbApiRequest.ps1`: it fails fast, before any HTTP call, when the connected +array's REST version can't satisfy an endpoint/parameter/field this toolchain has +recorded a later minimum version for. + +## Scripts + +Run in this order: + +1. **`Update-PfbApiSpecs.ps1`** — fetches every published REST version's OpenAPI spec + from `https://code.purestorage.com/swagger/` and caches it as pretty-printed JSON + under `tools/specs/fb.json`. Skips versions already cached unless `-Force`. + The spec isn't a plain downloadable file — see `lib/PfbSpecTools.ps1` for why, and how + it's extracted from the ReDoc reference page. + + `tools/specs/` is **gitignored, not committed**. It's a build-time input only — never + read at runtime — and at ~1-3MB per REST version (~48MB across the full history as of + 2026-07), committing it would bloat every clone of this repo. That matters here more + than in a typical repo: today, cloning this repo and copying the checkout straight into + `$env:PSModulePath` *is* the documented "from source" install method (see the root + README), so anything committed at repo root ships to every user, not just contributors. + Locally, just run the fetcher once and the cache persists on disk for reuse; CI + re-fetches the full set fresh on every run (a few minutes, and it only runs weekly). + + ```powershell + ./tools/Update-PfbApiSpecs.ps1 # fetch anything new + ./tools/Update-PfbApiSpecs.ps1 -Versions 2.26,2.27 -Force # re-fetch specific versions + ``` + +2. **`Build-PfbCapabilityMap.ps1`** — loads all cached specs in ascending version order + and diffs them into `Data/PfbCapabilityMap.json`. Runs entirely offline once specs are + cached. + + ```powershell + ./tools/Build-PfbCapabilityMap.ps1 + ``` + + Accepts `-MaxVersion '2.27'` to cap ingestion at a numerically-compared REST version + (`Major`/`Minor` as integers, never a string compare — `'2.9'` sorts above `'2.27'` as + a string) even if `tools/specs/` has newer cached files on disk. Defaults to `$null` + (no cap, ingest everything cached). Use this when a newer spec has been fetched but not + yet deliberately adopted — every acceptance number this toolchain's plan work is + calibrated against assumes a specific version ceiling, and an uncapped rebuild would + silently move those numbers. + + Besides `minVersion`/`parameters`/`bodyProperties` (each `{ name: introducedInVersion }`, + monotonic first-sight), each endpoint also carries, where non-empty: + - `readOnlyBodyProperties` / `deprecatedBodyProperties` (string arrays) — **last-seen-wins**, + not first-sight: unlike "introduced in version X", `readOnly` is not monotonic (a + field can go from read-only to writable across versions, and about half of the flips + in fb2.0-2.27 do exactly that), so these always reflect the newest spec that mentions + the endpoint, never the oldest. + - `parameterComponentOverrides` (`{ paramName: componentName }`, sorted by key) — see + below. + + **`parameterComponentDefaults`/`parameterComponentOverrides` resolution contract:** + which named OpenAPI `components/parameters/*` component backs a given + `(endpoint, parameter)` pair is recorded across two places instead of once per + endpoint, because the vast majority of parameters share one of a small set of common + components (`Filter`, `Limit`, `Sort`, …) — measured on fb2.0-2.27: 4102 total + `(endpoint, parameter) -> component` pairs, but only 224 distinct pairs and 179 + distinct parameter names, so a naive full per-endpoint map is ~90% duplication. To + resolve the component for a given `(endpoint, parameter)`: + 1. If the endpoint's `parameterComponentOverrides` contains the parameter name, use + that value — which **may be a JSON `null`**, meaning "this endpoint's parameter has + no component" (see below). An override, `null` or not, always wins over the default. + 2. Otherwise, if the manifest's top-level `parameterComponentDefaults` contains the + parameter name, use that value. + 3. Otherwise, the parameter has no known component at all. + + `parameterComponentDefaults` is built once, globally: for each parameter name, the + most frequently associated component name across every endpoint's current mapping, + ties broken **alphabetically** by component name (deterministic regardless of endpoint + processing order — required, since the weekly CI job opens a PR on any diff and an + unstable tie-break would spuriously fire it). `parameterComponentOverrides` then holds + only the endpoints where the current component differs from that default. + + The explicit-`null`-override case is the one to know about: a parameter declared + **inline** (no `$ref`) has no component at all — rare (7 of 4109 parameter + declarations in fb2.27), but if that parameter's *name* happens to also be a `$ref`'d + component elsewhere (and so has a global default), silently omitting it from + `parameterComponentOverrides` would make it wrongly inherit that default under the + resolution contract above. The builder emits an explicit `null` override for exactly + this case so "absent" (→ check the default) and "explicitly none" (→ `null`) stay + distinguishable. + +3. **`Update-PfbVersionMap.ps1`** — builds `Data/PfbVersionMap.json`, the REST-version to + Purity//FB-version pairing, from a single internal SSOT (Single Source of Truth) API + call that returns the full REST<->Purity//FB mapping table for every version in one + HTML response. This is an internal endpoint, so its base URI, topic ID, and API key + are all required inputs with no default or hardcoded value in this repo — sourced + from `$env:SSOT_BASE_URI`, + `$env:SSOT_TOPIC_ID`, and `$env:SSOT_API_KEY` (sent as an `x-api-key` header) + respectively. Without all three configured, this script just reports which versions + need lookup and exits without failing. + + ```powershell + $env:SSOT_BASE_URI = '...' + $env:SSOT_TOPIC_ID = '...' + $env:SSOT_API_KEY = '...' + ./tools/Update-PfbVersionMap.ps1 + ``` + +4. **`Build-PfbValueEnumMap.ps1`** — a separate analysis built on top of the capability + map (see "Value-enum extraction" below): loads the same cached specs and extracts + prose-documented value enumerations + (e.g. `Bucket.versioning`'s "Valid values are `none`, `enabled`, and `suspended`.") + into `Reports/PfbValueEnumMap.json`. Also runs entirely offline once specs are cached. + + ```powershell + ./tools/Build-PfbValueEnumMap.ps1 + ``` + +5. **`Build-PfbResponseShapeMap.ps1`** — builds `Data/PfbResponseShapeMap.json`, the + *response*-side counterpart to the capability map (see "Response-shape drift" below). + Loads the same cached specs and diffs each endpoint's response body across versions. + Must run **before** `Build-PfbApiDriftReport.ps1`, which reads the file it writes (and + which degrades to an explicit "Not analysed" banner, never a silent empty section, if + it is missing). + + ```powershell + ./tools/Build-PfbResponseShapeMap.ps1 + ``` + +6. **`Build-PfbApiDriftReport.ps1`** — the newest addition (see `Reports/README.md`): composes + the capability map, cmdlet inventory, and value-enum data above into one combined + "what's changed that we haven't caught up to" report, covering uncovered endpoints, new + parameters on endpoints we already call, drift on existing `ValidateSet`s, and new + `ValidateSet` candidates (reusing `Build-PfbFieldCmdletMap.ps1`'s `matched` output + directly). See `tools/lib/PfbApiDriftTools.ps1` for the underlying functions. + + ```powershell + ./tools/Build-PfbApiDriftReport.ps1 + ``` + + Pass `-SinceVersion` to isolate what a single new REST release actually added instead + of the full accumulated backlog -- e.g. after 2.27 ships, `-SinceVersion '2.26'` filters + `uncoveredEndpoints`/`parameterGaps` down to just the items introduced by 2.27. Only + those two categories support it: `validateSetDrift`/`newValidateSetCandidates` don't + carry a per-value introduced-version in the capability map to filter on. + + ```powershell + ./tools/Build-PfbApiDriftReport.ps1 -SinceVersion '2.26' + ``` + + `parameterGaps` also never reports a small set of non-actionable fields + (`$script:PfbNonActionableParameters` in `tools/lib/PfbApiDriftTools.ps1`: + `X-Request-ID`, `continuation_token`, `offset`) -- these are declared on nearly every + endpoint and would otherwise drown out real gaps. + + **`missingBodyProperties` enrichment (addable body-property gaps only).** On an + endpoint whose `confidence.level` is `'high'`, each `missingBodyProperties` entry is a + record, not a bare string: + + ```jsonc + { "name": "certificate_type", "type": "string", "format": null, "specRequired": false, + "synopsis": "The type of the certificate.", + "suggestedPowerShellType": "[string]", + "enumValues": ["appliance", "external"], "enumStatus": "matched", + "target": { "file": "Public/Certificate/Update-PfbCertificate.ps1", + "paramBlockLine": 37, "payloadVariable": "Attributes", + "assignmentStyle": "attributesOnly", "hasAttributes": true } } + ``` + + - **`missingQueryParameters` and `readOnlyFields` are NEVER enriched this way, and + never will be for the same reason on both** -- `readOnlyFields` are not addable at + all (nothing to build type/target coordinates FOR), and query-parameter gaps stay + bare name strings on purpose: ~896 enriched query-gap records would roughly double + this artifact's size for no consumer today (nothing downstream reads enriched + query-gap detail). Deliberate, documented asymmetry -- not an inconsistency. + - **Enrichment itself is gated on `confidence.level -eq 'high'`.** A + `'partial'`-confidence endpoint's `missingBodyProperties` stays bare strings, exactly + like before this feature existed. This is NOT a suppression -- every field name still + appears, unchanged -- it only withholds the extra metadata layer. Reason: a + partial-confidence endpoint's gap list can contain false positives (an unresolved + parameter may already cover the field through a path the AST-only inventory can't + see), and handing a human a fully-worked-out type/synopsis/enum/target for a field + that might not even be a real gap would overstate a confidence the endpoint's own + `confidence.caveat` is explicitly telling them not to have. Measured against the real + capability map + specs (2026-07-26): 402 addable gaps on high-confidence endpoints vs. + 605 across both confidence levels combined -- the two populations produce genuinely + different enum-join results (see below), confirming the gate is intentional scope, + not an oversight. + - **`suggestedPowerShellType`** comes from a fixed table: `integer`+`int64` -> `[long]`; + `integer`+`int32`/`uint32`/no format -> `[int]`; `number` (any format) -> `[double]`; + `string` -> `[string]`; `boolean` -> `[bool]`; `array` -> `[[]]` (element + mapped recursively, falling back to `[object[]]` when the element type can't be + resolved); anything else -> `[object]`. This branches on `format`, not just `type`, + because the failure mode is silent: **37 of the 402** real high-confidence addable + fields are `type: integer, format: int64` (measured 2026-07-26 -- NOT the 230 once + speculated for this figure; that number was investigated and could not be reproduced + against any of six candidate populations tried). Mapping bare `"type": "integer"` to + `[int]` without checking `format` would silently truncate every one of those 37 + fields. The raw `type`/`format` are always emitted alongside so a human can override. + - **`specRequired` is the OpenAPI spec's own `required:` flag for that field -- it must + NEVER be read as "make this a `[Parameter(Mandatory)]`".** This module has a recorded + hazard: a `Mandatory` parameter tested via `Should -Throw` hangs the terminal on + PowerShell's own "Supply values for parameters" interactive prompt; the convention + here is an optional parameter with an explicit `throw`. Keep it that way even for a + `specRequired: true` field. + - **`enumValues`/`enumStatus`** come from `Resolve-PfbFieldValueEnum` + (`tools/lib/PfbValueEnumTools.ps1`), never a bare wire-name lookup, keyed by the + field's `OwnerSchema` (from `Get-PfbSchemaPropertyDetails`, + `tools/lib/PfbSpecTools.ps1`) as `-ResourceHint` -- not the older cmdlet-name-derived + `Get-PfbResourceHint`, which only reaches 14 of the 33 real matches (e.g. + `Update-PfbNfsExportRule`'s derived hint `NfsExportRule` does not prefix-match the + real owning schema `NfsExportPolicyRuleBase` at all). `enumStatus` is always one of + that function's own literal values (`matched`/`collision`/`not-found-in-resource`/ + `no-spec-enum-found`), passed through verbatim. Measured over the 402 real + high-confidence addable gaps: **33 matched / 43 not-found-in-resource / 326 + no-spec-enum-found** (0 real `collision` results in this dataset, though the status + itself is never hardcoded away). + - **`target`** carries insertion-point COORDINATES ONLY -- `{ file, paramBlockLine, + payloadVariable, assignmentStyle, hasAttributes }` -- never a diff/patch: a patch goes + stale the moment the file is next touched and cannot see mutual-exclusivity/ + parameter-set constraints a human editing by hand must respect. + `payloadVariable`/`assignmentStyle`/`hasAttributes` describe what the target cmdlet's + OWN function body already does for its other body fields (see + `Get-PfbCmdletBodyInsertionTarget` in `tools/lib/PfbCmdletParamTools.ps1`), so a human + adding one more field matches the file's existing convention: `assignmentStyle` is + `'index'` for `$body['x'] = ...`, `'literal'` for a hashtable-literal initializer that + declares at least one key, `'attributesOnly'` when the request body is fed directly + by the cmdlet's own `-Attributes` parameter (there is no per-field line to imitate -- + adding a typed parameter here means introducing the first one), or `'unknown'` when + the payload variable resolves but this AST-only inspector can't find any assignment + into it at all (e.g. built by a private helper). When more than one cmdlet already + calls the same endpoint (5 real cases today), the alphabetically first cmdlet name is + picked as the one target, deterministically -- a human should still check for sibling + cmdlets on the same endpoint. + + **`analysedVersions` / `availableSpecVersions` / `versionDivergenceWarning` -- the + `generatedFrom` split.** The manifest never emits one ambiguous `generatedFrom` key. + `analysedVersions` is `Data/PfbCapabilityMap.json`'s own `generatedFrom` -- the version set + every gap/phantom-field/systemic-gap/convention-strength category below is actually scoped + against. `availableSpecVersions` is whatever `tools/specs/` has cached on disk right now, + which Task 5's enum join and category 3 (ValidateSet drift) DO read fresher-than-analysed + data from (see `Get-PfbValueEnumHistory`'s `$historyResult`). These two normally move + together, which is why keeping them as one key was invisible as a problem for a long time -- + but they are independent inputs that drift the moment specs are refreshed + (`Update-PfbApiSpecs.ps1`) without also rebuilding the capability map + (`Build-PfbCapabilityMap.ps1`): as of this writing, `tools/specs/` is cached through 2.28 while + `Data/PfbCapabilityMap.json` is deliberately still pinned at 2.27 (see item 2's `-MaxVersion` + note above for why that pin is deliberate). `versionDivergenceWarning` is a non-`$null` string + exactly when the two sets disagree, never silently absent -- read it before trusting any other + category in the manifest at face value. (This is the same "validated against whatever spec + happens to be on disk, not against the analysed set" root cause behind the one known, + persistent failure in `Tests/Build-PfbValueEnumMap.Tests.ps1` -- see "Tests" below.) + + **`confidence` and the norm reversal it represents (decision 5).** Every endpoint's gap + lists are *always* computed now -- there is no longer an all-or-nothing gate that discards an + endpoint's real gaps just because one parameter on it has an unresolved surface. This reverses + an earlier, more conservative design, and the reversal is deliberate, not an oversight: + - **The old design.** A `$fullyMapped` gate suppressed an endpoint's entire gap computation + the moment any cmdlet calling it had a parameter whose `Surface` was `AttributesOnly` or + `TypedUnresolved` (i.e. not cleanly `Typed`), routing the endpoint into a `notVerified` + bucket instead -- a bare count in the report's summary, with no detail table anywhere. The + reasoning was sound on its face: such a parameter *might* already cover the apparent gap + through a path this AST-only inventory cannot see, so reporting it outright risked a false + positive. + - **Why it was reversed.** Measured against the real module, roughly 70% of the endpoints + landing in `notVerified` turned out to be tool blindness -- real, fixable parser gaps, not + genuine ambiguity -- and the failure mode was silence: a reader had no way to know what, or + even which endpoints, might be missing something, because `notVerified` carried no detail. + - **The new design.** `notVerified`/`$fullyMapped` no longer exist anywhere in the output + shape. Every endpoint's `MissingQueryParameters`/`MissingBodyProperties`/`ReadOnlyFields` are + always populated. The uncertainty an `AttributesOnly`/`TypedUnresolved` parameter introduces + is instead carried per-endpoint as `confidence`: `{ level: 'high'|'partial', + unresolvedParameters: [{parameter, surface, file, line}], escapeHatchOnly: [...], caveat }`. + `level` is `'high'` iff `unresolvedParameters` is empty. A `'partial'` endpoint's gap lists + can still contain false positives from this same mechanism -- that risk did not go away -- + but they are never silently emptied because of it. The reader gets the field name **and** a + `file:line` to check it themselves (see the false-positive procedure immediately below). + - **What did NOT change.** `Build-PfbFieldCmdletMap.ps1`'s own `attributesOnly`/ + `typedUnresolved` report buckets (see "Field-to-cmdlet mapping" below) are a different norm + answering a different question -- "should this parameter become a `ValidateSet` or + `ArgumentCompleter`" is a categorically different judgment call than "does this endpoint have + an addable gap" -- and remain genuinely "reported, not resolved, requires a human decision." + That norm is not reversed by this change. + + **The false-positive resolution procedure (decision 6).** This report accepts **false + positives in order to eliminate false negatives**. A field is listed as missing even though + the module can already set it, when a parameter covering it could not be traced to a wire + name. Detection is only possible where `confidence.level` is `'partial'`; a `'high'`-confidence + row carries no false-positive risk from this mechanism. Reproduced here verbatim from the + generated Markdown's own "How to read this report" section (see + `tools/Build-PfbApiDriftReport.ps1`) because `tools/README.md` is a standalone reference a + maintainer may read without a generated report open: + 1. Open the named parameter at the given `file:line` and follow where its value goes. + 2. If it reaches the wire under the same name as the reported gap -> the gap is a false + positive AND a tooling bug: the parser does not recognise that idiom. File it as a parser + gap and fix the parser. + 3. If it reaches the wire under a different name -> the reported gap may still be real; check + that field against the spec. + 4. If it never reaches the wire -> the gap is real. + + A false positive here costs a reader one `file:line` lookup; a false negative costs an + undetected gap indefinitely. Every false positive is a parser-gap detector -- it either fixes + the tool permanently for every endpoint, or confirms a real gap. + + **`systemicGaps` / `conventionStrength` (decisions 7-8).** `systemicGaps` collapses every + *high*-confidence gap into one finding per distinct wire field name (never `'partial'` -- + folding a partial-confidence endpoint's possibly-false-positive gap into a systemic FINDING + would overstate a confidence the endpoint's own row already warns against). Each finding is + `{ name, endpointCount, queryEndpointCount, bodyEndpointCount, endpoints, annotations }` -- + turning hundreds of per-endpoint rows into a handful of real, actionable decisions: e.g. + `context_names` and `allow_errors` are each one decision, not one finding per affected + endpoint. `conventionStrength` then ranks each systemic-gap name by how many existing `Public/` + cmdlets already expose it as a `Typed` parameter somewhere in the module (`Get-PfbConventionStrength`), + sorted by that count descending: a high count (`names` at 306 cmdlets) means closing the + remaining gaps for that name is a mechanical batch fix; a count of zero (`context_names`, 0 -- + no cmdlet anywhere in the module has ever exposed this name as a `Typed` parameter) means no + established convention exists to extend at all, and closing it is an architectural decision, + not a mechanical one. Both keys read `docs/drift-annotations.json` (via + `Get-PfbDriftAnnotations`/`Find-PfbDriftAnnotation`) for recorded design decisions, prior + conclusions, or live-testing hazards matched by field name (`systemicGaps[].annotations`) or by + endpoint (`parameterGaps[].annotations`) -- the file is optional; its absence degrades every + lookup to "no annotations" rather than failing report generation. + + **Known limitation: `systemicGaps`/`conventionStrength` only aggregate over high-confidence + endpoints, silently omitting field names that appear ONLY on `'partial'`-confidence rows.** + This is a deliberate choice, not an oversight -- a partial-confidence endpoint's gap list can + contain false positives (see the false-positive procedure above), so folding it into a systemic + FINDING would overstate a confidence the endpoint's own row already warns against. But the + consequence is real: as of this writing, **54 distinct wire field names appear only on + partial-confidence rows and are therefore absent from both `systemicGaps` and + `conventionStrength` entirely** (e.g. `base_dn`, `bind_password`, `domain`, `nameservers`, + `qos_policy`, `storage_class`, `owner`, ...), and a name that DOES appear can still undercount: + `context_names`'s high-confidence-only `systemicGaps` figure is smaller than its true + cross-confidence total across every endpoint, by design (that's the limitation described + above, not a discrepancy worth investigating). No individual endpoint's own + `missingQueryParameters`/`missingBodyProperties` row is affected -- this is purely a gap in the + *aggregated triage view*, not in the underlying per-endpoint data. A future revisit should + aggregate `systemicGaps`/`conventionStrength` across ALL confidence levels, carrying the current + high-confidence-only count as `endpointCount` alongside a separate `partialEndpointCount`, rather + than silently dropping names or re-baselining `endpointCount` itself. Not fixed here because + doing so would change what `context_names`/`allow_errors`/etc. mean, which is a larger, separate + design decision from the invariant-based tests that verify today's high-confidence-only + aggregation (`Tests/PfbApiDriftTools.Tests.ps1`, `Tests/Build-PfbApiDriftReport.Tests.ps1`) -- + those tests recount/bound rather than pin to an exact figure, specifically so this kind of + real-data number does not need to stay in sync with prose like this. + + **`phantomFieldCount` -- two correct, differently-scoped numbers.** How many `(endpoint, + field)` pairs were silently dropped from every gap/read-only list because the field is + accumulated in the capability map's `parameters`/`bodyProperties` dictionaries but absent from + the newest ANALYSED spec (i.e. withdrawn from the real API after the version that first added + it)? Two numbers are simultaneously correct, because they answer different-population + questions: + - Measured across the FULL population of endpoints an existing cmdlet calls, regardless of + confidence level -- this is what `phantomFieldCount` in `Reports/PfbApiDriftReport.json` + actually reports. (Deliberately not pinned to an exact figure in this prose -- it changes + with the real API surface; `Tests/Build-PfbApiDriftReport.Tests.ps1` verifies it via a + self-consistency check instead of an exact number.) + - Measured over ONLY the high-confidence-endpoint subset -- this is the number in + `Get-PfbParameterCoverageGaps`'s own doc comment (illustrative example text, not a contract) + and in this task's real-data invariant tests (verified non-vacuous, likewise not pinned to + an exact figure). + Do not treat a mismatch between the two as a bug; check which population a given historical + reference is scoped to first. Computed by calling `Get-PfbParameterCoverageGaps` a second time + without `-CurrentSpecCapabilities` (its documented no-op default -- no phantom filtering) and + diffing the resulting `(endpoint, list, field)` triples against the real, phantom-filtered run + -- never a re-derivation of the phantom-detection logic itself. + + **The phantom-field limitation, and why it's documented-only.** `Build-PfbCapabilityMap.ps1` + is first-sight-only for *additions* and never records *removal* -- `parameters`/ + `bodyProperties` accumulate forever across every ingested spec version. A field withdrawn from + the API in a later spec (e.g. `PATCH /certificates|id` and `PATCH /certificates|name`: + read-only in every version 2.0-2.19, removed outright from 2.20 onward -- never writable, so + they are phantoms, not settable fields) still lingers in `Data/PfbCapabilityMap.json`'s own + `bodyProperties` dictionary indefinitely. The drift report's phantom-field filtering (this + section, and `Get-PfbParameterCoverageGaps -CurrentSpecCapabilities`) excludes these from every + gap/read-only list in the REPORT by cross-checking against the single newest analysed spec, but + the capability map itself never gets this correction -- it is a fundamentally different kind of + claim ("this field was ever seen" vs. "this field currently exists") and rebuilding the map to + track removal is out of scope for this effort. There is deliberately no `removedFields` + category anywhere in this toolchain. + + **`readOnlyFields` reflects the newest ANALYSED spec, not the accumulated/historical one -- + same reason as `Data/PfbCapabilityMap.json`'s own `readOnlyBodyProperties` (item 2 above).** + `readOnly` is not monotonic: a field can go from read-only to writable across versions (roughly + half the real flips in fb2.0-2.27 do exactly that), so first-sight semantics would wrongly + suppress a field that is genuinely settable today. Both the capability map's own + `readOnlyBodyProperties` and this report's `readOnlyFields` are last-seen-wins for the same + reason: spec text moves, and treating a field's *oldest* seen annotation as authoritative would + misreport its *current* state. + + **The REST 2.17 cliff -- a spec-authoring event, not API evolution.** Naively grepping every + cached spec for raw `"readOnly": true` annotation counts shows an apparent cliff at 2.17: 1091 + sites at 2.16, 408 at 2.17 -- a 63% drop that would lead a careless reader to conclude the API + lost read-only enforcement on hundreds of fields in one release. **It did not.** The real cause + is a `$ref` consolidation: named component (`$ref`) sites jump 10.5x in the same release (385 at + 2.16 -> 4038 at 2.17) -- duplicated inline `readOnly` annotations were consolidated into shared, + referenced components, so one annotation site now serves many endpoints instead of being + repeated inline at each one. Measured on *resolved* (endpoint, field) pairs -- the number that + actually matters -- read-only coverage did not fall across this transition, it **rose**: 219 + pairs at 2.16 to 262 at 2.17. Nothing was un-read-onlied. **This is a trap for whoever later + builds a `-SinceVersion`-style "since version X" delta feature over `readOnly`/`deprecated` + transitions**: a transition-count metric computed from raw annotation sites, or from anything + upstream of `$ref` resolution, will misread this release as instability that never happened. + Use resolved (endpoint, field) pairs (`Get-PfbSchemaPropertyDetails`'s own single resolving + walker, decision 3) for any future metric here, never a raw annotation-site count. + +## Response-shape drift (`Build-PfbResponseShapeMap.ps1`) + +Everything above tracks the **request** side — which endpoints, query parameters, and +request-body fields exist in which REST version. `Data/PfbResponseShapeMap.json` is the +**response** side: for every endpoint (496 of them, across the 29 analysed REST versions +2.0-2.28), the set of fields the spec says its response returns, each with the version it +was first seen in — plus, for fields that have since disappeared, the version they were +last seen in — and each endpoint's own first/last-seen version. (Present fields carry a +scalar `introducedVersion` only: `responseEnvelope`/`responseItemProperties` are +`{field: version}`, and their last-seen version is implicitly the endpoint's own +`lastSeenVersion`. An explicit `lastSeenVersion` appears only on `removedResponseFields` +entries.) `Get-PfbResponseShapeFindings` (`tools/lib/PfbApiDriftTools.ps1`) turns that +history into the drift report's response-side categories: field **removals**, **rename +candidates** (a removal paired with a still-present field on the same endpoint and in the +same location that first appears in the version *immediately after* the removed field's +last-seen version — adjacency, not co-occurrence, and reported as a candidate rather than +asserted), and unhandled envelope fields such as `errors`. + +**It is a separate file from `Data/PfbCapabilityMap.json`, deliberately, because it has no +runtime consumer.** `Private/Get-PfbCapabilityMap.ps1` loads the 298,841-byte capability +map into *every* user session; merging the response axis into it would add 242,917 bytes, +**+81%**, to that load — paid by every user of the module, forever, to serve an analysis +that only the drift report reads. And nothing may ever gate an API call on response data: +the capability check runs *before* the HTTP call, so response shape is not information it +could act on even in principle. The two artifacts answer different questions for different +audiences, so they stay in different files. + +**The monotonicity rule.** Presence is tracked at both ends: `introducedVersion` is +**first-seen** (once a field has appeared, the version it appeared in never changes), and +presence is **last-seen** (the newest analysed version whose spec still declares it). A +field is a **removal** when its last-seen version is older than its endpoint's own +`lastSeenVersion` — i.e. the field is absent from the newest analysed spec *in which its +endpoint still appears*. Note the comparison is against the endpoint's **own** last-seen +version, which is not necessarily the newest analysed version overall: a retired endpoint, +last seen at some older version, would still have its own field removals reported. Today +that distinction is invisible — all 496 endpoints have `lastSeenVersion` 2.28, so the two +coincide — but it is the rule as implemented, and "the endpoint is still in the newest +analysed spec" is not part of it. This is deliberately *not* the same rule as +`readOnly`/`deprecated`, which are **last-seen-wins** (see item 2 above): `readOnly` is an attribute that flips back and forth +and only its *current* value is meaningful, whereas presence is a monotonic timeline whose +endpoints are both meaningful — when a field first appeared and whether it is still there. +Applying last-seen-wins to presence would lose the introduction history; applying +first-sight to `readOnly` would report a stale annotation as current. + +**Known blind spot: transient fields are not reported at all.** There are 11 field-removal +*transition events* in the history but only **7** true removals. The other four are fields +that vanish for exactly one release and come back, all caused by upstream spec-authoring +defects, and all four are present again in fb2.28. The rule above — absent from the newest +analysed version in which the endpoint still exists — correctly excludes them, at the cost +of never surfacing a transient wobble as a finding. The two known cases: +`GET /active-directory`'s `continuation_token` and `total_item_count` (broken in 2.12, +fixed in 2.13 — the response schema dropped its `allOf` composition), and +`GET /buckets/performance`'s `max_total_bytes_per_sec` and `max_total_ops_per_sec` (broken +in 2.20, fixed in 2.21 — fb2.20 drops the `BucketPerformanceItems` component from the +document **entirely** (it is defined in both 2.19 and 2.21) and repoints the response's +`items[]` at the sibling `BucketPerformance` component, which has never carried those two +fields; this is a dropped component, not a mistyped `$ref`). Accepted: a +one-release wobble that the vendor has already fixed is not actionable for this module, +and reporting it would put four known-false rows in front of every reader of the 7 real +ones. + +**The depth trap — read this before changing any `MaxDepth`.** `Get-PfbSpecResponseShapes` +(`tools/lib/PfbSpecTools.ps1`) defaults to **`MaxDepth 32`**, not the `8` used by the other +walkers in that same file. This is not a stylistic inconsistency and must not be +"normalised". fb2.12-2.16 contain deep `allOf` composition chains; at depth 8 those chains +truncate, whole field sets go unseen in the middle of the history, and the presence +accumulator then reports them as vanished — **184 false removals against a true total of +7**. Worse, the failure is invisible from the newest spec: on fb2.28 every depth from 8 to +32 produces identical results, so any check written against current data will look +perfectly correct while the historical middle of the map is silently wrong. 184 removals is +the depth-8 signature — if that number (or anything near it) ever appears, suspect the +depth before suspecting the API. + +**Granularity: envelope plus `items[]`, one level deep.** Each endpoint records its +top-level response envelope fields and the fields of one level of `items[]`. Depth 3 was +measured and rejected: it produced 13 findings, of which 11 were duplicates of a level-1 +parent already reported and the remaining 2 were a spec-authoring false positive — zero new +real signal, at **2.2x** the artifact size. + +**Why additions are not findings.** The module reshapes almost nothing on the way out. +Measured over `Public/` on 2026-08-01 (542 `.ps1` files, one function each): **533 call +`Invoke-PfbApiRequest` at all** — the 9 that never do are session/credential helpers +(`Connect-PfbArray`, `Disconnect-PfbArray`, `Get-PfbConnection`, `Get-PfbApiVersion`, the +three `*-PfbCredential` cmdlets) plus two cmdlets that deliberately bypass +`Invoke-PfbApiRequest` and issue their REST call directly, because its `-Method` +`ValidateSet`/hashtable-only `-Body` cannot express what they need +(`Set-PfbPresetWorkload`, `Set-PfbWorkloadTag`) — and in +**526 of those 533, every one of those calls is a statement-level pipeline whose result is +emitted directly**, never captured into a variable or piped onward. (Only 7 capture or +post-process it, e.g. `Test-PfbConnection`, `Remove-PfbFileSystem`, `Get-PfbQuotaUser`.) +State the counting rule whenever you quote these: "calls it at all" and "emits its result +directly" are different questions with different answers, and an unqualified figure is what +made an earlier version of this paragraph wrong. Consequently a field the API *adds* to a +response reaches the caller automatically with no code change at all. There is nothing +to fix and therefore nothing to report. Only removals (a property callers may already +depend on that has gone away), renames, and unread envelope fields represent work. + +## What's deliberately NOT in the capability map + +The FlashBlade OpenAPI spec has no structural JSON Schema `enum` anywhere — verified +empty across every schema and parameter in both the oldest (fb2.10) and newest (fb2.27) +cached specs. Allowed values for fields like `Bucket.versioning` exist only as free-text +prose in `description` fields ("Valid values are `none`, `enabled`, and `suspended`."), +not as machine-readable constraints, so `Data/PfbCapabilityMap.json` +tracks endpoint, parameter, and request-body top-level property *existence* only — not +their legal values. That prose *is* now extracted, but into a separate file by a separate +generator — see "Value-enum extraction" below — precisely because it's a different kind +of claim with a different reliability bar (see that section for why per-*value* +"introduced in version X" tracking specifically remains out of scope even there). + +Also out of scope: hardware-model capability (//S vs //E — what the module's existing +~12 `-match`-based "not supported on this model" warnings actually gate on). That's a +separate axis from REST version, handled from a different data source and not part of +this toolchain. + +Also deliberately not built: version-aware `ArgumentCompleter`/`DynamicParam`s that would +hide a parameter an array's version doesn't support. Evaluated and shelved — completers +only complete a parameter's *value*, not its *name*, so they can't hide a parameter at +all; only `DynamicParam` can, and that would require editing every `Public/` cmdlet +individually for marginal benefit over the capability check above. + +## Value-enum extraction (`Build-PfbValueEnumMap.ps1`) + +A later, separate phase from the capability map above. `tools/lib/PfbValueEnumTools.ps1` +parses the "Valid/Possible values are/include ..." prose sentence out of a schema +property's or parameter's `description` — the only place these specs record a field's +legal value set, since (as above) there is no structural `enum` to read instead — and +`tools/Build-PfbValueEnumMap.ps1` diffs that across every cached spec version into +`Reports/PfbValueEnumMap.json`: + +```powershell +./tools/Build-PfbValueEnumMap.ps1 +``` + +Key correctness rules (each has a dedicated regression test in +`Tests/PfbValueEnumTools.Tests.ps1`): +- Entries are keyed by **`(SchemaName, PropertyName)`**, never by bare property name. + Two different schemas can share a property name with different legal values (e.g. + `NfsExportPolicyRuleBase.access` is `root-squash`/`all-squash`/`no-squash`, while the + presets-only `_presetWorkloadExportConfigurationNfsRule.access` is + `root-squash`/`all-squash`/`no-root-squash`) — collapsing by bare name would silently + merge them. +- The extractor also covers a parameter defined **inline** directly on a + `spec.paths..` operation — not just `components.schemas` properties and + named `components.parameters` entries. This matters because a field can be inline for + years before a later spec refactor turns it into a `$ref`: `GET /arrays/space`'s `type` + query parameter was inline (full "Valid values are `array`, `file-system`, + `object-store`." description) from REST 2.0 through 2.16, only becoming + `$ref: '#/components/parameters/Type'` at 2.17 — a pure documentation refactor, not an + API change. Without this pass, the field's true `minVersion` (2.0) would be invisible + and its earlier, inline-only history would be lost entirely. Keyed by + `" #"` (`Kind = 'inline-parameter'`), never the bare parameter + name, for the same never-collapse reason as above. +- Value extraction is scoped to the matched trigger *sentence* only, not the whole + description, since some descriptions repeat the same backtick-quoted values again in + explanatory prose that follows the enum sentence. +- A description that matches the trigger phrase but isn't actually a real enumeration + (a numeric range, or free-text prose that happens to contain the words "valid values") + is recorded as **unparsed**, not force-parsed and not silently dropped — surfaced in the + manifest's `unparsedCount`/`unparsed` fields, same "never silently over-claim coverage" + norm as the capability map's own coverage reporting. + +The builder also writes `Reports/PfbValueEnumReconciliation.md`, comparing every existing +hand-written cmdlet `ValidateSet` that encodes a spec-documented enum against this newly +extracted data (exact match / stale / not-found / collision with an unrelated same-named +field elsewhere in the spec). That report is informational only — it does not edit any +`Public/` cmdlet. + +**`Reports/PfbValueEnumMap.json`'s output is not consumed anywhere at runtime yet** — no +`ArgumentCompleter`, no `Assert-PfbApiCapability` enforcement. Whether/how to consume it +is a deliberate follow-on decision once real coverage/accuracy numbers exist from this +data, same as how the capability map above sat idle until `Assert-PfbApiCapability` was +built to consume it. + +Per-enum-*value* "introduced in version X" tracking (e.g. knowing that `suspended` was +added to `Bucket.versioning` at some later REST version, as opposed to the field's own +overall `minVersion`) is intentionally not attempted — no reliable way was found to diff +individual values across versions given how often the same prose gets reworded without +the value set itself changing. The manifest tracks each field's current legal value set +and the field's own earliest-seen version only. + +## Field-to-cmdlet mapping (`Build-PfbFieldCmdletMap.ps1`) + +Joins the cmdlet parameter inventory (from `PfbCmdletParamTools.ps1`, which reads `Public/` +cmdlet ASTs) against the prose value-enum data extracted above to recommend, per typed +`Public/` parameter that lacks a `ValidateSet` today, whether it should become a +`ValidateSet` or an `ArgumentCompleter`: + +```powershell +./tools/Build-PfbFieldCmdletMap.ps1 +``` + +Key correctness rules: +- A parameter is recommended `ValidateSet` only if: the parameter's wire field appears in + the spec, it has a documented value enumeration, that enumeration is present unchanged in + every REST version from the field's introduction onward, and the field was present since + the oldest cached version. +- A parameter is recommended `ArgumentCompleter` if the field exists but the value set + changed at any point in the history, or the field was introduced in a newer version. +- A parameter is classified `collision` if its wire name matches multiple schema keys with + different value sets, or `not-found-in-resource` if the wire name exists in the spec but + not under any schema the cmdlet's resource hint suggests. Both require manual follow-up + to ensure the intent is captured. +- An **`inline-parameter`**-kind value-enum record (see "Value-enum extraction" above) is + keyed by exact endpoint identity (`" #"`), so when + `PfbCmdletParamTools.ps1`'s AST inventory can determine exactly which endpoint a given + cmdlet parameter calls, an exact match there overrides an otherwise-ambiguous + `parameter`-kind wire name — this is precisely how `Get-PfbArraySpace -Type` resolves to + `matched`/`ValidateSet` instead of `collision`, even though its wire name `type` also + matches two disagreeing `components.parameters` definitions (`Type` and + `Type_for_performance`) elsewhere in the spec. +- `attributesOnly` and `typedUnresolved` entries are *reported*, not resolved — they list + parameters that either have no typed field to attach validation to (attributes-only), + or have a wire name that couldn't be resolved to a spec key. These require human + decisions (add a new typed parameter, or leave as-is); the script does not edit any + `Public/` cmdlet. + +The builder also writes `Reports/PfbFieldCmdletMapping.md`, a Markdown table summarizing +every candidate and its recommendation — informational only, not consumed at runtime. + +**`Reports/PfbFieldCmdletMap.json`'s output is not consumed anywhere at runtime yet** — no +`ValidateSet` or `ArgumentCompleter` is added to any `Public/` cmdlet by this script. +Whether/how to consume it is a deliberate follow-on decision. + +## Tests + +`Tests/PfbSpecTools.Tests.ps1` and `Tests/Build-PfbCapabilityMap.Tests.ps1` cover the +capability-map extraction/diffing logic against small synthetic fixtures — no network +access required. One additional test in `Build-PfbCapabilityMap.Tests.ps1` checks the +real committed manifest for coverage gaps against the newest locally-cached spec, and +skips gracefully if `tools/specs/` (gitignored — run `Update-PfbApiSpecs.ps1` first) or +`Data/PfbCapabilityMap.json` aren't present. + +`Tests/PfbVersionMapTools.Tests.ps1` covers the SSOT URL builder and HTML table parser +the same way — no network access, and no real base URI/topic ID/key needed, since those +are just parameters to a pure, synthetic-fixture-driven function. + +`Tests/PfbValueEnumTools.Tests.ps1` and `Tests/Build-PfbValueEnumMap.Tests.ps1` cover the +value-enum extraction/diffing logic the same way, plus a `Bucket.versioning` regression +fixture and a squash-mode-gotcha fixture (see "Value-enum extraction" above). Their real- +manifest checks skip gracefully if `tools/specs/` or `Reports/PfbValueEnumMap.json` aren't +present. + +**The one known, persistent failure.** When `tools/specs/` is locally cached past the +version `Data/PfbCapabilityMap.json` is pinned to, one real-manifest check in +`Tests/Build-PfbValueEnumMap.Tests.ps1` fails: it validates the value-enum map against +whatever spec version is newest on disk (`fb2.28`, as of this writing), not against the +2.27 version the capability map is deliberately pinned to (see item 2's `-MaxVersion` note +above). This is a pre-existing condition of the local dev environment, not a regression +from any task in this plan -- none of this plan's tasks touched value-enum extraction -- +and it does not reproduce in CI, which always fetches specs fresh rather than relying on a +locally-cached, possibly-ahead-of-pin `tools/specs/`. + +## CI + +`.github/workflows/update-api-capability-map.yml` runs this pipeline weekly (and on +manual dispatch): re-fetches the full spec history into an ephemeral (non-committed) +cache, rebuilds `Data/PfbCapabilityMap.json`, and opens a PR if it changed — i.e. the +swagger index published a new REST version, or an existing endpoint gained new +parameters/fields. Requires the repository's Actions settings to permit workflow-created +pull requests. The `SSOT_API_KEY`/`SSOT_BASE_URI`/`SSOT_TOPIC_ID` secrets are optional — +when any are absent, the version-map step is skipped gracefully and only the capability +map updates (see item 3 above). + +`Build-PfbValueEnumMap.ps1`, `Build-PfbFieldCmdletMap.ps1`, `Build-PfbResponseShapeMap.ps1`, +and `Build-PfbApiDriftReport.ps1` all run as part of the same weekly/dispatch job, right +after the capability map is rebuilt, so `Reports/PfbValueEnumMap.json`, +`Reports/PfbFieldCmdletMap.json`, `Reports/PfbApiDriftReport.json` (+ their Markdown +companions) and `Data/PfbResponseShapeMap.json` stay fresh alongside +`Data/PfbCapabilityMap.json`. + +`Build-PfbResponseShapeMap.ps1` sits between `Build-PfbFieldCmdletMap.ps1` and +`Build-PfbApiDriftReport.ps1`, and that position is required in both directions. It must +come **after** the spec-fetch step, because the generator only walks `tools/specs/` and +never fetches anything itself — run before the fetch, it would build the map from whatever +the restored cache happened to hold. It must come **before** the drift-report step, because +the report reads `Data/PfbResponseShapeMap.json`; run after, CI would emit a report derived +from the stale committed map and then overwrite that map, leaving a report and a map that +disagree with no error to signal it. (If the file were ever absent entirely the report emits +an explicit "Not analysed" banner rather than a silently empty section, so a missing map is +at least visible — but a *stale* one is not, which is why the ordering rather than the +presence check is what protects the report.) The drift report's three response-shape counts +are also surfaced in the workflow's PR body via the `drift_summary` output. diff --git a/tools/Update-PfbApiSpecs.ps1 b/tools/Update-PfbApiSpecs.ps1 new file mode 100644 index 0000000..b65a49c --- /dev/null +++ b/tools/Update-PfbApiSpecs.ps1 @@ -0,0 +1,102 @@ +#Requires -Version 7.0 +<# +.SYNOPSIS + Fetches every published FlashBlade REST API version's OpenAPI spec and caches it + locally as pretty-printed JSON. +.DESCRIPTION + Discovers the list of published versions from https://code.purestorage.com/swagger/, + then for each one downloads its ReDoc reference page and extracts the embedded + OpenAPI 3.0.1 document (see tools/lib/PfbSpecTools.ps1 for why this can't be a + simple file download). Existing cached specs are skipped unless -Force is passed, + so re-runs (including the scheduled CI job) only fetch newly-published versions. + + Output: tools/specs/fb.json (one file per REST version), cached on disk under + a gitignored directory (see tools/README.md for why) so Build-PfbCapabilityMap.ps1 runs + offline/incrementally without re-fetching every time. +.PARAMETER OutputDirectory + Where to write cached spec JSON files. Defaults to tools/specs relative to this + script's location. +.PARAMETER Force + Re-fetch and overwrite specs that are already cached. +.PARAMETER Versions + Fetch only these specific version strings (e.g. '2.26','2.27') instead of the full + discovered list. Useful for spot-checks and CI dry runs. +.EXAMPLE + ./tools/Update-PfbApiSpecs.ps1 +.EXAMPLE + ./tools/Update-PfbApiSpecs.ps1 -Versions 2.26,2.27 -Force +#> +[CmdletBinding()] +param( + [string]$OutputDirectory, + + [switch]$Force, + + [string[]]$Versions +) + +$ErrorActionPreference = 'Stop' + +$scriptDir = $PSScriptRoot +. (Join-Path $scriptDir 'lib/PfbSpecTools.ps1') + +if (-not $OutputDirectory) { + $OutputDirectory = Join-Path $scriptDir 'specs' +} +if (-not (Test-Path $OutputDirectory)) { + New-Item -ItemType Directory -Path $OutputDirectory -Force | Out-Null +} + +$indexUri = 'https://code.purestorage.com/swagger/' + +if ($Versions) { + $targetVersions = $Versions +} +else { + Write-Host "Fetching version index from $indexUri..." -ForegroundColor Cyan + $indexResponse = Invoke-WebRequest -Uri $indexUri -UseBasicParsing + $targetVersions = Get-PfbSwaggerIndexVersions -IndexHtml $indexResponse.Content + Write-Host "Discovered $($targetVersions.Count) published versions: $($targetVersions -join ', ')" -ForegroundColor Gray +} + +$fetched = [System.Collections.Generic.List[string]]::new() +$skipped = [System.Collections.Generic.List[string]]::new() +$failed = [System.Collections.Generic.List[string]]::new() + +foreach ($version in $targetVersions) { + $outPath = Join-Path $OutputDirectory "fb$version.json" + + if ((Test-Path $outPath) -and -not $Force) { + $skipped.Add($version) + continue + } + + $pageUri = "https://code.purestorage.com/swagger/redoc/fb$version-api-reference.html" + Write-Host "Fetching $pageUri..." -ForegroundColor Cyan + + try { + $page = Invoke-WebRequest -Uri $pageUri -UseBasicParsing + $spec = ConvertFrom-PfbRedocHtml -Html $page.Content + + if (-not $spec.openapi) { + throw "Extracted document has no 'openapi' field — extraction likely failed silently." + } + + # Re-serialize pretty-printed for easier local diffing/inspection of the cache. + # Depth must cover the deepest schema nesting (allOf chains, nested component refs). + $spec | ConvertTo-Json -Depth 64 | Set-Content -Path $outPath -Encoding UTF8 + Write-Host " -> $outPath ($((Get-Item $outPath).Length / 1KB | ForEach-Object { '{0:N0} KB' -f $_ }))" -ForegroundColor Green + $fetched.Add($version) + } + catch { + Write-Warning "Failed to fetch/extract version '$version': $($_.Exception.Message)" + $failed.Add($version) + } +} + +Write-Host '' +Write-Host "Fetched: $($fetched.Count) Skipped (cached): $($skipped.Count) Failed: $($failed.Count)" -ForegroundColor Cyan +if ($failed.Count -gt 0) { + Write-Host "Failed versions: $($failed -join ', ')" -ForegroundColor Red + exit 1 +} diff --git a/tools/Update-PfbVersionMap.ps1 b/tools/Update-PfbVersionMap.ps1 new file mode 100644 index 0000000..e15b189 --- /dev/null +++ b/tools/Update-PfbVersionMap.ps1 @@ -0,0 +1,123 @@ +#Requires -Version 7.0 +<# +.SYNOPSIS + Builds/updates the REST-API-version <-> Purity//FB-version map from the SSOT + (Single Source of Truth) API. +.DESCRIPTION + Each FlashBlade REST API version has a corresponding row in a single "FlashBlade + Management REST API Reference" table. This script fetches that table in + one call and derives the REST<->Purity//FB pairing, writing Data/PfbVersionMap.json + so runtime code (the capability check in Private/Assert-PfbApiCapability.ps1) can + render messages like "requires REST 2.26 / Purity//FB 4.8.1" without a live lookup. + + The base URI and topic ID are internal details and are not hardcoded in this repo - + both are required inputs, sourced from environment variables as with the API key. + + When the API key, base URI, or topic ID aren't configured (local dev without them, + or CI without the secrets set), this script does not fail - it reports which + versions still need lookup and exits, leaving PfbVersionMap.json untouched. +.PARAMETER SsotApiKey + API key for the SSOT proxy (`x-api-key` header). Defaults to $env:SSOT_API_KEY. +.PARAMETER BaseUri + Base URI of the SSOT proxy. Defaults to $env:SSOT_BASE_URI. +.PARAMETER TopicId + SSOT topic ID for the version-mapping reference page. Defaults to $env:SSOT_TOPIC_ID. +.PARAMETER Versions + REST versions to look up (e.g. '2.26','2.27'). Defaults to every version discovered + under tools/specs/ that is not yet present in Data/PfbVersionMap.json. +.PARAMETER OutputPath + Where to write the map. Defaults to Data/PfbVersionMap.json relative to the repo root. +#> +[CmdletBinding()] +param( + [string]$SsotApiKey = $env:SSOT_API_KEY, + + [string]$BaseUri = $env:SSOT_BASE_URI, + + [string]$TopicId = $env:SSOT_TOPIC_ID, + + [string[]]$Versions, + + [string]$OutputPath +) + +$ErrorActionPreference = 'Stop' + +$scriptDir = $PSScriptRoot +$repoRoot = Split-Path -Parent $scriptDir +. (Join-Path $scriptDir 'lib/PfbVersionMapTools.ps1') + +if (-not $OutputPath) { + $OutputPath = Join-Path (Join-Path $repoRoot 'Data') 'PfbVersionMap.json' +} + +# --- Determine which versions need looking up --- + +$existingMap = [ordered]@{} +if (Test-Path $OutputPath) { + (Get-Content -Path $OutputPath -Raw | ConvertFrom-Json -Depth 5).PSObject.Properties | + ForEach-Object { $existingMap[$_.Name] = $_.Value } +} + +if (-not $Versions) { + $specsDir = Join-Path $scriptDir 'specs' + if (Test-Path $specsDir) { + $Versions = Get-ChildItem -Path $specsDir -Filter 'fb*.json' | + Where-Object { $_.BaseName -match '^fb(\d+\.\d+)$' } | + ForEach-Object { $Matches[1] } | + Where-Object { -not $existingMap.Contains($_) } + } +} + +if (-not $Versions) { + Write-Host 'No versions need looking up (nothing new since the last run).' -ForegroundColor Green + return +} + +$missingConfig = @() +if (-not $SsotApiKey) { $missingConfig += '$env:SSOT_API_KEY / -SsotApiKey' } +if (-not $BaseUri) { $missingConfig += '$env:SSOT_BASE_URI / -BaseUri' } +if (-not $TopicId) { $missingConfig += '$env:SSOT_TOPIC_ID / -TopicId' } + +if ($missingConfig) { + Write-Warning @" +SSOT not fully configured - missing: $($missingConfig -join ', '). This step is +skipped in that case rather than failing - CI will proceed without updating the version +map, and Data/PfbVersionMap.json is left untouched. + +Versions still needing a REST<->Purity mapping: $($Versions -join ', ') +"@ + return +} + +# --- Fetch + parse the full mapping table in one call --- + +$uri = Get-PfbSsotVersionMapUri -BaseUri $BaseUri -TopicId $TopicId +Write-Host "Fetching $uri..." -ForegroundColor Cyan + +$response = Invoke-WebRequest -Uri $uri -Headers @{ 'x-api-key' = $SsotApiKey } -UseBasicParsing +$parsed = ConvertFrom-PfbSsotVersionMapHtml -Html $response.Content + +$updated = $false +foreach ($version in $Versions) { + if ($parsed.Contains($version)) { + $existingMap[$version] = $parsed[$version] + $updated = $true + Write-Host " -> REST $version = Purity//FB $($parsed[$version].purity)" -ForegroundColor Green + } + else { + Write-Warning "SSOT table has no row for REST $version." + } +} + +if ($updated) { + $outputDir = Split-Path -Parent $OutputPath + if (-not (Test-Path $outputDir)) { + New-Item -ItemType Directory -Path $outputDir -Force | Out-Null + } + $existingMap | ConvertTo-Json -Depth 5 | Set-Content -Path $OutputPath -Encoding UTF8 + Write-Host "Wrote $OutputPath" -ForegroundColor Green +} +else { + Write-Host 'No entries were successfully parsed; output left unchanged.' -ForegroundColor Yellow +} diff --git a/tools/lib/PfbApiDriftTools.ps1 b/tools/lib/PfbApiDriftTools.ps1 new file mode 100644 index 0000000..204d8f2 --- /dev/null +++ b/tools/lib/PfbApiDriftTools.ps1 @@ -0,0 +1,1571 @@ +<# +.SYNOPSIS + Category 1 (uncovered endpoints) and category 2 (parameter gaps) of the API drift + report. Dot-sourced by tools/Build-PfbApiDriftReport.ps1 and its Pester tests. + Category 3 (ValidateSet drift) lives here too -- see Get-PfbValidateSetDrift, added + in a later task -- reusing Resolve-PfbFieldValueEnum from + tools/lib/PfbValueEnumTools.ps1. Category 4 (new ValidateSet candidates) needs no new + code at all: it's tools/Build-PfbFieldCmdletMap.ps1's existing 'matched' output, + consumed directly by the orchestrator. +#> + +# Deliberately NOT Set-StrictMode -- same reasoning as PfbSpecTools.ps1/PfbValueEnumTools.ps1. + +# Endpoints reached exclusively through hand-written auth code that never calls +# Invoke-PfbApiRequest (it's what establishes the session Invoke-PfbApiRequest itself +# depends on) -- confirmed by reading Public/Connection/Connect-PfbArray.ps1, +# Public/Connection/Disconnect-PfbArray.ps1, and Private/Invoke-PfbOAuth2Login.ps1 +# directly. admins/api-tokens is deliberately NOT here: Connect-PfbArray.ps1 also touches +# it via raw Invoke-RestMethod for post-login token minting, but GET/POST/DELETE +# admins/api-tokens are already covered through the standard Invoke-PfbApiRequest +# convention by Public/Admin/{Get,New,Remove}-PfbApiToken.ps1, so Get-PfbModuleCalledEndpoints's +# normal scan already sees them without needing this allowlist. +$script:PfbBespokeAuthEndpoints = @( + 'GET /api/api_version', + 'POST /api/login', + 'POST /api/logout', + 'POST /oauth2/1.0/token' +) + +# Fields never worth reporting as a category-2 gap, each for its own confirmed reason -- +# not the same list as $script:PfbBespokeAuthEndpoints (that's endpoint keys; this is +# field names). Deliberately excludes context_names/allow_errors/sort/total_only -- those +# change request *behavior* (fleet-context targeting, bulk-error tolerance, sorting, +# aggregate-only responses) and are real, if broad, gaps, not noise. +# - X-Request-ID: no functional effect on the request -- request/response-log +# correlation only ("Supplied by client during request or generated by server," per +# the spec), present on 629/632 capability-map endpoints as of 2.27 with zero cmdlets +# exposing it anywhere (confirmed via grep of Public/+Private/). No Private/ injection +# site exists for it either -- confirmed by Get-PfbCentralInjectionSites finding zero +# assignment sites for this key -- so it stays a hand-written entry: there is nothing +# to derive an absence FROM. +# - offset: an alternate, unused positioning mechanism into the same paginated +# collections -AutoPaginate already retrieves in full; co-occurs with +# continuation_token on 208/212 of its capability-map endpoints, and zero cmdlets +# expose it. Same as X-Request-ID: no Private/ injection site exists, so this stays +# hand-written too. +# +# continuation_token is DELIBERATELY NOT here any more (Task 6) -- it used to be, with a +# hand-written justification identical in spirit to the two above ("superseded by +# Invoke-PfbApiRequest's own -AutoPaginate"). That hand-written comment could go silently +# stale the moment Invoke-PfbApiRequest.ps1 changed shape -- e.g. if a future edit sourced +# it from a parameter instead of `$response`, or stopped injecting it at all -- with +# nothing to notice. It is now DERIVED instead, by Get-PfbNonActionableParameters (below), +# from Get-PfbCentralInjectionSites' live AST scan of Private/Invoke-PfbApiRequest.ps1's +# actual `$QueryParams['continuation_token'] = $response.continuation_token` assignment +# (currently line 227): its right-hand side traces to `$response` -- server-issued data +# from the just-completed Invoke-RestMethod call, never a cmdlet/caller parameter, so no +# caller could meaningfully supply this value even if a typed parameter existed for it. +# This is a STRONGER claim than the general injection-detection mechanism's usual +# "coverage, not correctness" caveat (see Get-PfbCentralInjectionSites/ +# Get-PfbDerivedNonActionableParameters's own docs): continuation_token is structurally +# unable to ever become a cmdlet parameter, not merely "not currently seen as a gap". +$script:PfbNonActionableParameters = @( + 'X-Request-ID', + 'offset' +) + +. (Join-Path $PSScriptRoot 'PfbValueEnumTools.ps1') +. (Join-Path $PSScriptRoot 'PfbCmdletParamTools.ps1') + +function Test-PfbApiVersionNewerThan { + <# + .SYNOPSIS + Tests whether REST version string -Version is numerically newer than -Baseline. + Naive string comparison ranks '2.9' above '2.26' -- splits each into Major/Minor + integers instead (same idiom as Get-PfbValueEnumHistory in PfbValueEnumTools.ps1). + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] [string]$Version, + [Parameter(Mandatory)] [string]$Baseline + ) + + $vParts = $Version -split '\.' + $bParts = $Baseline -split '\.' + $vMajor = [int]$vParts[0]; $vMinor = [int]$vParts[1] + $bMajor = [int]$bParts[0]; $bMinor = [int]$bParts[1] + + if ($vMajor -ne $bMajor) { return $vMajor -gt $bMajor } + return $vMinor -gt $bMinor +} + +function Get-PfbModuleCalledEndpoints { + <# + .SYNOPSIS + Scans every Public/*.ps1 and Private/*.ps1 function for Invoke-PfbApiRequest + calls, extracting each literal -Method/-Endpoint pair, normalized to the same + " /" key format Data/PfbCapabilityMap.json uses (see + Private/Assert-PfbApiCapability.ps1:40-41). + .OUTPUTS + [PSCustomObject]@{ Key; Method; Endpoint; Resolved; Cmdlet; File }[] -- Resolved + is $false (Key/Method/Endpoint all $null) when a call's -Endpoint isn't a plain + string literal (built dynamically via interpolation or a variable), never + silently dropped or guessed at. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] [string]$PublicDirectory, + [Parameter(Mandatory)] [string]$PrivateDirectory + ) + + $results = [System.Collections.Generic.List[object]]::new() + # Belt-and-braces only -- the load-bearing sort is on the records at the end of this + # function (issue #85). Public/ and Private/ are sorted separately, preserving the + # Public-then-Private grouping this scan has always emitted in. + $files = @(Get-ChildItem -Path $PublicDirectory -Filter '*.ps1' -Recurse -File | Sort-Object -Property FullName -Culture '') + + @(Get-ChildItem -Path $PrivateDirectory -Filter '*.ps1' -Recurse -File | Sort-Object -Property FullName -Culture '') + + foreach ($file in $files) { + $tokens = $null; $parseErrors = $null + $ast = [System.Management.Automation.Language.Parser]::ParseFile($file.FullName, [ref]$tokens, [ref]$parseErrors) + $functionAsts = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) + + foreach ($funcAst in $functionAsts) { + $commands = $funcAst.FindAll({ + param($n) + $n -is [System.Management.Automation.Language.CommandAst] -and + $n.GetCommandName() -eq 'Invoke-PfbApiRequest' + }, $true) + + foreach ($cmd in $commands) { + $method = $null; $endpoint = $null + $elements = $cmd.CommandElements + for ($i = 0; $i -lt $elements.Count; $i++) { + $el = $elements[$i] + if ($el -isnot [System.Management.Automation.Language.CommandParameterAst]) { continue } + $next = if ($i + 1 -lt $elements.Count) { $elements[$i + 1] } else { $null } + if (-not $next) { continue } + if ($el.ParameterName -eq 'Method' -and $next -is [System.Management.Automation.Language.StringConstantExpressionAst]) { + $method = $next.Value + } + elseif ($el.ParameterName -eq 'Endpoint' -and $next -is [System.Management.Automation.Language.StringConstantExpressionAst]) { + $endpoint = $next.Value + } + } + + if ($method -and $endpoint) { + $normalizedEndpoint = '/' + $endpoint.TrimStart('/') + $results.Add([PSCustomObject]@{ + Key = "$($method.ToUpperInvariant()) $normalizedEndpoint" + Method = $method.ToUpperInvariant() + Endpoint = $normalizedEndpoint + Resolved = $true + Cmdlet = $funcAst.Name + File = $file.FullName + }) + } + else { + $results.Add([PSCustomObject]@{ + Key = $null + Method = $null + Endpoint = $null + Resolved = $false + Cmdlet = $funcAst.Name + File = $file.FullName + }) + } + } + } + } + + # Canonical emit order, not the filesystem's (issue #85) -- see + # Get-PfbCmdletParameterInventory's own comment in tools/lib/PfbCmdletParamTools.ps1 for + # the mechanism and the measured blast radius. $files above walks Public/ AND Private/ + # unsorted, and every downstream Group-Object here takes its group order from + # first-appearance, i.e. from that walk. -Culture '' pins the comparison to the invariant + # culture so the runner's locale cannot reintroduce the divergence. + return @($results | Sort-Object -Property Cmdlet, Key, File -Culture '') +} + +function Get-PfbEndpointCoverageGaps { + <# + .SYNOPSIS + Category 1: every Data/PfbCapabilityMap.json endpoint key that no + Get-PfbModuleCalledEndpoints result covers and that isn't on -BespokeAllowlist. + .PARAMETER SinceVersion + When given, only endpoints whose minVersion is strictly newer than this REST + version are returned -- e.g. -SinceVersion '2.26' isolates gaps introduced by + 2.27 only, dropping the rest of the accumulated backlog. + .OUTPUTS + [PSCustomObject]@{ Endpoint; MinVersion }[] + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] $CapabilityMap, + [Parameter(Mandatory)] [object[]]$CalledEndpoints, + [string[]]$BespokeAllowlist = @(), + [string]$SinceVersion + ) + + $calledKeys = [System.Collections.Generic.HashSet[string]]::new([string[]]@( + $CalledEndpoints | Where-Object { $_.Resolved } | ForEach-Object { $_.Key } + )) + + foreach ($key in $CapabilityMap.endpoints.PSObject.Properties.Name) { + if ($calledKeys.Contains($key)) { continue } + if ($BespokeAllowlist -contains $key) { continue } + $minVersion = $CapabilityMap.endpoints.$key.minVersion + if ($SinceVersion -and -not (Test-PfbApiVersionNewerThan -Version $minVersion -Baseline $SinceVersion)) { continue } + [PSCustomObject]@{ + Endpoint = $key + MinVersion = $minVersion + } + } +} + +function Get-PfbParameterCoverageGaps { + <# + .SYNOPSIS + Category 2: for each endpoint an existing cmdlet already calls, flags + capability-map-known query parameters and request-body properties the cmdlet + doesn't expose as a typed parameter. Gaps are ALWAYS computed -- there is no + longer an all-or-nothing gate that discards an endpoint's real gaps just because + ONE parameter on it has an unresolved surface (design decision 5: the old + `$fullyMapped` gate's failure mode was silence -- ~70% of its non-Typed + classifications turned out to be tool blindness, not real gaps, and none of it + was visible because the report simply got quieter). Uncertainty from an + unresolved (AttributesOnly/TypedUnresolved) parameter is instead carried + per-endpoint as `Confidence` (see .OUTPUTS): such a parameter may already cover + an apparent gap through a path this AST-only inventory can't see, so the gap + lists can contain false positives on a 'partial'-confidence endpoint -- but they + are never silently emptied because of that uncertainty. + + Output is split three ways instead of one flat list: + - MissingQueryParameters: capability-map `parameters` the cmdlet doesn't expose. + - MissingBodyProperties: capability-map `bodyProperties` the cmdlet doesn't + expose AND that are NOT read-only in the newest ANALYSED spec (addable). + - ReadOnlyFields: the same source, but read-only in the newest ANALYSED spec + (not addable -- reported separately, never merged into MissingBodyProperties). + Only 1 of 632 real endpoints has the same field name land in both the query and + body lists (POST /workloads/placement-recommendations|placement_names) -- no + dedup machinery is applied between them. + .PARAMETER SinceVersion + When given, a gap's missing-field lists are filtered down to only fields whose + capability-map version is strictly newer than this REST version -- e.g. + -SinceVersion '2.26' isolates fields introduced by 2.27 only. An endpoint whose + every missing field predates -SinceVersion is dropped entirely (not emitted with + empty lists). + .PARAMETER ExcludedFields + Field names never reported as a missing parameter, regardless of version -- + e.g. $script:PfbNonActionableParameters. Applied to BOTH + MissingQueryParameters and MissingBodyProperties: every field in that allowlist + happens to be query-side today, but nothing about the mechanism is query-only, + so a future body-side addition is not silently ignored. An endpoint whose every + missing field is excluded is dropped entirely (not emitted with empty lists). + .PARAMETER CurrentSpecCapabilities + Optional. Get-PfbSpecCapabilities's output for ONLY the single newest ANALYSED + spec -- i.e. tools/specs/fb.json where is + $CapabilityMap.generatedFrom[-1] -- never whatever happens to be newest on disk + under tools/specs/, which can be ahead of the map (e.g. specs refreshed to 2.28 + without rebuilding a 2.27 map produces annotations that can't be reconciled with + the gaps they sit next to). $CapabilityMap's own `parameters`/`bodyProperties` + dictionaries accumulate every field ever seen across every ingested version and + never record removal (Build-PfbCapabilityMap.ps1 is first-sight for both), so a + field withdrawn from the API in a later spec still lingers there forever -- a + "phantom". Without this parameter phantom fields are NOT filtered (a safe no-op + default so unit tests built on synthetic capability maps, never cross-checked + against a real spec, are unaffected); with it, a candidate missing field absent + from the endpoint's CURRENT parameter/body-property set is dropped from every + list rather than reported as an addable/read-only/query gap it does not actually + exist to be. 13 real (endpoint, field) pairs hit this against fb2.27 today, all + body-side (e.g. `PATCH /certificates|id` -- read-only 2.0-2.19, removed 2.20+). + .OUTPUTS + [PSCustomObject]@{ Endpoint; Cmdlets; MissingQueryParameters; MissingBodyProperties; + ReadOnlyFields; Confidence }[], one entry per endpoint where at least one of the + three field lists is non-empty. `notVerified` no longer exists anywhere in this + shape -- not as a field, not as a bucket. Confidence is + [PSCustomObject]@{ Level; UnresolvedParameters; EscapeHatchOnly; Caveat }: + - Level is 'high' iff UnresolvedParameters is empty, else 'partial'. + - UnresolvedParameters is [PSCustomObject]@{ Parameter; Surface; File; Line }[] + -- every non-Typed (AttributesOnly or TypedUnresolved) parameter on any + cmdlet calling this endpoint, Line coming from + Get-PfbCmdletParameterInventory's Line field + ($p.Extent.StartLineNumber) so every caveat is a click-through. + - EscapeHatchOnly is the subset of UnresolvedParameters' Parameter names whose + Surface is 'AttributesOnly' (a real -Attributes escape hatch exists -- + 'TypedUnresolved' has none at all). + - Caveat is a human-readable summary of what the confidence gap means for this + endpoint's lists, or '' when Level is 'high'. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] $CapabilityMap, + [Parameter(Mandatory)] [object[]]$CmdletInventory, + [Parameter(Mandatory)] [object[]]$CalledEndpoints, + [string]$SinceVersion, + [string[]]$ExcludedFields = @(), + [object[]]$CurrentSpecCapabilities = @() + ) + + $inventoryByCmdlet = $CmdletInventory | Group-Object -Property Cmdlet -AsHashTable -AsString + # Sort the GROUPS, not just their contents (issue #85): Group-Object's group order is + # first-appearance order in its input, so without this the `parameterGaps` row order in + # Reports/PfbApiDriftReport.json is whatever order the Public/ file walk happened to + # discover each endpoint in. + $endpointGroups = @($CalledEndpoints | Where-Object { $_.Resolved } | Group-Object -Property Key | + Sort-Object -Property Name -Culture '') + + # Endpoint key (" /", matching Data/PfbCapabilityMap.json's own key + # format) -> the Get-PfbSpecCapabilities record for that endpoint in the single + # newest ANALYSED spec, used only for phantom-field exclusion (see + # -CurrentSpecCapabilities above). A typed Dictionary, not a Hashtable: endpoint keys + # are not API field names so this one specifically isn't exposed to the + # .Keys-shadowing bug this function is otherwise built around avoiding, but nothing + # in this task trusts that reasoning per-callsite. + $currentByEndpoint = [System.Collections.Generic.Dictionary[string, object]]::new() + foreach ($cap in $CurrentSpecCapabilities) { + $currentByEndpoint["$($cap.Method) $($cap.Path)"] = $cap + } + + $gaps = [System.Collections.Generic.List[object]]::new() + + foreach ($group in $endpointGroups) { + $key = $group.Name + $entry = $CapabilityMap.endpoints.$key + if (-not $entry) { continue } + + # Sort-Object -Unique, not Select-Object -Unique: the latter preserves INPUT order and + # does not sort, which is the intra-row cmdlet flip observed in issue #85 + # (`Get-PfbArray, Test-PfbConnection` becoming `Test-PfbConnection, Get-PfbArray`). + # Matches the already-correct pattern in Get-PfbConventionStrength below. + $cmdlets = @($group.Group.Cmdlet | Sort-Object -Unique -Culture '') + + $exposedWireNames = [System.Collections.Generic.HashSet[string]]::new() + $unresolved = [System.Collections.Generic.List[object]]::new() + foreach ($cmdletName in $cmdlets) { + # The Where-Object is load-bearing, not defensive noise: Group-Object + # -AsHashTable yields $null for an absent key, and @($null) is a ONE-element + # array whose single element is $null -- not the empty array it reads as. + # Without it, this loop would run once with $row = $null and throw evaluating + # $null.Surface. A cmdlet contributes zero inventory rows when every + # parameter it declares is inventory-exempt (-Array/-Attributes are + # plumbing, never inventoried -- see Get-PfbCmdletParameterInventory), which + # is exactly the shape of the -Attributes-only write cmdlets (Update-PfbArray, + # Update-PfbPasswordPolicy, Update-PfbSupport, ...). No rows means nothing to + # report as unresolved for that cmdlet. + $rows = @($inventoryByCmdlet[$cmdletName] | Where-Object { $null -ne $_ }) + foreach ($row in $rows) { + if ($row.Surface -ne 'Typed') { + $unresolved.Add([PSCustomObject]@{ + Parameter = $row.Parameter + Surface = $row.Surface + File = $row.File + Line = $row.Line + }) + continue + } + if ($row.WireName) { [void]$exposedWireNames.Add($row.WireName) } + } + } + + $currentCap = $null + if ($currentByEndpoint.ContainsKey($key)) { $currentCap = $currentByEndpoint[$key] } + $currentQueryNames = if ($currentCap) { [System.Collections.Generic.HashSet[string]]::new([string[]]@($currentCap.Parameters)) } else { $null } + $currentBodyNames = if ($currentCap) { [System.Collections.Generic.HashSet[string]]::new([string[]]@($currentCap.BodyProperties)) } else { $null } + + # Typed Dictionary[string,string] for each of query/body, NOT a plain Hashtable -- + # this is the generalised fix for the keys-shadowing bug (a field literally named + # "keys"/"count"/"values" hijacking .Keys/.Count/.Values member access on a + # Hashtable/PSCustomObject-backed IDictionary). A real field named "keys" exists + # today (DELETE /workloads/tags), and .get_Keys() below is real-member access on a + # typed generic Dictionary, immune to that adapter trick (verified: a Hashtable + # key literally named "keys" shadows .Keys and returns the key's VALUE instead of + # the key collection; a Dictionary[string,string] with the same key does not). + $queryFieldVersions = [System.Collections.Generic.Dictionary[string, string]]::new() + if ($entry.parameters) { foreach ($p in $entry.parameters.PSObject.Properties) { $queryFieldVersions[$p.Name] = $p.Value } } + $bodyFieldVersions = [System.Collections.Generic.Dictionary[string, string]]::new() + if ($entry.bodyProperties) { foreach ($p in $entry.bodyProperties.PSObject.Properties) { $bodyFieldVersions[$p.Name] = $p.Value } } + + # @(...) wraps the WHOLE if/else, not just its true branch -- assigning an + # if/else expression straight to a variable collapses an EMPTY array result + # (the else branch, on every endpoint with no read-only body fields) to a bare + # $null instead of an empty array (confirmed live: `$x = if ($false) {@(1)} + # else {@()}` leaves $x -eq $null, not an empty array) -- the same family of + # "assigning a statement's output straight to a variable silently unwraps it" + # hazard already documented in tools/Build-PfbApiDriftReport.ps1 for a + # single-element ForEach-Object result, just the zero-element counterpart here. + $readOnlyList = @(if ($entry.readOnlyBodyProperties) { $entry.readOnlyBodyProperties } else { @() }) + $readOnlyNames = [System.Collections.Generic.HashSet[string]]::new([string[]]$readOnlyList) + + # Sort-Object on all three emitted lists below is load-bearing, not cosmetic: the + # Dictionary[string,string]s above preserve insertion order, but that order comes + # from $entry.parameters/$entry.bodyProperties, which were themselves deserialized + # from JSON -- .NET's JSON property enumeration is documented as insertion order + # for System.Text.Json but PSCustomObject's own PSObject.Properties enumeration is + # NOT guaranteed to match it, and (independent of that) two Hashtable-staged + # candidate sets built earlier in this file's history silently reordered + # run-to-run on identical input purely from .NET's per-process-randomized string + # hash codes (confirmed live: regenerating the drift report twice produced two + # byte-different files for the same underlying content). Sorting explicitly here + # is what makes MissingQueryParameters/MissingBodyProperties/ReadOnlyFields + # deterministic regardless of any of that. + $missingQuery = @($queryFieldVersions.get_Keys() | + Where-Object { -not $exposedWireNames.Contains($_) } | + Where-Object { -not $currentQueryNames -or $currentQueryNames.Contains($_) }) + if ($SinceVersion) { + $missingQuery = @($missingQuery | Where-Object { Test-PfbApiVersionNewerThan -Version $queryFieldVersions[$_] -Baseline $SinceVersion }) + } + if ($ExcludedFields) { + $missingQuery = @($missingQuery | Where-Object { $ExcludedFields -notcontains $_ }) + } + $missingQuery = @($missingQuery | Sort-Object) + + $missingBodyCandidates = @($bodyFieldVersions.get_Keys() | + Where-Object { -not $exposedWireNames.Contains($_) } | + Where-Object { -not $currentBodyNames -or $currentBodyNames.Contains($_) }) + if ($SinceVersion) { + $missingBodyCandidates = @($missingBodyCandidates | Where-Object { Test-PfbApiVersionNewerThan -Version $bodyFieldVersions[$_] -Baseline $SinceVersion }) + } + if ($ExcludedFields) { + $missingBodyCandidates = @($missingBodyCandidates | Where-Object { $ExcludedFields -notcontains $_ }) + } + + $readOnlyFields = @($missingBodyCandidates | Where-Object { $readOnlyNames.Contains($_) } | Sort-Object) + $missingBody = @($missingBodyCandidates | Where-Object { -not $readOnlyNames.Contains($_) } | Sort-Object) + + if ($missingQuery.Count -eq 0 -and $missingBody.Count -eq 0 -and $readOnlyFields.Count -eq 0) { continue } + + $escapeHatchOnly = @($unresolved | Where-Object { $_.Surface -eq 'AttributesOnly' } | ForEach-Object { $_.Parameter } | Sort-Object -Unique) + $hasBareUnresolved = [bool]($unresolved | Where-Object { $_.Surface -eq 'TypedUnresolved' }) + $level = if ($unresolved.Count -eq 0) { 'high' } else { 'partial' } + $caveat = + if ($level -eq 'high') { '' } + elseif ($escapeHatchOnly.Count -gt 0 -and -not $hasBareUnresolved) { + 'body reachable only via -Attributes; lists reflect typed-parameter coverage, not wire reachability' + } + elseif ($escapeHatchOnly.Count -eq 0 -and $hasBareUnresolved) { + 'one or more parameters could not be traced to a wire name and have no -Attributes escape hatch; lists reflect typed-parameter coverage only, not full wire reachability' + } + else { + 'body reachable via -Attributes for some parameters and untraceable for others; lists reflect typed-parameter coverage only, not full wire reachability' + } + + $confidence = [PSCustomObject]@{ + Level = $level + UnresolvedParameters = @($unresolved | Sort-Object Parameter, File, Line) + EscapeHatchOnly = $escapeHatchOnly + Caveat = $caveat + } + + $gaps.Add([PSCustomObject]@{ + Endpoint = $key + Cmdlets = $cmdlets + MissingQueryParameters = $missingQuery + MissingBodyProperties = $missingBody + ReadOnlyFields = $readOnlyFields + Confidence = $confidence + }) + } + + return $gaps.ToArray() +} + +function Get-PfbValidateSetDrift { + <# + .SYNOPSIS + Category 3: flags a typed Public/ parameter whose hand-written ValidateSet + disagrees with the spec's current documented value list for the same field -- + missing values (the spec documents a legal value the ValidateSet rejects + client-side, e.g. the real Get-PfbArrayPerformance -Protocol 'all' bug), + stale values (the ValidateSet allows a value the spec no longer, or never did, + document), or both. + .OUTPUTS + [PSCustomObject]@{ Cmdlet; Parameter; CurrentValidateSet; SpecValues; + MissingValues; StaleValues }[] -- only for parameters whose wire name resolves + to exactly one spec value-enum record ('matched' via Resolve-PfbFieldValueEnum); + a HasValidateSet parameter that resolves to 'collision'/'not-found-in-resource'/ + 'no-spec-enum-found' is silently excluded (not enough signal to compare against). + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] [object[]]$CmdletInventory, + [Parameter(Mandatory)] $History, + [Parameter(Mandatory)] [string]$OldestVersion + ) + + $withValidateSet = @($CmdletInventory | Where-Object { $_.Surface -eq 'Typed' -and $_.HasValidateSet }) + + foreach ($cand in $withValidateSet) { + $hint = Get-PfbResourceHint -CmdletName $cand.Cmdlet + $resolution = Resolve-PfbFieldValueEnum -WireName $cand.WireName -ResourceHint $hint -Endpoint $cand.Endpoint -Method $cand.Method -History $History -OldestVersion $OldestVersion + if ($resolution.Status -ne 'matched') { continue } + + $current = @($cand.ValidateSetValues) + $spec = @($resolution.SpecValues) + $missing = @($spec | Where-Object { $current -notcontains $_ }) + $stale = @($current | Where-Object { $spec -notcontains $_ }) + + if ($missing.Count -eq 0 -and $stale.Count -eq 0) { continue } + + [PSCustomObject]@{ + Cmdlet = $cand.Cmdlet + Parameter = $cand.Parameter + CurrentValidateSet = $current + SpecValues = $spec + MissingValues = $missing + StaleValues = $stale + } + } +} + +# --- Task 5: per-field enrichment + enum join for MissingBodyProperties ----------------- +# +# Consumed by tools/Build-PfbApiDriftReport.ps1, which ONLY calls this enrichment path for +# an endpoint whose Confidence.Level is 'high' -- a 'partial'-confidence endpoint's +# MissingBodyProperties list can contain false positives (see Get-PfbParameterCoverageGaps's +# own .OUTPUTS help: an unresolved parameter may already cover an apparent gap through a +# path this AST-only inventory cannot see), and handing a human a fully-worked-out +# suggestedPowerShellType/target/enumValues for a field that might not even be a real gap +# would overstate the confidence the endpoint's own `confidence.caveat` is explicitly +# telling them NOT to have. This is NOT a suppression (design decision 5 still holds -- +# every field name still appears, unchanged, in a partial-confidence endpoint's list): it +# only withholds this task's EXTRA metadata layer, never the gap itself. +# +# This gating is not a guess -- it is what makes this task's own pinned acceptance numbers +# reproducible. Measured against the real capability map + specs (2026-07-26): 402 addable +# MissingBodyProperties entries on 'high'-confidence endpoints vs. 605 across BOTH +# confidence levels. The enum-join split this task's acceptance is pinned to -- 33 matched / +# 43 not-found-in-resource / 326 no-spec-enum-found -- reproduces EXACTLY over the +# high-confidence-only 402, and does NOT reproduce over the combined 605 (which instead +# gives 43 matched / 66 not-found-in-resource / 496 no-spec-enum-found) -- confirming the +# gate is the intended scope, not an afterthought. + +# Sentinel -ResourceHint passed to Resolve-PfbFieldValueEnum when a body property's +# OwnerSchema is $null (no named component encloses its declaration -- see +# Get-PfbSchemaPropertyDetails's OwnerSchema doc in tools/lib/PfbSpecTools.ps1). +# Resolve-PfbFieldValueEnum matches -ResourceHint with "-like `"$ResourceHint*`"" against +# schema-kind history keys, so passing '' would degrade to a bare '*' wildcard that matches +# EVERY schema-kind entry in the whole history -- exactly wrong for "there is no owner to +# hint at". This sentinel starts with a character ('(') no real OpenAPI schema name in this +# spec uses, so it is guaranteed to prefix-match zero real history keys, correctly forcing +# the hinted-schema candidate set to stay empty rather than accidentally matching +# everything. Only 1 of the 402 real high-confidence addable body-property gaps hits this +# (POST /keytabs/upload|keytab_file -- a fully inline request body with no named schema +# anywhere in its chain) -- confirmed by real-data verification. +$script:PfbNoOwnerSchemaResourceHint = '(no-owner-schema)' + +function Get-PfbSuggestedPowerShellType { + <# + .SYNOPSIS + Maps an OpenAPI {type, format} pair (plus, for an array, its element {type, format}) + to a suggested PowerShell parameter type literal, e.g. '[string]', '[long]', + '[string[]]'. + .DESCRIPTION + Table (verbatim from the drift-report-actionable-plan brief): + integer + format 'int64' -> [long] + integer + format 'int32'/'uint32'/'' -> [int] (also the fallback for any OTHER + unrecognised integer format, + since [int]/Int32 is the + OpenAPI/JSON-Schema default + width for "integer" when format + is absent) + number (any format, incl. 'double'/'float'/none) -> [double] + string (any format) -> [string] + boolean -> [bool] + array -> "[[]]", element mapped by a + RECURSIVE call against -ItemType/ + -ItemFormat (falls back to [object] if + the item type could not be resolved -- + e.g. OwnerSchema was $null, or the + array's own "items" node is itself an + unresolved $ref with no inline "type", + which this function deliberately does + NOT follow -- see + Get-PfbBodyPropertyArrayItemType) + anything else ($null, '', 'object', or an unrecognised type string) -> [object], + a deliberately honest fallback, never a guessed scalar type. This function's + caller always ALSO emits the raw type/format alongside this suggestion (see + Build-PfbApiDriftReport.ps1), so a human overriding a wrong/absent guess has + the real spec facts to work from, not just this function's guess. + + Branches on FORMAT, not just TYPE, for integers specifically because silently is + the dangerous failure mode here: 37 of the 402 real high-confidence addable + body-property fields are `type: integer, format: int64` (measured against the real + capability map + fb2.27 spec, 2026-07-26 -- NOT the 230 the original brief + speculated; that figure was investigated and could not be reproduced against any of + six candidate populations, see tools/README.md). Mapping bare `"type": "integer"` + to `[int]` (Int32) without checking `format` would silently truncate every one of + those 37 real fields the first time a caller supplied a value above 2^31-1. + .OUTPUTS + String, e.g. '[string]', '[long]', '[string[]]'. Never $null or ''. + #> + [CmdletBinding()] + param( + [AllowNull()] [string]$Type, + [AllowNull()] [string]$Format, + [AllowNull()] [string]$ItemType, + [AllowNull()] [string]$ItemFormat + ) + + switch ($Type) { + 'integer' { + if ($Format -eq 'int64') { return '[long]' } + return '[int]' + } + 'number' { return '[double]' } + 'string' { return '[string]' } + 'boolean' { return '[bool]' } + 'array' { + $inner = Get-PfbSuggestedPowerShellType -Type $ItemType -Format $ItemFormat + # Strip exactly the OUTERMOST leading '[' / trailing ']' via anchored regex + # (never Trim(), which strips every matching char from each end and would + # over-strip a nested array-of-array's inner brackets too, e.g. Trim('[',']') + # on '[string[]]' yields 'string', not the intended 'string[]'). + $innerBare = $inner -replace '^\[', '' -replace '\]$', '' + return "[$innerBare[]]" + } + default { return '[object]' } + } +} + +function Find-PfbOwnSchemaPropertyNode { + <# + .SYNOPSIS + Internal helper for Get-PfbBodyPropertyArrayItemType/Get-PfbBodyPropertySynopsis: + finds -FieldName's own (raw, un-resolved-further) property node within ONE already- + identified named schema (-SchemaNode), searching its OWN inline "allOf" branches + but never crossing into a "$ref" branch. + .DESCRIPTION + Real-data correction (measured against fb2.27, 2026-07-26): the majority of real + OwnerSchema values -- e.g. `_certificateBase`, `NfsExportPolicyRuleBase`, + `ActiveDirectoryPatch`, `SmbSharePolicyRule` -- carry NO "properties" key directly + at their own top level; the schema is `{ allOf: [ {$ref: ...}, {properties: {...}} ] }` + and the field lives in the anonymous SECOND branch's own "properties". This is + exactly the shape Get-PfbSchemaPropertyDetails's OwnerSchema attribution already + anticipates ("OwnerSchema is the nearest enclosing NAMED component whose properties + block directly declares this property" -- an ANONYMOUS inline allOf branch is never + itself an owner, so its declared properties are attributed to the nearest NAMED + ancestor, i.e. THIS schema, even though they physically live one level down inside + its own allOf). A single-hop `schema.properties.$FieldName` lookup (the original, + too-naive implementation) therefore returned $null for nearly every real match, + silently defeating both `synopsis` and the array-item-type lookup. + + This is still NOT "a second schema walker" in the decision-3 sense: it only + descends into -SchemaNode's OWN anonymous ("allOf" with no "$ref") branches -- + never crosses into a "$ref" branch (that branch's properties belong to a DIFFERENT + named schema, whatever OwnerSchema attribution assigned it there -- searching into + it here would either find the wrong owner's field, or, worse, silently succeed for + the wrong reason if two schemas happen to share a field name) -- and never resolves + a property's OWN "$ref" or descends into ITS body (same top-level-only boundary + Get-PfbSchemaPropertyDetails's PIN already establishes). The recursion is bounded + and terminates naturally: it can only ever re-enter THIS one schema's own inline + branches, never a different named schema. + .OUTPUTS + The raw property node (PSCustomObject), or $null. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [AllowNull()] + $SchemaNode, + + [Parameter(Mandatory)] + [string]$FieldName + ) + + if ($null -eq $SchemaNode) { return $null } + + if ($SchemaNode.PSObject.Properties.Name -contains 'properties' -and $SchemaNode.properties -and + $SchemaNode.properties.PSObject.Properties.Name -contains $FieldName) { + return $SchemaNode.properties.$FieldName + } + + if ($SchemaNode.PSObject.Properties.Name -contains 'allOf' -and $SchemaNode.allOf) { + foreach ($branch in $SchemaNode.allOf) { + if ($branch.PSObject.Properties.Name -contains '$ref') { continue } + $found = Find-PfbOwnSchemaPropertyNode -SchemaNode $branch -FieldName $FieldName + if ($null -ne $found) { return $found } + } + } + + return $null +} + +function Get-PfbOwnerSchemaPropertyNode { + <# + .SYNOPSIS + Resolves -FieldName's own raw property node under -OwnerSchema in -Spec, or $null. + Shared by Get-PfbBodyPropertyArrayItemType and Get-PfbBodyPropertySynopsis so both + apply the exact same "which named schema, which branch" resolution -- see + Find-PfbOwnSchemaPropertyNode for why a single top-level dictionary hop is not + enough on real data. + .OUTPUTS + The raw property node (PSCustomObject), or $null. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] $Spec, + [AllowNull()] [string]$OwnerSchema, + [Parameter(Mandatory)] [string]$FieldName + ) + + if (-not $OwnerSchema) { return $null } + if (-not $Spec.components -or -not $Spec.components.schemas) { return $null } + if ($Spec.components.schemas.PSObject.Properties.Name -notcontains $OwnerSchema) { return $null } + + $schema = $Spec.components.schemas.$OwnerSchema + return Find-PfbOwnSchemaPropertyNode -SchemaNode $schema -FieldName $FieldName +} + +function Get-PfbBodyPropertyArrayItemType { + <# + .SYNOPSIS + For an array-typed body property, returns its element {Type, Format} via a DIRECT + lookup of the property's own node under -OwnerSchema (see + Get-PfbOwnerSchemaPropertyNode/Find-PfbOwnSchemaPropertyNode) -- never a second + schema walker in the decision-3 sense (it never crosses into a DIFFERENT named + schema or resolves any "$ref"). Reads exactly one property hop beyond that node: + its own "items" sibling key (see Get-PfbSchemaPropertyDetails's PIN: it never + follows a property's own "$ref" or descends further, and this respects the same + boundary for "items"). + .DESCRIPTION + $null when -OwnerSchema is $null (no named component encloses the property's + declaration -- this function does not attempt to re-derive one by walking the + operation's own inline body schema), when the named schema (searched through its + own inline allOf branches, never a $ref'd one) doesn't declare -FieldName at all, + or when the property has no "items" node (not actually an array, or "items" is + itself an unresolved $ref with no inline "type"/"format" sibling -- deliberately + not resolved, same "top-level only" rule Get-PfbSchemaPropertyDetails already + applies one level up). + .OUTPUTS + [PSCustomObject]@{ Type; Format }, or $null. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] $Spec, + [AllowNull()] [string]$OwnerSchema, + [Parameter(Mandatory)] [string]$FieldName + ) + + $propNode = Get-PfbOwnerSchemaPropertyNode -Spec $Spec -OwnerSchema $OwnerSchema -FieldName $FieldName + if ($null -eq $propNode) { return $null } + if ($propNode.PSObject.Properties.Name -notcontains 'items' -or -not $propNode.items) { return $null } + + $itemsNode = $propNode.items + $itemType = if ($itemsNode.PSObject.Properties.Name -contains 'type') { $itemsNode.type } else { $null } + $itemFormat = if ($itemsNode.PSObject.Properties.Name -contains 'format') { $itemsNode.format } else { $null } + + # An "items" node that is itself an unresolved $ref (e.g. { "$ref": "#/components/schemas/_tagRef" }) + # carries neither -- deliberately NOT followed (would be resolving one level further + # than this function's own direct-lookup contract allows). Return $null overall in + # that case, same as "no items node at all", rather than a half-populated record with + # both fields $null -- the caller (Get-PfbBodyPropertyEnrichment) only ever checks + # truthiness of this function's result before reading .Type/.Format. + if (-not $itemType -and -not $itemFormat) { return $null } + + return [PSCustomObject]@{ Type = $itemType; Format = $itemFormat } +} + +function Get-PfbBodyPropertySynopsis { + <# + .SYNOPSIS + First sentence of a body property's own `description`, newline-normalised -- a + DIRECT lookup of the property's own node under -OwnerSchema (see + Get-PfbOwnerSchemaPropertyNode), never a second schema walker in the decision-3 + sense (never crosses into a different named schema or resolves a "$ref"). + Get-PfbSchemaPropertyDetails (tools/lib/PfbSpecTools.ps1) already did the hard part + -- resolving the property's OWNING named schema through the allOf/$ref chain across + MULTIPLE schemas; finding that one field's own node inside the schema already + identified as its owner is a bounded, much smaller problem, so it does not earn a + second walker. + .DESCRIPTION + $null when -OwnerSchema is $null (no named component encloses the property's + declaration -- the operation's entire body schema was written fully inline with no + $ref anywhere in its chain). This function does NOT attempt to re-derive an owner by + re-walking the operation's own inline body schema (that would be a second walker). + Also $null when the named schema (searched through its own inline allOf branches, + never a $ref'd one -- see Find-PfbOwnSchemaPropertyNode) doesn't declare -FieldName + at all, or declares it with no (or empty) "description" -- surfaced as $null rather + than a wrong guess in every case, never a silently-empty string standing in for "no + synopsis available". + "First sentence": newlines are first replaced with a single space (these + descriptions wrap mid-sentence in the raw spec text), then the result up to and + including the first '.', '!', or '?' that is followed by whitespace or the end of + the string is taken. Sampled real fb2.27 descriptions do not use a + sentence-ending-like abbreviation before the field's own first true sentence break, + so this simple split is not a compromise for that data -- but it IS naive prose + parsing, not a guarantee for arbitrary future spec text. + .OUTPUTS + String, or $null. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] $Spec, + [AllowNull()] [string]$OwnerSchema, + [Parameter(Mandatory)] [string]$FieldName + ) + + $propNode = Get-PfbOwnerSchemaPropertyNode -Spec $Spec -OwnerSchema $OwnerSchema -FieldName $FieldName + if ($null -eq $propNode) { return $null } + if ($propNode.PSObject.Properties.Name -notcontains 'description' -or -not $propNode.description) { return $null } + + $normalised = ($propNode.description -replace '\r?\n', ' ').Trim() + $m = [regex]::Match($normalised, '^.*?[.!?](?=\s|$)') + if ($m.Success) { return $m.Value.Trim() } + return $normalised +} + +function Get-PfbBodyPropertyEnrichment { + <# + .SYNOPSIS + Composes every per-field enrichment fact Task 5 adds for ONE addable + missing-body-property gap: synopsis, suggested PowerShell type, and the + Resolve-PfbFieldValueEnum join. + .DESCRIPTION + The enum join always goes through Resolve-PfbFieldValueEnum, NEVER a bare wire-name + lookup -- bare-name lookup is exactly the collision class (43 of the 402 real + high-confidence addable gaps) this function exists to resolve correctly instead of + guessing. -OwnerSchema (from Get-PfbSchemaPropertyDetails, via the caller's own + BodyPropertyDetails lookup) is passed as -ResourceHint -- NOT the older + cmdlet-name-derived Get-PfbResourceHint, which only reaches 14 of the 33 real + matches (verified: Update-PfbNfsExportRule's derived hint 'NfsExportRule' does not + prefix-match the real owning schema 'NfsExportPolicyRuleBase' at all). When + -OwnerSchema is $null, $script:PfbNoOwnerSchemaResourceHint is substituted so the + hint can never accidentally wildcard-match every schema-kind entry (see that + variable's own comment). + .OUTPUTS + [PSCustomObject]@{ Synopsis; SuggestedPowerShellType; EnumValues; EnumStatus }. + EnumStatus is always one of Resolve-PfbFieldValueEnum's own literal values -- + 'matched' / 'collision' / 'not-found-in-resource' / 'no-spec-enum-found' -- passed + through VERBATIM, never renamed or coerced. (Note for anyone cross-referencing the + drift-report-actionable-plan's decisions register: its prose invents a 'collision' + bucket meaning "collision-class", but the real function returns + 'not-found-in-resource' for that entire population in real data -- 0 real + 'collision' results occur among the 402 high-confidence addable gaps. This function + still passes 'collision' through unchanged on the rare/future occasion + Resolve-PfbFieldValueEnum actually returns it, rather than hardcoding it away.) + EnumValues is always an array, empty (never $null) unless EnumStatus is 'matched'. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] [string]$FieldName, + [AllowNull()] [string]$Type, + [AllowNull()] [string]$Format, + [AllowNull()] [string]$OwnerSchema, + [Parameter(Mandatory)] $Spec, + [string]$Endpoint, + [string]$Method, + [Parameter(Mandatory)] $History, + [Parameter(Mandatory)] [string]$OldestVersion + ) + + $synopsis = Get-PfbBodyPropertySynopsis -Spec $Spec -OwnerSchema $OwnerSchema -FieldName $FieldName + + $itemType = $null + $itemFormat = $null + if ($Type -eq 'array') { + $items = Get-PfbBodyPropertyArrayItemType -Spec $Spec -OwnerSchema $OwnerSchema -FieldName $FieldName + if ($items) { $itemType = $items.Type; $itemFormat = $items.Format } + } + $suggestedType = Get-PfbSuggestedPowerShellType -Type $Type -Format $Format -ItemType $itemType -ItemFormat $itemFormat + + $hint = if ($OwnerSchema) { $OwnerSchema } else { $script:PfbNoOwnerSchemaResourceHint } + $resolution = Resolve-PfbFieldValueEnum -WireName $FieldName -ResourceHint $hint -Endpoint $Endpoint -Method $Method -History $History -OldestVersion $OldestVersion + $enumValues = @(if ($resolution.Status -eq 'matched') { $resolution.SpecValues } else { @() }) + + return [PSCustomObject]@{ + Synopsis = $synopsis + SuggestedPowerShellType = $suggestedType + EnumValues = $enumValues + EnumStatus = $resolution.Status + } +} + +# --- Task 6: systemic-gaps aggregation, convention strength, annotations, injection detection -- + +function Get-PfbGapFieldName { + <# + .SYNOPSIS + Internal helper: the wire name a MissingQueryParameters/MissingBodyProperties list + ENTRY represents, whether that entry is a bare string (Get-PfbParameterCoverageGaps' + own, always-bare-string shape) or an enriched record + (Build-PfbApiDriftReport.ps1's `{ name, type, format, ... }` shape, applied ONLY to + high-confidence MissingBodyProperties -- see Get-PfbBodyPropertyEnrichment). Kept + tolerant of both shapes so Get-PfbSystemicGaps works whether it's handed + Get-PfbParameterCoverageGaps' raw output (this task's own real-data verification) or + a later, already-enriched report structure (Task 7's job, not this task's). + .OUTPUTS + string, or $null if -Entry is neither shape. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [AllowNull()] + $Entry + ) + + if ($null -eq $Entry) { return $null } + if ($Entry -is [string]) { return $Entry } + if ($Entry.PSObject.Properties.Name -contains 'name') { return $Entry.name } + return $null +} + +function Get-PfbSystemicGaps { + <# + .SYNOPSIS + Decision 7: collapses Get-PfbParameterCoverageGaps's per-endpoint + MissingQueryParameters/MissingBodyProperties lists into ONE finding PER DISTINCT + WIRE NAME, across every endpoint AND both lists together -- e.g. `context_names` + showing up as a missing query parameter on hundreds of different endpoints becomes a + SINGLE finding with an EndpointCount matching that total, not one separate per-endpoint row. + This is what turns hundreds of individual gap rows into a handful of real, + actionable decisions. + .DESCRIPTION + An endpoint whose SAME field name happens to appear in both its own + MissingQueryParameters and MissingBodyProperties (measured against the real + capability map: exactly 1 real case, POST /workloads/placement-recommendations| + placement_names -- see Get-PfbParameterCoverageGaps's own .OUTPUTS help) still + contributes that endpoint only ONCE to the name's EndpointCount: endpoints are + deduplicated per name via a HashSet, never concatenated across the two lists. + Entries are read through Get-PfbGapFieldName so this tolerates either + Get-PfbParameterCoverageGaps's own bare-string lists or an already-enriched + MissingBodyProperties record shape. + .OUTPUTS + [PSCustomObject]@{ Name; EndpointCount; Endpoints; QueryEndpointCount; + BodyEndpointCount }[], sorted by EndpointCount descending, then Name ascending. + Endpoints is the sorted, deduplicated list of " /" endpoint keys. + QueryEndpointCount/BodyEndpointCount are the (possibly-overlapping) counts of + endpoints where the name showed up specifically in MissingQueryParameters / + MissingBodyProperties -- informational only; EndpointCount is always the + deduplicated total, never their sum. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [AllowEmptyCollection()] + [object[]]$Gaps + ) + + # Typed Dictionary[string, T], not a plain Hashtable, for every one of these three maps + # -- a wire field literally named 'keys'/'count'/'values' would shadow .Keys/.Count/ + # .Values member access on a Hashtable-backed IDictionary (the same hazard + # Get-PfbParameterCoverageGaps's own $queryFieldVersions/$bodyFieldVersions already + # avoid, see its comment above them). + $endpointsByName = [System.Collections.Generic.Dictionary[string, object]]::new() + $queryCountByName = [System.Collections.Generic.Dictionary[string, int]]::new() + $bodyCountByName = [System.Collections.Generic.Dictionary[string, int]]::new() + + foreach ($gap in $Gaps) { + foreach ($rawEntry in @($gap.MissingQueryParameters)) { + $name = Get-PfbGapFieldName -Entry $rawEntry + if (-not $name) { continue } + if (-not $endpointsByName.ContainsKey($name)) { $endpointsByName[$name] = [System.Collections.Generic.HashSet[string]]::new() } + [void]$endpointsByName[$name].Add($gap.Endpoint) + if (-not $queryCountByName.ContainsKey($name)) { $queryCountByName[$name] = 0 } + $queryCountByName[$name] = $queryCountByName[$name] + 1 + } + foreach ($rawEntry in @($gap.MissingBodyProperties)) { + $name = Get-PfbGapFieldName -Entry $rawEntry + if (-not $name) { continue } + if (-not $endpointsByName.ContainsKey($name)) { $endpointsByName[$name] = [System.Collections.Generic.HashSet[string]]::new() } + [void]$endpointsByName[$name].Add($gap.Endpoint) + if (-not $bodyCountByName.ContainsKey($name)) { $bodyCountByName[$name] = 0 } + $bodyCountByName[$name] = $bodyCountByName[$name] + 1 + } + } + + $findings = [System.Collections.Generic.List[object]]::new() + foreach ($name in $endpointsByName.get_Keys()) { + $endpoints = @($endpointsByName[$name] | Sort-Object) + $findings.Add([PSCustomObject]@{ + Name = $name + EndpointCount = $endpoints.Count + Endpoints = $endpoints + QueryEndpointCount = if ($queryCountByName.ContainsKey($name)) { $queryCountByName[$name] } else { 0 } + BodyEndpointCount = if ($bodyCountByName.ContainsKey($name)) { $bodyCountByName[$name] } else { 0 } + }) + } + + return @($findings | Sort-Object -Property @{ Expression = 'EndpointCount'; Descending = $true }, Name) +} + +function Get-PfbWireNameCmdletCounts { + <# + .SYNOPSIS + Wire name -> the distinct set of cmdlets across the WHOLE module that already + expose it as a resolved ('Typed') parameter -- the raw data + Get-PfbConventionStrength ranks names against. Built directly from + Get-PfbCmdletParameterInventory's own output (tools/lib/PfbCmdletParamTools.ps1); + this function does not re-derive wire-name resolution itself, per this task's + brief ("read that file to find the right existing data source rather than + re-deriving wire-name resolution from scratch"). + .OUTPUTS + System.Collections.Generic.Dictionary[string, System.Collections.Generic.HashSet[string]] + -- typed Dictionary, not a plain Hashtable, for the same keys-shadowing reason as + every other new map in this section (a wire name literally named 'keys' is real: + see Get-PfbParameterCoverageGaps's own comment for the confirmed example). + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [AllowEmptyCollection()] + [object[]]$CmdletInventory + ) + + $map = [System.Collections.Generic.Dictionary[string, object]]::new() + foreach ($row in $CmdletInventory) { + if ($row.Surface -ne 'Typed' -or -not $row.WireName) { continue } + if (-not $map.ContainsKey($row.WireName)) { $map[$row.WireName] = [System.Collections.Generic.HashSet[string]]::new() } + [void]$map[$row.WireName].Add($row.Cmdlet) + } + return $map +} + +function Get-PfbConventionStrength { + <# + .SYNOPSIS + Decision 8: for each of -Names (typically the field names Get-PfbSystemicGaps just + surfaced), how many DISTINCT cmdlets across the whole module already expose that + exact wire name as a resolved ('Typed') parameter somewhere -- a proxy for "how + mechanical would it be to close the REMAINING gaps for this name". A name already + used by hundreds of cmdlets (e.g. `names`) is a trivial batch fix on whatever + stragglers are left; a name used by ZERO cmdlets (e.g. `context_names`) has no + established convention to extend at all -- closing it is an architectural + decision, not a mechanical batch job. + .OUTPUTS + [PSCustomObject]@{ Name; CmdletCount; Cmdlets }[], one entry per -Names input IN + THE SAME ORDER (never re-sorted here -- a caller ranks by CmdletCount itself). + CmdletCount is 0 and Cmdlets is an empty array (never omitted, never $null) for a + name no cmdlet exposes at all -- that zero IS the finding for names like + `context_names`. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [AllowEmptyCollection()] + [object[]]$CmdletInventory, + + [Parameter(Mandatory)] + [string[]]$Names + ) + + $countsByName = Get-PfbWireNameCmdletCounts -CmdletInventory $CmdletInventory + + foreach ($name in $Names) { + $cmdlets = if ($countsByName.ContainsKey($name)) { @($countsByName[$name] | Sort-Object) } else { @() } + [PSCustomObject]@{ + Name = $name + CmdletCount = $cmdlets.Count + Cmdlets = $cmdlets + } + } +} + +function Get-PfbDriftAnnotations { + <# + .SYNOPSIS + Loads docs/drift-annotations.json -- ONE annotation channel serving three purposes + at once: a recorded design decision (`context_names`/`allow_errors`, both pointing + at docs/design/fusion-context-injection.md -- a forward reference to a NOT-YET- + WRITTEN design doc from a separate, related Fusion effort; this function does not + create that file, only references its path), a prior investigation conclusion, and + a live-testing hazard note (`management-access-policies`' + POST/PATCH/DELETE-return-403 finding). Deliberately checked in under docs/ + (tracked by git) and NOT under Data/ -- Data/ is strictly runtime-loaded by + Private/, and nothing under Private/ may ever wire this file's content into the + module at runtime; this is a tools/reporting-only concern. + .DESCRIPTION + Schema (see docs/drift-annotations.json's own top-level "schema" field for the same + description kept next to the data): + { + "schemaVersion": 1, + "annotations": [ + { + "matchType": "field" | "endpoint", + "match": string, + -- exact wire-field name for matchType 'field' (e.g. "context_names"); + an endpoint-key SUBSTRING for matchType 'endpoint', matched + case-insensitively against the " /" key format (e.g. + "management-access-policies" matches every method on that resource). + "kind": "designDecision" | "priorConclusion" | "liveTestingHazard", + "note": string, + -- human-readable, deliberately never a machine-verified assertion. + E.g. allow_errors' note says only "not yet implemented" -- NOT + whether its injection would be gated on the endpoint declaring the + parameter or sent unconditionally to every endpoint. That specific + behavioral question is explicitly deferred to a separate PR (#22) + and is not yet decided; this file must never assert either answer. + "reference": string | null + -- a doc path (repo-relative), or null when there's nowhere to point. + }, ... + ] + } + Easy to extend: append another object to "annotations" -- no code change needed for + a new entry with an existing "kind"/"matchType" value. + .OUTPUTS + The parsed PSCustomObject (schemaVersion/annotations), or $null if -Path doesn't + exist. Never throws for a missing file -- an absent annotations file means "no + annotations available (yet)", not a hard error, so a consumer keeps working even + before this file existed / if it's ever deleted. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string]$Path + ) + + if (-not (Test-Path $Path)) { return $null } + $json = Get-Content -Path $Path -Raw + # ConvertFrom-Json has no -Depth parameter on Windows PowerShell 5.1 (added in PS6) -- + # this file's own shape (schemaVersion + a flat array of annotation records) is far + # shallower than 5.1's own recursion limit (100), same reasoning as + # Private/Get-PfbCapabilityMap.ps1's identical guard. + if ($PSVersionTable.PSVersion.Major -ge 6) { + return $json | ConvertFrom-Json -Depth 10 + } + return $json | ConvertFrom-Json +} + +function Find-PfbDriftAnnotation { + <# + .SYNOPSIS + Looks up every annotation in -Annotations (Get-PfbDriftAnnotations's output) that + matches -FieldName (matchType 'field', exact match) and/or -Endpoint (matchType + 'endpoint', case-insensitive substring match against the endpoint key). + .OUTPUTS + [PSCustomObject][] -- zero or more matching annotation records, never $null (an + empty array when -Annotations is $null, has no "annotations" list, or nothing + matches). + #> + [CmdletBinding()] + param( + [AllowNull()] + $Annotations, + + [string]$FieldName, + + [string]$Endpoint + ) + + if (-not $Annotations -or -not $Annotations.annotations) { return @() } + + return @($Annotations.annotations | Where-Object { + ($_.matchType -eq 'field' -and $FieldName -and $_.match -eq $FieldName) -or + ($_.matchType -eq 'endpoint' -and $Endpoint -and + $Endpoint -like "*$([System.Management.Automation.WildcardPattern]::Escape($_.match))*") + }) +} + +function Test-PfbExpressionReferencesParameter { + <# + .SYNOPSIS + Internal helper for Get-PfbCentralInjectionSites: true if -ExpressionAst references, + ANYWHERE within its subtree, a variable whose name is in -ParameterNames (the + enclosing function's own declared parameter names) -- e.g. `$BoundParameters['Filter']` + references `$BoundParameters`, or `$Names -join ','` references `$Names`. + .OUTPUTS + [bool] + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [System.Management.Automation.Language.Ast]$ExpressionAst, + + [Parameter(Mandatory)] + [System.Collections.Generic.HashSet[string]]$ParameterNames + ) + + if ($ParameterNames.Count -eq 0) { return $false } + + $varRefs = $ExpressionAst.FindAll({ param($n) $n -is [System.Management.Automation.Language.VariableExpressionAst] }, $true) + foreach ($v in $varRefs) { + if ($ParameterNames.Contains($v.VariablePath.UserPath)) { return $true } + } + return $false +} + +function Test-PfbGuardedByParameterCondition { + <# + .SYNOPSIS + Internal helper for Get-PfbCentralInjectionSites: true if -Assignment sits inside an + enclosing `if` clause whose test expression, taken AS A WHOLE, is EXACTLY a bare + parameter reference (`if ($Names)`) or a `.ContainsKey(...)` call on a parameter + (`if ($BoundParameters.ContainsKey('TotalOnly'))`) -- the two guard shapes + Private/Add-PfbCommonQueryParams.ps1 actually uses. + .DESCRIPTION + Deliberately narrow, not "the condition mentions a parameter anywhere": a condition + that merely INCLUDES a parameter as one operand of a larger boolean expression -- + e.g. Private/Invoke-PfbApiRequest.ps1's `$AutoPaginate -and $response.continuation_token + -and -not $limitReached` -- does NOT match here, because $AutoPaginate there gates + WHETHER pagination happens at all, not WHERE the assigned key's VALUE comes from. + Only a condition that reduces, whole, to one of the two exact shapes above counts, + matching the real Add-PfbCommonQueryParams idiom this rule exists to recognize. + .OUTPUTS + [bool] + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [System.Management.Automation.Language.Ast]$Assignment, + + [Parameter(Mandatory)] + [System.Collections.Generic.HashSet[string]]$ParameterNames + ) + + if ($ParameterNames.Count -eq 0) { return $false } + + $node = $Assignment.Parent + while ($node) { + if ($node -is [System.Management.Automation.Language.IfStatementAst]) { + foreach ($clause in $node.Clauses) { + $withinBody = @($clause.Item2.FindAll({ param($n) $n -eq $Assignment }, $true)) + if ($withinBody.Count -eq 0) { continue } + + # Unwrap the PipelineAst/CommandExpressionAst layers the parser puts around + # a bare `if (...)` test expression -- same wrapping phenomenon + # Resolve-PfbSingleExpression (tools/lib/PfbCmdletParamTools.ps1) already + # peels for an assignment's right-hand side, reused here rather than + # re-deriving the same unwrap loop. + $condExpr = Resolve-PfbSingleExpression -Ast $clause.Item1 + + $bareVar = $condExpr -as [System.Management.Automation.Language.VariableExpressionAst] + if ($bareVar -and $ParameterNames.Contains($bareVar.VariablePath.UserPath)) { return $true } + + $memberCall = $condExpr -as [System.Management.Automation.Language.InvokeMemberExpressionAst] + if ($memberCall -and $memberCall.Member -is [System.Management.Automation.Language.StringConstantExpressionAst] -and + $memberCall.Member.Value -eq 'ContainsKey') { + $callTarget = $memberCall.Expression -as [System.Management.Automation.Language.VariableExpressionAst] + if ($callTarget -and $ParameterNames.Contains($callTarget.VariablePath.UserPath)) { return $true } + } + } + } + $node = $node.Parent + } + return $false +} + +function Get-PfbCentralInjectionSites { + <# + .SYNOPSIS + Scans every function in -PrivateDirectory for the same wire-key assignment AST + shape Get-PfbWireNameForParameter (tools/lib/PfbCmdletParamTools.ps1) already + recognizes for Public/ cmdlets -- `$Var['key'] = `, an IndexExpressionAst on + the assignment's left side -- restricted to the three variable names this module + actually uses, Private-side, for a request's wire-level payload hashtable: + 'queryParams' (Invoke-PfbApiRequest's own -QueryParams parameter), 'body' (its + -Body parameter), and 'into' (Add-PfbCommonQueryParams's -Into parameter, the + query-params hashtable a Public/ caller passes in by reference). + .DESCRIPTION + Deliberately narrower than "every IndexExpressionAst assignment anywhere in + Private/": this directory also splats plenty of OTHER hashtables that are + HTTP-transport/SSH plumbing, never spec-documented API fields -- $headers, + $restParams, $sshParams, $loginParams, $jwtParams, $oauthParams (confirmed by + direct inspection: Private/Invoke-PfbApiRequest.ps1, Private/Get-PfbApiTokenViaSsh.ps1, + Private/Invoke-PfbApiTokenLogin.ps1, Private/Invoke-PfbOAuth2Login.ps1). Including + those would pollute the candidate list with junk like 'Authorization' or + 'AcceptKey' that could never be a capability-map field name in the first place. + + For each matching assignment, classifies whether the assigned VALUE traces back to + a parameter of the ENCLOSING function: + - directly, via Test-PfbExpressionReferencesParameter on the right-hand side + (e.g. `$Into['filter'] = $BoundParameters['Filter']` references $BoundParameters, + a parameter of Add-PfbCommonQueryParams; `$Into['names'] = $Names -join ','` + references $Names directly); or + - via Test-PfbGuardedByParameterCondition, for the one real case where the + right-hand side is a bare literal but the key's PRESENCE is still gated on a + parameter (`if ($BoundParameters.ContainsKey('TotalOnly')) { $Into['total_only'] = 'true' }`). + Anything neither reaches is classified 'server-or-internal-derived' -- e.g. + Private/Invoke-PfbApiRequest.ps1's own + `$QueryParams['continuation_token'] = $response.continuation_token`: its RHS + references `$response` (not a declared parameter of Invoke-PfbApiRequest), and its + guard condition (`$AutoPaginate -and $response.continuation_token -and -not + $limitReached`) is a compound `-and` expression, not one of + Test-PfbGuardedByParameterCondition's two narrow whole-condition shapes, so it does + not get credited to $AutoPaginate merely because that parameter happens to appear + in it. + + This is intentionally narrow, not general symbolic data-flow analysis. See this + function's own Caveat output field (always populated) for the resulting COVERAGE + (never correctness) claim: this AST scan cannot verify injection ORDERING relative + to Assert-PfbApiCapability (the module's actual parameter-support gate) -- an + assignment could in principle run before or after that gate in a way this scan + does not model. + .OUTPUTS + [PSCustomObject]@{ Key; File; Function; Line; TargetVariable; ParameterTraced; + Classification; Caveat }[] -- Classification is 'parameter-sourced' (traces to a + cmdlet/caller parameter; must NEVER become a candidate for + $script:PfbNonActionableParameters) or 'server-or-internal-derived' (a candidate -- + see Get-PfbDerivedNonActionableParameters for how candidates are reduced to a final + per-key verdict). + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string]$PrivateDirectory + ) + + $targetVarNames = @('queryparams', 'body', 'into') + $coverageCaveat = 'coverage claim only -- this AST scan cannot verify injection ordering relative to Assert-PfbApiCapability' + + $results = [System.Collections.Generic.List[object]]::new() + # Belt-and-braces only -- see the record-level sort at the end of this function (#85). + $files = @(Get-ChildItem -Path $PrivateDirectory -Filter '*.ps1' -Recurse -File | Sort-Object -Property FullName -Culture '') + + foreach ($file in $files) { + $tokens = $null; $parseErrors = $null + $ast = [System.Management.Automation.Language.Parser]::ParseFile($file.FullName, [ref]$tokens, [ref]$parseErrors) + $functionAsts = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) + + foreach ($funcAst in $functionAsts) { + $paramBlock = $funcAst.Body.ParamBlock + $paramNames = [System.Collections.Generic.HashSet[string]]::new([string[]]@(), [System.StringComparer]::OrdinalIgnoreCase) + if ($paramBlock) { + foreach ($p in $paramBlock.Parameters) { [void]$paramNames.Add($p.Name.VariablePath.UserPath) } + } + + $assignments = $funcAst.FindAll({ + param($n) + $n -is [System.Management.Automation.Language.AssignmentStatementAst] -and + $n.Left -is [System.Management.Automation.Language.IndexExpressionAst] + }, $true) + + foreach ($assign in $assignments) { + $indexExpr = $assign.Left + $targetVar = $indexExpr.Target -as [System.Management.Automation.Language.VariableExpressionAst] + if (-not $targetVar) { continue } + if ($targetVarNames -notcontains $targetVar.VariablePath.UserPath.ToLowerInvariant()) { continue } + + $keyExpr = $indexExpr.Index -as [System.Management.Automation.Language.StringConstantExpressionAst] + if (-not $keyExpr) { continue } + + $rhsTraced = Test-PfbExpressionReferencesParameter -ExpressionAst $assign.Right -ParameterNames $paramNames + $conditionTraced = Test-PfbGuardedByParameterCondition -Assignment $assign -ParameterNames $paramNames + $traced = $rhsTraced -or $conditionTraced + $classification = if ($traced) { 'parameter-sourced' } else { 'server-or-internal-derived' } + + $results.Add([PSCustomObject]@{ + Key = $keyExpr.Value + File = $file.FullName + Function = $funcAst.Name + Line = $assign.Extent.StartLineNumber + TargetVariable = $targetVar.VariablePath.UserPath + ParameterTraced = $traced + Classification = $classification + Caveat = $coverageCaveat + }) + } + } + } + + # Third instance of the same defect class as issue #85: $files above is an unsorted + # recursive Get-ChildItem over Private/. Sorted here for the same reason, even though + # this function's only current consumer (Get-PfbDerivedNonActionableParameters) reduces + # per-Key and re-sorts by Name -- the determinism should not depend on that staying true. + return @($results | Sort-Object -Property Key, File, Line -Culture '') +} + +function Get-PfbDerivedNonActionableParameters { + <# + .SYNOPSIS + Reduces Get-PfbCentralInjectionSites's per-assignment-site output to one verdict + per distinct wire key: a key becomes a DERIVED non-actionable-parameter candidate + only when EVERY assignment site found for that key classified + 'server-or-internal-derived' -- if even ONE site for the same key traces to a + parameter (as filter/sort/limit/total_only/names/ids all do, via + Private/Add-PfbCommonQueryParams.ps1), the key is excluded from this result, never + silently added. Excluding those six would be actively harmful, not just imprecise: + it would silently delete real, still-open gaps on every endpoint/cmdlet that + doesn't yet expose that parameter (measured against the real capability map: 160 + genuine residual gaps across the six names combined -- 13+35+14+17+35+46). + .OUTPUTS + [PSCustomObject]@{ Name; Sites; Caveat; Strength }[], sorted by Name. + Strength is 'structural' for `continuation_token` specifically (its RHS is + provably server-response state, `$response.continuation_token` in + Private/Invoke-PfbApiRequest.ps1 -- can never be sourced from a cmdlet parameter + even in principle, not merely "not currently seen as a gap") and 'coverage' for + every other derived key (the general case -- this scan can only claim no OTHER + Private/ assignment for that key traced to a parameter; it is a coverage claim, + never a correctness one, per Get-PfbCentralInjectionSites's own Caveat). + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [AllowEmptyCollection()] + [object[]]$InjectionSites + ) + + $byKey = $InjectionSites | Group-Object -Property Key + + $results = [System.Collections.Generic.List[object]]::new() + foreach ($group in $byKey) { + $sites = @($group.Group) + $anyParameterSourced = [bool]($sites | Where-Object { $_.Classification -eq 'parameter-sourced' }) + if ($anyParameterSourced) { continue } + + $strength = if ($group.Name -eq 'continuation_token') { 'structural' } else { 'coverage' } + + $results.Add([PSCustomObject]@{ + Name = $group.Name + Sites = $sites + Caveat = $sites[0].Caveat + Strength = $strength + }) + } + + return @($results | Sort-Object Name) +} + +function Get-PfbNonActionableParameters { + <# + .SYNOPSIS + The full, up-to-date replacement for reading $script:PfbNonActionableParameters + directly: the two structurally-confirmed-absent hardcoded entries (X-Request-ID, + offset -- see the comment above $script:PfbNonActionableParameters for why no + injection site exists for either) UNIONED with whatever + Get-PfbCentralInjectionSites/Get-PfbDerivedNonActionableParameters currently derive + from the real -PrivateDirectory tree -- today, exactly 'continuation_token'. + .DESCRIPTION + Callers (e.g. tools/Build-PfbApiDriftReport.ps1's -ExcludedFields) should call this + function instead of reading $script:PfbNonActionableParameters directly, so a + future change to Private/Invoke-PfbApiRequest.ps1's injection shape -- e.g. if it + stopped injecting continuation_token, or started sourcing it from a parameter + instead of $response -- is reflected automatically the next time this runs, rather + than silently going stale the way the old hand-written comment could. + .OUTPUTS + string[], sorted, deduplicated. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string]$PrivateDirectory + ) + + # @(...) wraps BOTH calls, not just the final consumption -- the same "assigning a + # function's output straight to a variable silently collapses a ZERO-element result to + # $null, not a real empty array/collection" hazard already documented repeatedly + # elsewhere in this file (e.g. Get-PfbParameterCoverageGaps's $readOnlyList comment). + # -InjectionSites/-InjectionSites below both carry [AllowEmptyCollection()], which + # accepts a real empty array but still rejects an outright $null -- confirmed live: a + # -PrivateDirectory tree with zero matching assignment sites made + # Get-PfbCentralInjectionSites's `return $results.ToArray()` collapse to $null on + # assignment here, throwing a ParameterBindingValidationException one call downstream. + $sites = @(Get-PfbCentralInjectionSites -PrivateDirectory $PrivateDirectory) + $derived = @(Get-PfbDerivedNonActionableParameters -InjectionSites $sites) + $derivedNames = @($derived | ForEach-Object { $_.Name }) + + return @($script:PfbNonActionableParameters + $derivedNames | Select-Object -Unique | Sort-Object) +} + +function Get-PfbResponseShapeFindings { + <# + .SYNOPSIS + Derives response-side drift findings from Data/PfbResponseShapeMap.json. + .DESCRIPTION + Three categories, deliberately asymmetric with the request side: + + Removals -- a response field that existed in some analysed version and is absent + from the newest analysed version in which its endpoint still exists. This is the + genuinely dangerous case and the one thing this axis catches that nothing else + can: cmdlets pass responses through raw (measured 2026-08-01 over Public/'s 542 + functions -- 533 call Invoke-PfbApiRequest at all, and in 526 of those every call + is a statement-level pipeline whose result is emitted directly, never captured or + projected), so the module holds no schema of its own + and a user script binding a removed field silently receives $null. + + RenameCandidates -- a removal and a still-present field on the same endpoint and + in the same location, where the new field appears in the version immediately + after the old one vanished. Reported as a CANDIDATE and never asserted: adjacency + is suggestive, not proof. + + UnhandledEnvelopeFields -- envelope field names no `$response.` reference in + Private/Invoke-PfbApiRequest.ps1 ever reads. Informational: many envelope keys + legitimately need no handling in that function. This is a COVERAGE observation, + not a correctness claim -- it cannot tell whether a field is handled correctly, + only whether it is mentioned at all. + + ADDITIONS ARE DELIBERATELY NOT A FINDING. Because responses pass through raw, a new + response field reaches the caller with no module change required, so an "added field" + row would be pure noise. This inverts the request side's emphasis, and the inversion + is the point. + .OUTPUTS + [PSCustomObject]@{ Removals; RenameCandidates; UnhandledEnvelopeFields } + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + $ResponseShapeMap, + + [string]$RequestHandlerPath + ) + + $removals = [System.Collections.Generic.List[object]]::new() + $renameCandidates = [System.Collections.Generic.List[object]]::new() + $envelopeEndpointCounts = [System.Collections.Generic.Dictionary[string, int]]::new() + + $orderedVersions = @($ResponseShapeMap.generatedFrom) + + function Get-NextVersion { + param([string]$Version, [string[]]$Ordered) + $index = [Array]::IndexOf($Ordered, $Version) + if ($index -lt 0 -or $index -ge ($Ordered.Count - 1)) { return $null } + return $Ordered[$index + 1] + } + + $endpointNames = @($ResponseShapeMap.endpoints.PSObject.Properties.Name | Sort-Object) + + foreach ($epKey in $endpointNames) { + $endpoint = $ResponseShapeMap.endpoints.$epKey + + foreach ($name in @($endpoint.responseEnvelope.PSObject.Properties | ForEach-Object { $_.Name })) { + if (-not $envelopeEndpointCounts.ContainsKey($name)) { $envelopeEndpointCounts[$name] = 0 } + $envelopeEndpointCounts[$name]++ + } + + if ($endpoint.PSObject.Properties.Name -notcontains 'removedResponseFields') { continue } + + foreach ($removed in @($endpoint.removedResponseFields)) { + $removals.Add([PSCustomObject]@{ + Endpoint = $epKey + Field = $removed.field + Location = $removed.location + IntroducedVersion = $removed.introducedVersion + LastSeenVersion = $removed.lastSeenVersion + }) + + $successorVersion = Get-NextVersion -Version $removed.lastSeenVersion -Ordered $orderedVersions + if (-not $successorVersion) { continue } + + $bag = if ($removed.location -eq 'envelope') { + $endpoint.responseEnvelope + } + else { + $endpoint.responseItemProperties + } + + foreach ($candidateName in @($bag.PSObject.Properties | ForEach-Object { $_.Name } | Sort-Object)) { + if ($bag.$candidateName -ne $successorVersion) { continue } + $renameCandidates.Add([PSCustomObject]@{ + Endpoint = $epKey + Location = $removed.location + From = $removed.field + To = $candidateName + Version = $successorVersion + }) + } + } + } + + $unhandled = [System.Collections.Generic.List[object]]::new() + if ($RequestHandlerPath -and (Test-Path $RequestHandlerPath)) { + $handlerText = Get-Content -Path $RequestHandlerPath -Raw + $referenced = [System.Collections.Generic.HashSet[string]]::new() + foreach ($m in [regex]::Matches($handlerText, '\$response\.([A-Za-z_][A-Za-z0-9_]*)')) { + [void]$referenced.Add($m.Groups[1].Value) + } + + foreach ($name in ($envelopeEndpointCounts.get_Keys() | Sort-Object)) { + if ($referenced.Contains($name)) { continue } + $unhandled.Add([PSCustomObject]@{ + Field = $name + EndpointCount = $envelopeEndpointCounts[$name] + }) + } + } + + return [PSCustomObject]@{ + Removals = @($removals | Sort-Object Endpoint, Location, Field) + RenameCandidates = @($renameCandidates | Sort-Object Endpoint, Location, From) + UnhandledEnvelopeFields = @($unhandled | Sort-Object { -$_.EndpointCount }, { $_.Field }) + } +} diff --git a/tools/lib/PfbCmdletParamTools.ps1 b/tools/lib/PfbCmdletParamTools.ps1 new file mode 100644 index 0000000..be6345c --- /dev/null +++ b/tools/lib/PfbCmdletParamTools.ps1 @@ -0,0 +1,1062 @@ +<# +.SYNOPSIS + AST-based inventory of every typed parameter across Public/**/*.ps1, with best-effort + resolution of each parameter's REST "wire name" (the request-body or query-string key + it's assigned to). Dot-sourced by tools/Build-PfbFieldCmdletMap.ps1 and its Pester + tests, parallel to tools/lib/PfbSpecTools.ps1 and tools/lib/PfbValueEnumTools.ps1. + +.DESCRIPTION + Every cmdlet in this module follows one of a small number of body-construction + patterns (confirmed by direct inspection of New-PfbAlertWatcher, Get-PfbArrayPerformance, + New-PfbBucket, New-PfbNetworkInterface, New-PfbFileSystem, Update-PfbFileSystem): + + if ($Param) { $body['wire_name'] = $Param } + if ($Param) { $body['wire_name'] = @($Param) } # array parameters + if ($queryParams.ContainsKey(...)) ... # not matched, no enum data anyway + $queryParams['wire_name'] = $Param + $queryParams = @{ 'wire_name' = $Param } # hashtable-literal initializer + $body['wire_name'] = @{ name = $Param } # nested single-key reference + # object -- OUTER key wins + + ...plus one indirect pattern, where a shared private helper does the assignment for the + cmdlet and the cmdlet body therefore contains no literal key at all: + + Add-PfbCommonQueryParams -Into $queryParams -BoundParameters $PSBoundParameters ` + -Names $allNames -Ids $allIds + + That helper (Private/Add-PfbCommonQueryParams.ps1, introduced by issue #32/PR #49 and + extended across ~196 Get-Pfb* cmdlets by issue #33/PR #51) is mirrored by + Get-PfbCommonQueryParamMap. Without it, every migrated cmdlet's -Filter/-Sort/-Limit/ + -TotalOnly/-Name/-Id resolved no wire name, which cascaded into + Get-PfbParameterCoverageGaps (tools/lib/PfbApiDriftTools.ps1) demoting the cmdlet's whole + endpoint into its notVerified bucket -- i.e. a pure mechanical refactor made hundreds of + endpoints silently drop out of gap analysis, and the report's headline missing-field + count fell as if the gaps had been fixed. The hashtable-literal initializer above + (Get-PfbHashtableLiteralWireNameForParameter) was invisible for the same reason: only the + IndexExpressionAst form was ever recognized, so ~99 (cmdlet, parameter) pairs across ~82 + mostly-New-Pfb* cmdlets -- New-PfbApiClient's own -Name among them -- were reported as + AttributesOnly/TypedUnresolved despite demonstrably reaching the wire. + + The nested single-key reference object (Get-PfbNestedReferenceWireNameForParameter) is the + same class of blindness, one level deeper: the API models "point this resource at that + one" as `{"account": {"name": "acct1"}}`, and the capability map records TOP-LEVEL body + properties only -- there is no `account.name` in it -- so the field such a parameter + covers is the OUTER key. Leaving it unresolved cost 11 (cmdlet, parameter) pairs across 8 + cmdlets, and via the notVerified gate their whole endpoints. + + A parameter is classified into exactly one Surface: + - 'Typed': a wire name was resolved via a direct (optionally @()-wrapped) assignment. + - 'AttributesOnly': the cmdlet has an -Attributes hashtable escape hatch and this + parameter's value is NOT assigned via a simple pattern above (e.g. it's piped + through ForEach-Object first, like New-PfbNetworkInterface's -AttachedServers) -- + deliberately NOT guessed at, since over-matching here would misattribute a value + enum to the wrong field. + - 'TypedUnresolved': no -Attributes escape hatch exists AND no simple assignment was + found -- surfaced so a human can look, never silently dropped. + + -Array and -Attributes are never returned as inventory records themselves -- they are + plumbing, not spec-documented fields with values to validate. + + Each 'Typed' record also carries a best-effort Endpoint/Method: the literal + -Endpoint/-Method arguments of the Invoke-PfbApiRequest call the parameter's + resolved body/queryParams variable actually feeds, IF every such call in the + function agrees on exactly one (Method, Endpoint) pair. Left $null (never guessed) + when the variable feeds zero calls, or more than one distinct pair -- e.g. + Get-PfbNode's try/catch fallback that reuses the same $queryParams against two + genuinely different endpoints ('nodes' then 'blades'). This is what lets + Build-PfbFieldCmdletMap.ps1 resolve an 'inline-parameter'-kind value-enum record + (see tools/lib/PfbValueEnumTools.ps1), which is keyed by exact endpoint identity, + against the one specific cmdlet parameter that calls it. +#> + +# Deliberately NOT Set-StrictMode -- same reasoning as PfbSpecTools.ps1 / PfbValueEnumTools.ps1. + +function Test-PfbAssignmentGuardedBySwitch { + <# + .SYNOPSIS + True if $Assignment is lexically inside an `if ($ParameterName) { ... }` clause + whose condition is exactly a bare reference to $ParameterName -- the guard shape + Test-PfbAssignmentGuardedBySwitch's caller requires before trusting a literal + string assignment as switch-derived. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [System.Management.Automation.Language.Ast]$Assignment, + + [Parameter(Mandatory)] + [string]$ParameterName + ) + + $expectedCondition = '$' + $ParameterName + $node = $Assignment.Parent + while ($node) { + if ($node -is [System.Management.Automation.Language.IfStatementAst]) { + foreach ($clause in $node.Clauses) { + if ($clause.Item1.Extent.Text.Trim() -eq $expectedCondition) { + $withinBody = $clause.Item2.FindAll({ param($n) $n -eq $Assignment }, $true) + if (@($withinBody).Count -gt 0) { return $true } + } + } + } + $node = $node.Parent + } + return $false +} + +function Resolve-PfbSingleExpression { + <# + .SYNOPSIS + Unwraps the StatementAst layers PowerShell's parser puts around a single-expression + right-hand side, returning the innermost ExpressionAst (or the input unchanged when + it is not that shape). + .DESCRIPTION + Both AssignmentStatementAst.Right and a HashtableAst key/value pair's Item2 are typed + StatementAst, and the parser wraps a bare expression in a CommandExpressionAst -- + sometimes itself inside a single-element PipelineAst -- rather than exposing the + BinaryExpressionAst/HashtableAst/VariableExpressionAst directly. Casting without + unwrapping silently yields $null, which reads as "pattern not matched" instead of + "wrong layer". Both layers are peeled here, in either order, so callers do not each + re-derive the same lore (previously duplicated inline; see also + Find-PfbAccumulatorVariable, which peels a loop condition the same way). + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [System.Management.Automation.Language.Ast]$Ast + ) + + $node = $Ast + for ($i = 0; $i -lt 3; $i++) { + if ($node -is [System.Management.Automation.Language.PipelineAst]) { + if ($node.PipelineElements.Count -ne 1) { return $node } + $node = $node.PipelineElements[0] + continue + } + if ($node -is [System.Management.Automation.Language.CommandExpressionAst]) { + $node = $node.Expression + continue + } + break + } + return $node +} + +function Test-PfbWireValueIsParameter { + <# + .SYNOPSIS + True if -ValueAst -- the value half of a wire-key assignment, either + `$body['k'] = ` or the `@{ 'k' = }` literal form -- hands the named + parameter's own value to the wire in one of the exact shapes this repo uses. + .DESCRIPTION + Deliberately shape-exact rather than "mentions the variable anywhere": over-matching + would misattribute a wire name (and, downstream, a spec value enum) to the wrong + field. Accepted: + $Param -- direct + @($Param) -- array-wrapped + $Param -join ',' -- joined into a plural query key + 'literal' -- ONLY for a [switch] whose mere presence is keyed to a + hardcoded string, and only inside an `if ($Param)` guard + Refused (correctly, per this file's "never guess" contract): anything else, e.g. + `"$Param"`. The array-projection shape `@($Param | ForEach-Object { @{ name = $_ } })` + is also refused HERE by design -- it is matched by the sibling + Test-PfbWireValueIsParameterProjection, and only ever from the nested-reference + resolver, which credits the OUTER key. Matching it in this predicate would let the + direct resolvers credit the INNER key instead, naming a field that does not exist. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [System.Management.Automation.Language.Ast]$ValueAst, + + [Parameter(Mandatory)] + [string]$ParameterName, + + [switch]$IsSwitchParameter + ) + + $text = $ValueAst.Extent.Text.Trim() + $simple = '$' + $ParameterName + if ($text -eq $simple -or $text -eq ('@(' + $simple + ')')) { return $true } + + $expr = Resolve-PfbSingleExpression -Ast $ValueAst + + $binary = $expr -as [System.Management.Automation.Language.BinaryExpressionAst] + if ($binary -and $binary.Operator -eq [System.Management.Automation.Language.TokenKind]::Join) { + $joinLeft = $binary.Left -as [System.Management.Automation.Language.VariableExpressionAst] + if ($joinLeft -and $joinLeft.VariablePath.UserPath -eq $ParameterName) { return $true } + } + + if ($IsSwitchParameter -and $expr -is [System.Management.Automation.Language.StringConstantExpressionAst]) { + if (Test-PfbAssignmentGuardedBySwitch -Assignment $ValueAst -ParameterName $ParameterName) { return $true } + } + + return $false +} + +function Test-PfbWireValueIsParameterProjection { + <# + .SYNOPSIS + True if -ValueAst is an array PROJECTION of the named parameter into single-key + sub-objects -- `@($Param | ForEach-Object { @{ name = $_ } })` -- the idiom this + module uses to build an array-of-references body field from a friendly [string[]]. + .DESCRIPTION + Sibling of Test-PfbWireValueIsParameter, deliberately kept separate rather than + folded into it. The two answer different questions: that one asks "does this value + hand the parameter to the wire as-is", this one asks "does this value hand each + ELEMENT of the parameter to the wire wrapped in a sub-object". Only the nested- + reference resolver may act on the second, because only it credits the OUTER key -- + the direct resolvers would have nowhere correct to attribute it, and would name an + `attached_servers.name` field that does not exist in the capability map. + + Shape-exact, matching this file's "never guess" contract. Accepted: + @($Param | ForEach-Object { @{ key = $_ } }) + $Param | ForEach-Object { @{ key = $_ } } -- unwrapped + @($Param | % { @{ key = $_ } }) -- alias + The inner key need not be literally 'name' (see the scalar resolver's + eradication_config precedent). Refused: a multi-key projection hashtable (a + composite, whose per-field ownership cannot be attributed to one parameter); an + innermost value that is anything but the bare $_ (`@{ name = $_.Name }`); a + pipeline source that is not the bare parameter; a longer pipeline (a Where-Object + filter in between means the wire value is a SUBSET, so the parameter does not cover + the field); and a ForEach-Object carrying any argument other than its script block. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [System.Management.Automation.Language.Ast]$ValueAst, + + [Parameter(Mandatory)] + [string]$ParameterName + ) + + $candidate = Resolve-PfbSingleExpression -Ast $ValueAst + + # `@( ... )` wraps the pipeline in an ArrayExpressionAst; the unwrapped form arrives as + # the PipelineAst itself (Resolve-PfbSingleExpression returns a multi-element pipeline + # unchanged -- it only peels SINGLE-element ones). + $pipeline = $null + if ($candidate -is [System.Management.Automation.Language.ArrayExpressionAst]) { + if ($candidate.SubExpression.Statements.Count -ne 1) { return $false } + $pipeline = $candidate.SubExpression.Statements[0] -as [System.Management.Automation.Language.PipelineAst] + } + else { + $pipeline = $candidate -as [System.Management.Automation.Language.PipelineAst] + } + if (-not $pipeline -or $pipeline.PipelineElements.Count -ne 2) { return $false } + + # Element 1: the bare parameter, and nothing else. This is where the parameter's identity + # comes from -- the innermost value is $_, which names nothing. + $source = $pipeline.PipelineElements[0] -as [System.Management.Automation.Language.CommandExpressionAst] + if (-not $source) { return $false } + $sourceVar = $source.Expression -as [System.Management.Automation.Language.VariableExpressionAst] + if (-not $sourceVar -or $sourceVar.VariablePath.UserPath -ne $ParameterName) { return $false } + + # Element 2: ForEach-Object with exactly one argument, a script block. + $command = $pipeline.PipelineElements[1] -as [System.Management.Automation.Language.CommandAst] + if (-not $command -or $command.CommandElements.Count -ne 2) { return $false } + $commandName = $command.CommandElements[0] -as [System.Management.Automation.Language.StringConstantExpressionAst] + if (-not $commandName -or $commandName.Value -notin @('ForEach-Object', '%')) { return $false } + $scriptBlockExpr = $command.CommandElements[1] -as [System.Management.Automation.Language.ScriptBlockExpressionAst] + if (-not $scriptBlockExpr) { return $false } + + # Script block body: exactly one statement, a single-string-key hashtable literal whose + # one value is the bare pipeline variable. + $scriptBlock = $scriptBlockExpr.ScriptBlock + if ($scriptBlock.BeginBlock -or $scriptBlock.ProcessBlock -or $scriptBlock.DynamicParamBlock) { return $false } + if (-not $scriptBlock.EndBlock -or $scriptBlock.EndBlock.Statements.Count -ne 1) { return $false } + + $hash = (Resolve-PfbSingleExpression -Ast $scriptBlock.EndBlock.Statements[0]) -as [System.Management.Automation.Language.HashtableAst] + if (-not $hash -or $hash.KeyValuePairs.Count -ne 1) { return $false } + if (-not ($hash.KeyValuePairs[0].Item1 -as [System.Management.Automation.Language.StringConstantExpressionAst])) { return $false } + + $innerValue = Resolve-PfbSingleExpression -Ast $hash.KeyValuePairs[0].Item2 + $innerVar = $innerValue -as [System.Management.Automation.Language.VariableExpressionAst] + return ($null -ne $innerVar -and $innerVar.VariablePath.UserPath -eq '_') +} + +function Get-PfbCommonQueryParamMap { + <# + .SYNOPSIS + The wire-name mapping that Private/Add-PfbCommonQueryParams.ps1 performs on a + calling cmdlet's behalf, so a parameter routed through that shared helper still + resolves a wire name even though the calling cmdlet contains no literal + `$queryParams['...'] = $Param` line of its own. + .DESCRIPTION + HARDCODED MIRROR of Private/Add-PfbCommonQueryParams.ps1 (issue #32/PR #49 + centralized -Filter/-Sort/-Limit/-TotalOnly/-Names/-Ids there; issue #33/PR #51 + migrated the rest). It is deliberately a copy rather than derived at runtime: this + tools/ library parses Public/ as text and must not depend on the module being + importable. Tests/PfbCmdletParamTools.Tests.ps1 asserts this table still matches + the helper's own AST assignments, so the two cannot silently drift. + + Two different detection rules, because the helper learns its inputs two different ways: + + ByParameterName -- the helper reads these straight out of the caller's + $PSBoundParameters (`if ($BoundParameters.ContainsKey('Filter')) {...}`), so the + caller's own param() name is the ONLY signal available. Only trusted when the + call site actually forwards $PSBoundParameters. + ByHelperArgument -- the helper takes these as its own parameters (-Names/-Ids), so + the signal is whichever variable the call site passes to that argument. That is + usually a `process`-block accumulator ($allNames), not the parameter itself -- + handled for free by Get-PfbCmdletParameterInventory's existing + Find-PfbAccumulatorVariable retry. + + NOT included: the non-generic keys (file_system_names, policy_names, role_names, + member_names, ...). Per issue #32's design those cmdlets deliberately kept their own + explicit `$queryParams[...] = ...` lines after the helper call and were NOT routed + through -Names/-Ids, so the literal-assignment resolver still handles them -- and + must keep doing so, which is why the helper fallback runs only after it. + .OUTPUTS + [PSCustomObject]@{ HelperName; ByParameterName; ByHelperArgument } + #> + [CmdletBinding()] + param() + + return [PSCustomObject]@{ + HelperName = 'Add-PfbCommonQueryParams' + ByParameterName = [ordered]@{ + Filter = 'filter' + Sort = 'sort' + Limit = 'limit' + TotalOnly = 'total_only' + } + ByHelperArgument = [ordered]@{ + Names = 'names' + Ids = 'ids' + } + } +} + +function Get-PfbCommonQueryParamHelperWireName { + <# + .SYNOPSIS + The Add-PfbCommonQueryParams-aware half of wire-name resolution: finds the key the + shared helper assigns on this cmdlet's behalf for -ParameterName, or $null. + .DESCRIPTION + Never guesses, matching the rest of this file: requires a literal -Into + (that variable is what Get-PfbEndpointForVariable later traces to an + Invoke-PfbApiRequest call, so without it there is nothing to attribute), requires + -BoundParameters to be literally $PSBoundParameters before trusting the + ByParameterName rule, only reads plain variable arguments, and returns $null if two + helper calls in the same function disagree on the (WireName, TargetVariable) pair. + .OUTPUTS + $null, or [PSCustomObject]@{ WireName; TargetVariable } -- same shape as + Get-PfbWireNameForParameter. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [System.Management.Automation.Language.FunctionDefinitionAst]$FunctionAst, + + [Parameter(Mandatory)] + [string]$ParameterName + ) + + $map = Get-PfbCommonQueryParamMap + $helperName = $map.HelperName + + $calls = @($FunctionAst.FindAll({ + param($node) + $node -is [System.Management.Automation.Language.CommandAst] -and + $node.GetCommandName() -eq $helperName + }, $true)) + if ($calls.Count -eq 0) { return $null } + + $found = [System.Collections.Generic.List[string]]::new() + + foreach ($call in $calls) { + $elements = $call.CommandElements + $intoVariable = $null + $forwardsBoundParameters = $false + $argumentVariables = @{} + + for ($i = 0; $i -lt $elements.Count; $i++) { + $el = $elements[$i] + if ($el -isnot [System.Management.Automation.Language.CommandParameterAst]) { continue } + + # `-Into:$queryParams` colon form parks the value on the CommandParameterAst + # itself; `-Into $queryParams` puts it in the NEXT element. Reading $next + # unconditionally would misread the colon form as taking the following switch. + $argExpr = if ($el.Argument) { $el.Argument } + elseif ($i + 1 -lt $elements.Count) { $elements[$i + 1] } + else { $null } + if (-not $argExpr) { continue } + $argVar = $argExpr -as [System.Management.Automation.Language.VariableExpressionAst] + + if ($el.ParameterName -eq 'Into') { + if ($argVar) { $intoVariable = $argVar.VariablePath.UserPath } + } + elseif ($el.ParameterName -eq 'BoundParameters') { + if ($argVar -and $argVar.VariablePath.UserPath -eq 'PSBoundParameters') { $forwardsBoundParameters = $true } + } + elseif ($map.ByHelperArgument.Contains($el.ParameterName)) { + if ($argVar) { $argumentVariables[$el.ParameterName] = $argVar.VariablePath.UserPath } + } + } + + if (-not $intoVariable) { continue } + + foreach ($helperArg in $map.ByHelperArgument.Keys) { + if ($argumentVariables.ContainsKey($helperArg) -and $argumentVariables[$helperArg] -eq $ParameterName) { + $found.Add("$($map.ByHelperArgument[$helperArg])|$intoVariable") + } + } + + if ($forwardsBoundParameters -and $map.ByParameterName.Contains($ParameterName)) { + $found.Add("$($map.ByParameterName[$ParameterName])|$intoVariable") + } + } + + $distinct = @($found | Select-Object -Unique) + if ($distinct.Count -ne 1) { return $null } + + $parts = $distinct[0] -split '\|', 2 + return [PSCustomObject]@{ WireName = $parts[0]; TargetVariable = $parts[1] } +} + +function Get-PfbWireNameForParameter { + <# + .SYNOPSIS + Finds the request-body or query-string key a given parameter is assigned to + inside a cmdlet function body, or $null if no simple assignment pattern matches. + .OUTPUTS + $null, or [PSCustomObject]@{ WireName; TargetVariable } -- TargetVariable is the + literal variable name the assignment targeted ('body' or 'queryParams'), needed + by Get-PfbEndpointForVariable to find the specific Invoke-PfbApiRequest call(s) + that variable is later passed to. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [System.Management.Automation.Language.FunctionDefinitionAst]$FunctionAst, + + [Parameter(Mandatory)] + [string]$ParameterName, + + [switch]$IsSwitchParameter + ) + + $assignments = $FunctionAst.FindAll({ + param($node) + $node -is [System.Management.Automation.Language.AssignmentStatementAst] -and + $node.Left -is [System.Management.Automation.Language.IndexExpressionAst] + }, $true) + + foreach ($assign in $assignments) { + $indexExpr = $assign.Left + $targetVar = $indexExpr.Target -as [System.Management.Automation.Language.VariableExpressionAst] + if (-not $targetVar) { continue } + if ($targetVar.VariablePath.UserPath -notin @('body', 'queryParams')) { continue } + + $keyExpr = $indexExpr.Index -as [System.Management.Automation.Language.StringConstantExpressionAst] + if (-not $keyExpr) { continue } + + if (Test-PfbWireValueIsParameter -ValueAst $assign.Right -ParameterName $ParameterName -IsSwitchParameter:$IsSwitchParameter) { + return [PSCustomObject]@{ + WireName = $keyExpr.Value + TargetVariable = $targetVar.VariablePath.UserPath + } + } + } + + # Second idiom: the whole hashtable is built as a LITERAL initializer rather than keyed + # into afterwards -- `$queryParams = @{ 'names' = $Name }`, the dominant shape across + # New-Pfb*/Remove-Pfb*/Update-Pfb* (New-PfbApiClient, New-PfbAlertWatcher, + # New-PfbObjectStoreAccount, the whole Policy/*Rule family, ...). Runs after the index + # form, not instead of it: a cmdlet routinely does both (literal initializer for its + # -Name, then `$body['x'] = $X` lines), and both key sets must resolve. + $literalMatch = Get-PfbHashtableLiteralWireNameForParameter -FunctionAst $FunctionAst -ParameterName $ParameterName -IsSwitchParameter:$IsSwitchParameter + if ($literalMatch) { return $literalMatch } + + # Third idiom: a nested single-key REFERENCE OBJECT -- `$body['account'] = @{ name = + # $Account }` -- whose wire field is the OUTER key. Runs strictly after both direct + # forms above so it can only ever add a resolution, never rename one: a parameter that + # already resolved via a direct assignment returned before reaching here. + $nestedMatch = Get-PfbNestedReferenceWireNameForParameter -FunctionAst $FunctionAst -ParameterName $ParameterName -IsSwitchParameter:$IsSwitchParameter + if ($nestedMatch) { return $nestedMatch } + + # No literal assignment of any shape in this function body -- but the parameter may + # still reach the wire through the shared Private/Add-PfbCommonQueryParams.ps1 helper, + # which performs the assignment on the cmdlet's behalf (issue #32/#33). Deliberately + # LAST: a cmdlet whose Name/Id-equivalent maps to a non-generic key (policy_names, + # file_system_names, ...) kept its own explicit line after the helper call, and that + # literal must win. + return Get-PfbCommonQueryParamHelperWireName -FunctionAst $FunctionAst -ParameterName $ParameterName +} + +function Get-PfbHashtableLiteralWireNameForParameter { + <# + .SYNOPSIS + The hashtable-literal-initializer half of wire-name resolution: finds the key a + parameter is given inside `$body = @{ ... }` / `$queryParams = @{ ... }`, or $null. + .DESCRIPTION + Only TOP-LEVEL key/value pairs of a hashtable literal assigned directly to a + variable named body/queryParams are considered, and a nested sub-object's INNER key + is never treated as the wire name: in `$body = @{ group = @{ name = $GroupName } }` + (real: New-PfbQuotaGroup) the wire field is `group`, so crediting -GroupName with + `name` would both mis-name the field and collide with every other sub-object's + `name`. Resolving such a parameter to its OUTER key is a separate, deliberately + later step -- see Get-PfbNestedReferenceWireNameForParameter. Value shapes are + matched by the same Test-PfbWireValueIsParameter used by the index-assignment path, + so a pipeline transform is still refused rather than guessed at. + .OUTPUTS + $null, or [PSCustomObject]@{ WireName; TargetVariable } -- same shape as + Get-PfbWireNameForParameter. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [System.Management.Automation.Language.FunctionDefinitionAst]$FunctionAst, + + [Parameter(Mandatory)] + [string]$ParameterName, + + [switch]$IsSwitchParameter + ) + + $assignments = @($FunctionAst.FindAll({ + param($node) + $node -is [System.Management.Automation.Language.AssignmentStatementAst] -and + $node.Left -is [System.Management.Automation.Language.VariableExpressionAst] + }, $true)) + + foreach ($assign in $assignments) { + $targetVar = $assign.Left -as [System.Management.Automation.Language.VariableExpressionAst] + if ($targetVar.VariablePath.UserPath -notin @('body', 'queryParams')) { continue } + + $hashtable = (Resolve-PfbSingleExpression -Ast $assign.Right) -as [System.Management.Automation.Language.HashtableAst] + if (-not $hashtable) { continue } + + foreach ($pair in $hashtable.KeyValuePairs) { + # KeyValuePairs entries are Tuple. Keys in this repo + # are written both quoted ('names') and bare (destroyed); StringConstantExpressionAst + # covers both and exposes the unquoted text as .Value. + $keyExpr = $pair.Item1 -as [System.Management.Automation.Language.StringConstantExpressionAst] + if (-not $keyExpr) { continue } + + if (Test-PfbWireValueIsParameter -ValueAst $pair.Item2 -ParameterName $ParameterName -IsSwitchParameter:$IsSwitchParameter) { + return [PSCustomObject]@{ + WireName = $keyExpr.Value + TargetVariable = $targetVar.VariablePath.UserPath + } + } + } + } + + return $null +} + +function Get-PfbNestedReferenceWireNameForParameter { + <# + .SYNOPSIS + The nested-single-key-reference-object half of wire-name resolution: finds the OUTER + key of `$body['account'] = @{ name = $Account }` / `$body = @{ account = @{ name = + $Account } }`, or $null. + .DESCRIPTION + The REST API models a reference to another resource as a single-key sub-object + (`{"account": {"name": "acct1"}}`), and the capability map records TOP-LEVEL body + properties only -- there is no `account.name` field in it. So the wire name a + parameter feeding such a sub-object exposes is the outer key (`account`), and any + attempt to credit it with the inner key would name a field that does not exist. + + Two parameters legitimately resolving to the SAME outer key is therefore correct, + not a collision to suppress: `-Account`/`-AccountId` both address the one `account` + field, and the endpoint's gap analysis only ever asks whether `account` is covered. + + Never guesses, matching the rest of this file: + - the target variable must be body/queryParams (an intermediate like + New-PfbFileSystem's $nfsBody is not traceable to an Invoke-PfbApiRequest call); + - the outer key must be a literal string constant; + - the nested hashtable must have EXACTLY ONE key/value pair, itself string-keyed + -- a multi-key sub-object is a composite whose per-field ownership cannot be + attributed to one parameter; + - only ONE level of nesting is descended; + - the innermost value must satisfy either Test-PfbWireValueIsParameter (the scalar + form, `@{ name = $Account }`) or Test-PfbWireValueIsParameterProjection (the + array form, New-PfbNetworkInterface's `@($AttachedServers | ForEach-Object + { @{ name = $_ } })`). Shapes that still cannot be attributed to one parameter + remain refused: a multi-key projection item, an innermost `$_.Member` rather than + the bare `$_`, and a filtered pipeline, whose wire value is a SUBSET of the + parameter. + + Deliberately invoked LAST of the three literal forms by + Get-PfbWireNameForParameter, after both direct-assignment resolvers: it can then + only ever turn an unresolved parameter into a Typed one, never rename an + already-resolved wire name. + .OUTPUTS + $null, or [PSCustomObject]@{ WireName; TargetVariable } -- same shape as + Get-PfbWireNameForParameter. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [System.Management.Automation.Language.FunctionDefinitionAst]$FunctionAst, + + [Parameter(Mandatory)] + [string]$ParameterName, + + [switch]$IsSwitchParameter + ) + + # Local predicate: is $Candidate a single-string-key hashtable literal whose one value + # hands $ParameterName to the wire? + $isReferenceObjectFor = { + param($Candidate) + # Array-of-references projection: the parameter's identity is the pipeline SOURCE, so + # this cannot route through Test-PfbWireValueIsParameter the way the scalar form does. + if (Test-PfbWireValueIsParameterProjection -ValueAst $Candidate -ParameterName $ParameterName) { return $true } + + $hash = (Resolve-PfbSingleExpression -Ast $Candidate) -as [System.Management.Automation.Language.HashtableAst] + if (-not $hash) { return $false } + if ($hash.KeyValuePairs.Count -ne 1) { return $false } + $innerPair = $hash.KeyValuePairs[0] + if (-not ($innerPair.Item1 -as [System.Management.Automation.Language.StringConstantExpressionAst])) { return $false } + return (Test-PfbWireValueIsParameter -ValueAst $innerPair.Item2 -ParameterName $ParameterName -IsSwitchParameter:$IsSwitchParameter) + } + + $assignments = @($FunctionAst.FindAll({ + param($node) + $node -is [System.Management.Automation.Language.AssignmentStatementAst] + }, $true)) + + # Index form first, then the literal-initializer form, mirroring the order + # Get-PfbWireNameForParameter uses for the direct shapes. + foreach ($assign in $assignments) { + $indexExpr = $assign.Left -as [System.Management.Automation.Language.IndexExpressionAst] + if (-not $indexExpr) { continue } + $targetVar = $indexExpr.Target -as [System.Management.Automation.Language.VariableExpressionAst] + if (-not $targetVar) { continue } + if ($targetVar.VariablePath.UserPath -notin @('body', 'queryParams')) { continue } + + $keyExpr = $indexExpr.Index -as [System.Management.Automation.Language.StringConstantExpressionAst] + if (-not $keyExpr) { continue } + + if (& $isReferenceObjectFor $assign.Right) { + return [PSCustomObject]@{ + WireName = $keyExpr.Value + TargetVariable = $targetVar.VariablePath.UserPath + } + } + } + + foreach ($assign in $assignments) { + $targetVar = $assign.Left -as [System.Management.Automation.Language.VariableExpressionAst] + if (-not $targetVar) { continue } + if ($targetVar.VariablePath.UserPath -notin @('body', 'queryParams')) { continue } + + $hashtable = (Resolve-PfbSingleExpression -Ast $assign.Right) -as [System.Management.Automation.Language.HashtableAst] + if (-not $hashtable) { continue } + + foreach ($pair in $hashtable.KeyValuePairs) { + $keyExpr = $pair.Item1 -as [System.Management.Automation.Language.StringConstantExpressionAst] + if (-not $keyExpr) { continue } + + if (& $isReferenceObjectFor $pair.Item2) { + return [PSCustomObject]@{ + WireName = $keyExpr.Value + TargetVariable = $targetVar.VariablePath.UserPath + } + } + } + } + + return $null +} + +function Find-PfbAccumulatorVariable { + <# + .SYNOPSIS + Finds the accumulator variable a parameter feeds via + `foreach ($x in $Param) { $accumulator.Add($x) }`, so its eventual wire-name + assignment can be traced by re-running Get-PfbWireNameForParameter against the + accumulator's own name. + .DESCRIPTION + Never guesses: returns $null unless there is exactly one such foreach loop over + $ParameterName, its body contains exactly one .Add(...) call whose target is a + bare variable and whose single argument is the loop variable, AND that same + accumulator variable is never .Add()-ed to from anywhere else in the function + (an ambiguous shared accumulator fed by more than one parameter's own loop). + .OUTPUTS + $null, or the accumulator's bare variable name (string, no leading '$'). + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [System.Management.Automation.Language.FunctionDefinitionAst]$FunctionAst, + + [Parameter(Mandatory)] + [string]$ParameterName + ) + + # ForEachStatementAst.Condition (the collection expression after `in`) is always wrapped + # in a PipelineAst containing a single CommandExpressionAst -- the parser never exposes + # a bare VariableExpressionAst directly here, so unwrap two levels before casting (the + # same wrapping phenomenon as AssignmentStatementAst.Right, documented on + # Get-PfbWireNameForParameter's $rhsExpr, just one layer deeper for a loop condition). + $allForeachLoops = @($FunctionAst.FindAll({ + param($node) + $node -is [System.Management.Automation.Language.ForEachStatementAst] + }, $true)) + + $foreachLoops = @($allForeachLoops | Where-Object { + $cond = $_.Condition + if ($cond -is [System.Management.Automation.Language.PipelineAst] -and $cond.PipelineElements.Count -eq 1) { + $cond = $cond.PipelineElements[0] + } + if ($cond -is [System.Management.Automation.Language.CommandExpressionAst]) { + $cond = $cond.Expression + } + $cond -is [System.Management.Automation.Language.VariableExpressionAst] -and + $cond.VariablePath.UserPath -eq $ParameterName + }) + + if ($foreachLoops.Count -ne 1) { return $null } + $loop = $foreachLoops[0] + $loopVarName = $loop.Variable.VariablePath.UserPath + + $addCallsInLoop = @($loop.Body.FindAll({ + param($node) + $node -is [System.Management.Automation.Language.InvokeMemberExpressionAst] -and + $node.Member -is [System.Management.Automation.Language.StringConstantExpressionAst] -and + $node.Member.Value -eq 'Add' -and + $node.Expression -is [System.Management.Automation.Language.VariableExpressionAst] -and + $node.Arguments.Count -eq 1 -and + $node.Arguments[0] -is [System.Management.Automation.Language.VariableExpressionAst] -and + $node.Arguments[0].VariablePath.UserPath -eq $loopVarName + }, $true)) + + if ($addCallsInLoop.Count -ne 1) { return $null } + $accumulatorName = $addCallsInLoop[0].Expression.VariablePath.UserPath + + $allAddCallsForAccumulator = @($FunctionAst.FindAll({ + param($node) + $node -is [System.Management.Automation.Language.InvokeMemberExpressionAst] -and + $node.Member -is [System.Management.Automation.Language.StringConstantExpressionAst] -and + $node.Member.Value -eq 'Add' -and + $node.Expression -is [System.Management.Automation.Language.VariableExpressionAst] -and + $node.Expression.VariablePath.UserPath -eq $accumulatorName + }, $true)) + + if ($allAddCallsForAccumulator.Count -ne 1) { return $null } + + return $accumulatorName +} + +function Get-PfbEndpointForVariable { + <# + .SYNOPSIS + Finds the (Method, Endpoint) pair a body/queryParams variable is passed to via + Invoke-PfbApiRequest -Body/-QueryParams within a function, IF every such call + agrees on exactly one (Method, Endpoint) pair. + .DESCRIPTION + Never guesses: returns $null when the variable feeds zero Invoke-PfbApiRequest + calls, or more than one call with a DIFFERENT (Method, Endpoint) pair (e.g. + Get-PfbNode's try/catch fallback that reuses the same $queryParams against two + genuinely different endpoints, 'nodes' then 'blades' -- correctly ambiguous, + not a case to force-pick one of). Only literal, unquoted-bareword-or-quoted- + string -Method/-Endpoint arguments are recognized, matching the exclusively + literal style every cmdlet in this repo actually uses for both. + .OUTPUTS + $null, or [PSCustomObject]@{ Method; Endpoint } + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [System.Management.Automation.Language.FunctionDefinitionAst]$FunctionAst, + + [Parameter(Mandatory)] + [string]$TargetVariable + ) + + $targetParamName = switch ($TargetVariable) { + 'body' { 'Body' } + 'queryParams' { 'QueryParams' } + default { $null } + } + if (-not $targetParamName) { return $null } + + $commands = $FunctionAst.FindAll({ + param($node) + $node -is [System.Management.Automation.Language.CommandAst] -and + $node.GetCommandName() -eq 'Invoke-PfbApiRequest' + }, $true) + + $pairs = [System.Collections.Generic.List[string]]::new() + + foreach ($cmd in $commands) { + $elements = $cmd.CommandElements + $usesVariable = $false + $method = $null + $endpoint = $null + + for ($i = 0; $i -lt $elements.Count; $i++) { + $el = $elements[$i] + if ($el -isnot [System.Management.Automation.Language.CommandParameterAst]) { continue } + $next = if ($i + 1 -lt $elements.Count) { $elements[$i + 1] } else { $null } + if (-not $next) { continue } + + if ($el.ParameterName -eq $targetParamName -and + $next -is [System.Management.Automation.Language.VariableExpressionAst] -and + $next.VariablePath.UserPath -eq $TargetVariable) { + $usesVariable = $true + } + elseif ($el.ParameterName -eq 'Method' -and $next -is [System.Management.Automation.Language.StringConstantExpressionAst]) { + $method = $next.Value + } + elseif ($el.ParameterName -eq 'Endpoint' -and $next -is [System.Management.Automation.Language.StringConstantExpressionAst]) { + $endpoint = $next.Value + } + } + + if ($usesVariable -and $method -and $endpoint) { + $pairs.Add("$($method.ToUpperInvariant())|$endpoint") + } + } + + $distinct = @($pairs | Select-Object -Unique) + if ($distinct.Count -ne 1) { return $null } + + $parts = $distinct[0] -split '\|', 2 + return [PSCustomObject]@{ Method = $parts[0]; Endpoint = $parts[1] } +} + +function Get-PfbCmdletBodyInsertionTarget { + <# + .SYNOPSIS + Insertion-point coordinates (drift-report-actionable-plan decision 12) for adding a + typed parameter to -FunctionAst for a currently-missing body-property gap -- NEVER + a diff/patch: a patch goes stale the moment the file is next touched and cannot see + mutual-exclusivity/parameter-set constraints a human editing by hand must respect. + .DESCRIPTION + PayloadVariable/AssignmentStyle describe what THIS cmdlet's function body already + does for its OTHER body fields, so a human adding one more matches the file's own + convention instead of inventing a new one: + - PayloadVariable is the literal variable name every `-Body ` argument on + this function's Invoke-PfbApiRequest call(s) agrees on (never guessed when + calls disagree, or an argument isn't a plain variable -- same "only ever ADD a + resolution, never guess" discipline as every other function in this file). + - If PayloadVariable is this cmdlet's OWN -Attributes parameter (the common + "-Body $Attributes" shape for write cmdlets with no typed body parameters at + all, e.g. Update-PfbCertificate), AssignmentStyle is 'attributesOnly': there is + no existing per-field assignment line to imitate, because the caller supplies + the whole hashtable directly -- adding a typed parameter here means introducing + the FIRST one, not extending an established pattern. + - Otherwise AssignmentStyle counts existing assignments INTO PayloadVariable using + the same two literal-assignment idioms Get-PfbWireNameForParameter already + recognizes: `$var['key'] = ...` (index form) vs. `$var = @{ 'key' = ... }` + (hashtable-literal-initializer form, counted only when it declares at least ONE + key/value pair -- an EMPTY `$var = @{}` bootstrap is the standard first line of + the INDEX idiom too and must not be miscounted as 'literal'). Whichever has MORE + occurrences in this function wins; a single non-empty literal initializer (even + with zero further index-form assignments after it) still counts as 'literal', + since it establishes every key at once. 'unknown' when PayloadVariable resolved + but this function contains no assignment into it at all (e.g. populated by a + private helper this AST-only inspector does not trace) -- surfaced rather than + guessed, per this file's "never guess" convention. + ParamBlockLine is the line of the LAST existing parameter in the param() block (or + the block's own opening line if it declares none) -- inserting a new parameter + after that line keeps it inside the existing block, below whatever + identity/ParameterSet parameters the cmdlet already declares, matching every + hand-written cmdlet in this module. + HasAttributes reuses the exact detection Get-PfbCmdletParameterInventory already + uses (`$_.Name.VariablePath.UserPath -eq 'Attributes'`), so this never disagrees + with the cmdlet-inventory's own AttributesOnly/EscapeHatchOnly classification. + .OUTPUTS + [PSCustomObject]@{ ParamBlockLine; PayloadVariable; AssignmentStyle; HasAttributes } + -- $null if -FunctionAst has no param() block at all (a function with no + parameters cannot be an Invoke-PfbApiRequest-calling cmdlet in this module, so this + should not occur for any cmdlet name sourced from Get-PfbModuleCalledEndpoints). + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [System.Management.Automation.Language.FunctionDefinitionAst]$FunctionAst + ) + + $paramBlock = $FunctionAst.Body.ParamBlock + if (-not $paramBlock) { return $null } + + $hasAttributesParam = [bool]($paramBlock.Parameters | Where-Object { $_.Name.VariablePath.UserPath -eq 'Attributes' }) + + $paramBlockLine = if ($paramBlock.Parameters.Count -gt 0) { + ($paramBlock.Parameters | Select-Object -Last 1).Extent.EndLineNumber + } + else { + $paramBlock.Extent.StartLineNumber + } + + $bodyCalls = @($FunctionAst.FindAll({ + param($n) + $n -is [System.Management.Automation.Language.CommandAst] -and + $n.GetCommandName() -eq 'Invoke-PfbApiRequest' + }, $true)) + + $bodyVarNames = [System.Collections.Generic.List[string]]::new() + foreach ($cmd in $bodyCalls) { + $elements = $cmd.CommandElements + for ($i = 0; $i -lt $elements.Count; $i++) { + $el = $elements[$i] + if ($el -isnot [System.Management.Automation.Language.CommandParameterAst]) { continue } + if ($el.ParameterName -ne 'Body') { continue } + $argExpr = if ($el.Argument) { $el.Argument } elseif ($i + 1 -lt $elements.Count) { $elements[$i + 1] } else { $null } + $argVar = $argExpr -as [System.Management.Automation.Language.VariableExpressionAst] + if ($argVar) { $bodyVarNames.Add($argVar.VariablePath.UserPath) } + } + } + $distinctBodyVars = @($bodyVarNames | Select-Object -Unique) + $payloadVariable = if ($distinctBodyVars.Count -eq 1) { $distinctBodyVars[0] } else { $null } + + $assignmentStyle = $null + if ($payloadVariable -eq 'Attributes') { + $assignmentStyle = 'attributesOnly' + } + elseif ($payloadVariable) { + $indexAssignments = @($FunctionAst.FindAll({ + param($n) + $n -is [System.Management.Automation.Language.AssignmentStatementAst] -and + $n.Left -is [System.Management.Automation.Language.IndexExpressionAst] -and + ($n.Left.Target -as [System.Management.Automation.Language.VariableExpressionAst]) -and + ($n.Left.Target).VariablePath.UserPath -eq $payloadVariable + }, $true)) + + $literalCandidates = @($FunctionAst.FindAll({ + param($n) + $n -is [System.Management.Automation.Language.AssignmentStatementAst] -and + $n.Left -is [System.Management.Automation.Language.VariableExpressionAst] -and + $n.Left.VariablePath.UserPath -eq $payloadVariable + }, $true)) + # An EMPTY hashtable-literal bootstrap (`$body = @{}`) does not count as 'literal' + # style -- it establishes zero keys and is the standard first line of the 'index' + # idiom too (`$body = @{}` followed by `$body['x'] = ...`). Only a literal that + # actually declares at least one key/value pair reflects the "establish every key + # at once" convention this style name describes. + $literalAssignments = @($literalCandidates | Where-Object { + $hash = (Resolve-PfbSingleExpression -Ast $_.Right) -as [System.Management.Automation.Language.HashtableAst] + $hash -and $hash.KeyValuePairs.Count -gt 0 + }) + + # A genuine TIE (indexAssignments.Count -eq literalAssignments.Count, both > 0) + # falls through to this 'literal' branch silently -- there is no distinct 'tie' + # outcome. Measured across the real high-confidence gap population: 274 + # attributesOnly / 95 unknown / 25 index / 1 literal / 7 unresolved, i.e. a true + # tie is essentially unreachable in practice today. That is a measured-safe + # observation about current data, not a design guarantee -- a future cmdlet could + # legitimately produce a tie, and it would resolve to 'literal' without any flag + # that the detection was actually ambiguous. + if ($indexAssignments.Count -gt $literalAssignments.Count) { $assignmentStyle = 'index' } + elseif ($literalAssignments.Count -gt 0) { $assignmentStyle = 'literal' } + else { $assignmentStyle = 'unknown' } + } + + return [PSCustomObject]@{ + ParamBlockLine = $paramBlockLine + PayloadVariable = $payloadVariable + AssignmentStyle = $assignmentStyle + HasAttributes = $hasAttributesParam + } +} + +function Get-PfbCmdletParameterInventory { + <# + .SYNOPSIS + Inventories every typed parameter (excluding -Array/-Attributes themselves) + across every function defined under -PublicDirectory. + .OUTPUTS + [PSCustomObject]@{ File; Line; Cmdlet; Parameter; HasValidateSet; ValidateSetValues; + WireName; Surface; Endpoint; Method } + + Endpoint/Method are $null unless the parameter's wire-name assignment resolved + to exactly one Invoke-PfbApiRequest call's endpoint (see + Get-PfbEndpointForVariable) -- never guessed. + + Line is the parameter's own declaration line ($p.Extent.StartLineNumber), + alongside the File it already carried -- so a consumer reporting on a + non-'Typed' Surface (Get-PfbParameterCoverageGaps's `confidence.unresolvedParameters` + in tools/lib/PfbApiDriftTools.ps1) can point a reader at an exact file:line rather + than making them search the whole file for the parameter name. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string]$PublicDirectory + ) + + $results = [System.Collections.Generic.List[object]]::new() + # Belt-and-braces only -- the load-bearing sort is on the records at the end of this + # function (issue #85). This one keeps the walk itself, and so any intermediate debugging + # output, stable too. + $files = @(Get-ChildItem -Path $PublicDirectory -Filter '*.ps1' -Recurse -File | Sort-Object -Property FullName -Culture '') + + foreach ($file in $files) { + $tokens = $null + $parseErrors = $null + $ast = [System.Management.Automation.Language.Parser]::ParseFile($file.FullName, [ref]$tokens, [ref]$parseErrors) + + $functionAsts = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true) + + foreach ($funcAst in $functionAsts) { + $paramBlock = $funcAst.Body.ParamBlock + if (-not $paramBlock) { continue } + + $hasAttributesParam = [bool]($paramBlock.Parameters | Where-Object { $_.Name.VariablePath.UserPath -eq 'Attributes' }) + + foreach ($p in $paramBlock.Parameters) { + $paramName = $p.Name.VariablePath.UserPath + if ($paramName -in @('Array', 'Attributes')) { continue } + + $validateSetValues = $null + foreach ($attr in $p.Attributes) { + if ($attr -is [System.Management.Automation.Language.AttributeAst] -and $attr.TypeName.Name -eq 'ValidateSet') { + $validateSetValues = @($attr.PositionalArguments | ForEach-Object { $_.SafeGetValue() }) + } + } + + $isSwitch = $p.StaticType -eq [System.Management.Automation.SwitchParameter] + $wireInfo = Get-PfbWireNameForParameter -FunctionAst $funcAst -ParameterName $paramName -IsSwitchParameter:$isSwitch + if (-not $wireInfo) { + $accumulatorName = Find-PfbAccumulatorVariable -FunctionAst $funcAst -ParameterName $paramName + if ($accumulatorName) { + $wireInfo = Get-PfbWireNameForParameter -FunctionAst $funcAst -ParameterName $accumulatorName + } + } + $wireName = if ($wireInfo) { $wireInfo.WireName } else { $null } + + $endpointInfo = if ($wireInfo) { Get-PfbEndpointForVariable -FunctionAst $funcAst -TargetVariable $wireInfo.TargetVariable } else { $null } + + $surface = if ($wireName) { 'Typed' } + elseif ($hasAttributesParam) { 'AttributesOnly' } + else { 'TypedUnresolved' } + + $results.Add([PSCustomObject]@{ + File = $file.FullName + Line = $p.Extent.StartLineNumber + Cmdlet = $funcAst.Name + Parameter = $paramName + HasValidateSet = [bool]$validateSetValues + ValidateSetValues = $validateSetValues + WireName = $wireName + Surface = $surface + Endpoint = if ($endpointInfo) { $endpointInfo.Endpoint } else { $null } + Method = if ($endpointInfo) { $endpointInfo.Method } else { $null } + }) + } + } + } + + # Sort at EMIT, not merely at input (issue #85). $files above is an unsorted recursive + # Get-ChildItem, whose order is filesystem-dependent -- and this list is verbatim the + # emit order of `entries`/`attributesOnly`/`typedUnresolved` in + # Reports/PfbFieldCmdletMap.json and of every markdown row in + # Reports/PfbFieldCmdletMapping.md (tools/Build-PfbFieldCmdletMap.ps1:76-95, :137-148). + # Regenerating on a Linux runner instead of a Windows workstation therefore produced a + # 10,218-line diff with zero semantic change: all 2015 entries moved, not one changed + # content, and the two files were even identical in byte LENGTH. + # + # Sorting the file list alone would be the fragile fix -- FullName carries + # platform-specific separators -- so the canonical order is imposed on the records + # themselves. -Culture '' is the invariant culture: without it the runner's locale + # could reintroduce the very divergence this removes. File/Line are tiebreakers only, + # for the (not currently occurring) case of one function name declared twice. + return @($results | Sort-Object -Property Cmdlet, Parameter, File, Line -Culture '') +} diff --git a/tools/lib/PfbContextRuleTools.ps1 b/tools/lib/PfbContextRuleTools.ps1 new file mode 100644 index 0000000..48683a9 --- /dev/null +++ b/tools/lib/PfbContextRuleTools.ps1 @@ -0,0 +1,450 @@ +#Requires -Version 5.1 + +<# + PfbContextRuleTools -- the spec-vs-module-assumption check for the Fusion + `context_names` cardinality rule. + + Every other drift category asks "does the module cover what the API offers?". This one + asks a different question: "is an assumption baked into the module still true?" + + THE LOAD-BEARING DESIGN POINT: the rule is read from ONE declared place -- + Private/Test-PfbContextMultiValueCapable.ps1, dot-sourced below and actually EXECUTED. + It is never re-derived here. A check that re-implements the rule it is checking + verifies nothing; it would agree with itself forever while the module drifted. + + WHY THIS IS A CROSS-SIGNAL CHECK RATHER THAN RULE-VS-COMPONENT. The first version of + this file compared the module's rule against one spec signal, the component name. That + shape is structurally blind to the failure that actually exists: four fleet-scoped GETs + reference the multi-value component AND are size-1 on the wire, so rule and component + AGREE and both are wrong in the same direction. Two-signal comparison cannot see that. + This file therefore compares every available signal and reports any endpoint whose + signals do not all agree -- including the case where the rule and the component agree + but the remaining signals dissent. + + The signals, and what each is worth (measured against fb2.27; see section 8 of + docs/design/fusion-context-injection.md): + + component `Context_names_get` 139 endpoints wrong for 5 + declares `allow_errors` 135 endpoints correct on every case with evidence + declares an HTTP 207 response 124 endpoints correct but stricter; excludes 11 + endpoints of unknown status + carries an `errors` envelope 139 endpoints UNRELIABLE -- applied by an + allOf-composed `_errorContextResponse` + authoring convention to 15 endpoints + that declare no 207, including all four + defective ones. Deliberately NOT + implemented here, and must never be + used as a signal. + + HTTP 207 is available from the specs under tools/ but NOT from the capability map, + which records the request surface only. It is therefore a corroborating signal for this + maintainer check and never a runtime gate -- which is why it is optional throughout: an + unknown 207 is carried as $null and excluded from comparison, never silently scored as + "does not declare". + + This file REPORTS and deliberately does not adjudicate. A disagreement is not + automatically a spec defect -- the case worth catching is a genuine future + multi-context endpoint, where the module would be the wrong side. A human decides which + signal is wrong, each time. +#> + +$script:PfbContextParameterName = 'context_names' +$script:PfbContextMultiValueComponent = 'Context_names_get' +$script:PfbAllowErrorsParameterName = 'allow_errors' + +# Execute the module's own rule rather than copying it. Failing loudly here is correct: +# silently falling back to a local copy of the rule is the one outcome that would make +# this whole check meaningless. +$script:PfbContextRuleRepoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +$script:PfbContextRulePredicatePath = Join-Path $script:PfbContextRuleRepoRoot 'Private/Test-PfbContextMultiValueCapable.ps1' +if (-not (Test-Path $script:PfbContextRulePredicatePath)) { + throw ("PfbContextRuleTools: cannot locate the module's declared context-cardinality " + + "rule at '$script:PfbContextRulePredicatePath'. This check must execute the " + + 'module rule, never re-implement it -- refusing to load rather than silently ' + + 'verifying a private copy of the rule against itself.') +} +. $script:PfbContextRulePredicatePath + +function Get-PfbContextHttp207Endpoint { + <# + .SYNOPSIS + Endpoint keys (" /") in one spec document that declare an HTTP 207 + (Multi-Status) response. + .DESCRIPTION + The 207 signal is the reason this lives here rather than in the capability map: the + map holds no response data at all. Feed the result to Get-PfbContextParameterFact's + -Http207Endpoint so map-sourced facts can still carry the signal. + + Only the PRESENCE of the '207' key on the operation's `responses` object matters, so + no $ref resolution is needed or attempted -- a 207 whose value is a $ref to a + response component is still a declared 207. Path normalization goes through the + shared ConvertTo-PfbNormalizedPath so keys match Get-PfbSpecCapabilities exactly. + .OUTPUTS + [string[]] sorted endpoint keys. Empty array when the spec declares none. + #> + [CmdletBinding()] + [OutputType([string[]])] + param( + [Parameter(Mandatory)] + $Spec + ) + + if (-not $Spec.paths) { return @() } + + $found = [System.Collections.Generic.List[string]]::new() + foreach ($rawPath in $Spec.paths.PSObject.Properties.Name) { + $pathItem = $Spec.paths.$rawPath + $normalizedPath = ConvertTo-PfbNormalizedPath -Path $rawPath + foreach ($methodName in $pathItem.PSObject.Properties.Name) { + if ($script:PfbHttpMethods -notcontains $methodName) { continue } + $op = $pathItem.$methodName + if (-not $op.responses) { continue } + if ($op.responses.PSObject.Properties.Name -contains '207') { + $found.Add("$($methodName.ToUpper()) $normalizedPath") + } + } + } + + return @($found | Sort-Object -Unique) +} + +function Get-PfbContextParameterFact { + <# + .SYNOPSIS + Normalizes every endpoint carrying a `context_names` parameter into one comparable + signal record, from either the committed capability map or a single spec version's + Get-PfbSpecCapabilities output. + .DESCRIPTION + Two sources, deliberately both supported, because they answer different questions + and can legitimately disagree: + + -CapabilityMap Data/PfbCapabilityMap.json (parsed). Its component data is + LAST-SEEN-WINS across the versions it was generated from -- + it describes the NEWEST version's wire shape, not a union. + See Build-PfbCapabilityMap.ps1's + parameterComponentDefaults/parameterComponentOverrides + resolution contract. + -SpecCapabilities Get-PfbSpecCapabilities output for ONE fb.json. + Use this to ask the question of a specific REST version. + + Dual input matters more since rev 3: the four defective endpoints' signals will all + change when the upstream fix lands, and being able to ask a specific version is how + that gets confirmed rather than assumed. These two inputs also drift when specs are + refreshed without rebuilding the map, so every figure derived from them should be + stated together with which input and which version it came from. + + Component resolution for -CapabilityMap follows the documented three-step contract: + (1) the endpoint's parameterComponentOverrides value for the parameter if the KEY is + present -- which may be JSON null, meaning "this endpoint's parameter has no + component"; otherwise (2) the top-level parameterComponentDefaults value; otherwise + (3) no known component. Key-present-but-null and key-absent are distinguished, never + conflated. + .PARAMETER Http207Endpoint + Optional endpoint keys declaring an HTTP 207, from Get-PfbContextHttp207Endpoint. + When omitted, DeclaresHttp207 is $null on every record -- meaning UNKNOWN, not + "does not declare". Downstream comparison excludes unknown signals rather than + scoring them, so omitting this narrows what the check can see without ever making + it report something false. + .OUTPUTS + [PSCustomObject][] sorted by Endpoint, one per endpoint declaring `context_names`: + Endpoint " /" + Method HTTP method, upper-case + Path normalized path + ContextComponent resolved component name, or $null when the endpoint + declares the parameter inline with no "$ref" + ComponentSaysMultiValue [bool], or $null when ContextComponent is $null + DeclaresAllowErrors [bool] + DeclaresHttp207 [bool], or $null when not supplied (UNKNOWN) + RuleSaysMultiValue [bool] -- the MODULE's verdict, from + Test-PfbContextMultiValueCapable + #> + [CmdletBinding(DefaultParameterSetName = 'CapabilityMap')] + [OutputType([PSCustomObject[]])] + param( + [Parameter(Mandatory, ParameterSetName = 'CapabilityMap')] + $CapabilityMap, + + [Parameter(Mandatory, ParameterSetName = 'SpecCapabilities')] + [AllowEmptyCollection()] + [object[]]$SpecCapabilities, + + [AllowNull()] + [string[]]$Http207Endpoint + ) + + # $null (parameter omitted) must stay distinguishable from an empty set (a spec that + # genuinely declares no 207 anywhere) -- the first means "unknown", the second "none". + # + # Assigned by a plain if-statement, NOT `$set207 = if (...) { $hashSet }`: a HashSet + # emitted as the value of a scriptblock gets ENUMERATED, which turns an empty set into + # $null (silently reclassifying "none" as "unknown") and a populated one into a plain + # array that has lost the OrdinalIgnoreCase comparer. + $has207Data = $PSBoundParameters.ContainsKey('Http207Endpoint') -and $null -ne $Http207Endpoint + $set207 = $null + if ($has207Data) { + $set207 = [System.Collections.Generic.HashSet[string]]::new([string[]]@($Http207Endpoint), [System.StringComparer]::OrdinalIgnoreCase) + } + + $facts = [System.Collections.Generic.List[object]]::new() + + if ($PSCmdlet.ParameterSetName -eq 'CapabilityMap') { + if (-not $CapabilityMap -or -not $CapabilityMap.endpoints) { return @() } + $defaults = $CapabilityMap.parameterComponentDefaults + + foreach ($endpointKey in $CapabilityMap.endpoints.PSObject.Properties.Name) { + $entry = $CapabilityMap.endpoints.$endpointKey + if (-not $entry.parameters) { continue } + $paramNames = $entry.parameters.PSObject.Properties.Name + if ($paramNames -notcontains $script:PfbContextParameterName) { continue } + + $component = $null + $overrides = $entry.parameterComponentOverrides + if ($overrides -and ($overrides.PSObject.Properties.Name -contains $script:PfbContextParameterName)) { + # Key present -- authoritative, even when its value is null. + $component = $overrides.$($script:PfbContextParameterName) + } + elseif ($defaults -and ($defaults.PSObject.Properties.Name -contains $script:PfbContextParameterName)) { + $component = $defaults.$($script:PfbContextParameterName) + } + + # Endpoint keys are " /"; split once on the first space only, so + # a path can never be truncated. + $splitIndex = $endpointKey.IndexOf(' ') + if ($splitIndex -lt 1) { continue } + $method = $endpointKey.Substring(0, $splitIndex) + $path = $endpointKey.Substring($splitIndex + 1) + + $facts.Add((New-PfbContextParameterFactRecord -Method $method -Path $path ` + -Component $component ` + -DeclaresAllowErrors ($paramNames -contains $script:PfbAllowErrorsParameterName) ` + -Set207 $set207)) + } + } + else { + foreach ($capability in $SpecCapabilities) { + if (-not $capability) { continue } + $components = $capability.ParameterComponents + $parameters = @($capability.Parameters) + if ($parameters -notcontains $script:PfbContextParameterName) { continue } + + $component = $null + if ($components -and $components.ContainsKey($script:PfbContextParameterName)) { + $component = $components[$script:PfbContextParameterName] + } + + $facts.Add((New-PfbContextParameterFactRecord -Method $capability.Method -Path $capability.Path ` + -Component $component ` + -DeclaresAllowErrors ($parameters -contains $script:PfbAllowErrorsParameterName) ` + -Set207 $set207)) + } + } + + # Hashtable/PSObject property order is not guaranteed and this feeds a tracked report. + return @($facts | Sort-Object -Property Endpoint) +} + +function New-PfbContextParameterFactRecord { + <# + .SYNOPSIS + Internal helper for Get-PfbContextParameterFact: builds one signal record and, in + doing so, is the single point at which the MODULE's rule is executed. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] [string]$Method, + [Parameter(Mandatory)] [string]$Path, + [AllowNull()] [string]$Component, + [Parameter(Mandatory)] [bool]$DeclaresAllowErrors, + [AllowNull()] $Set207 + ) + + $upperMethod = $Method.ToUpperInvariant() + $endpoint = "$upperMethod $Path" + + # An empty-string component is treated as absent: the generator documents that a + # parameter with no "$ref" contributes NO key rather than an empty value, so an empty + # string here means malformed input, not "a component named ''". + $hasComponent = -not [string]::IsNullOrWhiteSpace($Component) + + return [PSCustomObject]@{ + Endpoint = $endpoint + Method = $upperMethod + Path = $Path + ContextComponent = if ($hasComponent) { $Component } else { $null } + ComponentSaysMultiValue = if ($hasComponent) { $Component -eq $script:PfbContextMultiValueComponent } else { $null } + DeclaresAllowErrors = $DeclaresAllowErrors + DeclaresHttp207 = if ($null -ne $Set207) { $Set207.Contains($endpoint) } else { $null } + RuleSaysMultiValue = [bool](Test-PfbContextMultiValueCapable -Method $upperMethod ` + -ContextComponent $Component -DeclaresAllowErrors $DeclaresAllowErrors) + } +} + +function Get-PfbContextSignalDisagreement { + <# + .SYNOPSIS + The check: every endpoint whose cardinality signals do not all agree. + .DESCRIPTION + Replaces the earlier rule-vs-component comparison, which was structurally unable to + see the defect that actually exists (rule and component agreeing while the wire + disagrees with both). + + Compared signals, per endpoint: the component identity, whether `allow_errors` is + declared, and -- when known -- whether an HTTP 207 is declared. Unanimity means no + finding. An UNKNOWN 207 is excluded from the comparison rather than scored as + "does not declare", so supplying less data can narrow what the check sees but can + never make it report something false. + + The module's own verdict (RuleSaysMultiValue) is carried on every finding for + transparency, but is not itself a compared signal: it is derived from two of them, + so including it would double-weight the component and `allow_errors` rather than + adding independent evidence. + + Endpoints whose `context_names` has no resolvable component are not comparable and + are excluded here rather than silently scored as size-1; retrieve them from + Get-PfbContextUnresolvedComponent so they stay visible. + + Findings are classified by Shape so the report can group them. The shapes are + exhaustive over the non-unanimous combinations: + + component-says-multi-value-but-no-allow-errors + The four fleet-scoped GETs today, and DELETE /management-access-policies in + 2.26/2.27. Strong evidence of a spec defect: the component claims fan-out + while the endpoint offers no partial-failure story. + size-1-component-but-declares-allow-errors + The named mirror case, PATCH /directory-services/test. + rule-says-capable-but-declares-no-207 + Rule and component and `allow_errors` all agree the endpoint fans out, but it + declares no 207. 11 endpoints at fb2.27 -- status genuinely unknown, which is + precisely why 207 is not used as a runtime gate. + size-1-but-declares-207 + Declares a partial-failure response while nothing else says it fans out. + .PARAMETER Fact + Get-PfbContextParameterFact output. + .OUTPUTS + [PSCustomObject][] sorted by Endpoint: + Endpoint, Method, ContextComponent, Shape, RuleSaysMultiValue, + ComponentSaysMultiValue, DeclaresAllowErrors, DeclaresHttp207, + MultiValueSignals, SizeOneSignals, Summary + #> + [CmdletBinding()] + [OutputType([PSCustomObject[]])] + param( + [Parameter(Mandatory)] + [AllowEmptyCollection()] + [object[]]$Fact + ) + + $findings = foreach ($f in $Fact) { + if ($null -eq $f.ComponentSaysMultiValue) { continue } + + $signals = [ordered]@{ + component = [bool]$f.ComponentSaysMultiValue + allow_errors = [bool]$f.DeclaresAllowErrors + } + if ($null -ne $f.DeclaresHttp207) { $signals['http207'] = [bool]$f.DeclaresHttp207 } + + $multiSignals = @($signals.Keys | Where-Object { $signals[$_] } | Sort-Object) + $sizeOneSignals = @($signals.Keys | Where-Object { -not $signals[$_] } | Sort-Object) + if ($multiSignals.Count -eq 0 -or $sizeOneSignals.Count -eq 0) { continue } # unanimous + + $component = $signals['component'] + $allowErrors = $signals['allow_errors'] + $shape = if ($component -and -not $allowErrors) { 'component-says-multi-value-but-no-allow-errors' } + elseif (-not $component -and $allowErrors) { 'size-1-component-but-declares-allow-errors' } + elseif ($component -and $allowErrors) { 'rule-says-capable-but-declares-no-207' } + else { 'size-1-but-declares-207' } + + [PSCustomObject]@{ + Endpoint = $f.Endpoint + Method = $f.Method + ContextComponent = $f.ContextComponent + Shape = $shape + RuleSaysMultiValue = $f.RuleSaysMultiValue + ComponentSaysMultiValue = $f.ComponentSaysMultiValue + DeclaresAllowErrors = $f.DeclaresAllowErrors + DeclaresHttp207 = $f.DeclaresHttp207 + MultiValueSignals = $multiSignals + SizeOneSignals = $sizeOneSignals + Summary = ("signals split -- multi-value: $($multiSignals -join ', '); " + + "size-1: $($sizeOneSignals -join ', '); module rule says " + + $(if ($f.RuleSaysMultiValue) { 'multi-value' } else { 'size-1' })) + } + } + + return @($findings | Sort-Object -Property Endpoint) +} + +function Get-PfbContextSizeOneWithAllowErrors { + <# + .SYNOPSIS + The named mirror case: endpoints whose component says size-1 yet which declare + `allow_errors`. + .DESCRIPTION + A NAMED SUBSET of Get-PfbContextSignalDisagreement's findings (Shape + 'size-1-component-but-declares-allow-errors'), never an additional category -- + reporting it separately as well would double-count one defect as two findings. + Surfaced under its own name only because it is a known, characterized case worth + pointing at directly: `PATCH /directory-services/test`, the one size-1 endpoint + declaring `allow_errors`, which rev 3's Appendix A records as NOT live-verified + (it is a mutating verb and was not probed). + + Its counterpart -- agreed multi-value endpoints lacking `allow_errors` -- is + deliberately NOT surfaced here any more. Under rev 3's rule that is a cardinality + finding, not a co-occurrence curiosity, and it belongs in the disagreement list. + .PARAMETER Fact + Get-PfbContextParameterFact output. + #> + [CmdletBinding()] + [OutputType([PSCustomObject[]])] + param( + [Parameter(Mandatory)] + [AllowEmptyCollection()] + [object[]]$Fact + ) + + return @($Fact | + Where-Object { $false -eq $_.ComponentSaysMultiValue -and $_.DeclaresAllowErrors } | + ForEach-Object { + [PSCustomObject]@{ + Endpoint = $_.Endpoint + Method = $_.Method + ContextComponent = $_.ContextComponent + DeclaresHttp207 = $_.DeclaresHttp207 + } + } | Sort-Object -Property Endpoint) +} + +function Get-PfbContextUnresolvedComponent { + <# + .SYNOPSIS + Endpoints declaring `context_names` with no resolvable component -- neither + checkable nor safe to score as size-1. + .DESCRIPTION + Expected to be empty: verified 0 occurrences across every spec where the parameter + exists at all, which is fb2.17-fb2.28 (`context_names` does not exist before 2.17, + so the wider fb2.0-2.28 range this comment previously claimed was not wrong so much + as vacuous over its first seventeen versions). + + Surfaced anyway because the failure mode it guards against is silent: were a future + spec to declare `context_names` inline with no "$ref", treating it as size-1 by + default would hide a genuine multi-value endpoint behind a hardcoded throw. + .PARAMETER Fact + Get-PfbContextParameterFact output. + #> + [CmdletBinding()] + [OutputType([PSCustomObject[]])] + param( + [Parameter(Mandatory)] + [AllowEmptyCollection()] + [object[]]$Fact + ) + + return @($Fact | Where-Object { $null -eq $_.ComponentSaysMultiValue } | + ForEach-Object { + [PSCustomObject]@{ + Endpoint = $_.Endpoint + Method = $_.Method + DeclaresAllowErrors = $_.DeclaresAllowErrors + } + } | Sort-Object -Property Endpoint) +} diff --git a/tools/lib/PfbSpecTools.ps1 b/tools/lib/PfbSpecTools.ps1 new file mode 100644 index 0000000..72d344d --- /dev/null +++ b/tools/lib/PfbSpecTools.ps1 @@ -0,0 +1,805 @@ +<# +.SYNOPSIS + Shared helpers for fetching and diffing FlashBlade OpenAPI specs across REST + versions. Dot-sourced by tools/Update-PfbApiSpecs.ps1, tools/Build-PfbCapabilityMap.ps1, + and their Pester tests. + +.DESCRIPTION + Each FlashBlade REST version's ReDoc reference page at + https://code.purestorage.com/swagger/redoc/fb-api-reference.html embeds the + full OpenAPI 3.0.1 document inline as a JavaScript object literal: + + + + There is no standalone .json/.yaml URL — the page's "Download" button serializes this + in-memory object to a client-side blob: URL, which cannot be fetched directly. These + helpers extract the embedded object server-side instead. + + Confirmed (2025-07-08, specs fb2.10 and fb2.27): + - The object is a single valid JSON value (ConvertFrom-Json handles it directly). + - Paths for versioned resource endpoints are prefixed with the REST version itself, + e.g. "/api/2.27/arrays" vs "/api/2.10/arrays" — must be normalized before + comparing the same logical endpoint across versions. A handful of auth/meta + endpoints (/api/login, /api/api_version, /api/logout, /api/login-banner, + /oauth2/1.0/token) are NOT version-prefixed and are left as-is. + - Path items include a vendor extension key "x-pure-authorization-resource" + alongside real HTTP-method keys — must filter to actual HTTP verbs. + - Parameters and request bodies are almost always $ref'd into + components.parameters / components.schemas rather than inlined. + - The spec contains NO structural JSON Schema "enum" anywhere (verified: zero + occurrences across all 925 schemas / 224 parameters in fb2.27, and again in + fb2.10). Allowed values for fields like Bucket.versioning are documented only in + free-text `description` prose ("Valid values are `none`, `enabled`, ..."). This + means per-enum-value "introduced in version X" tracking is NOT derivable from + structured data, and is intentionally out of scope for the generated capability + map — only endpoint, parameter, and request-body top-level property existence are + tracked. +#> + +# Deliberately NOT Set-StrictMode: these functions walk deeply heterogeneous +# PSCustomObjects deserialized from JSON where a given node legitimately may or may not +# have a given property (e.g. not every operation has .parameters or .requestBody). +# Under StrictMode -Version Latest, referencing a missing property throws instead of +# returning $null, which breaks the `if ($op.parameters)`-style presence checks used +# throughout. Every access here is deliberately null-tolerant. + +$script:PfbHttpMethods = @('get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace') + +function ConvertFrom-PfbRedocHtml { + <# + .SYNOPSIS + Extracts the embedded OpenAPI document from a FlashBlade ReDoc reference page. + .PARAMETER Html + The full HTML content of a fb-api-reference.html page. + .OUTPUTS + The OpenAPI document (PSCustomObject), i.e. __redoc_state.spec.data. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string]$Html + ) + + $marker = 'const __redoc_state = ' + $markerIdx = $Html.IndexOf($marker) + if ($markerIdx -lt 0) { + throw "Could not find '__redoc_state' assignment in the ReDoc page. The page format may have changed." + } + + $braceStart = $Html.IndexOf('{', $markerIdx) + if ($braceStart -lt 0) { + throw "Found '__redoc_state' marker but no opening brace followed it." + } + + # Balanced-brace scan respecting quoted strings and escape sequences, since the + # object is minified JSON embedded directly in a