Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SmbShare: Bugfix to enable change of the path when the share already exists. #286

Merged
merged 9 commits into from
Nov 16, 2019
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
- Fixed bug when description has any form of whitespace at beginning or
end the resource would not go into state - Fixes [Issue #258](https://github.com/PowerShell/ComputerManagementDsc/issues/258).
- SmbShare:
- Fixed bug where the resource would not update the path of a share if the
share exists on a different path. Adds a parameter Force to the SmbShare
resource to allow updating of the path - Fixes [Issue #215](https://github.com/PowerShell/ComputerManagementDsc/issues/215)
- Removal of duplicate code in Add-SmbShareAccessPermission helper function
fixes [Issue #226](https://github.com/PowerShell/ComputerManagementDsc/issues/226).

Expand Down
46 changes: 43 additions & 3 deletions DSCResources/MSFT_SmbShare/MSFT_SmbShare.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ function Get-TargetResource

.PARAMETER Ensure
Specifies if the SMB share should be added or removed.

.PARAMETER Force
Specifies if the SMB share is allowed to be dropped and recreated (required
when the path changes).
#>
function Set-TargetResource
{
Expand Down Expand Up @@ -242,7 +246,11 @@ function Set-TargetResource
[Parameter()]
[ValidateSet('Present', 'Absent')]
[System.String]
$Ensure = 'Present'
$Ensure = 'Present',

[Parameter()]
[System.Boolean]
$Force
)

Assert-AccessPermissionParameters @PSBoundParameters
Expand All @@ -261,11 +269,32 @@ function Set-TargetResource

if ($Ensure -eq 'Present')
{
if ($currentSmbShareConfiguration.Path -ne $Path -and $Force)
{
Write-Verbose -Message ($script:localizedData.RecreateShare -f $Name)

try
{
Remove-SmbShare -Name $Name -Force -ErrorAction Stop
New-SmbShare -Name $Name -Path $Path -ErrorAction Stop
}
catch
{
Write-Error -Message ($script:localizedData.RecreateShareError -f $Name, $_)
}
}
else
{
Write-Warning -Message (
$script:localizedData.NoRecreateShare -f $Name, $currentSmbShareConfiguration.Path, $Path
)
}

Write-Verbose -Message $script:localizedData.UpdatingProperties

$parametersToRemove = $smbShareParameters.Keys |
Where-Object -FilterScript {
$_ -in ('ChangeAccess','ReadAccess','FullAccess','NoAccess','Ensure','Path')
$_ -in ('ChangeAccess','ReadAccess','FullAccess','NoAccess','Ensure','Path','Force')
}

$parametersToRemove | ForEach-Object -Process {
Expand Down Expand Up @@ -316,6 +345,7 @@ function Set-TargetResource
if ($Ensure -eq 'Present')
{
$smbShareParameters.Remove('Ensure')
$smbShareParameters.Remove('Force')

Write-Verbose -Message ($script:localizedData.CreateShare -f $Name)

Expand Down Expand Up @@ -385,6 +415,10 @@ function Set-TargetResource

.PARAMETER Ensure
Specifies if the SMB share should be added or removed.

.PARAMETER Force
Specifies if the SMB share is allowed to be dropped and recreated (required
when the path changes).
#>
function Test-TargetResource
{
Expand Down Expand Up @@ -445,9 +479,15 @@ function Test-TargetResource
[Parameter()]
[ValidateSet('Present', 'Absent')]
[System.String]
$Ensure = 'Present'
$Ensure = 'Present',

[Parameter()]
[System.Boolean]
$Force
)

$null = $PSBoundParameters.Remove('Force')

Assert-AccessPermissionParameters @PSBoundParameters

Write-Verbose -Message ($script:localizedData.TestTargetResourceMessage -f $Name)
Expand Down
1 change: 1 addition & 0 deletions DSCResources/MSFT_SmbShare/MSFT_SmbShare.schema.mof
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class MSFT_SmbShare : OMI_BaseResource
[Write, Description("Specifies which accounts are denied access to the SMB share.")] String NoAccess[];
[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;
[Write, Description("Specifies if the SMB share is allowed to be dropped and recreated (required when the path changes).")] Boolean Force;
[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.")] Boolean ShadowCopy;
Expand Down
3 changes: 3 additions & 0 deletions DSCResources/MSFT_SmbShare/en-US/MSFT_SmbShare.strings.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ ConvertFrom-StringData @'
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}'.
RecreateShare = Dropping and recreating share with name '{0}'
RecreateShareError = Failed to recreate share with name '{0}'. The error was: '{1}'.
NoRecreateShare = The share with name '{0}' exists on path {1}, desired state is on path {2}. Set Force = $true to allow drop and recreate of the share.
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}'.
Expand Down
44 changes: 44 additions & 0 deletions Examples/Resources/SmbShare/4-SmbShare_RecreateShare_Config.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<#PSScriptInfo
.VERSION 1.0.0
.GUID ea641782-74b4-4673-94fe-336cbd196c16
.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 example creates an SMB share named 'Share' for the path 'C:\Share1',
using the default values of the cmdlet `New-SmbShare`. If the share
already exists, it will drop the share and recreate it on the new path
because Force is set to true.

.NOTES
To know the default values, see the documentation for the cmdlet
`New-SmbShare`.
#>
Configuration SmbShare_RecreateShare_Config
{
Import-DscResource -ModuleName ComputerManagementDsc

Node localhost
{
SmbShare 'RecreateShare'
{
Name = 'Share'
Path = 'C:\Share1'
Force = $true
}
}
}
48 changes: 48 additions & 0 deletions Tests/Integration/MSFT_SmbShare.Integration.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,54 @@ Describe "$($script:dcsResourceName)_Integration" {
}
}

$configurationName = "$($script:dcsResourceName)_RecreateShare1_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.SharePath2
}

It 'Should return $true when Test-DscConfiguration is run' {
Test-DscConfiguration -Verbose | Should -BeTrue
}
}


$configurationName = "$($script:dcsResourceName)_RemoveShare1_Config"

Context ('When using configuration {0}' -f $configurationName) {
Expand Down
20 changes: 20 additions & 0 deletions Tests/Integration/MSFT_SmbShare.config.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,26 @@ Configuration MSFT_SmbShare_RemovePermission_Config
}
}

<#
.SYNOPSIS
Drop and recreate share 1 on a new path.
#>
Configuration MSFT_SmbShare_RecreateShare1_Config
{
Import-DscResource -ModuleName 'ComputerManagementDsc'

node $AllNodes.NodeName
{
SmbShare 'Integration_Test'
{
Ensure = 'Present'
Name = $Node.ShareName1
Path = $Node.SharePath2
Force = $true
}
}
}


<#
.SYNOPSIS
Expand Down
55 changes: 50 additions & 5 deletions Tests/Unit/MSFT_SmbShare.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -63,39 +63,39 @@ try
(
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 'ScopeName' -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 'ScopeName' -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 'ScopeName' -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 'ScopeName' -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 'ScopeName' -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
Expand Down Expand Up @@ -341,6 +341,51 @@ try
Assert-MockCalled Remove-SmbShare -Exactly -Times 0 -Scope It
}
}

Context 'When the share exists, but on the wrong path and recreate is allowed' {
Mock -CommandName Get-TargetResource -MockWith {
return @{
Name = $mockSmbShare.Name
Path = $mockSmbShare.Path
Ensure = 'Present'
}
}

$setTargetResourceParameters = @{
Name = $mockSmbShare.Name
Path = 'TestDrive:\Temp'
Force = $true
}

It 'Should drop and recreate the share' {
Set-TargetResource @setTargetResourceParameters
Assert-MockCalled -CommandName Remove-SmbShare -Times 1
Assert-MockCalled -CommandName New-SmbShare -Times 1
}
}

Context 'When the share exists, but on the wrong path and recreate is not allowed' {
Mock -CommandName Get-TargetResource -MockWith {
return @{
Name = $mockSmbShare.Name
Path = $mockSmbShare.Path
Ensure = 'Present'
}
}

$setTargetResourceParameters = @{
Name = $mockSmbShare.Name
Path = 'TestDrive:\Temp'
Force = $false
}

It 'Should display a warning with the message the share cannot be updated' {
$message = Set-TargetResource @setTargetResourceParameters 3>&1
$message | Should -Be ($script:localizedData.NoRecreateShare -f
$setTargetResourceParameters['Name'], $mockSmbShare.Path, $setTargetResourceParameters['Path']
)
}
}
}
}

Expand Down