From 998c8e2512773ffc610286b92b19ce96ea2fbb78 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 30 Mar 2019 16:02:12 +0100 Subject: [PATCH 01/26] Changes to ComputerManagementDsc - Added new resource - SmbShare (moved and improved from deprecated module xSmbShare). - Common helpers - Updated Test-DscParameterState so it now can compare zero item collections (arrays). --- CHANGELOG.md | 5 + .../ComputerManagementDsc.Common.psm1 | 27 +- .../ComputerManagementDsc.Common.strings.psd1 | 1 + .../MSFT_SmbShare/MSFT_SmbShare.psm1 | 631 ++++++++++++++++++ .../MSFT_SmbShare/MSFT_SmbShare.schema.mof | 25 + .../en-US/MSFT_SmbShare.schema.mfl | 21 + .../en-US/MSFT_SmbShare.strings.psd1 | 9 + Tests/Unit/MSFT_SmbShare.Tests.ps1 | 580 ++++++++++++++++ 8 files changed, 1291 insertions(+), 8 deletions(-) create mode 100644 Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 create mode 100644 Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.schema.mof create mode 100644 Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.schema.mfl create mode 100644 Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 create mode 100644 Tests/Unit/MSFT_SmbShare.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ed15d55..cc0db7ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,11 @@ - Updated test header for all imtegration to version 1.3.3. - Enabled example publish to PowerShell Gallery by adding `gallery_api` environment variable to `AppVeyor.yml`. +- Added new resource + - SmbShare (moved and improved from deprecated module xSmbShare). +- Changes to ComputerManagementDsc.Common + - Updated Test-DscParameterState so it now can compare zero item + collections (arrays). ## 6.3.0.0 diff --git a/Modules/ComputerManagementDsc.Common/ComputerManagementDsc.Common.psm1 b/Modules/ComputerManagementDsc.Common/ComputerManagementDsc.Common.psm1 index 5a47be09..f77faf48 100644 --- a/Modules/ComputerManagementDsc.Common/ComputerManagementDsc.Common.psm1 +++ b/Modules/ComputerManagementDsc.Common/ComputerManagementDsc.Common.psm1 @@ -118,7 +118,7 @@ function Test-DscParameterState } else { - $desiredType = [psobject] @{ + $desiredType = [PSObject] @{ Name = 'Unknown' } } @@ -129,7 +129,7 @@ function Test-DscParameterState } else { - $currentType = [psobject] @{ + $currentType = [PSObject] @{ Name = 'Unknown' } } @@ -188,7 +188,7 @@ function Test-DscParameterState if (-not $checkDesiredValue) { - Write-Verbose -Message ($script:localizedData.MatchValueMessage -f $desiredType.Name, $key, $CurrentValues.$key, $desiredValuesClean.$key) + Write-Verbose -Message ($script:localizedData.MatchEmptyCollectionMessage -f $desiredType.Name, $key, $CurrentValues.$key, $desiredValuesClean.$key) continue } @@ -196,18 +196,29 @@ function Test-DscParameterState { Write-Verbose -Message ($script:localizedData.TestDscParameterCompareMessage -f $key) - if (-not $CurrentValues.ContainsKey($key) -or -not $CurrentValues.$key) + if ($CurrentValues.$key.Count -eq 0 -and $DesiredValues.$key.Count -eq 0) { - Write-Verbose -Message ($script:localizedData.NoMatchValueMessage -f $desiredType.Name, $key, $CurrentValues.$key, $desiredValuesClean.$key) - $returnValue = $false + Write-Verbose -Message ($script:localizedData.MatchEmptyCollectionMessage -f $desiredType.Name, $key) continue } + <# + This evaluation needs to be performed before the next evaluation, + because we need to be able to handle a that the current value + is a zero item collection, meaning `-not $CurrentValues.$key` + would otherwise return $true in the next evaluation. + #> elseif ($CurrentValues.$key.Count -ne $DesiredValues.$key.Count) { Write-Verbose -Message ($script:localizedData.NoMatchValueDifferentCountMessage -f $desiredType.Name, $key, $CurrentValues.$key.Count, $desiredValuesClean.$key.Count) $returnValue = $false continue } + elseif (-not $CurrentValues.ContainsKey($key) -or -not $CurrentValues.$key) + { + Write-Verbose -Message ($script:localizedData.NoMatchValueMessage -f $desiredType.Name, $key, $CurrentValues.$key, $desiredValuesClean.$key) + $returnValue = $false + continue + } else { $desiredArrayValues = $DesiredValues.$key @@ -221,7 +232,7 @@ function Test-DscParameterState } else { - $desiredType = [psobject]@{ + $desiredType = [PSObject]@{ Name = 'Unknown' } } @@ -232,7 +243,7 @@ function Test-DscParameterState } else { - $currentType = [psobject]@{ + $currentType = [PSObject]@{ Name = 'Unknown' } } diff --git a/Modules/ComputerManagementDsc.Common/en-us/ComputerManagementDsc.Common.strings.psd1 b/Modules/ComputerManagementDsc.Common/en-us/ComputerManagementDsc.Common.strings.psd1 index 9ba00267..56a31211 100644 --- a/Modules/ComputerManagementDsc.Common/en-us/ComputerManagementDsc.Common.strings.psd1 +++ b/Modules/ComputerManagementDsc.Common/en-us/ComputerManagementDsc.Common.strings.psd1 @@ -6,6 +6,7 @@ ConvertFrom-StringData @' NoMatchPsCredentialUsernameMessage = NOTMATCH: PSCredential username mismatch. Current state is '{0}' and desired state is '{1}'. NoMatchTypeMismatchMessage = NOTMATCH: Type mismatch for property '{0}' Current state type is '{1}' and desired type is '{2}'. MatchValueMessage = MATCH: Value (type '{0}') for property '{1}' does match. Current state is '{2}' and desired state is '{3}'. + MatchEmptyCollectionMessage = MATCH: Value (type '{0}') for property '{1}' does match. Current state and desired state both have zero items in the collection. NoMatchValueMessage = NOTMATCH: Value (type '{0}') for property '{1}' does not match. Current state is '{2}' and desired state is '{3}'. NoMatchValueDifferentCountMessage = NOTMATCH: Value (type '{0}') for property '{1}' does have a different count. Current state count is '{2}' and desired state count is '{3}'. NoMatchElementTypeMismatchMessage = NOTMATCH: Type mismatch for property '{0}' Current state type of element [{1}] is '{2}' and desired type is '{3}'. diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 new file mode 100644 index 00000000..232ec2ef --- /dev/null +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 @@ -0,0 +1,631 @@ +$modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules' + +# Import the ComputerManagementDsc Common Modules +Import-Module -Name (Join-Path -Path $modulePath ` + -ChildPath (Join-Path -Path 'ComputerManagementDsc.Common' ` + -ChildPath 'ComputerManagementDsc.Common.psm1')) -Force + +# Import the ComputerManagementDsc Resource Helper Module +Import-Module -Name (Join-Path -Path $modulePath ` + -ChildPath (Join-Path -Path 'ComputerManagementDsc.ResourceHelper' ` + -ChildPath 'ComputerManagementDsc.ResourceHelper.psm1')) + +# Import Localization Strings +$script:localizedData = Get-LocalizedData ` + -ResourceName 'MSFT_SmbShare' ` + -ResourcePath (Split-Path -Parent $Script:MyInvocation.MyCommand.Path) + +<# + .SYNOPSIS + Returns the current state of the SMB share. + + .PARAMETER Name + Specifies the name of the SMB share. + + .PARAMETER Path + Specifies the path of the SMB share. + + Not used in Get-TargetResource. +#> +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter(Mandatory = $true)] + [System.String] + $Path + ) + + Write-Verbose -Message ($script:localizedData.GetTargetResourceMessage -f $Name) + + $returnValue = @{ + Ensure = 'Absent' + Name = $Name + Path = $null + Description = $null + ConcurrentUserLimit = 0 + EncryptData = $false + FolderEnumerationMode = $null + CachingMode = $null + ContinuouslyAvailable = $false + ShareState = $null + ShareType = $null + ShadowCopy = $null + Special = $null + ChangeAccess = @() + ReadAccess = @() + FullAccess = @() + NoAccess = @() + } + + $smbShare = Get-SmbShare -Name $Name -ErrorAction 'SilentlyContinue' + if ($smbShare) + { + $returnValue['Ensure'] = 'Present' + $returnValue['Name'] = $smbShare.Name + $returnValue['Path'] = $smbShare.Path + $returnValue['Description'] = $smbShare.Description + $returnValue['ConcurrentUserLimit'] = $smbShare.ConcurrentUserLimit + $returnValue['EncryptData'] = $smbShare.EncryptData + $returnValue['FolderEnumerationMode'] = $smbShare.FolderEnumerationMode + $returnValue['CachingMode'] = $smbShare.CachingMode + $returnValue['ContinuouslyAvailable'] = $smbShare.ContinuouslyAvailable + $returnValue['ShareState'] = $smbShare.ShareState + $returnValue['ShareType'] = $smbShare.ShareType + $returnValue['ShadowCopy'] = $smbShare.ShadowCopy + $returnValue['Special'] = $smbShare.Special + + $smbShareAccess = Get-SmbShareAccess -Name $Name + foreach ($access in $smbShareAccess) + { + switch ($access.AccessRight) + { + 'Change' + { + if ($access.AccessControlType -eq 'Allow') + { + $returnValue['ChangeAccess'] += @($access.AccountName) + } + } + + 'Read' + { + if ($access.AccessControlType -eq 'Allow') + { + $returnValue['ReadAccess'] += @($access.AccountName) + } + } + + 'Full' + { + if ($access.AccessControlType -eq 'Allow') + { + $returnValue['FullAccess'] += @($access.AccountName) + } + + if ($access.AccessControlType -eq 'Deny') + { + $returnValue['NoAccess'] += @($access.AccountName) + } + } + } + } + } + else + { + Write-Verbose -Message ($script:localizedData.ShareNotFound -f $Name) + } + + return $returnValue +} + +<# + .SYNOPSIS + Creates or removes the SMB share. + + .PARAMETER Name + Specifies the name of the SMB share. + + .PARAMETER Path + Specifies the path of the SMB share. + + .PARAMETER Description + Specifies the description of the SMB share. + + .PARAMETER ConcurrentUserLimit + Specifies the maximum number of concurrently connected users that the + new SMB share may accommodate. If this parameter is set to zero (0), + then the number of users is unlimited. The default value is zero (0). + + .PARAMETER EncryptData + Indicates that the SMB share is encrypted. + + .PARAMETER FolderEnumerationMode + Specifies which files and folders in the new SMB share are visible to + users. { AccessBased | Unrestricted } + + .PARAMETER CachingMode + Specifies the caching mode of the offline files for the SMB share. + { 'None' | 'Manual' | 'Programs' | 'Documents' | 'BranchCache' } + + .PARAMETER ContinuouslyAvailable + Specifies whether the SMB share should be continuously available. + + .PARAMETER FullAccess + Specifies which accounts are granted full permission to access the + SMB share. + + .PARAMETER ChangeAccess + Specifies which user will be granted modify permission to access the + SMB share. + + .PARAMETER ReadAccess + Specifies which user is granted read permission to access the SMB share. + + .PARAMETER NoAccess + Specifies which accounts are denied access to the SMB share. + + .PARAMETER Ensure + Specifies if the SMB share should be added or removed. +#> +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter(Mandatory = $true)] + [System.String] + $Path, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.UInt32] + $ConcurrentUserLimit, + + [Parameter()] + [System.Boolean] + $EncryptData, + + [Parameter()] + [ValidateSet('AccessBased', 'Unrestricted')] + [System.String] + $FolderEnumerationMode, + + [Parameter()] + [ValidateSet('None', 'Manual', 'Programs', 'Documents', 'BranchCache')] + [System.String] + $CachingMode, + + [Parameter()] + [System.Boolean] + $ContinuouslyAvailable, + + [Parameter()] + [System.String[]] + $FullAccess, + + [Parameter()] + [System.String[]] + $ChangeAccess, + + [Parameter()] + [System.String[]] + $ReadAccess, + + [Parameter()] + [System.String[]] + $NoAccess, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present' + ) + + $PSBoundParameters.Remove('Debug') + + $shareExists = $false + $smbShare = Get-SmbShare -Name $Name -ErrorAction SilentlyContinue + if ($smbShare -ne $null) + { + Write-Verbose -Message "Share with name $Name exists" + $shareExists = $true + } + if ($Ensure -eq 'Present') + { + if ($shareExists -eq $false) + { + $PSBoundParameters.Remove('Ensure') + Write-Verbose "Creating share $Name to ensure it is Present" + + # Alter bound parameters + $newShareParameters = Get-SmbBoundParameters -BoundParameters $PSBoundParameters + + # Pass the parameter collection to New-SmbShare + New-SmbShare @newShareParameters + } + else + { + # Need to call either Set-SmbShare or *ShareAccess cmdlets + if ($PSBoundParameters.ContainsKey('ChangeAccess')) + { + $changeAccessValue = $PSBoundParameters['ChangeAccess'] + $PSBoundParameters.Remove('ChangeAccess') + } + if ($PSBoundParameters.ContainsKey('ReadAccess')) + { + $readAccessValue = $PSBoundParameters['ReadAccess'] + $PSBoundParameters.Remove('ReadAccess') + } + if ($PSBoundParameters.ContainsKey('FullAccess')) + { + $fullAccessValue = $PSBoundParameters['FullAccess'] + $PSBoundParameters.Remove('FullAccess') + } + if ($PSBoundParameters.ContainsKey('NoAccess')) + { + $noAccessValue = $PSBoundParameters['NoAccess'] + $PSBoundParameters.Remove('NoAccess') + } + + # Use Set-SmbShare for performing operations other than changing access + $PSBoundParameters.Remove('Ensure') + $PSBoundParameters.Remove('Path') + Set-SmbShare @PSBoundParameters -Force + + # Use *SmbShareAccess cmdlets to change access + $smbShareAccessValues = Get-SmbShareAccess -Name $Name + + # Remove Change permissions + $smbShareAccessValues | Where-Object { $_.AccessControlType -eq 'Allow' -and $_.AccessRight -eq 'Change' } ` + | ForEach-Object { + Remove-AccessPermission -Name $Name -UserName $_.AccountName -AccessPermission Change + } + + if ($ChangeAccess -ne $null) + { + # Add change permissions + $changeAccessValue | ForEach-Object { + Set-AccessPermission -Name $Name -AccessPermission 'Change' -Username $_ + } + } + + $smbShareAccessValues = Get-SmbShareAccess -Name $Name + + # Remove read access + $smbShareAccessValues | Where-Object { $_.AccessControlType -eq 'Allow' -and $_.AccessRight -eq 'Read' } ` + | ForEach-Object { + Remove-AccessPermission -Name $Name -UserName $_.AccountName -AccessPermission Read + } + + if ($ReadAccess -ne $null) + { + # Add read access + $readAccessValue | ForEach-Object { + Set-AccessPermission -Name $Name -AccessPermission 'Read' -Username $_ + } + } + + + $smbShareAccessValues = Get-SmbShareAccess -Name $Name + + # Remove full access + $smbShareAccessValues | Where-Object { $_.AccessControlType -eq 'Allow' -and $_.AccessRight -eq 'Full' } ` + | ForEach-Object { + Remove-AccessPermission -Name $Name -UserName $_.AccountName -AccessPermission Full + } + + + if ($FullAccess -ne $null) + { + + # Add full access + $fullAccessValue | ForEach-Object { + Set-AccessPermission -Name $Name -AccessPermission 'Full' -Username $_ + } + } + + $smbShareAccessValues = Get-SmbShareAccess -Name $Name + + # Remove explicit deny + $smbShareAccessValues | Where-Object { $_.AccessControlType -eq 'Deny' } ` + | ForEach-Object { + Remove-AccessPermission -Name $Name -UserName $_.AccountName -AccessPermission No + } + + + if ($NoAccess -ne $null) + { + # Add explicit deny + $noAccessValue | ForEach-Object { + Set-AccessPermission -Name $Name -AccessPermission 'No' -Username $_ + } + } + } + } + else + { + Write-Verbose "Removing share $Name to ensure it is Absent" + Remove-SmbShare -name $Name -Force + } +} + +<# + .SYNOPSIS + Determines if the SMB share is in the desired state. + + .PARAMETER Name + Specifies the name of the SMB share. + + .PARAMETER Path + Specifies the path of the SMB share. + + .PARAMETER Description + Specifies the description of the SMB share. + + .PARAMETER ConcurrentUserLimit + Specifies the maximum number of concurrently connected users that the + new SMB share may accommodate. If this parameter is set to zero (0), + then the number of users is unlimited. The default value is zero (0). + + .PARAMETER EncryptData + Indicates that the SMB share is encrypted. + + .PARAMETER FolderEnumerationMode + Specifies which files and folders in the new SMB share are visible to + users. { AccessBased | Unrestricted } + + .PARAMETER CachingMode + Specifies the caching mode of the offline files for the SMB share. + { 'None' | 'Manual' | 'Programs' | 'Documents' | 'BranchCache' } + + .PARAMETER ContinuouslyAvailable + Specifies whether the SMB share should be continuously available. + + .PARAMETER FullAccess + Specifies which accounts are granted full permission to access the + SMB share. + + .PARAMETER ChangeAccess + Specifies which user will be granted modify permission to access the + SMB share. + + .PARAMETER ReadAccess + Specifies which user is granted read permission to access the SMB share. + + .PARAMETER NoAccess + Specifies which accounts are denied access to the SMB share. + + .PARAMETER Ensure + Specifies if the SMB share should be added or removed. +#> +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter(Mandatory = $true)] + [System.String] + $Path, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.UInt32] + $ConcurrentUserLimit, + + [Parameter()] + [System.Boolean] + $EncryptData, + + [Parameter()] + [ValidateSet('AccessBased', 'Unrestricted')] + [System.String] + $FolderEnumerationMode, + + [Parameter()] + [ValidateSet('None', 'Manual', 'Programs', 'Documents', 'BranchCache')] + [System.String] + $CachingMode, + + [Parameter()] + [System.Boolean] + $ContinuouslyAvailable, + + [Parameter()] + [System.String[]] + $FullAccess, + + [Parameter()] + [System.String[]] + $ChangeAccess, + + [Parameter()] + [System.String[]] + $ReadAccess, + + [Parameter()] + [System.String[]] + $NoAccess, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present' + ) + + Write-Verbose -Message ($script:localizedData.TestTargetResourceMessage -f $Name) + + $testTargetResourceResult = $false + + $getTargetResourceResult = Get-TargetResource -Name $Name -Path $Path + + if ($getTargetResourceResult.Ensure -eq $Ensure ) + { + if ($Ensure -eq 'Present') + { + Write-Verbose -Message ($script:localizedData.IsPresent -f $Name) + + $valuesToCheck = @( + 'Name' + 'Path' + 'Description' + 'ConcurrentUserLimit' + 'EncryptData' + 'FolderEnumerationMode' + 'CachingMode' + 'ContinuouslyAvailable' + 'FullAccess' + 'ChangeAccess' + 'ReadAccess' + 'NoAccess' + ) + + <# + Using $VerbosePreference so that the verbose messages in + Test-DscParameterState is outputted, if the user requested + verbose messages. + #> + $testTargetResourceResult = Test-DscParameterState ` + -CurrentValues $getTargetResourceResult ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $valuesToCheck ` + -Verbose:$VerbosePreference + } + else + { + Write-Verbose -Message ($script:localizedData.IsAbsent -f $Name) + + $testTargetResourceResult = $true + } + } + + return $testTargetResourceResult +} + +function Set-AccessPermission +{ + [CmdletBinding()] + Param + ( + [Parameter()] + [System.String] + $Name, + + [Parameter()] + [System.String[]] + $UserName, + + [Parameter()] + [ValidateSet('Change', 'Full', 'Read', 'No')] + [System.String] + $AccessPermission + ) + $formattedString = '{0}{1}' -f $AccessPermission, 'Access' + Write-Verbose -Message "Setting $formattedString for $UserName" + + if ($AccessPermission -eq 'Change' -or $AccessPermission -eq 'Read' -or $AccessPermission -eq 'Full') + { + Grant-SmbShareAccess -Name $Name -AccountName $UserName -AccessRight $AccessPermission -Force + } + else + { + Block-SmbShareAccess -Name $Name -AccountName $UserName -Force + } +} + +function Get-SmbBoundParameters +{ + # Define parameters + Param + ( + [Parameter()] + [System.Collections.Hashtable] + $BoundParameters + ) + + # Check for null access before passing to New-SmbShare + if (($BoundParameters.ContainsKey('ChangeAccess')) -and ([string]::IsNullOrEmpty($BoundParameters['ChangeAccess']))) + { + Write-Verbose "Parameter ChangeAccess is null or empty, removing from collection." + # Remove the parameter + $BoundParameters.Remove('ChangeAccess') + } + + if (($BoundParameters.ContainsKey('ReadAccess')) -and ([string]::IsNullOrEmpty($BoundParameters['ReadAccess']))) + { + Write-Verbose "Parameter ReadAccess is null or empty, removing from collection." + # Remove the parameter + $BoundParameters.Remove('ReadAccess') + } + + if (($BoundParameters.ContainsKey('FullAccess')) -and ([string]::IsNullOrEmpty($BoundParameters['FullAccess']))) + { + Write-Verbose "Parameter FullAccess is null or empty, removing from collection." + # Remove the parameter + $BoundParameters.Remove('FullAccess') + } + + if (($BoundParameters.ContainsKey('NoAccess')) -and ([string]::IsNullOrEmpty($BoundParameters['NoAccess']))) + { + Write-Verbose "Parameter NoAccess is null or empty, removing from collection." + # Remove the parameter + $BoundParameters.Remove('NoAccess') + } + + # Return the parameter collection + return $BoundParameters +} + +function Remove-AccessPermission +{ + [CmdletBinding()] + Param + ( + [Parameter()] + [System.String] + $Name, + + [Parameter()] + [System.String[]] + $UserName, + + [Parameter()] + [ValidateSet('Change', 'Full', 'Read', 'No')] + [System.String] + $AccessPermission + ) + + $formattedString = '{0}{1}' -f $AccessPermission, 'Access' + + Write-Debug -Message "Removing $formattedString for $UserName" + + if ($AccessPermission -eq 'Change' -or $AccessPermission -eq 'Read' -or $AccessPermission -eq 'Full') + { + Revoke-SmbShareAccess -Name $Name -AccountName $UserName -Force + } + else + { + Unblock-SmbShareAccess -Name $Name -AccountName $UserName -Force + } +} diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.schema.mof b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.schema.mof new file mode 100644 index 00000000..2e22a62c --- /dev/null +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.schema.mof @@ -0,0 +1,25 @@ + +[ClassVersion("1.0.0.0"), FriendlyName("SmbShare")] +class MSFT_SmbShare : OMI_BaseResource +{ + [Key, Description("Specifies the name of the SMB share.")] String Name; + [Required, Description("Specifies the path of the SMB share.")] String Path; + [Write, Description("Specifies the description of the SMB share.")] String Description; + [Write, Description("Specifies which user will be granted modify permission to access the SMB share.")] String ChangeAccess[]; + [Write, Description("Specifies the maximum number of concurrently connected users that the new SMB share may accommodate. If this parameter is set to zero (0), then the number of users is unlimited. The default value is zero (0).")] Uint32 ConcurrentUserLimit; + [Write, Description("Indicates that the SMB share is encrypted.")] Boolean EncryptData; + [Write, Description("Specifies which files and folders in the new SMB share are visible to users. { AccessBased | Unrestricted }"), ValueMap{"AccessBased","Unrestricted"}, Values{"AccessBased","Unrestricted"}] String FolderEnumerationMode; + [Write, Description("Specifies the caching mode of the offline files for the SMB share. { 'None' | 'Manual' | 'Programs' | 'Documents' | 'BranchCache' }"), ValueMap{"None","Manual","Programs","Documents","BranchCache"}, Values{"None","Manual","Programs","Documents","BranchCache"}] String CachingMode; + [Write, Description("Specifies whether the SMB share should be continuously available.")] Boolean ContinuouslyAvailable; + [Write, Description("Specifies which accounts are granted full permission to access the SMB share.")] String FullAccess[]; + [Write, Description("Specifies which accounts are denied access to the SMB share.")] String NoAccess[]; + [Write, Description("Specifies which user is granted read permission to access the SMB share.")] String ReadAccess[]; + [Write, Description("Specifies if the SMB share should be added or removed."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; + [Read, Description("Specifies the state of the SMB share.")] String ShareState; + [Read, Description("Specifies the type of the SMB share.")] String ShareType; + [Read, Description("Specifies if this SMB share is a ShadowCopy.")] String ShadowCopy; + [Read, Description("Specifies if this SMB share is a special share. E.g. an admin share, default shares, or IPC$ share.")] String Special; +}; + + + diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.schema.mfl b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.schema.mfl new file mode 100644 index 00000000..46c2d061 --- /dev/null +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.schema.mfl @@ -0,0 +1,21 @@ +[Description("This resource is used to configure SMB shares.") : Amended,AMENDMENT, LOCALE("MS_409")] +class MSFT_SmbShare : OMI_BaseResource +{ + [Key, Description("Specifies the name of the SMB share.") : Amended] String Name; + [Description("Specifies the path of the SMB share.") : Amended] String Path; + [Description("Specifies the description of the SMB share.") : Amended] String Description; + [Description("Specifies which user will be granted modify permission to access the SMB share.") : Amended] String ChangeAccess[]; + [Description("Specifies the maximum number of concurrently connected users that the new SMB share may accommodate. If this parameter is set to zero (0), then the number of users is unlimited. The default value is zero (0).") : Amended] Uint32 ConcurrentUserLimit; + [Description("Indicates that the SMB share is encrypted.") : Amended] Boolean EncryptData; + [Description("Specifies which files and folders in the new SMB share are visible to users. { AccessBased | Unrestricted }") : Amended] String FolderEnumerationMode; + [Description("Specifies the caching mode of the offline files for the SMB share. { 'None' | 'Manual' | 'Programs' | 'Documents' | 'BranchCache' }") : Amended] String CachingMode; + [Description("Specifies whether the SMB share should be continuously available.") : Amended] Boolean ContinuouslyAvailable; + [Description("Specifies which accounts are granted full permission to access the SMB share.") : Amended] String FullAccess[]; + [Description("Specifies which accounts are denied access to the SMB share.") : Amended] String NoAccess[]; + [Description("Specifies which user is granted read permission to access the SMB share.") : Amended] String ReadAccess[]; + [Description("Specifies if the SMB share should be added or removed.") : Amended] String Ensure; + [Description("Specifies the state of the SMB share.") : Amended] String ShareState; + [Description("Specifies the type of the SMB share.") : Amended] String ShareType; + [Description("Specifies if this SMB share is a ShadowCopy.") : Amended] String ShadowCopy; + [Description("Specifies if this SMB share is a special share. E.g. an admin share, default shares, or IPC$ share.") : Amended] String Special; +}; diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 new file mode 100644 index 00000000..16a6fde8 --- /dev/null +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 @@ -0,0 +1,9 @@ +# Localized resources for WindowsOptionalFeature + +ConvertFrom-StringData @' + GetTargetResourceMessage = Getting the current state of the SMB share '{0}'. + TestTargetResourceMessage = Determining if the SMB share '{0}' is in the desired state. + ShareNotFound = Unable to find a SMB share with the name '{0}'. + IsPresent = The SMB share '{0}' exist, evaluating the properties. + IsAbsent = The SMB share '{0}' does not exist. +'@ diff --git a/Tests/Unit/MSFT_SmbShare.Tests.ps1 b/Tests/Unit/MSFT_SmbShare.Tests.ps1 new file mode 100644 index 00000000..6cb779b5 --- /dev/null +++ b/Tests/Unit/MSFT_SmbShare.Tests.ps1 @@ -0,0 +1,580 @@ +#region HEADER +$script:dscModuleName = 'ComputerManagementDsc' +$script:dscResourceName = 'MSFT_SmbShare' + +# Unit Test Template Version: 1.2.4 +$script:moduleRoot = Join-Path -Path $(Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $Script:MyInvocation.MyCommand.Path))) -ChildPath 'Modules\ComputerManagementDsc' +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.exe @('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 ` + -ResourceType 'Mof' ` + -TestType Unit + +#endregion HEADER + +function Invoke-TestSetup +{ +} + +function Invoke-TestCleanup +{ + Restore-TestEnvironment -TestEnvironment $TestEnvironment +} + +try +{ + Invoke-TestSetup + + InModuleScope $script:DSCResourceName { + $mockChangePermissionUserName = @('User1') + $mockReadPermissionUserName = @('User2') + $mockFullPermissionUserName = @('User3', 'User4') + $mockNoPermissionUserName = @('DeniedUser1') + + $mockSmbShare = ( + New-Object -TypeName Object | + Add-Member -MemberType NoteProperty -Name 'Name' -Value 'DummyShare' -PassThru | + Add-Member -MemberType NoteProperty -Name 'Path' -Value 'c:\temp' -PassThru | + Add-Member -MemberType NoteProperty -Name 'Description' 'Dummy share for unit testing' -PassThru | + Add-Member -MemberType NoteProperty -Name 'ConcurrentUserLimit' -Value 10 -PassThru | + Add-Member -MemberType NoteProperty -Name 'EncryptData' -Value $false -PassThru | + Add-Member -MemberType NoteProperty -Name 'FolderEnumerationMode' -Value 'AccessBased' -PassThru | # 0 AccessBased | 1 Unrestricted, but method expects text + Add-Member -MemberType NoteProperty -Name 'SharedState' -Value 1 -PassThru | # 0 Pending | 1 Online | 2 Offline + Add-Member -MemberType NoteProperty -Name 'ShadowCopy' -Value $false -PassThru | + Add-Member -MemberType NoteProperty -Name 'CachingMode' -Value 'Manual' -PassThru | + Add-Member -MemberType NoteProperty -Name 'ContinuouslyAvailable' -Value $true -PassThru | + Add-Member -MemberType NoteProperty -Name 'Special' -Value $false -PassThru -Force + ) + + $mockChangePermissionUserName = 'User1' + $mockSmbShareAccess = @( + ( + New-Object -TypeName Object | + Add-Member -MemberType NoteProperty -Name 'Name' -Value 'DummyShare' -PassThru | + Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockFullPermissionUserName -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Allow' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Full' -PassThru -Force + ), + ( + New-Object -TypeName Object | + Add-Member -MemberType NoteProperty -Name 'Name' -Value 'DummyShare' -PassThru | + Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockChangePermissionUserName -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Allow' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Change' -PassThru -Force + ), + ( + New-Object -TypeName Object | + Add-Member -MemberType NoteProperty -Name 'Name' -Value 'DummyShare' -PassThru | + Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockReadPermissionUserName -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Allow' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Read' -PassThru -Force + ), + ( + New-Object -TypeName Object | + Add-Member -MemberType NoteProperty -Name 'Name' -Value 'DummyShare' -PassThru | + Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockNoPermissionUserName -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Deny' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Full' -PassThru -Force + ) + ) + + Describe 'MSFT_SmbShare\Get-TargetResource' -Tag 'Get' { + Context 'When the system is in the desired state' { + BeforeAll { + Mock -CommandName Get-SmbShare -MockWith { + return $mockSmbShare + } + + Mock -CommandName Get-SmbShareAccess -MockWith { + return $mockSmbShareAccess + } + + $testParameters = @{ + Name = $mockSmbShare.Name + Path = $mockSmbShare.Path + Verbose = $true + } + } + + It 'Should mock call to Get-SmbShare and return membership' { + $getTargetResourceResult = Get-TargetResource @testParameters + + $getTargetResourceResult.ChangeAccess | Should -HaveCount 1 + $getTargetResourceResult.ChangeAccess[0] | Should -BeIn $mockChangePermissionUserName + + $getTargetResourceResult.ReadAccess | Should -HaveCount 1 + $getTargetResourceResult.ReadAccess[0] | Should -BeIn $mockReadPermissionUserName + + $getTargetResourceResult.FullAccess | Should -HaveCount 2 + $getTargetResourceResult.FullAccess[0] | Should -BeIn $mockFullPermissionUserName + $getTargetResourceResult.FullAccess[1] | Should -BeIn $mockFullPermissionUserName + + $getTargetResourceResult.NoAccess | Should -HaveCount 1 + $getTargetResourceResult.NoAccess[0] | Should -BeIn $mockNoPermissionUserName + } + + It 'Should call the mock function Get-SmbShare' { + $getTargetResourceResult = Get-TargetResource @testParameters + Assert-MockCalled Get-SmbShare -Exactly -Times 1 -Scope It + } + + It 'Should Call the mock function Get-SmbShareAccess' { + $getTargetResourceResult = Get-TargetResource @testParameters + Assert-MockCalled Get-SmbShareAccess -Exactly -Times 1 -Scope It + } + } + + Context 'When the system is not in the desired state' { + BeforeAll { + Mock -CommandName Get-SmbShare + + $testParameters = @{ + Name = $mockSmbShare.Name + Path = $mockSmbShare.Path + Verbose = $true + } + } + + It 'Should return the correct values' { + $getTargetResourceResult = Get-TargetResource @testParameters + + $getTargetResourceResult.Ensure | Should -Be 'Absent' + $getTargetResourceResult.Name | Should -Be $testParameters.Name + $getTargetResourceResult.Path | Should -BeNullOrEmpty + $getTargetResourceResult.Description | Should -BeNullOrEmpty + $getTargetResourceResult.ConcurrentUserLimit | Should -Be 0 + $getTargetResourceResult.EncryptData | Should -BeFalse + $getTargetResourceResult.FolderEnumerationMode | Should -BeNullOrEmpty + $getTargetResourceResult.CachingMode | Should -BeNullOrEmpty + $getTargetResourceResult.ContinuouslyAvailable | Should -BeFalse + $getTargetResourceResult.ShareState | Should -BeNullOrEmpty + $getTargetResourceResult.ShareType | Should -BeNullOrEmpty + $getTargetResourceResult.ShadowCopy | Should -BeNullOrEmpty + $getTargetResourceResult.Special | Should -BeNullOrEmpty + $getTargetResourceResult.ChangeAccess | Should -HaveCount 0 + $getTargetResourceResult.ReadAccess | Should -HaveCount 0 + $getTargetResourceResult.FullAccess | Should -HaveCount 0 + $getTargetResourceResult.NoAccess | Should -HaveCount 0 + } + + } + } + + # Describe 'MSFT_SmbShare\Set-TargetResource' -Tag 'Set' { + # Context 'When the system is not in the desired state' { + # BeforeAll { + # # Per context-block initialization + # } + + # # Set the testParameter collection + # $testParameters = @{ + # ChangeAccess = $mockChangePermissionUserName + # ReadAccess = $mockReadPermissionUserName + # FullAccess = $mockFullPermissionUserName + # NoAccess = $mockNoAccess + # Name = $mockSmbShare.Name + # Path = $mockSmbShare.Path + # Description = $mockSmbShare.Description + # ConcurrentUserLimit = $mockSmbShare.ConcurrentUserLimit + # EncryptData = $mockSmbShare.EncryptData + # FolderEnumerationMode = $mockSmbShare.FolderEnumerationMode + # Ensure = 'Present' + # } + + # # Init the script variables + # $script:ChangeAccess = @() + # $script:ReadAccess = @() + # $script:FullAccess = @() + # $script:NoAccess = @() + # $script:ChangeAccess += $mockDefaultChangePermissionUserName + # $script:ReadAccess += $mockDefaultReadPermissionUserName + # $script:FullAccess += $mockDefaultFullPermissionUserName + # $script:NoAccess += $mockDefaultNoPermissionUserName + + + # # Set mock function calls + # Mock -CommandName Get-SmbShare -MockWith { return @($mockSmbShare)} + # Mock -CommandName Get-SmbShareAccess -MockWith { return @($mockSmbShareAccess)} + # Mock -CommandName Set-SmbShare -MockWith { return $null} + # Mock -CommandName Grant-SmbShareAccess -MockWith { + # # Declare local array -- use of this variable was necessary as the script: context was losing the fact it was an array in the mock + # $localArray = @() + + # switch($AccessPermission) + # { + # 'Change' + # { + # $localArray += $script:ChangeAccess + # if ($localArray -notcontains $UserName) + # { + # $localArray += $UserName + # } + + # $script:ChangeAccess = $localArray + # break + # } + # 'Read' + # { + # $localArray += $script:ReadAccess + # if($localArray -notcontains $UserName) + # { + # $localArray += $UserName + # } + # $script:ReadAccess = $localArray + # break + # } + # 'Full' + # { + # $localArray += $script:FullAccess + # if($localArray -notcontains $UserName) + # { + # $localArray += $UserName + # } + # $script:FullAccess = $localArray + # break + # } + # } + # } + # Mock Block-SmbShareAccess -MockWith { + # $script:NoAccess += $UserName + # } + # Mock Revoke-SmbShareAccess -MockWith { + # switch($AccessPermission) + # { + # 'Change' + # { + # # Remove from array + # $script:ChangeAccess = $script:ChangeAccess | Where-Object {$_ -ne $UserName} + # break + # } + # 'Read' + # { + # $script:ReadAccess = $script:ReadAccess | Where-Object {$_ -ne $UserName} + # break + # } + # 'Full' + # { + # $script:FullAccess = $script:FullAccess | Where-Object {$_ -ne $UserName} + # break + # } + # } + # } + # Mock -CommandName Unblock-SmbShareAccess -MockWith { + # $script:NoAccess = $script:NoAccess | Where-Object {$_ -ne $UserName} + # } + + + + # It 'Should alter permissions' { + # $result = Set-TargetResource @testParameters + # $script:ChangeAccess | Should Be $mockChangePermissionUserName + # $script:ReadAccess | Should Be $mockReadPermissionUserName + # $script:FullAccess | Should Be $mockFullPermissionUserName + # #$script:NoAccess | Should Be $mockNoPermissionUserName + # } + + # It 'Should call the mock function Get-SmbShare' { + # $result = Set-TargetResource @testParameters + # Assert-MockCalled Get-SmbShare -Exactly -Times 1 -Scope It + # } + + # It 'Should Call the mock function Get-SmbShareAccess' { + # $result = Set-TargetResource @testParameters + # Assert-MockCalled Get-SmbShareAccess -Exactly -Times 4 -Scope It + # } + + # It 'Should call the mock function Set-SmbShare' { + # $result = Set-TargetResource @testParameters + # Assert-MockCalled Set-SmbShare -Exactly -Times 1 -Scope It + # } + # } + # } + + Describe 'MSFT_SmbShare\Test-TargetResource' -Tag 'Test' { + Context 'When the system is not in the desired state' { + Context 'When there is a configured SMB share' { + BeforeAll { + $mockDefaultTestCaseValues = @{ + TestCase = '' + Name = $mockSmbShare.Name + Path = $mockSmbShare.Path + Description = $mockSmbShare.Description + ConcurrentUserLimit = $mockSmbShare.ConcurrentUserLimit + EncryptData = $mockSmbShare.EncryptData + FolderEnumerationMode = $mockSmbShare.FolderEnumerationMode + CachingMode = $mockSmbShare.CachingMode + ContinuouslyAvailable = $mockSmbShare.ContinuouslyAvailable + FullAccess = @() + ChangeAccess = @() + ReadAccess = @() + NoAccess = @() + Ensure = 'Present' + } + + $mockTestCase1 = $mockDefaultTestCaseValues.Clone() + $testProperty = 'Path' + $mockTestCase1['TestCase'] = $testProperty + $mockTestCase1[$testProperty] = 'TestDrive:\NewFolder' + + $mockTestCase2 = $mockDefaultTestCaseValues.Clone() + $testProperty = 'Description' + $mockTestCase2['TestCase'] = $testProperty + $mockTestCase2[$testProperty] = 'New description' + + $mockTestCase3 = $mockDefaultTestCaseValues.Clone() + $testProperty = 'ConcurrentUserLimit' + $mockTestCase3['TestCase'] = $testProperty + $mockTestCase3[$testProperty] = 2 + + $mockTestCase4 = $mockDefaultTestCaseValues.Clone() + $testProperty = 'EncryptData' + $mockTestCase4['TestCase'] = $testProperty + $mockTestCase4[$testProperty] = $true + + $mockTestCase5 = $mockDefaultTestCaseValues.Clone() + $testProperty = 'FolderEnumerationMode' + $mockTestCase5['TestCase'] = $testProperty + $mockTestCase5[$testProperty] = 'Unrestricted' + + $mockTestCase6 = $mockDefaultTestCaseValues.Clone() + $testProperty = 'CachingMode' + $mockTestCase6['TestCase'] = $testProperty + $mockTestCase6[$testProperty] = 'Documents' + + $mockTestCase7 = $mockDefaultTestCaseValues.Clone() + $testProperty = 'ContinuouslyAvailable' + $mockTestCase7['TestCase'] = $testProperty + $mockTestCase7[$testProperty] = $false + + $mockTestCase8 = $mockDefaultTestCaseValues.Clone() + $testProperty = 'FullAccess' + $mockTestCase8['TestCase'] = $testProperty + $mockTestCase8[$testProperty] = @('NewUser') + + $mockTestCase9 = $mockDefaultTestCaseValues.Clone() + $testProperty = 'ChangeAccess' + $mockTestCase9['TestCase'] = $testProperty + $mockTestCase9[$testProperty] = @('NewUser') + + $mockTestCase10 = $mockDefaultTestCaseValues.Clone() + $testProperty = 'ReadAccess' + $mockTestCase10['TestCase'] = $testProperty + $mockTestCase10[$testProperty] = @('NewUser') + + $mockTestCase11 = $mockDefaultTestCaseValues.Clone() + $testProperty = 'NoAccess' + $mockTestCase11['TestCase'] = $testProperty + $mockTestCase11[$testProperty] = @('NewUser') + + $testCases = @( + $mockTestCase1 + $mockTestCase2 + $mockTestCase3 + $mockTestCase4 + $mockTestCase5 + $mockTestCase6 + $mockTestCase7 + $mockTestCase8 + $mockTestCase9 + $mockTestCase10 + $mockTestCase11 + ) + + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Name = $mockSmbShare.Name + Path = $mockSmbShare.Path + Description = $mockSmbShare.Description + ConcurrentUserLimit = [System.UInt32] $mockSmbShare.ConcurrentUserLimit + EncryptData = $mockSmbShare.EncryptData + FolderEnumerationMode = $mockSmbShare.FolderEnumerationMode + CachingMode = $mockSmbShare.CachingMode + ContinuouslyAvailable = $mockSmbShare.ContinuouslyAvailable + ShareState = $mockSmbShare.ShareState + ShareType = $mockSmbShare.ShareType + ShadowCopy = $mockSmbShare.ShadowCopy + Special = $mockSmbShare.Special + FullAccess = [System.String[]] @() + ChangeAccess = [System.String[]] @() + ReadAccess = [System.String[]] @() + NoAccess = [System.String[]] @() + Ensure = 'Present' + } + } + } + + It 'Should return $false when property has the wrong value' -TestCases $testCases { + param + ( + $Name, + $Path, + $Description, + $ConcurrentUserLimit, + $EncryptData, + $FolderEnumerationMode, + $CachingMode, + $ContinuouslyAvailable, + $FullAccess, + $ChangeAccess, + $ReadAccess, + $NoAccess, + $Ensure + ) + + $testTargetResourceParameters = @{ + Name = $Name + Path = $Path + Description = $Description + ConcurrentUserLimit = $ConcurrentUserLimit + EncryptData = $EncryptData + FolderEnumerationMode = $FolderEnumerationMode + CachingMode = $CachingMode + ContinuouslyAvailable = $ContinuouslyAvailable + FullAccess = $FullAccess + ChangeAccess = $ChangeAccess + ReadAccess = $ReadAccess + NoAccess = $NoAccess + Ensure = 'Present' + Verbose = $true + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -BeFalse + } + + It 'Should return $false when the desired state should be ''Absent''' { + $testTargetResourceParameters = @{ + Ensure = 'Absent' + Name = $mockSmbShare.Name + Path = $mockSmbShare.Path + Description = $mockSmbShare.Description + ConcurrentUserLimit = [System.UInt32] $mockSmbShare.ConcurrentUserLimit + EncryptData = $mockSmbShare.EncryptData + FolderEnumerationMode = $mockSmbShare.FolderEnumerationMode + CachingMode = $mockSmbShare.CachingMode + ContinuouslyAvailable = $mockSmbShare.ContinuouslyAvailable + FullAccess = @() + ChangeAccess = @() + ReadAccess = @() + NoAccess = @() + Verbose = $true + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -BeFalse + } + } + + Context 'When there are no configured SMB share' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Absent' + } + } + } + + It 'Should return $false when the desired state should ''Present''' { + $testTargetResourceParameters = @{ + Ensure = 'Present' + Name = $mockSmbShare.Name + Path = $mockSmbShare.Path + Verbose = $true + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -BeFalse + } + } + } + + Context 'When the system is in the desired state' { + Context 'When there is a configured SMB share' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Name = $mockSmbShare.Name + Path = $mockSmbShare.Path + Description = $mockSmbShare.Description + ConcurrentUserLimit = [System.UInt32] $mockSmbShare.ConcurrentUserLimit + EncryptData = $mockSmbShare.EncryptData + FolderEnumerationMode = $mockSmbShare.FolderEnumerationMode + CachingMode = $mockSmbShare.CachingMode + ContinuouslyAvailable = $mockSmbShare.ContinuouslyAvailable + ShareState = $mockSmbShare.ShareState + ShareType = $mockSmbShare.ShareType + ShadowCopy = $mockSmbShare.ShadowCopy + Special = $mockSmbShare.Special + FullAccess = [System.String[]] @() + ChangeAccess = [System.String[]] @() + ReadAccess = [System.String[]] @() + NoAccess = [System.String[]] @() + Ensure = 'Present' + } + } + } + + It 'Should return $true when the desired state should be ''Present''' { + $testTargetResourceParameters = @{ + Ensure = 'Present' + Name = $mockSmbShare.Name + Path = $mockSmbShare.Path + Description = $mockSmbShare.Description + ConcurrentUserLimit = [System.UInt32] $mockSmbShare.ConcurrentUserLimit + EncryptData = $mockSmbShare.EncryptData + FolderEnumerationMode = $mockSmbShare.FolderEnumerationMode + CachingMode = $mockSmbShare.CachingMode + ContinuouslyAvailable = $mockSmbShare.ContinuouslyAvailable + FullAccess = @() + ChangeAccess = @() + ReadAccess = @() + NoAccess = @() + Verbose = $true + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -BeTrue + } + } + + Context 'When there are no configured SMB share' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Absent' + } + } + } + + It 'Should return $true when the desired state should ''Absent''' { + $testTargetResourceParameters = @{ + Ensure = 'Absent' + Name = $mockSmbShare.Name + Path = $mockSmbShare.Path + Verbose = $true + } + + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -BeTrue + } + } + } + } + } +} +finally +{ + Invoke-TestCleanup +} From 0e9602a776e6e0ae3907140ed8b3270981d871b6 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 30 Mar 2019 17:33:18 +0100 Subject: [PATCH 02/26] Updated code for Set --- .../MSFT_SmbShare/MSFT_SmbShare.psm1 | 284 +++++++++--------- Tests/Unit/MSFT_SmbShare.Tests.ps1 | 20 +- 2 files changed, 157 insertions(+), 147 deletions(-) diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 index 232ec2ef..c94385fc 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 @@ -81,8 +81,8 @@ function Get-TargetResource $returnValue['ShadowCopy'] = $smbShare.ShadowCopy $returnValue['Special'] = $smbShare.Special - $smbShareAccess = Get-SmbShareAccess -Name $Name - foreach ($access in $smbShareAccess) + $getSmbShareAccessResult = Get-SmbShareAccess -Name $Name + foreach ($access in $getSmbShareAccessResult) { switch ($access.AccessRight) { @@ -235,131 +235,183 @@ function Set-TargetResource $Ensure = 'Present' ) - $PSBoundParameters.Remove('Debug') + $smbShareParameters = $PSBoundParameters.Clone() - $shareExists = $false - $smbShare = Get-SmbShare -Name $Name -ErrorAction SilentlyContinue - if ($smbShare -ne $null) + $smbShare = Get-SmbShare -Name $Name -ErrorAction 'SilentlyContinue' + if ($smbShare) { Write-Verbose -Message "Share with name $Name exists" - $shareExists = $true - } - if ($Ensure -eq 'Present') - { - if ($shareExists -eq $false) - { - $PSBoundParameters.Remove('Ensure') - Write-Verbose "Creating share $Name to ensure it is Present" - - # Alter bound parameters - $newShareParameters = Get-SmbBoundParameters -BoundParameters $PSBoundParameters - # Pass the parameter collection to New-SmbShare - New-SmbShare @newShareParameters - } - else + if ($Ensure -eq 'Present') { - # Need to call either Set-SmbShare or *ShareAccess cmdlets - if ($PSBoundParameters.ContainsKey('ChangeAccess')) - { - $changeAccessValue = $PSBoundParameters['ChangeAccess'] - $PSBoundParameters.Remove('ChangeAccess') - } - if ($PSBoundParameters.ContainsKey('ReadAccess')) - { - $readAccessValue = $PSBoundParameters['ReadAccess'] - $PSBoundParameters.Remove('ReadAccess') - } - if ($PSBoundParameters.ContainsKey('FullAccess')) - { - $fullAccessValue = $PSBoundParameters['FullAccess'] - $PSBoundParameters.Remove('FullAccess') - } - if ($PSBoundParameters.ContainsKey('NoAccess')) - { - $noAccessValue = $PSBoundParameters['NoAccess'] - $PSBoundParameters.Remove('NoAccess') + $parametersToRemove = $smbShareParameters.Keys | + Where-Object -FilterScript { + $_ -in ('ChangeAccess','ReadAccess','FullAccess','NoAccess','Ensure','Path') + } + + $parametersToRemove | ForEach-Object { + $smbShareParameters.Remove($_) } # Use Set-SmbShare for performing operations other than changing access - $PSBoundParameters.Remove('Ensure') - $PSBoundParameters.Remove('Path') - Set-SmbShare @PSBoundParameters -Force + Set-SmbShare @smbShareParameters -Force - # Use *SmbShareAccess cmdlets to change access - $smbShareAccessValues = Get-SmbShareAccess -Name $Name + <# + Get a collection of all accounts that currently have access + or are denied access + #> + $getSmbShareAccessResult = Get-SmbShareAccess -Name $Name - # Remove Change permissions - $smbShareAccessValues | Where-Object { $_.AccessControlType -eq 'Allow' -and $_.AccessRight -eq 'Change' } ` - | ForEach-Object { - Remove-AccessPermission -Name $Name -UserName $_.AccountName -AccessPermission Change - } + <# + First all access must be removed for accounts that should not + have permission, or should be unblocked (those that was denied + access). After that we add new accounts. - if ($ChangeAccess -ne $null) + The switch-statement will loop through all items in the + collection. + #> + switch ($getSmbShareAccessResult.AccessControlType) { - # Add change permissions - $changeAccessValue | ForEach-Object { - Set-AccessPermission -Name $Name -AccessPermission 'Change' -Username $_ - } - } + 'Allow' + { + $removeAccessPermissionParameters = @{ + Name = $Name + UserName = $_.AccountName + } - $smbShareAccessValues = Get-SmbShareAccess -Name $Name + if ($_.AccessRight -eq 'Change' -and $_.AccountName -notin $ChangeAccess) + { + $removeAccessPermissionParameters['AccessPermission'] = 'ChangeAccess' + Remove-AccessPermission @removeAccessPermissionParameters + } - # Remove read access - $smbShareAccessValues | Where-Object { $_.AccessControlType -eq 'Allow' -and $_.AccessRight -eq 'Read' } ` - | ForEach-Object { - Remove-AccessPermission -Name $Name -UserName $_.AccountName -AccessPermission Read - } + if ($_.AccessRight -eq 'Read' -and $_.AccountName -notin $ReadAccess) + { + $removeAccessPermissionParameters['AccessPermission'] = 'ReadAccess' + Remove-AccessPermission @removeAccessPermissionParameters + } - if ($ReadAccess -ne $null) - { - # Add read access - $readAccessValue | ForEach-Object { - Set-AccessPermission -Name $Name -AccessPermission 'Read' -Username $_ + if ($_.AccessRight -eq 'Full' -and $_.AccountName -notin $FullAccess) + { + $removeAccessPermissionParameters['AccessPermission'] = 'FullAccess' + Remove-AccessPermission @removeAccessPermissionParameters + } + } + + 'Deny' + { + if ($_.AccessRight -eq 'Full' -and $_.AccountName -notin $NoAccess) + { + Remove-AccessPermission -Name $Name -UserName $_.AccountName -AccessPermission 'NoAccess' + } } } + # Update the collection after all accounts have been removed. + $getSmbShareAccessResult = Get-SmbShareAccess -Name $Name + + if ($ChangeAccess) + { + # Get already added account names. + $changeAccessAccountNames = $getSmbShareAccessResult | Where-Object -FilterScript { + $_.AccessControlType -eq 'Allow' ` + -and $_.AccessRight -eq 'Change' + } - $smbShareAccessValues = Get-SmbShareAccess -Name $Name + $newAccountsToHaveChangeAccess = $ChangeAccess | Where-Object -FilterScript { + $_ -notin $changeAccessAccountNames + } - # Remove full access - $smbShareAccessValues | Where-Object { $_.AccessControlType -eq 'Allow' -and $_.AccessRight -eq 'Full' } ` - | ForEach-Object { - Remove-AccessPermission -Name $Name -UserName $_.AccountName -AccessPermission Full + # Add new accounts that should have change permission. + $newAccountsToHaveChangeAccess | ForEach-Object { + Set-AccessPermission -Name $Name -AccessPermission 'ChangeAccess' -Username $_ + } } - - if ($FullAccess -ne $null) + if ($ReadAccess) { + # Get already added account names. + $readAccessAccountNames = $getSmbShareAccessResult | Where-Object -FilterScript { + $_.AccessControlType -eq 'Allow' ` + -and $_.AccessRight -eq 'Read' + } - # Add full access - $fullAccessValue | ForEach-Object { - Set-AccessPermission -Name $Name -AccessPermission 'Full' -Username $_ + $newAccountsToHaveReadAccess = $ReadAccess | Where-Object -FilterScript { + $_ -notin $readAccessAccountNames + } + + # Add new accounts that should have read permission. + $newAccountsToHaveReadAccess | ForEach-Object { + Set-AccessPermission -Name $Name -AccessPermission 'ReadAccess' -Username $_ } } - $smbShareAccessValues = Get-SmbShareAccess -Name $Name + if ($FullAccess) + { + # Get already added account names. + $fullAccessAccountNames = $getSmbShareAccessResult | Where-Object -FilterScript { + $_.AccessControlType -eq 'Allow' ` + -and $_.AccessRight -eq 'Full' + } - # Remove explicit deny - $smbShareAccessValues | Where-Object { $_.AccessControlType -eq 'Deny' } ` - | ForEach-Object { - Remove-AccessPermission -Name $Name -UserName $_.AccountName -AccessPermission No - } + $newAccountsToHaveFullAccess = $FullAccess | Where-Object -FilterScript { + $_ -notin $fullAccessAccountNames + } + # Add new accounts that should have full permission. + $newAccountsToHaveFullAccess | ForEach-Object { + Set-AccessPermission -Name $Name -AccessPermission 'FullAccess' -Username $_ + } + } - if ($NoAccess -ne $null) + if ($NoAccess) { - # Add explicit deny - $noAccessValue | ForEach-Object { - Set-AccessPermission -Name $Name -AccessPermission 'No' -Username $_ + # Get already added account names. + $noAccessAccountNames = $getSmbShareAccessResult | Where-Object -FilterScript { + $_.AccessControlType -eq 'Deny' ` + -and $_.AccessRight -eq 'Full' + } + + $newAccountsToHaveNoAccess = $NoAccess | Where-Object -FilterScript { + $_ -notin $noAccessAccountNames + } + + # Add new accounts that should be denied permission. + $newAccountsToHaveNoAccess | ForEach-Object { + Set-AccessPermission -Name $Name -AccessPermission 'NoAccess' -Username $_ } } } + else + { + Write-Verbose "Removing share $Name to ensure it is Absent" + Remove-SmbShare -name $Name -Force + } } else { - Write-Verbose "Removing share $Name to ensure it is Absent" - Remove-SmbShare -name $Name -Force + if ($Ensure -eq 'Present') + { + $smbShareParameters.Remove('Ensure') + + Write-Verbose "Creating share $Name to ensure it is Present" + + <# + Remove access collections that are empty, since that is + already the default for the cmdlet New-SmbShare. + #> + foreach ($accessProperty in ('ChangeAccess','ReadAccess','FullAccess','NoAccess')) + { + if ($smbShareParameters.ContainsKey($accessProperty) -and -not $smbShareParameters[$accessProperty]) + { + Write-Verbose "Parameter $accessProperty is null or empty, removing from collection." + $smbShareParameters.Remove($accessProperty) + } + } + + # Pass the parameter collection to New-SmbShare + New-SmbShare @smbShareParameters + } } } @@ -537,14 +589,15 @@ function Set-AccessPermission $UserName, [Parameter()] - [ValidateSet('Change', 'Full', 'Read', 'No')] + [ValidateSet('ChangeAccess', 'FullAccess', 'ReadAccess', 'NoAccess')] [System.String] $AccessPermission ) + $formattedString = '{0}{1}' -f $AccessPermission, 'Access' Write-Verbose -Message "Setting $formattedString for $UserName" - if ($AccessPermission -eq 'Change' -or $AccessPermission -eq 'Read' -or $AccessPermission -eq 'Full') + if ($AccessPermission -in ('ChangeAccess', 'ReadAccess', 'FullAccess')) { Grant-SmbShareAccess -Name $Name -AccountName $UserName -AccessRight $AccessPermission -Force } @@ -554,49 +607,6 @@ function Set-AccessPermission } } -function Get-SmbBoundParameters -{ - # Define parameters - Param - ( - [Parameter()] - [System.Collections.Hashtable] - $BoundParameters - ) - - # Check for null access before passing to New-SmbShare - if (($BoundParameters.ContainsKey('ChangeAccess')) -and ([string]::IsNullOrEmpty($BoundParameters['ChangeAccess']))) - { - Write-Verbose "Parameter ChangeAccess is null or empty, removing from collection." - # Remove the parameter - $BoundParameters.Remove('ChangeAccess') - } - - if (($BoundParameters.ContainsKey('ReadAccess')) -and ([string]::IsNullOrEmpty($BoundParameters['ReadAccess']))) - { - Write-Verbose "Parameter ReadAccess is null or empty, removing from collection." - # Remove the parameter - $BoundParameters.Remove('ReadAccess') - } - - if (($BoundParameters.ContainsKey('FullAccess')) -and ([string]::IsNullOrEmpty($BoundParameters['FullAccess']))) - { - Write-Verbose "Parameter FullAccess is null or empty, removing from collection." - # Remove the parameter - $BoundParameters.Remove('FullAccess') - } - - if (($BoundParameters.ContainsKey('NoAccess')) -and ([string]::IsNullOrEmpty($BoundParameters['NoAccess']))) - { - Write-Verbose "Parameter NoAccess is null or empty, removing from collection." - # Remove the parameter - $BoundParameters.Remove('NoAccess') - } - - # Return the parameter collection - return $BoundParameters -} - function Remove-AccessPermission { [CmdletBinding()] @@ -611,7 +621,7 @@ function Remove-AccessPermission $UserName, [Parameter()] - [ValidateSet('Change', 'Full', 'Read', 'No')] + [ValidateSet('ChangeAccess', 'FullAccess', 'ReadAccess', 'NoAccess')] [System.String] $AccessPermission ) @@ -620,7 +630,7 @@ function Remove-AccessPermission Write-Debug -Message "Removing $formattedString for $UserName" - if ($AccessPermission -eq 'Change' -or $AccessPermission -eq 'Read' -or $AccessPermission -eq 'Full') + if ($AccessPermission -in ('ChangeAccess', 'ReadAccess', 'FullAccess')) { Revoke-SmbShareAccess -Name $Name -AccountName $UserName -Force } diff --git a/Tests/Unit/MSFT_SmbShare.Tests.ps1 b/Tests/Unit/MSFT_SmbShare.Tests.ps1 index 6cb779b5..f8f4844a 100644 --- a/Tests/Unit/MSFT_SmbShare.Tests.ps1 +++ b/Tests/Unit/MSFT_SmbShare.Tests.ps1 @@ -481,17 +481,17 @@ try BeforeAll { Mock -CommandName Get-TargetResource -MockWith { return @{ - Ensure = 'Absent' + Ensure = 'Absent' } } } It 'Should return $false when the desired state should ''Present''' { $testTargetResourceParameters = @{ - Ensure = 'Present' - Name = $mockSmbShare.Name - Path = $mockSmbShare.Path - Verbose = $true + Ensure = 'Present' + Name = $mockSmbShare.Name + Path = $mockSmbShare.Path + Verbose = $true } $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters @@ -553,17 +553,17 @@ try BeforeAll { Mock -CommandName Get-TargetResource -MockWith { return @{ - Ensure = 'Absent' + Ensure = 'Absent' } } } It 'Should return $true when the desired state should ''Absent''' { $testTargetResourceParameters = @{ - Ensure = 'Absent' - Name = $mockSmbShare.Name - Path = $mockSmbShare.Path - Verbose = $true + Ensure = 'Absent' + Name = $mockSmbShare.Name + Path = $mockSmbShare.Path + Verbose = $true } $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters From ac37b0ce0abc97e47aede192a0fe8ad6f91f9a58 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 31 Mar 2019 13:56:57 +0200 Subject: [PATCH 03/26] Update with unit tests and improved code --- .../MSFT_SmbShare/MSFT_SmbShare.psm1 | 438 +++++++---- .../en-US/MSFT_SmbShare.strings.psd1 | 12 +- Tests/Unit/MSFT_SmbShare.Tests.ps1 | 688 ++++++++++++++---- 3 files changed, 843 insertions(+), 295 deletions(-) diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 index c94385fc..a89f97bc 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 @@ -235,157 +235,69 @@ function Set-TargetResource $Ensure = 'Present' ) - $smbShareParameters = $PSBoundParameters.Clone() + <# + Copy the $PSBoundParameters to a new hash table, so we have the + original intact. + #> + $smbShareParameters = @{} + $PSBoundParameters - $smbShare = Get-SmbShare -Name $Name -ErrorAction 'SilentlyContinue' - if ($smbShare) + $getTargetResourceResult = Get-TargetResource -Name $Name -Path $Path + if ($getTargetResourceResult.Ensure -eq 'Present') { - Write-Verbose -Message "Share with name $Name exists" + Write-Verbose -Message ($script:localizedData.IsPresent -f $Name) if ($Ensure -eq 'Present') { + Write-Verbose -Message $script:localizedData.UpdatingProperties + $parametersToRemove = $smbShareParameters.Keys | Where-Object -FilterScript { $_ -in ('ChangeAccess','ReadAccess','FullAccess','NoAccess','Ensure','Path') } + # TODO: Make sure to remove parameters that already are in desired state. + $parametersToRemove | ForEach-Object { $smbShareParameters.Remove($_) } # Use Set-SmbShare for performing operations other than changing access - Set-SmbShare @smbShareParameters -Force - - <# - Get a collection of all accounts that currently have access - or are denied access - #> - $getSmbShareAccessResult = Get-SmbShareAccess -Name $Name + Set-SmbShare @smbShareParameters -Force -ErrorAction 'Stop' - <# - First all access must be removed for accounts that should not - have permission, or should be unblocked (those that was denied - access). After that we add new accounts. + $smbShareAccessPermissionParameters = @{ + Name = $Name + } - The switch-statement will loop through all items in the - collection. - #> - switch ($getSmbShareAccessResult.AccessControlType) + if ($PSBoundParameters.ContainsKey('FullAccess')) { - 'Allow' - { - $removeAccessPermissionParameters = @{ - Name = $Name - UserName = $_.AccountName - } - - if ($_.AccessRight -eq 'Change' -and $_.AccountName -notin $ChangeAccess) - { - $removeAccessPermissionParameters['AccessPermission'] = 'ChangeAccess' - Remove-AccessPermission @removeAccessPermissionParameters - } - - if ($_.AccessRight -eq 'Read' -and $_.AccountName -notin $ReadAccess) - { - $removeAccessPermissionParameters['AccessPermission'] = 'ReadAccess' - Remove-AccessPermission @removeAccessPermissionParameters - } - - if ($_.AccessRight -eq 'Full' -and $_.AccountName -notin $FullAccess) - { - $removeAccessPermissionParameters['AccessPermission'] = 'FullAccess' - Remove-AccessPermission @removeAccessPermissionParameters - } - } - - 'Deny' - { - if ($_.AccessRight -eq 'Full' -and $_.AccountName -notin $NoAccess) - { - Remove-AccessPermission -Name $Name -UserName $_.AccountName -AccessPermission 'NoAccess' - } - } + $smbShareAccessPermissionParameters['FullAccess'] = $FullAccess } - # Update the collection after all accounts have been removed. - $getSmbShareAccessResult = Get-SmbShareAccess -Name $Name - - if ($ChangeAccess) + if ($PSBoundParameters.ContainsKey('ChangeAccess')) { - # Get already added account names. - $changeAccessAccountNames = $getSmbShareAccessResult | Where-Object -FilterScript { - $_.AccessControlType -eq 'Allow' ` - -and $_.AccessRight -eq 'Change' - } - - $newAccountsToHaveChangeAccess = $ChangeAccess | Where-Object -FilterScript { - $_ -notin $changeAccessAccountNames - } - - # Add new accounts that should have change permission. - $newAccountsToHaveChangeAccess | ForEach-Object { - Set-AccessPermission -Name $Name -AccessPermission 'ChangeAccess' -Username $_ - } + $smbShareAccessPermissionParameters['ChangeAccess'] = $ChangeAccess } - if ($ReadAccess) + if ($PSBoundParameters.ContainsKey('ReadAccess')) { - # Get already added account names. - $readAccessAccountNames = $getSmbShareAccessResult | Where-Object -FilterScript { - $_.AccessControlType -eq 'Allow' ` - -and $_.AccessRight -eq 'Read' - } - - $newAccountsToHaveReadAccess = $ReadAccess | Where-Object -FilterScript { - $_ -notin $readAccessAccountNames - } - - # Add new accounts that should have read permission. - $newAccountsToHaveReadAccess | ForEach-Object { - Set-AccessPermission -Name $Name -AccessPermission 'ReadAccess' -Username $_ - } + $smbShareAccessPermissionParameters['ReadAccess'] = $ReadAccess } - if ($FullAccess) + if ($PSBoundParameters.ContainsKey('NoAccess')) { - # Get already added account names. - $fullAccessAccountNames = $getSmbShareAccessResult | Where-Object -FilterScript { - $_.AccessControlType -eq 'Allow' ` - -and $_.AccessRight -eq 'Full' - } - - $newAccountsToHaveFullAccess = $FullAccess | Where-Object -FilterScript { - $_ -notin $fullAccessAccountNames - } - - # Add new accounts that should have full permission. - $newAccountsToHaveFullAccess | ForEach-Object { - Set-AccessPermission -Name $Name -AccessPermission 'FullAccess' -Username $_ - } + $smbShareAccessPermissionParameters['NoAccess'] = $NoAccess } - if ($NoAccess) - { - # Get already added account names. - $noAccessAccountNames = $getSmbShareAccessResult | Where-Object -FilterScript { - $_.AccessControlType -eq 'Deny' ` - -and $_.AccessRight -eq 'Full' - } + # We should only pass the access collections that the user want to enforce. + Remove-SmbShareAccessPermission @smbShareAccessPermissionParameters - $newAccountsToHaveNoAccess = $NoAccess | Where-Object -FilterScript { - $_ -notin $noAccessAccountNames - } - - # Add new accounts that should be denied permission. - $newAccountsToHaveNoAccess | ForEach-Object { - Set-AccessPermission -Name $Name -AccessPermission 'NoAccess' -Username $_ - } - } + Add-SmbShareAccessPermission @smbShareAccessPermissionParameters } else { - Write-Verbose "Removing share $Name to ensure it is Absent" - Remove-SmbShare -name $Name -Force + Write-Verbose -Message ($script:localizedData.RemoveShare -f $Name) + + Remove-SmbShare -name $Name -Force -ErrorAction 'Stop' } } else @@ -394,7 +306,7 @@ function Set-TargetResource { $smbShareParameters.Remove('Ensure') - Write-Verbose "Creating share $Name to ensure it is Present" + Write-Verbose -Message ($script:localizedData.CreateShare -f $Name) <# Remove access collections that are empty, since that is @@ -404,13 +316,11 @@ function Set-TargetResource { if ($smbShareParameters.ContainsKey($accessProperty) -and -not $smbShareParameters[$accessProperty]) { - Write-Verbose "Parameter $accessProperty is null or empty, removing from collection." $smbShareParameters.Remove($accessProperty) } } - # Pass the parameter collection to New-SmbShare - New-SmbShare @smbShareParameters + New-SmbShare @smbShareParameters -ErrorAction 'Stop' } } } @@ -536,7 +446,11 @@ function Test-TargetResource { if ($Ensure -eq 'Present') { - Write-Verbose -Message ($script:localizedData.IsPresent -f $Name) + Write-Verbose -Message ( + '{0} {1}' -f ` + ($script:localizedData.IsPresent -f $Name), + $script:localizedData.EvaluatingProperties + ) $valuesToCheck = @( 'Name' @@ -575,67 +489,283 @@ function Test-TargetResource return $testTargetResourceResult } -function Set-AccessPermission +<# + .SYNOPSIS + Removes the access permission for accounts that are no longer part + of the respectively access collections (FullAccess, ChangeAccess, + ReadAccess, and NoAccess). + + .PARAMETER Name + The name of the SMB share for which to remove access permission. + + .PARAMETER FullAccess + A string collection of account names that _should have_ full access + permission. The accounts not in this collection will be removed from + the SMB share. + + .PARAMETER ChangeAccess + A string collection of account names that _should have_ change access + permission. The accounts not in this collection will be removed from + the SMB share. + + .PARAMETER ReadAccess + A string collection of account names that _should have_ read access + permission. The accounts not in this collection will be removed from + the SMB share. + + .PARAMETER NoAccess + A string collection of account names that _should be_ denied access + to the SMB share. The accounts not in this collection will be removed + from the SMB share. + + .NOTES + The access permission is only removed if the parameter was passed + into the function. +#> +function Remove-SmbShareAccessPermission { - [CmdletBinding()] - Param + param ( - [Parameter()] + [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter()] [System.String[]] - $UserName, + $FullAccess, [Parameter()] - [ValidateSet('ChangeAccess', 'FullAccess', 'ReadAccess', 'NoAccess')] - [System.String] - $AccessPermission - ) + [System.String[]] + $ChangeAccess, - $formattedString = '{0}{1}' -f $AccessPermission, 'Access' - Write-Verbose -Message "Setting $formattedString for $UserName" + [Parameter()] + [System.String[]] + $ReadAccess, - if ($AccessPermission -in ('ChangeAccess', 'ReadAccess', 'FullAccess')) - { - Grant-SmbShareAccess -Name $Name -AccountName $UserName -AccessRight $AccessPermission -Force - } - else + [Parameter()] + [System.String[]] + $NoAccess + ) + + <# + Get a collection of all accounts that currently have access + or are denied access + #> + $getSmbShareAccessResult = Get-SmbShareAccess -Name $Name + + <# + First all access must be removed for accounts that should not + have permission, or should be unblocked (those that was denied + access). After that we add new accounts. + #> + foreach ($smbShareAccess in $getSmbShareAccessResult) { - Block-SmbShareAccess -Name $Name -AccountName $UserName -Force + switch ($smbShareAccess.AccessControlType) + { + 'Allow' + { + $shouldRevokeAccess = $false + + if ($smbShareAccess.AccessRight -eq 'Change') + { + if ($PSBoundParameters.ContainsKey('ChangeAccess') -and $smbShareAccess.AccountName -notin $ChangeAccess) + { + $shouldRevokeAccess = $true + } + } + + if ($smbShareAccess.AccessRight -eq 'Read') + { + if ($PSBoundParameters.ContainsKey('ReadAccess') -and $smbShareAccess.AccountName -notin $ReadAccess) + { + $shouldRevokeAccess = $true + } + } + + if ($smbShareAccess.AccessRight -eq 'Full') + { + if ($PSBoundParameters.ContainsKey('FullAccess') -and $smbShareAccess.AccountName -notin $FullAccess) + { + $shouldRevokeAccess = $true + } + } + + if ($shouldRevokeAccess) + { + Write-Verbose -Message ($script:localizedData.RevokeAccess -f $smbShareAccess.AccountName, $Name) + + Revoke-SmbShareAccess -Name $Name -AccountName $smbShareAccess.AccountName -Force -ErrorAction 'Stop' + } + } + + 'Deny' + { + if ($smbShareAccess.AccessRight -eq 'Full') + { + if ($PSBoundParameters.ContainsKey('NoAccess') -and $smbShareAccess.AccountName -notin $NoAccess) + { + Write-Verbose -Message ($script:localizedData.UnblockAccess -f $smbShareAccess.AccountName, $Name) + + Unblock-SmbShareAccess -Name $Name -AccountName $smbShareAccess.AccountName -Force -ErrorAction 'Stop' + } + } + } + } } } -function Remove-AccessPermission +<# + .SYNOPSIS + Add the access permission to the SMB share for accounts, in the + respectively access collections (FullAccess, ChangeAccess, + ReadAccess, and NoAccess), that do not yet have access. + + .PARAMETER Name + The name of the SMB share to add access permission to. + + .PARAMETER FullAccess + A string collection of account names that should have full access + permission. The accounts in this collection will be added to the + SMB share. + + .PARAMETER ChangeAccess + A string collection of account names that should have change access + permission. The accounts in this collection will be added to the + SMB share. + + .PARAMETER ReadAccess + A string collection of account names that should have read access + permission. The accounts in this collection will be added to the + SMB share. + + .PARAMETER NoAccess + A string collection of account names that should be denied access + to the SMB share. The accounts in this collection will be added to + the SMB share. +#> +function Add-SmbShareAccessPermission { - [CmdletBinding()] - Param + param ( - [Parameter()] + [Parameter(Mandatory = $true)] [System.String] $Name, [Parameter()] [System.String[]] - $UserName, + $FullAccess, [Parameter()] - [ValidateSet('ChangeAccess', 'FullAccess', 'ReadAccess', 'NoAccess')] - [System.String] - $AccessPermission + [System.String[]] + $ChangeAccess, + + [Parameter()] + [System.String[]] + $ReadAccess, + + [Parameter()] + [System.String[]] + $NoAccess ) - $formattedString = '{0}{1}' -f $AccessPermission, 'Access' + # Update the collection after all accounts have been removed. + $getSmbShareAccessResult = Get-SmbShareAccess -Name $Name + + if ($PSBoundParameters.ContainsKey('ChangeAccess')) + { + # Get already added account names. + $smbShareChangeAccessObjects = $getSmbShareAccessResult | Where-Object -FilterScript { + $_.AccessControlType -eq 'Allow' ` + -and $_.AccessRight -eq 'Change' + } + + # Get a collection of just the account names. + $changeAccessAccountNames = @($smbShareChangeAccessObjects.AccountName) + + $newAccountsToHaveChangeAccess = $ChangeAccess | Where-Object -FilterScript { + $_ -notin $changeAccessAccountNames + } + + $accessRight = 'Change' + + # Add new accounts that should have change permission. + $newAccountsToHaveChangeAccess | ForEach-Object { + Write-Verbose -Message ($script:localizedData.GrantAccess -f $accessRight, $_, $Name) + + Grant-SmbShareAccess -Name $Name -AccountName $_ -AccessRight $accessRight -Force -ErrorAction 'Stop' + } + } + + if ($PSBoundParameters.ContainsKey('ReadAccess')) + { + # Get already added account names. + $smbShareReadAccessObjects = $getSmbShareAccessResult | Where-Object -FilterScript { + $_.AccessControlType -eq 'Allow' ` + -and $_.AccessRight -eq 'Read' + } + + # Get a collection of just the account names. + $readAccessAccountNames = @($smbShareReadAccessObjects.AccountName) - Write-Debug -Message "Removing $formattedString for $UserName" + $newAccountsToHaveReadAccess = $ReadAccess | Where-Object -FilterScript { + $_ -notin $readAccessAccountNames + } + + $accessRight = 'Read' + + # Add new accounts that should have read permission. + $newAccountsToHaveReadAccess | ForEach-Object { + Write-Verbose -Message ($script:localizedData.GrantAccess -f $accessRight, $_, $Name) + + Grant-SmbShareAccess -Name $Name -AccountName $_ -AccessRight $accessRight -Force -ErrorAction 'Stop' + } + } - if ($AccessPermission -in ('ChangeAccess', 'ReadAccess', 'FullAccess')) + if ($PSBoundParameters.ContainsKey('FullAccess')) { - Revoke-SmbShareAccess -Name $Name -AccountName $UserName -Force + # Get already added account names. + $smbShareFullAccessObjects = $getSmbShareAccessResult | Where-Object -FilterScript { + $_.AccessControlType -eq 'Allow' ` + -and $_.AccessRight -eq 'Full' + } + + # Get a collection of just the account names. + $fullAccessAccountNames = @($smbShareFullAccessObjects.AccountName) + + $newAccountsToHaveFullAccess = $FullAccess | Where-Object -FilterScript { + $_ -notin $fullAccessAccountNames + } + + $accessRight = 'Full' + + # Add new accounts that should have full permission. + $newAccountsToHaveFullAccess | ForEach-Object { + Write-Verbose -Message ($script:localizedData.GrantAccess -f $accessRight, $_, $Name) + + Grant-SmbShareAccess -Name $Name -AccountName $_ -AccessRight $accessRight -Force -ErrorAction 'Stop' + } } - else + + if ($PSBoundParameters.ContainsKey('NoAccess')) { - Unblock-SmbShareAccess -Name $Name -AccountName $UserName -Force + # Get already added account names. + $smbShareNoAccessObjects = $getSmbShareAccessResult | Where-Object -FilterScript { + $_.AccessControlType -eq 'Deny' ` + -and $_.AccessRight -eq 'Full' + } + + # Get a collection of just the account names. + $noAccessAccountNames = @($smbShareNoAccessObjects.AccountName) + + $newAccountsToHaveNoAccess = $NoAccess | Where-Object -FilterScript { + $_ -notin $noAccessAccountNames + } + + # Add new accounts that should be denied permission. + $newAccountsToHaveNoAccess | ForEach-Object { + Write-Verbose -Message ($script:localizedData.DenyAccess -f $_, $Name) + + Block-SmbShareAccess -Name $Name -AccountName $_ -Force -ErrorAction 'Stop' + } } } diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 index 16a6fde8..488df0a9 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 @@ -4,6 +4,14 @@ ConvertFrom-StringData @' GetTargetResourceMessage = Getting the current state of the SMB share '{0}'. TestTargetResourceMessage = Determining if the SMB share '{0}' is in the desired state. ShareNotFound = Unable to find a SMB share with the name '{0}'. - IsPresent = The SMB share '{0}' exist, evaluating the properties. - IsAbsent = The SMB share '{0}' does not exist. + IsPresent = The SMB share with the name '{0}' exist. + IsAbsent = The SMB share with the name '{0}' does not exist. + EvaluatingProperties = Evaluating the properties of the SMB share. + UpdatingProperties = Updating properties on the SMB share that are not in desired state. + RemoveShare = Removing the SMB share with the name '{0}'. + CreateShare = Creating a SMB share with the name '{0}'. + RevokeAccess = Revoking granted permission for account '{0}' on the SMB share with the name '{1}'. + UnblockAccess = Revoking denied permission for account '{0}' on the SMB share with the name '{1}'. + GrantAccess = Granting '{0}' permission for account '{1}' on the SMB share with the name '{2}'. + DenyAccess = Denying permission for account '{0}' on the SMB share with the name '{1}'. '@ diff --git a/Tests/Unit/MSFT_SmbShare.Tests.ps1 b/Tests/Unit/MSFT_SmbShare.Tests.ps1 index f8f4844a..e2cbdeef 100644 --- a/Tests/Unit/MSFT_SmbShare.Tests.ps1 +++ b/Tests/Unit/MSFT_SmbShare.Tests.ps1 @@ -34,6 +34,7 @@ try Invoke-TestSetup InModuleScope $script:DSCResourceName { + $mockShareName = 'TestShare' $mockChangePermissionUserName = @('User1') $mockReadPermissionUserName = @('User2') $mockFullPermissionUserName = @('User3', 'User4') @@ -41,7 +42,7 @@ try $mockSmbShare = ( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name 'Name' -Value 'DummyShare' -PassThru | + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | Add-Member -MemberType NoteProperty -Name 'Path' -Value 'c:\temp' -PassThru | Add-Member -MemberType NoteProperty -Name 'Description' 'Dummy share for unit testing' -PassThru | Add-Member -MemberType NoteProperty -Name 'ConcurrentUserLimit' -Value 10 -PassThru | @@ -54,37 +55,44 @@ try Add-Member -MemberType NoteProperty -Name 'Special' -Value $false -PassThru -Force ) - $mockChangePermissionUserName = 'User1' $mockSmbShareAccess = @( ( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name 'Name' -Value 'DummyShare' -PassThru | + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockFullPermissionUserName -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockFullPermissionUserName[0] -PassThru | Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Allow' -PassThru | Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Full' -PassThru -Force ), ( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name 'Name' -Value 'DummyShare' -PassThru | + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockChangePermissionUserName -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockFullPermissionUserName[1] -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Allow' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Full' -PassThru -Force + ), + ( + New-Object -TypeName Object | + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | + Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockChangePermissionUserName[0] -PassThru | Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Allow' -PassThru | Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Change' -PassThru -Force ), ( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name 'Name' -Value 'DummyShare' -PassThru | + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockReadPermissionUserName -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockReadPermissionUserName[0] -PassThru | Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Allow' -PassThru | Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Read' -PassThru -Force ), ( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name 'Name' -Value 'DummyShare' -PassThru | + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockNoPermissionUserName -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockNoPermissionUserName[0] -PassThru | Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Deny' -PassThru | Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Full' -PassThru -Force ) @@ -172,135 +180,148 @@ try } } - # Describe 'MSFT_SmbShare\Set-TargetResource' -Tag 'Set' { - # Context 'When the system is not in the desired state' { - # BeforeAll { - # # Per context-block initialization - # } - - # # Set the testParameter collection - # $testParameters = @{ - # ChangeAccess = $mockChangePermissionUserName - # ReadAccess = $mockReadPermissionUserName - # FullAccess = $mockFullPermissionUserName - # NoAccess = $mockNoAccess - # Name = $mockSmbShare.Name - # Path = $mockSmbShare.Path - # Description = $mockSmbShare.Description - # ConcurrentUserLimit = $mockSmbShare.ConcurrentUserLimit - # EncryptData = $mockSmbShare.EncryptData - # FolderEnumerationMode = $mockSmbShare.FolderEnumerationMode - # Ensure = 'Present' - # } - - # # Init the script variables - # $script:ChangeAccess = @() - # $script:ReadAccess = @() - # $script:FullAccess = @() - # $script:NoAccess = @() - # $script:ChangeAccess += $mockDefaultChangePermissionUserName - # $script:ReadAccess += $mockDefaultReadPermissionUserName - # $script:FullAccess += $mockDefaultFullPermissionUserName - # $script:NoAccess += $mockDefaultNoPermissionUserName - - - # # Set mock function calls - # Mock -CommandName Get-SmbShare -MockWith { return @($mockSmbShare)} - # Mock -CommandName Get-SmbShareAccess -MockWith { return @($mockSmbShareAccess)} - # Mock -CommandName Set-SmbShare -MockWith { return $null} - # Mock -CommandName Grant-SmbShareAccess -MockWith { - # # Declare local array -- use of this variable was necessary as the script: context was losing the fact it was an array in the mock - # $localArray = @() - - # switch($AccessPermission) - # { - # 'Change' - # { - # $localArray += $script:ChangeAccess - # if ($localArray -notcontains $UserName) - # { - # $localArray += $UserName - # } - - # $script:ChangeAccess = $localArray - # break - # } - # 'Read' - # { - # $localArray += $script:ReadAccess - # if($localArray -notcontains $UserName) - # { - # $localArray += $UserName - # } - # $script:ReadAccess = $localArray - # break - # } - # 'Full' - # { - # $localArray += $script:FullAccess - # if($localArray -notcontains $UserName) - # { - # $localArray += $UserName - # } - # $script:FullAccess = $localArray - # break - # } - # } - # } - # Mock Block-SmbShareAccess -MockWith { - # $script:NoAccess += $UserName - # } - # Mock Revoke-SmbShareAccess -MockWith { - # switch($AccessPermission) - # { - # 'Change' - # { - # # Remove from array - # $script:ChangeAccess = $script:ChangeAccess | Where-Object {$_ -ne $UserName} - # break - # } - # 'Read' - # { - # $script:ReadAccess = $script:ReadAccess | Where-Object {$_ -ne $UserName} - # break - # } - # 'Full' - # { - # $script:FullAccess = $script:FullAccess | Where-Object {$_ -ne $UserName} - # break - # } - # } - # } - # Mock -CommandName Unblock-SmbShareAccess -MockWith { - # $script:NoAccess = $script:NoAccess | Where-Object {$_ -ne $UserName} - # } - - - - # It 'Should alter permissions' { - # $result = Set-TargetResource @testParameters - # $script:ChangeAccess | Should Be $mockChangePermissionUserName - # $script:ReadAccess | Should Be $mockReadPermissionUserName - # $script:FullAccess | Should Be $mockFullPermissionUserName - # #$script:NoAccess | Should Be $mockNoPermissionUserName - # } - - # It 'Should call the mock function Get-SmbShare' { - # $result = Set-TargetResource @testParameters - # Assert-MockCalled Get-SmbShare -Exactly -Times 1 -Scope It - # } - - # It 'Should Call the mock function Get-SmbShareAccess' { - # $result = Set-TargetResource @testParameters - # Assert-MockCalled Get-SmbShareAccess -Exactly -Times 4 -Scope It - # } - - # It 'Should call the mock function Set-SmbShare' { - # $result = Set-TargetResource @testParameters - # Assert-MockCalled Set-SmbShare -Exactly -Times 1 -Scope It - # } - # } - # } + Describe 'MSFT_SmbShare\Set-TargetResource' -Tag 'Set' { + Context 'When the system is not in the desired state' { + BeforeAll { + Mock -CommandName New-SmbShare + Mock -CommandName Set-SmbShare + Mock -CommandName Remove-SmbShareAccessPermission + Mock -CommandName Add-SmbShareAccessPermission + Mock -CommandName Remove-SmbShare + } + + Context 'When the configuration should be present' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Name = $mockShareName + Path = $null + Description = $null + ConcurrentUserLimit = [System.UInt32] 0 + EncryptData = $false + FolderEnumerationMode = $null + CachingMode = $null + ContinuouslyAvailable = $false + ShareState = $null + ShareType = $null + ShadowCopy = $null + Special = $null + FullAccess = [System.String[]] @() + ChangeAccess = [System.String[]] @() + ReadAccess = [System.String[]] @() + NoAccess = [System.String[]] @() + Ensure = 'Absent' + } + } + } + + It 'Should call the correct mocks' { + $setTargetResourceParameters = @{ + Name = $mockShareName + Path = 'TestDrive:\Temp' + Description = 'Some description' + ConcurrentUserLimit = 2 + EncryptData = $false + FolderEnumerationMode = 'AccessBased' + CachingMode = 'Manual' + ContinuouslyAvailable = $true + ChangeAccess = $mockChangePermissionUserName + ReadAccess = $mockReadPermissionUserName + FullAccess = $mockFullPermissionUserName + # Testing removal of empty collections + NoAccess = @() + Verbose = $true + } + + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled New-SmbShare -Exactly -Times 1 -Scope It + Assert-MockCalled Set-SmbShare -Exactly -Times 0 -Scope It + Assert-MockCalled Remove-SmbShare -Exactly -Times 0 -Scope It + } + } + + Context 'When the configuration should be absent' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Name = $mockShareName + Ensure = 'Present' + } + } + } + + It 'Should call the correct mocks' { + $setTargetResourceParameters = @{ + Name = $mockShareName + Path = 'TestDrive:\Temp' + Ensure = 'Absent' + Verbose = $true + } + + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled New-SmbShare -Exactly -Times 0 -Scope It + Assert-MockCalled Set-SmbShare -Exactly -Times 0 -Scope It + Assert-MockCalled Remove-SmbShare -Exactly -Times 1 -Scope It + } + } + + Context 'When the configuration has a property that is not in desired state' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Name = $mockSmbShare.Name + Path = $mockSmbShare.Path + Description = $mockSmbShare.Description + ConcurrentUserLimit = [System.UInt32] $mockSmbShare.ConcurrentUserLimit + EncryptData = $mockSmbShare.EncryptData + FolderEnumerationMode = $mockSmbShare.FolderEnumerationMode + CachingMode = $mockSmbShare.CachingMode + # Property that is not in desired state. + ContinuouslyAvailable = $false + ShareState = $mockSmbShare.ShareState + ShareType = $mockSmbShare.ShareType + ShadowCopy = $mockSmbShare.ShadowCopy + Special = $mockSmbShare.Special + FullAccess = [System.String[]] $mockFullPermissionUserName + ChangeAccess = [System.String[]] $mockChangePermissionUserName + ReadAccess = [System.String[]] $mockReadPermissionUserName + NoAccess = [System.String[]] $mockNoPermissionUserName + Ensure = 'Present' + } + } + } + + It 'Should call the correct mocks' { + $setTargetResourceParameters = @{ + Name = $mockShareName + Path = 'TestDrive:\Temp' + Description = 'Some description' + ConcurrentUserLimit = 2 + EncryptData = $false + FolderEnumerationMode = 'AccessBased' + CachingMode = 'Manual' + ContinuouslyAvailable = $true + ChangeAccess = $mockChangePermissionUserName + ReadAccess = $mockReadPermissionUserName + FullAccess = $mockFullPermissionUserName + NoAccess = $mockNoPermissionUserName + Verbose = $true + } + + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled Set-SmbShare -Exactly -Times 1 -Scope It + Assert-MockCalled Remove-SmbShareAccessPermission -Exactly -Times 1 -Scope It + Assert-MockCalled Add-SmbShareAccessPermission -Exactly -Times 1 -Scope It + Assert-MockCalled New-SmbShare -Exactly -Times 0 -Scope It + Assert-MockCalled Remove-SmbShare -Exactly -Times 0 -Scope It + } + } + + } + } Describe 'MSFT_SmbShare\Test-TargetResource' -Tag 'Test' { Context 'When the system is not in the desired state' { @@ -572,6 +593,395 @@ try } } } + + Describe 'MSFT_SmbShare\Add-SmbShareAccessPermission' -Tag 'Helper' { + BeforeAll { + Mock -CommandName Grant-SmbShareAccess + Mock -CommandName Block-SmbShareAccess + + Mock -CommandName Get-SmbShareAccess -MockWith { + <# + Mocked permission: + + Full = @('User3', 'User4') + Change = @('User1') + Read = @('User2') + Denied = @('DeniedUser1') + #> + return $mockSmbShareAccess + } + } + + Context 'When adding granted permissions to an SMB share' { + BeforeAll { + $mockExpectedAccountToBeAdded = 'NewUser' + } + + AfterEach { + Assert-MockCalled -CommandName Block-SmbShareAccess -Exactly -Times 0 -Scope 'It' + } + + Context 'When an account with full access should be added' { + BeforeAll { + $addSmbShareAccessPermissionParameters = @{ + Name = $mockShareName + # User3 is an already present account. It should not be added. + FullAccess = @('User3', $mockExpectedAccountToBeAdded) + Verbose = $true + } + } + + It 'Should call the correct mock' { + { Add-SmbShareAccessPermission @addSmbShareAccessPermissionParameters } | Should -Not -Throw + + <# + Assert that Grant-SmbShareAccess is only called once, and + that only time was with the correct parameters. + #> + Assert-MockCalled -CommandName Grant-SmbShareAccess -Exactly -Times 1 -Scope 'It' + Assert-MockCalled -CommandName Grant-SmbShareAccess -ParameterFilter { + $Name -eq $mockShareName ` + -and $AccessRight -eq 'Full' ` + -and $AccountName -eq $mockExpectedAccountToBeAdded + } -Exactly -Times 1 -Scope 'It' + } + } + + Context 'When an account with change access should be added' { + BeforeAll { + $addSmbShareAccessPermissionParameters = @{ + Name = $mockShareName + # User1 is an already present account. It should not be added. + ChangeAccess = @('User1', $mockExpectedAccountToBeAdded) + Verbose = $true + } + } + + It 'Should call the correct mock' { + { Add-SmbShareAccessPermission @addSmbShareAccessPermissionParameters } | Should -Not -Throw + + <# + Assert that Grant-SmbShareAccess is only called once, and + that only time was with the correct parameters. + #> + Assert-MockCalled -CommandName Grant-SmbShareAccess -Exactly -Times 1 -Scope 'It' + Assert-MockCalled -CommandName Grant-SmbShareAccess -ParameterFilter { + $Name -eq $mockShareName ` + -and $AccessRight -eq 'Change' ` + -and $AccountName -eq $mockExpectedAccountToBeAdded + } -Exactly -Times 1 -Scope 'It' + } + } + + Context 'When an account with read access should be added' { + BeforeAll { + $addSmbShareAccessPermissionParameters = @{ + Name = $mockShareName + # User2 is an already present account. It should not be added. + ReadAccess = @('User2', $mockExpectedAccountToBeAdded) + Verbose = $true + } + } + + It 'Should call the correct mock' { + { Add-SmbShareAccessPermission @addSmbShareAccessPermissionParameters } | Should -Not -Throw + + <# + Assert that Grant-SmbShareAccess is only called once, and + that only time was with the correct parameters. + #> + Assert-MockCalled -CommandName Grant-SmbShareAccess -Exactly -Times 1 -Scope 'It' + Assert-MockCalled -CommandName Grant-SmbShareAccess -ParameterFilter { + $Name -eq $mockShareName ` + -and $AccessRight -eq 'Read' ` + -and $AccountName -eq $mockExpectedAccountToBeAdded + } -Exactly -Times 1 -Scope 'It' + } + } + + Context 'When accounts with different access should be added' { + BeforeAll { + $mockExpectedAccountToBeAdded1 = 'NweUser1' + $mockExpectedAccountToBeAdded2 = 'NweUser2' + $mockExpectedAccountToBeAdded3 = 'NweUser3' + + $addSmbShareAccessPermissionParameters = @{ + Name = $mockShareName + # User1, User2, and User3 is an already present account. It should not be added. + FullAccess = @('User3', $mockExpectedAccountToBeAdded1) + ReadAccess = @('User2', $mockExpectedAccountToBeAdded2) + ChangeAccess = @('User1', $mockExpectedAccountToBeAdded3) + Verbose = $true + } + } + + It 'Should call the correct mock' { + { Add-SmbShareAccessPermission @addSmbShareAccessPermissionParameters } | Should -Not -Throw + + <# + Assert that Grant-SmbShareAccess is only called once, and + that only time was with the correct parameters. + #> + Assert-MockCalled -CommandName Grant-SmbShareAccess -Exactly -Times 3 -Scope 'It' + Assert-MockCalled -CommandName Grant-SmbShareAccess -ParameterFilter { + $Name -eq $mockShareName ` + -and $AccessRight -eq 'Change' ` + -and $AccountName -eq $mockExpectedAccountToBeAdded3 + } -Exactly -Times 1 -Scope 'It' + + Assert-MockCalled -CommandName Grant-SmbShareAccess -ParameterFilter { + $Name -eq $mockShareName ` + -and $AccessRight -eq 'Full' ` + -and $AccountName -eq $mockExpectedAccountToBeAdded1 + } -Exactly -Times 1 -Scope 'It' + + Assert-MockCalled -CommandName Grant-SmbShareAccess -ParameterFilter { + $Name -eq $mockShareName ` + -and $AccessRight -eq 'Read' ` + -and $AccountName -eq $mockExpectedAccountToBeAdded2 + } -Exactly -Times 1 -Scope 'It' + } + } + } + + Context 'When denying permissions on an SMB share' { + AfterEach { + Assert-MockCalled -CommandName Grant-SmbShareAccess -Exactly -Times 0 -Scope 'It' + } + + Context 'When an account with denied access should be revoked' { + BeforeAll { + $mockExpectedAccountToBeBlocked = 'NewDeniedUser' + + $removeSmbShareAccessPermissionParameters = @{ + Name = $mockShareName + NoAccess = @('DeniedUser1', $mockExpectedAccountToBeBlocked) + Verbose = $true + } + } + + It 'Should call the correct mock' { + { Add-SmbShareAccessPermission @removeSmbShareAccessPermissionParameters } | Should -Not -Throw + + <# + Assert that Block-SmbShareAccess is only called once, and + that only time was with the correct parameters. + #> + Assert-MockCalled -CommandName Block-SmbShareAccess -Exactly -Times 1 -Scope 'It' + Assert-MockCalled -CommandName Block-SmbShareAccess -ParameterFilter { + $Name -eq $mockShareName ` + -and $AccountName -eq $mockExpectedAccountToBeBlocked + } -Exactly -Times 1 -Scope 'It' + } + } + } + } + + Describe 'MSFT_SmbShare\Remove-SmbShareAccessPermission' -Tag 'Helper' { + BeforeAll { + Mock -CommandName Revoke-SmbShareAccess + Mock -CommandName Unblock-SmbShareAccess + + Mock -CommandName Get-SmbShareAccess -MockWith { + <# + Mocked permission: + + Full = @('User3', 'User4') + Change = @('User1') + Read = @('User2') + Denied = @('DeniedUser1') + #> + return $mockSmbShareAccess + } + } + + Context 'When revoking granted permissions from an SMB share' { + AfterEach { + Assert-MockCalled -CommandName Unblock-SmbShareAccess -Exactly -Times 0 -Scope 'It' + } + + Context 'When an account with full access should be removed' { + BeforeAll { + $mockExpectedAccountToBeRemoved = 'User4' + + $removeSmbShareAccessPermissionParameters = @{ + Name = $mockShareName + FullAccess = @('User3') + Verbose = $true + } + } + + It 'Should call the correct mock' { + { Remove-SmbShareAccessPermission @removeSmbShareAccessPermissionParameters } | Should -Not -Throw + + <# + Assert that Revoke-SmbShareAccess is only called once, and + that only time was with the correct parameters. + #> + Assert-MockCalled -CommandName Revoke-SmbShareAccess -Exactly -Times 1 -Scope 'It' + Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { + $Name -eq $mockShareName ` + -and $AccountName -eq $mockExpectedAccountToBeRemoved + } -Exactly -Times 1 -Scope 'It' + } + } + + Context 'When an all accounts with full access should be removed' { + BeforeAll { + $mockExpectedAccountToBeRemoved1 = 'User3' + $mockExpectedAccountToBeRemoved2 = 'User4' + + $removeSmbShareAccessPermissionParameters = @{ + Name = $mockShareName + FullAccess = @() + Verbose = $true + } + } + + It 'Should call the correct mocks' { + { Remove-SmbShareAccessPermission @removeSmbShareAccessPermissionParameters } | Should -Not -Throw + + <# + Assert that Revoke-SmbShareAccess is called twice, and + that both times it is called with the correct parameters. + #> + Assert-MockCalled -CommandName Revoke-SmbShareAccess -Exactly -Times 2 -Scope 'It' + Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { + $Name -eq $mockShareName ` + -and $AccountName -eq $mockExpectedAccountToBeRemoved1 + } -Exactly -Times 1 -Scope 'It' + + Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { + $Name -eq $mockShareName ` + -and $AccountName -eq $mockExpectedAccountToBeRemoved2 + } -Exactly -Times 1 -Scope 'It' + } + } + + Context 'When an account with change access should be removed' { + BeforeAll { + $mockExpectedAccountToBeRemoved = 'User1' + + $removeSmbShareAccessPermissionParameters = @{ + Name = $mockShareName + ChangeAccess = @() + Verbose = $true + } + } + + It 'Should call the correct mock' { + { Remove-SmbShareAccessPermission @removeSmbShareAccessPermissionParameters } | Should -Not -Throw + + <# + Assert that Revoke-SmbShareAccess is only called once, and + that only time was with the correct parameters. + #> + Assert-MockCalled -CommandName Revoke-SmbShareAccess -Exactly -Times 1 -Scope 'It' + Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { + $Name -eq $mockShareName ` + -and $AccountName -eq $mockExpectedAccountToBeRemoved + } -Exactly -Times 1 -Scope 'It' + } + } + + Context 'When an account with read access should be removed' { + BeforeAll { + $mockExpectedAccountToBeRemoved = 'User2' + + $removeSmbShareAccessPermissionParameters = @{ + Name = $mockShareName + ReadAccess = @() + Verbose = $true + } + } + + It 'Should call the correct mock' { + { Remove-SmbShareAccessPermission @removeSmbShareAccessPermissionParameters } | Should -Not -Throw + + <# + Assert that Revoke-SmbShareAccess is only called for each account, + and each time with the correct parameters. + #> + Assert-MockCalled -CommandName Revoke-SmbShareAccess -Exactly -Times 1 -Scope 'It' + Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { + $Name -eq $mockShareName ` + -and $AccountName -eq $mockExpectedAccountToBeRemoved + } -Exactly -Times 1 -Scope 'It' + + + } + } + + Context 'When an all granted access should be removed' { + BeforeAll { + $removeSmbShareAccessPermissionParameters = @{ + Name = $mockShareName + FullAccess = @() + ChangeAccess = @() + ReadAccess = @() + Verbose = $true + } + } + + It 'Should call the correct mock' { + { Remove-SmbShareAccessPermission @removeSmbShareAccessPermissionParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Revoke-SmbShareAccess -Exactly -Times 4 -Scope 'It' + Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { + $Name -eq $mockShareName ` + -and $AccountName -eq 'User1' + } -Exactly -Times 1 -Scope 'It' + + Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { + $Name -eq $mockShareName ` + -and $AccountName -eq 'User2' + } -Exactly -Times 1 -Scope 'It' + + Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { + $Name -eq $mockShareName ` + -and $AccountName -eq 'User3' + } -Exactly -Times 1 -Scope 'It' + + Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { + $Name -eq $mockShareName ` + -and $AccountName -eq 'User4' + } -Exactly -Times 1 -Scope 'It' + } + } + } + + Context 'When revoking denied permissions from an SMB share' { + AfterEach { + Assert-MockCalled -CommandName Revoke-SmbShareAccess -Exactly -Times 0 -Scope 'It' + } + + Context 'When an account with denied access should be revoked' { + BeforeAll { + $mockExpectedAccountToBeUnblocked = 'DeniedUser1' + + $removeSmbShareAccessPermissionParameters = @{ + Name = $mockShareName + NoAccess = @() + Verbose = $true + } + } + + It 'Should call the correct mock' { + { Remove-SmbShareAccessPermission @removeSmbShareAccessPermissionParameters } | Should -Not -Throw + + <# + Assert that Block-SmbShareAccess is only called once, and + that only time was with the correct parameters. + #> + Assert-MockCalled -CommandName Unblock-SmbShareAccess -Exactly -Times 1 -Scope 'It' + Assert-MockCalled -CommandName Unblock-SmbShareAccess -ParameterFilter { + $Name -eq $mockShareName ` + -and $AccountName -eq $mockExpectedAccountToBeUnblocked + } -Exactly -Times 1 -Scope 'It' + } + } + } + } } } finally From 9e0d8020b2c37927a954f2f143357f2c1414e359 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 31 Mar 2019 14:04:59 +0200 Subject: [PATCH 04/26] Updated README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4b477cc7..aaf33cfa 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ The **ComputerManagementDsc** module contains the following resources: which is only available on Windows Server 2012/Windows 8 and above. DSC configurations containing this resource may be compiled on Windows Server 2008 R2/Windows 7 but can not be applied._ +- **SmbShare**: this resource is use to manage SMB shares on a machine. - **TimeZone**: this resource is used for setting the time zone on a machine. - **VirtualMemory**: allows configuration of properties of the paging file on the local computer. From 574cebee946fa52585ff5d0422687d0dd8b8632d Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 31 Mar 2019 14:06:43 +0200 Subject: [PATCH 05/26] Added resource README.md --- .../DSCResources/MSFT_SmbShare/README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/README.md diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/README.md b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/README.md new file mode 100644 index 00000000..cce9e1a5 --- /dev/null +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/README.md @@ -0,0 +1,4 @@ +# Description + +The resource is used to manage SMB shares and access permissions to +SMB shares. From fc0eac78b020d83c8feb20b26782b8067e92ff16 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 31 Mar 2019 14:33:25 +0200 Subject: [PATCH 06/26] Update unit test --- Tests/Unit/MSFT_SmbShare.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Unit/MSFT_SmbShare.Tests.ps1 b/Tests/Unit/MSFT_SmbShare.Tests.ps1 index e2cbdeef..1204b8f0 100644 --- a/Tests/Unit/MSFT_SmbShare.Tests.ps1 +++ b/Tests/Unit/MSFT_SmbShare.Tests.ps1 @@ -254,7 +254,7 @@ try It 'Should call the correct mocks' { $setTargetResourceParameters = @{ Name = $mockShareName - Path = 'TestDrive:\Temp' + Path = 'AnyValue' Ensure = 'Absent' Verbose = $true } From 1acaf853c3e8fca517af7a3e0918d56fff39397b Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 31 Mar 2019 14:33:32 +0200 Subject: [PATCH 07/26] Adding examples --- .../1-SmbShare_CreateShare_Config.ps1 | 37 +++++++++++++++ ...bShare_CreateShareAllProperties_Config.ps1 | 47 +++++++++++++++++++ .../3-SmbShare_RemoveShare_Config.ps1 | 41 ++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 Modules/ComputerManagementDsc/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 create mode 100644 Modules/ComputerManagementDsc/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 create mode 100644 Modules/ComputerManagementDsc/Examples/Resources/SmbShare/3-SmbShare_RemoveShare_Config.ps1 diff --git a/Modules/ComputerManagementDsc/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 b/Modules/ComputerManagementDsc/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 new file mode 100644 index 00000000..b1256261 --- /dev/null +++ b/Modules/ComputerManagementDsc/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 @@ -0,0 +1,37 @@ +<#PSScriptInfo +.VERSION 1.0.0 +.GUID d0847694-6a83-4f5b-bf6f-30cb078033bc +.AUTHOR Microsoft Corporation +.COMPANYNAME Microsoft Corporation +.COPYRIGHT (c) Microsoft Corporation. All rights reserved. +.TAGS DSCConfiguration +.LICENSEURI https://github.com/PowerShell/ComputerManagementDsc/blob/master/LICENSE +.PROJECTURI https://github.com/PowerShell/ComputerManagementDsc +.ICONURI +.EXTERNALMODULEDEPENDENCIES +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +.RELEASENOTES First version. +.PRIVATEDATA 2016-Datacenter,2016-Datacenter-Server-Core +#> + +#Requires -module ComputerManagementDsc + +<# + .DESCRIPTION + This examples create a SMB share named 'Temp' for the path 'C:\Temp', + using the default values of the cmdlet `New-SmbShare`. +#> +Configuration SmbShare_CreateShare_Config +{ + Import-DscResource -ModuleName ComputerManagementDsc + + Node localhost + { + SmbShare 'TempShare' + { + Name = 'Temp' + Path = 'C:\Temp' + } + } +} diff --git a/Modules/ComputerManagementDsc/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 b/Modules/ComputerManagementDsc/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 new file mode 100644 index 00000000..9d28e519 --- /dev/null +++ b/Modules/ComputerManagementDsc/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 @@ -0,0 +1,47 @@ +<#PSScriptInfo +.VERSION 1.0.0 +.GUID 27cc4f2a-e366-49cb-93d6-2f094567ebf3 +.AUTHOR Microsoft Corporation +.COMPANYNAME Microsoft Corporation +.COPYRIGHT (c) Microsoft Corporation. All rights reserved. +.TAGS DSCConfiguration +.LICENSEURI https://github.com/PowerShell/ComputerManagementDsc/blob/master/LICENSE +.PROJECTURI https://github.com/PowerShell/ComputerManagementDsc +.ICONURI +.EXTERNALMODULEDEPENDENCIES +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +.RELEASENOTES First version. +.PRIVATEDATA 2016-Datacenter,2016-Datacenter-Server-Core +#> + +#Requires -module ComputerManagementDsc + +<# + .DESCRIPTION + This examples create a SMB share named 'Temp' for the path 'C:\Temp', + using the default values of the cmdlet `New-SmbShare`. +#> +Configuration SmbShare_CreateShareAllProperties_Config +{ + Import-DscResource -ModuleName ComputerManagementDsc + + Node localhost + { + SmbShare 'TempShare' + { + Name = 'Temp' + Path = 'C:\Temp' + Description = 'Some description' + ConcurrentUserLimit = 20 + EncryptData = $false + FolderEnumerationMode = 'AccessBased' + CachingMode = 'Manual' + ContinuouslyAvailable = $false + FullAccess = @() + ChangeAccess = @('AdminUser1') + ReadAccess = @('Everyone') + NoAccess = @('DeniedUser1') + } + } +} diff --git a/Modules/ComputerManagementDsc/Examples/Resources/SmbShare/3-SmbShare_RemoveShare_Config.ps1 b/Modules/ComputerManagementDsc/Examples/Resources/SmbShare/3-SmbShare_RemoveShare_Config.ps1 new file mode 100644 index 00000000..52d38201 --- /dev/null +++ b/Modules/ComputerManagementDsc/Examples/Resources/SmbShare/3-SmbShare_RemoveShare_Config.ps1 @@ -0,0 +1,41 @@ +<#PSScriptInfo +.VERSION 1.0.0 +.GUID f11d7558-0748-4a72-b743-34424cbf4407 +.AUTHOR Microsoft Corporation +.COMPANYNAME Microsoft Corporation +.COPYRIGHT (c) Microsoft Corporation. All rights reserved. +.TAGS DSCConfiguration +.LICENSEURI https://github.com/PowerShell/ComputerManagementDsc/blob/master/LICENSE +.PROJECTURI https://github.com/PowerShell/ComputerManagementDsc +.ICONURI +.EXTERNALMODULEDEPENDENCIES +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +.RELEASENOTES First version. +.PRIVATEDATA 2016-Datacenter,2016-Datacenter-Server-Core +#> + +#Requires -module ComputerManagementDsc + +<# + .DESCRIPTION + This examples removes a SMB share named 'Temp'. + + .NOTES + Path must be specified because it is a mandatory parameter, + but it can be set to any value. +#> +Configuration SmbShare_RemoveShare_Config +{ + Import-DscResource -ModuleName ComputerManagementDsc + + Node localhost + { + SmbShare 'TempShare' + { + Ensure = 'Absent' + Name = 'Temp' + Path = 'NotUsed' + } + } +} From 9d02d42e7dff2166b4d73b0d18f26626d09bfb42 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 31 Mar 2019 19:06:04 +0200 Subject: [PATCH 08/26] Added integration tests --- .../MSFT_SmbShare.Integration.Tests.ps1 | 198 ++++++++++++++++++ Tests/Integration/MSFT_SmbShare.config.ps1 | 103 +++++++++ 2 files changed, 301 insertions(+) create mode 100644 Tests/Integration/MSFT_SmbShare.Integration.Tests.ps1 create mode 100644 Tests/Integration/MSFT_SmbShare.config.ps1 diff --git a/Tests/Integration/MSFT_SmbShare.Integration.Tests.ps1 b/Tests/Integration/MSFT_SmbShare.Integration.Tests.ps1 new file mode 100644 index 00000000..c4235ba6 --- /dev/null +++ b/Tests/Integration/MSFT_SmbShare.Integration.Tests.ps1 @@ -0,0 +1,198 @@ +<# + .SYNOPSIS + Integration tests for DSC resource SmbShare. +#> + +$script:dscModuleName = 'ComputerManagementDsc' +$script:dscResourceFriendlyName = 'SmbShare' +$script:dscResourceName = "MSFT_$($script:dscResourceFriendlyName)" + +#region HEADER +# Integration Test Template Version: 1.3.3 +$script:moduleRoot = Join-Path -Path $(Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $Script:MyInvocation.MyCommand.Path))) -ChildPath 'Modules\ComputerManagementDsc' +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 Integration +#endregion + +$script:dscResourceFriendlyName = 'SmbShare' +$script:dcsResourceName = "MSFT_$($script:dscResourceFriendlyName)" + +#region Integration Tests +$configurationFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dcsResourceName).config.ps1" +. $configurationFile + +Describe "$($script:dcsResourceName)_Integration" { + $configurationName = "$($script:dcsResourceName)_Prerequisites_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + } + + $configurationName = "$($script:dcsResourceName)_CreateShare_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.ShareName + $resourceCurrentState.Path | Should -Be $ConfigurationData.AllNodes.SharePath + $resourceCurrentState.Description | Should -BeNullOrEmpty + $resourceCurrentState.EncryptData | Should -BeFalse + $resourceCurrentState.FolderEnumerationMode | Should -Be 'Unrestricted' + $resourceCurrentState.CachingMode | Should -Be 'Manual' + $resourceCurrentState.ContinuouslyAvailable | Should -BeFalse + $resourceCurrentState.ShareState | Should -Be 'Online' + $resourceCurrentState.ShareType | Should -Be 'FileSystemDirectory' + $resourceCurrentState.ShadowCopy | Should -BeFalse + $resourceCurrentState.Special | Should -BeFalse + + # Default these access permissions are empty. + $resourceCurrentState.FullAccess | Should -BeNullOrEmpty + $resourceCurrentState.ChangeAccess | Should -BeNullOrEmpty + $resourceCurrentState.NoAccess | Should -BeNullOrEmpty + + # Default Everyone is added with Read access. + $resourceCurrentState.ReadAccess | Should -HaveCount 1 + $resourceCurrentState.ReadAccess[0] | Should -Be 'Everyone' + + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dcsResourceName)_RemoveShare_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Absent' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.ShareName + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dcsResourceName)_Cleanup_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + } +} +#endregion diff --git a/Tests/Integration/MSFT_SmbShare.config.ps1 b/Tests/Integration/MSFT_SmbShare.config.ps1 new file mode 100644 index 00000000..facd9b0b --- /dev/null +++ b/Tests/Integration/MSFT_SmbShare.config.ps1 @@ -0,0 +1,103 @@ +#region HEADER +# Integration Test Config Template Version: 1.2.0 +#endregion + +$configFile = [System.IO.Path]::ChangeExtension($MyInvocation.MyCommand.Path, 'json') +if (Test-Path -Path $configFile) +{ + <# + Allows reading the configuration data from a JSON file + for real testing scenarios outside of the CI. + #> + $ConfigurationData = Get-Content -Path $configFile | ConvertFrom-Json +} +else +{ + $ConfigurationData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + CertificateFile = $env:DscPublicCertificatePath + + ShareName = 'DscTestShare' + SharePath = 'C:\DscTestShare' + } + ) + } +} + +<# + .SYNOPSIS + Creates the prerequisites for the other tests. + This creates a folder that will be shared. +#> +Configuration MSFT_SmbShare_Prerequisites_Config +{ + Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + + node $AllNodes.NodeName + { + File DirectoryCopy + { + Ensure = 'Present' + Type = 'Directory' + DestinationPath = $Node.SharePath + } + } +} + +<# + .SYNOPSIS + Create the share with default values. +#> +Configuration MSFT_SmbShare_CreateShare_Config +{ + Import-DscResource -ModuleName 'ComputerManagementDsc' + + node $AllNodes.NodeName + { + SmbShare 'Integration_Test' + { + Name = $Node.ShareName + Path = $Node.SharePath + } + } +} + +<# + .SYNOPSIS + Remove the share. +#> +Configuration MSFT_SmbShare_RemoveShare_Config +{ + Import-DscResource -ModuleName 'ComputerManagementDsc' + + node $AllNodes.NodeName + { + SmbShare 'Integration_Test' + { + Ensure = 'Absent' + Name = $Node.ShareName + Path = 'NotUsed_CanBeAnyValue' + } + } +} + +<# + .SYNOPSIS + Clean up the prerequisites. +#> +Configuration MSFT_SmbShare_Cleanup_Config +{ + Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + + node $AllNodes.NodeName + { + File DirectoryCopy + { + Ensure = 'Absent' + Type = 'Directory' + DestinationPath = $Node.SharePath + } + } +} From e1e288a8ffaa28fca57c374d70e8edccb66e2905 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 31 Mar 2019 20:07:11 +0200 Subject: [PATCH 09/26] Fix correct type on read properties --- .../DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 | 8 ++++---- .../MSFT_SmbShare/MSFT_SmbShare.schema.mof | 4 ++-- Tests/Unit/MSFT_SmbShare.Tests.ps1 | 14 ++++++++------ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 index a89f97bc..f17f0650 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 @@ -56,8 +56,8 @@ function Get-TargetResource ContinuouslyAvailable = $false ShareState = $null ShareType = $null - ShadowCopy = $null - Special = $null + ShadowCopy = $false + Special = $false ChangeAccess = @() ReadAccess = @() FullAccess = @() @@ -76,8 +76,8 @@ function Get-TargetResource $returnValue['FolderEnumerationMode'] = $smbShare.FolderEnumerationMode $returnValue['CachingMode'] = $smbShare.CachingMode $returnValue['ContinuouslyAvailable'] = $smbShare.ContinuouslyAvailable - $returnValue['ShareState'] = $smbShare.ShareState - $returnValue['ShareType'] = $smbShare.ShareType + $returnValue['ShareState'] = [System.String] $smbShare.ShareState + $returnValue['ShareType'] = [System.String] $smbShare.ShareType $returnValue['ShadowCopy'] = $smbShare.ShadowCopy $returnValue['Special'] = $smbShare.Special diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.schema.mof b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.schema.mof index 2e22a62c..e3d4decb 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.schema.mof +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.schema.mof @@ -17,8 +17,8 @@ class MSFT_SmbShare : OMI_BaseResource [Write, Description("Specifies if the SMB share should be added or removed."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; [Read, Description("Specifies the state of the SMB share.")] String ShareState; [Read, Description("Specifies the type of the SMB share.")] String ShareType; - [Read, Description("Specifies if this SMB share is a ShadowCopy.")] String ShadowCopy; - [Read, Description("Specifies if this SMB share is a special share. E.g. an admin share, default shares, or IPC$ share.")] String Special; + [Read, Description("Specifies if this SMB share is a ShadowCopy.")] Boolean ShadowCopy; + [Read, Description("Specifies if this SMB share is a special share. E.g. an admin share, default shares, or IPC$ share.")] Boolean Special; }; diff --git a/Tests/Unit/MSFT_SmbShare.Tests.ps1 b/Tests/Unit/MSFT_SmbShare.Tests.ps1 index 1204b8f0..431810d4 100644 --- a/Tests/Unit/MSFT_SmbShare.Tests.ps1 +++ b/Tests/Unit/MSFT_SmbShare.Tests.ps1 @@ -47,8 +47,10 @@ try Add-Member -MemberType NoteProperty -Name 'Description' 'Dummy share for unit testing' -PassThru | Add-Member -MemberType NoteProperty -Name 'ConcurrentUserLimit' -Value 10 -PassThru | Add-Member -MemberType NoteProperty -Name 'EncryptData' -Value $false -PassThru | - Add-Member -MemberType NoteProperty -Name 'FolderEnumerationMode' -Value 'AccessBased' -PassThru | # 0 AccessBased | 1 Unrestricted, but method expects text - Add-Member -MemberType NoteProperty -Name 'SharedState' -Value 1 -PassThru | # 0 Pending | 1 Online | 2 Offline + # 0 AccessBased | 1 Unrestricted + Add-Member -MemberType NoteProperty -Name 'FolderEnumerationMode' -Value 'AccessBased' -PassThru | + # 0 Pending | 1 Online | 2 Offline + Add-Member -MemberType NoteProperty -Name 'SharedState' -Value 'Online' -PassThru | Add-Member -MemberType NoteProperty -Name 'ShadowCopy' -Value $false -PassThru | Add-Member -MemberType NoteProperty -Name 'CachingMode' -Value 'Manual' -PassThru | Add-Member -MemberType NoteProperty -Name 'ContinuouslyAvailable' -Value $true -PassThru | @@ -169,8 +171,8 @@ try $getTargetResourceResult.ContinuouslyAvailable | Should -BeFalse $getTargetResourceResult.ShareState | Should -BeNullOrEmpty $getTargetResourceResult.ShareType | Should -BeNullOrEmpty - $getTargetResourceResult.ShadowCopy | Should -BeNullOrEmpty - $getTargetResourceResult.Special | Should -BeNullOrEmpty + $getTargetResourceResult.ShadowCopy | Should -BeFalse + $getTargetResourceResult.Special | Should -BeFalse $getTargetResourceResult.ChangeAccess | Should -HaveCount 0 $getTargetResourceResult.ReadAccess | Should -HaveCount 0 $getTargetResourceResult.FullAccess | Should -HaveCount 0 @@ -204,8 +206,8 @@ try ContinuouslyAvailable = $false ShareState = $null ShareType = $null - ShadowCopy = $null - Special = $null + ShadowCopy = $false + Special = $false FullAccess = [System.String[]] @() ChangeAccess = [System.String[]] @() ReadAccess = [System.String[]] @() From c73a48c6504f45a83dd2f706c5ed42b9589c16b9 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 31 Mar 2019 21:03:13 +0200 Subject: [PATCH 10/26] Revert change in common module --- .../ComputerManagementDsc.Common.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/ComputerManagementDsc.Common/ComputerManagementDsc.Common.psm1 b/Modules/ComputerManagementDsc.Common/ComputerManagementDsc.Common.psm1 index f77faf48..670faa6f 100644 --- a/Modules/ComputerManagementDsc.Common/ComputerManagementDsc.Common.psm1 +++ b/Modules/ComputerManagementDsc.Common/ComputerManagementDsc.Common.psm1 @@ -188,7 +188,7 @@ function Test-DscParameterState if (-not $checkDesiredValue) { - Write-Verbose -Message ($script:localizedData.MatchEmptyCollectionMessage -f $desiredType.Name, $key, $CurrentValues.$key, $desiredValuesClean.$key) + Write-Verbose -Message ($script:localizedData.MatchValueMessage -f $desiredType.Name, $key, $CurrentValues.$key, $desiredValuesClean.$key) continue } From 9acd3159dffc1ce350fc99aa5c1922f42ec1329f Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 31 Mar 2019 21:14:25 +0200 Subject: [PATCH 11/26] Remove ValuesToCheck to just check desired properties --- .../MSFT_SmbShare/MSFT_SmbShare.psm1 | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 index f17f0650..949a766d 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 @@ -452,21 +452,6 @@ function Test-TargetResource $script:localizedData.EvaluatingProperties ) - $valuesToCheck = @( - 'Name' - 'Path' - 'Description' - 'ConcurrentUserLimit' - 'EncryptData' - 'FolderEnumerationMode' - 'CachingMode' - 'ContinuouslyAvailable' - 'FullAccess' - 'ChangeAccess' - 'ReadAccess' - 'NoAccess' - ) - <# Using $VerbosePreference so that the verbose messages in Test-DscParameterState is outputted, if the user requested @@ -475,7 +460,6 @@ function Test-TargetResource $testTargetResourceResult = Test-DscParameterState ` -CurrentValues $getTargetResourceResult ` -DesiredValues $PSBoundParameters ` - -ValuesToCheck $valuesToCheck ` -Verbose:$VerbosePreference } else From a107bb3a3df8d057377935e851ac871cdc41f376 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Fri, 5 Apr 2019 19:28:58 +0200 Subject: [PATCH 12/26] Add missing code --- .../MSFT_SmbShare/MSFT_SmbShare.psm1 | 122 +++++- .../DSCResources/MSFT_SmbShare/README.md | 27 +- .../en-US/MSFT_SmbShare.strings.psd1 | 1 + .../MSFT_SmbShare.Integration.Tests.ps1 | 254 ++++++++++- Tests/Integration/MSFT_SmbShare.config.ps1 | 200 ++++++++- Tests/Unit/MSFT_SmbShare.Tests.ps1 | 410 ++++++++++++------ 6 files changed, 833 insertions(+), 181 deletions(-) diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 index 949a766d..51f70f8a 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 @@ -58,12 +58,13 @@ function Get-TargetResource ShareType = $null ShadowCopy = $false Special = $false - ChangeAccess = @() - ReadAccess = @() - FullAccess = @() - NoAccess = @() } + $accountsFullAccess = [system.string[]] @() + $accountsChangeAccess = [system.string[]] @() + $accountsReadAccess = [system.string[]] @() + $accountsNoAccess = [system.string[]] @() + $smbShare = Get-SmbShare -Name $Name -ErrorAction 'SilentlyContinue' if ($smbShare) { @@ -90,7 +91,7 @@ function Get-TargetResource { if ($access.AccessControlType -eq 'Allow') { - $returnValue['ChangeAccess'] += @($access.AccountName) + $accountsChangeAccess += @($access.AccountName) } } @@ -98,7 +99,7 @@ function Get-TargetResource { if ($access.AccessControlType -eq 'Allow') { - $returnValue['ReadAccess'] += @($access.AccountName) + $accountsReadAccess += @($access.AccountName) } } @@ -106,12 +107,12 @@ function Get-TargetResource { if ($access.AccessControlType -eq 'Allow') { - $returnValue['FullAccess'] += @($access.AccountName) + $accountsFullAccess += @($access.AccountName) } if ($access.AccessControlType -eq 'Deny') { - $returnValue['NoAccess'] += @($access.AccountName) + $accountsNoAccess += @($access.AccountName) } } } @@ -122,6 +123,15 @@ function Get-TargetResource Write-Verbose -Message ($script:localizedData.ShareNotFound -f $Name) } + <# + This adds either an empty array, or a populated array depending + if accounts with the respectively access was found. + #> + $returnValue['FullAccess'] = [System.String[]] $accountsFullAccess + $returnValue['ChangeAccess'] = [System.String[]] $accountsChangeAccess + $returnValue['ReadAccess'] = [System.String[]] $accountsReadAccess + $returnValue['NoAccess'] = [System.String[]] $accountsNoAccess + return $returnValue } @@ -235,6 +245,8 @@ function Set-TargetResource $Ensure = 'Present' ) + Assert-AccessPermissionParameters @PSBoundParameters + <# Copy the $PSBoundParameters to a new hash table, so we have the original intact. @@ -309,8 +321,9 @@ function Set-TargetResource Write-Verbose -Message ($script:localizedData.CreateShare -f $Name) <# - Remove access collections that are empty, since that is - already the default for the cmdlet New-SmbShare. + Remove access collections that are empty, since empty + collections are not allowed to be provided to the cmdlet + New-SmbShare. #> foreach ($accessProperty in ('ChangeAccess','ReadAccess','FullAccess','NoAccess')) { @@ -436,6 +449,8 @@ function Test-TargetResource $Ensure = 'Present' ) + Assert-AccessPermissionParameters @PSBoundParameters + Write-Verbose -Message ($script:localizedData.TestTargetResourceMessage -f $Name) $testTargetResourceResult = $false @@ -753,3 +768,90 @@ function Add-SmbShareAccessPermission } } } + +<# + .SYNOPSIS + Assert that not only empty collections are passed in the + respectively access permission collections (FullAccess, + ChangeAccess, ReadAccess, and NoAccess). + + .PARAMETER Name + The name of the SMB share to add access permission to. + + .PARAMETER FullAccess + A string collection of account names that should have full access + permission. The accounts in this collection will be added to the + SMB share. + + .PARAMETER ChangeAccess + A string collection of account names that should have change access + permission. The accounts in this collection will be added to the + SMB share. + + .PARAMETER ReadAccess + A string collection of account names that should have read access + permission. The accounts in this collection will be added to the + SMB share. + + .PARAMETER NoAccess + A string collection of account names that should be denied access + to the SMB share. The accounts in this collection will be added to + the SMB share. + + .PARAMETER RemainingParameters + Container for the rest of the potentially splatted parameters from + the $PSBoundParameters object. + + .NOTES + The group 'Everyone' is automatically given read access by + the cmdlet New-SmbShare if all access permission parameters + (FullAccess, ChangeAccess, ReadAccess, NoAccess) is set to @(). + For that reason we are need either none of the parameters, or + at least one to specify an account. + +#> +function Assert-AccessPermissionParameters +{ + param + ( + [Parameter()] + [System.String[]] + $FullAccess, + + [Parameter()] + [System.String[]] + $ChangeAccess, + + [Parameter()] + [System.String[]] + $ReadAccess, + + [Parameter()] + [System.String[]] + $NoAccess, + + [Parameter(ValueFromRemainingArguments)] + [System.Collections.Generic.List`1[System.Object]] + $RemainingParameters + ) + + <# + First check if ReadAccess is monitored (part of the configuration). + If it is not monitored, then we don't need to worry if Everyone is + added. + #> + if ($PSBoundParameters.ContainsKey('ReadAccess') -and -not $ReadAccess) + { + $fullAccessHasNoMembers = $PSBoundParameters.ContainsKey('FullAccess') -and -not $FullAccess + $changeAccessHasNoMembers = $PSBoundParameters.ContainsKey('ChangeAccess') -and -not $ChangeAccess + $noAccessHasNoMembers = $PSBoundParameters.ContainsKey('NoAccess') -and -not $NoAccess + <# + If ReadAccess should have no members, then we need at least one + member in one of the other access permission collections. + #> + if ($fullAccessHasNoMembers -and $changeAccessHasNoMembers -and $noAccessHasNoMembers) + { + New-InvalidArgumentException -Message $script:localizedData.WrongAccessParameters -ArgumentName 'FullAccess, ChangeAccess, ReadAccess, NoAccess' + } + } +} diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/README.md b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/README.md index cce9e1a5..d035b8db 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/README.md +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/README.md @@ -1,4 +1,29 @@ # Description -The resource is used to manage SMB shares and access permissions to +The resource is used to manage SMB shares, and access permissions to SMB shares. + +## Requirements + +It is not allowed to provide empty collections in the configuration for +the access permissions parameters. The below configuration will throw an +error. + +```powershell +SmbShare 'Integration_Test' +{ + Name = 'TestShare' + Path = 'C:\Temp' + FullAccess = @() + ChangeAccess = @() + ReadAccess = @() + NoAccess = @() +} +``` + +The access permission parameters must either be all removed to manage +the access permission manually, or add at least one member to one of +the access permission parameters. If all the access permission parameters +are removed, then by design of the cmdlet `New-SmbShare` it will add +the *Everyone* group with read access permission to the SMB share. +To prevent that, add a member to either access permission parameters. diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 index 488df0a9..099e22b6 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 @@ -14,4 +14,5 @@ ConvertFrom-StringData @' UnblockAccess = Revoking denied permission for account '{0}' on the SMB share with the name '{1}'. GrantAccess = Granting '{0}' permission for account '{1}' on the SMB share with the name '{2}'. DenyAccess = Denying permission for account '{0}' on the SMB share with the name '{1}'. + WrongAccessParameters = Not allowed to have all access permission parameters set to empty collections. Must either remove the access permission parameters completely, or add at least one member to one of the access permission parameters. '@ diff --git a/Tests/Integration/MSFT_SmbShare.Integration.Tests.ps1 b/Tests/Integration/MSFT_SmbShare.Integration.Tests.ps1 index c4235ba6..890a3ee1 100644 --- a/Tests/Integration/MSFT_SmbShare.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_SmbShare.Integration.Tests.ps1 @@ -57,7 +57,7 @@ Describe "$($script:dcsResourceName)_Integration" { } } - $configurationName = "$($script:dcsResourceName)_CreateShare_Config" + $configurationName = "$($script:dcsResourceName)_CreateShare1_Config" Context ('When using configuration {0}' -f $configurationName) { It 'Should compile and apply the MOF without throwing' { @@ -95,27 +95,263 @@ Describe "$($script:dcsResourceName)_Integration" { } $resourceCurrentState.Ensure | Should -Be 'Present' - $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.ShareName - $resourceCurrentState.Path | Should -Be $ConfigurationData.AllNodes.SharePath + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.ShareName1 + $resourceCurrentState.Path | Should -Be $ConfigurationData.AllNodes.SharePath1 $resourceCurrentState.Description | Should -BeNullOrEmpty $resourceCurrentState.EncryptData | Should -BeFalse - $resourceCurrentState.FolderEnumerationMode | Should -Be 'Unrestricted' + $resourceCurrentState.ConcurrentUserLimit | Should -Be 0 + $resourceCurrentState.Description | Should -BeNullOrEmpty $resourceCurrentState.CachingMode | Should -Be 'Manual' $resourceCurrentState.ContinuouslyAvailable | Should -BeFalse $resourceCurrentState.ShareState | Should -Be 'Online' $resourceCurrentState.ShareType | Should -Be 'FileSystemDirectory' $resourceCurrentState.ShadowCopy | Should -BeFalse $resourceCurrentState.Special | Should -BeFalse + $resourceCurrentState.FullAccess | Should -BeNullOrEmpty + $resourceCurrentState.ChangeAccess | Should -BeNullOrEmpty + $resourceCurrentState.NoAccess | Should -BeNullOrEmpty + + <# + By design of the cmdlet `New-SmbShare`, the Everyone group is + always added when not providing any access permission members + in the configuration. + #> + $resourceCurrentState.ReadAccess | Should -HaveCount 1 + $resourceCurrentState.ReadAccess | Should -Contain 'Everyone' + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dcsResourceName)_CreateShare2_Config" - # Default these access permissions are empty. + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.ShareName2 + $resourceCurrentState.Path | Should -Be $ConfigurationData.AllNodes.SharePath2 + $resourceCurrentState.Description | Should -BeNullOrEmpty + $resourceCurrentState.EncryptData | Should -BeFalse + $resourceCurrentState.ConcurrentUserLimit | Should -Be 0 + $resourceCurrentState.Description | Should -BeNullOrEmpty + $resourceCurrentState.CachingMode | Should -Be 'Manual' + $resourceCurrentState.ContinuouslyAvailable | Should -BeFalse + $resourceCurrentState.ShareState | Should -Be 'Online' + $resourceCurrentState.ShareType | Should -Be 'FileSystemDirectory' + $resourceCurrentState.ShadowCopy | Should -BeFalse + $resourceCurrentState.Special | Should -BeFalse + $resourceCurrentState.FullAccess | Should -BeNullOrEmpty + $resourceCurrentState.ReadAccess | Should -BeNullOrEmpty + $resourceCurrentState.NoAccess | Should -BeNullOrEmpty + + <# + By design of the cmdlet `New-SmbShare`, the Everyone group is + always added when using `ReadAccess = @()` in the configuration. + #> + $resourceCurrentState.ChangeAccess | Should -HaveCount 1 + $resourceCurrentState.ChangeAccess | Should -Contain $ConfigurationData.AllNodes.UserName1 + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dcsResourceName)_UpdateProperties_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.ShareName1 + $resourceCurrentState.Path | Should -Be $ConfigurationData.AllNodes.SharePath1 + $resourceCurrentState.Description | Should -Be 'A new description' + #$resourceCurrentState.EncryptData | Should -BeTrue + $resourceCurrentState.ConcurrentUserLimit | Should -Be 20 + #$resourceCurrentState.FolderEnumerationMode | Should -Be 'AccessBased' + #$resourceCurrentState.CachingMode | Should -Be 'None' + #$resourceCurrentState.ContinuouslyAvailable | Should -BeTrue + $resourceCurrentState.ShareState | Should -Be 'Online' + $resourceCurrentState.ShareType | Should -Be 'FileSystemDirectory' + $resourceCurrentState.ShadowCopy | Should -BeFalse + $resourceCurrentState.Special | Should -BeFalse + + $resourceCurrentState.FullAccess | Should -HaveCount 1 + $resourceCurrentState.FullAccess | Should -Contain $ConfigurationData.AllNodes.UserName1 + + $resourceCurrentState.ChangeAccess | Should -HaveCount 1 + $resourceCurrentState.ChangeAccess | Should -Contain $ConfigurationData.AllNodes.UserName2 + + $resourceCurrentState.ReadAccess | Should -HaveCount 1 + $resourceCurrentState.ReadAccess | Should -Contain $ConfigurationData.AllNodes.UserName3 + + $resourceCurrentState.NoAccess | Should -HaveCount 1 + $resourceCurrentState.NoAccess | Should -Contain $ConfigurationData.AllNodes.UserName4 + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dcsResourceName)_RemovePermission_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.ShareName1 $resourceCurrentState.FullAccess | Should -BeNullOrEmpty $resourceCurrentState.ChangeAccess | Should -BeNullOrEmpty $resourceCurrentState.NoAccess | Should -BeNullOrEmpty - # Default Everyone is added with Read access. $resourceCurrentState.ReadAccess | Should -HaveCount 1 - $resourceCurrentState.ReadAccess[0] | Should -Be 'Everyone' + $resourceCurrentState.ReadAccess | Should -Contain 'Everyone' + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dcsResourceName)_RemoveShare1_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $resourceCurrentState.Ensure | Should -Be 'Absent' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.ShareName1 } It 'Should return $true when Test-DscConfiguration is run' { @@ -123,7 +359,7 @@ Describe "$($script:dcsResourceName)_Integration" { } } - $configurationName = "$($script:dcsResourceName)_RemoveShare_Config" + $configurationName = "$($script:dcsResourceName)_RemoveShare2_Config" Context ('When using configuration {0}' -f $configurationName) { It 'Should compile and apply the MOF without throwing' { @@ -161,7 +397,7 @@ Describe "$($script:dcsResourceName)_Integration" { } $resourceCurrentState.Ensure | Should -Be 'Absent' - $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.ShareName + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.ShareName2 } It 'Should return $true when Test-DscConfiguration is run' { diff --git a/Tests/Integration/MSFT_SmbShare.config.ps1 b/Tests/Integration/MSFT_SmbShare.config.ps1 index facd9b0b..b1e84beb 100644 --- a/Tests/Integration/MSFT_SmbShare.config.ps1 +++ b/Tests/Integration/MSFT_SmbShare.config.ps1 @@ -19,8 +19,17 @@ else NodeName = 'localhost' CertificateFile = $env:DscPublicCertificatePath - ShareName = 'DscTestShare' - SharePath = 'C:\DscTestShare' + ShareName1 = 'DscTestShare1' + SharePath1 = 'C:\DscTestShare1' + + ShareName2 = 'DscTestShare2' + SharePath2 = 'C:\DscTestShare2' + + UserName1 = ('{0}\SmbUser1' -f $env:COMPUTERNAME) + UserName2 = ('{0}\SmbUser2' -f $env:COMPUTERNAME) + UserName3 = ('{0}\SmbUser3' -f $env:COMPUTERNAME) + UserName4 = ('{0}\SmbUser4' -f $env:COMPUTERNAME) + Password = 'P@ssw0rd1' } ) } @@ -33,24 +42,79 @@ else #> Configuration MSFT_SmbShare_Prerequisites_Config { - Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + Import-DscResource -ModuleName 'PSDscResources' node $AllNodes.NodeName { - File DirectoryCopy + File 'CreateFolderToShare1' + { + Ensure = 'Present' + Type = 'Directory' + DestinationPath = $Node.SharePath1 + } + + File 'CreateFolderToShare2' { - Ensure = 'Present' - Type = 'Directory' - DestinationPath = $Node.SharePath + Ensure = 'Present' + Type = 'Directory' + DestinationPath = $Node.SharePath2 + } + + User 'CreateAccountUser1' + { + Ensure = 'Present' + UserName = Split-Path -Path $Node.UserName1 -Leaf + Password = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @( + (Split-Path -Path $Node.UserName1 -Leaf), + (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force) + ) + } + + User 'CreateAccountUser2' + { + Ensure = 'Present' + UserName = Split-Path -Path $Node.UserName2 -Leaf + Password = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @( + (Split-Path -Path $Node.UserName2 -Leaf), + (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force) + ) + } + + User 'CreateAccountUser3' + { + Ensure = 'Present' + UserName = Split-Path -Path $Node.UserName3 -Leaf + Password = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @( + (Split-Path -Path $Node.UserName3 -Leaf), + (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force) + ) + } + + User 'CreateAccountUser4' + { + Ensure = 'Present' + UserName = Split-Path -Path $Node.UserName4 -Leaf + Password = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @( + (Split-Path -Path $Node.UserName1 -Leaf), + (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force) + ) } } } <# .SYNOPSIS - Create the share with default values. + Create the SMB share with default values, and no permissions. #> -Configuration MSFT_SmbShare_CreateShare_Config +Configuration MSFT_SmbShare_CreateShare1_Config { Import-DscResource -ModuleName 'ComputerManagementDsc' @@ -58,17 +122,91 @@ Configuration MSFT_SmbShare_CreateShare_Config { SmbShare 'Integration_Test' { - Name = $Node.ShareName - Path = $Node.SharePath + Name = $Node.ShareName1 + Path = $Node.SharePath1 } } } <# .SYNOPSIS - Remove the share. + Create the SMB share with default values, and no permissions. #> -Configuration MSFT_SmbShare_RemoveShare_Config +Configuration MSFT_SmbShare_CreateShare2_Config +{ + Import-DscResource -ModuleName 'ComputerManagementDsc' + + node $AllNodes.NodeName + { + SmbShare 'Integration_Test' + { + Name = $Node.ShareName2 + Path = $Node.SharePath2 + FullAccess = @() + ChangeAccess = @($Node.UserName1) + ReadAccess = @() + NoAccess = @() + } + } +} + +<# + .SYNOPSIS + Update all properties of the SMB share. +#> +Configuration MSFT_SmbShare_UpdateProperties_Config +{ + Import-DscResource -ModuleName 'ComputerManagementDsc' + + node $AllNodes.NodeName + { + SmbShare 'Integration_Test' + { + Name = $Node.ShareName1 + Path = $Node.SharePath1 + FolderEnumerationMode = 'AccessBased' + CachingMode = 'None' + ConcurrentUserLimit = 20 + #TODO: https://blog.workinghardinit.work/tag/continuously-available-file-shares/ + #ContinuouslyAvailable = $true + Description = 'A new description' + EncryptData = $true + FullAccess = @($Node.UserName1) + ChangeAccess = @($Node.UserName2) + ReadAccess = @($Node.UserName3) + NoAccess = @($Node.UserName4) + } + } +} + +<# + .SYNOPSIS + Remove permission, and no other properties should be changed. +#> +Configuration MSFT_SmbShare_RemovePermission_Config +{ + Import-DscResource -ModuleName 'ComputerManagementDsc' + + node $AllNodes.NodeName + { + SmbShare 'Integration_Test' + { + Name = $Node.ShareName1 + Path = $Node.SharePath1 + FullAccess = @() + ChangeAccess = @() + ReadAccess = @('Everyone') + NoAccess = @() + } + } +} + + +<# + .SYNOPSIS + Remove the share 1. +#> +Configuration MSFT_SmbShare_RemoveShare1_Config { Import-DscResource -ModuleName 'ComputerManagementDsc' @@ -77,7 +215,26 @@ Configuration MSFT_SmbShare_RemoveShare_Config SmbShare 'Integration_Test' { Ensure = 'Absent' - Name = $Node.ShareName + Name = $Node.ShareName1 + Path = 'NotUsed_CanBeAnyValue' + } + } +} + +<# + .SYNOPSIS + Remove the share 2. +#> +Configuration MSFT_SmbShare_RemoveShare2_Config +{ + Import-DscResource -ModuleName 'ComputerManagementDsc' + + node $AllNodes.NodeName + { + SmbShare 'Integration_Test' + { + Ensure = 'Absent' + Name = $Node.ShareName2 Path = 'NotUsed_CanBeAnyValue' } } @@ -93,11 +250,18 @@ Configuration MSFT_SmbShare_Cleanup_Config node $AllNodes.NodeName { - File DirectoryCopy + File 'RemoveShareFolder1' { - Ensure = 'Absent' - Type = 'Directory' - DestinationPath = $Node.SharePath + Ensure = 'Absent' + Type = 'Directory' + DestinationPath = $Node.SharePath1 + } + + File 'RemoveShareFolder2s' + { + Ensure = 'Absent' + Type = 'Directory' + DestinationPath = $Node.SharePath2 } } } diff --git a/Tests/Unit/MSFT_SmbShare.Tests.ps1 b/Tests/Unit/MSFT_SmbShare.Tests.ps1 index 431810d4..57f15e7c 100644 --- a/Tests/Unit/MSFT_SmbShare.Tests.ps1 +++ b/Tests/Unit/MSFT_SmbShare.Tests.ps1 @@ -42,61 +42,61 @@ try $mockSmbShare = ( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | - Add-Member -MemberType NoteProperty -Name 'Path' -Value 'c:\temp' -PassThru | - Add-Member -MemberType NoteProperty -Name 'Description' 'Dummy share for unit testing' -PassThru | - Add-Member -MemberType NoteProperty -Name 'ConcurrentUserLimit' -Value 10 -PassThru | - Add-Member -MemberType NoteProperty -Name 'EncryptData' -Value $false -PassThru | - # 0 AccessBased | 1 Unrestricted - Add-Member -MemberType NoteProperty -Name 'FolderEnumerationMode' -Value 'AccessBased' -PassThru | - # 0 Pending | 1 Online | 2 Offline - Add-Member -MemberType NoteProperty -Name 'SharedState' -Value 'Online' -PassThru | - Add-Member -MemberType NoteProperty -Name 'ShadowCopy' -Value $false -PassThru | - Add-Member -MemberType NoteProperty -Name 'CachingMode' -Value 'Manual' -PassThru | - Add-Member -MemberType NoteProperty -Name 'ContinuouslyAvailable' -Value $true -PassThru | - Add-Member -MemberType NoteProperty -Name 'Special' -Value $false -PassThru -Force + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | + Add-Member -MemberType NoteProperty -Name 'Path' -Value 'c:\temp' -PassThru | + Add-Member -MemberType NoteProperty -Name 'Description' 'Dummy share for unit testing' -PassThru | + Add-Member -MemberType NoteProperty -Name 'ConcurrentUserLimit' -Value 10 -PassThru | + Add-Member -MemberType NoteProperty -Name 'EncryptData' -Value $false -PassThru | + # 0 AccessBased | 1 Unrestricted + Add-Member -MemberType NoteProperty -Name 'FolderEnumerationMode' -Value 'AccessBased' -PassThru | + # 0 Pending | 1 Online | 2 Offline + Add-Member -MemberType NoteProperty -Name 'SharedState' -Value 'Online' -PassThru | + Add-Member -MemberType NoteProperty -Name 'ShadowCopy' -Value $false -PassThru | + Add-Member -MemberType NoteProperty -Name 'CachingMode' -Value 'Manual' -PassThru | + Add-Member -MemberType NoteProperty -Name 'ContinuouslyAvailable' -Value $true -PassThru | + Add-Member -MemberType NoteProperty -Name 'Special' -Value $false -PassThru -Force ) $mockSmbShareAccess = @( ( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | - Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockFullPermissionUserName[0] -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Allow' -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Full' -PassThru -Force + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | + Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockFullPermissionUserName[0] -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Allow' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Full' -PassThru -Force ), ( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | - Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockFullPermissionUserName[1] -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Allow' -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Full' -PassThru -Force + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | + Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockFullPermissionUserName[1] -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Allow' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Full' -PassThru -Force ), ( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | - Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockChangePermissionUserName[0] -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Allow' -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Change' -PassThru -Force + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | + Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockChangePermissionUserName[0] -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Allow' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Change' -PassThru -Force ), ( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | - Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockReadPermissionUserName[0] -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Allow' -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Read' -PassThru -Force + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | + Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockReadPermissionUserName[0] -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Allow' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Read' -PassThru -Force ), ( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | - Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockNoPermissionUserName[0] -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Deny' -PassThru | - Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Full' -PassThru -Force + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockShareName -PassThru | + Add-Member -MemberType NoteProperty -Name 'ScopName' -Value '*' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccountName' -Value $mockNoPermissionUserName[0] -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessControlType' -Value 'Deny' -PassThru | + Add-Member -MemberType NoteProperty -Name 'AccessRight' -Value 'Full' -PassThru -Force ) ) @@ -160,23 +160,23 @@ try It 'Should return the correct values' { $getTargetResourceResult = Get-TargetResource @testParameters - $getTargetResourceResult.Ensure | Should -Be 'Absent' - $getTargetResourceResult.Name | Should -Be $testParameters.Name - $getTargetResourceResult.Path | Should -BeNullOrEmpty - $getTargetResourceResult.Description | Should -BeNullOrEmpty - $getTargetResourceResult.ConcurrentUserLimit | Should -Be 0 - $getTargetResourceResult.EncryptData | Should -BeFalse + $getTargetResourceResult.Ensure | Should -Be 'Absent' + $getTargetResourceResult.Name | Should -Be $testParameters.Name + $getTargetResourceResult.Path | Should -BeNullOrEmpty + $getTargetResourceResult.Description | Should -BeNullOrEmpty + $getTargetResourceResult.ConcurrentUserLimit | Should -Be 0 + $getTargetResourceResult.EncryptData | Should -BeFalse $getTargetResourceResult.FolderEnumerationMode | Should -BeNullOrEmpty - $getTargetResourceResult.CachingMode | Should -BeNullOrEmpty + $getTargetResourceResult.CachingMode | Should -BeNullOrEmpty $getTargetResourceResult.ContinuouslyAvailable | Should -BeFalse - $getTargetResourceResult.ShareState | Should -BeNullOrEmpty - $getTargetResourceResult.ShareType | Should -BeNullOrEmpty - $getTargetResourceResult.ShadowCopy | Should -BeFalse - $getTargetResourceResult.Special | Should -BeFalse - $getTargetResourceResult.ChangeAccess | Should -HaveCount 0 - $getTargetResourceResult.ReadAccess | Should -HaveCount 0 - $getTargetResourceResult.FullAccess | Should -HaveCount 0 - $getTargetResourceResult.NoAccess | Should -HaveCount 0 + $getTargetResourceResult.ShareState | Should -BeNullOrEmpty + $getTargetResourceResult.ShareType | Should -BeNullOrEmpty + $getTargetResourceResult.ShadowCopy | Should -BeFalse + $getTargetResourceResult.Special | Should -BeFalse + $getTargetResourceResult.ChangeAccess | Should -HaveCount 0 + $getTargetResourceResult.ReadAccess | Should -HaveCount 0 + $getTargetResourceResult.FullAccess | Should -HaveCount 0 + $getTargetResourceResult.NoAccess | Should -HaveCount 0 } } @@ -217,29 +217,53 @@ try } } - It 'Should call the correct mocks' { - $setTargetResourceParameters = @{ - Name = $mockShareName - Path = 'TestDrive:\Temp' - Description = 'Some description' - ConcurrentUserLimit = 2 - EncryptData = $false - FolderEnumerationMode = 'AccessBased' - CachingMode = 'Manual' - ContinuouslyAvailable = $true - ChangeAccess = $mockChangePermissionUserName - ReadAccess = $mockReadPermissionUserName - FullAccess = $mockFullPermissionUserName - # Testing removal of empty collections - NoAccess = @() - Verbose = $true + Context 'When no access permission is given' { + It 'Should call the correct mocks' { + $setTargetResourceParameters = @{ + Name = $mockShareName + Path = 'TestDrive:\Temp' + Description = 'Some description' + ConcurrentUserLimit = 2 + EncryptData = $false + FolderEnumerationMode = 'AccessBased' + CachingMode = 'Manual' + ContinuouslyAvailable = $true + ChangeAccess = @() + ReadAccess = @() + FullAccess = @() + NoAccess = @() + Verbose = $true + } + + { Set-TargetResource @setTargetResourceParameters } | Should -Throw $script:localizedData.WrongAccessParameters } + } - { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + Context 'When access permissions are given' { + It 'Should call the correct mocks' { + $setTargetResourceParameters = @{ + Name = $mockShareName + Path = 'TestDrive:\Temp' + Description = 'Some description' + ConcurrentUserLimit = 2 + EncryptData = $false + FolderEnumerationMode = 'AccessBased' + CachingMode = 'Manual' + ContinuouslyAvailable = $true + ChangeAccess = $mockChangePermissionUserName + ReadAccess = $mockReadPermissionUserName + FullAccess = $mockFullPermissionUserName + NoAccess = $mockNoPermissionUserName + Verbose = $true + } - Assert-MockCalled New-SmbShare -Exactly -Times 1 -Scope It - Assert-MockCalled Set-SmbShare -Exactly -Times 0 -Scope It - Assert-MockCalled Remove-SmbShare -Exactly -Times 0 -Scope It + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled New-SmbShare -Exactly -Times 1 -Scope It + Assert-MockCalled Set-SmbShare -Exactly -Times 0 -Scope It + Assert-MockCalled Remove-SmbShare -Exactly -Times 0 -Scope It + Assert-MockCalled Remove-SmbShareAccessPermission -Exactly -Times 0 -Scope It + } } } @@ -247,17 +271,17 @@ try BeforeAll { Mock -CommandName Get-TargetResource -MockWith { return @{ - Name = $mockShareName - Ensure = 'Present' + Name = $mockShareName + Ensure = 'Present' } } } It 'Should call the correct mocks' { $setTargetResourceParameters = @{ - Name = $mockShareName - Path = 'AnyValue' - Ensure = 'Absent' + Name = $mockShareName + Path = 'AnyValue' + Ensure = 'Absent' Verbose = $true } @@ -297,19 +321,19 @@ try It 'Should call the correct mocks' { $setTargetResourceParameters = @{ - Name = $mockShareName - Path = 'TestDrive:\Temp' - Description = 'Some description' - ConcurrentUserLimit = 2 - EncryptData = $false + Name = $mockShareName + Path = 'TestDrive:\Temp' + Description = 'Some description' + ConcurrentUserLimit = 2 + EncryptData = $false FolderEnumerationMode = 'AccessBased' - CachingMode = 'Manual' + CachingMode = 'Manual' ContinuouslyAvailable = $true - ChangeAccess = $mockChangePermissionUserName - ReadAccess = $mockReadPermissionUserName - FullAccess = $mockFullPermissionUserName - NoAccess = $mockNoPermissionUserName - Verbose = $true + ChangeAccess = $mockChangePermissionUserName + ReadAccess = $mockReadPermissionUserName + FullAccess = $mockFullPermissionUserName + NoAccess = $mockNoPermissionUserName + Verbose = $true } { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw @@ -327,6 +351,27 @@ try Describe 'MSFT_SmbShare\Test-TargetResource' -Tag 'Test' { Context 'When the system is not in the desired state' { + Context 'When no members in provided in neither access permission collection' { + BeforeAll { + $testTargetResourceParameters = @{ + Name = $mockShareName + Path = 'TestDrive:\Temp' + FullAccess = @() + ChangeAccess = @() + ReadAccess = @() + NoAccess = @() + Ensure = 'Present' + Verbose = $true + } + } + + It 'Should throw the correct error' { + { + Test-TargetResource @testTargetResourceParameters + } | Should -Throw $script:localizedData.WrongAccessParameters + } + } + Context 'When there is a configured SMB share' { BeforeAll { $mockDefaultTestCaseValues = @{ @@ -341,7 +386,7 @@ try ContinuouslyAvailable = $mockSmbShare.ContinuouslyAvailable FullAccess = @() ChangeAccess = @() - ReadAccess = @() + ReadAccess = @($mockReadPermissionUserName) NoAccess = @() Ensure = 'Present' } @@ -490,7 +535,7 @@ try ContinuouslyAvailable = $mockSmbShare.ContinuouslyAvailable FullAccess = @() ChangeAccess = @() - ReadAccess = @() + ReadAccess = @($mockReadPermissionUserName) NoAccess = @() Verbose = $true } @@ -542,7 +587,7 @@ try Special = $mockSmbShare.Special FullAccess = [System.String[]] @() ChangeAccess = [System.String[]] @() - ReadAccess = [System.String[]] @() + ReadAccess = [System.String[]] @($mockReadPermissionUserName) NoAccess = [System.String[]] @() Ensure = 'Present' } @@ -562,7 +607,7 @@ try ContinuouslyAvailable = $mockSmbShare.ContinuouslyAvailable FullAccess = @() ChangeAccess = @() - ReadAccess = @() + ReadAccess = @($mockReadPermissionUserName) NoAccess = @() Verbose = $true } @@ -626,10 +671,10 @@ try Context 'When an account with full access should be added' { BeforeAll { $addSmbShareAccessPermissionParameters = @{ - Name = $mockShareName + Name = $mockShareName # User3 is an already present account. It should not be added. FullAccess = @('User3', $mockExpectedAccountToBeAdded) - Verbose = $true + Verbose = $true } } @@ -643,8 +688,8 @@ try Assert-MockCalled -CommandName Grant-SmbShareAccess -Exactly -Times 1 -Scope 'It' Assert-MockCalled -CommandName Grant-SmbShareAccess -ParameterFilter { $Name -eq $mockShareName ` - -and $AccessRight -eq 'Full' ` - -and $AccountName -eq $mockExpectedAccountToBeAdded + -and $AccessRight -eq 'Full' ` + -and $AccountName -eq $mockExpectedAccountToBeAdded } -Exactly -Times 1 -Scope 'It' } } @@ -652,10 +697,10 @@ try Context 'When an account with change access should be added' { BeforeAll { $addSmbShareAccessPermissionParameters = @{ - Name = $mockShareName + Name = $mockShareName # User1 is an already present account. It should not be added. ChangeAccess = @('User1', $mockExpectedAccountToBeAdded) - Verbose = $true + Verbose = $true } } @@ -669,8 +714,8 @@ try Assert-MockCalled -CommandName Grant-SmbShareAccess -Exactly -Times 1 -Scope 'It' Assert-MockCalled -CommandName Grant-SmbShareAccess -ParameterFilter { $Name -eq $mockShareName ` - -and $AccessRight -eq 'Change' ` - -and $AccountName -eq $mockExpectedAccountToBeAdded + -and $AccessRight -eq 'Change' ` + -and $AccountName -eq $mockExpectedAccountToBeAdded } -Exactly -Times 1 -Scope 'It' } } @@ -678,10 +723,10 @@ try Context 'When an account with read access should be added' { BeforeAll { $addSmbShareAccessPermissionParameters = @{ - Name = $mockShareName + Name = $mockShareName # User2 is an already present account. It should not be added. ReadAccess = @('User2', $mockExpectedAccountToBeAdded) - Verbose = $true + Verbose = $true } } @@ -695,8 +740,8 @@ try Assert-MockCalled -CommandName Grant-SmbShareAccess -Exactly -Times 1 -Scope 'It' Assert-MockCalled -CommandName Grant-SmbShareAccess -ParameterFilter { $Name -eq $mockShareName ` - -and $AccessRight -eq 'Read' ` - -and $AccountName -eq $mockExpectedAccountToBeAdded + -and $AccessRight -eq 'Read' ` + -and $AccountName -eq $mockExpectedAccountToBeAdded } -Exactly -Times 1 -Scope 'It' } } @@ -708,12 +753,12 @@ try $mockExpectedAccountToBeAdded3 = 'NweUser3' $addSmbShareAccessPermissionParameters = @{ - Name = $mockShareName + Name = $mockShareName # User1, User2, and User3 is an already present account. It should not be added. - FullAccess = @('User3', $mockExpectedAccountToBeAdded1) - ReadAccess = @('User2', $mockExpectedAccountToBeAdded2) + FullAccess = @('User3', $mockExpectedAccountToBeAdded1) + ReadAccess = @('User2', $mockExpectedAccountToBeAdded2) ChangeAccess = @('User1', $mockExpectedAccountToBeAdded3) - Verbose = $true + Verbose = $true } } @@ -727,20 +772,20 @@ try Assert-MockCalled -CommandName Grant-SmbShareAccess -Exactly -Times 3 -Scope 'It' Assert-MockCalled -CommandName Grant-SmbShareAccess -ParameterFilter { $Name -eq $mockShareName ` - -and $AccessRight -eq 'Change' ` - -and $AccountName -eq $mockExpectedAccountToBeAdded3 + -and $AccessRight -eq 'Change' ` + -and $AccountName -eq $mockExpectedAccountToBeAdded3 } -Exactly -Times 1 -Scope 'It' Assert-MockCalled -CommandName Grant-SmbShareAccess -ParameterFilter { $Name -eq $mockShareName ` - -and $AccessRight -eq 'Full' ` - -and $AccountName -eq $mockExpectedAccountToBeAdded1 + -and $AccessRight -eq 'Full' ` + -and $AccountName -eq $mockExpectedAccountToBeAdded1 } -Exactly -Times 1 -Scope 'It' Assert-MockCalled -CommandName Grant-SmbShareAccess -ParameterFilter { $Name -eq $mockShareName ` - -and $AccessRight -eq 'Read' ` - -and $AccountName -eq $mockExpectedAccountToBeAdded2 + -and $AccessRight -eq 'Read' ` + -and $AccountName -eq $mockExpectedAccountToBeAdded2 } -Exactly -Times 1 -Scope 'It' } } @@ -756,9 +801,9 @@ try $mockExpectedAccountToBeBlocked = 'NewDeniedUser' $removeSmbShareAccessPermissionParameters = @{ - Name = $mockShareName + Name = $mockShareName NoAccess = @('DeniedUser1', $mockExpectedAccountToBeBlocked) - Verbose = $true + Verbose = $true } } @@ -772,7 +817,7 @@ try Assert-MockCalled -CommandName Block-SmbShareAccess -Exactly -Times 1 -Scope 'It' Assert-MockCalled -CommandName Block-SmbShareAccess -ParameterFilter { $Name -eq $mockShareName ` - -and $AccountName -eq $mockExpectedAccountToBeBlocked + -and $AccountName -eq $mockExpectedAccountToBeBlocked } -Exactly -Times 1 -Scope 'It' } } @@ -807,9 +852,9 @@ try $mockExpectedAccountToBeRemoved = 'User4' $removeSmbShareAccessPermissionParameters = @{ - Name = $mockShareName + Name = $mockShareName FullAccess = @('User3') - Verbose = $true + Verbose = $true } } @@ -823,7 +868,7 @@ try Assert-MockCalled -CommandName Revoke-SmbShareAccess -Exactly -Times 1 -Scope 'It' Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { $Name -eq $mockShareName ` - -and $AccountName -eq $mockExpectedAccountToBeRemoved + -and $AccountName -eq $mockExpectedAccountToBeRemoved } -Exactly -Times 1 -Scope 'It' } } @@ -834,14 +879,14 @@ try $mockExpectedAccountToBeRemoved2 = 'User4' $removeSmbShareAccessPermissionParameters = @{ - Name = $mockShareName + Name = $mockShareName FullAccess = @() - Verbose = $true + Verbose = $true } } It 'Should call the correct mocks' { - { Remove-SmbShareAccessPermission @removeSmbShareAccessPermissionParameters } | Should -Not -Throw + { Remove-SmbShareAccessPermission @removeSmbShareAccessPermissionParameters } | Should -Not -Throw <# Assert that Revoke-SmbShareAccess is called twice, and @@ -850,12 +895,12 @@ try Assert-MockCalled -CommandName Revoke-SmbShareAccess -Exactly -Times 2 -Scope 'It' Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { $Name -eq $mockShareName ` - -and $AccountName -eq $mockExpectedAccountToBeRemoved1 + -and $AccountName -eq $mockExpectedAccountToBeRemoved1 } -Exactly -Times 1 -Scope 'It' Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { $Name -eq $mockShareName ` - -and $AccountName -eq $mockExpectedAccountToBeRemoved2 + -and $AccountName -eq $mockExpectedAccountToBeRemoved2 } -Exactly -Times 1 -Scope 'It' } } @@ -865,9 +910,9 @@ try $mockExpectedAccountToBeRemoved = 'User1' $removeSmbShareAccessPermissionParameters = @{ - Name = $mockShareName + Name = $mockShareName ChangeAccess = @() - Verbose = $true + Verbose = $true } } @@ -881,7 +926,7 @@ try Assert-MockCalled -CommandName Revoke-SmbShareAccess -Exactly -Times 1 -Scope 'It' Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { $Name -eq $mockShareName ` - -and $AccountName -eq $mockExpectedAccountToBeRemoved + -and $AccountName -eq $mockExpectedAccountToBeRemoved } -Exactly -Times 1 -Scope 'It' } } @@ -891,9 +936,9 @@ try $mockExpectedAccountToBeRemoved = 'User2' $removeSmbShareAccessPermissionParameters = @{ - Name = $mockShareName + Name = $mockShareName ReadAccess = @() - Verbose = $true + Verbose = $true } } @@ -907,7 +952,7 @@ try Assert-MockCalled -CommandName Revoke-SmbShareAccess -Exactly -Times 1 -Scope 'It' Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { $Name -eq $mockShareName ` - -and $AccountName -eq $mockExpectedAccountToBeRemoved + -and $AccountName -eq $mockExpectedAccountToBeRemoved } -Exactly -Times 1 -Scope 'It' @@ -917,11 +962,11 @@ try Context 'When an all granted access should be removed' { BeforeAll { $removeSmbShareAccessPermissionParameters = @{ - Name = $mockShareName - FullAccess = @() + Name = $mockShareName + FullAccess = @() ChangeAccess = @() - ReadAccess = @() - Verbose = $true + ReadAccess = @() + Verbose = $true } } @@ -931,22 +976,22 @@ try Assert-MockCalled -CommandName Revoke-SmbShareAccess -Exactly -Times 4 -Scope 'It' Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { $Name -eq $mockShareName ` - -and $AccountName -eq 'User1' + -and $AccountName -eq 'User1' } -Exactly -Times 1 -Scope 'It' Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { $Name -eq $mockShareName ` - -and $AccountName -eq 'User2' + -and $AccountName -eq 'User2' } -Exactly -Times 1 -Scope 'It' Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { $Name -eq $mockShareName ` - -and $AccountName -eq 'User3' + -and $AccountName -eq 'User3' } -Exactly -Times 1 -Scope 'It' Assert-MockCalled -CommandName Revoke-SmbShareAccess -ParameterFilter { $Name -eq $mockShareName ` - -and $AccountName -eq 'User4' + -and $AccountName -eq 'User4' } -Exactly -Times 1 -Scope 'It' } } @@ -962,9 +1007,9 @@ try $mockExpectedAccountToBeUnblocked = 'DeniedUser1' $removeSmbShareAccessPermissionParameters = @{ - Name = $mockShareName + Name = $mockShareName NoAccess = @() - Verbose = $true + Verbose = $true } } @@ -978,12 +1023,91 @@ try Assert-MockCalled -CommandName Unblock-SmbShareAccess -Exactly -Times 1 -Scope 'It' Assert-MockCalled -CommandName Unblock-SmbShareAccess -ParameterFilter { $Name -eq $mockShareName ` - -and $AccountName -eq $mockExpectedAccountToBeUnblocked + -and $AccountName -eq $mockExpectedAccountToBeUnblocked } -Exactly -Times 1 -Scope 'It' } } } } + + Describe 'MSFT_SmbShare\Assert-AccessPermissionParameters' -Tag 'Helper' { + Context 'When asserting correct provided access permissions parameters' { + Context 'When providing at least one member in one of the access permission collections' { + BeforeAll { + $testCases = @( + @{ + TestCase = 'FullAccess' + FullAccess = @('Member1') + ChangeAccess = @() + ReadAccess = @() + NoAccess = @() + }, + @{ + TestCase = 'ChangeAccess' + FullAccess = @() + ChangeAccess = @('Member1') + ReadAccess = @() + NoAccess = @() + }, + @{ + TestCase = 'ReadAccess' + FullAccess = @() + ChangeAccess = @() + ReadAccess = @('Member1') + NoAccess = @() + }, + @{ + TestCase = 'NoAccess' + FullAccess = @() + ChangeAccess = @() + ReadAccess = @('Member1') + NoAccess = @() + } + ) + } + + It 'Should not throw an error when testing a member in ' -TestCases $testCases { + param + ( + $FullAccess, + $ChangeAccess, + $ReadAccess, + $NoAccess + ) + + # We must using splatting to test 'ValueFromRemainingArguments' parameter. + $assertAccessPermissionParameters = @{ + FullAccess = $FullAccess + ChangeAccess = $ChangeAccess + ReadAccess = $ReadAccess + NoAccess = $NoAccess + DummyParameter = 'Testing ValueFromRemainingArguments' + } + + { + Assert-AccessPermissionParameters @assertAccessPermissionParameters + } | Should -Not -Throw + } + } + + Context 'When providing no member in either of the access permission collections' { + It 'Should throw the correct error' { + # We must using splatting to test 'ValueFromRemainingArguments' parameter. + $assertAccessPermissionParameters = @{ + FullAccess = @() + ChangeAccess = @() + ReadAccess = @() + NoAccess = @() + DummyParameter = 'Testing ValueFromRemainingArguments' + } + + { + Assert-AccessPermissionParameters @assertAccessPermissionParameters + } | Should -Throw $script:localizedData.WrongAccessParameters + } + } + } + } } } finally From fe58508bfd3ade54333eefac9107f170256cd37b Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 13 Apr 2019 10:17:12 +0200 Subject: [PATCH 13/26] Fix return types in Get-TargetResource --- .../MSFT_SmbShare/MSFT_SmbShare.psm1 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 index 51f70f8a..beb5ea47 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 @@ -47,15 +47,15 @@ function Get-TargetResource $returnValue = @{ Ensure = 'Absent' Name = $Name - Path = $null - Description = $null + Path = [System.String] $null + Description = [System.String] $null ConcurrentUserLimit = 0 EncryptData = $false - FolderEnumerationMode = $null - CachingMode = $null + FolderEnumerationMode = [System.String] $null + CachingMode = [System.String] $null ContinuouslyAvailable = $false - ShareState = $null - ShareType = $null + ShareState = [System.String] $null + ShareType = [System.String] $null ShadowCopy = $false Special = $false } @@ -74,8 +74,8 @@ function Get-TargetResource $returnValue['Description'] = $smbShare.Description $returnValue['ConcurrentUserLimit'] = $smbShare.ConcurrentUserLimit $returnValue['EncryptData'] = $smbShare.EncryptData - $returnValue['FolderEnumerationMode'] = $smbShare.FolderEnumerationMode - $returnValue['CachingMode'] = $smbShare.CachingMode + $returnValue['FolderEnumerationMode'] = $smbShare.FolderEnumerationMode.ToString() + $returnValue['CachingMode'] = $smbShare.CachingMode.ToString() $returnValue['ContinuouslyAvailable'] = $smbShare.ContinuouslyAvailable $returnValue['ShareState'] = [System.String] $smbShare.ShareState $returnValue['ShareType'] = [System.String] $smbShare.ShareType From 89f53cafc04594d1655b9c5fd7727722bc1cac3b Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 13 Apr 2019 12:08:17 +0200 Subject: [PATCH 14/26] Add note about the property ContinuouslyAvailable --- Tests/Integration/MSFT_SmbShare.config.ps1 | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Tests/Integration/MSFT_SmbShare.config.ps1 b/Tests/Integration/MSFT_SmbShare.config.ps1 index b1e84beb..0d3edae8 100644 --- a/Tests/Integration/MSFT_SmbShare.config.ps1 +++ b/Tests/Integration/MSFT_SmbShare.config.ps1 @@ -153,6 +153,18 @@ Configuration MSFT_SmbShare_CreateShare2_Config <# .SYNOPSIS Update all properties of the SMB share. + + .NOTES + The property ContinuouslyAvailable cannot be set to $true because that + property requires the share to be a cluster share in a Failover Cluster. + + Log Name: Microsoft-Windows-SMBServer/Operational + Event ID: 1800 + Level: Error + Description: + CA failure - Failed to set continuously available property on a new or + existing file share as the file share is not a cluster share. + #> Configuration MSFT_SmbShare_UpdateProperties_Config { @@ -167,8 +179,7 @@ Configuration MSFT_SmbShare_UpdateProperties_Config FolderEnumerationMode = 'AccessBased' CachingMode = 'None' ConcurrentUserLimit = 20 - #TODO: https://blog.workinghardinit.work/tag/continuously-available-file-shares/ - #ContinuouslyAvailable = $true + ContinuouslyAvailable = $false Description = 'A new description' EncryptData = $true FullAccess = @($Node.UserName1) From a3cc36c7f762add08bef83a3fcf32aab3eb85f9b Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 13 Apr 2019 12:36:46 +0200 Subject: [PATCH 15/26] Update README.md --- .../DSCResources/MSFT_SmbShare/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/README.md b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/README.md index d035b8db..ba61a96b 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/README.md +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/README.md @@ -5,6 +5,17 @@ SMB shares. ## Requirements +### Cluster Shares + +To property `ContinuouslyAvailable` can only be set to `$true` when +the SMB share is a cluster share in a failover cluster. Also in the blog +[SMB Transparent Failover – making file shares continuously available](https://blogs.technet.microsoft.com/filecab/2016/03/25/smb-transparent-failover-making-file-shares-continuously-available-2) +by [Claus Joergensen](https://github.com/clausjor) it is mentioned that +SMB Transparent Failover does not support cluster disks with 8.3 name +generation enabled. + +### Access permissions + It is not allowed to provide empty collections in the configuration for the access permissions parameters. The below configuration will throw an error. From 6fc6e7081a2d4aeb408e305f7f0ab8a04f9ff6de Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 14 Apr 2019 09:03:31 +0200 Subject: [PATCH 16/26] Move files after project model refactor --- .../MSFT_SmbShare/MSFT_SmbShare.psm1 | 0 .../MSFT_SmbShare/MSFT_SmbShare.schema.mof | 0 .../DSCResources => DSCResources}/MSFT_SmbShare/README.md | 0 .../MSFT_SmbShare/en-US/MSFT_SmbShare.schema.mfl | 0 .../MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 | 0 .../Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 | 0 .../2-SmbShare_CreateShareAllProperties_Config.ps1 | 0 .../Resources/SmbShare/3-SmbShare_RemoveShare_Config.ps1 | 0 Tests/Unit/MSFT_SmbShare.Tests.ps1 | 7 ++++--- 9 files changed, 4 insertions(+), 3 deletions(-) rename {Modules/ComputerManagementDsc/DSCResources => DSCResources}/MSFT_SmbShare/MSFT_SmbShare.psm1 (100%) rename {Modules/ComputerManagementDsc/DSCResources => DSCResources}/MSFT_SmbShare/MSFT_SmbShare.schema.mof (100%) rename {Modules/ComputerManagementDsc/DSCResources => DSCResources}/MSFT_SmbShare/README.md (100%) rename {Modules/ComputerManagementDsc/DSCResources => DSCResources}/MSFT_SmbShare/en-US/MSFT_SmbShare.schema.mfl (100%) rename {Modules/ComputerManagementDsc/DSCResources => DSCResources}/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 (100%) rename {Modules/ComputerManagementDsc/Examples => Examples}/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 (100%) rename {Modules/ComputerManagementDsc/Examples => Examples}/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 (100%) rename {Modules/ComputerManagementDsc/Examples => Examples}/Resources/SmbShare/3-SmbShare_RemoveShare_Config.ps1 (100%) diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 b/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 similarity index 100% rename from Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 rename to DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.schema.mof b/DSCResources/MSFT_SmbShare/MSFT_SmbShare.schema.mof similarity index 100% rename from Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/MSFT_SmbShare.schema.mof rename to DSCResources/MSFT_SmbShare/MSFT_SmbShare.schema.mof diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/README.md b/DSCResources/MSFT_SmbShare/README.md similarity index 100% rename from Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/README.md rename to DSCResources/MSFT_SmbShare/README.md diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.schema.mfl b/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.schema.mfl similarity index 100% rename from Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.schema.mfl rename to DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.schema.mfl diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 b/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 similarity index 100% rename from Modules/ComputerManagementDsc/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 rename to DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 diff --git a/Modules/ComputerManagementDsc/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 b/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 similarity index 100% rename from Modules/ComputerManagementDsc/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 rename to Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 diff --git a/Modules/ComputerManagementDsc/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 b/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 similarity index 100% rename from Modules/ComputerManagementDsc/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 rename to Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 diff --git a/Modules/ComputerManagementDsc/Examples/Resources/SmbShare/3-SmbShare_RemoveShare_Config.ps1 b/Examples/Resources/SmbShare/3-SmbShare_RemoveShare_Config.ps1 similarity index 100% rename from Modules/ComputerManagementDsc/Examples/Resources/SmbShare/3-SmbShare_RemoveShare_Config.ps1 rename to Examples/Resources/SmbShare/3-SmbShare_RemoveShare_Config.ps1 diff --git a/Tests/Unit/MSFT_SmbShare.Tests.ps1 b/Tests/Unit/MSFT_SmbShare.Tests.ps1 index 57f15e7c..3cc29ffa 100644 --- a/Tests/Unit/MSFT_SmbShare.Tests.ps1 +++ b/Tests/Unit/MSFT_SmbShare.Tests.ps1 @@ -2,12 +2,14 @@ $script:dscModuleName = 'ComputerManagementDsc' $script:dscResourceName = 'MSFT_SmbShare' +Import-Module -Name (Join-Path -Path (Join-Path -Path (Split-Path $PSScriptRoot -Parent) -ChildPath 'TestHelpers') -ChildPath 'CommonTestHelper.psm1') -Global + # Unit Test Template Version: 1.2.4 -$script:moduleRoot = Join-Path -Path $(Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $Script:MyInvocation.MyCommand.Path))) -ChildPath 'Modules\ComputerManagementDsc' +$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.exe @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath 'DscResource.Tests')) + & 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 @@ -17,7 +19,6 @@ $TestEnvironment = Initialize-TestEnvironment ` -DSCResourceName $script:dscResourceName ` -ResourceType 'Mof' ` -TestType Unit - #endregion HEADER function Invoke-TestSetup From 2f78d0ece42dbee5572b85c0c5d126a26a8bce4a Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 14 Apr 2019 10:16:04 +0200 Subject: [PATCH 17/26] Fix correct template in integration test --- Tests/Integration/MSFT_SmbShare.Integration.Tests.ps1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Tests/Integration/MSFT_SmbShare.Integration.Tests.ps1 b/Tests/Integration/MSFT_SmbShare.Integration.Tests.ps1 index 890a3ee1..707ace10 100644 --- a/Tests/Integration/MSFT_SmbShare.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_SmbShare.Integration.Tests.ps1 @@ -3,13 +3,13 @@ Integration tests for DSC resource SmbShare. #> +#region HEADER $script:dscModuleName = 'ComputerManagementDsc' $script:dscResourceFriendlyName = 'SmbShare' $script:dscResourceName = "MSFT_$($script:dscResourceFriendlyName)" -#region HEADER # Integration Test Template Version: 1.3.3 -$script:moduleRoot = Join-Path -Path $(Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $Script:MyInvocation.MyCommand.Path))) -ChildPath 'Modules\ComputerManagementDsc' +$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'))) ) { @@ -23,6 +23,8 @@ $TestEnvironment = Initialize-TestEnvironment ` -TestType Integration #endregion +#region HEADER + $script:dscResourceFriendlyName = 'SmbShare' $script:dcsResourceName = "MSFT_$($script:dscResourceFriendlyName)" From 1c341692c2023f0781eef9e62f58f868a2674118 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 14 Apr 2019 11:41:57 +0200 Subject: [PATCH 18/26] Consistently using ToString() --- DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 b/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 index beb5ea47..1019dcc4 100644 --- a/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 +++ b/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 @@ -77,8 +77,8 @@ function Get-TargetResource $returnValue['FolderEnumerationMode'] = $smbShare.FolderEnumerationMode.ToString() $returnValue['CachingMode'] = $smbShare.CachingMode.ToString() $returnValue['ContinuouslyAvailable'] = $smbShare.ContinuouslyAvailable - $returnValue['ShareState'] = [System.String] $smbShare.ShareState - $returnValue['ShareType'] = [System.String] $smbShare.ShareType + $returnValue['ShareState'] = $smbShare.ShareState.ToString() + $returnValue['ShareType'] = $smbShare.ShareType.ToString() $returnValue['ShadowCopy'] = $smbShare.ShadowCopy $returnValue['Special'] = $smbShare.Special From 932396ebf0014b6e88b69609eb1a4f94c126158c Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 14 Apr 2019 12:06:53 +0200 Subject: [PATCH 19/26] Fix mock in tests --- Tests/Unit/MSFT_SmbShare.Tests.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/Unit/MSFT_SmbShare.Tests.ps1 b/Tests/Unit/MSFT_SmbShare.Tests.ps1 index 3cc29ffa..27ac5c56 100644 --- a/Tests/Unit/MSFT_SmbShare.Tests.ps1 +++ b/Tests/Unit/MSFT_SmbShare.Tests.ps1 @@ -51,7 +51,8 @@ try # 0 AccessBased | 1 Unrestricted Add-Member -MemberType NoteProperty -Name 'FolderEnumerationMode' -Value 'AccessBased' -PassThru | # 0 Pending | 1 Online | 2 Offline - Add-Member -MemberType NoteProperty -Name 'SharedState' -Value 'Online' -PassThru | + Add-Member -MemberType NoteProperty -Name 'ShareState' -Value 'Online' -PassThru | + Add-Member -MemberType NoteProperty -Name 'ShareType' -Value 'FileSystemDirectory' -PassThru | Add-Member -MemberType NoteProperty -Name 'ShadowCopy' -Value $false -PassThru | Add-Member -MemberType NoteProperty -Name 'CachingMode' -Value 'Manual' -PassThru | Add-Member -MemberType NoteProperty -Name 'ContinuouslyAvailable' -Value $true -PassThru | From 16a8c08a95a91584ebfbda7b89a57c41a5937773 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 23 Jun 2019 12:37:53 +0200 Subject: [PATCH 20/26] Update CHANGELOG.md --- CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc0db7ab..79b9b5a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ - xComputer: - Fix for 'directory service is busy' error when joining a domain and renaming a computer when JoinOU is specified - Fixes [Issue #221](https://github.com/PowerShell/ComputerManagementDsc/issues/221). +- Added new resource + - SmbShare (moved and improved from deprecated module xSmbShare). +- Changes to ComputerManagementDsc.Common + - Updated Test-DscParameterState so it now can compare zero item + collections (arrays). ## 6.4.0.0 @@ -20,11 +25,6 @@ - Updated test header for all imtegration to version 1.3.3. - Enabled example publish to PowerShell Gallery by adding `gallery_api` environment variable to `AppVeyor.yml`. -- Added new resource - - SmbShare (moved and improved from deprecated module xSmbShare). -- Changes to ComputerManagementDsc.Common - - Updated Test-DscParameterState so it now can compare zero item - collections (arrays). ## 6.3.0.0 From b4c557c6578befc1c4d13ded28a84e8c4db767a5 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 23 Jun 2019 13:38:18 +0200 Subject: [PATCH 21/26] FIx review comments at r4 --- DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 | 100 +++++++----------- .../MSFT_SmbShare/MSFT_SmbShare.schema.mof | 8 +- DSCResources/MSFT_SmbShare/README.md | 8 +- .../en-US/MSFT_SmbShare.schema.mfl | 8 +- .../en-US/MSFT_SmbShare.strings.psd1 | 2 +- .../1-SmbShare_CreateShare_Config.ps1 | 6 +- ...bShare_CreateShareAllProperties_Config.ps1 | 9 +- .../3-SmbShare_RemoveShare_Config.ps1 | 2 +- .../ComputerManagementDsc.Common.psm1 | 2 +- Tests/Integration/MSFT_SmbShare.config.ps1 | 26 ++++- 10 files changed, 92 insertions(+), 79 deletions(-) diff --git a/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 b/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 index 1019dcc4..14e68d34 100644 --- a/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 +++ b/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 @@ -66,6 +66,7 @@ function Get-TargetResource $accountsNoAccess = [system.string[]] @() $smbShare = Get-SmbShare -Name $Name -ErrorAction 'SilentlyContinue' + if ($smbShare) { $returnValue['Ensure'] = 'Present' @@ -82,8 +83,9 @@ function Get-TargetResource $returnValue['ShadowCopy'] = $smbShare.ShadowCopy $returnValue['Special'] = $smbShare.Special - $getSmbShareAccessResult = Get-SmbShareAccess -Name $Name - foreach ($access in $getSmbShareAccessResult) + $currentSmbShareAccessPermissions = Get-SmbShareAccess -Name $Name + + foreach ($access in $currentSmbShareAccessPermissions) { switch ($access.AccessRight) { @@ -172,11 +174,11 @@ function Get-TargetResource SMB share. .PARAMETER ChangeAccess - Specifies which user will be granted modify permission to access the + Specifies which accounts will be granted modify permission to access the SMB share. .PARAMETER ReadAccess - Specifies which user is granted read permission to access the SMB share. + Specifies which accounts is granted read permission to access the SMB share. .PARAMETER NoAccess Specifies which accounts are denied access to the SMB share. @@ -253,8 +255,9 @@ function Set-TargetResource #> $smbShareParameters = @{} + $PSBoundParameters - $getTargetResourceResult = Get-TargetResource -Name $Name -Path $Path - if ($getTargetResourceResult.Ensure -eq 'Present') + $currentSmbShareConfiguration = Get-TargetResource -Name $Name -Path $Path + + if ($currentSmbShareConfiguration.Ensure -eq 'Present') { Write-Verbose -Message ($script:localizedData.IsPresent -f $Name) @@ -267,8 +270,6 @@ function Set-TargetResource $_ -in ('ChangeAccess','ReadAccess','FullAccess','NoAccess','Ensure','Path') } - # TODO: Make sure to remove parameters that already are in desired state. - $parametersToRemove | ForEach-Object { $smbShareParameters.Remove($_) } @@ -300,7 +301,7 @@ function Set-TargetResource $smbShareAccessPermissionParameters['NoAccess'] = $NoAccess } - # We should only pass the access collections that the user want to enforce. + # We should only pass the access collections that the user wants to enforce. Remove-SmbShareAccessPermission @smbShareAccessPermissionParameters Add-SmbShareAccessPermission @smbShareAccessPermissionParameters @@ -375,11 +376,11 @@ function Set-TargetResource SMB share. .PARAMETER ChangeAccess - Specifies which user will be granted modify permission to access the + Specifies which accounts will be granted modify permission to access the SMB share. .PARAMETER ReadAccess - Specifies which user is granted read permission to access the SMB share. + Specifies which accounts is granted read permission to access the SMB share. .PARAMETER NoAccess Specifies which accounts are denied access to the SMB share. @@ -453,11 +454,11 @@ function Test-TargetResource Write-Verbose -Message ($script:localizedData.TestTargetResourceMessage -f $Name) - $testTargetResourceResult = $false + $resourceRequiresUpdate = $false - $getTargetResourceResult = Get-TargetResource -Name $Name -Path $Path + $currentSmbShareConfiguration = Get-TargetResource -Name $Name -Path $Path - if ($getTargetResourceResult.Ensure -eq $Ensure ) + if ($currentSmbShareConfiguration.Ensure -eq $Ensure) { if ($Ensure -eq 'Present') { @@ -472,8 +473,8 @@ function Test-TargetResource Test-DscParameterState is outputted, if the user requested verbose messages. #> - $testTargetResourceResult = Test-DscParameterState ` - -CurrentValues $getTargetResourceResult ` + $resourceRequiresUpdate = Test-DscParameterState ` + -CurrentValues $currentSmbShareConfiguration ` -DesiredValues $PSBoundParameters ` -Verbose:$VerbosePreference } @@ -481,11 +482,11 @@ function Test-TargetResource { Write-Verbose -Message ($script:localizedData.IsAbsent -f $Name) - $testTargetResourceResult = $true + $resourceRequiresUpdate = $true } } - return $testTargetResourceResult + return $resourceRequiresUpdate } <# @@ -546,18 +547,15 @@ function Remove-SmbShareAccessPermission $NoAccess ) - <# - Get a collection of all accounts that currently have access - or are denied access - #> - $getSmbShareAccessResult = Get-SmbShareAccess -Name $Name + $currentSmbShareAccessPermissions = Get-SmbShareAccess -Name $Name <# First all access must be removed for accounts that should not have permission, or should be unblocked (those that was denied - access). After that we add new accounts. + access). After that we can add new accounts using the function + Add-SmbShareAccessPermission. #> - foreach ($smbShareAccess in $getSmbShareAccessResult) + foreach ($smbShareAccess in $currentSmbShareAccessPermissions) { switch ($smbShareAccess.AccessControlType) { @@ -565,28 +563,11 @@ function Remove-SmbShareAccessPermission { $shouldRevokeAccess = $false - if ($smbShareAccess.AccessRight -eq 'Change') + foreach ($accessRight in 'Change','Read','Full') { - if ($PSBoundParameters.ContainsKey('ChangeAccess') -and $smbShareAccess.AccountName -notin $ChangeAccess) - { - $shouldRevokeAccess = $true - } - } - - if ($smbShareAccess.AccessRight -eq 'Read') - { - if ($PSBoundParameters.ContainsKey('ReadAccess') -and $smbShareAccess.AccountName -notin $ReadAccess) - { - $shouldRevokeAccess = $true - } - } - - if ($smbShareAccess.AccessRight -eq 'Full') - { - if ($PSBoundParameters.ContainsKey('FullAccess') -and $smbShareAccess.AccountName -notin $FullAccess) - { - $shouldRevokeAccess = $true - } + $accessRightVariableName = "${accessRight}Access" + $shouldRevokeAccess = $shouldRevokeAccess ` + -or ($PSBoundParameters.ContainsKey($accessRightVariableName) -and $smbShareAccess.AccountName -notin $PSBoundParameters[$accessRightVariableName]) } if ($shouldRevokeAccess) @@ -667,13 +648,12 @@ function Add-SmbShareAccessPermission $NoAccess ) - # Update the collection after all accounts have been removed. - $getSmbShareAccessResult = Get-SmbShareAccess -Name $Name + $currentSmbShareAccessPermissions = Get-SmbShareAccess -Name $Name if ($PSBoundParameters.ContainsKey('ChangeAccess')) { # Get already added account names. - $smbShareChangeAccessObjects = $getSmbShareAccessResult | Where-Object -FilterScript { + $smbShareChangeAccessObjects = $currentSmbShareAccessPermissions | Where-Object -FilterScript { $_.AccessControlType -eq 'Allow' ` -and $_.AccessRight -eq 'Change' } @@ -698,7 +678,7 @@ function Add-SmbShareAccessPermission if ($PSBoundParameters.ContainsKey('ReadAccess')) { # Get already added account names. - $smbShareReadAccessObjects = $getSmbShareAccessResult | Where-Object -FilterScript { + $smbShareReadAccessObjects = $currentSmbShareAccessPermissions | Where-Object -FilterScript { $_.AccessControlType -eq 'Allow' ` -and $_.AccessRight -eq 'Read' } @@ -723,7 +703,7 @@ function Add-SmbShareAccessPermission if ($PSBoundParameters.ContainsKey('FullAccess')) { # Get already added account names. - $smbShareFullAccessObjects = $getSmbShareAccessResult | Where-Object -FilterScript { + $smbShareFullAccessObjects = $currentSmbShareAccessPermissions | Where-Object -FilterScript { $_.AccessControlType -eq 'Allow' ` -and $_.AccessRight -eq 'Full' } @@ -748,7 +728,7 @@ function Add-SmbShareAccessPermission if ($PSBoundParameters.ContainsKey('NoAccess')) { # Get already added account names. - $smbShareNoAccessObjects = $getSmbShareAccessResult | Where-Object -FilterScript { + $smbShareNoAccessObjects = $currentSmbShareAccessPermissions | Where-Object -FilterScript { $_.AccessControlType -eq 'Deny' ` -and $_.AccessRight -eq 'Full' } @@ -806,9 +786,8 @@ function Add-SmbShareAccessPermission The group 'Everyone' is automatically given read access by the cmdlet New-SmbShare if all access permission parameters (FullAccess, ChangeAccess, ReadAccess, NoAccess) is set to @(). - For that reason we are need either none of the parameters, or - at least one to specify an account. - + For that reason we need neither of the parameters, or at least + one to specify an account. #> function Assert-AccessPermissionParameters { @@ -842,16 +821,17 @@ function Assert-AccessPermissionParameters #> if ($PSBoundParameters.ContainsKey('ReadAccess') -and -not $ReadAccess) { - $fullAccessHasNoMembers = $PSBoundParameters.ContainsKey('FullAccess') -and -not $FullAccess - $changeAccessHasNoMembers = $PSBoundParameters.ContainsKey('ChangeAccess') -and -not $ChangeAccess - $noAccessHasNoMembers = $PSBoundParameters.ContainsKey('NoAccess') -and -not $NoAccess + $fullAccessIsEmpty = $PSBoundParameters.ContainsKey('FullAccess') -and -not $FullAccess + $changeAccessIsEmpty = $PSBoundParameters.ContainsKey('ChangeAccess') -and -not $ChangeAccess + $noAccessIsEmpty = $PSBoundParameters.ContainsKey('NoAccess') -and -not $NoAccess + <# If ReadAccess should have no members, then we need at least one member in one of the other access permission collections. #> - if ($fullAccessHasNoMembers -and $changeAccessHasNoMembers -and $noAccessHasNoMembers) + if ($fullAccessIsEmpty -and $changeAccessIsEmpty -and $noAccessIsEmpty) { - New-InvalidArgumentException -Message $script:localizedData.WrongAccessParameters -ArgumentName 'FullAccess, ChangeAccess, ReadAccess, NoAccess' + New-InvalidArgumentException -Message $script:localizedData.InvalidAccessParametersCombination -ArgumentName 'FullAccess, ChangeAccess, ReadAccess, NoAccess' } } } diff --git a/DSCResources/MSFT_SmbShare/MSFT_SmbShare.schema.mof b/DSCResources/MSFT_SmbShare/MSFT_SmbShare.schema.mof index e3d4decb..965d6c91 100644 --- a/DSCResources/MSFT_SmbShare/MSFT_SmbShare.schema.mof +++ b/DSCResources/MSFT_SmbShare/MSFT_SmbShare.schema.mof @@ -5,15 +5,15 @@ class MSFT_SmbShare : OMI_BaseResource [Key, Description("Specifies the name of the SMB share.")] String Name; [Required, Description("Specifies the path of the SMB share.")] String Path; [Write, Description("Specifies the description of the SMB share.")] String Description; - [Write, Description("Specifies which user will be granted modify permission to access the SMB share.")] String ChangeAccess[]; + [Write, Description("Specifies which accounts will be granted modify permission to access the SMB share.")] String ChangeAccess[]; [Write, Description("Specifies the maximum number of concurrently connected users that the new SMB share may accommodate. If this parameter is set to zero (0), then the number of users is unlimited. The default value is zero (0).")] Uint32 ConcurrentUserLimit; [Write, Description("Indicates that the SMB share is encrypted.")] Boolean EncryptData; - [Write, Description("Specifies which files and folders in the new SMB share are visible to users. { AccessBased | Unrestricted }"), ValueMap{"AccessBased","Unrestricted"}, Values{"AccessBased","Unrestricted"}] String FolderEnumerationMode; - [Write, Description("Specifies the caching mode of the offline files for the SMB share. { 'None' | 'Manual' | 'Programs' | 'Documents' | 'BranchCache' }"), ValueMap{"None","Manual","Programs","Documents","BranchCache"}, Values{"None","Manual","Programs","Documents","BranchCache"}] String CachingMode; + [Write, Description("Specifies which files and folders in the new SMB share are visible to users."), ValueMap{"AccessBased","Unrestricted"}, Values{"AccessBased","Unrestricted"}] String FolderEnumerationMode; + [Write, Description("Specifies the caching mode of the offline files for the SMB share."), ValueMap{"None","Manual","Programs","Documents","BranchCache"}, Values{"None","Manual","Programs","Documents","BranchCache"}] String CachingMode; [Write, Description("Specifies whether the SMB share should be continuously available.")] Boolean ContinuouslyAvailable; [Write, Description("Specifies which accounts are granted full permission to access the SMB share.")] String FullAccess[]; [Write, Description("Specifies which accounts are denied access to the SMB share.")] String NoAccess[]; - [Write, Description("Specifies which user is granted read permission to access the SMB share.")] String ReadAccess[]; + [Write, Description("Specifies which accounts is granted read permission to access the SMB share.")] String ReadAccess[]; [Write, Description("Specifies if the SMB share should be added or removed."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; [Read, Description("Specifies the state of the SMB share.")] String ShareState; [Read, Description("Specifies the type of the SMB share.")] String ShareType; diff --git a/DSCResources/MSFT_SmbShare/README.md b/DSCResources/MSFT_SmbShare/README.md index ba61a96b..064cc216 100644 --- a/DSCResources/MSFT_SmbShare/README.md +++ b/DSCResources/MSFT_SmbShare/README.md @@ -7,7 +7,7 @@ SMB shares. ### Cluster Shares -To property `ContinuouslyAvailable` can only be set to `$true` when +The property `ContinuouslyAvailable` can only be set to `$true` when the SMB share is a cluster share in a failover cluster. Also in the blog [SMB Transparent Failover – making file shares continuously available](https://blogs.technet.microsoft.com/filecab/2016/03/25/smb-transparent-failover-making-file-shares-continuously-available-2) by [Claus Joergensen](https://github.com/clausjor) it is mentioned that @@ -17,8 +17,8 @@ generation enabled. ### Access permissions It is not allowed to provide empty collections in the configuration for -the access permissions parameters. The below configuration will throw an -error. +the access permissions parameters. The configuration below will cause an +exception to be thrown. ```powershell SmbShare 'Integration_Test' @@ -35,6 +35,6 @@ SmbShare 'Integration_Test' The access permission parameters must either be all removed to manage the access permission manually, or add at least one member to one of the access permission parameters. If all the access permission parameters -are removed, then by design of the cmdlet `New-SmbShare` it will add +are removed, then by design, the cmdlet New-SmbShare will add the *Everyone* group with read access permission to the SMB share. To prevent that, add a member to either access permission parameters. diff --git a/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.schema.mfl b/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.schema.mfl index 46c2d061..9d2fd5ec 100644 --- a/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.schema.mfl +++ b/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.schema.mfl @@ -4,15 +4,15 @@ class MSFT_SmbShare : OMI_BaseResource [Key, Description("Specifies the name of the SMB share.") : Amended] String Name; [Description("Specifies the path of the SMB share.") : Amended] String Path; [Description("Specifies the description of the SMB share.") : Amended] String Description; - [Description("Specifies which user will be granted modify permission to access the SMB share.") : Amended] String ChangeAccess[]; + [Description("Specifies which accounts will be granted modify permission to access the SMB share.") : Amended] String ChangeAccess[]; [Description("Specifies the maximum number of concurrently connected users that the new SMB share may accommodate. If this parameter is set to zero (0), then the number of users is unlimited. The default value is zero (0).") : Amended] Uint32 ConcurrentUserLimit; [Description("Indicates that the SMB share is encrypted.") : Amended] Boolean EncryptData; - [Description("Specifies which files and folders in the new SMB share are visible to users. { AccessBased | Unrestricted }") : Amended] String FolderEnumerationMode; - [Description("Specifies the caching mode of the offline files for the SMB share. { 'None' | 'Manual' | 'Programs' | 'Documents' | 'BranchCache' }") : Amended] String CachingMode; + [Description("Specifies which files and folders in the new SMB share are visible to users.") : Amended] String FolderEnumerationMode; + [Description("Specifies the caching mode of the offline files for the SMB share.") : Amended] String CachingMode; [Description("Specifies whether the SMB share should be continuously available.") : Amended] Boolean ContinuouslyAvailable; [Description("Specifies which accounts are granted full permission to access the SMB share.") : Amended] String FullAccess[]; [Description("Specifies which accounts are denied access to the SMB share.") : Amended] String NoAccess[]; - [Description("Specifies which user is granted read permission to access the SMB share.") : Amended] String ReadAccess[]; + [Description("Specifies which accounts is granted read permission to access the SMB share.") : Amended] String ReadAccess[]; [Description("Specifies if the SMB share should be added or removed.") : Amended] String Ensure; [Description("Specifies the state of the SMB share.") : Amended] String ShareState; [Description("Specifies the type of the SMB share.") : Amended] String ShareType; diff --git a/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 b/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 index 099e22b6..6311a490 100644 --- a/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 +++ b/DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1 @@ -14,5 +14,5 @@ ConvertFrom-StringData @' UnblockAccess = Revoking denied permission for account '{0}' on the SMB share with the name '{1}'. GrantAccess = Granting '{0}' permission for account '{1}' on the SMB share with the name '{2}'. DenyAccess = Denying permission for account '{0}' on the SMB share with the name '{1}'. - WrongAccessParameters = Not allowed to have all access permission parameters set to empty collections. Must either remove the access permission parameters completely, or add at least one member to one of the access permission parameters. + InvalidAccessParametersCombination = Not allowed to have all access permission parameters set to empty collections. Must either remove the access permission parameters completely, or add at least one member to one of the access permission parameters. '@ diff --git a/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 b/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 index b1256261..380ed54e 100644 --- a/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 +++ b/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 @@ -19,8 +19,12 @@ <# .DESCRIPTION - This examples create a SMB share named 'Temp' for the path 'C:\Temp', + This examples create an SMB share named 'Temp' for the path 'C:\Temp', using the default values of the cmdlet `New-SmbShare`. + + .NOTES + To know the default values, see the documentation for the cmdlet + `New-SmbShare`. #> Configuration SmbShare_CreateShare_Config { diff --git a/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 b/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 index 9d28e519..054f3b6b 100644 --- a/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 +++ b/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 @@ -19,8 +19,13 @@ <# .DESCRIPTION - This examples create a SMB share named 'Temp' for the path 'C:\Temp', - using the default values of the cmdlet `New-SmbShare`. + This examples create an SMB share named 'Temp' for the path 'C:\Temp', + using specific values for each supported property. + + .NOTES + Any other property not yet súpported will use the default values of the + cmdlet `New-SmbShare`.To know the default values, see the documentation + for the cmdlet `New-SmbShare`. #> Configuration SmbShare_CreateShareAllProperties_Config { diff --git a/Examples/Resources/SmbShare/3-SmbShare_RemoveShare_Config.ps1 b/Examples/Resources/SmbShare/3-SmbShare_RemoveShare_Config.ps1 index 52d38201..a2e26eb3 100644 --- a/Examples/Resources/SmbShare/3-SmbShare_RemoveShare_Config.ps1 +++ b/Examples/Resources/SmbShare/3-SmbShare_RemoveShare_Config.ps1 @@ -19,7 +19,7 @@ <# .DESCRIPTION - This examples removes a SMB share named 'Temp'. + This example removes a SMB share named 'Temp'. .NOTES Path must be specified because it is a mandatory parameter, diff --git a/Modules/ComputerManagementDsc.Common/ComputerManagementDsc.Common.psm1 b/Modules/ComputerManagementDsc.Common/ComputerManagementDsc.Common.psm1 index 670faa6f..57e295b5 100644 --- a/Modules/ComputerManagementDsc.Common/ComputerManagementDsc.Common.psm1 +++ b/Modules/ComputerManagementDsc.Common/ComputerManagementDsc.Common.psm1 @@ -203,7 +203,7 @@ function Test-DscParameterState } <# This evaluation needs to be performed before the next evaluation, - because we need to be able to handle a that the current value + because we need to be able to handle when the current value is a zero item collection, meaning `-not $CurrentValues.$key` would otherwise return $true in the next evaluation. #> diff --git a/Tests/Integration/MSFT_SmbShare.config.ps1 b/Tests/Integration/MSFT_SmbShare.config.ps1 index 0d3edae8..de1e0bf0 100644 --- a/Tests/Integration/MSFT_SmbShare.config.ps1 +++ b/Tests/Integration/MSFT_SmbShare.config.ps1 @@ -42,7 +42,7 @@ else #> Configuration MSFT_SmbShare_Prerequisites_Config { - Import-DscResource -ModuleName 'PSDscResources' + Import-DscResource -ModuleName 'PSDesiredStateConfiguration' node $AllNodes.NodeName { @@ -274,5 +274,29 @@ Configuration MSFT_SmbShare_Cleanup_Config Type = 'Directory' DestinationPath = $Node.SharePath2 } + + User 'RemoveAccountUser1' + { + Ensure = 'Absent' + UserName = Split-Path -Path $Node.UserName1 -Leaf + } + + User 'RemoveAccountUser2' + { + Ensure = 'Absent' + UserName = Split-Path -Path $Node.UserName2 -Leaf + } + + User 'RemoveAccountUser3' + { + Ensure = 'Absent' + UserName = Split-Path -Path $Node.UserName3 -Leaf + } + + User 'RemoveAccountUser4' + { + Ensure = 'Absent' + UserName = Split-Path -Path $Node.UserName4 -Leaf + } } } From 0c9cda0321c282c115a3e93abbd811c3a1c27664 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 23 Jun 2019 14:32:06 +0200 Subject: [PATCH 22/26] Fix bug in new code --- DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 b/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 index 14e68d34..611535db 100644 --- a/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 +++ b/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 @@ -567,7 +567,11 @@ function Remove-SmbShareAccessPermission { $accessRightVariableName = "${accessRight}Access" $shouldRevokeAccess = $shouldRevokeAccess ` - -or ($PSBoundParameters.ContainsKey($accessRightVariableName) -and $smbShareAccess.AccountName -notin $PSBoundParameters[$accessRightVariableName]) + -or ( + $smbShareAccess.AccessRight -eq $accessRight ` + -and $PSBoundParameters.ContainsKey($accessRightVariableName) ` + -and $smbShareAccess.AccountName -notin $PSBoundParameters[$accessRightVariableName] + ) } if ($shouldRevokeAccess) From d016483c208b2b714a49d2791e664811eb668c5e Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 23 Jun 2019 14:34:43 +0200 Subject: [PATCH 23/26] Fix variable --- DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 b/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 index 611535db..4f069a10 100644 --- a/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 +++ b/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 @@ -565,7 +565,7 @@ function Remove-SmbShareAccessPermission foreach ($accessRight in 'Change','Read','Full') { - $accessRightVariableName = "${accessRight}Access" + $accessRightVariableName = '{0}Access' -f $accessRight $shouldRevokeAccess = $shouldRevokeAccess ` -or ( $smbShareAccess.AccessRight -eq $accessRight ` From 667b707992579679f0b0bcb3f438ba4da7b6974d Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 23 Jun 2019 14:38:49 +0200 Subject: [PATCH 24/26] Fix examples --- Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 | 2 +- .../SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 b/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 index 380ed54e..6d5b083c 100644 --- a/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 +++ b/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 @@ -19,7 +19,7 @@ <# .DESCRIPTION - This examples create an SMB share named 'Temp' for the path 'C:\Temp', + This example create an SMB share named 'Temp' for the path 'C:\Temp', using the default values of the cmdlet `New-SmbShare`. .NOTES diff --git a/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 b/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 index 054f3b6b..838169f0 100644 --- a/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 +++ b/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 @@ -19,7 +19,7 @@ <# .DESCRIPTION - This examples create an SMB share named 'Temp' for the path 'C:\Temp', + This example create an SMB share named 'Temp' for the path 'C:\Temp', using specific values for each supported property. .NOTES From 808c77bb85edfd08b8ea03aaaae140b2b17dd44f Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 23 Jun 2019 14:41:10 +0200 Subject: [PATCH 25/26] Fix more typos in examples --- Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 | 2 +- .../SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 b/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 index 6d5b083c..47154bb7 100644 --- a/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 +++ b/Examples/Resources/SmbShare/1-SmbShare_CreateShare_Config.ps1 @@ -19,7 +19,7 @@ <# .DESCRIPTION - This example create an SMB share named 'Temp' for the path 'C:\Temp', + This example creates an SMB share named 'Temp' for the path 'C:\Temp', using the default values of the cmdlet `New-SmbShare`. .NOTES diff --git a/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 b/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 index 838169f0..3afe5d23 100644 --- a/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 +++ b/Examples/Resources/SmbShare/2-SmbShare_CreateShareAllProperties_Config.ps1 @@ -19,7 +19,7 @@ <# .DESCRIPTION - This example create an SMB share named 'Temp' for the path 'C:\Temp', + This example creates an SMB share named 'Temp' for the path 'C:\Temp', using specific values for each supported property. .NOTES From 6ed69fe551b5fb1badcb25e0fda3d40fffdc96d4 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Wed, 3 Jul 2019 10:16:34 +0200 Subject: [PATCH 26/26] Fix review comments at r5 --- CHANGELOG.md | 8 ++-- DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 | 10 ++--- .../MSFT_WindowsEventLog.psm1 | 2 +- .../ComputerManagementDsc.Common.psm1 | 2 +- .../MSFT_ScheduledTask.Integration.Tests.ps1 | 6 +-- Tests/Unit/MSFT_SmbShare.Tests.ps1 | 41 ++++++++----------- 6 files changed, 32 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79b9b5a9..4d3f2588 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,14 +2,16 @@ ## Unreleased -- xComputer: +- Computer: - Fix for 'directory service is busy' error when joining a domain and renaming a computer when JoinOU is specified - Fixes [Issue #221](https://github.com/PowerShell/ComputerManagementDsc/issues/221). -- Added new resource - - SmbShare (moved and improved from deprecated module xSmbShare). +- Added new resource SmbShare + - Moved and improved from deprecated module xSmbShare. - Changes to ComputerManagementDsc.Common - Updated Test-DscParameterState so it now can compare zero item collections (arrays). +- Changes to WindowsEventLog + - Minor style guideline cleanup. ## 6.4.0.0 diff --git a/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 b/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 index 4f069a10..105ffa71 100644 --- a/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 +++ b/DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1 @@ -270,7 +270,7 @@ function Set-TargetResource $_ -in ('ChangeAccess','ReadAccess','FullAccess','NoAccess','Ensure','Path') } - $parametersToRemove | ForEach-Object { + $parametersToRemove | ForEach-Object -Process { $smbShareParameters.Remove($_) } @@ -672,7 +672,7 @@ function Add-SmbShareAccessPermission $accessRight = 'Change' # Add new accounts that should have change permission. - $newAccountsToHaveChangeAccess | ForEach-Object { + $newAccountsToHaveChangeAccess | ForEach-Object -Process { Write-Verbose -Message ($script:localizedData.GrantAccess -f $accessRight, $_, $Name) Grant-SmbShareAccess -Name $Name -AccountName $_ -AccessRight $accessRight -Force -ErrorAction 'Stop' @@ -697,7 +697,7 @@ function Add-SmbShareAccessPermission $accessRight = 'Read' # Add new accounts that should have read permission. - $newAccountsToHaveReadAccess | ForEach-Object { + $newAccountsToHaveReadAccess | ForEach-Object -Process { Write-Verbose -Message ($script:localizedData.GrantAccess -f $accessRight, $_, $Name) Grant-SmbShareAccess -Name $Name -AccountName $_ -AccessRight $accessRight -Force -ErrorAction 'Stop' @@ -722,7 +722,7 @@ function Add-SmbShareAccessPermission $accessRight = 'Full' # Add new accounts that should have full permission. - $newAccountsToHaveFullAccess | ForEach-Object { + $newAccountsToHaveFullAccess | ForEach-Object -Process { Write-Verbose -Message ($script:localizedData.GrantAccess -f $accessRight, $_, $Name) Grant-SmbShareAccess -Name $Name -AccountName $_ -AccessRight $accessRight -Force -ErrorAction 'Stop' @@ -745,7 +745,7 @@ function Add-SmbShareAccessPermission } # Add new accounts that should be denied permission. - $newAccountsToHaveNoAccess | ForEach-Object { + $newAccountsToHaveNoAccess | ForEach-Object -Process { Write-Verbose -Message ($script:localizedData.DenyAccess -f $_, $Name) Block-SmbShareAccess -Name $Name -AccountName $_ -Force -ErrorAction 'Stop' diff --git a/DSCResources/MSFT_WindowsEventLog/MSFT_WindowsEventLog.psm1 b/DSCResources/MSFT_WindowsEventLog/MSFT_WindowsEventLog.psm1 index e5d254bc..18e543ca 100644 --- a/DSCResources/MSFT_WindowsEventLog/MSFT_WindowsEventLog.psm1 +++ b/DSCResources/MSFT_WindowsEventLog/MSFT_WindowsEventLog.psm1 @@ -177,7 +177,7 @@ function Set-TargetResource if ($PSBoundParameters.ContainsKey('LogRetentionDays')) { - if ($LogMode -eq 'AutoBackup' -and (Get-EventLog -List | Where-Object {$_.Log -like $LogName})) + if ($LogMode -eq 'AutoBackup' -and (Get-EventLog -List | Where-Object -FilterScript {$_.Log -like $LogName})) { $matchingEventLog = Get-EventLog -List | Where-Object -FilterScript { $_.Log -eq $LogName diff --git a/Modules/ComputerManagementDsc.Common/ComputerManagementDsc.Common.psm1 b/Modules/ComputerManagementDsc.Common/ComputerManagementDsc.Common.psm1 index 57e295b5..162b27a2 100644 --- a/Modules/ComputerManagementDsc.Common/ComputerManagementDsc.Common.psm1 +++ b/Modules/ComputerManagementDsc.Common/ComputerManagementDsc.Common.psm1 @@ -33,7 +33,7 @@ function Remove-CommonParameter $commonParameters = [System.Management.Automation.PSCmdlet]::CommonParameters $commonParameters += [System.Management.Automation.PSCmdlet]::OptionalCommonParameters - $Hashtable.Keys | Where-Object { $_ -in $commonParameters } | ForEach-Object { + $Hashtable.Keys | Where-Object -FilterScript { $_ -in $commonParameters } | ForEach-Object -Process { $inputClone.Remove($_) } diff --git a/Tests/Integration/MSFT_ScheduledTask.Integration.Tests.ps1 b/Tests/Integration/MSFT_ScheduledTask.Integration.Tests.ps1 index 831913c8..9b11b8fb 100644 --- a/Tests/Integration/MSFT_ScheduledTask.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_ScheduledTask.Integration.Tests.ps1 @@ -177,7 +177,7 @@ try } It 'Should have set the resource and all the parameters should match' { - $current = Get-DscConfiguration | Where-Object {$_.ConfigurationName -eq $currentConfig} + $current = Get-DscConfiguration | Where-Object -FilterScript {$_.ConfigurationName -eq $currentConfig} $current.TaskName | Should -Be 'Test task once cross timezone' $current.TaskPath | Should -Be '\ComputerManagementDsc\' $current.ActionExecutable | Should -Be 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' @@ -229,7 +229,7 @@ try $expectedStartTime = '2018-10-01T01:00:00' It 'Should have set the resource and all the parameters should match' { - $current = Get-DscConfiguration | Where-Object {$_.ConfigurationName -eq $currentConfig} + $current = Get-DscConfiguration | Where-Object -FilterScript {$_.ConfigurationName -eq $currentConfig} $current.TaskName | Should -Be 'Test task sync across time zone disabled' $current.TaskPath | Should -Be '\ComputerManagementDsc\' $current.ActionExecutable | Should -Be 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' @@ -276,7 +276,7 @@ try $expectedStartTime = '2018-10-01T01:00:00' + (Get-Date -Format 'zzz') It 'Should have set the resource and all the parameters should match' { - $current = Get-DscConfiguration | Where-Object {$_.ConfigurationName -eq $currentConfig} + $current = Get-DscConfiguration | Where-Object -FilterScript {$_.ConfigurationName -eq $currentConfig} $current.TaskName | Should -Be 'Test task sync across time zone enabled' $current.TaskPath | Should -Be '\ComputerManagementDsc\' $current.ActionExecutable | Should -Be 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' diff --git a/Tests/Unit/MSFT_SmbShare.Tests.ps1 b/Tests/Unit/MSFT_SmbShare.Tests.ps1 index 27ac5c56..ac582fcd 100644 --- a/Tests/Unit/MSFT_SmbShare.Tests.ps1 +++ b/Tests/Unit/MSFT_SmbShare.Tests.ps1 @@ -120,7 +120,7 @@ try } } - It 'Should mock call to Get-SmbShare and return membership' { + It 'Should return the correct access memberships' { $getTargetResourceResult = Get-TargetResource @testParameters $getTargetResourceResult.ChangeAccess | Should -HaveCount 1 @@ -135,15 +135,8 @@ try $getTargetResourceResult.NoAccess | Should -HaveCount 1 $getTargetResourceResult.NoAccess[0] | Should -BeIn $mockNoPermissionUserName - } - It 'Should call the mock function Get-SmbShare' { - $getTargetResourceResult = Get-TargetResource @testParameters Assert-MockCalled Get-SmbShare -Exactly -Times 1 -Scope It - } - - It 'Should Call the mock function Get-SmbShareAccess' { - $getTargetResourceResult = Get-TargetResource @testParameters Assert-MockCalled Get-SmbShareAccess -Exactly -Times 1 -Scope It } } @@ -179,8 +172,9 @@ try $getTargetResourceResult.ReadAccess | Should -HaveCount 0 $getTargetResourceResult.FullAccess | Should -HaveCount 0 $getTargetResourceResult.NoAccess | Should -HaveCount 0 - } + Assert-MockCalled Get-SmbShare -Exactly -Times 1 -Scope It + } } } @@ -220,7 +214,7 @@ try } Context 'When no access permission is given' { - It 'Should call the correct mocks' { + It 'Should throw the correct error' { $setTargetResourceParameters = @{ Name = $mockShareName Path = 'TestDrive:\Temp' @@ -347,13 +341,12 @@ try Assert-MockCalled Remove-SmbShare -Exactly -Times 0 -Scope It } } - } } Describe 'MSFT_SmbShare\Test-TargetResource' -Tag 'Test' { Context 'When the system is not in the desired state' { - Context 'When no members in provided in neither access permission collection' { + Context 'When no member are provided in any of the access permission collections' { BeforeAll { $testTargetResourceParameters = @{ Name = $mockShareName @@ -680,7 +673,7 @@ try } } - It 'Should call the correct mock' { + It 'Should not throw an error and call the correct mocks' { { Add-SmbShareAccessPermission @addSmbShareAccessPermissionParameters } | Should -Not -Throw <# @@ -706,7 +699,7 @@ try } } - It 'Should call the correct mock' { + It 'Should not throw an error and call the correct mocks' { { Add-SmbShareAccessPermission @addSmbShareAccessPermissionParameters } | Should -Not -Throw <# @@ -732,7 +725,7 @@ try } } - It 'Should call the correct mock' { + It 'Should not throw an error and call the correct mocks' { { Add-SmbShareAccessPermission @addSmbShareAccessPermissionParameters } | Should -Not -Throw <# @@ -764,7 +757,7 @@ try } } - It 'Should call the correct mock' { + It 'Should not throw an error and call the correct mocks' { { Add-SmbShareAccessPermission @addSmbShareAccessPermissionParameters } | Should -Not -Throw <# @@ -809,7 +802,7 @@ try } } - It 'Should call the correct mock' { + It 'Should not throw an error and call the correct mocks' { { Add-SmbShareAccessPermission @removeSmbShareAccessPermissionParameters } | Should -Not -Throw <# @@ -860,7 +853,7 @@ try } } - It 'Should call the correct mock' { + It 'Should not throw an error and call the correct mocks' { { Remove-SmbShareAccessPermission @removeSmbShareAccessPermissionParameters } | Should -Not -Throw <# @@ -887,7 +880,7 @@ try } } - It 'Should call the correct mocks' { + It 'Should not throw an error and call the correct mocks' { { Remove-SmbShareAccessPermission @removeSmbShareAccessPermissionParameters } | Should -Not -Throw <# @@ -918,7 +911,7 @@ try } } - It 'Should call the correct mock' { + It 'Should not throw an error and call the correct mocks' { { Remove-SmbShareAccessPermission @removeSmbShareAccessPermissionParameters } | Should -Not -Throw <# @@ -944,7 +937,7 @@ try } } - It 'Should call the correct mock' { + It 'Should not throw an error and call the correct mocks' { { Remove-SmbShareAccessPermission @removeSmbShareAccessPermissionParameters } | Should -Not -Throw <# @@ -972,7 +965,7 @@ try } } - It 'Should call the correct mock' { + It 'Should not throw an error and call the correct mocks' { { Remove-SmbShareAccessPermission @removeSmbShareAccessPermissionParameters } | Should -Not -Throw Assert-MockCalled -CommandName Revoke-SmbShareAccess -Exactly -Times 4 -Scope 'It' @@ -1015,7 +1008,7 @@ try } } - It 'Should call the correct mock' { + It 'Should not throw an error and call the correct mocks' { { Remove-SmbShareAccessPermission @removeSmbShareAccessPermissionParameters } | Should -Not -Throw <# @@ -1092,7 +1085,7 @@ try } } - Context 'When providing no member in either of the access permission collections' { + Context 'When not providing any members in any of the access permission collections' { It 'Should throw the correct error' { # We must using splatting to test 'ValueFromRemainingArguments' parameter. $assertAccessPermissionParameters = @{