From bff34fb1e2ca1d717b180cd04126d91024083533 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 18 Jul 2017 13:10:33 +0200 Subject: [PATCH 01/19] xFailOverCluster: Localization support, localized strings in xClusterDisk and some cleanup (#111) - Changes to xFailOverCluster - Added a common resource helper module with helper functions for localization. - Added helper functions; Get-LocalizedData, New-InvalidResultException, New-ObjectNotFoundException, New-InvalidOperationException and New-InvalidArgumentException. - Fixed lint error MD034 and fixed typos in README.md. - Changes to xClusterDisk - Enabled localization for all strings ([issue #84](https://github.com/PowerShell/xFailOverCluster/issues/84)). - Changes to xClusterNetwork - Replaced the URL for the parameter Role in README.md. The new URL is a more generic description of the possible settings for the Role parameter. The previous URL was still correct but focused on Hyper-V in particular. - Fixed typos in parameter descriptions in README.md. --- CHANGELOG.md | 62 ++-- DSCResources/CommonResourceHelper.psm1 | 265 ++++++++++++++++++ .../MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 | 11 +- .../en-US/MSFT_xClusterDisk.strings.psd1 | 7 + README.md | 6 +- Tests/Unit/CommonResourceHelper.Tests.ps1 | 191 +++++++++++++ 6 files changed, 512 insertions(+), 30 deletions(-) create mode 100644 DSCResources/CommonResourceHelper.psm1 create mode 100644 DSCResources/MSFT_xClusterDisk/en-US/MSFT_xClusterDisk.strings.psd1 create mode 100644 Tests/Unit/CommonResourceHelper.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 24f9454..9d2cc7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,49 +2,63 @@ ## Unreleased +- Changes to xFailOverCluster + - Added a common resource helper module with helper functions for localization. + - Added helper functions; Get-LocalizedData, New-InvalidResultException, + New-ObjectNotFoundException, New-InvalidOperationException and + New-InvalidArgumentException. + - Fixed lint error MD034 and fixed typos in README.md. +- Changes to xClusterDisk + - Enabled localization for all strings ([issue #84](https://github.com/PowerShell/xFailOverCluster/issues/84)). +- Changes to xClusterNetwork + - Replaced the URL for the parameter Role in README.md. The new URL is a more + generic description of the possible settings for the Role parameter. The + previous URL was still correct but focused on Hyper-V in particular. + - Fixed typos in parameter descriptions in README.md. + ## 1.7.0.0 - Changes to xClusterPreferredOwner - - Script Analyzer warnings have been fixed (issue #50). This also failed the + - Script Analyzer warnings have been fixed ([issue #50](https://github.com/PowerShell/xFailOverCluster/issues/50)). This also failed the tests for the resource. - Changes to xClusterDisk - - Fixed test that was failing in AppVeyor (issue #55). + - Fixed test that was failing in AppVeyor ([issue #55](https://github.com/PowerShell/xFailOverCluster/issues/55)). - Changes to xFailOverCluster - - Added 'Code of Conduct' text to the README.md (issue #44). - - Added TOC for all resources in the README.md (issue #43). + - Added 'Code of Conduct' text to the README.md ([issue #44](https://github.com/PowerShell/xFailOverCluster/issues/44)). + - Added TOC for all resources in the README.md ([issue #43](https://github.com/PowerShell/xFailOverCluster/issues/43)). - Fixed typos and lint errors in README.md. - Fixed style issue in example in README.md. - Removed 'Unreleased' "tag" from the resources xClusterQuorum and - xClusterDisk (issue #36) + xClusterDisk ([issue #36](https://github.com/PowerShell/xFailOverCluster/issues/36)) - Added new sections to each resource (Requirements, Parameters and Examples) in the README.md. Some does not yet have any examples, so they are set to 'None.'. - Added GitHub templates PULL\_REQUEST\_TEMPLATE, ISSUE_TEMPLATE and - CONTRIBUTING.md (issue #45). + CONTRIBUTING.md ([issue #45](https://github.com/PowerShell/xFailOverCluster/issues/45)). - Split the change log from README.md to a separate file CHANGELOG.md - (issue #48). - - Added the resource xClusterPreferredOwner to README.md (issue #51). - - Added the resource xClusterNetwork to README.md (issue #56). + [issue #48](https://github.com/PowerShell/xFailOverCluster/issues/48). + - Added the resource xClusterPreferredOwner to README.md ([issue #51](https://github.com/PowerShell/xFailOverCluster/issues/51)). + - Added the resource xClusterNetwork to README.md ([issue #56](https://github.com/PowerShell/xFailOverCluster/issues/56)). - Removed Credential parameter from parameter list for xWaitForCluster. Parameter Credential does not exist in the schema.mof of the resource - (issue #62). + ([issue #62](https://github.com/PowerShell/xFailOverCluster/issues/62)). - Now all parameters in the README.md list their data type and type qualifier - (issue #58.) + ([issue #58](https://github.com/PowerShell/xFailOverCluster/issues/58)). - Added Import-DscResource to example in README.md. - - Added CodeCov and opt-in for all common tests (issue #41). + - Added CodeCov and opt-in for all common tests ([issue #41](https://github.com/PowerShell/xFailOverCluster/issues/41)). - Added CodeCov badge to README.md - Fixed CodeCov badge links so they now can be clicked on. - Fixed lint rule MD013 in CHANGELOG.md. - Fixed lint rule MD013 in README.md. - Fixed lint rule MD024 in README.md. - Fixed lint rule MD032 in README.md. - - Removed example from README.md (issue #42). + - Removed example from README.md ([issue #42](https://github.com/PowerShell/xFailOverCluster/issues/42)). - Fixed typo in filename for ISSUE\_TEMPLATE. Was 'ISSUE\_TEMPLATE', now it is correctly 'ISSUE\_TEMPLATE.md'. - Changed appveyor.yml to use the new default test framework in the AppVeyor module in DscResource.Tests (AppVeyor.psm1). - Added VS Code workspace settings file with formatting settings matching the - Style Guideline (issue #67). That will make it possible inside VS Code to + Style Guideline ([issue #67](https://github.com/PowerShell/xFailOverCluster/issues/67)). That will make it possible inside VS Code to press SHIFT+ALT+F, or press F1 and choose 'Format document' in the list. The PowerShell code will then be formatted according to the Style Guideline (although maybe not complete, but would help a long way). @@ -63,41 +77,41 @@ - Fixed typo in xCluster parameter description. - Added links to examples from README.md - Refactored the unit test for this resource to use stubs and increase coverage - (issue #73). + ([issue #73](https://github.com/PowerShell/xFailOverCluster/issues/73)). - Removed the password file (MSFT_xCluster.password.txt) which seemed unnecessary. - Test-TargetResource now throws and error if domain name cannot be evaluated - (issue #72). + ([issue #72](https://github.com/PowerShell/xFailOverCluster/issues/72)). - Set-TargetResource now correctly throws and error if domain name cannot be - evaluated (issue #71). + evaluated ([issue #71](https://github.com/PowerShell/xFailOverCluster/issues/71)). - Changes to xWaitForCluster - Added example - 1-WaitForFailoverClusterToBePresent.ps1 - Added link to example from README.md - Changes to xClusterDisk - Refactored the unit test for this resource to use stubs and increase coverage - (issue #74). + ([issue #74](https://github.com/PowerShell/xFailOverCluster/issues/74)). - Removed an evaluation that called Test-TargetResource in Set-TargetResource method and instead added logic so that Set-TargetResource evaluates if it - should remove a disk (issue #90). + should remove a disk ([issue #90](https://github.com/PowerShell/xFailOverCluster/issues/90)). - Changed the code to be more aligned with the style guideline. - - Added examples (issue #46) + - Added examples ([issue #46](https://github.com/PowerShell/xFailOverCluster/issues/46)) - 1-AddClusterDisk.ps1 - 2-RemoveClusterDisk.ps1 - Added links to examples from README.md. - Changes to xClusterPreferredOwner - Refactored the unit test for this resource to use stubs and increase coverage - (issue #76). + ([issue #76](https://github.com/PowerShell/xFailOverCluster/issues/76)). - Changed the code to be more aligned with the style guideline. - - Added examples (issue #52) + - Added examples ([issue #52](https://github.com/PowerShell/xFailOverCluster/issues/52)) - 1-AddPreferredOwner.ps1 - 2-RemovePreferredOwner.ps1 - Added links to examples from README.md. - Changes to xClusterNetwork - Refactored the unit test for this resource to use stubs and increase coverage - (issue #75). + ([issue #75](https://github.com/PowerShell/xFailOverCluster/issues/75)). - Changed the code to be more aligned with the style guideline. - Updated resource and parameter description in README.md and schema.mof. - - Added example (issue #57) + - Added example ([issue #57](https://github.com/PowerShell/xFailOverCluster/issues/57)) - 1-ChangeClusterNetwork.ps1 - Added links to examples from README.md. diff --git a/DSCResources/CommonResourceHelper.psm1 b/DSCResources/CommonResourceHelper.psm1 new file mode 100644 index 0000000..3541fa5 --- /dev/null +++ b/DSCResources/CommonResourceHelper.psm1 @@ -0,0 +1,265 @@ +<# + .SYNOPSIS + Creates and throws an invalid argument exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ArgumentName + The name of the invalid argument that is causing this error to be thrown. +#> +function New-InvalidArgumentException +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ArgumentName + ) + + $argumentException = New-Object -TypeName 'ArgumentException' ` + -ArgumentList @($Message, $ArgumentName) + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @($argumentException, $ArgumentName, 'InvalidArgument', $null) + } + + $errorRecord = New-Object @newObjectParameters + + throw $errorRecord +} + +<# + .SYNOPSIS + Creates and throws an invalid operation exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error. +#> +function New-InvalidOperationException +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) + { + $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` + -ArgumentList @($Message) + } + else + { + $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $invalidOperationException.ToString(), + 'MachineStateIncorrect', + 'InvalidOperation', + $null + ) + } + + $errorRecordToThrow = New-Object @newObjectParameters + + throw $errorRecordToThrow +} + +<# + .SYNOPSIS + Creates and throws an object not found exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error. +#> +function New-ObjectNotFoundException +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message) + } + else + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $exception.ToString(), + 'MachineStateIncorrect', + 'ObjectNotFound', + $null + ) + } + + $errorRecordToThrow = New-Object @newObjectParameters + + throw $errorRecordToThrow +} + +<# + .SYNOPSIS + Creates and throws an invalid result exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error. +#> +function New-InvalidResultException +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message) + } + else + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $exception.ToString(), + 'MachineStateIncorrect', + 'InvalidResult', + $null + ) + } + + $errorRecordToThrow = New-Object @newObjectParameters + + throw $errorRecordToThrow +} + +<# + .SYNOPSIS + Retrieves the localized string data based on the machine's culture. + Falls back to en-US strings if the machine's culture is not supported. + + .PARAMETER ResourceName + The name of the resource as it appears before '.strings.psd1' of the localized string file. + For example: + For WindowsOptionalFeature: MSFT_WindowsOptionalFeature + For Service: MSFT_ServiceResource + For Registry: MSFT_RegistryResource + For Helper: xSQLServerHelper + + .PARAMETER ScriptRoot + Optional. The root path where to expect to find the culture folder. This is only needed + for localization in helper modules. This should not normally be used for resources. +#> +function Get-LocalizedData +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ResourceName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $ScriptRoot + ) + + if ( -not $ScriptRoot ) + { + $resourceDirectory = Join-Path -Path $PSScriptRoot -ChildPath $ResourceName + $localizedStringFileLocation = Join-Path -Path $resourceDirectory -ChildPath $PSUICulture + } + else + { + $localizedStringFileLocation = Join-Path -Path $ScriptRoot -ChildPath $PSUICulture + } + + if (-not (Test-Path -Path $localizedStringFileLocation)) + { + # Fallback to en-US + if ( -not $ScriptRoot ) + { + $localizedStringFileLocation = Join-Path -Path $resourceDirectory -ChildPath 'en-US' + } + else + { + $localizedStringFileLocation = Join-Path -Path $ScriptRoot -ChildPath 'en-US' + } + } + + Import-LocalizedData ` + -BindingVariable 'localizedData' ` + -FileName "$ResourceName.strings.psd1" ` + -BaseDirectory $localizedStringFileLocation + + return $localizedData +} + +Export-ModuleMember -Function @( + 'New-InvalidArgumentException', + 'New-InvalidOperationException', + 'New-ObjectNotFoundException', + 'New-InvalidResultException', + 'Get-LocalizedData' ) diff --git a/DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 b/DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 index 22b5793..e3db69b 100644 --- a/DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 +++ b/DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 @@ -1,3 +1,8 @@ +Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) ` + -ChildPath 'CommonResourceHelper.psm1') + +$script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xClusterDisk' + <# .SYNOPSIS Returns the current state of the failover cluster disk resource. @@ -78,14 +83,14 @@ function Set-TargetResource { if ($getTargetResourceResult.Ensure -ne $Ensure) { - Write-Verbose "Add the disk $Number to the cluster" + Write-Verbose -Message ($script:localizedData.AddDiskToCluster -f $Number) Get-ClusterAvailableDisk | Where-Object -FilterScript { $_.Number -eq $Number } | Add-ClusterDisk } if ($getTargetResourceResult.Label -ne $Label) { - Write-Verbose "Set the disk $Number label to '$Label'" + Write-Verbose -Message ($script:localizedData.SetDiskLabel -f $Number, $Label) $diskInstance = Get-CimInstance -ClassName MSCluster_Disk -Namespace 'Root\MSCluster' -Filter "Number = $Number" @@ -102,7 +107,7 @@ function Set-TargetResource { if ($getTargetResourceResult.Ensure -eq 'Present' -and $Ensure -eq 'Absent') { - Write-Verbose "Remove the disk $Number from the cluster" + Write-Verbose -Message ($script:localizedData.RemoveDiskFromCluster -f $Number) $diskInstance = Get-CimInstance -ClassName MSCluster_Disk -Namespace 'Root\MSCluster' -Filter "Number = $Number" diff --git a/DSCResources/MSFT_xClusterDisk/en-US/MSFT_xClusterDisk.strings.psd1 b/DSCResources/MSFT_xClusterDisk/en-US/MSFT_xClusterDisk.strings.psd1 new file mode 100644 index 0000000..1bb5917 --- /dev/null +++ b/DSCResources/MSFT_xClusterDisk/en-US/MSFT_xClusterDisk.strings.psd1 @@ -0,0 +1,7 @@ +# Localized resources for xClusterDisk + +ConvertFrom-StringData @' + AddDiskToCluster = Add the disk {0} to the cluster. + SetDiskLabel = Set the disk label for the disk {0} to '{1}'. + RemoveDiskFromCluster = Remove the disk {0} from the cluster. +'@ diff --git a/README.md b/README.md index e6ca700..4697234 100644 --- a/README.md +++ b/README.md @@ -107,9 +107,9 @@ Configures a cluster network in a failover cluster. #### Parameters for xClusterNetwork -* **`[String]` Address** _(Key)_: The adress for the cluster network in the format +* **`[String]` Address** _(Key)_: The address for the cluster network in the format '10.0.0.0'. -* **`[String]` AddressMask** _(Key)_: The adress mask for the cluster network in +* **`[String]` AddressMask** _(Key)_: The address mask for the cluster network in the format '255.255.255.0'. * **`[String]` Name** _(Write)_: The name of the cluster network. If the cluster network name is not in desired state it will be renamed to match this name. @@ -132,7 +132,7 @@ The cluster network role can be set to either the value 0, 1 or 3. 3 = Allow cluster network communication and client connectivity See this article for more information about cluster network role values; -https://technet.microsoft.com/en-us/library/dn550728(v=ws.11).aspx +[Configuring Windows Failover Cluster Networks](https://blogs.technet.microsoft.com/askcore/2014/02/19/configuring-windows-failover-cluster-networks/) #### Examples for xClusterNetwork diff --git a/Tests/Unit/CommonResourceHelper.Tests.ps1 b/Tests/Unit/CommonResourceHelper.Tests.ps1 new file mode 100644 index 0000000..7df82a7 --- /dev/null +++ b/Tests/Unit/CommonResourceHelper.Tests.ps1 @@ -0,0 +1,191 @@ +Describe 'CommonResourceHelper Unit Tests' { + BeforeAll { + # Import the CommonResourceHelper module to test + $dscResourcesFolderFilePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) ` + -ChildPath 'DscResources' + + Import-Module -Name (Join-Path -Path $dscResourcesFolderFilePath ` + -ChildPath 'CommonResourceHelper.psm1') -Force + } + + InModuleScope 'CommonResourceHelper' { + Describe 'Get-LocalizedData' { + $mockTestPath = { + return $mockTestPathReturnValue + } + + $mockImportLocalizedData = { + $BaseDirectory | Should Be $mockExpectedLanguagePath + } + + BeforeEach { + Mock -CommandName Test-Path -MockWith $mockTestPath -Verifiable + Mock -CommandName Import-LocalizedData -MockWith $mockImportLocalizedData -Verifiable + } + + Context 'When loading localized data for Swedish' { + $mockExpectedLanguagePath = 'sv-SE' + $mockTestPathReturnValue = $true + + It 'Should call Import-LocalizedData with sv-SE language' { + Mock -CommandName Join-Path -MockWith { + return 'sv-SE' + } -Verifiable + + { Get-LocalizedData -ResourceName 'DummyResource' } | Should Not Throw + + Assert-MockCalled -CommandName Join-Path -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It + } + + $mockExpectedLanguagePath = 'en-US' + $mockTestPathReturnValue = $false + + It 'Should call Import-LocalizedData and fallback to en-US if sv-SE language does not exist' { + Mock -CommandName Join-Path -MockWith { + return $ChildPath + } -Verifiable + + { Get-LocalizedData -ResourceName 'DummyResource' } | Should Not Throw + + Assert-MockCalled -CommandName Join-Path -Exactly -Times 3 -Scope It + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It + } + + Context 'When $ScriptRoot is set to a path' { + $mockExpectedLanguagePath = 'sv-SE' + $mockTestPathReturnValue = $true + + It 'Should call Import-LocalizedData with sv-SE language' { + Mock -CommandName Join-Path -MockWith { + return 'sv-SE' + } -Verifiable + + { Get-LocalizedData -ResourceName 'DummyResource' -ScriptRoot '.' } | Should Not Throw + + Assert-MockCalled -CommandName Join-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It + } + + $mockExpectedLanguagePath = 'en-US' + $mockTestPathReturnValue = $false + + It 'Should call Import-LocalizedData and fallback to en-US if sv-SE language does not exist' { + Mock -CommandName Join-Path -MockWith { + return $ChildPath + } -Verifiable + + { Get-LocalizedData -ResourceName 'DummyResource' -ScriptRoot '.' } | Should Not Throw + + Assert-MockCalled -CommandName Join-Path -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It + } + } + } + + Context 'When loading localized data for English' { + Mock -CommandName Join-Path -MockWith { + return 'en-US' + } -Verifiable + + $mockExpectedLanguagePath = 'en-US' + $mockTestPathReturnValue = $true + + It 'Should call Import-LocalizedData with en-US language' { + { Get-LocalizedData -ResourceName 'DummyResource' } | Should Not Throw + } + } + + Assert-VerifiableMocks + } + + Describe 'New-InvalidResultException' { + Context 'When calling with Message parameter only' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + + { New-InvalidResultException -Message $mockErrorMessage } | Should Throw $mockErrorMessage + } + } + + Context 'When calling with both the Message and ErrorRecord parameter' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + $mockExceptionErrorMessage = 'Mocked exception error message' + + $mockException = New-Object System.Exception $mockExceptionErrorMessage + $mockErrorRecord = New-Object System.Management.Automation.ErrorRecord $mockException, $null, 'InvalidResult', $null + + { New-InvalidResultException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should Throw ('System.Exception: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage) + } + } + + Assert-VerifiableMocks + } + + Describe 'New-ObjectNotFoundException' { + Context 'When calling with Message parameter only' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + + { New-ObjectNotFoundException -Message $mockErrorMessage } | Should Throw $mockErrorMessage + } + } + + Context 'When calling with both the Message and ErrorRecord parameter' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + $mockExceptionErrorMessage = 'Mocked exception error message' + + $mockException = New-Object System.Exception $mockExceptionErrorMessage + $mockErrorRecord = New-Object System.Management.Automation.ErrorRecord $mockException, $null, 'InvalidResult', $null + + { New-ObjectNotFoundException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should Throw ('System.Exception: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage) + } + } + + Assert-VerifiableMocks + } + + Describe 'New-InvalidOperationException' { + Context 'When calling with Message parameter only' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + + { New-InvalidOperationException -Message $mockErrorMessage } | Should Throw $mockErrorMessage + } + } + + Context 'When calling with both the Message and ErrorRecord parameter' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + $mockExceptionErrorMessage = 'Mocked exception error message' + + $mockException = New-Object System.Exception $mockExceptionErrorMessage + $mockErrorRecord = New-Object System.Management.Automation.ErrorRecord $mockException, $null, 'InvalidResult', $null + + { New-InvalidOperationException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should Throw ('System.InvalidOperationException: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage) + } + } + + Assert-VerifiableMocks + } + + Describe 'New-InvalidArgumentException' { + Context 'When calling with both the Message and ArgumentName parameter' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + $mockArgumentName = 'MockArgument' + + { New-InvalidArgumentException -Message $mockErrorMessage -ArgumentName $mockArgumentName } | Should Throw ('Parameter name: {0}' -f $mockArgumentName) + } + } + + Assert-VerifiableMocks + } + } +} From 1fc44a76c57b5083264e9f33aa887872cf2ec8f5 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 18 Jul 2017 20:50:17 +0200 Subject: [PATCH 02/19] xClusterDisk: Fixed a data type that was not fully qualified (#112) - Changes to xClusterDisk - Fixed the OutputType data type that was not fully qualified. --- CHANGELOG.md | 1 + DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d2cc7d..6a8aab1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Fixed lint error MD034 and fixed typos in README.md. - Changes to xClusterDisk - Enabled localization for all strings ([issue #84](https://github.com/PowerShell/xFailOverCluster/issues/84)). + - Fixed the OutputType data type that was not fully qualified. - Changes to xClusterNetwork - Replaced the URL for the parameter Role in README.md. The new URL is a more generic description of the possible settings for the Role parameter. The diff --git a/DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 b/DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 index e3db69b..df47df1 100644 --- a/DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 +++ b/DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 @@ -13,7 +13,7 @@ $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xClusterDisk' function Get-TargetResource { [CmdletBinding()] - [OutputType([Hashtable])] + [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] From d0727e67f6ee88bccd6c0a0050e063dea4ff5f21 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Fri, 21 Jul 2017 12:29:03 +0200 Subject: [PATCH 03/19] xCluster: Fixed PSSA rule warnings (#110) - Changes to xCluster - Resolved Script Analyzer rule warnings by changing Get-WmiObject to Get-CimInstance (issue #49). --- CHANGELOG.md | 3 +++ DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 | 6 ++--- Tests/Unit/MSFT_xCluster.Tests.ps1 | 24 +++++++++---------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a8aab1..ee14e49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ generic description of the possible settings for the Role parameter. The previous URL was still correct but focused on Hyper-V in particular. - Fixed typos in parameter descriptions in README.md. +- Changes to xCluster + - Resolved Script Analyzer rule warnings by changing Get-WmiObject to + Get-CimInstance ([issue #49](https://github.com/PowerShell/xFailOverCluster/issues/49)). ## 1.7.0.0 diff --git a/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 b/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 index abf5502..82f7984 100644 --- a/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 +++ b/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 @@ -29,7 +29,7 @@ function Get-TargetResource $DomainAdministratorCredential ) - $computerInformation = Get-WmiObject -Class Win32_ComputerSystem + $computerInformation = Get-CimInstance -ClassName Win32_ComputerSystem if (($null -eq $computerInformation) -or ($null -eq $computerInformation.Domain)) { throw 'Can''t find machine''s domain name' @@ -107,7 +107,7 @@ function Set-TargetResource Write-Verbose -Message "Checking if Cluster $Name is present ..." - $computerInformation = Get-WmiObject -Class Win32_ComputerSystem + $computerInformation = Get-CimInstance -ClassName Win32_ComputerSystem if (($null -eq $computerInformation) -or ($null -eq $computerInformation.Domain)) { throw 'Can''t find machine''s domain name' @@ -228,7 +228,7 @@ function Test-TargetResource Write-Verbose -Message "Checking if Cluster $Name is present ..." - $ComputerInfo = Get-WmiObject -Class Win32_ComputerSystem + $ComputerInfo = Get-CimInstance -ClassName Win32_ComputerSystem if (($null -eq $ComputerInfo) -or ($null -eq $ComputerInfo.Domain)) { throw "Can't find machine's domain name" diff --git a/Tests/Unit/MSFT_xCluster.Tests.ps1 b/Tests/Unit/MSFT_xCluster.Tests.ps1 index 8aa5cd7..a6a7d67 100644 --- a/Tests/Unit/MSFT_xCluster.Tests.ps1 +++ b/Tests/Unit/MSFT_xCluster.Tests.ps1 @@ -45,15 +45,15 @@ try $mockClusterName = 'CLUSTER001' $mockStaticIpAddress = '192.168.10.10' - $mockGetWmiObject = { + $mockGetCimInstance = { return [PSCustomObject] @{ Domain = $mockDynamicDomainName Name = $mockDynamicServerName } } - $mockGetWmiObject_ParameterFilter = { - $Class -eq 'Win32_ComputerSystem' + $mockGetCimInstance_ParameterFilter = { + $ClassName -eq 'Win32_ComputerSystem' } $mockGetCluster = { @@ -108,7 +108,7 @@ try $mockDynamicDomainName = $null $mockDynamicServerName = $mockServerName - Mock -CommandName Get-WmiObject -MockWith $mockGetWmiObject -ParameterFilter $mockGetWmiObject_ParameterFilter -Verifiable + Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockGetCimInstance_ParameterFilter -Verifiable { Get-TargetResource @mockDefaultParameters } | Should -Throw "Can't find machine's domain name" } @@ -120,7 +120,7 @@ try $mockDynamicServerName = $mockServerName Mock -CommandName Get-Cluster -Verifiable - Mock -CommandName Get-WmiObject -MockWith $mockGetWmiObject -ParameterFilter $mockGetWmiObject_ParameterFilter -Verifiable + Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockGetCimInstance_ParameterFilter -Verifiable { Get-TargetResource @mockDefaultParameters } | Should -Throw ("Can't find the cluster {0}" -f $mockClusterName) } @@ -128,7 +128,7 @@ try Context 'When the system is not in the desired state' { BeforeEach { - Mock -CommandName Get-WmiObject -MockWith $mockGetWmiObject -ParameterFilter $mockGetWmiObject_ParameterFilter -Verifiable + Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockGetCimInstance_ParameterFilter -Verifiable Mock -CommandName Get-Cluster -MockWith $mockGetCluster -ParameterFilter $mockGetCluster_ParameterFilter -Verifiable Mock -CommandName Get-ClusterGroup -MockWith $mockGetClusterGroup -ParameterFilter $mockGetClusterGroup_ParameterFilter -Verifiable Mock -CommandName Get-ClusterParameter -MockWith $mockGetClusterParameter -Verifiable @@ -158,7 +158,7 @@ try $mockDynamicDomainName = $null $mockDynamicServerName = $mockServerName - Mock -CommandName Get-WmiObject -MockWith $mockGetWmiObject -ParameterFilter $mockGetWmiObject_ParameterFilter -Verifiable + Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockGetCimInstance_ParameterFilter -Verifiable { Set-TargetResource @mockDefaultParameters } | Should -Throw "Can't find machine's domain name" } @@ -169,7 +169,7 @@ try Mock -CommandName New-Cluster -Verifiable Mock -CommandName Remove-ClusterNode -Verifiable Mock -CommandName Add-ClusterNode -Verifiable - Mock -CommandName Get-WmiObject -MockWith $mockGetWmiObject -ParameterFilter $mockGetWmiObject_ParameterFilter -Verifiable + Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockGetCimInstance_ParameterFilter -Verifiable } $mockDynamicDomainName = $mockDomainName @@ -261,7 +261,7 @@ try Mock -CommandName New-Cluster -Verifiable Mock -CommandName Remove-ClusterNode -Verifiable Mock -CommandName Add-ClusterNode -Verifiable - Mock -CommandName Get-WmiObject -MockWith $mockGetWmiObject -ParameterFilter $mockGetWmiObject_ParameterFilter -Verifiable + Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockGetCimInstance_ParameterFilter -Verifiable Mock -CommandName Get-Cluster -MockWith $mockGetCluster -ParameterFilter $mockGetCluster_ParameterFilter -Verifiable Mock -CommandName Get-ClusterParameter -MockWith $mockGetClusterParameter -Verifiable @@ -301,7 +301,7 @@ try $mockDynamicDomainName = $null $mockDynamicServerName = $mockServerName - Mock -CommandName Get-WmiObject -MockWith $mockGetWmiObject -ParameterFilter $mockGetWmiObject_ParameterFilter -Verifiable + Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockGetCimInstance_ParameterFilter -Verifiable { Test-TargetResource @mockDefaultParameters } | Should -Throw "Can't find machine's domain name" } @@ -309,7 +309,7 @@ try Context 'When the system is not in the desired state' { BeforeEach { - Mock -CommandName Get-WmiObject -MockWith $mockGetWmiObject -ParameterFilter $mockGetWmiObject_ParameterFilter -Verifiable + Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockGetCimInstance_ParameterFilter -Verifiable } $mockDynamicDomainName = $mockDomainName @@ -374,7 +374,7 @@ try Context 'When the system is in the desired state' { BeforeEach { Mock -CommandName Get-ClusterNode -MockWith $mockGetClusterNode -Verifiable - Mock -CommandName Get-WmiObject -MockWith $mockGetWmiObject -ParameterFilter $mockGetWmiObject_ParameterFilter -Verifiable + Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockGetCimInstance_ParameterFilter -Verifiable Mock -CommandName Get-Cluster -MockWith $mockGetCluster -ParameterFilter $mockGetCluster_ParameterFilter -Verifiable } From 7730be31e001ab3093b30946bf386795b340008b Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Fri, 21 Jul 2017 13:27:42 +0200 Subject: [PATCH 04/19] Changes to xFailOverCluster (#120) - Changes to xFailOverCluster - Opt-in for module files common tests (issue #119). - Removed Byte Order Mark (BOM) from the files; CommonResourceHelper.psm1 and FailoverClusters.stubs.psm1. --- .MetaTestOptIn.json | 3 +- CHANGELOG.md | 2 + DSCResources/CommonResourceHelper.psm1 | 2 +- Tests/Unit/Stubs/FailoverClusters.stubs.psm1 | 1868 +++++++++++++++++- 4 files changed, 1792 insertions(+), 83 deletions(-) diff --git a/.MetaTestOptIn.json b/.MetaTestOptIn.json index 8a15ee5..0897a45 100644 --- a/.MetaTestOptIn.json +++ b/.MetaTestOptIn.json @@ -1,4 +1,5 @@ [ "Common Tests - Validate Example Files", - "Common Tests - Validate Markdown Files" + "Common Tests - Validate Markdown Files", + "Common Tests - Validate Module Files" ] diff --git a/CHANGELOG.md b/CHANGELOG.md index ee14e49..83c149f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ New-ObjectNotFoundException, New-InvalidOperationException and New-InvalidArgumentException. - Fixed lint error MD034 and fixed typos in README.md. + - Opt-in for module files common tests ([issue #119](https://github.com/PowerShell/xFailOverCluster/issues/119)). + - Removed Byte Order Mark (BOM) from the files; CommonResourceHelper.psm1 and FailoverClusters.stubs.psm1. - Changes to xClusterDisk - Enabled localization for all strings ([issue #84](https://github.com/PowerShell/xFailOverCluster/issues/84)). - Fixed the OutputType data type that was not fully qualified. diff --git a/DSCResources/CommonResourceHelper.psm1 b/DSCResources/CommonResourceHelper.psm1 index 3541fa5..c385d79 100644 --- a/DSCResources/CommonResourceHelper.psm1 +++ b/DSCResources/CommonResourceHelper.psm1 @@ -1,4 +1,4 @@ -<# +<# .SYNOPSIS Creates and throws an invalid argument exception. diff --git a/Tests/Unit/Stubs/FailoverClusters.stubs.psm1 b/Tests/Unit/Stubs/FailoverClusters.stubs.psm1 index 3805433..002c515 100644 --- a/Tests/Unit/Stubs/FailoverClusters.stubs.psm1 +++ b/Tests/Unit/Stubs/FailoverClusters.stubs.psm1 @@ -1,4 +1,4 @@ -<# +<# .SYNOPSIS This is stub cmdlets for the module FailoverClusters which can be used in Pester unit tests to be able to test code without having the actual module installed. @@ -16,560 +16,2266 @@ param() function Add-ClusterCheckpoint { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216179')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${ResourceName}, [Parameter(HelpMessage='Name of crypto checkpoint to add.')] [string] ${CryptoCheckpointName}, [Parameter(HelpMessage='Type of crypto checkpoint to add.')] [string] ${CryptoCheckpointType}, [Parameter(HelpMessage='Key of crypto checkpoint to add.')] [string] ${CryptoCheckpointKey}, [Parameter(HelpMessage='Name of registry checkpoint to add.')] [string] ${RegistryCheckpoint}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216179')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${ResourceName}, + + [Parameter(HelpMessage='Name of crypto checkpoint to add.')] + [string] + ${CryptoCheckpointName}, + + [Parameter(HelpMessage='Type of crypto checkpoint to add.')] + [string] + ${CryptoCheckpointType}, + + [Parameter(HelpMessage='Key of crypto checkpoint to add.')] + [string] + ${CryptoCheckpointKey}, + + [Parameter(HelpMessage='Name of registry checkpoint to add.')] + [string] + ${RegistryCheckpoint}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Add-ClusterDisk { - [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216180')] param( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] [ValidateNotNull()] [psobject[]] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216180')] + param( + [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject[]] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Add-ClusterFileServerRole { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216183')] param( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Storage}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${StaticAddress}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${IgnoreNetwork}, [Parameter(Position=0)] [string] ${Name}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216183')] + param( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Storage}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${StaticAddress}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${IgnoreNetwork}, + + [Parameter(Position=0)] + [string] + ${Name}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Add-ClusterGenericApplicationRole { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216184')] param( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] ${CommandLine}, [string] ${Parameters}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${CheckpointKey}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Storage}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${StaticAddress}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${IgnoreNetwork}, [Parameter(Position=0)] [string] ${Name}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216184')] + param( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + ${CommandLine}, + + [string] + ${Parameters}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${CheckpointKey}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Storage}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${StaticAddress}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${IgnoreNetwork}, + + [Parameter(Position=0)] + [string] + ${Name}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Add-ClusterGenericScriptRole { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216186')] param( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] ${ScriptFilePath}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Storage}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${StaticAddress}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${IgnoreNetwork}, [Parameter(Position=0)] [string] ${Name}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216186')] + param( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + ${ScriptFilePath}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Storage}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${StaticAddress}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${IgnoreNetwork}, + + [Parameter(Position=0)] + [string] + ${Name}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Add-ClusterGenericServiceRole { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216187')] param( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] ${ServiceName}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${CheckpointKey}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Storage}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${StaticAddress}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${IgnoreNetwork}, [Parameter(Position=0)] [string] ${Name}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216187')] + param( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + ${ServiceName}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${CheckpointKey}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Storage}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${StaticAddress}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${IgnoreNetwork}, + + [Parameter(Position=0)] + [string] + ${Name}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Add-ClusterGroup { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216189')] param( [Parameter(Mandatory=$true, Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [Parameter(Position=1)] [Object] ${GroupType}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216189')] + param( + [Parameter(Mandatory=$true, Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [Parameter(Position=1)] + [Object] + ${GroupType}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Add-ClusteriSCSITargetServerRole { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=229636')] param( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Storage}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${StaticAddress}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${IgnoreNetwork}, [Parameter(Position=0)] [string] ${Name}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=229636')] + param( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Storage}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${StaticAddress}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${IgnoreNetwork}, + + [Parameter(Position=0)] + [string] + ${Name}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Add-ClusterNode { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216190')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [switch] ${NoStorage}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216190')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [switch] + ${NoStorage}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Add-ClusterResource { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216192')] param( [Parameter(Mandatory=$true, Position=0)] [ValidateNotNullOrEmpty()] [string] ${Name}, [Parameter(Position=1)] [ValidateNotNullOrEmpty()] [string] ${Group}, [Parameter(Mandatory=$true, Position=2)] [Alias('ResType','Type')] [ValidateNotNullOrEmpty()] [string] ${ResourceType}, [switch] ${SeparateMonitor}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216192')] + param( + [Parameter(Mandatory=$true, Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [Parameter(Position=1)] + [ValidateNotNullOrEmpty()] + [string] + ${Group}, + + [Parameter(Mandatory=$true, Position=2)] + [Alias('ResType','Type')] + [ValidateNotNullOrEmpty()] + [string] + ${ResourceType}, + + [switch] + ${SeparateMonitor}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Add-ClusterResourceDependency { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216193')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Resource}, [Parameter(Position=1)] [ValidateNotNullOrEmpty()] [string] ${Provider}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216193')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Resource}, + + [Parameter(Position=1)] + [ValidateNotNullOrEmpty()] + [string] + ${Provider}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Add-ClusterResourceType { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216194')] param( [Parameter(Mandatory=$true, Position=0)] [ValidateNotNullOrEmpty()] [string] ${Name}, [Parameter(Mandatory=$true, Position=1)] [ValidateNotNullOrEmpty()] [string] ${Dll}, [Parameter(Position=2)] [ValidateNotNullOrEmpty()] [string] ${DisplayName}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216194')] + param( + [Parameter(Mandatory=$true, Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [Parameter(Mandatory=$true, Position=1)] + [ValidateNotNullOrEmpty()] + [string] + ${Dll}, + + [Parameter(Position=2)] + [ValidateNotNullOrEmpty()] + [string] + ${DisplayName}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Add-ClusterScaleOutFileServerRole { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216200')] param( [Parameter(Position=0)] [string] ${Name}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216200')] + param( + [Parameter(Position=0)] + [string] + ${Name}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Add-ClusterServerRole { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216195')] param( [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Storage}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${StaticAddress}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${IgnoreNetwork}, [Parameter(Position=0)] [string] ${Name}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216195')] + param( + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Storage}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${StaticAddress}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${IgnoreNetwork}, + + [Parameter(Position=0)] + [string] + ${Name}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Add-ClusterSharedVolume { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216196')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216196')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Add-ClusterVirtualMachineRole { - [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216198')] param( [ValidateNotNullOrEmpty()] [string] ${Name}, [Parameter(Position=0, ValueFromPipelineByPropertyName=$true)] [string] ${VMName}, [Alias('VM')] [string] ${VirtualMachine}, [Parameter(ValueFromPipelineByPropertyName=$true)] [guid] ${VMId}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216198')] + param( + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [Parameter(Position=0, ValueFromPipelineByPropertyName=$true)] + [string] + ${VMName}, + + [Alias('VM')] + [string] + ${VirtualMachine}, + + [Parameter(ValueFromPipelineByPropertyName=$true)] + [guid] + ${VMId}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Add-ClusterVMMonitoredItem { - [CmdletBinding(DefaultParameterSetName='VirtualMachine', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216199')] param( [System.Collections.Specialized.StringCollection] ${Service}, [string] ${EventLog}, [string] ${EventSource}, [int] ${EventId}, [switch] ${OverrideServiceRecoveryActions}, [Parameter(ParameterSetName='VirtualMachine', Position=0)] [Alias('VM')] [ValidateNotNullOrEmpty()] [string] ${VirtualMachine}, [Parameter(ParameterSetName='VMId', ValueFromPipelineByPropertyName=$true)] [guid] ${VMId}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='VirtualMachine', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216199')] + param( + [System.Collections.Specialized.StringCollection] + ${Service}, + + [string] + ${EventLog}, + + [string] + ${EventSource}, + + [int] + ${EventId}, + + [switch] + ${OverrideServiceRecoveryActions}, + + [Parameter(ParameterSetName='VirtualMachine', Position=0)] + [Alias('VM')] + [ValidateNotNullOrEmpty()] + [string] + ${VirtualMachine}, + + [Parameter(ParameterSetName='VMId', ValueFromPipelineByPropertyName=$true)] + [guid] + ${VMId}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Block-ClusterAccess { - [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216202')] param( [Parameter(Mandatory=$true, Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${User}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216202')] + param( + [Parameter(Mandatory=$true, Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${User}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Clear-ClusterDiskReservation { - [CmdletBinding(ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216203')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Node}, [Parameter(Mandatory=$true)] [uint32[]] ${Disk}, [switch] ${Force} + [CmdletBinding(ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216203')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Node}, + + [Parameter(Mandatory=$true)] + [uint32[]] + ${Disk}, + + [switch] + ${Force} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Clear-ClusterNode { - [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216205')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [switch] ${Force}, [int] ${Wait}, [switch] ${CleanupDisks}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216205')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [switch] + ${Force}, + + [int] + ${Wait}, + + [switch] + ${CleanupDisks}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-Cluster { - [CmdletBinding(DefaultParameterSetName='Name', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216206')] param( [Parameter(ParameterSetName='Name', Position=0)] [ValidateNotNullOrEmpty()] [string] ${Name}, [string] ${Domain} + [CmdletBinding(DefaultParameterSetName='Name', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216206')] + param( + [Parameter(ParameterSetName='Name', Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [string] + ${Domain} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterAccess { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216207')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${User}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216207')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${User}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterAvailableDisk { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216208')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Cluster}, [Parameter(ValueFromPipeline=$true)] [ValidateNotNull()] [ciminstance] ${Disk}, [switch] ${All}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216208')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Cluster}, + + [Parameter(ValueFromPipeline=$true)] + [ValidateNotNull()] + [ciminstance] + ${Disk}, + + [switch] + ${All}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterCheckpoint { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216209')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${ResourceName}, [Parameter(HelpMessage='Searches for checkpoints with a specific name, wildcard expressions are accepted.')] [string] ${CheckpointName}, [Parameter(HelpMessage='If specified, command will output registry checkpoints.')] [switch] ${RegistryCheckpoint}, [Parameter(HelpMessage='If specified, command will output crypto checkpoints.')] [switch] ${CryptoCheckpoint}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216209')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${ResourceName}, + + [Parameter(HelpMessage='Searches for checkpoints with a specific name, wildcard expressions are accepted.')] + [string] + ${CheckpointName}, + + [Parameter(HelpMessage='If specified, command will output registry checkpoints.')] + [switch] + ${RegistryCheckpoint}, + + [Parameter(HelpMessage='If specified, command will output crypto checkpoints.')] + [switch] + ${CryptoCheckpoint}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterGroup { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216210')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [Parameter(ValueFromPipelineByPropertyName=$true)] [guid] ${VMId}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216210')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [Parameter(ValueFromPipelineByPropertyName=$true)] + [guid] + ${VMId}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterLog { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216212')] param( [Parameter(Position=0)] [ValidateNotNull()] [System.Collections.Specialized.StringCollection] ${Node}, [ValidateNotNullOrEmpty()] [string] ${Destination}, [Alias('Span')] [uint32] ${TimeSpan}, [Parameter(HelpMessage='Generate the cluster log using local time instead of GMT.')] [Alias('lt')] [switch] ${UseLocalTime}, [Parameter(HelpMessage='Generate the cluster log without retrieving cluster state information.')] [Alias('scs')] [switch] ${SkipClusterState}, [Parameter(HelpMessage='Generate the cluster health logs.')] [switch] ${Health}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216212')] + param( + [Parameter(Position=0)] + [ValidateNotNull()] + [System.Collections.Specialized.StringCollection] + ${Node}, + + [ValidateNotNullOrEmpty()] + [string] + ${Destination}, + + [Alias('Span')] + [uint32] + ${TimeSpan}, + + [Parameter(HelpMessage='Generate the cluster log using local time instead of GMT.')] + [Alias('lt')] + [switch] + ${UseLocalTime}, + + [Parameter(HelpMessage='Generate the cluster log without retrieving cluster state information.')] + [Alias('scs')] + [switch] + ${SkipClusterState}, + + [Parameter(HelpMessage='Generate the cluster health logs.')] + [switch] + ${Health}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterNetwork { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216213')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216213')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterNetworkInterface { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216214')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Node}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Network}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216214')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Node}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Network}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterNode { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216215')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216215')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterOwnerNode { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216216')] param( [Alias('Res')] [ValidateNotNullOrEmpty()] [string] ${Resource}, [ValidateNotNullOrEmpty()] [string] ${Group}, [Alias('ResType')] [ValidateNotNullOrEmpty()] [string] ${ResourceType}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216216')] + param( + [Alias('Res')] + [ValidateNotNullOrEmpty()] + [string] + ${Resource}, + + [ValidateNotNullOrEmpty()] + [string] + ${Group}, + + [Alias('ResType')] + [ValidateNotNullOrEmpty()] + [string] + ${ResourceType}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterParameter { - [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216217')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216217')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterQuorum { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216218')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Cluster}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216218')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Cluster}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterResource { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216219')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [Parameter(ValueFromPipelineByPropertyName=$true)] [guid] ${VMId}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216219')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [Parameter(ValueFromPipelineByPropertyName=$true)] + [guid] + ${VMId}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterResourceDependency { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216220')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Resource}, [switch] ${Guid}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216220')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Resource}, + + [switch] + ${Guid}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterResourceDependencyReport { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216221')] param( [ValidateNotNullOrEmpty()] [string] ${Resource}, [ValidateNotNullOrEmpty()] [string] ${Group}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216221')] + param( + [ValidateNotNullOrEmpty()] + [string] + ${Resource}, + + [ValidateNotNullOrEmpty()] + [string] + ${Group}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterResourceType { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216222')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216222')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterSharedVolume { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216223')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216223')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterSharedVolumeState { - [CmdletBinding()] param( [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Node}, [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding()] + param( + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Node}, + + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Get-ClusterVMMonitoredItem { - [CmdletBinding(DefaultParameterSetName='VirtualMachine', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216224')] param( [Parameter(ParameterSetName='VirtualMachine', Position=0)] [Alias('VM')] [ValidateNotNullOrEmpty()] [string] ${VirtualMachine}, [Parameter(ParameterSetName='VMId', ValueFromPipelineByPropertyName=$true)] [guid] ${VMId}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='VirtualMachine', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216224')] + param( + [Parameter(ParameterSetName='VirtualMachine', Position=0)] + [Alias('VM')] + [ValidateNotNullOrEmpty()] + [string] + ${VirtualMachine}, + + [Parameter(ParameterSetName='VMId', ValueFromPipelineByPropertyName=$true)] + [guid] + ${VMId}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Grant-ClusterAccess { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216225')] param( [Parameter(Mandatory=$true, Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${User}, [switch] ${Full}, [switch] ${ReadOnly}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216225')] + param( + [Parameter(Mandatory=$true, Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${User}, + + [switch] + ${Full}, + + [switch] + ${ReadOnly}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Move-ClusterGroup { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216226')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Name}, [Parameter(Position=1)] [ValidateNotNullOrEmpty()] [string] ${Node}, [switch] ${IgnoreLocked}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216226')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [Parameter(Position=1)] + [ValidateNotNullOrEmpty()] + [string] + ${Node}, + + [switch] + ${IgnoreLocked}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Move-ClusterResource { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216227')] param( [Parameter(Position=0)] [ValidateNotNull()] [string] ${Name}, [Parameter(Position=1)] [ValidateNotNull()] [string] ${Group}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216227')] + param( + [Parameter(Position=0)] + [ValidateNotNull()] + [string] + ${Name}, + + [Parameter(Position=1)] + [ValidateNotNull()] + [string] + ${Group}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Move-ClusterSharedVolume { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216228')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Name}, [Parameter(Position=1)] [ValidateNotNullOrEmpty()] [string] ${Node}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216228')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [Parameter(Position=1)] + [ValidateNotNullOrEmpty()] + [string] + ${Node}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Move-ClusterVirtualMachineRole { - [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216229')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Name}, [Parameter(Position=1)] [ValidateNotNullOrEmpty()] [string] ${Node}, [switch] ${Cancel}, [Object] ${MigrationType}, [switch] ${IgnoreLocked}, [Parameter(ValueFromPipelineByPropertyName=$true)] [guid] ${VMId}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216229')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [Parameter(Position=1)] + [ValidateNotNullOrEmpty()] + [string] + ${Node}, + + [switch] + ${Cancel}, + + [Object] + ${MigrationType}, + + [switch] + ${IgnoreLocked}, + + [Parameter(ValueFromPipelineByPropertyName=$true)] + [guid] + ${VMId}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function New-Cluster { - [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216230')] param( [Parameter(Mandatory=$true, Position=0)] [ValidateNotNullOrEmpty()] [string] ${Name}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Node}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${StaticAddress}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${IgnoreNetwork}, [switch] ${NoStorage}, [switch] ${S2D}, [Alias('aap')] [Object] ${AdministrativeAccessPoint}, [switch] ${Force} + [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216230')] + param( + [Parameter(Mandatory=$true, Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Node}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${StaticAddress}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${IgnoreNetwork}, + + [switch] + ${NoStorage}, + + [switch] + ${S2D}, + + [Alias('aap')] + [Object] + ${AdministrativeAccessPoint}, + + [switch] + ${Force} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function New-ClusterNameAccount { - [CmdletBinding(DefaultParameterSetName='InputObject')] param( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] ${Name}, [Parameter(ParameterSetName='Credentials', Mandatory=$true)] [Parameter(ParameterSetName='InputObject')] [pscredential] ${Credentials}, [Parameter(ParameterSetName='Credentials', Mandatory=$true)] [Parameter(ParameterSetName='InputObject')] [string] ${Domain}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject')] + param( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [Parameter(ParameterSetName='Credentials', Mandatory=$true)] + [Parameter(ParameterSetName='InputObject')] + [pscredential] + ${Credentials}, + + [Parameter(ParameterSetName='Credentials', Mandatory=$true)] + [Parameter(ParameterSetName='InputObject')] + [string] + ${Domain}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Remove-Cluster { - [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216231')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Cluster}, [switch] ${CleanupAD}, [switch] ${Force}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject} + [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216231')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Cluster}, + + [switch] + ${CleanupAD}, + + [switch] + ${Force}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Remove-ClusterAccess { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216232')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${User}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216232')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${User}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Remove-ClusterCheckpoint { - [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216233')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${ResourceName}, [switch] ${Force}, [Parameter(HelpMessage='Searches for checkpoints with a specific name, regular expressions are accepted.')] [string] ${CheckpointName}, [Parameter(HelpMessage='If specified, command will remove registry checkpoints.')] [switch] ${RegistryCheckpoint}, [Parameter(HelpMessage='If specified, command will remove crypto checkpoints.')] [switch] ${CryptoCheckpoint}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216233')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${ResourceName}, + + [switch] + ${Force}, + + [Parameter(HelpMessage='Searches for checkpoints with a specific name, regular expressions are accepted.')] + [string] + ${CheckpointName}, + + [Parameter(HelpMessage='If specified, command will remove registry checkpoints.')] + [switch] + ${RegistryCheckpoint}, + + [Parameter(HelpMessage='If specified, command will remove crypto checkpoints.')] + [switch] + ${CryptoCheckpoint}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Remove-ClusterGroup { - [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216234')] param( [Parameter(ValueFromPipelineByPropertyName=$true)] [guid] ${VMId}, [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [switch] ${Force}, [switch] ${RemoveResources}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216234')] + param( + [Parameter(ValueFromPipelineByPropertyName=$true)] + [guid] + ${VMId}, + + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [switch] + ${Force}, + + [switch] + ${RemoveResources}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Remove-ClusterNode { - [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216235')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [switch] ${Force}, [int] ${Wait}, [switch] ${IgnoreStorageConnectivityLoss}, [switch] ${CleanupDisks}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216235')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [switch] + ${Force}, + + [int] + ${Wait}, + + [switch] + ${IgnoreStorageConnectivityLoss}, + + [switch] + ${CleanupDisks}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Remove-ClusterResource { - [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216236')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [switch] ${Force}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216236')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [switch] + ${Force}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Remove-ClusterResourceDependency { - [CmdletBinding(ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216237')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Resource}, [Parameter(Position=1)] [ValidateNotNullOrEmpty()] [string] ${Provider}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216237')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Resource}, + + [Parameter(Position=1)] + [ValidateNotNullOrEmpty()] + [string] + ${Provider}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Remove-ClusterResourceType { - [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216238')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216238')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Remove-ClusterSharedVolume { - [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216239')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216239')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Remove-ClusterVMMonitoredItem { - [CmdletBinding(DefaultParameterSetName='VirtualMachine', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216240')] param( [Parameter(ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [System.Collections.Specialized.StringCollection] ${Service}, [string] ${EventLog}, [string] ${EventSource}, [int] ${EventId}, [Parameter(ParameterSetName='VirtualMachine', Position=0)] [Alias('VM')] [ValidateNotNullOrEmpty()] [string] ${VirtualMachine}, [Parameter(ParameterSetName='VMId', ValueFromPipelineByPropertyName=$true)] [guid] ${VMId}, [int] ${Wait}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='VirtualMachine', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216240')] + param( + [Parameter(ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [System.Collections.Specialized.StringCollection] + ${Service}, + + [string] + ${EventLog}, + + [string] + ${EventSource}, + + [int] + ${EventId}, + + [Parameter(ParameterSetName='VirtualMachine', Position=0)] + [Alias('VM')] + [ValidateNotNullOrEmpty()] + [string] + ${VirtualMachine}, + + [Parameter(ParameterSetName='VMId', ValueFromPipelineByPropertyName=$true)] + [guid] + ${VMId}, + + [int] + ${Wait}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Reset-ClusterVMMonitoredState { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216243')] param( [int] ${Wait} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216243')] + param( + [int] + ${Wait} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Resume-ClusterNode { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216244')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [Parameter(Position=1)] [ValidateNotNullOrEmpty()] [Object] ${Failback}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216244')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [Parameter(Position=1)] + [ValidateNotNullOrEmpty()] + [Object] + ${Failback}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Resume-ClusterResource { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216245')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Name}, [string] ${VolumeName}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216245')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [string] + ${VolumeName}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Set-ClusterLog { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216246')] param( [int] ${Size}, [int] ${Level}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216246')] + param( + [int] + ${Size}, + + [int] + ${Level}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Set-ClusterOwnerNode { - [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216247')] param( [ValidateNotNullOrEmpty()] [string] ${Resource}, [ValidateNotNullOrEmpty()] [string] ${Group}, [Parameter(Mandatory=$true, ValueFromRemainingArguments=$true)] [ValidateNotNull()] [System.Collections.Specialized.StringCollection] ${Owners}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216247')] + param( + [ValidateNotNullOrEmpty()] + [string] + ${Resource}, + + [ValidateNotNullOrEmpty()] + [string] + ${Group}, + + [Parameter(Mandatory=$true, ValueFromRemainingArguments=$true)] + [ValidateNotNull()] + [System.Collections.Specialized.StringCollection] + ${Owners}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Set-ClusterParameter { - [CmdletBinding(DefaultParameterSetName='NoMultiple', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216248')] param( [Parameter(ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [Parameter(ParameterSetName='Single Parameter', Position=0)] [ValidateNotNullOrEmpty()] [string] ${Name}, [Parameter(ParameterSetName='Multiple Parameter', Position=0)] [ValidateNotNull()] [hashtable] ${Multiple}, [Parameter(ParameterSetName='Single Parameter', Position=1)] [psobject] ${Value}, [switch] ${Create}, [switch] ${Delete}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='NoMultiple', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216248')] + param( + [Parameter(ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [Parameter(ParameterSetName='Single Parameter', Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [Parameter(ParameterSetName='Multiple Parameter', Position=0)] + [ValidateNotNull()] + [hashtable] + ${Multiple}, + + [Parameter(ParameterSetName='Single Parameter', Position=1)] + [psobject] + ${Value}, + + [switch] + ${Create}, + + [switch] + ${Delete}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Set-ClusterQuorum { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216249')] param( [ValidateNotNullOrEmpty()] [string] ${DiskOnly}, [Alias('NodeMajority')] [switch] ${NoWitness}, [Alias('NodeAndDiskMajority')] [ValidateNotNullOrEmpty()] [string] ${DiskWitness}, [Alias('NodeAndFileShareMajority')] [ValidateNotNullOrEmpty()] [string] ${FileShareWitness}, [switch] ${CloudWitness}, [ValidateNotNullOrEmpty()] [string] ${AccountName}, [string] ${Endpoint}, [ValidateNotNullOrEmpty()] [string] ${AccessKey}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216249')] + param( + [ValidateNotNullOrEmpty()] + [string] + ${DiskOnly}, + + [Alias('NodeMajority')] + [switch] + ${NoWitness}, + + [Alias('NodeAndDiskMajority')] + [ValidateNotNullOrEmpty()] + [string] + ${DiskWitness}, + + [Alias('NodeAndFileShareMajority')] + [ValidateNotNullOrEmpty()] + [string] + ${FileShareWitness}, + + [switch] + ${CloudWitness}, + + [ValidateNotNullOrEmpty()] + [string] + ${AccountName}, + + [string] + ${Endpoint}, + + [ValidateNotNullOrEmpty()] + [string] + ${AccessKey}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Set-ClusterResourceDependency { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216250')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Resource}, [Parameter(Position=1)] [string] ${Dependency}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216250')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Resource}, + + [Parameter(Position=1)] + [string] + ${Dependency}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Start-Cluster { - [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216251')] param( [Parameter(Position=0)] [Alias('Cluster')] [ValidateNotNullOrEmpty()] [string] ${Name}, [Alias('ips')] [switch] ${IgnorePersistentState}, [int] ${Wait} + [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216251')] + param( + [Parameter(Position=0)] + [Alias('Cluster')] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [Alias('ips')] + [switch] + ${IgnorePersistentState}, + + [int] + ${Wait} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Start-ClusterGroup { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216252')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Name}, [switch] ${IgnoreLocked}, [switch] ${ChooseBestNode}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216252')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [switch] + ${IgnoreLocked}, + + [switch] + ${ChooseBestNode}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Start-ClusterNode { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216253')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [Parameter(HelpMessage='Specifies if the cluster is in a force quorum state.')] [Alias('fq','FixQuorum')] [switch] ${ForceQuorum}, [Parameter(HelpMessage='Specifies whether to clear quarantine state when starting the cluster node')] [Alias('cq')] [switch] ${ClearQuarantine}, [Parameter(HelpMessage='Specifies whether the cluster will bring online groups that were online when the cluster was shut down.')] [Alias('ips')] [switch] ${IgnorePersistentState}, [Alias('pq')] [switch] ${PreventQuorum}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216253')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [Parameter(HelpMessage='Specifies if the cluster is in a force quorum state.')] + [Alias('fq','FixQuorum')] + [switch] + ${ForceQuorum}, + + [Parameter(HelpMessage='Specifies whether to clear quarantine state when starting the cluster node')] + [Alias('cq')] + [switch] + ${ClearQuarantine}, + + [Parameter(HelpMessage='Specifies whether the cluster will bring online groups that were online when the cluster was shut down.')] + [Alias('ips')] + [switch] + ${IgnorePersistentState}, + + [Alias('pq')] + [switch] + ${PreventQuorum}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Start-ClusterResource { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216254')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Name}, [switch] ${IgnoreLocked}, [switch] ${ChooseBestNode}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216254')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [switch] + ${IgnoreLocked}, + + [switch] + ${ChooseBestNode}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Stop-Cluster { - [CmdletBinding(DefaultParameterSetName='Cluster name', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216255')] param( [Parameter(ParameterSetName='Cluster name', Position=0)] [Alias('Name')] [ValidateNotNullOrEmpty()] [string] ${Cluster}, [switch] ${Force}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject} + [CmdletBinding(DefaultParameterSetName='Cluster name', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216255')] + param( + [Parameter(ParameterSetName='Cluster name', Position=0)] + [Alias('Name')] + [ValidateNotNullOrEmpty()] + [string] + ${Cluster}, + + [switch] + ${Force}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Stop-ClusterGroup { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216256')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Name}, [switch] ${IgnoreLocked}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216256')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [switch] + ${IgnoreLocked}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Stop-ClusterNode { - [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216257')] param( [Parameter(Position=0)] [System.Collections.Specialized.StringCollection] ${Name}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216257')] + param( + [Parameter(Position=0)] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Stop-ClusterResource { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216258')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Name}, [switch] ${IgnoreLocked}, [int] ${Wait}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216258')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [switch] + ${IgnoreLocked}, + + [int] + ${Wait}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Suspend-ClusterNode { - [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216259')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [switch] ${Drain}, [switch] ${ForceDrain}, [switch] ${Wait}, [Parameter(Position=1)] [ValidateNotNullOrEmpty()] [string] ${TargetNode}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216259')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [switch] + ${Drain}, + + [switch] + ${ForceDrain}, + + [switch] + ${Wait}, + + [Parameter(Position=1)] + [ValidateNotNullOrEmpty()] + [string] + ${TargetNode}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Suspend-ClusterResource { - [CmdletBinding(ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216260')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Name}, [string] ${VolumeName}, [Alias('FileSystemRedirectedAccess')] [switch] ${RedirectedAccess}, [switch] ${Force}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216260')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [string] + ${VolumeName}, + + [Alias('FileSystemRedirectedAccess')] + [switch] + ${RedirectedAccess}, + + [switch] + ${Force}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Test-Cluster { - [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216261')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Node}, [ValidateNotNullOrEmpty()] [System.Object[]] ${Disk}, [ValidateNotNullOrEmpty()] [System.Object[]] ${Pool}, [ValidateNotNullOrEmpty()] [string] ${ReportName}, [switch] ${List}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Include}, [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Ignore}, [switch] ${Force}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216261')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Node}, + + [ValidateNotNullOrEmpty()] + [System.Object[]] + ${Disk}, + + [ValidateNotNullOrEmpty()] + [System.Object[]] + ${Pool}, + + [ValidateNotNullOrEmpty()] + [string] + ${ReportName}, + + [switch] + ${List}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Include}, + + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Ignore}, + + [switch] + ${Force}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Test-ClusterResourceFailure { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216262')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Name}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216262')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Update-ClusterFunctionalLevel { - [CmdletBinding(DefaultParameterSetName='InputObject')] param( [switch] ${Force}, [switch] ${WhatIf}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject')] + param( + [switch] + ${Force}, + + [switch] + ${WhatIf}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Update-ClusterIPResource { - [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216264')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Name}, [switch] ${Renew}, [switch] ${Release}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216264')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [switch] + ${Renew}, + + [switch] + ${Release}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Update-ClusterNetworkNameResource { - [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216265')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.StringCollection] ${Name}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(DefaultParameterSetName='InputObject', HelpUri='http://go.microsoft.com/fwlink/?LinkId=216265')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [System.Collections.Specialized.StringCollection] + ${Name}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand } function Update-ClusterVirtualMachineConfiguration { - [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216266')] param( [Parameter(Position=0)] [ValidateNotNullOrEmpty()] [string] ${Name}, [Parameter(ValueFromPipelineByPropertyName=$true)] [guid] ${VMId}, [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] [ValidateNotNull()] [psobject] ${InputObject}, [ValidateNotNullOrEmpty()] [string] ${Cluster} + [CmdletBinding(HelpUri='http://go.microsoft.com/fwlink/?LinkId=216266')] + param( + [Parameter(Position=0)] + [ValidateNotNullOrEmpty()] + [string] + ${Name}, + + [Parameter(ValueFromPipelineByPropertyName=$true)] + [guid] + ${VMId}, + + [Parameter(ParameterSetName='InputObject', ValueFromPipeline=$true)] + [ValidateNotNull()] + [psobject] + ${InputObject}, + + [ValidateNotNullOrEmpty()] + [string] + ${Cluster} ) throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand From 7464d529ccba517e5a1e90942f638bce586ab060 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 23 Jul 2017 12:44:21 +0200 Subject: [PATCH 05/19] xWaitForCluster: Added unit test (#108) - Changes to xWaitForCluster - Refactored the unit test for this resource to use stubs and increase coverage (issue #78). - Now the Test-TargetResource correctly returns false if the domain name cannot be evaluated (issue #107). - Changed the code to be more aligned with the style guideline. - Updated parameter description in the schema.mof. - Resolved Script Analyzer warnings (issue #54). - Changes to xCluster - Minor style change in tests. Removed '-' infront of '-Be', '-Not', '-Throw', etc. --- CHANGELOG.md | 9 + .../MSFT_xWaitForCluster.psm1 | 163 ++++++++----- .../MSFT_xWaitForCluster.schema.mof | 13 +- Tests/Unit/MSFT_xCluster.Tests.ps1 | 30 +-- Tests/Unit/MSFT_xWaitForCluster.Tests.ps1 | 219 ++++++++++++++++++ 5 files changed, 354 insertions(+), 80 deletions(-) create mode 100644 Tests/Unit/MSFT_xWaitForCluster.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 83c149f..b98c14e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,15 @@ - Changes to xCluster - Resolved Script Analyzer rule warnings by changing Get-WmiObject to Get-CimInstance ([issue #49](https://github.com/PowerShell/xFailOverCluster/issues/49)). + Minor style change in tests. Removed '-' infront of '-Be', '-Not', '-Throw', etc. +- Changes to xWaitForCluster + - Refactored the unit test for this resource to use stubs and increase coverage + ([issue #78](https://github.com/PowerShell/xFailOverCluster/issues/78)). + - Now the Test-TargetResource correctly returns false if the domain name cannot + be evaluated ([issue #107](https://github.com/PowerShell/xFailOverCluster/issues/107)). + - Changed the code to be more aligned with the style guideline. + - Updated parameter description in the schema.mof. + - Resolved Script Analyzer warnings ([issue #54](https://github.com/PowerShell/xFailOverCluster/issues/54)). ## 1.7.0.0 diff --git a/DSCResources/MSFT_xWaitForCluster/MSFT_xWaitForCluster.psm1 b/DSCResources/MSFT_xWaitForCluster/MSFT_xWaitForCluster.psm1 index e222ea2..34d3b0a 100644 --- a/DSCResources/MSFT_xWaitForCluster/MSFT_xWaitForCluster.psm1 +++ b/DSCResources/MSFT_xWaitForCluster/MSFT_xWaitForCluster.psm1 @@ -1,21 +1,34 @@ -# -# xWaitForCluster: DSC Resource that will wait for given name of Cluster, it checks the state of the cluster for given # interval until the cluster is found or the number of retries is reached. -# -# +<# + .SYNOPSIS + Get the values for which failover cluster and for how long to wait for the + cluster to exist. + .PARAMETER Name + Name of the cluster to wait for. -# -# The Get-TargetResource cmdlet. -# + .PARAMETER RetryIntervalSec + Interval to check for cluster existence. Default values is 10 seconds. + + .PARAMETER RetryCount + Maximum number of retries to check for cluster existence. Default value + is 50 retries. +#> function Get-TargetResource { - [OutputType([Hashtable])] + [OutputType([System.Collections.Hashtable])] param - ( - [parameter(Mandatory)][string] $Name, - - [UInt64] $RetryIntervalSec = 10, - [UInt32] $RetryCount = 50 + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter()] + [System.UInt64] + $RetryIntervalSec = 10, + + [Parameter()] + [System.UInt32] + $RetryCount = 50 ) @{ @@ -25,100 +38,138 @@ function Get-TargetResource } } -# -# The Set-TargetResource cmdlet. -# +<# + .SYNOPSIS + Waits for the specific failover cluster to exist. It will throw an error if the + cluster has not been detected during the timeout period. + + .PARAMETER Name + Name of the cluster to wait for. + + .PARAMETER RetryIntervalSec + Interval to check for cluster existence. Default values is 10 seconds. + + .PARAMETER RetryCount + Maximum number of retries to check for cluster existence. Default value + is 50 retries. +#> function Set-TargetResource { param - ( - [parameter(Mandatory)][string] $Name, - - [UInt64] $RetryIntervalSec = 10, - [UInt32] $RetryCount = 50 + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter()] + [System.UInt64] + $RetryIntervalSec = 10, + + [Parameter()] + [System.UInt32] + $RetryCount = 50 ) $clusterFound = $false - Write-Verbose -Message "Checking for cluster $Name ..." + Write-Verbose -Message "Checking for the existance of failover cluster $Name." for ($count = 0; $count -lt $RetryCount; $count++) { try { - $ComputerInfo = Get-WmiObject Win32_ComputerSystem - if (($ComputerInfo -eq $null) -or ($ComputerInfo.Domain -eq $null)) + $computerObject = Get-CimInstance -ClassName Win32_ComputerSystem + if ($null -eq $computerObject -or $null -eq $computerObject.Domain) { Write-Verbose -Message "Can't find machine's domain name" - break; + break } - $cluster = Get-Cluster -Name $Name -Domain $ComputerInfo.Domain + $cluster = Get-Cluster -Name $Name -Domain $computerObject.Domain - if ($cluster -ne $null) + if ($null -ne $cluster) { - Write-Verbose -Message "Found cluster $Name" + Write-Verbose -Message "Found failover cluster $Name" $clusterFound = $true - - break; + break } - } catch { - Write-Verbose -Message "Cluster $Name not found. Will retry again after $RetryIntervalSec sec" + Write-Verbose -Message "Failover cluster $Name not found. Will retry again after $RetryIntervalSec sec" } - - Write-Verbose -Message "Cluster $Name not found. Will retry again after $RetryIntervalSec sec" + + Write-Verbose -Message "Failover cluster $Name not found. Will retry again after $RetryIntervalSec sec" Start-Sleep -Seconds $RetryIntervalSec } if (! $clusterFound) { - throw "Cluster $Name not found after $count attempts with $RetryIntervalSec sec interval" + throw "Failover cluster $Name not found after $count attempts with $RetryIntervalSec sec interval" } } -# -# The Test-TargetResource cmdlet. -# +<# + .SYNOPSIS + Test if the specific failover cluster exist. + + .PARAMETER Name + Name of the cluster to wait for. + + .PARAMETER RetryIntervalSec + Interval to check for cluster existence. Default values is 10 seconds. + + .PARAMETER RetryCount + Maximum number of retries to check for cluster existence. Default value + is 50 retries. +#> function Test-TargetResource { [OutputType([Boolean])] param - ( - [parameter(Mandatory)][string] $Name, - - [UInt64] $RetryIntervalSec = 10, - [UInt32] $RetryCount = 50 + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter()] + [System.UInt64] + $RetryIntervalSec = 10, + + [Parameter()] + [System.UInt32] + $RetryCount = 50 ) Write-Verbose -Message "Checking for Cluster $Name ..." + $testTargetResourceReturnValue = $false + try { - $ComputerInfo = Get-WmiObject Win32_ComputerSystem - if (($ComputerInfo -eq $null) -or ($ComputerInfo.Domain -eq $null)) + $computerObject = Get-CimInstance -ClassName Win32_ComputerSystem + if ($null -eq $computerObject -or $null -eq $computerObject.Domain) { Write-Verbose -Message "Can't find machine's domain name" - $false - } - - $cluster = Get-Cluster -Name $Name -Domain $ComputerInfo.Domain - if ($cluster -eq $null) - { - Write-Verbose -Message "Cluster $Name not found in domain $ComputerInfo.Domain" - $false } else { - Write-Verbose -Message "Found cluster $Name" - $true + $cluster = Get-Cluster -Name $Name -Domain $computerObject.Domain + if ($null -eq $cluster) + { + Write-Verbose -Message "Cluster $Name not found in domain $computerObject.Domain" + } + else + { + Write-Verbose -Message "Found cluster $Name" + $testTargetResourceReturnValue = $true + } } } catch { Write-Verbose -Message "Cluster $Name not found" - $false } + + $testTargetResourceReturnValue } diff --git a/DSCResources/MSFT_xWaitForCluster/MSFT_xWaitForCluster.schema.mof b/DSCResources/MSFT_xWaitForCluster/MSFT_xWaitForCluster.schema.mof index a3f9d33..6da3968 100644 --- a/DSCResources/MSFT_xWaitForCluster/MSFT_xWaitForCluster.schema.mof +++ b/DSCResources/MSFT_xWaitForCluster/MSFT_xWaitForCluster.schema.mof @@ -1,16 +1,11 @@ #pragma namespace("\\\\.\\root\\microsoft\\windows\\DesiredStateConfiguration") -[ClassVersion("1.0.0"), FriendlyName("xWaitForCluster")] +[ClassVersion("1.0.0"), FriendlyName("xWaitForCluster")] class MSFT_xWaitForCluster : OMI_BaseResource { - [key, Description("Name of the cluster")] - string Name; - - [Write, Description("Interval to check the cluster existency")] - Uint64 RetryIntervalSec; - - [Write, Description("Maximum number of retries to check cluster existency")] - Uint32 RetryCount; + [Key, Description("Name of the cluster to wait for.")] string Name; + [Write, Description("Interval to check for cluster existence. Default values is 10 seconds.")] Uint64 RetryIntervalSec; + [Write, Description("Maximum number of retries to check for cluster existence. Default value is 50 retries.")] Uint32 RetryCount; }; diff --git a/Tests/Unit/MSFT_xCluster.Tests.ps1 b/Tests/Unit/MSFT_xCluster.Tests.ps1 index a6a7d67..db306bc 100644 --- a/Tests/Unit/MSFT_xCluster.Tests.ps1 +++ b/Tests/Unit/MSFT_xCluster.Tests.ps1 @@ -110,7 +110,7 @@ try Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockGetCimInstance_ParameterFilter -Verifiable - { Get-TargetResource @mockDefaultParameters } | Should -Throw "Can't find machine's domain name" + { Get-TargetResource @mockDefaultParameters } | Should Throw "Can't find machine's domain name" } } @@ -122,7 +122,7 @@ try Mock -CommandName Get-Cluster -Verifiable Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockGetCimInstance_ParameterFilter -Verifiable - { Get-TargetResource @mockDefaultParameters } | Should -Throw ("Can't find the cluster {0}" -f $mockClusterName) + { Get-TargetResource @mockDefaultParameters } | Should Throw ("Can't find the cluster {0}" -f $mockClusterName) } } @@ -160,7 +160,7 @@ try Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockGetCimInstance_ParameterFilter -Verifiable - { Set-TargetResource @mockDefaultParameters } | Should -Throw "Can't find machine's domain name" + { Set-TargetResource @mockDefaultParameters } | Should Throw "Can't find machine's domain name" } } @@ -184,7 +184,7 @@ try # This is used to evaluate that cluster do exists after New-Cluster cmdlet has been run. Mock -CommandName Get-Cluster -MockWith $mockGetCluster - { Set-TargetResource @mockDefaultParameters } | Should -Not -Throw + { Set-TargetResource @mockDefaultParameters } | Should Not Throw Assert-MockCalled -CommandName New-Cluster -Exactly -Times 1 -Scope It Assert-MockCalled -CommandName Remove-ClusterNode -Exactly -Times 0 -Scope It @@ -202,7 +202,7 @@ try # This is used to evaluate that cluster do exists after New-Cluster cmdlet has been run. Mock -CommandName Get-Cluster -MockWith $mockGetCluster - { Set-TargetResource @mockDefaultParameters } | Should -Not -Throw + { Set-TargetResource @mockDefaultParameters } | Should Not Throw Assert-MockCalled -CommandName New-Cluster -Exactly -Times 1 -Scope It Assert-MockCalled -CommandName Remove-ClusterNode -Exactly -Times 0 -Scope It @@ -215,7 +215,7 @@ try It 'Should throw the correct error message' { Mock -CommandName Get-Cluster - { Set-TargetResource @mockDefaultParameters } | Should -Throw 'Cluster creation failed. Please verify output of ''Get-Cluster'' command' + { Set-TargetResource @mockDefaultParameters } | Should Throw 'Cluster creation failed. Please verify output of ''Get-Cluster'' command' Assert-MockCalled -CommandName New-Cluster -Exactly -Times 1 -Scope It Assert-MockCalled -CommandName Remove-ClusterNode -Exactly -Times 0 -Scope It @@ -228,7 +228,7 @@ try Mock -CommandName Get-ClusterNode Mock -CommandName Get-Cluster -MockWith $mockGetCluster -ParameterFilter $mockGetCluster_ParameterFilter - { Set-TargetResource @mockDefaultParameters } | Should -Not -Throw + { Set-TargetResource @mockDefaultParameters } | Should Not Throw Assert-MockCalled -CommandName New-Cluster -Exactly -Times 0 -Scope It Assert-MockCalled -CommandName Remove-ClusterNode -Exactly -Times 0 -Scope It @@ -246,7 +246,7 @@ try It 'Should call both Remove-ClusterNode and Add-ClusterNode cmdlet' { Mock -CommandName Get-Cluster -MockWith $mockGetCluster -ParameterFilter $mockGetCluster_ParameterFilter - { Set-TargetResource @mockDefaultParameters } | Should -Not -Throw + { Set-TargetResource @mockDefaultParameters } | Should Not Throw Assert-MockCalled -CommandName New-Cluster -Exactly -Times 0 -Scope It Assert-MockCalled -CommandName Remove-ClusterNode -Exactly -Times 1 -Scope It @@ -283,7 +283,7 @@ try It 'Should not call any of the cluster cmdlets' -Skip { Mock -CommandName Get-Cluster -MockWith $mockGetCluster -ParameterFilter $mockGetCluster_ParameterFilter -Verifiable - { Set-TargetResource @mockDefaultParameters } | Should -Not -Throw + { Set-TargetResource @mockDefaultParameters } | Should Not Throw Assert-MockCalled -CommandName New-Cluster -Exactly -Times 0 -Scope It Assert-MockCalled -CommandName Remove-ClusterNode -Exactly -Times 0 -Scope It @@ -303,7 +303,7 @@ try Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockGetCimInstance_ParameterFilter -Verifiable - { Test-TargetResource @mockDefaultParameters } | Should -Throw "Can't find machine's domain name" + { Test-TargetResource @mockDefaultParameters } | Should Throw "Can't find machine's domain name" } } @@ -320,7 +320,7 @@ try Mock -CommandName Get-Cluster -Verifiable $testTargetResourceResult = Test-TargetResource @mockDefaultParameters - $testTargetResourceResult | Should -Be $false + $testTargetResourceResult | Should Be $false } Assert-VerifiableMocks @@ -333,7 +333,7 @@ try } -Verifiable $testTargetResourceResult = Test-TargetResource @mockDefaultParameters - $testTargetResourceResult | Should -Be $false + $testTargetResourceResult | Should Be $false } Assert-VerifiableMocks @@ -346,7 +346,7 @@ try $testTargetResourceResult = Test-TargetResource @mockDefaultParameters - $testTargetResourceResult | Should -Be $false + $testTargetResourceResult | Should Be $false } Assert-VerifiableMocks @@ -363,7 +363,7 @@ try It 'Should return $false' { $testTargetResourceResult = Test-TargetResource @mockDefaultParameters - $testTargetResourceResult | Should -Be $false + $testTargetResourceResult | Should Be $false } Assert-VerifiableMocks @@ -385,7 +385,7 @@ try Context 'When the node already exist' { It 'Should return $true' { $testTargetResourceResult = Test-TargetResource @mockDefaultParameters - $testTargetResourceResult | Should -Be $true + $testTargetResourceResult | Should Be $true } Assert-VerifiableMocks diff --git a/Tests/Unit/MSFT_xWaitForCluster.Tests.ps1 b/Tests/Unit/MSFT_xWaitForCluster.Tests.ps1 new file mode 100644 index 0000000..1feaf7e --- /dev/null +++ b/Tests/Unit/MSFT_xWaitForCluster.Tests.ps1 @@ -0,0 +1,219 @@ +$script:DSCModuleName = 'xFailOverCluster' +$script:DSCResourceName = 'MSFT_xWaitForCluster' + +#region HEADER + +# Unit Test Template Version: 1.2.0 +$script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) +{ + & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) +} + +Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -Force + +$TestEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:DSCModuleName ` + -DSCResourceName $script:DSCResourceName ` + -TestType Unit + +#endregion HEADER + +function Invoke-TestSetup +{ + Import-Module -Name (Join-Path -Path (Join-Path -Path (Join-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'Tests') -ChildPath 'Unit') -ChildPath 'Stubs') -ChildPath 'FailoverClusters.stubs.psm1') -Global -Force +} + +function Invoke-TestCleanup +{ + Restore-TestEnvironment -TestEnvironment $TestEnvironment +} + +# Begin Testing +try +{ + Invoke-TestSetup + + InModuleScope $script:DSCResourceName { + $mockDomainName = 'domain.local' + $mockClusterName = 'CLUSTER001' + $mockRetryIntervalSec = '1' + $mockRetryCount = '1' + + $mockGetCimInstance = { + return [PSCustomObject] @{ + Domain = $mockDynamicDomainName + } + } + + $mockCimInstance_ParameterFilter = { + $ClassName -eq 'Win32_ComputerSystem' + } + + $mockGetCluster = { + return [PSCustomObject] @{ + Domain = $mockDomainName + Name = $mockClusterName + } + } + + $mockGetCluster_ParameterFilter = { + $Name -eq $mockClusterName -and $Domain -eq $mockDomainName + } + + $mockDefaultParameters = @{ + Name = $mockClusterName + RetryIntervalSec = $mockRetryIntervalSec + RetryCount = $mockRetryCount + } + + Describe 'xCluster\Get-TargetResource' -Tag Get { + Context 'When the system is either in the desired state or not in the desired state' { + It 'Returns a [System.Collection.Hashtable] type' { + $getTargetResourceResult = Get-TargetResource @mockDefaultParameters + $getTargetResourceResult | Should BeOfType [System.Collections.Hashtable] + } + + It 'Returns the same values passed as parameters' { + $getTargetResourceResult = Get-TargetResource @mockDefaultParameters + $getTargetResourceResult.Name | Should Be $mockDefaultParameters.Name + $getTargetResourceResult.RetryIntervalSec | Should Be $mockDefaultParameters.RetryIntervalSec + $getTargetResourceResult.RetryCount | Should Be $mockDefaultParameters.RetryCount + } + + Assert-VerifiableMocks + } + } + + Describe 'xCluster\Set-TargetResource' -Tag Set { + Context 'When computers domain name cannot be evaluated' { + $mockDynamicDomainName = $null + + It 'Should throw the correct error message' { + Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockCimInstance_ParameterFilter -Verifiable + + { Set-TargetResource @mockDefaultParameters } | Should Throw ('Failover cluster {0} not found after {1} attempts with {2} sec interval' -f $mockClusterName, ($mockRetryCount-1), $mockRetryIntervalSec) + } + } + + Context 'When the system is not in the desired state' { + BeforeEach { + Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockCimInstance_ParameterFilter -Verifiable + } + + Context 'When the cluster does not exist' { + Context 'When Get-Cluster throws an error' { + BeforeEach { + # This is used for the evaluation of a cluster that does not exist + Mock -CommandName Get-Cluster -MockWith { + throw 'Mock Get-Cluster throw error' + } -ParameterFilter $mockGetCluster_ParameterFilter + } + + $mockDynamicDomainName = $mockDomainName + + It 'Should throw the correct error message' { + { Set-TargetResource @mockDefaultParameters } | Should Throw ('Failover cluster {0} not found after {1} attempts with {2} sec interval' -f $mockClusterName, $mockRetryCount, $mockRetryIntervalSec) + } + + Assert-VerifiableMocks + } + } + } + + Context 'When the system is in the desired state' { + Context 'When the cluster exist' { + BeforeEach { + Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockCimInstance_ParameterFilter -Verifiable + Mock -CommandName Get-Cluster -MockWith $mockGetCluster -ParameterFilter $mockGetCluster_ParameterFilter -Verifiable + } + + $mockDynamicDomainName = $mockDomainName + + It 'Should not throw any error' { + { Set-TargetResource @mockDefaultParameters } | Should Not Throw + } + + Assert-VerifiableMocks + } + } + } + + Describe 'xCluster\Test-TargetResource' -Tag Test { + BeforeEach { + Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockCimInstance_ParameterFilter -Verifiable + } + + Context 'When computers domain name cannot be evaluated' { + $mockDynamicDomainName = $null + + It 'Should return the value $false' { + $testTargetResourceResult = Test-TargetResource @mockDefaultParameters + $testTargetResourceResult | Should Be $false + } + } + + Context 'When the system is not in the desired state' { + Context 'When the cluster does not exist' { + Context 'When Get-Cluster throws an error' { + BeforeEach { + # This is used for the evaluation of a cluster that does not exist. + Mock -CommandName Get-Cluster -MockWith { + throw 'Mock Get-Cluster throw error' + } -ParameterFilter $mockGetCluster_ParameterFilter + } + + $mockDynamicDomainName = $mockDomainName + + It 'Should return the value $false' { + $testTargetResourceResult = Test-TargetResource @mockDefaultParameters + $testTargetResourceResult | Should Be $false + } + + Assert-VerifiableMocks + } + + Context 'When Get-Cluster returns nothing' { + BeforeEach { + # This is used for the evaluation of a cluster that does not exist. + Mock -CommandName Get-Cluster -MockWith { + $null + } -ParameterFilter $mockGetCluster_ParameterFilter + } + + $mockDynamicDomainName = $mockDomainName + + It 'Should return the value $false' { + $testTargetResourceResult = Test-TargetResource @mockDefaultParameters + $testTargetResourceResult | Should Be $false + } + + Assert-VerifiableMocks + } + } + } + + Context 'When the system is in the desired state' { + Context 'When the cluster exist' { + BeforeEach { + Mock -CommandName Get-Cluster -MockWith $mockGetCluster -ParameterFilter $mockGetCluster_ParameterFilter -Verifiable + } + + $mockDynamicDomainName = $mockDomainName + + It 'Should return the value $true' { + $testTargetResourceResult = Test-TargetResource @mockDefaultParameters + $testTargetResourceResult | Should Be $true + } + + Assert-VerifiableMocks + } + } + } + } +} +finally +{ + Invoke-TestCleanup +} From da9b0f1a7c9232480a0087ac0b2ceaabed5d571b Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 23 Jul 2017 12:50:45 +0200 Subject: [PATCH 06/19] xClusterQuorum: Refactor unit test (#104) - Changes to xClusterQuorum - Refactored the unit test for this resource to use stubs and increase coverage (issue #77). - Changed the code to be more aligned with the style guideline. - Updated parameter description in the schema.mof. - Added example (issue #47) - 1-SetQuorumToNodeMajority.ps1 - 2-SetQuorumToNodeAndDiskMajority.ps1 - 3-SetQuorumToNodeAndFileShareMajority.ps1 - 4-SetQuorumToDiskOnly.ps1 - Added links to examples from README.md. --- CHANGELOG.md | 11 + .../MSFT_xClusterQuorum.psm1 | 138 ++-- .../MSFT_xClusterQuorum.schema.mof | 8 +- .../1-SetQuorumToNodeMajority.ps1 | 21 + .../2-SetQuorumToNodeAndDiskMajority.ps1 | 22 + .../3-SetQuorumToNodeAndFileShareMajority.ps1 | 30 + .../xClusterQuorum/4-SetQuorumToDiskOnly.ps1 | 22 + README.md | 5 +- Tests/MSFT_xClusterQuorum.Tests.ps1 | 595 ----------------- Tests/Unit/MSFT_xClusterQuorum.Tests.ps1 | 609 ++++++++++++++++++ 10 files changed, 813 insertions(+), 648 deletions(-) create mode 100644 Examples/Resources/xClusterQuorum/1-SetQuorumToNodeMajority.ps1 create mode 100644 Examples/Resources/xClusterQuorum/2-SetQuorumToNodeAndDiskMajority.ps1 create mode 100644 Examples/Resources/xClusterQuorum/3-SetQuorumToNodeAndFileShareMajority.ps1 create mode 100644 Examples/Resources/xClusterQuorum/4-SetQuorumToDiskOnly.ps1 delete mode 100644 Tests/MSFT_xClusterQuorum.Tests.ps1 create mode 100644 Tests/Unit/MSFT_xClusterQuorum.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index b98c14e..01d1926 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,17 @@ - Changed the code to be more aligned with the style guideline. - Updated parameter description in the schema.mof. - Resolved Script Analyzer warnings ([issue #54](https://github.com/PowerShell/xFailOverCluster/issues/54)). +- Changes to xClusterQuorum + - Refactored the unit test for this resource to use stubs and increase coverage + ([issue #77](https://github.com/PowerShell/xFailOverCluster/issues/77)). + - Changed the code to be more aligned with the style guideline. + - Updated parameter description in the schema.mof. + - Added example ([issue #47](https://github.com/PowerShell/xFailOverCluster/issues/47)) + - 1-SetQuorumToNodeMajority.ps1 + - 2-SetQuorumToNodeAndDiskMajority.ps1 + - 3-SetQuorumToNodeAndFileShareMajority.ps1 + - 4-SetQuorumToDiskOnly.ps1 + - Added links to examples from README.md. ## 1.7.0.0 diff --git a/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.psm1 b/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.psm1 index 783b25c..0b39095 100644 --- a/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.psm1 +++ b/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.psm1 @@ -1,84 +1,103 @@ +<# + .SYNOPSIS + Returns the current state of the failover cluster quorum. + .PARAMETER IsSingleInstance + Specifies the resource is a single instance, the value must be 'Yes'. +#> function Get-TargetResource { [CmdletBinding()] - [OutputType([Hashtable])] + [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] [ValidateSet('Yes')] - [String] $IsSingleInstance, - - [Parameter(Mandatory = $false)] - [ValidateSet('NodeMajority', 'NodeAndDiskMajority', 'NodeAndFileShareMajority', 'DiskOnly')] - [String] $Type, - - [Parameter(Mandatory = $false)] - [String] $Resource + [System.String] + $IsSingleInstance ) - $ClusterQuorum = Get-ClusterQuorum + $getClusterQuorumResult = Get-ClusterQuorum - switch ($ClusterQuorum.QuorumType) + switch ($getClusterQuorumResult.QuorumType) { # WS2016 only 'Majority' { - if ($ClusterQuorum.QuorumResource -eq $null) + if ($null -eq $getClusterQuorumResult.QuorumResource) { - $ClusterQuorumType = 'NodeMajority' + $clusterQuorumType = 'NodeMajority' } - elseif ($ClusterQuorum.QuorumResource.ResourceType.DisplayName -eq 'Physical Disk') + elseif ($getClusterQuorumResult.QuorumResource.ResourceType.DisplayName -eq 'Physical Disk') { - $ClusterQuorumType = 'NodeAndDiskMajority' + $clusterQuorumType = 'NodeAndDiskMajority' } - elseif ($ClusterQuorum.QuorumResource.ResourceType.DisplayName -eq 'File Share Witness') + elseif ($getClusterQuorumResult.QuorumResource.ResourceType.DisplayName -eq 'File Share Witness') { - $ClusterQuorumType = 'NodeAndFileShareMajority' + $clusterQuorumType = 'NodeAndFileShareMajority' } else { - throw "Unknown quorum resource: $($ClusterQuorum.QuorumResource)" + throw "Unknown quorum resource: $($getClusterQuorumResult.QuorumResource)" } } # WS2012R2 only 'NodeMajority' { - $ClusterQuorumType = 'NodeMajority' + $clusterQuorumType = 'NodeMajority' } + 'NodeAndDiskMajority' { - $ClusterQuorumType = 'NodeAndDiskMajority' + $clusterQuorumType = 'NodeAndDiskMajority' } + 'NodeAndFileShareMajority' { - $ClusterQuorumType = 'NodeAndFileShareMajority' + $clusterQuorumType = 'NodeAndFileShareMajority' } # All 'DiskOnly' { - $ClusterQuorumType = 'DiskOnly' + $clusterQuorumType = 'DiskOnly' } # Default default { - throw "Unknown quorum type: $($ClusterQuorum.QuorumType)" + throw "Unknown quorum type: $($getClusterQuorumResult.QuorumType)" } } - if ($ClusterQuorumType -eq 'NodeAndFileShareMajority') + if ($clusterQuorumType -eq 'NodeAndFileShareMajority') { - $ClusterQuorumResource = $ClusterQuorum.QuorumResource | Get-ClusterParameter -Name SharePath | Select-Object -ExpandProperty Value + $clusterQuorumResource = $getClusterQuorumResult.QuorumResource | + Get-ClusterParameter -Name SharePath | + Select-Object -ExpandProperty Value } else { - $ClusterQuorumResource = [String] $ClusterQuorum.QuorumResource.Name + $clusterQuorumResource = [String] $getClusterQuorumResult.QuorumResource.Name } @{ IsSingleInstance = $IsSingleInstance - Type = $ClusterQuorumType - Resource = $ClusterQuorumResource + Type = $clusterQuorumType + Resource = $clusterQuorumResource } } +<# + .SYNOPSIS + Configures the failover cluster quorum. + + .PARAMETER IsSingleInstance + Specifies the resource is a single instance, the value must be 'Yes'. + + .PARAMETER Type + Quorum type to use. Can be set to either NodeMajority, NodeAndDiskMajority, + NodeAndFileShareMajority or DiskOnly. + + .PARAMETER Resource + The name of the disk or file share resource to use as witness. This parameter + is optional if the quorum type is set to NodeMajority. +#> function Set-TargetResource { [CmdletBinding()] @@ -86,14 +105,17 @@ function Set-TargetResource ( [Parameter(Mandatory = $true)] [ValidateSet('Yes')] - [String] $IsSingleInstance, + [System.String] + $IsSingleInstance, - [Parameter(Mandatory = $false)] + [Parameter()] [ValidateSet('NodeMajority', 'NodeAndDiskMajority', 'NodeAndFileShareMajority', 'DiskOnly')] - [String] $Type, - - [Parameter(Mandatory = $false)] - [String] $Resource + [System.String] + $Type, + + [Parameter()] + [System.String] + $Resource ) switch ($Type) @@ -116,6 +138,21 @@ function Set-TargetResource } } +<# + .SYNOPSIS + Tests the current state of the failover cluster quorum. + + .PARAMETER IsSingleInstance + Specifies the resource is a single instance, the value must be 'Yes'. + + .PARAMETER Type + Quorum type to use. Can be set to either NodeMajority, NodeAndDiskMajority, + NodeAndFileShareMajority or DiskOnly. + + .PARAMETER Resource + The name of the disk or file share resource to use as witness. This parameter + is optional if the quorum type is set to NodeMajority. +#> function Test-TargetResource { [CmdletBinding()] @@ -124,22 +161,29 @@ function Test-TargetResource ( [Parameter(Mandatory = $true)] [ValidateSet('Yes')] - [String] $IsSingleInstance, + [System.String] + $IsSingleInstance, - [Parameter(Mandatory = $false)] + [Parameter()] [ValidateSet('NodeMajority', 'NodeAndDiskMajority', 'NodeAndFileShareMajority', 'DiskOnly')] - [String] $Type, - - [Parameter(Mandatory = $false)] - [String] $Resource - ) - - $CurrentQuorum = Get-TargetResource -IsSingleInstance $IsSingleInstance - - return ( - ($CurrentQuorum.Type -eq $Type) -and - ($CurrentQuorum.Resource -eq $Resource) + [System.String] + $Type, + + [Parameter()] + [System.String] + $Resource ) + + $getGetTargetResourceResult = Get-TargetResource -IsSingleInstance $IsSingleInstance + + $testTargetResourceReturnValue = $false + + if ($getGetTargetResourceResult.Type -eq $Type -and $getGetTargetResourceResult.Resource -eq $Resource) + { + $testTargetResourceReturnValue = $true + } + + $testTargetResourceReturnValue } Export-ModuleMember -Function *-TargetResource diff --git a/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.schema.mof b/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.schema.mof index ef0851d..0e22d07 100644 --- a/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.schema.mof +++ b/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.schema.mof @@ -1,9 +1,7 @@ [ClassVersion("1.0.0.0"), FriendlyName("xClusterQuorum")] class MSFT_xClusterQuorum : OMI_BaseResource { - [Key, ValueMap{"Yes"}, Values{"Yes"}] string IsSingleInstance; - - [Write, ValueMap{"NodeMajority", "NodeAndDiskMajority", "NodeAndFileShareMajority", "DiskOnly"}, Values{"NodeMajority", "NodeAndDiskMajority", "NodeAndFileShareMajority", "DiskOnly"}] string Type; - - [Write] String Resource; + [Key, Description("Specifies the resource is a single instance, the value must be 'Yes'."), ValueMap{"Yes"}, Values{"Yes"}] string IsSingleInstance; + [Write, Description("Quorum type to use. Can be set to either NodeMajority, NodeAndDiskMajority, NodeAndFileShareMajority or DiskOnly."), ValueMap{"NodeMajority", "NodeAndDiskMajority", "NodeAndFileShareMajority", "DiskOnly"}, Values{"NodeMajority", "NodeAndDiskMajority", "NodeAndFileShareMajority", "DiskOnly"}] string Type; + [Write, Description("The name of the disk or file share resource to use as witness. This parameter is optional if the quorum type is set to NodeMajority.")] String Resource; }; diff --git a/Examples/Resources/xClusterQuorum/1-SetQuorumToNodeMajority.ps1 b/Examples/Resources/xClusterQuorum/1-SetQuorumToNodeMajority.ps1 new file mode 100644 index 0000000..436fba9 --- /dev/null +++ b/Examples/Resources/xClusterQuorum/1-SetQuorumToNodeMajority.ps1 @@ -0,0 +1,21 @@ +<# +.EXAMPLE + This example shows how to set the quorum in a failover cluster to use + node majority. + + This example assumes the failover cluster is already present. +#> + +Configuration Example +{ + Import-DscResource -ModuleName xFailOverCluster + + Node localhost + { + xClusterQuorum 'SetQuorumToNodeMajority' + { + IsSingleInstance = 'Yes' + Type = 'NodeMajority' + } + } +} diff --git a/Examples/Resources/xClusterQuorum/2-SetQuorumToNodeAndDiskMajority.ps1 b/Examples/Resources/xClusterQuorum/2-SetQuorumToNodeAndDiskMajority.ps1 new file mode 100644 index 0000000..ffd067d --- /dev/null +++ b/Examples/Resources/xClusterQuorum/2-SetQuorumToNodeAndDiskMajority.ps1 @@ -0,0 +1,22 @@ +<# +.EXAMPLE + This example shows how to set the quorum in a failover cluster to use + node and disk majority. + + This example assumes the failover cluster is already present. +#> + +Configuration Example +{ + Import-DscResource -ModuleName xFailOverCluster + + Node localhost + { + xClusterQuorum 'SetQuorumToNodeAndDiskMajority' + { + IsSingleInstance = 'Yes' + Type = 'NodeAndDiskMajority' + Resource = 'Witness Cluster Disk' + } + } +} diff --git a/Examples/Resources/xClusterQuorum/3-SetQuorumToNodeAndFileShareMajority.ps1 b/Examples/Resources/xClusterQuorum/3-SetQuorumToNodeAndFileShareMajority.ps1 new file mode 100644 index 0000000..9c8b4eb --- /dev/null +++ b/Examples/Resources/xClusterQuorum/3-SetQuorumToNodeAndFileShareMajority.ps1 @@ -0,0 +1,30 @@ +<# +.EXAMPLE + This example shows how to set the quorum in a failover cluster to use + node and file share majority. + + This example assumes the failover cluster is already present. + + This example also assumes that path \\witness.company.local\witness$ is already + present and has the right permission to be used by the cluster. + Either the user running the configuration or the Cluster Name Object (CNO) + should have full control on the share to be able to create the witness folder + and set the permissions. More than one cluster can use the same share. + Here is a link for setting up the high availability for the file share witness + https://blogs.msdn.microsoft.com/clustering/2014/03/31/configuring-a-file-share-witness-on-a-scale-out-file-server/ +#> + +Configuration Example +{ + Import-DscResource -ModuleName xFailOverCluster + + Node localhost + { + xClusterQuorum 'SetQuorumToNodeAndDiskMajority' + { + IsSingleInstance = 'Yes' + Type = 'NodeAndFileShareMajority' + Resource = '\\witness.company.local\witness$' + } + } +} diff --git a/Examples/Resources/xClusterQuorum/4-SetQuorumToDiskOnly.ps1 b/Examples/Resources/xClusterQuorum/4-SetQuorumToDiskOnly.ps1 new file mode 100644 index 0000000..38b5786 --- /dev/null +++ b/Examples/Resources/xClusterQuorum/4-SetQuorumToDiskOnly.ps1 @@ -0,0 +1,22 @@ +<# +.EXAMPLE + This example shows how to set the quorum in a failover cluster to use + disk only. + + This example assumes the failover cluster is already present. +#> + +Configuration Example +{ + Import-DscResource -ModuleName xFailOverCluster + + Node localhost + { + xClusterQuorum 'SetQuorumToDiskOnly' + { + IsSingleInstance = 'Yes' + Type = 'DiskOnly' + Resource = 'Witness Cluster Disk' + } + } +} diff --git a/README.md b/README.md index 4697234..683a0ed 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,10 @@ Configures quorum in a cluster. #### Examples for xClusterQuorum -None. +* [Set quorum to node majority](/Examples/Resources/xClusterQuorum/1-SetQuorumToNodeMajority.ps1) +* [Set quorum to node and disk majority](/Examples/Resources/xClusterQuorum/2-SetQuorumToNodeAndDiskMajority.ps1) +* [Set quorum to node and file share majority](/Examples/Resources/xClusterQuorum/3-SetQuorumToNodeAndFileShareMajority.ps1) +* [Set quorum to disk only](/Examples/Resources/xClusterQuorum/4-SetQuorumToDiskOnly.ps1) ### xWaitForCluster diff --git a/Tests/MSFT_xClusterQuorum.Tests.ps1 b/Tests/MSFT_xClusterQuorum.Tests.ps1 deleted file mode 100644 index 00ed8da..0000000 --- a/Tests/MSFT_xClusterQuorum.Tests.ps1 +++ /dev/null @@ -1,595 +0,0 @@ -[CmdletBinding()] -param -( -) - -if (!$PSScriptRoot) -{ - $PSScriptRoot = [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Path) -} - -$RootPath = (Resolve-Path -Path "$PSScriptRoot\..").Path -$ModuleName = 'MSFT_xClusterQuorum' - -try -{ - if (-not (Get-WindowsFeature -Name RSAT-Clustering-PowerShell -ErrorAction Stop).Installed) - { - Add-WindowsFeature -Name RSAT-Clustering-PowerShell -ErrorAction Stop - } -} -catch -{ - Write-Warning $_ -} - -Import-Module (Join-Path -Path $RootPath -ChildPath "DSCResources\$ModuleName\$ModuleName.psm1") -Force - - -## General test for the xClusterQuorum resource - -Describe 'xClusterQuorum' { - - InModuleScope $ModuleName { - - $TestParameter = @{ - IsSingleInstance = 'Yes' - Type = 'NodeAndDiskMajority' - Resource = 'Witness' - } - - Mock -CommandName 'Get-ClusterQuorum' -MockWith { - [PSCustomObject] @{ - Cluster = 'CLUSTER01' - QuorumResource = 'Witness' - QuorumType = 'NodeAndDiskMajority' - } - } - - Mock -CommandName 'Set-ClusterQuorum' -MockWith { - } - - Context 'Validate Get-TargetResource method' { - - It 'Returns a [System.Collection.Hashtable] type' { - - $Result = Get-TargetResource @TestParameter - - $Result -is [System.Collections.Hashtable] | Should Be $true - } - } - - Context 'Validate Set-TargetResource method' { - - It 'Returns nothing' { - - $Result = Set-TargetResource @TestParameter - - $Result -eq $null | Should Be $true - } - } - - Context 'Validate Test-TargetResource method' { - - It 'Returns a [System.Boolean] type' { - - $Result = Test-TargetResource @TestParameter - - $Result -is [System.Boolean] | Should Be $true - } - } - } -} - - -## Test NodeMajority quorum type - -Describe 'xClusterQuorum (NodeMajority / WS2012R2)' { - - InModuleScope $ModuleName { - - $TestParameter = @{ - IsSingleInstance = 'Yes' - Type = 'NodeMajority' - Resource = '' - } - - Mock -CommandName 'Get-ClusterQuorum' -MockWith { - [PSCustomObject] @{ - Cluster = 'CLUSTER01' - QuorumType = 'NodeMajority' - QuorumResource = $null - } - } - - Mock -CommandName 'Set-ClusterQuorum' -ParameterFilter { $NoWitness -eq $true } -MockWith { - } - - Context 'Validate Get-TargetResource method' { - - It 'Returns current configuration' { - - $Result = Get-TargetResource @TestParameter - - $Result.IsSingleInstance | Should Be $TestParameter.IsSingleInstance - $Result.Type | Should Be $TestParameter.Type - $Result.Resource | Should Be $TestParameter.Resource - } - } - - Context 'Validate Set-TargetResource method' { - - It 'Set the new configuration' { - - $Result = Set-TargetResource @TestParameter - - Assert-MockCalled -CommandName 'Set-ClusterQuorum' -ParameterFilter { $NoWitness -eq $true } -Times 1 - } - } - - Context 'Validate Test-TargetResource method' { - - It 'Check the current configuration' { - - $Result = Test-TargetResource @TestParameter - - $Result | Should Be $true - } - } - } -} - -Describe 'xClusterQuorum (NodeMajority / WS2016Prev)' { - - InModuleScope $ModuleName { - - $TestParameter = @{ - IsSingleInstance = 'Yes' - Type = 'NodeMajority' - Resource = '' - } - - Mock -CommandName 'Get-ClusterQuorum' -MockWith { - [PSCustomObject] @{ - Cluster = 'CLUSTER01' - QuorumType = 'Majority' - QuorumResource = $null - } - } - - Mock -CommandName 'Set-ClusterQuorum' -ParameterFilter { $NoWitness -eq $true } -MockWith { - } - - Context 'Validate Get-TargetResource method' { - - It 'Returns current configuration' { - - $Result = Get-TargetResource @TestParameter - - $Result.IsSingleInstance | Should Be $TestParameter.IsSingleInstance - $Result.Type | Should Be $TestParameter.Type - $Result.Resource | Should Be $TestParameter.Resource - } - } - - Context 'Validate Set-TargetResource method' { - - It 'Set the new configuration' { - - $Result = Set-TargetResource @TestParameter - - Assert-MockCalled -CommandName 'Set-ClusterQuorum' -ParameterFilter { $NoWitness -eq $true } -Times 1 - } - } - - Context 'Validate Test-TargetResource method' { - - It 'Check the current configuration' { - - $Result = Test-TargetResource @TestParameter - - $Result | Should Be $true - } - } - } -} - - -## Test NodeAndDiskMajority quorum type - -Describe 'xClusterQuorum (NodeAndDiskMajority / WS2012R2)' { - - InModuleScope $ModuleName { - - $TestParameter = @{ - IsSingleInstance = 'Yes' - Type = 'NodeAndDiskMajority' - Resource = 'Witness' - } - - Mock -CommandName 'Get-ClusterQuorum' -MockWith { - [PSCustomObject] @{ - Cluster = 'CLUSTER01' - QuorumType = 'NodeAndDiskMajority' - QuorumResource = [PSCustomObject] @{ - Name = 'Witness' - OwnerGroup = 'Cluster Group' - ResourceType = [PSCustomObject] @{ - DisplayName = 'Physical Disk' - } - } - } - } - - Mock -CommandName 'Set-ClusterQuorum' -ParameterFilter { $DiskWitness -eq 'Witness' } -MockWith { - } - - Context 'Validate Get-TargetResource method' { - - It 'Returns current configuration' { - - $Result = Get-TargetResource @TestParameter - - $Result.IsSingleInstance | Should Be $TestParameter.IsSingleInstance - $Result.Type | Should Be $TestParameter.Type - $Result.Resource | Should Be $TestParameter.Resource - } - } - - Context 'Validate Set-TargetResource method' { - - It 'Set the new configuration' { - - $Result = Set-TargetResource @TestParameter - - Assert-MockCalled -CommandName 'Set-ClusterQuorum' -ParameterFilter { $DiskWitness -eq 'Witness' } -Times 1 - } - } - - Context 'Validate Test-TargetResource method' { - - It 'Check the current configuration' { - - $Result = Test-TargetResource @TestParameter - - $Result | Should Be $true - } - } - } -} - -Describe 'xClusterQuorum (NodeAndDiskMajority / WS2016Prev)' { - - InModuleScope $ModuleName { - - $TestParameter = @{ - IsSingleInstance = 'Yes' - Type = 'NodeAndDiskMajority' - Resource = 'Witness' - } - - Mock -CommandName 'Get-ClusterQuorum' -MockWith { - [PSCustomObject] @{ - Cluster = 'CLUSTER01' - QuorumType = 'Majority' - QuorumResource = [PSCustomObject] @{ - Name = 'Witness' - OwnerGroup = 'Cluster Group' - ResourceType = [PSCustomObject] @{ - DisplayName = 'Physical Disk' - } - } - } - } - - Mock -CommandName 'Set-ClusterQuorum' -ParameterFilter { $DiskWitness -eq 'Witness' } -MockWith { - } - - Context 'Validate Get-TargetResource method' { - - It 'Returns current configuration' { - - $Result = Get-TargetResource @TestParameter - - $Result.IsSingleInstance | Should Be $TestParameter.IsSingleInstance - $Result.Type | Should Be $TestParameter.Type - $Result.Resource | Should Be $TestParameter.Resource - } - } - - Context 'Validate Set-TargetResource method' { - - It 'Set the new configuration' { - - $Result = Set-TargetResource @TestParameter - - Assert-MockCalled -CommandName 'Set-ClusterQuorum' -ParameterFilter { $DiskWitness -eq 'Witness' } -Times 1 - } - } - - Context 'Validate Test-TargetResource method' { - - It 'Check the current configuration' { - - $Result = Test-TargetResource @TestParameter - - $Result | Should Be $true - } - } - } -} - - -## Test NodeAndFileShareMajority quorum type - -Describe 'xClusterQuorum (NodeAndFileShareMajority / WS2012R2)' { - - InModuleScope $ModuleName { - - $TestParameter = @{ - IsSingleInstance = 'Yes' - Type = 'NodeAndFileShareMajority' - Resource = '\\FILE01\CLUSTER01' - } - - Mock -CommandName 'Get-ClusterQuorum' -MockWith { - [PSCustomObject] @{ - Cluster = 'CLUSTER01' - QuorumType = 'NodeAndFileShareMajority' - QuorumResource = [PSCustomObject] @{ - Name = 'File Share Witness' - OwnerGroup = 'Cluster Group' - ResourceType = [PSCustomObject] @{ - DisplayName = 'File Share Witness' - } - } - } - } - - Mock -CommandName 'Get-ClusterParameter' -ParameterFilter { $Name -eq 'SharePath' } -MockWith { - @( - [PSCustomObject] @{ - ClusterObject = 'File Share Witness' - Name = 'SharePath' - IsReadOnly = 'False' - ParameterType = 'String' - Value = '\\FILE01\CLUSTER01' - } - ) - } - - Mock -CommandName 'Set-ClusterQuorum' -ParameterFilter { $FileShareWitness -eq '\\FILE01\CLUSTER01' } -MockWith { - } - - Context 'Validate Get-TargetResource method' { - - It 'Returns current configuration' { - - $Result = Get-TargetResource @TestParameter - - $Result.IsSingleInstance | Should Be $TestParameter.IsSingleInstance - $Result.Type | Should Be $TestParameter.Type - $Result.Resource | Should Be $TestParameter.Resource - } - } - - Context 'Validate Set-TargetResource method' { - - It 'Set the new configuration' { - - $Result = Set-TargetResource @TestParameter - - Assert-MockCalled -CommandName 'Set-ClusterQuorum' -ParameterFilter { $FileShareWitness -eq '\\FILE01\CLUSTER01' } -Times 1 - } - } - - Context 'Validate Test-TargetResource method' { - - It 'Check the current configuration' { - - $Result = Test-TargetResource @TestParameter - - $Result | Should Be $true - } - } - } -} - -Describe 'xClusterQuorum (NodeAndFileShareMajority / WS2016Prev)' { - - InModuleScope $ModuleName { - - $TestParameter = @{ - IsSingleInstance = 'Yes' - Type = 'NodeAndFileShareMajority' - Resource = '\\FILE01\CLUSTER01' - } - - Mock -CommandName 'Get-ClusterQuorum' -MockWith { - [PSCustomObject] @{ - Cluster = 'CLUSTER01' - QuorumType = 'Majority' - QuorumResource = [PSCustomObject] @{ - Name = 'File Share Witness' - OwnerGroup = 'Cluster Group' - ResourceType = [PSCustomObject] @{ - DisplayName = 'File Share Witness' - } - } - } - } - - Mock -CommandName 'Get-ClusterParameter' -ParameterFilter { $Name -eq 'SharePath' } -MockWith { - @( - [PSCustomObject] @{ - ClusterObject = 'File Share Witness' - Name = 'SharePath' - IsReadOnly = 'False' - ParameterType = 'String' - Value = '\\FILE01\CLUSTER01' - } - ) - } - - Mock -CommandName 'Set-ClusterQuorum' -ParameterFilter { $FileShareWitness -eq '\\FILE01\CLUSTER01' } -MockWith { - } - - Context 'Validate Get-TargetResource method' { - - It 'Returns current configuration' { - - $Result = Get-TargetResource @TestParameter - - $Result.IsSingleInstance | Should Be $TestParameter.IsSingleInstance - $Result.Type | Should Be $TestParameter.Type - $Result.Resource | Should Be $TestParameter.Resource - } - } - - Context 'Validate Set-TargetResource method' { - - It 'Set the new configuration' { - - $Result = Set-TargetResource @TestParameter - - Assert-MockCalled -CommandName 'Set-ClusterQuorum' -ParameterFilter { $FileShareWitness -eq '\\FILE01\CLUSTER01' } -Times 1 - } - } - - Context 'Validate Test-TargetResource method' { - - It 'Check the current configuration' { - - $Result = Test-TargetResource @TestParameter - - $Result | Should Be $true - } - } - } -} - - -## Test DiskOnly quorum type - -Describe 'xClusterQuorum (NodeAndDiskMajority / WS2012R2)' { - - InModuleScope $ModuleName { - - $TestParameter = @{ - IsSingleInstance = 'Yes' - Type = 'DiskOnly' - Resource = 'Witness' - } - - Mock -CommandName 'Get-ClusterQuorum' -MockWith { - [PSCustomObject] @{ - Cluster = 'CLUSTER01' - QuorumType = 'DiskOnly' - QuorumResource = [PSCustomObject] @{ - Name = 'Witness' - OwnerGroup = 'Cluster Group' - ResourceType = [PSCustomObject] @{ - DisplayName = 'Physical Disk' - } - } - } - } - - Mock -CommandName 'Set-ClusterQuorum' -ParameterFilter { $DiskOnly -eq 'Witness' } -MockWith { - } - - Context 'Validate Get-TargetResource method' { - - It 'Returns current configuration' { - - $Result = Get-TargetResource @TestParameter - - $Result.IsSingleInstance | Should Be $TestParameter.IsSingleInstance - $Result.Type | Should Be $TestParameter.Type - $Result.Resource | Should Be $TestParameter.Resource - } - } - - Context 'Validate Set-TargetResource method' { - - It 'Set the new configuration' { - - $Result = Set-TargetResource @TestParameter - - Assert-MockCalled -CommandName 'Set-ClusterQuorum' -ParameterFilter { $DiskOnly -eq 'Witness' } -Times 1 - } - } - - Context 'Validate Test-TargetResource method' { - - It 'Check the current configuration' { - - $Result = Test-TargetResource @TestParameter - - $Result | Should Be $true - } - } - } -} - -Describe 'xClusterQuorum (NodeAndDiskMajority / WS2016Prev)' { - - InModuleScope $ModuleName { - - $TestParameter = @{ - IsSingleInstance = 'Yes' - Type = 'DiskOnly' - Resource = 'Witness' - } - - Mock -CommandName 'Get-ClusterQuorum' -MockWith { - [PSCustomObject] @{ - Cluster = 'CLUSTER01' - QuorumType = 'DiskOnly' - QuorumResource = [PSCustomObject] @{ - Name = 'Witness' - OwnerGroup = 'Cluster Group' - ResourceType = [PSCustomObject] @{ - DisplayName = 'Physical Disk' - } - } - } - } - - Mock -CommandName 'Set-ClusterQuorum' -ParameterFilter { $DiskOnly -eq 'Witness' } -MockWith { - } - - Context 'Validate Get-TargetResource method' { - - It 'Returns current configuration' { - - $Result = Get-TargetResource @TestParameter - - $Result.IsSingleInstance | Should Be $TestParameter.IsSingleInstance - $Result.Type | Should Be $TestParameter.Type - $Result.Resource | Should Be $TestParameter.Resource - } - } - - Context 'Validate Set-TargetResource method' { - - It 'Set the new configuration' { - - $Result = Set-TargetResource @TestParameter - - Assert-MockCalled -CommandName 'Set-ClusterQuorum' -ParameterFilter { $DiskOnly -eq 'Witness' } -Times 1 - } - } - - Context 'Validate Test-TargetResource method' { - - It 'Check the current configuration' { - - $Result = Test-TargetResource @TestParameter - - $Result | Should Be $true - } - } - } -} - diff --git a/Tests/Unit/MSFT_xClusterQuorum.Tests.ps1 b/Tests/Unit/MSFT_xClusterQuorum.Tests.ps1 new file mode 100644 index 0000000..885be0f --- /dev/null +++ b/Tests/Unit/MSFT_xClusterQuorum.Tests.ps1 @@ -0,0 +1,609 @@ +$script:DSCModuleName = 'xFailOverCluster' +$script:DSCResourceName = 'MSFT_xClusterQuorum' + +#region Header + +# Unit Test Template Version: 1.2.0 +$script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) +{ + & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) +} + +Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -Force + +$TestEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:DSCModuleName ` + -DSCResourceName $script:DSCResourceName ` + -TestType Unit + +#endregion Header + +function Invoke-TestSetup +{ + Import-Module -Name (Join-Path -Path (Join-Path -Path (Join-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'Tests') -ChildPath 'Unit') -ChildPath 'Stubs') -ChildPath 'FailoverClusters.stubs.psm1') -Global -Force +} + +function Invoke-TestCleanup +{ + Restore-TestEnvironment -TestEnvironment $TestEnvironment +} + +# Begin Testing +try +{ + Invoke-TestSetup + + InModuleScope $script:DSCResourceName { + $mockQuorumType_Majority = 'Majority' + $mockQuorumType_NodeMajority = 'NodeMajority' + $mockQuorumType_NodeAndDiskMajority = 'NodeAndDiskMajority' + $mockQuorumType_NodeAndFileShareMajority = 'NodeAndFileShareMajority' + $mockQuorumType_DiskOnly = 'DiskOnly' + $mockQuorumType_Unknown = 'Unknown' + + $mockQuorumResourceName = 'Witness' + $mockQuorumFileShareWitnessPath = '\\FILE01\CLUSTER01' + + $mockGetClusterQuorum = { + $getClusterQuorumReturnValue = [PSCustomObject] @{ + Cluster = 'CLUSTER01' + QuorumType = $mockDynamicQuorumType + QuorumResource = [PSCustomObject] @{ + Name = $mockDynamicQuorumResourceName + OwnerGroup = 'Cluster Group' + ResourceType = [PSCustomObject] @{ + DisplayName = 'Physical Disk' + } + } + } + + switch ($mockDynamicExcpectedQuorumType) + { + $mockQuorumType_NodeMajority + { + $getClusterQuorumReturnValue.QuorumResource = $null + } + + $mockQuorumType_NodeAndDiskMajority + { + $getClusterQuorumReturnValue.QuorumResource.ResourceType.DisplayName = 'Physical Disk' + } + + $mockQuorumType_NodeAndFileShareMajority + { + $getClusterQuorumReturnValue.QuorumResource.ResourceType.DisplayName = 'File Share Witness' + } + + $mockQuorumType_Unknown + { + $getClusterQuorumReturnValue.QuorumResource.ResourceType.DisplayName = 'Unknown' + } + } + + $getClusterQuorumReturnValue + } + + $mockGetClusterParameter = { + @( + [PSCustomObject] @{ + ClusterObject = 'File Share Witness' + Name = 'SharePath' + IsReadOnly = 'False' + ParameterType = 'String' + Value = $mockQuorumFileShareWitnessPath + } + ) + } + + $mockGetClusterParameter_ParameterFilter = { + $Name -eq 'SharePath' + } + + $mockSetClusterQuorum_NoWitness_ParameterFilter = { + $NoWitness -eq $true + } + + $mockSetClusterQuorum_DiskWitness_ParameterFilter = { + $PSBoundParameters.ContainsKey('DiskWitness') -eq $true + } + + $mockSetClusterQuorum_FileShareWitness_ParameterFilter = { + $PSBoundParameters.ContainsKey('FileShareWitness') -eq $true + } + + $mockSetClusterQuorum_DiskOnly_ParameterFilter = { + $PSBoundParameters.ContainsKey('DiskOnly') -eq $true + } + + $mockSetClusterQuorum = { + $wrongParameters = $false + + # Evaluate if the Set-ClusterQuorum is called with the correct parameters. + switch ($mockDynamicSetClusterQuorum_ExcpectedQuorumType) + { + $mockQuorumType_NodeMajority + { + if (-not $NoWitness) + { + $wrongParameters = $true + } + } + + $mockQuorumType_NodeAndDiskMajority + { + if ($DiskWitness -ne $mockDynamicQuorumResourceName) + { + $wrongParameters = $true + } + } + + $mockQuorumType_NodeAndFileShareMajority + { + if ($FileShareWitness -ne $mockDynamicQuorumResourceName) + { + $wrongParameters = $true + } + } + + $mockQuorumType_DiskOnly + { + if ($DiskOnly -ne $mockDynamicQuorumResourceName) + { + $wrongParameters = $true + } + } + + default + { + $wrongParameters = $true + } + } + + if ($wrongParameters) + { + throw 'Mock Set-ClusterQuorum was called with the wrong parameters.' + } + } + + $mockDefaultParameters = @{ + IsSingleInstance = 'Yes' + } + + Describe 'xClusterQuorum\Get-TargetResource' { + BeforeEach { + Mock -CommandName 'Get-ClusterQuorum' -MockWith $mockGetClusterQuorum + Mock -CommandName 'Get-ClusterParameter' -MockWith $mockGetClusterParameter -ParameterFilter $mockGetClusterParameter_ParameterFilter + + $mockTestParameters = $mockDefaultParameters.Clone() + } + + Context 'When the system is either in the desired state or not in the desired state' { + BeforeAll { + $mockDynamicQuorumResourceName = $mockQuorumResourceName + } + + Context 'When quorum type should be NodeMajority' { + Context 'When target node is Windows Server 2012 R2' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_NodeMajority + } + + It 'Should return the correct type' { + $getTargetResourceResult = Get-TargetResource @mockTestParameters + $getTargetResourceResult | Should BeOfType [System.Collections.Hashtable] + } + + It 'Should return the same values as passed as parameters' { + $getTargetResourceResult = Get-TargetResource @mockTestParameters + $getTargetResourceResult.IsSingleInstance | Should Be $mockTestParameters.IsSingleInstance + } + + It 'Should return the correct values' { + $getTargetResourceResult = Get-TargetResource @mockTestParameters + $getTargetResourceResult.Type | Should Be $mockQuorumType_NodeMajority + $getTargetResourceResult.Resource | Should Be $mockQuorumResourceName + } + } + + Context 'When target node is Windows Server 2016 and newer' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_Majority + $mockDynamicExcpectedQuorumType = $mockQuorumType_NodeMajority + } + + It 'Should return the same values as passed as parameters' { + $getTargetResourceResult = Get-TargetResource @mockTestParameters + $getTargetResourceResult.IsSingleInstance | Should Be $mockTestParameters.IsSingleInstance + } + + It 'Should return the correct values' { + $getTargetResourceResult = Get-TargetResource @mockTestParameters + $getTargetResourceResult.Type | Should Be $mockQuorumType_NodeMajority + $getTargetResourceResult.Resource | Should BeNullorEmpty + } + } + } + + Context 'When desired state should be NodeAndDiskMajority' { + Context 'When target node is Windows Server 2012 R2' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_NodeAndDiskMajority + } + + It 'Should return the same values as passed as parameters' { + $getTargetResourceResult = Get-TargetResource @mockTestParameters + $getTargetResourceResult.IsSingleInstance | Should Be $mockTestParameters.IsSingleInstance + } + + It 'Should return the correct values' { + $getTargetResourceResult = Get-TargetResource @mockTestParameters + $getTargetResourceResult.Type | Should Be $mockQuorumType_NodeAndDiskMajority + $getTargetResourceResult.Resource | Should Be $mockQuorumResourceName + } + } + + Context 'When target node is Windows Server 2016 and newer' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_Majority + $mockDynamicExcpectedQuorumType = $mockQuorumType_NodeAndDiskMajority + } + + It 'Should return the same values as passed as parameters' { + $getTargetResourceResult = Get-TargetResource @mockTestParameters + $getTargetResourceResult.IsSingleInstance | Should Be $mockTestParameters.IsSingleInstance + } + + It 'Should return the correct values' { + $getTargetResourceResult = Get-TargetResource @mockTestParameters + $getTargetResourceResult.Type | Should Be $mockQuorumType_NodeAndDiskMajority + $getTargetResourceResult.Resource | Should Be $mockQuorumResourceName + } + } + } + + Context 'When desired state should be NodeAndFileShareMajority' { + Context 'When target node is Windows Server 2012 R2' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_NodeAndFileShareMajority + } + + It 'Should return the same values as passed as parameters' { + $getTargetResourceResult = Get-TargetResource @mockTestParameters + $getTargetResourceResult.IsSingleInstance | Should Be $mockTestParameters.IsSingleInstance + } + + It 'Should return the correct values' { + $getTargetResourceResult = Get-TargetResource @mockTestParameters + $getTargetResourceResult.Type | Should Be $mockQuorumType_NodeAndFileShareMajority + $getTargetResourceResult.Resource | Should Be $mockQuorumFileShareWitnessPath + } + } + + Context 'When target node is Windows Server 2016 and newer' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_Majority + $mockDynamicExcpectedQuorumType = $mockQuorumType_NodeAndFileShareMajority + } + + It 'Should return the same values as passed as parameters' { + $getTargetResourceResult = Get-TargetResource @mockTestParameters + $getTargetResourceResult.IsSingleInstance | Should Be $mockTestParameters.IsSingleInstance + } + + It 'Should return the correct values' { + $getTargetResourceResult = Get-TargetResource @mockTestParameters + $getTargetResourceResult.Type | Should Be $mockQuorumType_NodeAndFileShareMajority + $getTargetResourceResult.Resource | Should Be $mockQuorumFileShareWitnessPath + } + } + } + + Context 'When desired state should be DiskOnly' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_DiskOnly + } + + It 'Should return the same values as passed as parameters' { + $getTargetResourceResult = Get-TargetResource @mockTestParameters + $getTargetResourceResult.IsSingleInstance | Should Be $mockTestParameters.IsSingleInstance + } + + It 'Should return the correct values' { + $getTargetResourceResult = Get-TargetResource @mockTestParameters + $getTargetResourceResult.Type | Should Be $mockQuorumType_DiskOnly + $getTargetResourceResult.Resource | Should Be $mockQuorumResourceName + } + } + + Context 'When quorum type is unknown' { + Context 'When target node is Windows Server 2012 R2' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_Unknown + } + + It 'Should throw the correct error message' { + { Get-TargetResource @mockTestParameters } | Should Throw ('Unknown quorum type: {0}' -f $mockQuorumType_Unknown) + } + } + + Context 'When target node is Windows Server 2016 and newer' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_Majority + $mockDynamicExcpectedQuorumType = $mockQuorumType_Unknown + } + + It 'Should throw the correct error message' { + { Get-TargetResource @mockTestParameters } | Should Throw ('Unknown quorum resource: {0}' -f '@{Name=Witness; OwnerGroup=Cluster Group; ResourceType=}') + } + } + } + } + } + + Describe 'xClusterQuorum\Test-TargetResource' { + BeforeEach { + Mock -CommandName 'Get-ClusterQuorum' -MockWith $mockGetClusterQuorum + Mock -CommandName 'Get-ClusterParameter' -MockWith $mockGetClusterParameter -ParameterFilter $mockGetClusterParameter_ParameterFilter + + $mockTestParameters = $mockDefaultParameters.Clone() + } + + Context 'When the system is not in the desired state' { + Context 'When quorum type should be NodeMajority' { + BeforeEach { + $mockTestParameters['Type'] = $mockQuorumType_NodeMajority + $mockTestParameters['Resource'] = $mockQuorumResourceName + } + + Context 'When target node is Windows Server 2012 R2' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_NodeAndDiskMajority + $mockDynamicQuorumResourceName = $mockQuorumResourceName + } + + It 'Should return the value $false' { + $testTargetResourceResult = Test-TargetResource @mockTestParameters + $testTargetResourceResult | Should Be $false + } + } + + Context 'When target node is Windows Server 2016 and newer' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_Majority + $mockDynamicQuorumResourceName = $mockQuorumResourceName + + $mockDynamicExcpectedQuorumType = $mockQuorumType_NodeAndDiskMajority + } + + It 'Should return the value $false' { + $testTargetResourceResult = Test-TargetResource @mockTestParameters + $testTargetResourceResult | Should Be $false + } + } + } + + Context 'When quorum type is NodeMajority but the resource is not in desired state' { + BeforeEach { + $mockTestParameters['Type'] = $mockQuorumType_NodeMajority + $mockTestParameters['Resource'] = $mockQuorumResourceName + } + + Context 'When target node is Windows Server 2012 R2' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_NodeMajority + $mockDynamicQuorumResourceName = 'Unknown' + } + + It 'Should return the value $false' { + $testTargetResourceResult = Test-TargetResource @mockTestParameters + $testTargetResourceResult | Should Be $false + } + } + + Context 'When target node is Windows Server 2016 and newer' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_Majority + + $mockDynamicExcpectedQuorumType = $mockQuorumType_NodeMajority + } + + It 'Should return the value $false' { + $testTargetResourceResult = Test-TargetResource @mockTestParameters + $testTargetResourceResult | Should Be $false + } + } + } + + Context 'When desired state should be NodeAndDiskMajority' { + Context 'When target node is Windows Server 2012 R2' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_NodeAndDiskMajority + } + + It 'Should return the value $false' { + $testTargetResourceResult = Test-TargetResource @mockTestParameters + $testTargetResourceResult | Should Be $false + } + } + + Context 'When target node is Windows Server 2016 and newer' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_Majority + $mockDynamicExcpectedQuorumType = $mockQuorumType_NodeAndDiskMajority + } + + It 'Should return the value $false' { + $testTargetResourceResult = Test-TargetResource @mockTestParameters + $testTargetResourceResult | Should Be $false + } + } + } + + Context 'When desired state should be NodeAndFileShareMajority' { + Context 'When target node is Windows Server 2012 R2' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_NodeAndFileShareMajority + } + + It 'Should return the value $false' { + $testTargetResourceResult = Test-TargetResource @mockTestParameters + $testTargetResourceResult | Should Be $false + } + } + + Context 'When target node is Windows Server 2016 and newer' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_Majority + $mockDynamicExcpectedQuorumType = $mockQuorumType_NodeAndFileShareMajority + } + + It 'Should return the value $false' { + $testTargetResourceResult = Test-TargetResource @mockTestParameters + $testTargetResourceResult | Should Be $false + } + } + } + + Context 'When desired state should be DiskOnly' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_DiskOnly + } + + It 'Should return the value $false' { + $testTargetResourceResult = Test-TargetResource @mockTestParameters + $testTargetResourceResult | Should Be $false + } + } + } + + Context 'When the system is in the desired state' { + Context 'When quorum type is NodeMajority but the resource is not in desired state' { + BeforeEach { + $mockTestParameters['Type'] = $mockQuorumType_NodeMajority + $mockTestParameters['Resource'] = $mockQuorumResourceName + } + + Context 'When target node is Windows Server 2012 R2' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_NodeMajority + $mockDynamicQuorumResourceName = $mockQuorumResourceName + } + + It 'Should return the value $true' { + $testTargetResourceResult = Test-TargetResource @mockTestParameters + $testTargetResourceResult | Should Be $true + } + } + + Context 'When target node is Windows Server 2016 and newer' { + BeforeEach { + $mockDynamicQuorumType = $mockQuorumType_Majority + $mockTestParameters['Resource'] = $null + + $mockDynamicExcpectedQuorumType = $mockQuorumType_NodeMajority + } + + It 'Should return the value $true' { + $testTargetResourceResult = Test-TargetResource @mockTestParameters + $testTargetResourceResult | Should Be $true + } + } + } + } + } + + Describe 'xClusterQuorum\Set-TargetResource' { + BeforeEach { + $mockTestParameters = $mockDefaultParameters.Clone() + } + + Context 'When quorum type should be NodeMajority' { + BeforeEach { + Mock -CommandName 'Set-ClusterQuorum' -MockWith $mockSetClusterQuorum ` + -ParameterFilter $mockSetClusterQuorum_NoWitness_ParameterFilter + + $mockTestParameters['Type'] = $mockQuorumType_NodeMajority + + $mockDynamicSetClusterQuorum_ExcpectedQuorumType = $mockQuorumType_NodeMajority + } + + It 'Should set the quorum in the cluster without throwing an error' { + { Set-TargetResource @mockTestParameters } | Should Not Throw + + Assert-MockCalled -CommandName 'Set-ClusterQuorum' ` + -ParameterFilter $mockSetClusterQuorum_NoWitness_ParameterFilter ` + -Exactly -Times 1 -Scope It + } + } + + Context 'When quorum type should be NodeMajority' { + BeforeEach { + Mock -CommandName 'Set-ClusterQuorum' -MockWith $mockSetClusterQuorum ` + -ParameterFilter $mockSetClusterQuorum_DiskWitness_ParameterFilter + + $mockTestParameters['Type'] = $mockQuorumType_NodeAndDiskMajority + $mockTestParameters['Resource'] = $mockQuorumResourceName + + $mockDynamicQuorumResourceName = $mockQuorumResourceName + $mockDynamicSetClusterQuorum_ExcpectedQuorumType = $mockQuorumType_NodeAndDiskMajority + } + + It 'Should set the quorum in the cluster without throwing an error' { + { Set-TargetResource @mockTestParameters } | Should Not Throw + + Assert-MockCalled -CommandName 'Set-ClusterQuorum' ` + -ParameterFilter $mockSetClusterQuorum_DiskWitness_ParameterFilter ` + -Exactly -Times 1 -Scope It + } + } + + Context 'When quorum type should be NodeMajority' { + BeforeEach { + Mock -CommandName 'Set-ClusterQuorum' -MockWith $mockSetClusterQuorum ` + -ParameterFilter $mockSetClusterQuorum_FileShareWitness_ParameterFilter + + $mockTestParameters['Type'] = $mockQuorumType_NodeAndFileShareMajority + $mockTestParameters['Resource'] = $mockQuorumResourceName + + $mockDynamicQuorumResourceName = $mockQuorumResourceName + $mockDynamicSetClusterQuorum_ExcpectedQuorumType = $mockQuorumType_NodeAndFileShareMajority + } + + It 'Should set the quorum in the cluster without throwing an error' { + { Set-TargetResource @mockTestParameters } | Should Not Throw + + Assert-MockCalled -CommandName 'Set-ClusterQuorum' ` + -ParameterFilter $mockSetClusterQuorum_FileShareWitness_ParameterFilter ` + -Exactly -Times 1 -Scope It + } + } + + Context 'When quorum type should be NodeMajority' { + BeforeEach { + Mock -CommandName 'Set-ClusterQuorum' -MockWith $mockSetClusterQuorum ` + -ParameterFilter $mockSetClusterQuorum_DiskOnly_ParameterFilter + + $mockTestParameters['Type'] = $mockQuorumType_DiskOnly + $mockTestParameters['Resource'] = $mockQuorumResourceName + + $mockDynamicQuorumResourceName = $mockQuorumResourceName + $mockDynamicSetClusterQuorum_ExcpectedQuorumType = $mockQuorumType_DiskOnly + } + + It 'Should set the quorum in the cluster without throwing an error' { + { Set-TargetResource @mockTestParameters } | Should Not Throw + + Assert-MockCalled -CommandName 'Set-ClusterQuorum' ` + -ParameterFilter $mockSetClusterQuorum_DiskOnly_ParameterFilter ` + -Exactly -Times 1 -Scope It + } + } + } + } +} +finally +{ + Invoke-TestCleanup +} From 6bb6d54a50fbb50f88b1210d790779e1c6e88da3 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 23 Jul 2017 12:58:34 +0200 Subject: [PATCH 07/19] xFailOverCluster: Fix typos in CHANGELOG.md (#123) - Changes to xFailOverCluster - Fix typos in CHANGELOG.md. --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01d1926..11d843e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,8 @@ - Changes to xCluster - Resolved Script Analyzer rule warnings by changing Get-WmiObject to Get-CimInstance ([issue #49](https://github.com/PowerShell/xFailOverCluster/issues/49)). - Minor style change in tests. Removed '-' infront of '-Be', '-Not', '-Throw', etc. + - Minor style change in tests. Removed '-' in front of '-Be', '-Not', '-Throw', + etc. - Changes to xWaitForCluster - Refactored the unit test for this resource to use stubs and increase coverage ([issue #78](https://github.com/PowerShell/xFailOverCluster/issues/78)). From 8cd78ce7e5d10678339c0fd9075249726898fc74 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 23 Jul 2017 13:02:18 +0200 Subject: [PATCH 08/19] xFailOverCluster: Opt-in for script files common tests (#122) - Opt-in for script files common tests (issue #121). - Removed Byte Order Mark (BOM) from the files; CommonResourceHelper.Tests.ps1, MSFT\_xCluster.Tests.ps1, MSFT\_xClusterDisk.Tests.ps1, MSFT\_xClusterPreferredOwner.Tests.ps1. --- .MetaTestOptIn.json | 3 ++- CHANGELOG.md | 4 ++++ Tests/Unit/CommonResourceHelper.Tests.ps1 | 2 +- Tests/Unit/MSFT_xCluster.Tests.ps1 | 2 +- Tests/Unit/MSFT_xClusterDisk.Tests.ps1 | 2 +- Tests/Unit/MSFT_xClusterPreferredOwner.Tests.ps1 | 2 +- 6 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.MetaTestOptIn.json b/.MetaTestOptIn.json index 0897a45..8955237 100644 --- a/.MetaTestOptIn.json +++ b/.MetaTestOptIn.json @@ -1,5 +1,6 @@ [ "Common Tests - Validate Example Files", "Common Tests - Validate Markdown Files", - "Common Tests - Validate Module Files" + "Common Tests - Validate Module Files", + "Common Tests - Validate Script Files" ] diff --git a/CHANGELOG.md b/CHANGELOG.md index 11d843e..a234b0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ - Fixed lint error MD034 and fixed typos in README.md. - Opt-in for module files common tests ([issue #119](https://github.com/PowerShell/xFailOverCluster/issues/119)). - Removed Byte Order Mark (BOM) from the files; CommonResourceHelper.psm1 and FailoverClusters.stubs.psm1. + - Opt-in for script files common tests ([issue #121](https://github.com/PowerShell/xFailOverCluster/issues/121)). + - Removed Byte Order Mark (BOM) from the files; CommonResourceHelper.Tests.ps1, + MSFT\_xCluster.Tests.ps1, MSFT\_xClusterDisk.Tests.ps1, + MSFT\_xClusterPreferredOwner.Tests.ps1. - Changes to xClusterDisk - Enabled localization for all strings ([issue #84](https://github.com/PowerShell/xFailOverCluster/issues/84)). - Fixed the OutputType data type that was not fully qualified. diff --git a/Tests/Unit/CommonResourceHelper.Tests.ps1 b/Tests/Unit/CommonResourceHelper.Tests.ps1 index 7df82a7..177eefc 100644 --- a/Tests/Unit/CommonResourceHelper.Tests.ps1 +++ b/Tests/Unit/CommonResourceHelper.Tests.ps1 @@ -1,4 +1,4 @@ -Describe 'CommonResourceHelper Unit Tests' { +Describe 'CommonResourceHelper Unit Tests' { BeforeAll { # Import the CommonResourceHelper module to test $dscResourcesFolderFilePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) ` diff --git a/Tests/Unit/MSFT_xCluster.Tests.ps1 b/Tests/Unit/MSFT_xCluster.Tests.ps1 index db306bc..28b51ba 100644 --- a/Tests/Unit/MSFT_xCluster.Tests.ps1 +++ b/Tests/Unit/MSFT_xCluster.Tests.ps1 @@ -1,4 +1,4 @@ -$script:DSCModuleName = 'xFailOverCluster' +$script:DSCModuleName = 'xFailOverCluster' $script:DSCResourceName = 'MSFT_xCluster' #region HEADER diff --git a/Tests/Unit/MSFT_xClusterDisk.Tests.ps1 b/Tests/Unit/MSFT_xClusterDisk.Tests.ps1 index 12c9d28..faa8806 100644 --- a/Tests/Unit/MSFT_xClusterDisk.Tests.ps1 +++ b/Tests/Unit/MSFT_xClusterDisk.Tests.ps1 @@ -1,4 +1,4 @@ -$script:DSCModuleName = 'xFailOverCluster' +$script:DSCModuleName = 'xFailOverCluster' $script:DSCResourceName = 'MSFT_xClusterDisk' #region Header diff --git a/Tests/Unit/MSFT_xClusterPreferredOwner.Tests.ps1 b/Tests/Unit/MSFT_xClusterPreferredOwner.Tests.ps1 index 3543b73..f469b64 100644 --- a/Tests/Unit/MSFT_xClusterPreferredOwner.Tests.ps1 +++ b/Tests/Unit/MSFT_xClusterPreferredOwner.Tests.ps1 @@ -1,4 +1,4 @@ -$script:DSCModuleName = 'xFailOverCluster' +$script:DSCModuleName = 'xFailOverCluster' $script:DSCResourceName = 'MSFT_xClusterPreferredOwner' #region Header From 4e977ade5c7023507f605b8b35d6e7cef9fb8c8c Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 23 Jul 2017 20:45:09 +0200 Subject: [PATCH 09/19] xFailoverCluster: Removed Byte Order Mark (BOM) from script file (#130) - Changes to xFailoverCluster - Removed Byte Order Mark (BOM) from the file; MSFT_xWaitForCluster.Tests.ps1. --- CHANGELOG.md | 2 +- Tests/Unit/MSFT_xWaitForCluster.Tests.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a234b0f..4b777f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ - Opt-in for script files common tests ([issue #121](https://github.com/PowerShell/xFailOverCluster/issues/121)). - Removed Byte Order Mark (BOM) from the files; CommonResourceHelper.Tests.ps1, MSFT\_xCluster.Tests.ps1, MSFT\_xClusterDisk.Tests.ps1, - MSFT\_xClusterPreferredOwner.Tests.ps1. + MSFT\_xClusterPreferredOwner.Tests.ps1, MSFT_xWaitForCluster.Tests.ps1. - Changes to xClusterDisk - Enabled localization for all strings ([issue #84](https://github.com/PowerShell/xFailOverCluster/issues/84)). - Fixed the OutputType data type that was not fully qualified. diff --git a/Tests/Unit/MSFT_xWaitForCluster.Tests.ps1 b/Tests/Unit/MSFT_xWaitForCluster.Tests.ps1 index 1feaf7e..6e55407 100644 --- a/Tests/Unit/MSFT_xWaitForCluster.Tests.ps1 +++ b/Tests/Unit/MSFT_xWaitForCluster.Tests.ps1 @@ -1,4 +1,4 @@ -$script:DSCModuleName = 'xFailOverCluster' +$script:DSCModuleName = 'xFailOverCluster' $script:DSCResourceName = 'MSFT_xWaitForCluster' #region HEADER From 64b42cad54becf09c589a80b817fea3073ee164d Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Wed, 26 Jul 2017 09:59:04 +0200 Subject: [PATCH 10/19] xClusterPreferredOwner: Enabled localization (#117) - Changes to xClusterPreferredOwner - Enabled localization for all strings (issue #86). - Fixed typo in the returned hash table from Get-TargetResource. --- CHANGELOG.md | 3 ++ .../MSFT_xClusterPreferredOwner.psm1 | 44 ++++++++++--------- .../MSFT_xClusterPreferredOwner.strings.psd1 | 15 +++++++ 3 files changed, 42 insertions(+), 20 deletions(-) create mode 100644 DSCResources/MSFT_xClusterPreferredOwner/en-US/MSFT_xClusterPreferredOwner.strings.psd1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b777f6..09c46a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,9 @@ - 3-SetQuorumToNodeAndFileShareMajority.ps1 - 4-SetQuorumToDiskOnly.ps1 - Added links to examples from README.md. +- Changes to xClusterPreferredOwner + - Enabled localization for all strings ([issue #86](https://github.com/PowerShell/xFailOverCluster/issues/86)). + - Fixed typo in the returned hash table from Get-TargetResource. ## 1.7.0.0 diff --git a/DSCResources/MSFT_xClusterPreferredOwner/MSFT_xClusterPreferredOwner.psm1 b/DSCResources/MSFT_xClusterPreferredOwner/MSFT_xClusterPreferredOwner.psm1 index 4dd9f2a..e83e311 100644 --- a/DSCResources/MSFT_xClusterPreferredOwner/MSFT_xClusterPreferredOwner.psm1 +++ b/DSCResources/MSFT_xClusterPreferredOwner/MSFT_xClusterPreferredOwner.psm1 @@ -1,3 +1,8 @@ +Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) ` + -ChildPath 'CommonResourceHelper.psm1') + +$script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xClusterPreferredOwner' + <# .SYNOPSIS Returns the current state of the failover cluster group and cluster @@ -46,11 +51,10 @@ function Get-TargetResource $Ensure = 'Present' ) - Write-Verbose -Message "Retrieving Owner information for cluster $ClusterName..." + Write-Verbose -Message ($script:localizedData.GetOwnerInformationForCluster -f $ClusterName) $ownerNodes = @( - - Write-Verbose -Message "Retrieving Owner information for Cluster Group $ClusterGroup" + Write-Verbose -Message ($script:localizedData.GetOwnerInformationForClusterGroup -f $ClusterGroup) (((Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript { $_.Name -like $ClusterGroup } | Get-ClusterOwnerNode).OwnerNodes).Name @@ -59,7 +63,7 @@ function Get-TargetResource { foreach ($resource in $ClusterResources) { - Write-Verbose -Message "Retrieving Owner information for Cluster Resource $resource" + Write-Verbose -Message ($script:localizedData.GetOwnerInformationForClusterResource -f $resource) (((Get-ClusterResource -Cluster $ClusterName) | Where-Object -FilterScript { $_.Name -like $resource } | Get-ClusterOwnerNode).OwnerNodes).Name @@ -71,7 +75,7 @@ function Get-TargetResource @{ ClusterGroup = $ClusterGroup - Clustername = $ClusterName + ClusterName = $ClusterName Nodes = $ownerNodes ClusterResources = $ClusterResources Ensure = $Ensure @@ -125,12 +129,12 @@ function Set-TargetResource $Ensure = 'Present' ) - Write-Verbose -Message "Retrieving all owners from cluster $ClusterName" + Write-Verbose -Message ($script:localizedData.GetAllNodesOfCluster -f $ClusterName) $allNodes = (Get-ClusterNode -Cluster $ClusterName).Name if ($Ensure -eq 'Present') { - Write-Verbose -Message "Setting Cluster owners for Group $ClusterGroup to $Nodes" + Write-Verbose -Message ($script:localizedData.SetOwnerForClusterGroup -f $ClusterGroup, $Nodes) $null = (Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript { $_.Name -like $ClusterGroup } | Set-ClusterOwnerNode -Owners $Nodes @@ -139,14 +143,14 @@ function Set-TargetResource $_.OwnerGroup -like $ClusterGroup } | Set-ClusterOwnerNode -Owners $allNodes - Write-Verbose -Message "Moving Cluster Group $ClusterGroup to node $($Nodes[0])" + Write-Verbose -Message ($script:localizedData.MoveClusterGroup -f $ClusterGroup, $Nodes[0]) $null = (Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript { $_.name -like $ClusterGroup } | Move-ClusterGroup -Node $Nodes[0] foreach ($resource in $ClusterResources) { - Write-Verbose -Message "Setting Cluster owners for Resource $resource to $Nodes" + Write-Verbose -Message ($script:localizedData.SetOwnerForClusterResource -f $resource, $Nodes) $null = (Get-ClusterResource -Cluster $ClusterName) | Where-Object -FilterScript { $_.Name -like $resource } | Set-ClusterOwnerNode -Owners $Nodes @@ -155,7 +159,7 @@ function Set-TargetResource if ($Ensure -eq 'Absent') { - Write-Verbose -Message "Retrieving current cluster owners for group $ClusterGroup" + Write-Verbose -Message ($script:localizedData.GetOwnerInformationForClusterGroup -f $ClusterGroup) $currentOwners = (((Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript { $_.Name -like $ClusterGroup } | Get-ClusterOwnerNode).OwnerNodes).Name | Sort-Object -Unique @@ -170,24 +174,24 @@ function Set-TargetResource } ) - Write-Verbose -Message "Removing owners from group $($ClusterGroup): $Nodes" + Write-Verbose -Message ($script:localizedData.RemoveOwnerFromClusterGroup -f $ClusterGroup, $Nodes) $null = (Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript { $_.Name -like $ClusterGroup } | Set-ClusterOwnerNode $newOwners - Write-Verbose -Message "Setting Cluster owners for Group $ClusterGroup to $newOwners" + Write-Verbose -Message ($script:localizedData.SetOwnerForClusterGroup -f $ClusterGroup, $newOwners) $null = (Get-ClusterResource) | Where-Object -FilterScript { $_.OwnerGroup -like $ClusterGroup } | Set-ClusterOwnerNode $allNodes - Write-Verbose -Message "Moving Cluster Group $ClusterGroup to node $($newOwners[0])" + Write-Verbose -Message ($script:localizedData.MoveClusterGroup -f $ClusterGroup, $newOwners[0]) $null = (Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript { $_.Name -like $ClusterGroup } | Move-ClusterGroup -Node $newOwners[0] foreach ($resource in $ClusterResources) { - Write-Verbose -Message "Retrieving current cluster owners for resource $resource" + Write-Verbose -Message ($script:localizedData.GetOwnerInformationForClusterResource -f $resource) $currentOwners = ((Get-ClusterResource -Cluster $ClusterName | Where-Object -FilterScript { $_.Name -like $resource } | Get-ClusterOwnerNode).OwnerNodes).Name | Sort-Object -Unique @@ -202,7 +206,7 @@ function Set-TargetResource } ) - Write-Verbose -Message "Setting Cluster owners for Resource $resource to $newOwners" + Write-Verbose -Message ($script:localizedData.SetOwnerForClusterResource -f $resource, $newOwners) $null = Get-ClusterResource -Cluster $ClusterName | Where-Object -FilterScript { $_.Name -like $resource } | Set-ClusterOwnerNode -Owners $newOwners @@ -259,7 +263,7 @@ function Test-TargetResource $Ensure = 'Present' ) - Write-Verbose -Message "Testing Owner information for cluster $ClusterName..." + Write-Verbose -Message ($script:localizedData.TestOwnerInformationForCluster -f $ClusterName) $getTargetResourceResult = (Get-TargetResource @PSBoundParameters).Nodes $result = $true @@ -270,7 +274,7 @@ function Test-TargetResource { if ($Nodes -notcontains $object) { - Write-Verbose -Message "$object was NOT found as possible owner" + Write-Verbose -Message ($script:localizedData.WasNotFoundAsPossibleOwner -f $object) $result = $false } } @@ -279,7 +283,7 @@ function Test-TargetResource { if ($getTargetResourceResult -notcontains $object) { - Write-Verbose -Message "$object was NOT found as possible owner" + Write-Verbose -Message ($script:localizedData.WasNotFoundAsPossibleOwner -f $object) $result = $false } } @@ -291,7 +295,7 @@ function Test-TargetResource { if ($Nodes -contains $object) { - Write-Verbose -Message "$object WAS found as possible owner" + Write-Verbose -Message ($script:localizedData.WasFoundAsPossibleOwner -f $object) $result = $false } } @@ -300,7 +304,7 @@ function Test-TargetResource { if ($getTargetResourceResult -contains $object) { - Write-Verbose -Message "$object WAS found as possible owner" + Write-Verbose -Message ($script:localizedData.WasFoundAsPossibleOwner -f $object) $result = $false } } diff --git a/DSCResources/MSFT_xClusterPreferredOwner/en-US/MSFT_xClusterPreferredOwner.strings.psd1 b/DSCResources/MSFT_xClusterPreferredOwner/en-US/MSFT_xClusterPreferredOwner.strings.psd1 new file mode 100644 index 0000000..c481da3 --- /dev/null +++ b/DSCResources/MSFT_xClusterPreferredOwner/en-US/MSFT_xClusterPreferredOwner.strings.psd1 @@ -0,0 +1,15 @@ +# Localized resources for xClusterPreferredOwner + +ConvertFrom-StringData @' + GetOwnerInformationForCluster = Retrieving owner information for cluster {0}. + GetOwnerInformationForClusterGroup = Retrieving owner information for cluster role/group {0}. + GetOwnerInformationForClusterResource = Retrieving owner information for cluster resource {0}. + GetAllNodesOfCluster = Retrieving all nodes from cluster {0}. + SetOwnerForClusterGroup = Setting owners for cluster role/group {0} to {1}. + SetOwnerForClusterResource = Setting owners for cluster resource {0} to {1}. + MoveClusterGroup = Moving cluster role/group {0} to node {1}. + RemoveOwnerFromClusterGroup = Removing owners {1} from cluster role/group {0}. + TestOwnerInformationForCluster = Testing Owner information for cluster {0}. + WasFoundAsPossibleOwner = {0} was found as possible owner. + WasNotFoundAsPossibleOwner = {0} was NOT found as possible owner. +'@ From 657062a6e6d57653dca305a7d7cfc3e8dba685ed Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Wed, 26 Jul 2017 10:00:39 +0200 Subject: [PATCH 11/19] xClusterNetwork: Enabled localization (#116) - Changes to xClusterNetwork - Enabled localization for all strings (issue #85). - Fixed typo in parameter descriptions in comment-based help, and schema.mof. --- CHANGELOG.md | 3 ++- .../MSFT_xClusterNetwork.psm1 | 23 +++++++++++-------- .../MSFT_xClusterNetwork.schema.mof | 4 ++-- .../en-US/MSFT_xClusterNetwork.strings.psd1 | 7 ++++++ 4 files changed, 25 insertions(+), 12 deletions(-) create mode 100644 DSCResources/MSFT_xClusterNetwork/en-US/MSFT_xClusterNetwork.strings.psd1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 09c46a0..7fdaced 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,8 @@ - Replaced the URL for the parameter Role in README.md. The new URL is a more generic description of the possible settings for the Role parameter. The previous URL was still correct but focused on Hyper-V in particular. - - Fixed typos in parameter descriptions in README.md. + - Fixed typos in parameter descriptions in README.md, comment-based help and schema.mof. + - Enabled localization for all strings ([issue #85](https://github.com/PowerShell/xFailOverCluster/issues/85)). - Changes to xCluster - Resolved Script Analyzer rule warnings by changing Get-WmiObject to Get-CimInstance ([issue #49](https://github.com/PowerShell/xFailOverCluster/issues/49)). diff --git a/DSCResources/MSFT_xClusterNetwork/MSFT_xClusterNetwork.psm1 b/DSCResources/MSFT_xClusterNetwork/MSFT_xClusterNetwork.psm1 index fd43531..30eb57e 100644 --- a/DSCResources/MSFT_xClusterNetwork/MSFT_xClusterNetwork.psm1 +++ b/DSCResources/MSFT_xClusterNetwork/MSFT_xClusterNetwork.psm1 @@ -1,12 +1,17 @@ +Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) ` + -ChildPath 'CommonResourceHelper.psm1') + +$script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xClusterNetwork' + <# .SYNOPSIS Returns the current state of the failover cluster network resource. .PARAMETER Address - The adress for the cluster network in the format '10.0.0.0'. + The address for the cluster network in the format '10.0.0.0'. .PARAMETER AddressMask - The adress mask for the cluster network in the format '255.255.255.0'. + The address mask for the cluster network in the format '255.255.255.0'. #> function Get-TargetResource { @@ -42,10 +47,10 @@ function Get-TargetResource network resource. .PARAMETER Address - The adress for the cluster network in the format '10.0.0.0'. + The address for the cluster network in the format '10.0.0.0'. .PARAMETER AddressMask - The adress mask for the cluster network in the format '255.255.255.0'. + The address mask for the cluster network in the format '255.255.255.0'. .PARAMETER Name The name of the cluster network. If the cluster network name is not in @@ -100,7 +105,7 @@ function Set-TargetResource if ($PSBoundParameters.ContainsKey('Name') -and $getTargetResourceResult.Name -ne $Name) { - Write-Verbose "Changing the name of network $Address/$AddressMask to '$Name'" + Write-Verbose -Message ($script:localizedData.ChangeNetworkName -f $Address, $AddressMask, $Name) $clusterNetworkResource = Get-ClusterNetwork | Where-Object -FilterScript { $_.Address -eq $Address -and $_.AddressMask -eq $AddressMask @@ -111,7 +116,7 @@ function Set-TargetResource if ($PSBoundParameters.ContainsKey('Role') -and $getTargetResourceResult.Role -ne $Role) { - Write-Verbose "Changing the role of network $Address/$AddressMask to '$Role'" + Write-Verbose -Message ($script:localizedData.ChangeNetworkRole -f $Address, $AddressMask, $Role) $clusterNetworkResource = Get-ClusterNetwork | Where-Object -FilterScript { $_.Address -eq $Address -and $_.AddressMask -eq $AddressMask @@ -122,7 +127,7 @@ function Set-TargetResource if ($PSBoundParameters.ContainsKey('Metric') -and $getTargetResourceResult.Metric -ne $Metric) { - Write-Verbose "Changing the metric of network $Address/$AddressMask to '$Metric'" + Write-Verbose -Message ($script:localizedData.ChangeNetworkMetric -f $Address, $AddressMask, $Metric) $clusterNetworkResource = Get-ClusterNetwork | Where-Object -FilterScript { $_.Address -eq $Address -and $_.AddressMask -eq $AddressMask @@ -138,10 +143,10 @@ function Set-TargetResource values for the properties Name, Role and Metric. .PARAMETER Address - The adress for the cluster network in the format '10.0.0.0'. + The address for the cluster network in the format '10.0.0.0'. .PARAMETER AddressMask - The adress mask for the cluster network in the format '255.255.255.0'. + The address mask for the cluster network in the format '255.255.255.0'. .PARAMETER Name The name of the cluster network. If the cluster network name is not in diff --git a/DSCResources/MSFT_xClusterNetwork/MSFT_xClusterNetwork.schema.mof b/DSCResources/MSFT_xClusterNetwork/MSFT_xClusterNetwork.schema.mof index 3699b96..63945b7 100644 --- a/DSCResources/MSFT_xClusterNetwork/MSFT_xClusterNetwork.schema.mof +++ b/DSCResources/MSFT_xClusterNetwork/MSFT_xClusterNetwork.schema.mof @@ -1,8 +1,8 @@ [ClassVersion("1.0.0.0"), FriendlyName("xClusterNetwork")] class MSFT_xClusterNetwork : OMI_BaseResource { - [Key, Description("The adress for the cluster network in the format '10.0.0.0'.")] String Address; - [Key, Description("The adress mask for the cluster network in the format '255.255.255.0'.")] String AddressMask; + [Key, Description("The address for the cluster network in the format '10.0.0.0'.")] String Address; + [Key, Description("The address mask for the cluster network in the format '255.255.255.0'.")] String AddressMask; [Write, Description("The name of the cluster network. If the cluster network name is not in desired state it will be renamed to match this name.")] String Name; [Write, Description("he role of the cluster network. If the cluster network role is not in desired state it will change to match this role."), ValueMap{"0","1","3"}, Values{"0","1","3"}] String Role; [Write, Description("The metric number for the cluster network. If the cluster network metric number is not in desired state it will be changed to match this metric number.")] String Metric; diff --git a/DSCResources/MSFT_xClusterNetwork/en-US/MSFT_xClusterNetwork.strings.psd1 b/DSCResources/MSFT_xClusterNetwork/en-US/MSFT_xClusterNetwork.strings.psd1 new file mode 100644 index 0000000..6341410 --- /dev/null +++ b/DSCResources/MSFT_xClusterNetwork/en-US/MSFT_xClusterNetwork.strings.psd1 @@ -0,0 +1,7 @@ +# Localized resources for xClusterNetwork + +ConvertFrom-StringData @' + ChangeNetworkName = Changing the name of the network {0}/{1} to '{2}'. + ChangeNetworkRole = Changing the role of the network {0}/{1} to '{2}'. + ChangeNetworkMetric = Changing the metric of the network {0}/{1} to '{2}'. +'@ From adaa7acd4c20067d41af5f43d6ebcb7350a9ff91 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Wed, 26 Jul 2017 10:01:48 +0200 Subject: [PATCH 12/19] xCluster: Enabled localization (#118) - Changes to xFailOverCluster - Added common test helper functions to help test the throwing of localized error strings. - Get-InvalidArgumentRecord - Get-InvalidOperationRecord - Get-ObjectNotFoundException - Get-InvalidResultException. - Changes to xCluster - Enabled localization for all strings (issue #83). - Added tests to improve code coverage. --- CHANGELOG.md | 7 + DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 | 64 +++--- .../en-US/MSFT_xCluster.strings.psd1 | 21 ++ Tests/TestHelpers/CommonTestHelper.psm1 | 210 ++++++++++++++++++ Tests/Unit/MSFT_xCluster.Tests.ps1 | 85 ++++++- 5 files changed, 357 insertions(+), 30 deletions(-) create mode 100644 DSCResources/MSFT_xCluster/en-US/MSFT_xCluster.strings.psd1 create mode 100644 Tests/TestHelpers/CommonTestHelper.psm1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fdaced..2d920b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,11 @@ - Removed Byte Order Mark (BOM) from the files; CommonResourceHelper.Tests.ps1, MSFT\_xCluster.Tests.ps1, MSFT\_xClusterDisk.Tests.ps1, MSFT\_xClusterPreferredOwner.Tests.ps1, MSFT_xWaitForCluster.Tests.ps1. + - Added common test helper functions to help test the throwing of localized error strings. + - Get-InvalidArgumentRecord + - Get-InvalidOperationRecord + - Get-ObjectNotFoundException + - Get-InvalidResultException. - Changes to xClusterDisk - Enabled localization for all strings ([issue #84](https://github.com/PowerShell/xFailOverCluster/issues/84)). - Fixed the OutputType data type that was not fully qualified. @@ -28,6 +33,8 @@ Get-CimInstance ([issue #49](https://github.com/PowerShell/xFailOverCluster/issues/49)). - Minor style change in tests. Removed '-' in front of '-Be', '-Not', '-Throw', etc. + - Enabled localization for all strings ([issue #83](https://github.com/PowerShell/xFailOverCluster/issues/83)). + - Added tests to improve code coverage. - Changes to xWaitForCluster - Refactored the unit test for this resource to use stubs and increase coverage ([issue #78](https://github.com/PowerShell/xFailOverCluster/issues/78)). diff --git a/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 b/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 index 82f7984..711ee63 100644 --- a/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 +++ b/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 @@ -1,3 +1,8 @@ +Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) ` + -ChildPath 'CommonResourceHelper.psm1') + +$script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xCluster' + <# .SYNOPSIS Returns the current state of the failover cluster. @@ -32,7 +37,8 @@ function Get-TargetResource $computerInformation = Get-CimInstance -ClassName Win32_ComputerSystem if (($null -eq $computerInformation) -or ($null -eq $computerInformation.Domain)) { - throw 'Can''t find machine''s domain name' + $errorMessage = $script:localizedData.TargetNodeDomainMissing + New-InvalidOperationException -Message $errorMessage } try @@ -42,7 +48,8 @@ function Get-TargetResource $cluster = Get-Cluster -Name $Name -Domain $computerInformation.Domain if ($null -eq $cluster) { - throw "Can't find the cluster $Name" + $errorMessage = $script:localizedData.ClusterNameNotFound -f $Name + New-ObjectNotFoundException -Message $errorMessage } $address = Get-ClusterGroup -Cluster $Name -Name 'Cluster IP Address' | Get-ClusterParameter -Name 'Address' @@ -105,12 +112,13 @@ function Set-TargetResource $bCreate = $true - Write-Verbose -Message "Checking if Cluster $Name is present ..." + Write-Verbose -Message ($script:localizedData.CheckClusterPresent -f $Name) $computerInformation = Get-CimInstance -ClassName Win32_ComputerSystem if (($null -eq $computerInformation) -or ($null -eq $computerInformation.Domain)) { - throw 'Can''t find machine''s domain name' + $errorMessage = $script:localizedData.TargetNodeDomainMissing + New-InvalidOperationException -Message $errorMessage } try @@ -133,40 +141,41 @@ function Set-TargetResource if ($bCreate) { - Write-Verbose -Message "Cluster $Name is NOT present" + Write-Verbose -Message ($script:localizedData.ClusterAbsent -f $Name) New-Cluster -Name $Name -Node $env:COMPUTERNAME -StaticAddress $StaticIPAddress -NoStorage -Force -ErrorAction Stop if ( -not (Get-Cluster)) { - throw 'Cluster creation failed. Please verify output of ''Get-Cluster'' command' + $errorMessage = $script:localizedData.FailedCreatingCluster + New-InvalidOperationException -Message $errorMessage } - Write-Verbose -Message "Created Cluster $Name" + Write-Verbose -Message ($script:localizedData.ClusterCreated -f $Name) } else { - Write-Verbose -Message "Add node to Cluster $Name ..." + $targetNodeName = $env:COMPUTERNAME - Write-Verbose -Message "Add-ClusterNode $env:COMPUTERNAME to cluster $Name" + Write-Verbose -Message ($script:localizedData.AddNodeToCluster -f $targetNodeName, $Name) $list = Get-ClusterNode -Cluster $Name foreach ($node in $list) { - if ($node.Name -eq $env:COMPUTERNAME) + if ($node.Name -eq $targetNodeName) { if ($node.State -eq 'Down') { - Write-Verbose -Message "Node $env:COMPUTERNAME was down, need remove it from the list." + Write-Verbose -Message ($script:localizedData.RemoveOfflineNodeFromCluster -f $targetNodeName, $Name) - Remove-ClusterNode -Name $env:COMPUTERNAME -Cluster $Name -Force + Remove-ClusterNode -Name $targetNodeName -Cluster $Name -Force } } } - Add-ClusterNode -Name $env:COMPUTERNAME -Cluster $Name -NoStorage + Add-ClusterNode -Name $targetNodeName -Cluster $Name -NoStorage - Write-Verbose -Message "Added node to Cluster $Name" + Write-Verbose -Message ($script:localizedData.AddNodeToClusterSuccessful -f $targetNodeName, $Name) } } finally @@ -226,12 +235,13 @@ function Test-TargetResource $returnValue = $false - Write-Verbose -Message "Checking if Cluster $Name is present ..." + Write-Verbose -Message ($script:localizedData.CheckClusterPresent -f $Name) $ComputerInfo = Get-CimInstance -ClassName Win32_ComputerSystem if (($null -eq $ComputerInfo) -or ($null -eq $ComputerInfo.Domain)) { - throw "Can't find machine's domain name" + $errorMessage = $script:localizedData.TargetNodeDomainMissing + New-InvalidOperationException -Message $errorMessage } try @@ -240,17 +250,19 @@ function Test-TargetResource $cluster = Get-Cluster -Name $Name -Domain $ComputerInfo.Domain - Write-Verbose -Message "Cluster $Name is present" + Write-Verbose -Message ($script:localizedData.ClusterPresent -f $Name) if ($cluster) { - Write-Verbose -Message "Checking if the node is in cluster $Name ..." + $targetNodeName = $env:COMPUTERNAME + + Write-Verbose -Message ($script:localizedData.CheckClusterNodeIsUp -f $targetNodeName, $Name) $allNodes = Get-ClusterNode -Cluster $Name foreach ($node in $allNodes) { - if ($node.Name -eq $env:COMPUTERNAME) + if ($node.Name -eq $targetNodeName) { if ($node.State -eq 'Up') { @@ -258,7 +270,7 @@ function Test-TargetResource } else { - Write-Verbose -Message "Node is in cluster $Name but is NOT up, treat as NOT in cluster." + Write-Verbose -Message ($script:localizedData.ClusterNodeIsDown -f $targetNodeName, $Name) } break @@ -267,17 +279,17 @@ function Test-TargetResource if ($returnValue) { - Write-Verbose -Message "Node is in cluster $Name" + Write-Verbose -Message ($script:localizedData.ClusterNodePresent -f $targetNodeName, $Name) } else { - Write-Verbose -Message "Node is NOT in cluster $Name" + Write-Verbose -Message ($script:localizedData.ClusterNodeAbsent -f $targetNodeName, $Name) } } } catch { - Write-Verbose -Message "Cluster $Name is NOT present with Error $_.Message" + Write-Verbose -Message ($script:localizedData.ClusterAbsentWithError -f $Name, $_.Message) } finally { @@ -361,7 +373,8 @@ function Set-ImpersonateAs } else { - throw "Can't Logon as User $($Credential.GetNetworkCredential().UserName)." + $errorMessage = $script:localizedData.UnableToImpersonateUser -f $Credential.GetNetworkCredential().UserName + New-InvalidOperationException -Message $errorMessage } $context, $userToken @@ -392,6 +405,7 @@ function Close-UserToken $bLogin = $ImpersonateLib::CloseHandle($Token) if (-not $bLogin) { - throw 'Can''t close token' + $errorMessage = $script:localizedData.UnableToCloseToken -f $Token.ToString() + New-InvalidOperationException -Message $errorMessage } } diff --git a/DSCResources/MSFT_xCluster/en-US/MSFT_xCluster.strings.psd1 b/DSCResources/MSFT_xCluster/en-US/MSFT_xCluster.strings.psd1 new file mode 100644 index 0000000..d9f3bf1 --- /dev/null +++ b/DSCResources/MSFT_xCluster/en-US/MSFT_xCluster.strings.psd1 @@ -0,0 +1,21 @@ +# Localized resources for xCluster + +ConvertFrom-StringData @' + CheckClusterPresent = Checking if cluster {0} is present. + ClusterPresent = Cluster {0} is present. + ClusterAbsent = Cluster {0} is NOT present. + ClusterCreated = Created cluster {0}. + AddNodeToCluster = Adding node {0} to cluster {1}. + RemoveOfflineNodeFromCluster = Node {0} is down, need to remove it from the cluster {1}. + AddNodeToClusterSuccessful = Added node {0} to cluster {1}. + CheckClusterNodeIsUp = Checking if the node {0} is a member of the cluster {1}, and so that node status is 'Up'. + ClusterNodeIsDown = Node {0} is in the cluster {1} but the status is not 'Up'. Node will be treated as NOT being a member of the cluster {1}. + ClusterNodePresent = Cluster node {0} is a member of cluster {1}. + ClusterNodeAbsent = Cluster node {0} is NOT a member of cluster {1}. + ClusterAbsentWithError = Cluster {0} is NOT present with error: {1} + TargetNodeDomainMissing = Can't find the target node's domain name. + ClusterNameNotFound = Can't find the cluster {0}. + FailedCreatingCluster = Cluster creation failed. Please verify output of 'Get-Cluster' command. + UnableToImpersonateUser = Can't logon as user {0}. + UnableToCloseToken = Can't close impersonation token {0}. +'@ diff --git a/Tests/TestHelpers/CommonTestHelper.psm1 b/Tests/TestHelpers/CommonTestHelper.psm1 new file mode 100644 index 0000000..c37a0c2 --- /dev/null +++ b/Tests/TestHelpers/CommonTestHelper.psm1 @@ -0,0 +1,210 @@ +<# + .SYNOPSIS + Returns an invalid argument exception object + + .PARAMETER Message + The message explaining why this error is being thrown + + .PARAMETER ArgumentName + The name of the invalid argument that is causing this error to be thrown +#> +function Get-InvalidArgumentRecord +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ArgumentName + ) + + $argumentException = New-Object -TypeName 'ArgumentException' ` + -ArgumentList @( + $Message, + $ArgumentName + ) + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $argumentException, + $ArgumentName, + 'InvalidArgument', + $null + ) + } + + return New-Object @newObjectParameters +} + +<# + .SYNOPSIS + Returns an invalid operation exception object + + .PARAMETER Message + The message explaining why this error is being thrown + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error +#> +function Get-InvalidOperationRecord +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) + { + $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` + -ArgumentList @( $Message ) + } + else + { + $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` + -ArgumentList @( + $Message, + $ErrorRecord.Exception + ) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $invalidOperationException.ToString(), + 'MachineStateIncorrect', + 'InvalidOperation', + $null + ) + } + + return New-Object @newObjectParameters +} + +<# + .SYNOPSIS + Returns an object not found exception object + + .PARAMETER Message + The message explaining why this error is being thrown + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error +#> +function Get-ObjectNotFoundException +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) + { + $objectNotFoundException = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message) + } + else + { + $objectNotFoundException = New-Object -TypeName 'System.Exception' ` + -ArgumentList @( + $Message, + $ErrorRecord.Exception + ) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $objectNotFoundException.ToString(), + 'MachineStateIncorrect', + 'ObjectNotFound', + $null + ) + } + + return New-Object @newObjectParameters +} + +<# + .SYNOPSIS + Returns an invalid result exception object + + .PARAMETER Message + The message explaining why this error is being thrown + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error +#> +function Get-InvalidResultException +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message) + } + else + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @( + $Message, + $ErrorRecord.Exception + ) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $exception.ToString(), + 'MachineStateIncorrect', + 'InvalidResult', + $null + ) + } + + return New-Object @newObjectParameters +} + +Export-ModuleMember -Function @( + 'Get-InvalidArgumentRecord' + 'Get-InvalidOperationRecord' + 'Get-ObjectNotFoundException' + 'Get-InvalidResultException' +) diff --git a/Tests/Unit/MSFT_xCluster.Tests.ps1 b/Tests/Unit/MSFT_xCluster.Tests.ps1 index 28b51ba..dcd2da2 100644 --- a/Tests/Unit/MSFT_xCluster.Tests.ps1 +++ b/Tests/Unit/MSFT_xCluster.Tests.ps1 @@ -1,3 +1,10 @@ +<# + Suppressing this rule because a plain text password variable is used to mock the LogonUser static + method and is required for the tests. +#> +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')] +param() + $script:DSCModuleName = 'xFailOverCluster' $script:DSCResourceName = 'MSFT_xCluster' @@ -23,6 +30,7 @@ $TestEnvironment = Initialize-TestEnvironment ` function Invoke-TestSetup { Import-Module -Name (Join-Path -Path (Join-Path -Path (Join-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'Tests') -ChildPath 'Unit') -ChildPath 'Stubs') -ChildPath 'FailoverClusters.stubs.psm1') -Global -Force + Import-Module -Name (Join-Path -Path (Join-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'Tests') -ChildPath 'TestHelpers') -ChildPath 'CommonTestHelper.psm1') -Global -Force } function Invoke-TestCleanup @@ -102,7 +110,38 @@ try DomainAdministratorCredential = $mockAdministratorCredential } + class MockLibImpersonation + { + static [bool] $ReturnValue = $false + + static [bool]LogonUser( + [string] $userName, + [string] $domain, + [string] $password, + [int] $logonType, + [int] $logonProvider, + [ref] $token + ) + { + return [MockLibImpersonation]::ReturnValue + } + + static [bool]CloseHandle([System.IntPtr]$Token) + { + return [MockLibImpersonation]::ReturnValue + } + } + + [MockLibImpersonation]::ReturnValue = $true + $mockLibImpersonationObject = [MockLibImpersonation]::New() + Describe 'xCluster\Get-TargetResource' { + BeforeAll { + Mock -CommandName Add-Type -MockWith { + return $mockLibImpersonationObject + } + } + Context 'When the computers domain name cannot be evaluated' { It 'Should throw the correct error message' { $mockDynamicDomainName = $null @@ -110,7 +149,8 @@ try Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockGetCimInstance_ParameterFilter -Verifiable - { Get-TargetResource @mockDefaultParameters } | Should Throw "Can't find machine's domain name" + $mockCorrectErrorRecord = Get-InvalidOperationRecord -Message $script:localizedData.TargetNodeDomainMissing + { Get-TargetResource @mockDefaultParameters } | Should Throw $mockCorrectErrorRecord } } @@ -122,7 +162,8 @@ try Mock -CommandName Get-Cluster -Verifiable Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockGetCimInstance_ParameterFilter -Verifiable - { Get-TargetResource @mockDefaultParameters } | Should Throw ("Can't find the cluster {0}" -f $mockClusterName) + $mockCorrectErrorRecord = Get-ObjectNotFoundException -Message ($script:localizedData.ClusterNameNotFound -f $mockClusterName) + { Get-TargetResource @mockDefaultParameters } | Should Throw $mockCorrectErrorRecord } } @@ -160,7 +201,8 @@ try Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockGetCimInstance_ParameterFilter -Verifiable - { Set-TargetResource @mockDefaultParameters } | Should Throw "Can't find machine's domain name" + $mockCorrectErrorRecord = Get-InvalidOperationRecord -Message $script:localizedData.TargetNodeDomainMissing + { Set-TargetResource @mockDefaultParameters } | Should Throw $mockCorrectErrorRecord } } @@ -215,7 +257,8 @@ try It 'Should throw the correct error message' { Mock -CommandName Get-Cluster - { Set-TargetResource @mockDefaultParameters } | Should Throw 'Cluster creation failed. Please verify output of ''Get-Cluster'' command' + $mockCorrectErrorRecord = Get-InvalidOperationRecord -Message $script:localizedData.FailedCreatingCluster + { Set-TargetResource @mockDefaultParameters } | Should Throw $mockCorrectErrorRecord Assert-MockCalled -CommandName New-Cluster -Exactly -Times 1 -Scope It Assert-MockCalled -CommandName Remove-ClusterNode -Exactly -Times 0 -Scope It @@ -303,7 +346,8 @@ try Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockGetCimInstance_ParameterFilter -Verifiable - { Test-TargetResource @mockDefaultParameters } | Should Throw "Can't find machine's domain name" + $mockCorrectErrorRecord = Get-InvalidOperationRecord -Message $script:localizedData.TargetNodeDomainMissing + { Test-TargetResource @mockDefaultParameters } | Should Throw $mockCorrectErrorRecord } } @@ -392,6 +436,37 @@ try } } } + + [MockLibImpersonation]::ReturnValue = $false + $mockLibImpersonationObject = [MockLibImpersonation]::New() + + Describe 'xCluster\Set-ImpersonateAs' -Tag 'Helper' { + Context 'When impersonating credentials fails' { + It 'Should throw the correct error message' { + Mock -CommandName Add-Type -MockWith { + return $mockLibImpersonationObject + } + + $mockCorrectErrorRecord = Get-InvalidOperationRecord -Message ($script:localizedData.UnableToImpersonateUser -f $mockAdministratorCredential.GetNetworkCredential().UserName) + { Set-ImpersonateAs -Credential $mockAdministratorCredential } | Should Throw $mockCorrectErrorRecord + } + } + } + + Describe 'xCluster\Close-UserToken' -Tag 'Helper' { + Context 'When closing user token fails' { + It 'Should throw the correct error message' { + Mock -CommandName Add-Type -MockWith { + return $mockLibImpersonationObject + } -Verifiable + + $mockToken = [System.IntPtr]::New(12345) + + $mockCorrectErrorRecord = Get-InvalidOperationRecord -Message ($script:localizedData.UnableToCloseToken -f $mockToken.ToString()) + { Close-UserToken -Token $mockToken } | Should Throw $mockCorrectErrorRecord + } + } + } } } finally From 6aabed622c714fc2b69e231eaaa422069d6dfa83 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Thu, 27 Jul 2017 09:16:48 +0200 Subject: [PATCH 13/19] xCluster: Fixed random problem with tests failing (#134) - Changes to xCluster - Fixed random problem with tests failing with error "Invalid token for impersonation - it cannot be duplicated." (issue #133). --- CHANGELOG.md | 2 ++ Tests/Unit/MSFT_xCluster.Tests.ps1 | 31 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d920b6..4e398a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,8 @@ etc. - Enabled localization for all strings ([issue #83](https://github.com/PowerShell/xFailOverCluster/issues/83)). - Added tests to improve code coverage. + - Fixed random problem with tests failing with error "Invalid token for + impersonation - it cannot be duplicated." ([issue #133](https://github.com/PowerShell/xFailOverCluster/issues/133)). - Changes to xWaitForCluster - Refactored the unit test for this resource to use stubs and increase coverage ([issue #78](https://github.com/PowerShell/xFailOverCluster/issues/78)). diff --git a/Tests/Unit/MSFT_xCluster.Tests.ps1 b/Tests/Unit/MSFT_xCluster.Tests.ps1 index dcd2da2..f13482e 100644 --- a/Tests/Unit/MSFT_xCluster.Tests.ps1 +++ b/Tests/Unit/MSFT_xCluster.Tests.ps1 @@ -104,6 +104,19 @@ try ) } + $mockNewObjectWindowsIdentity = { + return [PSCustomObject] @{} | + Add-Member -MemberType ScriptMethod -Name Impersonate -Value { + return [PSCustomObject] @{} | + Add-Member -MemberType ScriptMethod -Name Undo -Value {} -PassThru | + Add-Member -MemberType ScriptMethod -Name Dispose -Value {} -PassThru -Force + } -PassThru -Force + } + + $mockNewObjectWindowsIdentity_ParameterFilter = { + $TypeName -eq 'Security.Principal.WindowsIdentity' + } + $mockDefaultParameters = @{ Name = $mockClusterName StaticIPAddress = $mockStaticIpAddress @@ -140,6 +153,8 @@ try Mock -CommandName Add-Type -MockWith { return $mockLibImpersonationObject } + + Mock -CommandName New-Object -MockWith $mockNewObjectWindowsIdentity -ParameterFilter $mockNewObjectWindowsIdentity_ParameterFilter -Verifiable } Context 'When the computers domain name cannot be evaluated' { @@ -194,6 +209,14 @@ try } Describe 'xCluster\Set-TargetResource' { + BeforeAll { + Mock -CommandName Add-Type -MockWith { + return $mockLibImpersonationObject + } + + Mock -CommandName New-Object -MockWith $mockNewObjectWindowsIdentity -ParameterFilter $mockNewObjectWindowsIdentity_ParameterFilter -Verifiable + } + Context 'When computers domain name cannot be evaluated' { It 'Should throw the correct error message' { $mockDynamicDomainName = $null @@ -339,6 +362,14 @@ try } Describe 'xCluster\Test-TargetResource' { + BeforeAll { + Mock -CommandName Add-Type -MockWith { + return $mockLibImpersonationObject + } + + Mock -CommandName New-Object -MockWith $mockNewObjectWindowsIdentity -ParameterFilter $mockNewObjectWindowsIdentity_ParameterFilter -Verifiable + } + Context 'When computers domain name cannot be evaluated' { It 'Should throw the correct error message' { $mockDynamicDomainName = $null From 9879aa51ad3e07c3463316944ecdafb92d93e069 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Thu, 27 Jul 2017 09:27:46 +0200 Subject: [PATCH 14/19] xFailOverCluster: Update year 2017 in license file and module manifest - Changes to xFailOverCluster - Updated year to 2017 in license file and module manifest (issue #131). --- CHANGELOG.md | 1 + LICENSE | 4 ++-- xFailOverCluster.psd1 | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e398a3..a87cfd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Get-InvalidOperationRecord - Get-ObjectNotFoundException - Get-InvalidResultException. + - Updated year to 2017 in license file and module manifest ([issue #131](https://github.com/PowerShell/xFailOverCluster/issues/131)). - Changes to xClusterDisk - Enabled localization for all strings ([issue #84](https://github.com/PowerShell/xFailOverCluster/issues/84)). - Fixed the OutputType data type that was not fully qualified. diff --git a/LICENSE b/LICENSE index 567fd6a..a1d2d91 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Microsoft Corporation. +Copyright (c) 2017 Microsoft Corporation. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file +THE SOFTWARE. diff --git a/xFailOverCluster.psd1 b/xFailOverCluster.psd1 index 144f996..ee2b65b 100644 --- a/xFailOverCluster.psd1 +++ b/xFailOverCluster.psd1 @@ -8,7 +8,7 @@ Author = 'Microsoft Corporation' CompanyName = 'Microsoft Corporation' -Copyright = '(c) 2014 Microsoft Corporation. All rights reserved.' +Copyright = '(c) 2017 Microsoft Corporation. All rights reserved.' Description = 'Module containing DSC resources used to configure Failover Clusters.' From e6adc5f8280f57e13d9cf853c7dcd4721f8df982 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Fri, 28 Jul 2017 10:59:41 +0200 Subject: [PATCH 15/19] xWaitForCluster: Enabled localization (#139) - Changes to xWaitForCluster - Enabled localization for all strings (issue #88). --- CHANGELOG.md | 1 + .../MSFT_xWaitForCluster.psm1 | 32 ++++++++++++------- .../en-US/MSFT_xWaitForCluster.strings.psd1 | 13 ++++++++ Tests/Unit/MSFT_xWaitForCluster.Tests.ps1 | 6 ++-- 4 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 DSCResources/MSFT_xWaitForCluster/en-US/MSFT_xWaitForCluster.strings.psd1 diff --git a/CHANGELOG.md b/CHANGELOG.md index a87cfd0..590dfee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ - Changed the code to be more aligned with the style guideline. - Updated parameter description in the schema.mof. - Resolved Script Analyzer warnings ([issue #54](https://github.com/PowerShell/xFailOverCluster/issues/54)). + - Enabled localization for all strings ([issue #88](https://github.com/PowerShell/xFailOverCluster/issues/88)). - Changes to xClusterQuorum - Refactored the unit test for this resource to use stubs and increase coverage ([issue #77](https://github.com/PowerShell/xFailOverCluster/issues/77)). diff --git a/DSCResources/MSFT_xWaitForCluster/MSFT_xWaitForCluster.psm1 b/DSCResources/MSFT_xWaitForCluster/MSFT_xWaitForCluster.psm1 index 34d3b0a..eb23baf 100644 --- a/DSCResources/MSFT_xWaitForCluster/MSFT_xWaitForCluster.psm1 +++ b/DSCResources/MSFT_xWaitForCluster/MSFT_xWaitForCluster.psm1 @@ -1,3 +1,8 @@ +Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) ` +-ChildPath 'CommonResourceHelper.psm1') + +$script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xWaitForCluster' + <# .SYNOPSIS Get the values for which failover cluster and for how long to wait for the @@ -31,6 +36,8 @@ function Get-TargetResource $RetryCount = 50 ) + Write-Verbose -Message $script:localizedData.ReturnParameterValues + @{ Name = $Name RetryIntervalSec = $RetryIntervalSec @@ -71,7 +78,7 @@ function Set-TargetResource ) $clusterFound = $false - Write-Verbose -Message "Checking for the existance of failover cluster $Name." + Write-Verbose -Message ($script:localizedData.CheckClusterPresent -f $Name) for ($count = 0; $count -lt $RetryCount; $count++) { @@ -80,7 +87,7 @@ function Set-TargetResource $computerObject = Get-CimInstance -ClassName Win32_ComputerSystem if ($null -eq $computerObject -or $null -eq $computerObject.Domain) { - Write-Verbose -Message "Can't find machine's domain name" + Write-Verbose -Message $script:localizedData.TargetNodeDomainMissing break } @@ -88,23 +95,24 @@ function Set-TargetResource if ($null -ne $cluster) { - Write-Verbose -Message "Found failover cluster $Name" + Write-Verbose -Message ($script:localizedData.ClusterPresent -f $Name) $clusterFound = $true break } } catch { - Write-Verbose -Message "Failover cluster $Name not found. Will retry again after $RetryIntervalSec sec" + Write-Verbose -Message ($script:localizedData.ClusterAbsent -f $Name, $RetryIntervalSec) } - Write-Verbose -Message "Failover cluster $Name not found. Will retry again after $RetryIntervalSec sec" + Write-Verbose -Message ($script:localizedData.ClusterAbsent -f $Name, $RetryIntervalSec) Start-Sleep -Seconds $RetryIntervalSec } - if (! $clusterFound) + if (-not $clusterFound) { - throw "Failover cluster $Name not found after $count attempts with $RetryIntervalSec sec interval" + $errorMessage = $script:localizedData.ClusterAbsentAfterTimeOut -f $Name, $count, $RetryIntervalSec + New-InvalidOperationException -Message $errorMessage } } @@ -140,7 +148,7 @@ function Test-TargetResource $RetryCount = 50 ) - Write-Verbose -Message "Checking for Cluster $Name ..." + Write-Verbose -Message ($script:localizedData.EvaluatingClusterPresent -f $Name) $testTargetResourceReturnValue = $false @@ -149,25 +157,25 @@ function Test-TargetResource $computerObject = Get-CimInstance -ClassName Win32_ComputerSystem if ($null -eq $computerObject -or $null -eq $computerObject.Domain) { - Write-Verbose -Message "Can't find machine's domain name" + Write-Verbose -Message $script:localizedData.TargetNodeDomainMissing } else { $cluster = Get-Cluster -Name $Name -Domain $computerObject.Domain if ($null -eq $cluster) { - Write-Verbose -Message "Cluster $Name not found in domain $computerObject.Domain" + Write-Verbose -Message ($script:localizedData.ClusterAbsentWithDomain -f $Name, $computerObject.Domain) } else { - Write-Verbose -Message "Found cluster $Name" + Write-Verbose -Message ($script:localizedData.ClusterPresent -f $Name) $testTargetResourceReturnValue = $true } } } catch { - Write-Verbose -Message "Cluster $Name not found" + Write-Verbose -Message ($script:localizedData.ClusterAbsentWithError -f $Name, $_.Message) } $testTargetResourceReturnValue diff --git a/DSCResources/MSFT_xWaitForCluster/en-US/MSFT_xWaitForCluster.strings.psd1 b/DSCResources/MSFT_xWaitForCluster/en-US/MSFT_xWaitForCluster.strings.psd1 new file mode 100644 index 0000000..60e652f --- /dev/null +++ b/DSCResources/MSFT_xWaitForCluster/en-US/MSFT_xWaitForCluster.strings.psd1 @@ -0,0 +1,13 @@ +# Localized resources for xWaitForCluster + +ConvertFrom-StringData @' + ReturnParameterValues = Returning the values passed as parameters. + CheckClusterPresent = Checking if cluster {0} is present. + TargetNodeDomainMissing = Can't find the target node's domain name. + ClusterPresent = Cluster {0} is present. + ClusterAbsent = Cluster {0} is NOT present. Will retry again after {1} seconds. + ClusterAbsentWithDomain = Cluster {0} is NOT present in Active Directory domain '{1}'. + ClusterAbsentWithError = Cluster {0} is NOT present with error: {1} + ClusterAbsentAfterTimeOut = Failover cluster {0} was not found after {1} attempts with {2} seconds interval. + EvaluatingClusterPresent = Evaluating if cluster {0} is present. +'@ diff --git a/Tests/Unit/MSFT_xWaitForCluster.Tests.ps1 b/Tests/Unit/MSFT_xWaitForCluster.Tests.ps1 index 6e55407..3497d85 100644 --- a/Tests/Unit/MSFT_xWaitForCluster.Tests.ps1 +++ b/Tests/Unit/MSFT_xWaitForCluster.Tests.ps1 @@ -93,7 +93,8 @@ try It 'Should throw the correct error message' { Mock -CommandName Get-CimInstance -MockWith $mockGetCimInstance -ParameterFilter $mockCimInstance_ParameterFilter -Verifiable - { Set-TargetResource @mockDefaultParameters } | Should Throw ('Failover cluster {0} not found after {1} attempts with {2} sec interval' -f $mockClusterName, ($mockRetryCount-1), $mockRetryIntervalSec) + $mockCorrectErrorRecord = Get-InvalidOperationRecord -Message ($script:localizedData.ClusterAbsentAfterTimeOut -f $mockClusterName, ($mockRetryCount-1), $mockRetryIntervalSec) + { Set-TargetResource @mockDefaultParameters } | Should Throw $mockCorrectErrorRecord } } @@ -114,7 +115,8 @@ try $mockDynamicDomainName = $mockDomainName It 'Should throw the correct error message' { - { Set-TargetResource @mockDefaultParameters } | Should Throw ('Failover cluster {0} not found after {1} attempts with {2} sec interval' -f $mockClusterName, $mockRetryCount, $mockRetryIntervalSec) + $mockCorrectErrorRecord = Get-InvalidOperationRecord -Message ($script:localizedData.ClusterAbsentAfterTimeOut -f $mockClusterName, $mockRetryCount, $mockRetryIntervalSec) + { Set-TargetResource @mockDefaultParameters } | Should Throw $mockCorrectErrorRecord } Assert-VerifiableMocks From a37efebdfa5ee0b8fe01ab09bbbf70164022ad94 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 29 Jul 2017 11:17:21 +0200 Subject: [PATCH 16/19] xFailOverCluster: Minor style changes and applied auto-formatting (#136) - Changes to xCluster - Minor style changes. - Changes to xClusterDisk - Minor style changes. - Changes to xClusterNetwork - Minor style changes. - Changes to xClusterPreferredOwner - Minor style changes. - Changes to xClusterQuorum - Minor style changes. - Changes to xWaitForCluster - Minor style changes. --- CHANGELOG.md | 6 + DSCResources/CommonResourceHelper.psm1 | 28 ++-- DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 | 4 +- .../MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 | 16 ++- .../MSFT_xClusterNetwork.psm1 | 2 +- .../MSFT_xClusterPreferredOwner.psm1 | 46 +++---- .../MSFT_xClusterQuorum.psm1 | 30 +++-- .../MSFT_xWaitForCluster.psm1 | 4 +- .../1-CreateFirstNodeOfAFailoverCluster.ps1 | 18 +-- .../2-JoinAdditionalNodeToFailoverCluster.ps1 | 22 +-- .../3-CreateFailoverClusterWithTwoNodes.ps1 | 64 ++++----- .../xClusterDisk/1-AddClusterDisk.ps1 | 4 +- .../xClusterDisk/2-RemoveClusterDisk.ps1 | 4 +- .../1-ChangeClusterNetwork.ps1 | 16 +-- .../1-AddPreferredOwner.ps1 | 20 +-- .../2-RemovePreferredOwner.ps1 | 20 +-- .../1-SetQuorumToNodeMajority.ps1 | 2 +- .../2-SetQuorumToNodeAndDiskMajority.ps1 | 4 +- .../3-SetQuorumToNodeAndFileShareMajority.ps1 | 4 +- .../xClusterQuorum/4-SetQuorumToDiskOnly.ps1 | 4 +- .../1-WaitForFailoverClusterToBePresent.ps1 | 22 +-- Tests/TestHelpers/CommonTestHelper.psm1 | 48 +++---- Tests/Unit/CommonResourceHelper.Tests.ps1 | 4 +- Tests/Unit/Stubs/Write-ModuleStubFile.ps1 | 126 +++++++++--------- 24 files changed, 270 insertions(+), 248 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 590dfee..9a52468 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,12 +23,14 @@ - Changes to xClusterDisk - Enabled localization for all strings ([issue #84](https://github.com/PowerShell/xFailOverCluster/issues/84)). - Fixed the OutputType data type that was not fully qualified. + - Minor style changes. - Changes to xClusterNetwork - Replaced the URL for the parameter Role in README.md. The new URL is a more generic description of the possible settings for the Role parameter. The previous URL was still correct but focused on Hyper-V in particular. - Fixed typos in parameter descriptions in README.md, comment-based help and schema.mof. - Enabled localization for all strings ([issue #85](https://github.com/PowerShell/xFailOverCluster/issues/85)). + - Minor style changes. - Changes to xCluster - Resolved Script Analyzer rule warnings by changing Get-WmiObject to Get-CimInstance ([issue #49](https://github.com/PowerShell/xFailOverCluster/issues/49)). @@ -38,6 +40,7 @@ - Added tests to improve code coverage. - Fixed random problem with tests failing with error "Invalid token for impersonation - it cannot be duplicated." ([issue #133](https://github.com/PowerShell/xFailOverCluster/issues/133)). + - Minor style changes. - Changes to xWaitForCluster - Refactored the unit test for this resource to use stubs and increase coverage ([issue #78](https://github.com/PowerShell/xFailOverCluster/issues/78)). @@ -47,6 +50,7 @@ - Updated parameter description in the schema.mof. - Resolved Script Analyzer warnings ([issue #54](https://github.com/PowerShell/xFailOverCluster/issues/54)). - Enabled localization for all strings ([issue #88](https://github.com/PowerShell/xFailOverCluster/issues/88)). + - Minor style changes. - Changes to xClusterQuorum - Refactored the unit test for this resource to use stubs and increase coverage ([issue #77](https://github.com/PowerShell/xFailOverCluster/issues/77)). @@ -58,9 +62,11 @@ - 3-SetQuorumToNodeAndFileShareMajority.ps1 - 4-SetQuorumToDiskOnly.ps1 - Added links to examples from README.md. + - Minor style changes. - Changes to xClusterPreferredOwner - Enabled localization for all strings ([issue #86](https://github.com/PowerShell/xFailOverCluster/issues/86)). - Fixed typo in the returned hash table from Get-TargetResource. + - Minor style changes. ## 1.7.0.0 diff --git a/DSCResources/CommonResourceHelper.psm1 b/DSCResources/CommonResourceHelper.psm1 index c385d79..61e4f76 100644 --- a/DSCResources/CommonResourceHelper.psm1 +++ b/DSCResources/CommonResourceHelper.psm1 @@ -25,10 +25,10 @@ function New-InvalidArgumentException ) $argumentException = New-Object -TypeName 'ArgumentException' ` - -ArgumentList @($Message, $ArgumentName) + -ArgumentList @($Message, $ArgumentName) $newObjectParameters = @{ - TypeName = 'System.Management.Automation.ErrorRecord' + TypeName = 'System.Management.Automation.ErrorRecord' ArgumentList = @($argumentException, $ArgumentName, 'InvalidArgument', $null) } @@ -66,22 +66,22 @@ function New-InvalidOperationException if ($null -eq $ErrorRecord) { $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` - -ArgumentList @($Message) + -ArgumentList @($Message) } else { $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` - -ArgumentList @($Message, $ErrorRecord.Exception) + -ArgumentList @($Message, $ErrorRecord.Exception) } $newObjectParameters = @{ - TypeName = 'System.Management.Automation.ErrorRecord' + TypeName = 'System.Management.Automation.ErrorRecord' ArgumentList = @( $invalidOperationException.ToString(), 'MachineStateIncorrect', 'InvalidOperation', $null - ) + ) } $errorRecordToThrow = New-Object @newObjectParameters @@ -118,22 +118,22 @@ function New-ObjectNotFoundException if ($null -eq $ErrorRecord) { $exception = New-Object -TypeName 'System.Exception' ` - -ArgumentList @($Message) + -ArgumentList @($Message) } else { $exception = New-Object -TypeName 'System.Exception' ` - -ArgumentList @($Message, $ErrorRecord.Exception) + -ArgumentList @($Message, $ErrorRecord.Exception) } $newObjectParameters = @{ - TypeName = 'System.Management.Automation.ErrorRecord' + TypeName = 'System.Management.Automation.ErrorRecord' ArgumentList = @( $exception.ToString(), 'MachineStateIncorrect', 'ObjectNotFound', $null - ) + ) } $errorRecordToThrow = New-Object @newObjectParameters @@ -170,22 +170,22 @@ function New-InvalidResultException if ($null -eq $ErrorRecord) { $exception = New-Object -TypeName 'System.Exception' ` - -ArgumentList @($Message) + -ArgumentList @($Message) } else { $exception = New-Object -TypeName 'System.Exception' ` - -ArgumentList @($Message, $ErrorRecord.Exception) + -ArgumentList @($Message, $ErrorRecord.Exception) } $newObjectParameters = @{ - TypeName = 'System.Management.Automation.ErrorRecord' + TypeName = 'System.Management.Automation.ErrorRecord' ArgumentList = @( $exception.ToString(), 'MachineStateIncorrect', 'InvalidResult', $null - ) + ) } $errorRecordToThrow = New-Object @newObjectParameters diff --git a/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 b/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 index 711ee63..1d73ebe 100644 --- a/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 +++ b/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 @@ -1,5 +1,5 @@ Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) ` - -ChildPath 'CommonResourceHelper.psm1') + -ChildPath 'CommonResourceHelper.psm1') $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xCluster' @@ -368,7 +368,7 @@ function Set-ImpersonateAs if ($bLogin) { - $Identity = New-Object Security.Principal.WindowsIdentity $userToken + $Identity = New-Object -TypeName Security.Principal.WindowsIdentity -ArgumentList $userToken $context = $Identity.Impersonate() } else diff --git a/DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 b/DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 index df47df1..491db1f 100644 --- a/DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 +++ b/DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 @@ -85,7 +85,9 @@ function Set-TargetResource { Write-Verbose -Message ($script:localizedData.AddDiskToCluster -f $Number) - Get-ClusterAvailableDisk | Where-Object -FilterScript { $_.Number -eq $Number } | Add-ClusterDisk + Get-ClusterAvailableDisk | Where-Object -FilterScript { + $_.Number -eq $Number + } | Add-ClusterDisk } if ($getTargetResourceResult.Label -ne $Label) @@ -95,8 +97,10 @@ function Set-TargetResource $diskInstance = Get-CimInstance -ClassName MSCluster_Disk -Namespace 'Root\MSCluster' -Filter "Number = $Number" $diskResource = Get-ClusterResource | - Where-Object -FilterScript { $_.ResourceType -eq 'Physical Disk' } | - Where-Object -FilterScript { ($_ | Get-ClusterParameter -Name DiskIdGuid).Value -eq $diskInstance.Id } + Where-Object -FilterScript { $_.ResourceType -eq 'Physical Disk' } | + Where-Object -FilterScript { + ($_ | Get-ClusterParameter -Name DiskIdGuid).Value -eq $diskInstance.Id + } # Set the label of the cluster disk $diskResource.Name = $Label @@ -112,8 +116,10 @@ function Set-TargetResource $diskInstance = Get-CimInstance -ClassName MSCluster_Disk -Namespace 'Root\MSCluster' -Filter "Number = $Number" $diskResource = Get-ClusterResource | - Where-Object -FilterScript { $_.ResourceType -eq 'Physical Disk' } | - Where-Object -FilterScript { ($_ | Get-ClusterParameter -Name DiskIdGuid).Value -eq $diskInstance.Id } + Where-Object -FilterScript { $_.ResourceType -eq 'Physical Disk' } | + Where-Object -FilterScript { + ($_ | Get-ClusterParameter -Name DiskIdGuid).Value -eq $diskInstance.Id + } # Remove the cluster disk $diskResource | Remove-ClusterResource -Force diff --git a/DSCResources/MSFT_xClusterNetwork/MSFT_xClusterNetwork.psm1 b/DSCResources/MSFT_xClusterNetwork/MSFT_xClusterNetwork.psm1 index 30eb57e..fb013c4 100644 --- a/DSCResources/MSFT_xClusterNetwork/MSFT_xClusterNetwork.psm1 +++ b/DSCResources/MSFT_xClusterNetwork/MSFT_xClusterNetwork.psm1 @@ -1,5 +1,5 @@ Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) ` - -ChildPath 'CommonResourceHelper.psm1') + -ChildPath 'CommonResourceHelper.psm1') $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xClusterNetwork' diff --git a/DSCResources/MSFT_xClusterPreferredOwner/MSFT_xClusterPreferredOwner.psm1 b/DSCResources/MSFT_xClusterPreferredOwner/MSFT_xClusterPreferredOwner.psm1 index e83e311..18a6813 100644 --- a/DSCResources/MSFT_xClusterPreferredOwner/MSFT_xClusterPreferredOwner.psm1 +++ b/DSCResources/MSFT_xClusterPreferredOwner/MSFT_xClusterPreferredOwner.psm1 @@ -1,5 +1,5 @@ Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) ` - -ChildPath 'CommonResourceHelper.psm1') + -ChildPath 'CommonResourceHelper.psm1') $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xClusterPreferredOwner' @@ -55,18 +55,18 @@ function Get-TargetResource $ownerNodes = @( Write-Verbose -Message ($script:localizedData.GetOwnerInformationForClusterGroup -f $ClusterGroup) - (((Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript { - $_.Name -like $ClusterGroup - } | Get-ClusterOwnerNode).OwnerNodes).Name + ((Get-ClusterGroup -Cluster $ClusterName | Where-Object -FilterScript { + $_.Name -like $ClusterGroup + } | Get-ClusterOwnerNode).OwnerNodes).Name if ($ClusterResources) { foreach ($resource in $ClusterResources) { Write-Verbose -Message ($script:localizedData.GetOwnerInformationForClusterResource -f $resource) - (((Get-ClusterResource -Cluster $ClusterName) | Where-Object -FilterScript { - $_.Name -like $resource - } | Get-ClusterOwnerNode).OwnerNodes).Name + ((Get-ClusterResource -Cluster $ClusterName | Where-Object -FilterScript { + $_.Name -like $resource + } | Get-ClusterOwnerNode).OwnerNodes).Name } } ) @@ -74,11 +74,11 @@ function Get-TargetResource $ownerNodes = $ownerNodes | Select-Object -Unique @{ - ClusterGroup = $ClusterGroup - ClusterName = $ClusterName - Nodes = $ownerNodes + ClusterGroup = $ClusterGroup + ClusterName = $ClusterName + Nodes = $ownerNodes ClusterResources = $ClusterResources - Ensure = $Ensure + Ensure = $Ensure } } @@ -135,23 +135,23 @@ function Set-TargetResource if ($Ensure -eq 'Present') { Write-Verbose -Message ($script:localizedData.SetOwnerForClusterGroup -f $ClusterGroup, $Nodes) - $null = (Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript { + $null = Get-ClusterGroup -Cluster $ClusterName | Where-Object -FilterScript { $_.Name -like $ClusterGroup } | Set-ClusterOwnerNode -Owners $Nodes - $null = (Get-ClusterResource) | Where-Object { + $null = Get-ClusterResource | Where-Object { $_.OwnerGroup -like $ClusterGroup } | Set-ClusterOwnerNode -Owners $allNodes Write-Verbose -Message ($script:localizedData.MoveClusterGroup -f $ClusterGroup, $Nodes[0]) - $null = (Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript { + $null = Get-ClusterGroup -Cluster $ClusterName | Where-Object -FilterScript { $_.name -like $ClusterGroup } | Move-ClusterGroup -Node $Nodes[0] foreach ($resource in $ClusterResources) { Write-Verbose -Message ($script:localizedData.SetOwnerForClusterResource -f $resource, $Nodes) - $null = (Get-ClusterResource -Cluster $ClusterName) | Where-Object -FilterScript { + $null = Get-ClusterResource -Cluster $ClusterName | Where-Object -FilterScript { $_.Name -like $resource } | Set-ClusterOwnerNode -Owners $Nodes } @@ -160,9 +160,9 @@ function Set-TargetResource if ($Ensure -eq 'Absent') { Write-Verbose -Message ($script:localizedData.GetOwnerInformationForClusterGroup -f $ClusterGroup) - $currentOwners = (((Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript { - $_.Name -like $ClusterGroup - } | Get-ClusterOwnerNode).OwnerNodes).Name | Sort-Object -Unique + $currentOwners = ((Get-ClusterGroup -Cluster $ClusterName | Where-Object -FilterScript { + $_.Name -like $ClusterGroup + } | Get-ClusterOwnerNode).OwnerNodes).Name | Sort-Object -Unique $newOwners = @( foreach ($currentOwner in $currentOwners) @@ -175,17 +175,17 @@ function Set-TargetResource ) Write-Verbose -Message ($script:localizedData.RemoveOwnerFromClusterGroup -f $ClusterGroup, $Nodes) - $null = (Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript { + $null = Get-ClusterGroup -Cluster $ClusterName | Where-Object -FilterScript { $_.Name -like $ClusterGroup } | Set-ClusterOwnerNode $newOwners Write-Verbose -Message ($script:localizedData.SetOwnerForClusterGroup -f $ClusterGroup, $newOwners) - $null = (Get-ClusterResource) | Where-Object -FilterScript { + $null = Get-ClusterResource | Where-Object -FilterScript { $_.OwnerGroup -like $ClusterGroup } | Set-ClusterOwnerNode $allNodes Write-Verbose -Message ($script:localizedData.MoveClusterGroup -f $ClusterGroup, $newOwners[0]) - $null = (Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript { + $null = Get-ClusterGroup -Cluster $ClusterName | Where-Object -FilterScript { $_.Name -like $ClusterGroup } | Move-ClusterGroup -Node $newOwners[0] @@ -193,8 +193,8 @@ function Set-TargetResource { Write-Verbose -Message ($script:localizedData.GetOwnerInformationForClusterResource -f $resource) $currentOwners = ((Get-ClusterResource -Cluster $ClusterName | Where-Object -FilterScript { - $_.Name -like $resource - } | Get-ClusterOwnerNode).OwnerNodes).Name | Sort-Object -Unique + $_.Name -like $resource + } | Get-ClusterOwnerNode).OwnerNodes).Name | Sort-Object -Unique $newOwners = @( foreach ($currentOwner in $currentOwners) diff --git a/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.psm1 b/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.psm1 index 0b39095..3be48a9 100644 --- a/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.psm1 +++ b/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.psm1 @@ -22,7 +22,8 @@ function Get-TargetResource switch ($getClusterQuorumResult.QuorumType) { # WS2016 only - 'Majority' { + 'Majority' + { if ($null -eq $getClusterQuorumResult.QuorumResource) { $clusterQuorumType = 'NodeMajority' @@ -42,25 +43,30 @@ function Get-TargetResource } # WS2012R2 only - 'NodeMajority' { + 'NodeMajority' + { $clusterQuorumType = 'NodeMajority' } - 'NodeAndDiskMajority' { + 'NodeAndDiskMajority' + { $clusterQuorumType = 'NodeAndDiskMajority' } - 'NodeAndFileShareMajority' { + 'NodeAndFileShareMajority' + { $clusterQuorumType = 'NodeAndFileShareMajority' } # All - 'DiskOnly' { + 'DiskOnly' + { $clusterQuorumType = 'DiskOnly' } # Default - default { + default + { throw "Unknown quorum type: $($getClusterQuorumResult.QuorumType)" } } @@ -120,19 +126,23 @@ function Set-TargetResource switch ($Type) { - 'NodeMajority' { + 'NodeMajority' + { Set-ClusterQuorum -NoWitness } - 'NodeAndDiskMajority' { + 'NodeAndDiskMajority' + { Set-ClusterQuorum -DiskWitness $Resource } - 'NodeAndFileShareMajority' { + 'NodeAndFileShareMajority' + { Set-ClusterQuorum -FileShareWitness $Resource } - 'DiskOnly' { + 'DiskOnly' + { Set-ClusterQuorum -DiskOnly $Resource } } diff --git a/DSCResources/MSFT_xWaitForCluster/MSFT_xWaitForCluster.psm1 b/DSCResources/MSFT_xWaitForCluster/MSFT_xWaitForCluster.psm1 index eb23baf..b833631 100644 --- a/DSCResources/MSFT_xWaitForCluster/MSFT_xWaitForCluster.psm1 +++ b/DSCResources/MSFT_xWaitForCluster/MSFT_xWaitForCluster.psm1 @@ -39,9 +39,9 @@ function Get-TargetResource Write-Verbose -Message $script:localizedData.ReturnParameterValues @{ - Name = $Name + Name = $Name RetryIntervalSec = $RetryIntervalSec - RetryCount = $RetryCount + RetryCount = $RetryCount } } diff --git a/Examples/Resources/xCluster/1-CreateFirstNodeOfAFailoverCluster.ps1 b/Examples/Resources/xCluster/1-CreateFirstNodeOfAFailoverCluster.ps1 index 046dbe7..0b3bfcc 100644 --- a/Examples/Resources/xCluster/1-CreateFirstNodeOfAFailoverCluster.ps1 +++ b/Examples/Resources/xCluster/1-CreateFirstNodeOfAFailoverCluster.ps1 @@ -18,27 +18,27 @@ Configuration Example WindowsFeature AddFailoverFeature { Ensure = 'Present' - Name = 'Failover-clustering' + Name = 'Failover-clustering' } WindowsFeature AddRemoteServerAdministrationToolsClusteringPowerShellFeature { - Ensure = 'Present' - Name = 'RSAT-Clustering-PowerShell' + Ensure = 'Present' + Name = 'RSAT-Clustering-PowerShell' DependsOn = '[WindowsFeature]AddFailoverFeature' } WindowsFeature AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature { - Ensure = 'Present' - Name = 'RSAT-Clustering-CmdInterface' + Ensure = 'Present' + Name = 'RSAT-Clustering-CmdInterface' DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringPowerShellFeature' } xCluster CreateCluster { - Name = 'Cluster01' - StaticIPAddress = '192.168.100.20/24' + Name = 'Cluster01' + StaticIPAddress = '192.168.100.20/24' <# This user must have the permission to create the CNO (Cluster Name Object) in Active Directory, @@ -46,7 +46,7 @@ Configuration Example #> DomainAdministratorCredential = $ActiveDirectoryAdministratorCredential - DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature' - } + DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature' + } } } diff --git a/Examples/Resources/xCluster/2-JoinAdditionalNodeToFailoverCluster.ps1 b/Examples/Resources/xCluster/2-JoinAdditionalNodeToFailoverCluster.ps1 index d628bd6..a9e7d40 100644 --- a/Examples/Resources/xCluster/2-JoinAdditionalNodeToFailoverCluster.ps1 +++ b/Examples/Resources/xCluster/2-JoinAdditionalNodeToFailoverCluster.ps1 @@ -18,37 +18,37 @@ Configuration Example WindowsFeature AddFailoverFeature { Ensure = 'Present' - Name = 'Failover-clustering' + Name = 'Failover-clustering' } WindowsFeature AddRemoteServerAdministrationToolsClusteringPowerShellFeature { - Ensure = 'Present' - Name = 'RSAT-Clustering-PowerShell' + Ensure = 'Present' + Name = 'RSAT-Clustering-PowerShell' DependsOn = '[WindowsFeature]AddFailoverFeature' } WindowsFeature AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature { - Ensure = 'Present' - Name = 'RSAT-Clustering-CmdInterface' + Ensure = 'Present' + Name = 'RSAT-Clustering-CmdInterface' DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringPowerShellFeature' } xWaitForCluster WaitForCluster { - Name = 'Cluster01' + Name = 'Cluster01' RetryIntervalSec = 10 - RetryCount = 60 - DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature' + RetryCount = 60 + DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature' } xCluster JoinSecondNodeToCluster { - Name = 'Cluster01' - StaticIPAddress = '192.168.100.20/24' + Name = 'Cluster01' + StaticIPAddress = '192.168.100.20/24' DomainAdministratorCredential = $ActiveDirectoryAdministratorCredential - DependsOn = '[xWaitForCluster]WaitForCluster' + DependsOn = '[xWaitForCluster]WaitForCluster' } } } diff --git a/Examples/Resources/xCluster/3-CreateFailoverClusterWithTwoNodes.ps1 b/Examples/Resources/xCluster/3-CreateFailoverClusterWithTwoNodes.ps1 index 82bd5dd..9b01e38 100644 --- a/Examples/Resources/xCluster/3-CreateFailoverClusterWithTwoNodes.ps1 +++ b/Examples/Resources/xCluster/3-CreateFailoverClusterWithTwoNodes.ps1 @@ -14,7 +14,7 @@ $ConfigurationData = @{ AllNodes = @( @{ - NodeName= '*' + NodeName = '*' <# Replace with the correct path to your own public certificate part of the same certificate @@ -41,7 +41,7 @@ $ConfigurationData = @{ parameter CertificateFile. For this example it is assumed that both machines have the same certificate installed. #> - Thumbprint = "E513EEFCB763E6954C52BA66A1A81231BF3F551E" + Thumbprint = "E513EEFCB763E6954C52BA66A1A81231BF3F551E" <# Replace with your own CNO (Cluster Name Object) and IP address. @@ -51,27 +51,27 @@ $ConfigurationData = @{ If the CNO is not prestaged, then the credential used in the xCluster resource must have the permission in Active Directory to create the CNO (Cluster Name Object). #> - ClusterName = 'Cluster01' - ClusterIPAddress = '192.168.100.20/24' + ClusterName = 'Cluster01' + ClusterIPAddress = '192.168.100.20/24' }, # Node01 - First cluster node. @{ # Replace with the name of the actual target node. - NodeName= 'Node01' + NodeName = 'Node01' # This is used in the configuration to know which resource to compile. - Role = 'FirstServerNode' - }, + Role = 'FirstServerNode' + }, - # Node02 - Second cluster node - @{ + # Node02 - Second cluster node + @{ # Replace with the name of the actual target node. - NodeName= 'Node02' + NodeName = 'Node02' # This is used in the configuration to know which resource to compile. - Role = 'AdditionalServerNode' - } + Role = 'AdditionalServerNode' + } ) } @@ -90,31 +90,31 @@ Configuration Example WindowsFeature AddFailoverFeature { Ensure = 'Present' - Name = 'Failover-clustering' + Name = 'Failover-clustering' } WindowsFeature AddRemoteServerAdministrationToolsClusteringPowerShellFeature { - Ensure = 'Present' - Name = 'RSAT-Clustering-PowerShell' + Ensure = 'Present' + Name = 'RSAT-Clustering-PowerShell' DependsOn = '[WindowsFeature]AddFailoverFeature' } WindowsFeature AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature { - Ensure = 'Present' - Name = 'RSAT-Clustering-CmdInterface' + Ensure = 'Present' + Name = 'RSAT-Clustering-CmdInterface' DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringPowerShellFeature' } xCluster CreateCluster { - Name = $Node.ClusterName - StaticIPAddress = $Node.ClusterIPAddress + Name = $Node.ClusterName + StaticIPAddress = $Node.ClusterIPAddress # This user must have the permission to create the CNO (Cluster Name Object) in Active Directory, unless it is prestaged. DomainAdministratorCredential = $ActiveDirectoryAdministratorCredential - DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature' - } + DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature' + } } Node $AllNodes.Where{ $_.Role -eq 'AdditionalServerNode' }.NodeName @@ -122,37 +122,37 @@ Configuration Example WindowsFeature AddFailoverFeature { Ensure = 'Present' - Name = 'Failover-clustering' + Name = 'Failover-clustering' } WindowsFeature AddRemoteServerAdministrationToolsClusteringPowerShellFeature { - Ensure = 'Present' - Name = 'RSAT-Clustering-PowerShell' + Ensure = 'Present' + Name = 'RSAT-Clustering-PowerShell' DependsOn = '[WindowsFeature]AddFailoverFeature' } WindowsFeature AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature { - Ensure = 'Present' - Name = 'RSAT-Clustering-CmdInterface' + Ensure = 'Present' + Name = 'RSAT-Clustering-CmdInterface' DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringPowerShellFeature' } xWaitForCluster WaitForCluster { - Name = $Node.ClusterName + Name = $Node.ClusterName RetryIntervalSec = 10 - RetryCount = 60 - DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature' + RetryCount = 60 + DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature' } xCluster JoinSecondNodeToCluster { - Name = $Node.ClusterName - StaticIPAddress = $Node.ClusterIPAddress + Name = $Node.ClusterName + StaticIPAddress = $Node.ClusterIPAddress DomainAdministratorCredential = $ActiveDirectoryAdministratorCredential - DependsOn = '[xWaitForCluster]WaitForCluster' + DependsOn = '[xWaitForCluster]WaitForCluster' } } } diff --git a/Examples/Resources/xClusterDisk/1-AddClusterDisk.ps1 b/Examples/Resources/xClusterDisk/1-AddClusterDisk.ps1 index 8677b3d..ca50536 100644 --- a/Examples/Resources/xClusterDisk/1-AddClusterDisk.ps1 +++ b/Examples/Resources/xClusterDisk/1-AddClusterDisk.ps1 @@ -16,14 +16,14 @@ Configuration Example { Number = 1 Ensure = 'Present' - Label = 'SQL2016-DATA' + Label = 'SQL2016-DATA' } xClusterDisk 'AddClusterDisk-SQL2017-LOG' { Number = 2 Ensure = 'Present' - Label = 'SQL2016-LOG' + Label = 'SQL2016-LOG' } } } diff --git a/Examples/Resources/xClusterDisk/2-RemoveClusterDisk.ps1 b/Examples/Resources/xClusterDisk/2-RemoveClusterDisk.ps1 index 867c4a7..412b7a8 100644 --- a/Examples/Resources/xClusterDisk/2-RemoveClusterDisk.ps1 +++ b/Examples/Resources/xClusterDisk/2-RemoveClusterDisk.ps1 @@ -16,14 +16,14 @@ Configuration Example { Number = 1 Ensure = 'Absent' - Label = 'SQL2016-DATA' + Label = 'SQL2016-DATA' } xClusterDisk 'AddClusterDisk-SQL2017-LOG' { Number = 2 Ensure = 'Absent' - Label = 'SQL2016-LOG' + Label = 'SQL2016-LOG' } } } diff --git a/Examples/Resources/xClusterNetwork/1-ChangeClusterNetwork.ps1 b/Examples/Resources/xClusterNetwork/1-ChangeClusterNetwork.ps1 index d857d4c..ac8cd16 100644 --- a/Examples/Resources/xClusterNetwork/1-ChangeClusterNetwork.ps1 +++ b/Examples/Resources/xClusterNetwork/1-ChangeClusterNetwork.ps1 @@ -14,20 +14,20 @@ Configuration Example { xClusterNetwork 'ChangeNetwork-10' { - Address = '10.0.0.0' + Address = '10.0.0.0' AddressMask = '255.255.255.0' - Name = 'Client1' - Role = '3' - Metric = '10' + Name = 'Client1' + Role = '3' + Metric = '10' } xClusterNetwork 'ChangeNetwork-192' { - Address = '192.168.0.0' + Address = '192.168.0.0' AddressMask = '255.255.255.0' - Name = 'Heartbeat' - Role = '1' - Metric = '200' + Name = 'Heartbeat' + Role = '1' + Metric = '200' } } } diff --git a/Examples/Resources/xClusterPreferredOwner/1-AddPreferredOwner.ps1 b/Examples/Resources/xClusterPreferredOwner/1-AddPreferredOwner.ps1 index 794faa4..b8a6c49 100644 --- a/Examples/Resources/xClusterPreferredOwner/1-AddPreferredOwner.ps1 +++ b/Examples/Resources/xClusterPreferredOwner/1-AddPreferredOwner.ps1 @@ -14,20 +14,20 @@ Configuration Example { xClusterPreferredOwner 'AddOwnersForClusterGroup1' { - Ensure = 'Present' - ClusterName = 'TESTCLU1' - ClusterGroup = 'Cluster Group 1' - Nodes = @('Node1', 'Node2') - ClusterResources = @('Resource1','Resource2') + Ensure = 'Present' + ClusterName = 'TESTCLU1' + ClusterGroup = 'Cluster Group 1' + Nodes = @('Node1', 'Node2') + ClusterResources = @('Resource1', 'Resource2') } xClusterPreferredOwner 'AddOwnersForClusterGroup2' { - Ensure = 'Present' - ClusterName = 'TESTCLU1' - ClusterGroup = 'Cluster Group 2' - Nodes = @('Node1', 'Node2') - ClusterResources = @('Resource3','Resource4') + Ensure = 'Present' + ClusterName = 'TESTCLU1' + ClusterGroup = 'Cluster Group 2' + Nodes = @('Node1', 'Node2') + ClusterResources = @('Resource3', 'Resource4') } } } diff --git a/Examples/Resources/xClusterPreferredOwner/2-RemovePreferredOwner.ps1 b/Examples/Resources/xClusterPreferredOwner/2-RemovePreferredOwner.ps1 index a4675c4..0b7c78c 100644 --- a/Examples/Resources/xClusterPreferredOwner/2-RemovePreferredOwner.ps1 +++ b/Examples/Resources/xClusterPreferredOwner/2-RemovePreferredOwner.ps1 @@ -19,20 +19,20 @@ Configuration Example { xClusterPreferredOwner 'RemoveOwnersForClusterGroup1' { - Ensure = 'Absent' - ClusterName = 'TESTCLU1' - ClusterGroup = 'Cluster Group 1' - Nodes = @('Node1', 'Node2') - ClusterResources = @('Resource1','Resource2') + Ensure = 'Absent' + ClusterName = 'TESTCLU1' + ClusterGroup = 'Cluster Group 1' + Nodes = @('Node1', 'Node2') + ClusterResources = @('Resource1', 'Resource2') } xClusterPreferredOwner 'RemoveOwnersForClusterGroup2' { - Ensure = 'Absent' - ClusterName = 'TESTCLU1' - ClusterGroup = 'Cluster Group 2' - Nodes = @('Node1', 'Node2') - ClusterResources = @('Resource3','Resource4') + Ensure = 'Absent' + ClusterName = 'TESTCLU1' + ClusterGroup = 'Cluster Group 2' + Nodes = @('Node1', 'Node2') + ClusterResources = @('Resource3', 'Resource4') } } } diff --git a/Examples/Resources/xClusterQuorum/1-SetQuorumToNodeMajority.ps1 b/Examples/Resources/xClusterQuorum/1-SetQuorumToNodeMajority.ps1 index 436fba9..30ac9a5 100644 --- a/Examples/Resources/xClusterQuorum/1-SetQuorumToNodeMajority.ps1 +++ b/Examples/Resources/xClusterQuorum/1-SetQuorumToNodeMajority.ps1 @@ -15,7 +15,7 @@ Configuration Example xClusterQuorum 'SetQuorumToNodeMajority' { IsSingleInstance = 'Yes' - Type = 'NodeMajority' + Type = 'NodeMajority' } } } diff --git a/Examples/Resources/xClusterQuorum/2-SetQuorumToNodeAndDiskMajority.ps1 b/Examples/Resources/xClusterQuorum/2-SetQuorumToNodeAndDiskMajority.ps1 index ffd067d..db5a06e 100644 --- a/Examples/Resources/xClusterQuorum/2-SetQuorumToNodeAndDiskMajority.ps1 +++ b/Examples/Resources/xClusterQuorum/2-SetQuorumToNodeAndDiskMajority.ps1 @@ -15,8 +15,8 @@ Configuration Example xClusterQuorum 'SetQuorumToNodeAndDiskMajority' { IsSingleInstance = 'Yes' - Type = 'NodeAndDiskMajority' - Resource = 'Witness Cluster Disk' + Type = 'NodeAndDiskMajority' + Resource = 'Witness Cluster Disk' } } } diff --git a/Examples/Resources/xClusterQuorum/3-SetQuorumToNodeAndFileShareMajority.ps1 b/Examples/Resources/xClusterQuorum/3-SetQuorumToNodeAndFileShareMajority.ps1 index 9c8b4eb..2f762c8 100644 --- a/Examples/Resources/xClusterQuorum/3-SetQuorumToNodeAndFileShareMajority.ps1 +++ b/Examples/Resources/xClusterQuorum/3-SetQuorumToNodeAndFileShareMajority.ps1 @@ -23,8 +23,8 @@ Configuration Example xClusterQuorum 'SetQuorumToNodeAndDiskMajority' { IsSingleInstance = 'Yes' - Type = 'NodeAndFileShareMajority' - Resource = '\\witness.company.local\witness$' + Type = 'NodeAndFileShareMajority' + Resource = '\\witness.company.local\witness$' } } } diff --git a/Examples/Resources/xClusterQuorum/4-SetQuorumToDiskOnly.ps1 b/Examples/Resources/xClusterQuorum/4-SetQuorumToDiskOnly.ps1 index 38b5786..6f3e7f1 100644 --- a/Examples/Resources/xClusterQuorum/4-SetQuorumToDiskOnly.ps1 +++ b/Examples/Resources/xClusterQuorum/4-SetQuorumToDiskOnly.ps1 @@ -15,8 +15,8 @@ Configuration Example xClusterQuorum 'SetQuorumToDiskOnly' { IsSingleInstance = 'Yes' - Type = 'DiskOnly' - Resource = 'Witness Cluster Disk' + Type = 'DiskOnly' + Resource = 'Witness Cluster Disk' } } } diff --git a/Examples/Resources/xWaitForCluster/1-WaitForFailoverClusterToBePresent.ps1 b/Examples/Resources/xWaitForCluster/1-WaitForFailoverClusterToBePresent.ps1 index 117d181..a062057 100644 --- a/Examples/Resources/xWaitForCluster/1-WaitForFailoverClusterToBePresent.ps1 +++ b/Examples/Resources/xWaitForCluster/1-WaitForFailoverClusterToBePresent.ps1 @@ -21,37 +21,37 @@ Configuration Example WindowsFeature AddFailoverFeature { Ensure = 'Present' - Name = 'Failover-clustering' + Name = 'Failover-clustering' } WindowsFeature AddRemoteServerAdministrationToolsClusteringPowerShellFeature { - Ensure = 'Present' - Name = 'RSAT-Clustering-PowerShell' + Ensure = 'Present' + Name = 'RSAT-Clustering-PowerShell' DependsOn = '[WindowsFeature]AddFailoverFeature' } WindowsFeature AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature { - Ensure = 'Present' - Name = 'RSAT-Clustering-CmdInterface' + Ensure = 'Present' + Name = 'RSAT-Clustering-CmdInterface' DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringPowerShellFeature' } xWaitForCluster WaitForCluster { - Name = 'Cluster01' + Name = 'Cluster01' RetryIntervalSec = 10 - RetryCount = 60 - DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature' + RetryCount = 60 + DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature' } xCluster JoinSecondNodeToCluster { - Name = 'Cluster01' - StaticIPAddress = '192.168.100.20/24' + Name = 'Cluster01' + StaticIPAddress = '192.168.100.20/24' DomainAdministratorCredential = $ActiveDirectoryAdministratorCredential - DependsOn = '[xWaitForCluster]WaitForCluster' + DependsOn = '[xWaitForCluster]WaitForCluster' } } } diff --git a/Tests/TestHelpers/CommonTestHelper.psm1 b/Tests/TestHelpers/CommonTestHelper.psm1 index c37a0c2..7c56f3b 100644 --- a/Tests/TestHelpers/CommonTestHelper.psm1 +++ b/Tests/TestHelpers/CommonTestHelper.psm1 @@ -25,13 +25,13 @@ function Get-InvalidArgumentRecord ) $argumentException = New-Object -TypeName 'ArgumentException' ` - -ArgumentList @( - $Message, - $ArgumentName - ) + -ArgumentList @( + $Message, + $ArgumentName + ) $newObjectParameters = @{ - TypeName = 'System.Management.Automation.ErrorRecord' + TypeName = 'System.Management.Automation.ErrorRecord' ArgumentList = @( $argumentException, $ArgumentName, @@ -72,19 +72,19 @@ function Get-InvalidOperationRecord if ($null -eq $ErrorRecord) { $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` - -ArgumentList @( $Message ) + -ArgumentList @( $Message ) } else { $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` - -ArgumentList @( - $Message, - $ErrorRecord.Exception - ) + -ArgumentList @( + $Message, + $ErrorRecord.Exception + ) } $newObjectParameters = @{ - TypeName = 'System.Management.Automation.ErrorRecord' + TypeName = 'System.Management.Automation.ErrorRecord' ArgumentList = @( $invalidOperationException.ToString(), 'MachineStateIncorrect', @@ -125,25 +125,25 @@ function Get-ObjectNotFoundException if ($null -eq $ErrorRecord) { $objectNotFoundException = New-Object -TypeName 'System.Exception' ` - -ArgumentList @($Message) + -ArgumentList @($Message) } else { $objectNotFoundException = New-Object -TypeName 'System.Exception' ` - -ArgumentList @( - $Message, - $ErrorRecord.Exception - ) + -ArgumentList @( + $Message, + $ErrorRecord.Exception + ) } $newObjectParameters = @{ - TypeName = 'System.Management.Automation.ErrorRecord' + TypeName = 'System.Management.Automation.ErrorRecord' ArgumentList = @( $objectNotFoundException.ToString(), 'MachineStateIncorrect', 'ObjectNotFound', $null - ) + ) } return New-Object @newObjectParameters @@ -178,19 +178,19 @@ function Get-InvalidResultException if ($null -eq $ErrorRecord) { $exception = New-Object -TypeName 'System.Exception' ` - -ArgumentList @($Message) + -ArgumentList @($Message) } else { $exception = New-Object -TypeName 'System.Exception' ` - -ArgumentList @( - $Message, - $ErrorRecord.Exception - ) + -ArgumentList @( + $Message, + $ErrorRecord.Exception + ) } $newObjectParameters = @{ - TypeName = 'System.Management.Automation.ErrorRecord' + TypeName = 'System.Management.Automation.ErrorRecord' ArgumentList = @( $exception.ToString(), 'MachineStateIncorrect', diff --git a/Tests/Unit/CommonResourceHelper.Tests.ps1 b/Tests/Unit/CommonResourceHelper.Tests.ps1 index 177eefc..284b579 100644 --- a/Tests/Unit/CommonResourceHelper.Tests.ps1 +++ b/Tests/Unit/CommonResourceHelper.Tests.ps1 @@ -2,10 +2,10 @@ Describe 'CommonResourceHelper Unit Tests' { BeforeAll { # Import the CommonResourceHelper module to test $dscResourcesFolderFilePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) ` - -ChildPath 'DscResources' + -ChildPath 'DscResources' Import-Module -Name (Join-Path -Path $dscResourcesFolderFilePath ` - -ChildPath 'CommonResourceHelper.psm1') -Force + -ChildPath 'CommonResourceHelper.psm1') -Force } InModuleScope 'CommonResourceHelper' { diff --git a/Tests/Unit/Stubs/Write-ModuleStubFile.ps1 b/Tests/Unit/Stubs/Write-ModuleStubFile.ps1 index 95dcdae..96a75cc 100644 --- a/Tests/Unit/Stubs/Write-ModuleStubFile.ps1 +++ b/Tests/Unit/Stubs/Write-ModuleStubFile.ps1 @@ -59,73 +59,73 @@ function Write-ModuleStubFile $cmdletToStub = Get-Command -Module $module -CommandType 'Cmdlet' $cmdletToStub | ForEach-Object -Begin { - $operatingSystemInformation = Get-CimInstance -class Win32_OperatingSystem + $operatingSystemInformation = Get-CimInstance -class Win32_OperatingSystem + + "<#" + " .SYNOPSIS" + " This is stub cmdlets for the module $($module.Name) which can be used in" + " Pester unit tests to be able to test code without having the actual module installed." + if ($Description) + { + "" + " .DESCRIPTION" + " $($Description -join "`r`n ")" + } + "" + " .NOTES" + " Generated from module $($module.Name) (version $($module.Version.ToString())), on" + " operating system $($operatingSystemInformation.Caption) $($operatingSystemInformation.OSArchitecture) ($($operatingSystemInformation.Version))" + "#>" + "" + "<#" + " Suppressing this rule because these functions are from an external module" + " and are only being used as stubs." + "#>" + "[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingUserNameAndPassWordParams', '')]" + "param()" + "" + } -Process { + $signature = $null + $command = $_ + $endOfDefinition = $false + $metadata = New-Object -TypeName System.Management.Automation.CommandMetaData -ArgumentList $command + $definition = [System.Management.Automation.ProxyCommand]::Create($metadata) + foreach ($line in $definition -split "`n") + { + # Replaces any type the in the $ReplaceType variable with the Object type. + $ReplaceType | ForEach-Object -Process { + $line = $line -replace "\[$_\]", '[Object]' + } + + $line = $line -replace 'SupportsShouldProcess=\$true, ', '' - "<#" - " .SYNOPSIS" - " This is stub cmdlets for the module $($module.Name) which can be used in" - " Pester unit tests to be able to test code without having the actual module installed." - if ($Description) + if ( $line.Contains( '})' ) ) { - "" - " .DESCRIPTION" - " $($Description -join "`r`n ")" + $line = $line.Remove( $line.Length - 2 ) + $endOfDefinition = $true } - "" - " .NOTES" - " Generated from module $($module.Name) (version $($module.Version.ToString())), on" - " operating system $($operatingSystemInformation.Caption) $($operatingSystemInformation.OSArchitecture) ($($operatingSystemInformation.Version))" - "#>" - "" - "<#" - " Suppressing this rule because these functions are from an external module" - " and are only being used as stubs." - "#>" - "[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingUserNameAndPassWordParams', '')]" - "param()" - "" - } -Process { - $signature = $null - $command = $_ - $endOfDefinition = $false - $metadata = New-Object -TypeName System.Management.Automation.CommandMetaData -ArgumentList $command - $definition = [System.Management.Automation.ProxyCommand]::Create($metadata) - foreach ($line in $definition -split "`n") + + if ( $line.Trim() -ne '' ) { - # Replaces any type the in the $ReplaceType variable with the Object type. - $ReplaceType | ForEach-Object -Process { - $line = $line -replace "\[$_\]", '[Object]' - } - - $line = $line -replace 'SupportsShouldProcess=\$true, ', '' - - if ( $line.Contains( '})' ) ) - { - $line = $line.Remove( $line.Length - 2 ) - $endOfDefinition = $true - } - - if ( $line.Trim() -ne '' ) - { - $signature += " $line" - } - else - { - $signature += $line - } - - if ( $endOfDefinition ) - { - $signature += "`n )" - break - } + $signature += " $line" + } + else + { + $signature += $line } - "function $($command.Name) {" - "$signature" - "" - " throw '{0}: StubNotImplemented' -f $`MyInvocation.MyCommand" - "}" - "" - } | Out-String | Out-File (Join-Path -Path $Path -ChildPath "$($ModuleName).stubs.psm1") -Encoding utf8 -Append + if ( $endOfDefinition ) + { + $signature += "`n )" + break + } + } + + "function $($command.Name) {" + "$signature" + "" + " throw '{0}: StubNotImplemented' -f $`MyInvocation.MyCommand" + "}" + "" + } | Out-String | Out-File (Join-Path -Path $Path -ChildPath "$($ModuleName).stubs.psm1") -Encoding utf8 -Append } From 0419fa408c01641aa1563483ebb59be7844abe5d Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 29 Jul 2017 11:23:55 +0200 Subject: [PATCH 17/19] xClusterQuorum: Enabled localization (#138) - Changes to xClusterQuorum - Enabled localization for all strings (issue #87). --- CHANGELOG.md | 1 + .../MSFT_xClusterQuorum/MSFT_xClusterQuorum.psm1 | 11 +++++++++++ .../en-US/MSFT_xClusterQuorum.strings.psd1 | 7 +++++++ 3 files changed, 19 insertions(+) create mode 100644 DSCResources/MSFT_xClusterQuorum/en-US/MSFT_xClusterQuorum.strings.psd1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a52468..3569285 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,7 @@ - 4-SetQuorumToDiskOnly.ps1 - Added links to examples from README.md. - Minor style changes. + - Enabled localization for all strings ([issue #87](https://github.com/PowerShell/xFailOverCluster/issues/87)). - Changes to xClusterPreferredOwner - Enabled localization for all strings ([issue #86](https://github.com/PowerShell/xFailOverCluster/issues/86)). - Fixed typo in the returned hash table from Get-TargetResource. diff --git a/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.psm1 b/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.psm1 index 3be48a9..4967de0 100644 --- a/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.psm1 +++ b/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.psm1 @@ -1,3 +1,8 @@ +Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) ` +-ChildPath 'CommonResourceHelper.psm1') + +$script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xClusterQuorum' + <# .SYNOPSIS Returns the current state of the failover cluster quorum. @@ -17,6 +22,8 @@ function Get-TargetResource $IsSingleInstance ) + Write-Verbose -Message $script:localizedData.GetClusterQuorumInformation + $getClusterQuorumResult = Get-ClusterQuorum switch ($getClusterQuorumResult.QuorumType) @@ -124,6 +131,8 @@ function Set-TargetResource $Resource ) + Write-Verbose -Message ($script:localizedData.SetClusterQuorum -f $Type) + switch ($Type) { 'NodeMajority' @@ -184,6 +193,8 @@ function Test-TargetResource $Resource ) + Write-Verbose -Message $script:localizedData.EvaluatingClusterQuorumInformation + $getGetTargetResourceResult = Get-TargetResource -IsSingleInstance $IsSingleInstance $testTargetResourceReturnValue = $false diff --git a/DSCResources/MSFT_xClusterQuorum/en-US/MSFT_xClusterQuorum.strings.psd1 b/DSCResources/MSFT_xClusterQuorum/en-US/MSFT_xClusterQuorum.strings.psd1 new file mode 100644 index 0000000..26d01cd --- /dev/null +++ b/DSCResources/MSFT_xClusterQuorum/en-US/MSFT_xClusterQuorum.strings.psd1 @@ -0,0 +1,7 @@ +# Localized resources for xClusterQuorum + +ConvertFrom-StringData @' + GetClusterQuorumInformation = Retrieving quorum information for cluster. + SetClusterQuorum = Setting quorum to type '{0}'. + EvaluatingClusterQuorumInformation = Evaluating state of quorum for cluster. +'@ From e32199f1ce8d5def31f2a35d64e1331e982bb53a Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 29 Jul 2017 11:36:27 +0200 Subject: [PATCH 18/19] xFailOverCluster: Fixed Script Analyzer warnings for Write-Verbose (#137) - Changes to xCluster - Fixed Script Analyzer warnings for Write-Verbose. - Changes to xClusterDisk - Fixed Script Analyzer warnings for Write-Verbose. - Changes to xClusterNetwork - Fixed Script Analyzer warnings for Write-Verbose. --- CHANGELOG.md | 3 +++ DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 | 2 ++ DSCResources/MSFT_xCluster/en-US/MSFT_xCluster.strings.psd1 | 1 + DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 | 4 ++++ .../MSFT_xClusterDisk/en-US/MSFT_xClusterDisk.strings.psd1 | 2 ++ DSCResources/MSFT_xClusterNetwork/MSFT_xClusterNetwork.psm1 | 4 ++++ .../en-US/MSFT_xClusterNetwork.strings.psd1 | 2 ++ 7 files changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3569285..f734729 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - Enabled localization for all strings ([issue #84](https://github.com/PowerShell/xFailOverCluster/issues/84)). - Fixed the OutputType data type that was not fully qualified. - Minor style changes. + - Fixed Script Analyzer warnings for Write-Verbose. - Changes to xClusterNetwork - Replaced the URL for the parameter Role in README.md. The new URL is a more generic description of the possible settings for the Role parameter. The @@ -31,6 +32,7 @@ - Fixed typos in parameter descriptions in README.md, comment-based help and schema.mof. - Enabled localization for all strings ([issue #85](https://github.com/PowerShell/xFailOverCluster/issues/85)). - Minor style changes. + - Fixed Script Analyzer warnings for Write-Verbose. - Changes to xCluster - Resolved Script Analyzer rule warnings by changing Get-WmiObject to Get-CimInstance ([issue #49](https://github.com/PowerShell/xFailOverCluster/issues/49)). @@ -41,6 +43,7 @@ - Fixed random problem with tests failing with error "Invalid token for impersonation - it cannot be duplicated." ([issue #133](https://github.com/PowerShell/xFailOverCluster/issues/133)). - Minor style changes. + - Fixed Script Analyzer warnings for Write-Verbose. - Changes to xWaitForCluster - Refactored the unit test for this resource to use stubs and increase coverage ([issue #78](https://github.com/PowerShell/xFailOverCluster/issues/78)). diff --git a/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 b/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 index 1d73ebe..66494ae 100644 --- a/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 +++ b/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 @@ -34,6 +34,8 @@ function Get-TargetResource $DomainAdministratorCredential ) + Write-Verbose -Message ($script:localizedData.GetClusterInformation -f $Name) + $computerInformation = Get-CimInstance -ClassName Win32_ComputerSystem if (($null -eq $computerInformation) -or ($null -eq $computerInformation.Domain)) { diff --git a/DSCResources/MSFT_xCluster/en-US/MSFT_xCluster.strings.psd1 b/DSCResources/MSFT_xCluster/en-US/MSFT_xCluster.strings.psd1 index d9f3bf1..353c2d4 100644 --- a/DSCResources/MSFT_xCluster/en-US/MSFT_xCluster.strings.psd1 +++ b/DSCResources/MSFT_xCluster/en-US/MSFT_xCluster.strings.psd1 @@ -18,4 +18,5 @@ ConvertFrom-StringData @' FailedCreatingCluster = Cluster creation failed. Please verify output of 'Get-Cluster' command. UnableToImpersonateUser = Can't logon as user {0}. UnableToCloseToken = Can't close impersonation token {0}. + GetClusterInformation = Retrieving information for cluster {0}. '@ diff --git a/DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 b/DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 index 491db1f..0cfcd56 100644 --- a/DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 +++ b/DSCResources/MSFT_xClusterDisk/MSFT_xClusterDisk.psm1 @@ -21,6 +21,8 @@ function Get-TargetResource $Number ) + Write-Verbose -Message ($script:localizedData.GetClusterDiskInformation -f $Number) + if ($null -ne ($diskInstance = Get-CimInstance -ClassName MSCluster_Disk -Namespace 'Root\MSCluster' -Filter "Number = $Number")) { $diskResource = Get-ClusterResource | @@ -163,6 +165,8 @@ function Test-TargetResource $Label ) + Write-Verbose -Message ($script:localizedData.EvaluatingClusterDiskInformation -f $Number) + $getTargetResourceResult = Get-TargetResource -Number $Number if($Ensure -eq 'Present') diff --git a/DSCResources/MSFT_xClusterDisk/en-US/MSFT_xClusterDisk.strings.psd1 b/DSCResources/MSFT_xClusterDisk/en-US/MSFT_xClusterDisk.strings.psd1 index 1bb5917..761c712 100644 --- a/DSCResources/MSFT_xClusterDisk/en-US/MSFT_xClusterDisk.strings.psd1 +++ b/DSCResources/MSFT_xClusterDisk/en-US/MSFT_xClusterDisk.strings.psd1 @@ -4,4 +4,6 @@ ConvertFrom-StringData @' AddDiskToCluster = Add the disk {0} to the cluster. SetDiskLabel = Set the disk label for the disk {0} to '{1}'. RemoveDiskFromCluster = Remove the disk {0} from the cluster. + GetClusterDiskInformation = Retrieving information for cluster disk {0}. + EvaluatingClusterDiskInformation = Evaluating state of cluster disk {0}. '@ diff --git a/DSCResources/MSFT_xClusterNetwork/MSFT_xClusterNetwork.psm1 b/DSCResources/MSFT_xClusterNetwork/MSFT_xClusterNetwork.psm1 index fb013c4..494be5a 100644 --- a/DSCResources/MSFT_xClusterNetwork/MSFT_xClusterNetwork.psm1 +++ b/DSCResources/MSFT_xClusterNetwork/MSFT_xClusterNetwork.psm1 @@ -28,6 +28,8 @@ function Get-TargetResource $AddressMask ) + Write-Verbose -Message ($script:localizedData.GetClusterNetworkInformation -f $Name) + $NetworkResource = Get-ClusterNetwork | Where-Object -FilterScript { $_.Address -eq $Address -and $_.AddressMask -eq $AddressMask } @@ -198,6 +200,8 @@ function Test-TargetResource $Metric ) + Write-Verbose -Message ($script:localizedData.EvaluatingClusterNetworkInformation -f $Name) + $getTargetResourceResult = Get-TargetResource -Address $Address -AddressMask $AddressMask $testTargetResourceReturnValue = $true diff --git a/DSCResources/MSFT_xClusterNetwork/en-US/MSFT_xClusterNetwork.strings.psd1 b/DSCResources/MSFT_xClusterNetwork/en-US/MSFT_xClusterNetwork.strings.psd1 index 6341410..06a0a88 100644 --- a/DSCResources/MSFT_xClusterNetwork/en-US/MSFT_xClusterNetwork.strings.psd1 +++ b/DSCResources/MSFT_xClusterNetwork/en-US/MSFT_xClusterNetwork.strings.psd1 @@ -4,4 +4,6 @@ ConvertFrom-StringData @' ChangeNetworkName = Changing the name of the network {0}/{1} to '{2}'. ChangeNetworkRole = Changing the role of the network {0}/{1} to '{2}'. ChangeNetworkMetric = Changing the metric of the network {0}/{1} to '{2}'. + GetClusterNetworkInformation = Retrieving information for cluster network {0}. + EvaluatingClusterNetworkInformation = Evaluating state of cluster network {0}. '@ From 66fd10d2d66ad2bccfbef426aa74b901bd7e6a27 Mon Sep 17 00:00:00 2001 From: Katie Keim Date: Wed, 23 Aug 2017 12:59:01 -0700 Subject: [PATCH 19/19] Releasing version 1.8.0.0 --- CHANGELOG.md | 2 + xFailOverCluster.psd1 | 152 +++++++++++++++++------------------------- 2 files changed, 65 insertions(+), 89 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f734729..9a90e9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.8.0.0 + - Changes to xFailOverCluster - Added a common resource helper module with helper functions for localization. - Added helper functions; Get-LocalizedData, New-InvalidResultException, diff --git a/xFailOverCluster.psd1 b/xFailOverCluster.psd1 index ee2b65b..73e91f6 100644 --- a/xFailOverCluster.psd1 +++ b/xFailOverCluster.psd1 @@ -1,6 +1,6 @@ @{ -ModuleVersion = '1.7.0.0' +ModuleVersion = '1.8.0.0' GUID = '026e7fd8-06dd-41bc-b373-59366ab18679' @@ -33,102 +33,75 @@ PrivateData = @{ # IconUri = '' # ReleaseNotes of this module - ReleaseNotes = '- Changes to xClusterPreferredOwner - - Script Analyzer warnings have been fixed (issue 50). This also failed the - tests for the resource. + ReleaseNotes = '- Changes to xFailOverCluster + - Added a common resource helper module with helper functions for localization. + - Added helper functions; Get-LocalizedData, New-InvalidResultException, + New-ObjectNotFoundException, New-InvalidOperationException and + New-InvalidArgumentException. + - Fixed lint error MD034 and fixed typos in README.md. + - Opt-in for module files common tests ([issue 119](https://github.com/PowerShell/xFailOverCluster/issues/119)). + - Removed Byte Order Mark (BOM) from the files; CommonResourceHelper.psm1 and FailoverClusters.stubs.psm1. + - Opt-in for script files common tests ([issue 121](https://github.com/PowerShell/xFailOverCluster/issues/121)). + - Removed Byte Order Mark (BOM) from the files; CommonResourceHelper.Tests.ps1, + MSFT\_xCluster.Tests.ps1, MSFT\_xClusterDisk.Tests.ps1, + MSFT\_xClusterPreferredOwner.Tests.ps1, MSFT_xWaitForCluster.Tests.ps1. + - Added common test helper functions to help test the throwing of localized error strings. + - Get-InvalidArgumentRecord + - Get-InvalidOperationRecord + - Get-ObjectNotFoundException + - Get-InvalidResultException. + - Updated year to 2017 in license file and module manifest ([issue 131](https://github.com/PowerShell/xFailOverCluster/issues/131)). - Changes to xClusterDisk - - Fixed test that was failing in AppVeyor (issue 55). -- Changes to xFailOverCluster - - Added "Code of Conduct" text to the README.md (issue 44). - - Added TOC for all resources in the README.md (issue 43). - - Fixed typos and lint errors in README.md. - - Fixed style issue in example in README.md. - - Removed "Unreleased" "tag" from the resources xClusterQuorum and - xClusterDisk (issue 36) - - Added new sections to each resource (Requirements, Parameters and Examples) - in the README.md. Some does not yet have any examples, so they are set to - "None.". - - Added GitHub templates PULL\_REQUEST\_TEMPLATE, ISSUE_TEMPLATE and - CONTRIBUTING.md (issue 45). - - Split the change log from README.md to a separate file CHANGELOG.md - (issue 48). - - Added the resource xClusterPreferredOwner to README.md (issue 51). - - Added the resource xClusterNetwork to README.md (issue 56). - - Removed Credential parameter from parameter list for xWaitForCluster. - Parameter Credential does not exist in the schema.mof of the resource - (issue 62). - - Now all parameters in the README.md list their data type and type qualifier - (issue 58.) - - Added Import-DscResource to example in README.md. - - Added CodeCov and opt-in for all common tests (issue 41). - - Added CodeCov badge to README.md - - Fixed CodeCov badge links so they now can be clicked on. - - Fixed lint rule MD013 in CHANGELOG.md. - - Fixed lint rule MD013 in README.md. - - Fixed lint rule MD024 in README.md. - - Fixed lint rule MD032 in README.md. - - Removed example from README.md (issue 42). - - Fixed typo in filename for ISSUE\_TEMPLATE. Was "ISSUE\_TEMPLATE", now it is - correctly "ISSUE\_TEMPLATE.md". - - Changed appveyor.yml to use the new default test framework in the AppVeyor - module in DscResource.Tests (AppVeyor.psm1). - - Added VS Code workspace settings file with formatting settings matching the - Style Guideline (issue 67). That will make it possible inside VS Code to - press SHIFT+ALT+F, or press F1 and choose "Format document" in the list. The - PowerShell code will then be formatted according to the Style Guideline - (although maybe not complete, but would help a long way). - - Added new stubs for FailoverClusters module - (Tests\Unit\Stubs\FailoverClusters.stubs.psm1) to be able to run unit tests - on a computer that does not have or can install Failover Clustering - PowerShell module. - - Added a script file (Tests\Unit\Stubs\Write-ModuleStubFile.ps1) to be able - to rebuild the stub file (FailoverClusters.stubs.psm1) whenever needed. - - Added code block around types in README.md. + - Enabled localization for all strings ([issue 84](https://github.com/PowerShell/xFailOverCluster/issues/84)). + - Fixed the OutputType data type that was not fully qualified. + - Minor style changes. + - Fixed Script Analyzer warnings for Write-Verbose. +- Changes to xClusterNetwork + - Replaced the URL for the parameter Role in README.md. The new URL is a more + generic description of the possible settings for the Role parameter. The + previous URL was still correct but focused on Hyper-V in particular. + - Fixed typos in parameter descriptions in README.md, comment-based help and schema.mof. + - Enabled localization for all strings ([issue 85](https://github.com/PowerShell/xFailOverCluster/issues/85)). + - Minor style changes. + - Fixed Script Analyzer warnings for Write-Verbose. - Changes to xCluster - - Added examples - - 1-CreateFirstNodeOfAFailoverCluster.ps1 - - 2-JoinAdditionalNodeToFailoverCluster.ps1 - - 3-CreateFailoverClusterWithTwoNodes.ps1 (this is the example from README.md) - - Fixed typo in xCluster parameter description. - - Added links to examples from README.md - - Refactored the unit test for this resource to use stubs and increase coverage - (issue 73). - - Removed the password file (MSFT_xCluster.password.txt) which seemed unnecessary. - - Test-TargetResource now throws and error if domain name cannot be evaluated - (issue 72). - - Set-TargetResource now correctly throws and error if domain name cannot be - evaluated (issue 71). + - Resolved Script Analyzer rule warnings by changing Get-WmiObject to + Get-CimInstance ([issue 49](https://github.com/PowerShell/xFailOverCluster/issues/49)). + - Minor style change in tests. Removed "-" in front of "-Be", "-Not", "-Throw", + etc. + - Enabled localization for all strings ([issue 83](https://github.com/PowerShell/xFailOverCluster/issues/83)). + - Added tests to improve code coverage. + - Fixed random problem with tests failing with error "Invalid token for + impersonation - it cannot be duplicated." ([issue 133](https://github.com/PowerShell/xFailOverCluster/issues/133)). + - Minor style changes. + - Fixed Script Analyzer warnings for Write-Verbose. - Changes to xWaitForCluster - - Added example - - 1-WaitForFailoverClusterToBePresent.ps1 - - Added link to example from README.md -- Changes to xClusterDisk - Refactored the unit test for this resource to use stubs and increase coverage - (issue 74). - - Removed an evaluation that called Test-TargetResource in Set-TargetResource - method and instead added logic so that Set-TargetResource evaluates if it - should remove a disk (issue 90). + ([issue 78](https://github.com/PowerShell/xFailOverCluster/issues/78)). + - Now the Test-TargetResource correctly returns false if the domain name cannot + be evaluated ([issue 107](https://github.com/PowerShell/xFailOverCluster/issues/107)). - Changed the code to be more aligned with the style guideline. - - Added examples (issue 46) - - 1-AddClusterDisk.ps1 - - 2-RemoveClusterDisk.ps1 - - Added links to examples from README.md. -- Changes to xClusterPreferredOwner + - Updated parameter description in the schema.mof. + - Resolved Script Analyzer warnings ([issue 54](https://github.com/PowerShell/xFailOverCluster/issues/54)). + - Enabled localization for all strings ([issue 88](https://github.com/PowerShell/xFailOverCluster/issues/88)). + - Minor style changes. +- Changes to xClusterQuorum - Refactored the unit test for this resource to use stubs and increase coverage - (issue 76). + ([issue 77](https://github.com/PowerShell/xFailOverCluster/issues/77)). - Changed the code to be more aligned with the style guideline. - - Added examples (issue 52) - - 1-AddPreferredOwner.ps1 - - 2-RemovePreferredOwner.ps1 - - Added links to examples from README.md. -- Changes to xClusterNetwork - - Refactored the unit test for this resource to use stubs and increase coverage - (issue 75). - - Changed the code to be more aligned with the style guideline. - - Updated resource and parameter description in README.md and schema.mof. - - Added example (issue 57) - - 1-ChangeClusterNetwork.ps1 + - Updated parameter description in the schema.mof. + - Added example ([issue 47](https://github.com/PowerShell/xFailOverCluster/issues/47)) + - 1-SetQuorumToNodeMajority.ps1 + - 2-SetQuorumToNodeAndDiskMajority.ps1 + - 3-SetQuorumToNodeAndFileShareMajority.ps1 + - 4-SetQuorumToDiskOnly.ps1 - Added links to examples from README.md. + - Minor style changes. + - Enabled localization for all strings ([issue 87](https://github.com/PowerShell/xFailOverCluster/issues/87)). +- Changes to xClusterPreferredOwner + - Enabled localization for all strings ([issue 86](https://github.com/PowerShell/xFailOverCluster/issues/86)). + - Fixed typo in the returned hash table from Get-TargetResource. + - Minor style changes. ' @@ -138,3 +111,4 @@ PrivateData = @{ } +