diff --git a/DSCResources/MSFT_xDFSNamespaceServerConfiguration/MSFT_xDFSNamespaceServerConfiguration.psm1 b/DSCResources/MSFT_xDFSNamespaceServerConfiguration/MSFT_xDFSNamespaceServerConfiguration.psm1 new file mode 100644 index 0000000..0ff3334 --- /dev/null +++ b/DSCResources/MSFT_xDFSNamespaceServerConfiguration/MSFT_xDFSNamespaceServerConfiguration.psm1 @@ -0,0 +1,236 @@ +data LocalizedData +{ +# culture="en-US" +ConvertFrom-StringData -StringData @' +GettingNamespaceServerConfigurationMessage=Getting DFS Namespace Server Configuration. +SettingNamespaceServerConfigurationMessage=Setting DFS Namespace Server Configuration. +NamespaceServerConfigurationUpdateParameterMessage=Setting DFS Namespace Server Configuration parameter {0} to "{1}". +NamespaceServerConfigurationUpdatedMessage=Setting DFS Namespace Server Configuration updated. +NamespaceServerConfigurationServiceRestartedMessage=DFS Namespace Server restarted. +TestingNamespaceServerConfigurationMessage=Testing DFS Namespace Server Configuration. +NamespaceServerConfigurationParameterNeedsUpdateMessage=DFS Namespace Server Configuration parameter "{0}" is "{1}" but should be "{2}". Change required. +'@ +} + +<# + This is an array of all the parameters used by this resource + If the property Restart is true then when this property is updated the service + Will be restarted. +#> +data ParameterList +{ + @( + @{ Name = 'LdapTimeoutSec'; Type = 'Uint32' }, + @{ Name = 'SyncIntervalSec'; Type = 'String' }, + @{ Name = 'UseFQDN'; Type = 'Uint32'; Restart = $True } + ) +} + +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([Hashtable])] + param + ( + [parameter(Mandatory = $true)] + [ValidateSet('Yes')] + [String] + $IsSingleInstance + ) + + Write-Verbose -Message ( @( + "$($MyInvocation.MyCommand): " + $($LocalizedData.GettingNamespaceServerConfigurationMessage) + ) -join '' ) + + # The ComputerName will always be LocalHost unless a good reason can be provided to + # enable it as a parameter. + $ComputerName = 'LocalHost' + + # Get the current DFSN Server Configuration + $ServerConfiguration = Get-DfsnServerConfiguration ` + -ComputerName $ComputerName ` + -ErrorAction Stop + + # Generate the return object. + $ReturnValue = @{ + IsSingleInstance = 'Yes' + } + foreach ($parameter in $ParameterList) + { + $ReturnValue += @{ $parameter.Name = $ServerConfiguration.$($parameter.name) } + } # foreach + + return $ReturnValue +} # Get-TargetResource + +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [parameter(Mandatory = $true)] + [ValidateSet('Yes')] + [String] + $IsSingleInstance, + + [Uint32] + $LdapTimeoutSec, + + [Uint32] + $SyncIntervalSec, + + [Boolean] + $UseFQDN + ) + + Write-Verbose -Message ( @( + "$($MyInvocation.MyCommand): " + $($LocalizedData.SettingNamespaceServerConfigurationMessage) + ) -join '' ) + + # The ComputerName will always be LocalHost unless a good reason can be provided to + # enable it as a parameter. + $ComputerName = 'LocalHost' + + # Get the current DFSN Server Configuration + $ServerConfiguration = Get-DfsnServerConfiguration ` + -ComputerName $ComputerName ` + -ErrorAction Stop + + # Generate a list of parameters that will need to be changed. + $ChangeParameters = @{} + $Restart = $False + foreach ($parameter in $ParameterList) + { + $ParameterSource = $ServerConfiguration.$($parameter.name) + $ParameterNew = (Invoke-Expression -Command "`$$($parameter.name)") + if ($PSBoundParameters.ContainsKey($parameter.Name) ` + -and ($ParameterSource -ne $ParameterNew)) + { + $ChangeParameters += @{ + $($parameter.name) = $ParameterNew + } + Write-Verbose -Message ( @( + "$($MyInvocation.MyCommand): " + $($LocalizedData.NamespaceServerConfigurationUpdateParameterMessage) ` + -f $parameter.Name,$ParameterNew + ) -join '' ) + if ($parameter.Restart) + { + $Restart = $True + } # if + } # if + } # foreach + if ($ChangeParameters.Count -gt 0) + { + # Update any parameters that were identified as different + $null = Set-DfsnServerConfiguration ` + -ComputerName $ComputerName ` + @ChangeParameters ` + -ErrorAction Stop + + Write-Verbose -Message ( @( + "$($MyInvocation.MyCommand): " + $($LocalizedData.NamespaceServerConfigurationUpdatedMessage) + ) -join '' ) + + if ($Restart) + { + # Restart the DFS Service + $null = Restart-Service ` + -Name DFS ` + -Force ` + -ErrorAction Stop + + Write-Verbose -Message ( @( + "$($MyInvocation.MyCommand): " + $($LocalizedData.NamespaceServerConfigurationServiceRestartedMessage) + ) -join '' ) + } + } # if +} # Set-TargetResource + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [parameter(Mandatory = $true)] + [ValidateSet('Yes')] + [String] + $IsSingleInstance, + + [Uint32] + $LdapTimeoutSec, + + [Uint32] + $SyncIntervalSec, + + [Boolean] + $UseFQDN + ) + + Write-Verbose -Message ( @( + "$($MyInvocation.MyCommand): " + $($LocalizedData.TestingNamespaceServerConfigurationMessage) + ) -join '' ) + + # The ComputerName will always be LocalHost unless a good reason can be provided to + # enable it as a parameter. + $ComputerName = 'LocalHost' + + # Flag to signal whether settings are correct + [Boolean] $DesiredConfigurationMatch = $true + + # Get the current DFSN Server Configuration + $ServerConfiguration = Get-DfsnServerConfiguration ` + -ComputerName $ComputerName ` + -ErrorAction Stop + + # Check each parameter + foreach ($parameter in $ParameterList) + { + $ParameterSource = $ServerConfiguration.$($parameter.name) + $ParameterNew = (Invoke-Expression -Command "`$$($parameter.name)") + if ($PSBoundParameters.ContainsKey($parameter.Name) ` + -and ($ParameterSource -ne $ParameterNew)) { + Write-Verbose -Message ( @( + "$($MyInvocation.MyCommand): " + $($LocalizedData.NamespaceServerConfigurationParameterNeedsUpdateMessage) ` + -f $parameter.Name,$ParameterSource,$ParameterNew + ) -join '' ) + $desiredConfigurationMatch = $false + } # if + } # foreach + + return $DesiredConfigurationMatch +} # Test-TargetResource + +# Helper Functions +function New-TerminatingError +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory)] + [String] $ErrorId, + + [Parameter(Mandatory)] + [String] $ErrorMessage, + + [Parameter(Mandatory)] + [System.Management.Automation.ErrorCategory] $ErrorCategory + ) + + $exception = New-Object ` + -TypeName System.InvalidOperationException ` + -ArgumentList $errorMessage + $errorRecord = New-Object ` + -TypeName System.Management.Automation.ErrorRecord ` + -ArgumentList $exception, $errorId, $errorCategory, $null + $PSCmdlet.ThrowTerminatingError($errorRecord) +} + +Export-ModuleMember -Function *-TargetResource diff --git a/DSCResources/MSFT_xDFSNamespaceServerConfiguration/MSFT_xDFSNamespaceServerConfiguration.schema.mof b/DSCResources/MSFT_xDFSNamespaceServerConfiguration/MSFT_xDFSNamespaceServerConfiguration.schema.mof new file mode 100644 index 0000000..e43822e --- /dev/null +++ b/DSCResources/MSFT_xDFSNamespaceServerConfiguration/MSFT_xDFSNamespaceServerConfiguration.schema.mof @@ -0,0 +1,8 @@ +[ClassVersion("1.0.0.0"), FriendlyName("xDFSNamespaceServerConfiguration")] +class MSFT_xDFSNamespaceServerConfiguration : OMI_BaseResource +{ + [Key, Description("Specifies the resource is a single instance, the value must be 'Yes'"), ValueMap{"Yes"}, Values{"Yes"}] String IsSingleInstance; + [Write, Description("Specifies a time-out value, in seconds, for Lightweight Directory Access Protocol (LDAP) requests for the DFS namespace server.")] Uint32 LdapTimeoutSec; + [Write, Description("This interval controls how often domain-based DFS namespace root servers and domain controllers connect to the PDC emulator to get updates of DFS namespace metadata.")] Uint32 SyncIntervalSec; + [Write, Description("Indicates whether a DFS namespace server uses FQDNs in referrals.")] Boolean UseFQDN; +}; diff --git a/Examples/Sample_xDFSNamespaceRoot_Domain_MultipleTarget.ps1 b/Examples/Sample_xDFSNamespace_Domain_MultipleTarget.ps1 similarity index 100% rename from Examples/Sample_xDFSNamespaceRoot_Domain_MultipleTarget.ps1 rename to Examples/Sample_xDFSNamespace_Domain_MultipleTarget.ps1 diff --git a/Examples/Sample_xDFSNamespaceRoot_Domain_SingleTarget.ps1 b/Examples/Sample_xDFSNamespace_Domain_SingleTarget.ps1 similarity index 100% rename from Examples/Sample_xDFSNamespaceRoot_Domain_SingleTarget.ps1 rename to Examples/Sample_xDFSNamespace_Domain_SingleTarget.ps1 diff --git a/Examples/Sample_xDFSNamespaceRoot_Standalone.ps1 b/Examples/Sample_xDFSNamespace_Standalone.ps1 similarity index 88% rename from Examples/Sample_xDFSNamespaceRoot_Standalone.ps1 rename to Examples/Sample_xDFSNamespace_Standalone.ps1 index 9d55180..a876b1f 100644 --- a/Examples/Sample_xDFSNamespaceRoot_Standalone.ps1 +++ b/Examples/Sample_xDFSNamespace_Standalone.ps1 @@ -1,4 +1,4 @@ -Configuration DFSNamespace_Standalone_Public +Configuration DFSNamespace_Standalone { param ( @@ -12,10 +12,10 @@ Configuration DFSNamespace_Standalone_Public { # Install the Prerequisite features first # Requires Windows Server 2012 R2 Full install - WindowsFeature RSATDFSMgmtConInstall - { - Ensure = "Present" - Name = "RSAT-DFS-Mgmt-Con" + WindowsFeature RSATDFSMgmtConInstall + { + Ensure = "Present" + Name = "RSAT-DFS-Mgmt-Con" } WindowsFeature DFS @@ -56,7 +56,7 @@ $ConfigData = @{ } ) } -DFSNamespace_Standalone_Public ` +DFSNamespace_Standalone ` -configurationData $ConfigData ` -Credential (Get-Credential -Message "Domain Credentials") Start-DscConfiguration ` @@ -64,5 +64,5 @@ Start-DscConfiguration ` -Force ` -Verbose ` -ComputerName $ComputerName ` - -Path $PSScriptRoot\DFSNamespace_Standalone_Public ` + -Path $PSScriptRoot\DFSNamespace_Standalone ` -Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") diff --git a/Examples/Sample_xDFSNamespace_Standalone_FQDN.ps1 b/Examples/Sample_xDFSNamespace_Standalone_FQDN.ps1 new file mode 100644 index 0000000..da4b4db --- /dev/null +++ b/Examples/Sample_xDFSNamespace_Standalone_FQDN.ps1 @@ -0,0 +1,76 @@ +Configuration DFSNamespace_Standalone_FQDN +{ + param + ( + [Parameter(Mandatory)] + [pscredential] $Credential + ) + + Import-DscResource -ModuleName 'xDFS' + + Node $NodeName + { + # Install the Prerequisite features first + # Requires Windows Server 2012 R2 Full install + WindowsFeature RSATDFSMgmtConInstall + { + Ensure = "Present" + Name = "RSAT-DFS-Mgmt-Con" + } + + WindowsFeature DFS + { + Name = 'FS-DFS-Namespace' + Ensure = 'Present' + } + + # Configure the namespace server + xDFSNamespaceServerConfiguration DFSNamespaceConfig + { + IsSingleInstance = 'Yes' + UseFQDN = $true + PsDscRunAsCredential = $Credential + } # End of xDFSNamespaceServerConfiguration Resource + + # Configure the namespace + xDFSNamespaceRoot DFSNamespaceRoot_Standalone_Public + { + Path = '\\fileserver1.contoso.com\public' + TargetPath = '\\fileserver1.contoso.com\public' + Ensure = 'present' + Type = 'Standalone' + Description = 'Standalone DFS namespace for storing public files' + PsDscRunAsCredential = $Credential + } # End of DFSNamespaceRoot Resource + + # Configure the namespace folder + xDFSNamespaceFolder DFSNamespaceFolder_Standalone_PublicBrochures + { + Path = '\\fileserver1.contoso.com\public\brochures' + TargetPath = '\\fileserver2.contoso.com\brochures' + Ensure = 'present' + Description = 'Standalone DFS namespace for storing public brochure files' + PsDscRunAsCredential = $Credential + } # End of DFSNamespaceFolder Resource + } +} +$ComputerName = Read-Host -Prompt 'Computer Name' +$ConfigData = @{ + AllNodes = @( + @{ + Nodename = $ComputerName + CertificateFile = "C:\publicKeys\targetNode.cer" + Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" + } + ) +} +DFSNamespace_Standalone_FQDN ` + -configurationData $ConfigData ` + -Credential (Get-Credential -Message "Domain Credentials") +Start-DscConfiguration ` + -Wait ` + -Force ` + -Verbose ` + -ComputerName $ComputerName ` + -Path $PSScriptRoot\DFSNamespace_Standalone_FQDN ` + -Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") diff --git a/README.md b/README.md index 7eff602..e0a8673 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,9 @@ The **xDFS** module contains DSC resources for configuring Distributed File System Replication and Namespaces. Currently in this version only Replication folders are supported. Namespaces will be supported in a future release. +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + ## Requirements * **Windows Management Framework 5.0**: Required because the PSDSCRunAsCredential DSC Resource parameter is needed. @@ -155,10 +158,10 @@ configuration Sample_xDFSReplicationGroup { # Install the Prerequisite features first # Requires Windows Server 2012 R2 Full install - WindowsFeature RSATDFSMgmtConInstall - { - Ensure = "Present" - Name = "RSAT-DFS-Mgmt-Con" + WindowsFeature RSATDFSMgmtConInstall + { + Ensure = "Present" + Name = "RSAT-DFS-Mgmt-Con" } # Configure the Replication Group @@ -374,6 +377,15 @@ This resource is used to create, edit or remove folders from DFS namespaces. Wh * **ReferralPriorityClass**: Specifies the target priority class for a DFS namespace folder. { Global-High | SiteCost-High | SiteCost-Normal | SiteCost-Low | Global-Low }. Optional. * **ReferralPriorityRank**: Specifies the priority rank, as an integer, for a target in the DFS namespace. Uint32. Optional +### xDFSNamespaceServerConfiguration +This resource is used to configure DFS Namespace server settings. This is a single instance resource that can only be used once in a DSC Configuration. + +#### Parameters +* **IsSingleInstance**: Specifies if the resource is a single instance, the value must be 'Yes'. Required. +* **LdapTimeoutSec**: Specifies a time-out value, in seconds, for Lightweight Directory Access Protocol (LDAP) requests for the DFS namespace server. Uint32. Optional. +* **SyncIntervalSec**: This interval controls how often domain-based DFS namespace root servers and domain controllers connect to the PDC emulator to get updates of DFS namespace metadata. Uint32. Optional. +* **UseFQDN**: Indicates whether a DFS namespace server uses FQDNs in referrals. Boolean. Optional. + ### Examples Create an AD Domain V2 based DFS namespace called departments in the domain contoso.com with a single root target on the computer fs_1. Two subfolders are defined with targets that direct to shares on servers fs_3 and fs_8. ```powershell @@ -406,7 +418,7 @@ Configuration DFSNamespace_Domain_SingleTarget # Configure the namespace xDFSNamespaceRoot DFSNamespaceRoot_Domain_Departments { - Path = '\\contoso.com\departments' + Path = '\\contoso.com\departments' TargetPath = '\\fs_1\departments' Ensure = 'present' Type = 'DomainV2' @@ -418,7 +430,7 @@ Configuration DFSNamespace_Domain_SingleTarget # Configure the namespace folders xDFSNamespaceFolder DFSNamespaceFolder_Domain_Finance { - Path = '\\contoso.com\departments\finance' + Path = '\\contoso.com\departments\finance' TargetPath = '\\fs_3\Finance' Ensure = 'present' Description = 'AD Domain based DFS namespace folder for storing finance files' @@ -428,7 +440,7 @@ Configuration DFSNamespace_Domain_SingleTarget xDFSNamespaceFolder DFSNamespaceFolder_Domain_Management { - Path = '\\contoso.com\departments\management' + Path = '\\contoso.com\departments\management' TargetPath = '\\fs_8\Management' Ensure = 'present' Description = 'AD Domain based DFS namespace folder for storing management files' @@ -572,7 +584,7 @@ Start-DscConfiguration ` Create a standalone DFS namespace called public on the server fileserver1. A namespace folder called Brochures is also created in this namespace that targets the \\fileserver2\brochures share. ```powershell -Configuration DFSNamespace_Standalone_Public +Configuration DFSNamespace_Standalone { param ( @@ -587,7 +599,7 @@ Configuration DFSNamespace_Standalone_Public # Install the Prerequisite features first # Requires Windows Server 2012 R2 Full install WindowsFeature RSATDFSMgmtConInstall - { + { Ensure = "Present" Name = "RSAT-DFS-Mgmt-Con" } @@ -630,7 +642,7 @@ $ConfigData = @{ } ) } -DFSNamespace_Standalone_Public ` +DFSNamespace_Standalone ` -configurationData $ConfigData ` -Credential (Get-Credential -Message "Domain Credentials") Start-DscConfiguration ` @@ -638,11 +650,98 @@ Start-DscConfiguration ` -Force ` -Verbose ` -ComputerName $ComputerName ` - -Path $PSScriptRoot\DFSNamespace_Standalone_Public ` + -Path $PSScriptRoot\DFSNamespace_Standalone ` + -Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") +``` + +Create a standalone DFS namespace using FQDN called public on the server fileserver1.contoso.com. A namespace folder called Brochures is also created in this namespace that targets the \\fileserver2.contoso.com\brochures share. +```powershell +Configuration DFSNamespace_Standalone_FQDN +{ + param + ( + [Parameter(Mandatory)] + [pscredential] $Credential + ) + + Import-DscResource -ModuleName 'xDFS' + + Node $NodeName + { + # Install the Prerequisite features first + # Requires Windows Server 2012 R2 Full install + WindowsFeature RSATDFSMgmtConInstall + { + Ensure = "Present" + Name = "RSAT-DFS-Mgmt-Con" + } + + WindowsFeature DFS + { + Name = 'FS-DFS-Namespace' + Ensure = 'Present' + } + + # Configure the namespace server + xDFSNamespaceServerConfiguration DFSNamespaceConfig + { + IsSingleInstance = 'Yes' + UseFQDN = $true + PsDscRunAsCredential = $Credential + } # End of xDFSNamespaceServerConfiguration Resource + + # Configure the namespace + xDFSNamespaceRoot DFSNamespaceRoot_Standalone_Public + { + Path = '\\fileserver1.contoso.com\public' + TargetPath = '\\fileserver1.contoso.com\public' + Ensure = 'present' + Type = 'Standalone' + Description = 'Standalone DFS namespace for storing public files' + PsDscRunAsCredential = $Credential + } # End of DFSNamespaceRoot Resource + + # Configure the namespace folder + xDFSNamespaceFolder DFSNamespaceFolder_Standalone_PublicBrochures + { + Path = '\\fileserver1.contoso.com\public\brochures' + TargetPath = '\\fileserver2.contoso.com\brochures' + Ensure = 'present' + Description = 'Standalone DFS namespace for storing public brochure files' + PsDscRunAsCredential = $Credential + } # End of DFSNamespaceFolder Resource + } +} +$ComputerName = Read-Host -Prompt 'Computer Name' +$ConfigData = @{ + AllNodes = @( + @{ + Nodename = $ComputerName + CertificateFile = "C:\publicKeys\targetNode.cer" + Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" + } + ) +} +DFSNamespace_Standalone_FQDN ` + -configurationData $ConfigData ` + -Credential (Get-Credential -Message "Domain Credentials") +Start-DscConfiguration ` + -Wait ` + -Force ` + -Verbose ` + -ComputerName $ComputerName ` + -Path $PSScriptRoot\DFSNamespace_Standalone_FQDN ` -Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") ``` ## Versions +### Unreleased + +### 3.1.0.0 +* MSFT_xDFSNamespaceServerConfiguration- resource added. +* Corrected names of DFS Namespace sample files to indicate that they are setting Namespace roots and folders. +* Removed Pester version from AppVeyor.yml. + ### 3.0.0.0 * RepGroup renamed to ReplicationGroup in all files. * xDFSReplicationGroupConnection- Changed DisableConnection parameter to EnsureEnabled. @@ -657,8 +756,8 @@ Start-DscConfiguration ` * MSFT_xDFSRepGroup- Fixed issue when using FQDN member names. * MSFT_xDFSRepGroupMembership- Fixed issue with Get-TargetResource when using FQDN ComputerName. * MSFT_xDFSRepGroupConnection- Fixed issue with Get-TargetResource when using FQDN SourceComputerName or FQDN DestinationComputerName. -* MSFT_xDFSNamespaceRoot- Added write support to TimeToLiveSec parameter. -* MSFT_xDFSNamespaceFolder- Added write support to TimeToLiveSec parameter. +* MSFT_xDFSNamespaceRoot- Added write support to TimeToLiveSec parameter. +* MSFT_xDFSNamespaceFolder- Added write support to TimeToLiveSec parameter. ### 2.0.0.0 * MSFT_xDFSNamespaceRoot- resource added. diff --git a/Tests/Integration/MSFT_xDFSNamespaceServerConfiguration.Integration.Tests.ps1 b/Tests/Integration/MSFT_xDFSNamespaceServerConfiguration.Integration.Tests.ps1 new file mode 100644 index 0000000..aa4e936 --- /dev/null +++ b/Tests/Integration/MSFT_xDFSNamespaceServerConfiguration.Integration.Tests.ps1 @@ -0,0 +1,94 @@ +$Global:DSCModuleName = 'xDFS' +$Global:DSCResourceName = 'MSFT_xDFSNamespaceServerConfiguration' + +#region HEADER +# Integration Test Template Version: 1.1.0 +[String] $moduleRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $Script:MyInvocation.MyCommand.Path)) +if ( (-not (Test-Path -Path (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) +{ + & git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $moduleRoot -ChildPath '\DSCResource.Tests\')) +} + +Import-Module (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force +$TestEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $Global:DSCModuleName ` + -DSCResourceName $Global:DSCResourceName ` + -TestType Integration +#endregion + +# Using try/finally to always cleanup even if something awful happens. +try +{ + # Ensure that the tests can be performed on this computer + $ProductType = (Get-CimInstance Win32_OperatingSystem).ProductType + Describe 'Environment' { + Context 'Operating System' { + It 'Should be a Server OS' { + $ProductType | Should Be 3 + } + } + } + if ($ProductType -ne 3) + { + Break + } + + $Installed = (Get-WindowsFeature -Name FS-DFS-Namespace).Installed + Describe 'Environment' { + Context 'Windows Features' { + It 'Should have the DFS Namespace Feature Installed' { + $Installed | Should Be $true + } + } + } + if ($Installed -eq $false) + { + Break + } + + # Backup the existing settings + $ServerConfigurationBackup = Get-DFSNServerConfiguration ` + -ComputerName LocalHost + + #region Integration Tests + $ConfigFile = Join-Path -Path $PSScriptRoot -ChildPath "$($Global:DSCResourceName).config.ps1" + . $ConfigFile + + Describe "$($Global:DSCResourceName)_Integration" { + #region DEFAULT TESTS + It 'Should compile without throwing' { + { + Invoke-Expression -Command "$($Global:DSCResourceName)_Config -OutputPath `$TestEnvironment.WorkingFolder" + Start-DscConfiguration -Path $TestEnvironment.WorkingFolder -ComputerName localhost -Wait -Verbose -Force + } | Should not throw + } + + It 'should be able to call Get-DscConfiguration without throwing' { + { Get-DscConfiguration -Verbose -ErrorAction Stop } | Should Not throw + } + #endregion + + It 'Should have set the resource and all the parameters should match' { + # Get the Rule details + $NamespaceServerConfigurationNew = Get-DfsnServerConfiguration -ComputerName LocalHost + $NamespaceServerConfigurationNew.LdapTimeoutSec = $NamespaceServerConfiguration.LdapTimeoutSec + $NamespaceServerConfigurationNew.SyncIntervalSec = $NamespaceServerConfiguration.SyncIntervalSec + $NamespaceServerConfigurationNew.UseFQDN = $NamespaceServerConfiguration.UseFQDN + } + + # Clean up + Set-DFSNServerConfiguration ` + -ComputerName LocalHost ` + -LdapTimeoutSec $ServerConfigurationBackup.LdapTimeoutSec ` + -SyncIntervalSec $ServerConfigurationBackup.SyncIntervalSec ` + -UseFQDN $ServerConfigurationBackup.UseFQDN + } + #endregion +} +finally +{ + #region FOOTER + Restore-TestEnvironment -TestEnvironment $TestEnvironment + #endregion +} diff --git a/Tests/Integration/MSFT_xDFSNamespaceServerConfiguration.config.ps1 b/Tests/Integration/MSFT_xDFSNamespaceServerConfiguration.config.ps1 new file mode 100644 index 0000000..0409669 --- /dev/null +++ b/Tests/Integration/MSFT_xDFSNamespaceServerConfiguration.config.ps1 @@ -0,0 +1,17 @@ +$NamespaceServerConfiguration = @{ + LdapTimeoutSec = 45 + SyncIntervalSec = 5000 + UseFQDN = $True +} + +Configuration MSFT_xDFSNamespaceServerConfiguration_Config { + Import-DscResource -ModuleName xDFS + node localhost { + xDFSNamespaceServerConfiguration Integration_Test { + IsSingleInstance = 'Yes' + LdapTimeoutSec = $NamespaceServerConfiguration.LdapTimeoutSec + SyncIntervalSec = $NamespaceServerConfiguration.SyncIntervalSec + UseFQDN = $NamespaceServerConfiguration.UseFQDN + } + } +} diff --git a/Tests/Unit/MSFT_xDFSNamespaceServerConfiguration.Tests.ps1 b/Tests/Unit/MSFT_xDFSNamespaceServerConfiguration.Tests.ps1 new file mode 100644 index 0000000..a835f3c --- /dev/null +++ b/Tests/Unit/MSFT_xDFSNamespaceServerConfiguration.Tests.ps1 @@ -0,0 +1,223 @@ +$Global:DSCModuleName = 'xDFS' +$Global:DSCResourceName = 'MSFT_xDFSNamespaceServerConfiguration' + +#region HEADER +# Unit Test Template Version: 1.1.0 +[String] $moduleRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $Script:MyInvocation.MyCommand.Path)) +if ( (-not (Test-Path -Path (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) +{ + & git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $moduleRoot -ChildPath '\DSCResource.Tests\')) +} + +Import-Module (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force +$TestEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $Global:DSCModuleName ` + -DSCResourceName $Global:DSCResourceName ` + -TestType Unit +#endregion HEADER + +# Begin Testing +try +{ + # Ensure that the tests can be performed on this computer + $ProductType = (Get-CimInstance Win32_OperatingSystem).ProductType + Describe 'Environment' { + Context 'Operating System' { + It 'Should be a Server OS' { + $ProductType | Should Be 3 + } + } + } + if ($ProductType -ne 3) + { + Break + } + + $Installed = (Get-WindowsFeature -Name FS-DFS-Namespace).Installed + Describe 'Environment' { + Context 'Windows Features' { + It 'Should have the DFS Namespace Feature Installed' { + $Installed | Should Be $true + } + } + } + if ($Installed -eq $false) + { + Break + } + + #region Pester Tests + InModuleScope $Global:DSCResourceName { + + # Create the Mock Objects that will be used for running tests + $NamespaceServerConfiguration = [PSObject]@{ + LdapTimeoutSec = 45 + SyncIntervalSec = 5000 + UseFQDN = $True + } + $NamespaceServerConfigurationSplat = [PSObject]@{ + IsSingleInstance = 'Yes' + LdapTimeoutSec = $NamespaceServerConfiguration.LdapTimeoutSec + SyncIntervalSec = $NamespaceServerConfiguration.SyncIntervalSec + UseFQDN = $NamespaceServerConfiguration.UseFQDN + } + + Describe "$($Global:DSCResourceName)\Get-TargetResource" { + + Context 'Namespace Server Configuration Exists' { + + Mock Get-DFSNServerConfiguration -MockWith { $NamespaceServerConfiguration } + + It 'should return correct namespace server configuration values' { + $Result = Get-TargetResource -IsSingleInstance 'Yes' + $Result.LdapTimeoutSec | Should Be $NamespaceServerConfiguration.LdapTimeoutSec + $Result.SyncIntervalSec | Should Be $NamespaceServerConfiguration.SyncIntervalSec + $Result.UseFQDN | Should Be $NamespaceServerConfiguration.UseFQDN + } + It 'should call the expected mocks' { + Assert-MockCalled -commandName Get-DFSNServerConfiguration -Exactly 1 + } + } + } + + Describe "$($Global:DSCResourceName)\Set-TargetResource" { + + Mock Get-DFSNServerConfiguration -MockWith { $NamespaceServerConfiguration } + Mock Set-DFSNServerConfiguration + + Context 'Namespace Server Configuration all parameters are the same' { + It 'should not throw error' { + { + $Splat = $NamespaceServerConfigurationSplat.Clone() + Set-TargetResource @Splat + } | Should Not Throw + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-DFSNServerConfiguration -Exactly 1 + Assert-MockCalled -commandName Set-DFSNServerConfiguration -Exactly 0 + } + } + + Context 'Namespace Server Configuration LdapTimeoutSec is different' { + It 'should not throw error' { + { + $Splat = $NamespaceServerConfigurationSplat.Clone() + $Splat.LdapTimeoutSec = $Splat.LdapTimeoutSec + 1 + Set-TargetResource @Splat + } | Should Not Throw + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-DFSNServerConfiguration -Exactly 1 + Assert-MockCalled -commandName Set-DFSNServerConfiguration -Exactly 1 + } + } + + Context 'Namespace Server Configuration SyncIntervalSec is different' { + It 'should not throw error' { + { + $Splat = $NamespaceServerConfigurationSplat.Clone() + $Splat.SyncIntervalSec = $Splat.SyncIntervalSec + 1 + Set-TargetResource @Splat + } | Should Not Throw + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-DFSNServerConfiguration -Exactly 1 + Assert-MockCalled -commandName Set-DFSNServerConfiguration -Exactly 1 + } + } + + Context 'Namespace Server Configuration UseFQDN is different' { + It 'should not throw error' { + { + $Splat = $NamespaceServerConfigurationSplat.Clone() + $Splat.UseFQDN = -not $Splat.UseFQDN + Set-TargetResource @Splat + } | Should Not Throw + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-DFSNServerConfiguration -Exactly 1 + Assert-MockCalled -commandName Set-DFSNServerConfiguration -Exactly 1 + } + } + } + + Describe "$($Global:DSCResourceName)\Test-TargetResource" { + + Mock Get-DFSNServerConfiguration -MockWith { $NamespaceServerConfiguration } + + Context 'Namespace Server Configuration all parameters are the same' { + It 'should return true' { + $Splat = $NamespaceServerConfigurationSplat.Clone() + Test-TargetResource @Splat | Should Be $True + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-DFSNServerConfiguration -Exactly 1 + } + } + + Context 'Namespace Server Configuration LdapTimeoutSec is different' { + It 'should return false' { + $Splat = $NamespaceServerConfigurationSplat.Clone() + $Splat.LdapTimeoutSec = $Splat.LdapTimeoutSec + 1 + Test-TargetResource @Splat | Should Be $False + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-DFSNServerConfiguration -Exactly 1 + } + } + + Context 'Namespace Server Configuration SyncIntervalSec is different' { + It 'should return false' { + $Splat = $NamespaceServerConfigurationSplat.Clone() + $Splat.SyncIntervalSec = $Splat.SyncIntervalSec + 1 + Test-TargetResource @Splat | Should Be $False + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-DFSNServerConfiguration -Exactly 1 + } + } + + Context 'Namespace Server Configuration UseFQDN is different' { + It 'should return false' { + $Splat = $NamespaceServerConfigurationSplat.Clone() + $Splat.UseFQDN = -not $Splat.UseFQDN + Test-TargetResource @Splat | Should Be $False + } + It 'should call expected Mocks' { + Assert-MockCalled -commandName Get-DFSNServerConfiguration -Exactly 1 + } + } + } + + Describe "$($Global:DSCResourceName)\New-TerminatingError" { + + Context 'Create a TestError Exception' { + + It 'should throw an TestError exception' { + $errorId = 'TestError' + $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument + $errorMessage = 'Test Error Message' + $exception = New-Object ` + -TypeName System.InvalidOperationException ` + -ArgumentList $errorMessage + $errorRecord = New-Object ` + -TypeName System.Management.Automation.ErrorRecord ` + -ArgumentList $exception, $errorId, $errorCategory, $null + + { New-TerminatingError ` + -ErrorId $errorId ` + -ErrorMessage $errorMessage ` + -ErrorCategory $errorCategory } | Should Throw $errorRecord + } + } + } + } + #endregion +} +finally +{ + #region FOOTER + Restore-TestEnvironment -TestEnvironment $TestEnvironment + #endregion +} diff --git a/appveyor.yml b/appveyor.yml index 5962b1a..0e393ba 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,9 +2,9 @@ # environment configuration # #---------------------------------# os: WMF 5 -version: 3.0.{build}.0 +version: 3.1.{build}.0 install: - - cinst -y pester --version 3.3.13 + - cinst -y pester - git clone https://github.com/PowerShell/DscResource.Tests - ps: Push-Location - cd DscResource.Tests @@ -41,7 +41,7 @@ deploy_script: # Creating project artifact $stagingDirectory = (Resolve-Path ..).Path $manifest = Join-Path $pwd "xDFS.psd1" - (Get-Content $manifest -Raw).Replace("3.0.0.0", $env:APPVEYOR_BUILD_VERSION) | Out-File $manifest + (Get-Content $manifest -Raw).Replace("3.1.0.0", $env:APPVEYOR_BUILD_VERSION) | Out-File $manifest $zipFilePath = Join-Path $stagingDirectory "$(Split-Path $pwd -Leaf).zip" Add-Type -assemblyname System.IO.Compression.FileSystem [System.IO.Compression.ZipFile]::CreateFromDirectory($pwd, $zipFilePath) diff --git a/xDFS.psd1 b/xDFS.psd1 index 53b0d5a..25deddf 100644 --- a/xDFS.psd1 +++ b/xDFS.psd1 @@ -4,7 +4,7 @@ # RootModule = '' # Version number of this module. -ModuleVersion = '3.0.0.0' +ModuleVersion = '3.1.0.0' # ID used to uniquely identify this module GUID = '3bcb9c66-ea0b-4675-bd46-c390a382c388' @@ -96,7 +96,11 @@ PrivateData = @{ # IconUri = '' # ReleaseNotes of this module - # ReleaseNotes = '' + ReleaseNotes = '* MSFT_xDFSNamespaceServerConfiguration- resource added. +* Corrected names of DFS Namespace sample files to indicate that they are setting Namespace roots and folders. +* Removed Pester version from AppVeyor.yml. + +' # External dependent modules of this module # ExternalModuleDependencies = '' @@ -112,3 +116,4 @@ PrivateData = @{ # DefaultCommandPrefix = '' } +