From 0589284aa4e3c252d4ba703bc8208f891903fef1 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Mon, 18 Jun 2018 11:50:19 +0200 Subject: [PATCH 1/5] Activate the GitHub App Stale on the GitHub repository (#22) --- .github/stale.yml | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000..84f3eaa --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,44 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +limitPerRun: 30 + +pulls: + daysUntilStale: 14 + daysUntilClose: false + exemptProjects: true + exemptMilestones: true + staleLabel: abandoned + exemptLabels: + - needs review + - on hold + - waiting for CLA pass + + markComment: > + Labeling this pull request (PR) as abandoned since it has gone 14 days or more + since the last update. An abandoned PR can be continued by another contributor. + The abandoned label will be removed if work on this PR is taken up again. + +issues: + daysUntilStale: 30 + daysUntilClose: 40 + exemptProjects: true + exemptMilestones: true + staleLabel: stale + exemptLabels: + - bug + - enhancement + - tests + - documentation + - resource proposal + - on hold + + markComment: > + This issue has been automatically marked as stale because + it has not had activity from the community in the last 30 days. It will be + closed if no further activity occurs within 10 days. If the issue is labelled + with any of the work labels (e.g bug, enhancement, documentation, or tests) + then the issue will not auto-close. + + closeComment: > + This issue has been automatically closed because it is has not had activity + from the community in the last 40 days. From 1e8137c514ad7076fc53f4700dcdbe489c39bf33 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Thu, 23 Aug 2018 20:20:42 +0200 Subject: [PATCH 2/5] Add pull request template and issue templates (#23) --- .github/ISSUE_TEMPLATE/General.md | 7 +++ .../ISSUE_TEMPLATE/Problem_with_resource.md | 57 +++++++++++++++++++ .github/ISSUE_TEMPLATE/Resource_proposal.md | 21 +++++++ .github/PULL_REQUEST_TEMPLATE.md | 47 +++++++++++++++ 4 files changed, 132 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/General.md create mode 100644 .github/ISSUE_TEMPLATE/Problem_with_resource.md create mode 100644 .github/ISSUE_TEMPLATE/Resource_proposal.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE/General.md b/.github/ISSUE_TEMPLATE/General.md new file mode 100644 index 0000000..fbcdf24 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/General.md @@ -0,0 +1,7 @@ +--- +name: General question or documentation update +about: If you have a general question or documentation update suggestion around the resource module. +--- + diff --git a/.github/ISSUE_TEMPLATE/Problem_with_resource.md b/.github/ISSUE_TEMPLATE/Problem_with_resource.md new file mode 100644 index 0000000..2431f65 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Problem_with_resource.md @@ -0,0 +1,57 @@ +--- +name: Problem with a resource +about: If you have a problem, bug, or enhancement with a resource in this resource module. +--- + +#### Details of the scenario you tried and the problem that is occurring + +#### Verbose logs showing the problem + +#### Suggested solution to the issue + +#### The DSC configuration that is used to reproduce the issue (as detailed as possible) +```powershell +# insert configuration here +``` + +#### The operating system the target node is running + + +#### Version and build of PowerShell the target node is running + + +#### Version of the DSC module that was used ('dev' if using current dev branch) diff --git a/.github/ISSUE_TEMPLATE/Resource_proposal.md b/.github/ISSUE_TEMPLATE/Resource_proposal.md new file mode 100644 index 0000000..9f2a069 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Resource_proposal.md @@ -0,0 +1,21 @@ +--- +name: New resource proposal +about: If you have a new resource proposal that you think should be added to this resource module. +--- + +### Description + +### Proposed properties + +### Special considerations or limitations diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..84e1ea8 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,47 @@ + +#### Pull Request (PR) description + + +#### This Pull Request (PR) fixes the following issues + + +#### Task list + +- [ ] Added an entry under the Unreleased section of the change log in the README.md. + Entry should say what was changed, and how that affects users (if applicable). +- [ ] Resource documentation added/updated in README.md. +- [ ] Resource parameter descriptions added/updated in README.md, schema.mof + and comment-based help. +- [ ] Comment-based help added/updated. +- [ ] Localization strings added/updated in all localization files as appropriate. +- [ ] Examples appropriately added/updated. +- [ ] Unit tests added/updated. See [DSC Resource Testing Guidelines](https://github.com/PowerShell/DscResources/blob/master/TestsGuidelines.md). +- [ ] Integration tests added/updated (where possible). See [DSC Resource Testing Guidelines](https://github.com/PowerShell/DscResources/blob/master/TestsGuidelines.md). +- [ ] New/changed code adheres to [DSC Resource Style Guidelines](https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md) and [Best Practices](https://github.com/PowerShell/DscResources/blob/master/BestPractices.md). From a76f5e03514fba72bc17c7e4505e03e2d4fabf93 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 22 Sep 2018 16:33:56 +0200 Subject: [PATCH 3/5] Update to use template files (#25) --- .codecov.yml | 24 ++++++++++ .gitattributes | 2 + .gitignore | 3 +- .vscode/analyzersettings.psd1 | 53 ++++++++++++++++++++++ .vscode/settings.json | 14 ++++++ README.md | 4 ++ appveyor.yml | 82 +++++++++++++---------------------- 7 files changed, 128 insertions(+), 54 deletions(-) create mode 100644 .codecov.yml create mode 100644 .gitattributes create mode 100644 .vscode/analyzersettings.psd1 create mode 100644 .vscode/settings.json diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000..29a05dd --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,24 @@ +codecov: + notify: + require_ci_to_pass: no + +comment: + layout: "reach, diff" + behavior: default + +coverage: + range: 50..80 + round: down + precision: 0 + + status: + project: + default: + # Set the overall project code coverage requirement to 70% + target: 70 + patch: + default: + # Set the pull request requirement to not regress overall coverage by more than 5% + # and let codecov.io set the goal for the code changed in the patch. + target: auto + threshold: 5 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5613d53 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Needed for publishing of examples, build worker defaults to core.autocrlf=input. +* text eol=crlf diff --git a/.gitignore b/.gitignore index 69c4f81..2f5bc1c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,4 @@ .vs .psproj .sln -DscResource.Tests -DscResource.Tests/* +DSCResource.Tests diff --git a/.vscode/analyzersettings.psd1 b/.vscode/analyzersettings.psd1 new file mode 100644 index 0000000..be415e4 --- /dev/null +++ b/.vscode/analyzersettings.psd1 @@ -0,0 +1,53 @@ +@{ + <# + For the custom rules to work, the DscResource.Tests repo must be + cloned. It is automatically clone as soon as any unit or + integration tests are run. + #> + CustomRulePath = '.\DSCResource.Tests\DscResource.AnalyzerRules' + + IncludeRules = @( + # DSC Resource Kit style guideline rules. + 'PSAvoidDefaultValueForMandatoryParameter', + 'PSAvoidDefaultValueSwitchParameter', + 'PSAvoidInvokingEmptyMembers', + 'PSAvoidNullOrEmptyHelpMessageAttribute', + 'PSAvoidUsingCmdletAliases', + 'PSAvoidUsingComputerNameHardcoded', + 'PSAvoidUsingDeprecatedManifestFields', + 'PSAvoidUsingEmptyCatchBlock', + 'PSAvoidUsingInvokeExpression', + 'PSAvoidUsingPositionalParameters', + 'PSAvoidShouldContinueWithoutForce', + 'PSAvoidUsingWMICmdlet', + 'PSAvoidUsingWriteHost', + 'PSDSCReturnCorrectTypesForDSCFunctions', + 'PSDSCStandardDSCFunctionsInResource', + 'PSDSCUseIdenticalMandatoryParametersForDSC', + 'PSDSCUseIdenticalParametersForDSC', + 'PSMisleadingBacktick', + 'PSMissingModuleManifestField', + 'PSPossibleIncorrectComparisonWithNull', + 'PSProvideCommentHelp', + 'PSReservedCmdletChar', + 'PSReservedParams', + 'PSUseApprovedVerbs', + 'PSUseCmdletCorrectly', + 'PSUseOutputTypeCorrectly', + 'PSAvoidGlobalVars', + 'PSAvoidUsingConvertToSecureStringWithPlainText', + 'PSAvoidUsingPlainTextForPassword', + 'PSAvoidUsingUsernameAndPasswordParams', + 'PSDSCUseVerboseMessageInDSCResource', + 'PSShouldProcess', + 'PSUseDeclaredVarsMoreThanAssignments', + 'PSUsePSCredentialType', + + <# + This is to test all the DSC Resource Kit custom rules. + The name of the function-blocks of each custom rule start + with 'Measure*'. + #> + 'Measure-*' + ) +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0969e57 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,14 @@ +{ + "powershell.codeFormatting.openBraceOnSameLine": false, + "powershell.codeFormatting.newLineAfterOpenBrace": false, + "powershell.codeFormatting.newLineAfterCloseBrace": true, + "powershell.codeFormatting.whitespaceBeforeOpenBrace": true, + "powershell.codeFormatting.whitespaceBeforeOpenParen": true, + "powershell.codeFormatting.whitespaceAroundOperator": true, + "powershell.codeFormatting.whitespaceAfterSeparator": true, + "powershell.codeFormatting.ignoreOneLineBlock": false, + "powershell.codeFormatting.preset": "Custom", + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "powershell.scriptAnalysis.settingsPath": ".vscode\\analyzersettings.psd1" +} diff --git a/README.md b/README.md index aabc01a..bbc7814 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,10 @@ Please refer to [this article](http://technet.microsoft.com/en-us/library/dd8832 ### Unreleased +* Update appveyor.yml to use the default template. +* Added default template files .codecov.yml, .gitattributes, and .gitignore, and + .vscode folder. + ### 1.4.0.0 * Changes to xFileSystemAccessRule diff --git a/appveyor.yml b/appveyor.yml index 4f3f60a..bffbe55 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,64 +1,42 @@ -#---------------------------------# -# environment configuration # -#---------------------------------# +#---------------------------------# +# environment configuration # +#---------------------------------# + version: 1.2.{build}.0 -install: +environment: + gallery_api: + secure: 9ekJzfsPCDBkyLrfmov83XbbhZ6E2N3z+B/Io8NbDetbHc6hWS19zsDmy7t0Vvxv + +install: - git clone https://github.com/PowerShell/DscResource.Tests - - ps: | - Import-Module -Name .\DscResource.Tests\TestHelper.psm1 -Force - Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force - Install-Module -Name Pester -Repository PSGallery -Force + - ps: Write-Verbose -Message "PowerShell version $($PSVersionTable.PSVersion)" -Verbose + - ps: Import-Module -Name "$env:APPVEYOR_BUILD_FOLDER\DscResource.Tests\AppVeyor.psm1" + - ps: Invoke-AppveyorInstallTask -#---------------------------------# -# build configuration # -#---------------------------------# +#---------------------------------# +# build configuration # +#---------------------------------# build: false -#---------------------------------# -# test configuration # -#---------------------------------# +#---------------------------------# +# test configuration # +#---------------------------------# test_script: - ps: | - $testResultsFile = ".\TestsResults.xml" - $res = Invoke-Pester -OutputFormat NUnitXml -OutputFile $testResultsFile -PassThru - (New-Object 'System.Net.WebClient').UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path $testResultsFile)) - if ($res.FailedCount -gt 0) { - throw "$($res.FailedCount) tests failed." - } - -#---------------------------------# -# deployment configuration # -#---------------------------------# - -# scripts to run before deployment -deploy_script: - - ps: | - # Creating project artifact - $stagingDirectory = (Resolve-Path ..).Path - $manifest = Join-Path $pwd "xSystemSecurity.psd1" - (Get-Content $manifest -Raw).Replace("1.2.0.0", $env:APPVEYOR_BUILD_VERSION) | Out-File $manifest - $zipFilePath = Join-Path $stagingDirectory "$(Split-Path $pwd -Leaf).zip" - Add-Type -assemblyname System.IO.Compression.FileSystem - [System.IO.Compression.ZipFile]::CreateFromDirectory($pwd, $zipFilePath) - - # Creating NuGet package artifact - New-Nuspec -packageName $env:APPVEYOR_PROJECT_NAME -version $env:APPVEYOR_BUILD_VERSION -author "Microsoft" -owners "Microsoft" -licenseUrl "https://github.com/PowerShell/DscResources/blob/master/LICENSE" -projectUrl "https://github.com/$($env:APPVEYOR_REPO_NAME)" -packageDescription $env:APPVEYOR_PROJECT_NAME -tags "DesiredStateConfiguration DSC DSCResourceKit" -destinationPath . - nuget pack ".\$($env:APPVEYOR_PROJECT_NAME).nuspec" -outputdirectory . - $nuGetPackageName = $env:APPVEYOR_PROJECT_NAME + "." + $env:APPVEYOR_BUILD_VERSION + ".nupkg" - $nuGetPackagePath = (Get-ChildItem $nuGetPackageName).FullName - - @( - # You can add other artifacts here - $zipFilePath, - $nuGetPackagePath - ) | % { - Write-Host "Pushing package $_ as Appveyor artifact" - Push-AppveyorArtifact $_ - } - - + Invoke-AppveyorTestScriptTask -CodeCoverage -CodeCovIo +# scripts to run before deployment +after_test: + - ps: | + Import-Module -Name "$env:APPVEYOR_BUILD_FOLDER\DscResource.Tests\AppVeyor.psm1" + Invoke-AppveyorAfterTestTask +#---------------------------------# +# deployment configuration # +#---------------------------------# +deploy_script: + - ps: | + Invoke-AppVeyorDeployTask From a84f9652eaff2b5960f7efda95085809887cc565 Mon Sep 17 00:00:00 2001 From: Travis Drake Date: Tue, 28 Jan 2020 08:28:00 -0800 Subject: [PATCH 4/5] xFileSystemAccessRule: Fixing enum flag handling and adding better tests (#28) - Corrected xFileSystemAccessRule flag handling so that the DSC resources 'Test' passes correctly. - xFileSystemAccessRule Ensure: Absent with no rights specified will now correctly remove existing ACLs for the specified identity, rather than silently leaving them there. --- .../MSFT_xFileSystemAccessRule.psm1 | 134 +++--- README.md | 2 + ...FileSystemAccessRule.integration.tests.ps1 | 406 ++++++++++++++++-- .../Unit/MSFT_xFileSystemAccessRule.tests.ps1 | 38 +- 4 files changed, 470 insertions(+), 110 deletions(-) diff --git a/DSCResources/MSFT_xFileSystemAccessRule/MSFT_xFileSystemAccessRule.psm1 b/DSCResources/MSFT_xFileSystemAccessRule/MSFT_xFileSystemAccessRule.psm1 index 98263db..5f4ff5a 100644 --- a/DSCResources/MSFT_xFileSystemAccessRule/MSFT_xFileSystemAccessRule.psm1 +++ b/DSCResources/MSFT_xFileSystemAccessRule/MSFT_xFileSystemAccessRule.psm1 @@ -1,17 +1,15 @@ -<# +<# .SYNOPSIS Gets the rights of the specified filesystem object for the specified identity. - .PARAMETER Path The path to the item that should have permissions set. - .PARAMETER Identity The identity to set permissions for. #> function Get-TargetResource { [CmdletBinding()] - [OutputType([System.Collections.Hashtable])] + [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] @@ -26,10 +24,10 @@ function Get-TargetResource $result = @{ Path = $Path Identity = $Identity - Rights = [System.String[]] @() + Rights = [System.string[]] @() IsActiveNode = $true } - + if ( -not ( Test-Path -Path $Path ) ) { $isClusterResource = $false @@ -40,7 +38,7 @@ function Get-TargetResource if ( $msCluster ) { Write-Verbose -Message "$($env:COMPUTERNAME) is a member of the Windows Server Failover Cluster '$($msCluster.Name)'" - + # Is the defined path built off of a known mount point in the cluster? $clusterPartition = Get-CimInstance -Namespace root/MSCluster -ClassName MSCluster_ClusterDiskPartition | Where-Object -FilterScript { @@ -54,13 +52,13 @@ function Get-TargetResource # Get the possible owner nodes for the partition [array]$possibleOwners = $clusterPartition | Get-CimAssociatedInstance -ResultClassName 'MSCluster_Resource' | - Get-CimAssociatedInstance -Association 'MSCluster_ResourceToPossibleOwner' | + Get-CimAssociatedInstance -Association 'MSCluster_ResourceToPossibleOwner' | Select-Object -ExpandProperty Name -Unique - + # Ensure the current node is a possible owner of the drive if ( $possibleOwners -contains $env:COMPUTERNAME ) { - $isClusterResource = $true + $isClusterResource = $true $result.IsActiveNode = $false } else @@ -79,11 +77,23 @@ function Get-TargetResource $acl = Get-Acl -Path $Path $accessRules = $acl.Access - $result.Rights = [System.String[]] @( - $accessRules | - Where-Object -FilterScript { $_.IdentityReference -eq $Identity } | - Select-Object -ExpandProperty FileSystemRights -Unique - ) + # Set works without BUILTIN\, but Get fails (silently) without this logic. + # This is tested by the 'Users' group in the Functional + # Integration test logic, which is actually BUILTIN\USERS per ACLs, however + # this is not obvious to users and results in unexpected functionality + # such as successful SETs, but TEST's that fail every time, so this regex + # workaround for the common windows identifier prefixes makes behavior consistent. + # Local groups are fully qualified with $env:ComputerName\. + $regexEscapedIdentity = [RegEx]::Escape($Identity) + $escapedComputerName = [RegEx]::Escape($ENV:ComputerName) + $regex = "^(NT AUTHORITY|BUILTIN|NT SERVICES|$escapedComputerName)\\$regexEscapedIdentity" + $matchingRules = $accessRules | Where-Object -FilterScript { $_.IdentityReference -eq $Identity -or $_.IdentityReference -match $regex } + if ( $matchingRules ) + { + $result.Rights = @( + ( $matchingRules.FileSystemRights -split ', ' ) | Select-Object -Unique + ) + } } return $result } @@ -91,19 +101,15 @@ function Get-TargetResource <# .SYNOPSIS Sets the rights of the specified filesystem object for the specified identity. - .PARAMETER Path The path to the item that should have permissions set. - .PARAMETER Identity The identity to set permissions for. - + .PARAMETER Rights The permissions to include in this rule. Optional if Ensure is set to value 'Absent'. - .PARAMETER Ensure Present to create the rule, Absent to remove an existing rule. Default value is 'Present'. - .PARAMETER ProcessOnlyOnActiveNode Specifies that the resource will only determine if a change is needed if the target node is the active host of the filesystem object. The user the configuration is run as must have permission to the Windows Server Failover Cluster. Not used in Set-TargetResource. @@ -175,16 +181,16 @@ function Set-TargetResource { throw "No rights were specified for '$Identity' on '$Path'" } - + Write-Verbose -Message "Setting access rules for '$Identity' on '$Path'" $newFileSystemAccessRuleParameters = @{ TypeName = 'System.Security.AccessControl.FileSystemAccessRule' ArgumentList = @( - $Identity, - [System.Security.AccessControl.FileSystemRights]$Rights, - 'ContainerInherit,ObjectInherit', - 'None', + $Identity, + [System.Security.AccessControl.FileSystemRights]$Rights, + 'ContainerInherit,ObjectInherit', + 'None', 'Allow' ) } @@ -213,26 +219,22 @@ function Set-TargetResource <# .SYNOPSIS Tests the rights of the specified filesystem object for the specified identity. - .PARAMETER Path The path to the item that should have permissions set. - .PARAMETER Identity The identity to set permissions for. - + .PARAMETER Rights The permissions to include in this rule. Optional if Ensure is set to value 'Absent'. - .PARAMETER Ensure Present to create the rule, Absent to remove an existing rule. Default value is 'Present'. - .PARAMETER ProcessOnlyOnActiveNode Specifies that the resource will only determine if a change is needed if the target node is the active host of the filesystem object. The user the configuration is run as must have permission to the Windows Server Failover Cluster. #> function Test-TargetResource { [CmdletBinding()] - [OutputType([System.Boolean])] + [OutputType([System.Boolean])] param ( [Parameter(Mandatory = $true)] @@ -276,7 +278,7 @@ function Test-TargetResource [ValidateSet('Present','Absent')] [String] $Ensure = 'Present', - + [Parameter()] [Boolean] $ProcessOnlyOnActiveNode @@ -288,7 +290,7 @@ function Test-TargetResource Path = $Path Identity = $Identity } - + $currentValues = Get-TargetResource @getTargetResourceParameters <# @@ -301,6 +303,7 @@ function Test-TargetResource return $result } + switch ( $Ensure ) { 'Absent' @@ -311,12 +314,40 @@ function Test-TargetResource # Set rights to an empty array $Rights = @() } - - # If the right is defined and currently set, return it - $comparisonResult = Compare-Object -ReferenceObject $Rights -DifferenceObject $currentValues.Rights -ExcludeDifferent -IncludeEqual | - Select-Object -ExpandProperty InputObject + if ( $currentValues.Rights -and (-not $Rights) ) + { + $result = $false + Write-Verbose -Message ( 'Returning false. The identity "{0}" has the rights "{1}" when expected no rights by the Ensure Absent.' -f $Identity,( $currentValues.Rights -join ', ' ) ) + } + elseif ( -not $currentValues.Rights ) + { + $result = $true + Write-Verbose -Message ( 'Returning true. The identity "{0}" has no rights as expected by the Ensure Absent.' -f $Identity) + } + elseif ( $Rights ) # always hit, but just clarifying what the actual case is by filling in the if block + { + foreach ($right in $Rights) + { + $notAllowed = [System.Security.AccessControl.FileSystemRights]$right + + # If any rights that we want to deny are individually a full subset of existing rights... + $currentRightResult = -not ($notAllowed -eq ( $notAllowed -band ([System.Security.AccessControl.FileSystemRights] $currentValues.Rights ) ) ) + + if (-not $currentRightResult) + { + Write-Verbose -Message ( 'Testing right {0} absence: false. The identity "{1}" has the rights "{2}" which include "{0}", which are included in the desired Absent rights "{3}".' -f $notAllowed, $Identity,( $currentValues.Rights -join ', ' ), ($Rights -join ', ') ) + } + else + { + Write-Verbose -Message ( 'Testing right {0} absence: true. The identity "{1}" has the rights "{2}" which do not contain "{0}".' -f $notAllowed, $Identity,( $currentValues.Rights -join ', ' ) ) + } + + $result = $result -and $currentRightResult + } + Write-Verbose -Message ( 'Returning {0}.' -f $result ) + } } - + 'Present' { # Validate the rights parameter was passed @@ -324,19 +355,19 @@ function Test-TargetResource { throw "No rights were specified for '$Identity' on '$Path'" } - - # If the right is defined and missing, return it - $comparisonResult = Compare-Object -ReferenceObject $Rights -DifferenceObject $currentValues.Rights | - Where-Object -FilterScript { $_.SideIndicator -eq '<=' } | - Select-Object -ExpandProperty InputObject - } - } + # This isn't always the same as the input if parts of the input are subset permissions, so pre-cast it. + # For example [System.Security.AccessControl.FileSystemRights]@('Modify', 'Read', 'Write') is actually just 'Modify' within the flagged enum, so test as such to avoid false test failures. + $expected = [System.Security.AccessControl.FileSystemRights]$Rights - # If results were found from the comparison - if ( $comparisonResult.Count -gt 0 ) - { - Write-Verbose -Message ( 'The identity "{0}" has the rights "{1}". The expected rights are "{2}".' -f $Identity,( $currentValues.Rights -join ', ' ),( $Rights -join ', ' ) ) - $result = $false + $result = $false + if ($currentValues.Rights) + { + # At minimum the AND result of the current and expected rights should be the expected rights (allow extra rights, but not missing). + # Otherwise permission flags are missing from the enum. + $result = $expected -eq ($expected -band ([System.Security.AccessControl.FileSystemRights] $currentValues.Rights)) + Write-Verbose -Message ( 'Returning {0}. The identity "{1}" has the rights "{2}". The expected rights are "{3}" (combined from input Rights "{4}").' -f $result, $Identity,( $currentValues.Rights -join ', ' ), $expected,( $Rights -join ', ' ) ) + } + } } return $result @@ -345,7 +376,6 @@ function Test-TargetResource <# .SYNOPSIS Retrieves the access control list from a filesystem object. - .PARAMETER Path The path of the filesystem object to retrieve the ACL from. #> @@ -357,6 +387,6 @@ function Get-AclAccess [String] $Path ) - + return (Get-Item -Path $Path).GetAccessControl('Access') } diff --git a/README.md b/README.md index bbc7814..967d515 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,8 @@ Please refer to [this article](http://technet.microsoft.com/en-us/library/dd8832 * Update appveyor.yml to use the default template. * Added default template files .codecov.yml, .gitattributes, and .gitignore, and .vscode folder. +* Corrected xFileSystemAccessRule flag handling so that the DSC resources 'Test' passes correctly. +* xFileSystemAccessRule Ensure: Absent with no rights specified will now correctly remove existing ACLs for the specified identity, rather than silently leaving them there. ### 1.4.0.0 diff --git a/Tests/Integration/MSFT_xFileSystemAccessRule.integration.tests.ps1 b/Tests/Integration/MSFT_xFileSystemAccessRule.integration.tests.ps1 index 8832762..fdec866 100644 --- a/Tests/Integration/MSFT_xFileSystemAccessRule.integration.tests.ps1 +++ b/Tests/Integration/MSFT_xFileSystemAccessRule.integration.tests.ps1 @@ -1,98 +1,424 @@ -$script:DSCModuleName = 'xSystemSecurity' -$script:DSCResourceName = 'MSFT_xFileSystemAccessRule' +$script:DSCModuleName = 'xSystemSecurity' +$script:DSCResourceName = 'MSFT_xFileSystemAccessRule' + +# Basic integration tests setup +try +{ + $cleanupTestIdentity = $true + #region SETUP + $testIdentity = "xFSAR_Test" + + $foundGroup = Get-LocalGroup -Name $testIdentity -ErrorAction SilentlyContinue + if (-not $foundGroup) + { + # create an empty local group if it doesn't already exist, + # which we will be assigning permissions to a temp folder to in these tests. + try + { + Write-Verbose -Verbose "Trying to create local group '$testIdentity'" + New-LocalGroup -Description "Group for MSFT_xFileSystemAccessRule tests" -Name $testIdentity -ErrorAction 'Stop' + } + catch + { + if ($_ -like "*Access denied.*") + { + # Attempt to use an arbitrary existing group + $cleanupTestIdentity = $false + $testIdentity = 'Users' + Write-Warning "Couldn't create a temporary local group. Instead using '$testIdentity'" + Write-Verbose -Verbose "Using testIdentity '$testIdentity'" + } + else + { + throw "Need to run as administrator" + } + } + } + #endregion SETUP + + Import-Module "$PSScriptRoot\..\..\DSCResources\MSFT_xFileSystemAccessRule\MSFT_xFileSystemAccessRule.psm1" + Describe "MSFT_xFileSystemAccessRule Functional unit tests" { + Context "Test-TargetResource when ACL is absent" { + BeforeAll { + $testRoot = "$TestDrive\xFSAR_TestFolder" + New-Item $testRoot -ItemType Directory -Force -ErrorAction 'Stop' + Set-TargetResource -Verbose -Path "$testRoot" -Identity $testIdentity -Rights @() -Ensure Absent + + # Shouldn't throw when run twice, not necessary for DSC but just verifying my test setup is safe + Set-TargetResource -Verbose -Path "$testRoot" -Identity $testIdentity -Rights @() -Ensure Absent + } + $absentAclTestCases = @( + @{ + Rights = @() + Ensure = 'Absent' + ExpectedResult = $true + Explanation = "Permission for nothing absent should succeed as nothing should be present currently" + }, + @{ + Rights = @("Write") + Ensure = 'Present' + ExpectedResult = $false + Explanation = "Permissions should have been removed" + }, + @{ + Rights = @("Write", "Read") + Ensure = 'Present' + ExpectedResult = $false + Explanation = "Permissions should have been removed" + }, + @{ + Rights = @("Write", "Read") + Ensure = 'Absent' + ExpectedResult = $true + Explanation = "Permissions should have been removed" + }, + @{ + Rights = @("Synchronize") + Ensure = 'Absent' + ExpectedResult = $true + Explanation = "Permissions should have been removed" + }, + @{ + Rights = @("Read") + Ensure = 'Absent' + ExpectedResult = $true + Explanation = "Permissions should have been removed" + } + ) + It 'Returns for Ensure and Rights with no existing rights' -TestCases $absentAclTestCases { + Param( + $Ensure, + $Rights, + $ExpectedResult, + $Explanation + ) + $result = Test-TargetResource -Verbose -Path "$testRoot" -Identity $testIdentity -Rights $Rights -Ensure $Ensure + $result | Should -Be $ExpectedResult -Because $Explanation + } + } + + + Context "Set and Test when multiple permissions including a subset is applied" { + BeforeAll { + $testRoot = "$TestDrive\xFSAR_TestFolder" + New-Item $testRoot -ItemType Directory -Force -ErrorAction 'Stop' + # This should effectively end up as 'Write, ReadAndExecute' + Set-TargetResource -Verbose -Path "$testRoot" -Identity $testIdentity -Rights @("Write", "Read", "ExecuteFile") -Ensure Present + } + $setSubsetReadAndExecuteTests = @( + @{ + Rights = @("Write") + Ensure = 'Present' + ExpectedResult = $true + Explanation = "Permission for write should be added" + }, + @{ + Rights = @("Read") + Ensure = 'Present' + ExpectedResult = $true + Explanation = "Permission for read should be added" + }, + @{ + Rights = @("ReadAndExecute") + Ensure = 'Present' + ExpectedResult = $true + Explanation = "Permission for ReadAndExecute should be added and supported via Flags" + }, + @{ + Rights = @("FullControl") + Ensure = 'Present' + ExpectedResult = $false + Explanation = "Permission for FullControl should NOT exist yet" + }, + @{ + Rights = @("FullControl") + Ensure = 'Absent' + ExpectedResult = $true + Explanation = "Permission for FullControl should NOT be considered to be on the object for the absent so test should pass" + } + ) + It 'Returns for Ensure and Rights with combined multi-rights' -TestCases $setSubsetReadAndExecuteTests { + Param( + $Ensure, + $Rights, + $ExpectedResult, + $Explanation + ) + $result = Test-TargetResource -Verbose -Path "$testRoot" -Identity $testIdentity -Rights $Rights -Ensure $Ensure + $result | Should -Be $ExpectedResult -Because $Explanation + } + } + + Context "Set and Test subsets of a big permission like FullControl" { + BeforeAll { + $testRoot = "$TestDrive\xFSAR_TestFolder" + New-Item $testRoot -ItemType Directory -Force -ErrorAction 'Stop' + Set-TargetResource -Verbose -Path "$testRoot" -Identity $testIdentity -Rights @("FullControl") -Ensure Present + } + $fullControlSubsetTests = @( + @{ + Rights = @("FullControl") + Ensure = 'Present' + ExpectedResult = $true + Explanation = "Permission for FullControl should be added now" + }, + @{ + Rights = @("FullControl") + Ensure = 'Absent' + ExpectedResult = $false + Explanation = "Permission for FullControl absent should fail" + }, + @{ + Rights = @("Modify") + Ensure = 'Absent' + ExpectedResult = $false + Explanation = "Permission for Modify absent should fail as it is encompassed in FullControl" + }, + @{ + Rights = @("Modify") + Ensure = 'Present' + ExpectedResult = $true + Explanation = "Permission for Modify true should succeed as it is encompassed in FullControl" + }, + @{ + Rights = @("Read") + Ensure = 'Absent' + ExpectedResult = $false + Explanation = "Permission for Read absent should fail as it is encompassed in FullControl" + }, + @{ + Rights = @("Read", "Write") + Ensure = 'Absent' + ExpectedResult = $false + Explanation = "Permission for Read and Write absent should fail as both is encompassed in FullControl" + }, + @{ + Rights = @("Read", "Write") + Ensure = 'Present' + ExpectedResult = $true + Explanation = "Permission for Read and Write present should succeed as both are encompassed in FullControl" + }, + @{ + Rights = @("Read", "Write", "ExecuteFile") + Ensure = 'Present' + ExpectedResult = $true + Explanation = "Permission for Read and Write absent should fail as both is encompassed in FullControl" + } + ) + It 'Returns for Ensure and Rights with FullControl existing rights' -TestCases $fullControlSubsetTests { + Param( + $Ensure, + $Rights, + $ExpectedResult, + $Explanation + ) + $result = Test-TargetResource -Verbose -Path "$testRoot" -Identity $testIdentity -Rights $Rights -Ensure $Ensure + $result | Should -Be $ExpectedResult -Because $Explanation + } + } + + Context "Set and Test against an existing multi-flag permission: Read, Write" { + BeforeAll { + $testRoot = "$TestDrive\xFSAR_TestFolder" + New-Item $testRoot -ItemType Directory -Force -ErrorAction 'Stop' + Set-TargetResource -Verbose -Path "$testRoot" -Identity $testIdentity -Rights @() -Ensure Absent + + Set-TargetResource -Verbose -Path "$testRoot" -Identity $testIdentity -Rights @("Read", "Write") -Ensure Present + } + + $existingMultiPermission = @( + @{ + Rights = @() + Ensure = 'Absent' + ExpectedResult = $false + Explanation = "Permission for nothing absent should fail as there are permissions to remove currently" + }, + @{ + Rights = @("Read", "Write") + Ensure = 'Absent' + ExpectedResult = $false + Explanation = "Permission for Read and Write absent should fail as both are present" + }, + @{ + Rights = @("Read", "FullControl") + Ensure = 'Absent' + ExpectedResult = $false + Explanation = "Permission for Read and FullControl absent should fail as Read is present currently even though FullControl is not" + }, + @{ + Rights = @('Read') + Ensure = 'Absent' + ExpectedResult = $false + Explanation = "Permission for Read absent should fail as Read is present currently" + }, + @{ + Rights = @('Read') + Ensure = 'Present' + ExpectedResult = $true + Explanation = "Permission Read is present currently" + }, + @{ + Rights = @('Write') + Ensure = 'Present' + ExpectedResult = $true + Explanation = "Permission Write is present currently" + }, + @{ + Rights = @('Read', 'Write') + Ensure = 'Present' + ExpectedResult = $true + Explanation = "Permission Read and Write are present currently" + }, + @{ + Rights = @('Synchronize') + Ensure = 'Present' + ExpectedResult = $true + Explanation = "Permission Read should have applied Synchronize automatically by the operating system" + } + ) + It 'Returns for Ensure and Rights with Read, Write existing rights' -TestCases $existingMultiPermission { + Param( + $Ensure, + $Rights, + $ExpectedResult, + $Explanation + ) + $result = Test-TargetResource -Verbose -Path "$testRoot" -Identity $testIdentity -Rights $Rights -Ensure $Ensure + $result | Should -Be $ExpectedResult -Because $Explanation + } + } + + Context "Set and Test against a non-existant user" { + BeforeAll { + $testRoot = "$TestDrive\xFSAR_TestFolder" + New-Item $testRoot -ItemType Directory -Force -ErrorAction 'Stop' + Set-TargetResource -Verbose -Path "$testRoot" -Identity $testIdentity -Rights @() -Ensure Absent + + Set-TargetResource -Verbose -Path "$testRoot" -Identity $testIdentity -Rights @("Read", "Write") -Ensure Present + } + + $nonExistantUserPermission = @( + @{ + Rights = @() + Ensure = 'Absent' + ExpectedResult = $true + Explanation = "Permission for unspecified absent on something with no ACLs should succeed" + Identity = "Fake" + }, + @{ + Rights = @("Write") + Ensure = 'Absent' + ExpectedResult = $true + Explanation = "Permission for Write absent on something with no ACLs should succeed" + Identity = "Fake" + }, + @{ + Rights = @("Read") + Ensure = 'Present' + ExpectedResult = $false + Explanation = "Permission for Read present on something that doesn't exist should not pass" + Identity = "Fake" + } + ) + It 'Returns for Ensure and Rights for a non-existent identity' -TestCases $nonExistantUserPermission { + Param( + $Ensure, + $Rights, + $ExpectedResult, + $Explanation, + $Identity + ) + $result = Test-TargetResource -Verbose -Path "$testRoot" -Identity $Identity -Rights $Rights -Ensure $Ensure + $result | Should -Be $ExpectedResult -Because $Explanation + } + } + } +} +finally +{ + if ($cleanupTestIdentity) + { + Get-LocalGroup $testIdentity -ErrorAction 'SilentlyContinue' | Remove-LocalGroup -ErrorAction 'Stop' + } +} + [String] $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'))) ) + (-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\')) + & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) } Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force -$TestEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $script:DSCModuleName ` - -DSCResourceName $script:DSCResourceName ` - -TestType Integration +$initializeTestEnvironmentSplat = @{ + DscResourceName = $script:DSCResourceName + TestType = 'Integration' + DscModuleName = $script:DSCModuleName +} +$TestEnvironment = Initialize-TestEnvironment @initializeTestEnvironmentSplat +New-Item -Path "$env:SystemDrive\SampleFolder" -ItemType Directory try { $ConfigFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:DSCResourceName).config.ps1" . $ConfigFile - New-Item -Path "$env:SystemDrive\SampleFolder" -ItemType Directory - Describe "$($script:DSCResourceName)_Integration" { It 'New rule - Should compile without throwing' { - { - Invoke-Expression -Command "$($script:DSCResourceName)_NewRule -OutputPath `$TestDrive" - } | Should not throw + Invoke-Expression -Command "$($script:DSCResourceName)_NewRule -OutputPath `$TestDrive" } It "New rule - Should apply without throwing" { - { - Start-DscConfiguration -Path $TestDrive ` - -ComputerName localhost -Wait -Verbose -Force - } | Should not throw + Start-DscConfiguration -Path $TestDrive -ComputerName localhost -Wait -Verbose -Force -ErrorAction 'Stop' } It 'New rule - Should be able to call Get-DscConfiguration without throwing' { - { Get-DscConfiguration -Verbose -ErrorAction Stop } | Should Not throw + Get-DscConfiguration -Verbose -ErrorAction Stop } It 'New rule - Should have set the resource and all the parameters should match' { - Test-DscConfiguration -Path $TestDrive | Should Be $true + Test-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' | Should Be $true } It 'Update rule - Should compile without throwing' { - { - Invoke-Expression -Command "$($script:DSCResourceName)_UpdateRule -OutputPath `$TestDrive" - } | Should not throw + Invoke-Expression -Command "$($script:DSCResourceName)_UpdateRule -OutputPath `$TestDrive" -ErrorAction 'Stop' } It "Update rule - Should apply without throwing" { - { - Start-DscConfiguration -Path $TestDrive ` - -ComputerName localhost -Wait -Verbose -Force - } | Should not throw + Start-DscConfiguration -Path $TestDrive -ComputerName localhost -Wait -Verbose -Force -ErrorAction 'Stop' } It 'Update rule - Should be able to call Get-DscConfiguration without throwing' { - { Get-DscConfiguration -Verbose -ErrorAction Stop } | Should Not throw + Get-DscConfiguration -Verbose -ErrorAction Stop } It 'Remove rule - Should have set the resource and all the parameters should match' { - Test-DscConfiguration -Path $TestDrive | Should Be $true + Test-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' | Should Be $true } It 'Remove rule - Should compile without throwing' { - { - Invoke-Expression -Command "$($script:DSCResourceName)_RemoveRule -OutputPath `$TestDrive" - } | Should not throw + Invoke-Expression -Command "$($script:DSCResourceName)_RemoveRule -OutputPath `$TestDrive" -ErrorAction 'Stop' } It "Remove rule - Should apply without throwing" { - { - Start-DscConfiguration -Path $TestDrive ` - -ComputerName localhost -Wait -Verbose -Force - } | Should not throw + Start-DscConfiguration -Path $TestDrive -ComputerName localhost -Wait -Verbose -Force -ErrorAction 'Stop' } It 'Remove rule - Should be able to call Get-DscConfiguration without throwing' { - { Get-DscConfiguration -Verbose -ErrorAction Stop } | Should Not throw + Get-DscConfiguration -Verbose -ErrorAction Stop } It 'New rule - Should have set the resource and all the parameters should match' { - Test-DscConfiguration -Path $TestDrive | Should Be $true + Test-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' | Should Be $true } - - - Remove-Item -Path "$env:SystemDrive\SampleFolder" -Recurse -Force -Confirm:$false } } finally { + Remove-Item -Path "$env:SystemDrive\SampleFolder" -Recurse -Force -Confirm:$false -ErrorAction 'SilentlyContinue' Restore-TestEnvironment -TestEnvironment $TestEnvironment } diff --git a/Tests/Unit/MSFT_xFileSystemAccessRule.tests.ps1 b/Tests/Unit/MSFT_xFileSystemAccessRule.tests.ps1 index c466e80..9768924 100644 --- a/Tests/Unit/MSFT_xFileSystemAccessRule.tests.ps1 +++ b/Tests/Unit/MSFT_xFileSystemAccessRule.tests.ps1 @@ -41,17 +41,19 @@ try $mockIdentity = 'NT AUTHORITY\NETWORK SERVICE' $mockPath = "$($env:SystemDrive)\TestFolder" $mockRights = @('ReadData','WriteAttributes') + # The filesystem doesn't return a string array for ACLs, it returns a bit-flagged [System.Security.AccessControl.FileSystemRights] + $mockRightsResult = [System.Security.AccessControl.FileSystemRights] @('ReadData','WriteAttributes') $mockTestPathResult = $true #endregion Mock Variables #region Cmdlet Mocks - $mockGetAcl = { + $mockGetAcl = { return New-Object -TypeName PsObject | Add-Member -MemberType NoteProperty -Name Access -Value @( New-Object -TypeName PsObject | Add-Member -MemberType NoteProperty -Name IdentityReference -Value $mockIdentity -PassThru | - Add-Member -MemberType NoteProperty -Name FileSystemRights -Value $mockRights -PassThru - ) -PassThru | + Add-Member -MemberType NoteProperty -Name FileSystemRights -Value $mockRightsResult -PassThru + ) -PassThru | Add-Member -MemberType ScriptMethod -Name "SetAccessRule" -Value {} -PassThru | Add-Member -MemberType ScriptMethod -Name "RemoveAccessRule" -Value {} -PassThru } @@ -96,8 +98,8 @@ try Add-Member -MemberType NoteProperty -Name Access -Value @( New-Object -TypeName PsObject | Add-Member -MemberType NoteProperty -Name IdentityReference -Value $mockIdentity -PassThru | - Add-Member -MemberType NoteProperty -Name FileSystemRights -Value $mockRights -PassThru - ) -PassThru | + Add-Member -MemberType NoteProperty -Name FileSystemRights -Value $mockRightsResult -PassThru + ) -PassThru | Add-Member -MemberType ScriptMethod -Name "SetAccessRule" -Value {} -PassThru | Add-Member -MemberType ScriptMethod -Name "RemoveAccessRule" -Value {} -PassThru } -PassThru @@ -161,7 +163,7 @@ try ProcessOnlyOnActiveNode = $false } ) - + $setTargetResourceTestCasesPresent = @( @{ Path = $mockPath @@ -179,7 +181,7 @@ try Rights = $null Ensure = 'Absent' ProcessOnlyOnActiveNode = $false - TestResult = $true + TestResult = $false # Per discussion with Johlju the previous behavior was non-intuitive, and this case implies all ACL permissions should be removed, not a silent pass. ClusterNodes = $mockClusterNodes } @{ @@ -317,7 +319,7 @@ try } } - Context 'When the specified path does not exist' { + Context 'When the specified path does not exist' { BeforeEach { $mockTestPathResult = $false } @@ -384,7 +386,7 @@ try $Ensure, $ProcessOnlyOnActiveNode ) - + $setTargetResourceParameters = @{ Path = $Path Identity = $Identity @@ -411,7 +413,7 @@ try ) $mockTestPathResult = $false - + $setTargetResourceParameters = @{ Path = $Path Identity = $Identity @@ -438,7 +440,7 @@ try $Ensure, $ProcessOnlyOnActiveNode ) - + $setTargetResourceParameters = @{ Path = $Path Identity = $Identity @@ -465,7 +467,7 @@ try ) $mockTestPathResult = $false - + $setTargetResourceParameters = @{ Path = $Path Identity = $Identity @@ -490,7 +492,7 @@ try $Ensure, $ProcessOnlyOnActiveNode ) - + $setTargetResourceParameters = @{ Path = $Path Identity = $Identity @@ -559,7 +561,7 @@ try { $testTargetResourceParameters.Add('Rights',$Rights) } - + Test-TargetResource @testTargetResourceParameters | Should -Be $TestResult Assert-MockCalled -CommandName Get-Acl -Times $assertMockCalledGetAcl -Exactly -Scope It @@ -605,7 +607,7 @@ try Ensure = $Ensure ProcessOnlyOnActiveNode = $ProcessOnlyOnActiveNode } - + Test-TargetResource @testTargetResourceParameters | Should -Be $TestResult Assert-MockCalled -CommandName Get-Acl -Times $assertMockCalledGetAcl -Exactly -Scope It @@ -647,7 +649,7 @@ try Ensure = $Ensure ProcessOnlyOnActiveNode = $ProcessOnlyOnActiveNode } - + { Test-TargetResource @testTargetResourceParameters } | Should -Throw "No rights were specified for '$Identity' on '$Path'" Assert-MockCalled -CommandName Get-Acl -Times $assertMockCalledGetAcl -Exactly -Scope It @@ -657,7 +659,6 @@ try Assert-MockCalled -CommandName Get-CimInstance -Times $assertMockCalledGetCimInstance -Exactly -Scope It -ParameterFilter { $ClassName -eq 'MSCluster_ClusterDiskPartition' } Assert-MockCalled -CommandName Test-Path -Times 1 -Exactly -Scope It } - } } @@ -671,7 +672,7 @@ try $result = Get-AclAccess -Path $mockPath $result.Access[0].IdentityReference | Should -Be $mockIdentity - $result.Access[0].FileSystemRights | Should -Be $mockRights + $result.Access[0].FileSystemRights | Should -Be $mockRightsResult Assert-MockCalled -CommandName Get-Item -Times 1 -Exactly -Scope It } @@ -683,3 +684,4 @@ finally { Invoke-TestCleanup } + From cb584d796e0d77c054be03ce56036cd8e2355602 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Wed, 29 Jan 2020 12:33:14 +0100 Subject: [PATCH 5/5] xSystemSecurity: Added continuous delivery with a new CI pipeline (#30) --- .codecov.yml | 24 -- .gitattributes | 6 + .../ISSUE_TEMPLATE/Problem_with_resource.md | 2 +- .github/PULL_REQUEST_TEMPLATE.md | 22 +- .gitignore | 8 +- .markdownlint.json | 10 + .vscode/analyzersettings.psd1 | 15 +- .vscode/settings.json | 28 +- .vscode/tasks.json | 125 ++++++ CHANGELOG.md | 48 +++ CODE_OF_CONDUCT.md | 3 + CONTRIBUTING.md | 3 + DSCResources/xIEEsc/xIEEsc.psd1 | 44 --- DSCResources/xUAC/xUAC.psd1 | 95 ----- GitVersion.yml | 26 ++ LICENSE | 4 +- README.md | 162 +++++--- RequiredModules.psd1 | 22 ++ Resolve-Dependency.ps1 | 289 ++++++++++++++ Resolve-Dependency.psd1 | 6 + .../MSFT_xFileSystemAccessRule.config.ps1 | 33 -- appveyor.yml | 42 -- azure-pipelines.yml | 194 ++++++++++ build.ps1 | 365 ++++++++++++++++++ build.yaml | 88 +++++ .../MSFT_xFileSystemAccessRule.psm1 | 4 + .../MSFT_xFileSystemAccessRule.schema.mof | 0 source/DSCResources/xIEEsc/xIEEsc.psd1 | 36 ++ .../DSCResources}/xIEEsc/xIEEsc.schema.psm1 | 24 +- source/DSCResources/xUAC/xUAC.psd1 | 33 ++ .../DSCResources}/xUAC/xUAC.schema.psm1 | 66 ++-- source/build.psd1 | 6 + source/en-US/about_xSystemSecurity.help.txt | 28 ++ source/xSystemSecurity.psd1 | 68 ++++ ...FileSystemAccessRule.Integration.Tests.ps1 | 363 +++++++++++++---- .../MSFT_xFileSystemAccessRule.config.ps1 | 105 +++++ .../Unit/MSFT_xFileSystemAccessRule.Tests.ps1 | 328 ++++++++-------- xSystemSecurity.psd1 | 72 ---- 38 files changed, 2111 insertions(+), 686 deletions(-) delete mode 100644 .codecov.yml create mode 100644 .markdownlint.json create mode 100644 .vscode/tasks.json create mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md delete mode 100644 DSCResources/xIEEsc/xIEEsc.psd1 delete mode 100644 DSCResources/xUAC/xUAC.psd1 create mode 100644 GitVersion.yml create mode 100644 RequiredModules.psd1 create mode 100644 Resolve-Dependency.ps1 create mode 100644 Resolve-Dependency.psd1 delete mode 100644 Tests/Integration/MSFT_xFileSystemAccessRule.config.ps1 delete mode 100644 appveyor.yml create mode 100644 azure-pipelines.yml create mode 100644 build.ps1 create mode 100644 build.yaml rename {DSCResources => source/DSCResources}/MSFT_xFileSystemAccessRule/MSFT_xFileSystemAccessRule.psm1 (99%) rename {DSCResources => source/DSCResources}/MSFT_xFileSystemAccessRule/MSFT_xFileSystemAccessRule.schema.mof (100%) create mode 100644 source/DSCResources/xIEEsc/xIEEsc.psd1 rename {DSCResources => source/DSCResources}/xIEEsc/xIEEsc.schema.psm1 (59%) create mode 100644 source/DSCResources/xUAC/xUAC.psd1 rename {DSCResources => source/DSCResources}/xUAC/xUAC.schema.psm1 (55%) create mode 100644 source/build.psd1 create mode 100644 source/en-US/about_xSystemSecurity.help.txt create mode 100644 source/xSystemSecurity.psd1 rename Tests/Integration/MSFT_xFileSystemAccessRule.integration.tests.ps1 => tests/Integration/MSFT_xFileSystemAccessRule.Integration.Tests.ps1 (59%) create mode 100644 tests/Integration/MSFT_xFileSystemAccessRule.config.ps1 rename Tests/Unit/MSFT_xFileSystemAccessRule.tests.ps1 => tests/Unit/MSFT_xFileSystemAccessRule.Tests.ps1 (74%) delete mode 100644 xSystemSecurity.psd1 diff --git a/.codecov.yml b/.codecov.yml deleted file mode 100644 index 29a05dd..0000000 --- a/.codecov.yml +++ /dev/null @@ -1,24 +0,0 @@ -codecov: - notify: - require_ci_to_pass: no - -comment: - layout: "reach, diff" - behavior: default - -coverage: - range: 50..80 - round: down - precision: 0 - - status: - project: - default: - # Set the overall project code coverage requirement to 70% - target: 70 - patch: - default: - # Set the pull request requirement to not regress overall coverage by more than 5% - # and let codecov.io set the goal for the code changed in the patch. - target: auto - threshold: 5 diff --git a/.gitattributes b/.gitattributes index 5613d53..d49b050 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,8 @@ # Needed for publishing of examples, build worker defaults to core.autocrlf=input. * text eol=crlf + +# Ensure any exe files are treated as binary +*.exe binary +*.jpg binary +*.xl* binary +*.pfx binary diff --git a/.github/ISSUE_TEMPLATE/Problem_with_resource.md b/.github/ISSUE_TEMPLATE/Problem_with_resource.md index 2431f65..159e81c 100644 --- a/.github/ISSUE_TEMPLATE/Problem_with_resource.md +++ b/.github/ISSUE_TEMPLATE/Problem_with_resource.md @@ -54,4 +54,4 @@ about: If you have a problem, bug, or enhancement with a resource in this resour $PSVersionTable --> -#### Version of the DSC module that was used ('dev' if using current dev branch) +#### Version of the DSC module that was used diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 84e1ea8..73031e9 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -14,15 +14,19 @@ #### Pull Request (PR) description #### This Pull Request (PR) fixes the following issues #### Task list @@ -34,14 +38,16 @@ Change to [x] for each task in the task list that applies to your PR. For those task that don't apply to you PR, leave those as is. --> -- [ ] Added an entry under the Unreleased section of the change log in the README.md. - Entry should say what was changed, and how that affects users (if applicable). +- [ ] Added an entry to the change log under the Unreleased section of the + file CHANGELOG.md. Entry should say what was changed and how that + affects users (if applicable), and reference the issue being resolved + (if applicable). - [ ] Resource documentation added/updated in README.md. - [ ] Resource parameter descriptions added/updated in README.md, schema.mof and comment-based help. - [ ] Comment-based help added/updated. - [ ] Localization strings added/updated in all localization files as appropriate. - [ ] Examples appropriately added/updated. -- [ ] Unit tests added/updated. See [DSC Resource Testing Guidelines](https://github.com/PowerShell/DscResources/blob/master/TestsGuidelines.md). -- [ ] Integration tests added/updated (where possible). See [DSC Resource Testing Guidelines](https://github.com/PowerShell/DscResources/blob/master/TestsGuidelines.md). -- [ ] New/changed code adheres to [DSC Resource Style Guidelines](https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md) and [Best Practices](https://github.com/PowerShell/DscResources/blob/master/BestPractices.md). +- [ ] Unit tests added/updated. See [DSC Community Testing Guidelines](https://dsccommunity.org/guidelines/testing-guidelines). +- [ ] Integration tests added/updated (where possible). See [DSC Community Testing Guidelines](https://dsccommunity.org/guidelines/testing-guidelines). +- [ ] New/changed code adheres to [DSC Community Style Guidelines](https://dsccommunity.org/styleguidelines). diff --git a/.gitignore b/.gitignore index 2f5bc1c..3563fb1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,3 @@ -*.suo -*.user -*.coverage +output/ .vs -.psproj -.sln -DSCResource.Tests +.vscode diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..87b7da5 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,10 @@ +{ + "default": true, + "MD029": { + "style": "one" + }, + "MD013": true, + "MD024": false, + "MD034": false, + "no-hard-tabs": true +} diff --git a/.vscode/analyzersettings.psd1 b/.vscode/analyzersettings.psd1 index be415e4..78312d2 100644 --- a/.vscode/analyzersettings.psd1 +++ b/.vscode/analyzersettings.psd1 @@ -1,11 +1,6 @@ @{ - <# - For the custom rules to work, the DscResource.Tests repo must be - cloned. It is automatically clone as soon as any unit or - integration tests are run. - #> - CustomRulePath = '.\DSCResource.Tests\DscResource.AnalyzerRules' - + CustomRulePath = '.\output\RequiredModules\DscResource.AnalyzerRules' + includeDefaultRules = $true IncludeRules = @( # DSC Resource Kit style guideline rules. 'PSAvoidDefaultValueForMandatoryParameter', @@ -43,11 +38,7 @@ 'PSUseDeclaredVarsMoreThanAssignments', 'PSUsePSCredentialType', - <# - This is to test all the DSC Resource Kit custom rules. - The name of the function-blocks of each custom rule start - with 'Measure*'. - #> 'Measure-*' ) + } diff --git a/.vscode/settings.json b/.vscode/settings.json index 0969e57..a060627 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,14 +1,38 @@ { "powershell.codeFormatting.openBraceOnSameLine": false, - "powershell.codeFormatting.newLineAfterOpenBrace": false, + "powershell.codeFormatting.newLineAfterOpenBrace": true, "powershell.codeFormatting.newLineAfterCloseBrace": true, "powershell.codeFormatting.whitespaceBeforeOpenBrace": true, "powershell.codeFormatting.whitespaceBeforeOpenParen": true, "powershell.codeFormatting.whitespaceAroundOperator": true, "powershell.codeFormatting.whitespaceAfterSeparator": true, "powershell.codeFormatting.ignoreOneLineBlock": false, + "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationAfterEveryPipeline", "powershell.codeFormatting.preset": "Custom", + "powershell.codeFormatting.alignPropertyValuePairs": true, "files.trimTrailingWhitespace": true, "files.insertFinalNewline": true, - "powershell.scriptAnalysis.settingsPath": ".vscode\\analyzersettings.psd1" + "powershell.scriptAnalysis.settingsPath": ".vscode\\analyzersettings.psd1", + "powershell.scriptAnalysis.enable": true, + "files.associations": { + "*.ps1xml": "xml" + }, + "cSpell.words": [ + "COMPANYNAME", + "ICONURI", + "LICENSEURI", + "PROJECTURI", + "RELEASENOTES", + "buildhelpers", + "endregion", + "gitversion", + "icontains", + "keepachangelog", + "notin", + "pscmdlet", + "steppable" + ], + "[markdown]": { + "files.encoding": "utf8" + } } diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..4085e3e --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "_runner": "terminal", + "windows": { + "options": { + "shell": { + "executable": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command" + ] + } + } + }, + "linux": { + "options": { + "shell": { + "executable": "/usr/bin/pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + } + }, + "osx": { + "options": { + "shell": { + "executable": "/usr/local/bin/pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + } + }, + "tasks": [ + { + "label": "build", + "type": "shell", + "command": "&${cwd}/build.ps1", + "args": ["-AutoRestore"], + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "new", + "clear": false + }, + "runOptions": { + "runOn": "default" + }, + "problemMatcher": [ + { + "owner": "powershell", + "fileLocation": [ + "absolute" + ], + "severity": "error", + "pattern": [ + { + "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", + "message": 1 + }, + { + "regexp": "(.*)", + "code": 1 + }, + { + "regexp": "" + }, + { + "regexp": "^.*,\\s*(.*):\\s*line\\s*(\\d+).*", + "file": 1, + "line": 2 + } + ] + } + ] + }, + { + "label": "test", + "type": "shell", + "command": "&${cwd}/build.ps1", + "args": ["-AutoRestore","-Tasks","test"], + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "dedicated", + "showReuseMessage": true, + "clear": false + }, + "problemMatcher": [ + { + "owner": "powershell", + "fileLocation": [ + "absolute" + ], + "severity": "error", + "pattern": [ + { + "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", + "message": 1 + }, + { + "regexp": "(.*)", + "code": 1 + }, + { + "regexp": "" + }, + { + "regexp": "^.*,\\s*(.*):\\s*line\\s*(\\d+).*", + "file": 1, + "line": 2 + } + ] + } + ] + } + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..380659d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,48 @@ +# Change log for xSystemSecurity + +The format is based on and uses the types of changes according to [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- xSystemSecurity + - Added continuous delivery with a new CI pipeline. + +### Fixed + +- xFileSystemAccessRule + - Corrected flag handling so that the `Test-TargetResource` passes + correctly. + - Using `Ensure = 'Absent'` with no rights specified will now correctly + remove existing ACLs for the specified identity, rather than silently + leaving them there. + - Correctly returns property `Ensure` from the function `Get-TargetResource`. + +## [1.4.0.0] - 2018-06-13 + +- Changes to xFileSystemAccessRule + - Fixed issue when cluster shared disk is not present on the server + ([issue #16](https://github.com/dsccommunity/xSystemSecurity/issues/16)). + [Dan Reist (@randomnote1)](https://github.com/randomnote1) + +### [1.3.0.0] - 2017-12-20 + +- Updated FileSystemACL Set + +### [1.2.0.0] - 2016-09-21 + +- Converted appveyor.yml to install Pester from PSGallery instead of from + Chocolatey. +- Added xFileSystemAccessRule resource + +### [1.1.0.0] - 2015-09-11 + +- Fixed encoding + +### [1.0.0.0] - 2015-04-23 + +- Initial release with the following resources + - xUAC + - xIEEsc diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..d7589dd --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Code of Conduct + +This project has adopted the [DSC Community Code of Conduct](https://dsccommunity.org/code_of_conduct). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..f6895ab --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contributing + +Please check out common DSC Community [contributing guidelines](https://dsccommunity.org/guidelines/contributing). diff --git a/DSCResources/xIEEsc/xIEEsc.psd1 b/DSCResources/xIEEsc/xIEEsc.psd1 deleted file mode 100644 index c1c9c5b..0000000 --- a/DSCResources/xIEEsc/xIEEsc.psd1 +++ /dev/null @@ -1,44 +0,0 @@ -# -# Module manifest for module 'xIEEsc' -# -# Generated on: 3/19/2014 -# - -@{ - -# Script module or binary module file associated with this manifest. -RootModule = 'xIEEsc.schema.psm1' - -# Version number of this module. -ModuleVersion = '1.0' - -# ID used to uniquely identify this module -GUID = '59cdb269-a864-46ce-a1d1-bed25fabe8b6' - -# Author of this module -Author = 'Arun Chandrasekhar' - -# Company or vendor of this module -CompanyName = 'Microsoft' - -# Copyright statement for this module -Copyright = '(c) 2014 Microsoft Corporation. All rights reserved.' - -# Description of the functionality provided by this module -Description = 'Enables or Disables IE Enhanced Security Configuration' - -# Functions to export from this module -FunctionsToExport = '*' - -# Cmdlets to export from this module -CmdletsToExport = '*' - -# Variables to export from this module -VariablesToExport = '*' - -# Aliases to export from this module -AliasesToExport = '*' - -} - - diff --git a/DSCResources/xUAC/xUAC.psd1 b/DSCResources/xUAC/xUAC.psd1 deleted file mode 100644 index 620b393..0000000 --- a/DSCResources/xUAC/xUAC.psd1 +++ /dev/null @@ -1,95 +0,0 @@ -# -# Module manifest for module 'xUAC' -# -# Generated on: 3/19/2014 -# - -@{ - -# Script module or binary module file associated with this manifest. -RootModule = 'xUAC.schema.psm1' - -# Version number of this module. -ModuleVersion = '1.0' - -# ID used to uniquely identify this module -GUID = '26e39b71-70cf-468e-849d-dd85858fb30c' - -# Author of this module -Author = 'Arun Chandrasekhar' - -# Company or vendor of this module -CompanyName = 'Microsoft' - -# Copyright statement for this module -Copyright = '(c) 2014 Microsoft Corporation. All rights reserved.' - -# Description of the functionality provided by this module -# Description = '' - -# Minimum version of the Windows PowerShell engine required by this module -# PowerShellVersion = '' - -# Name of the Windows PowerShell host required by this module -# PowerShellHostName = '' - -# Minimum version of the Windows PowerShell host required by this module -# PowerShellHostVersion = '' - -# Minimum version of Microsoft .NET Framework required by this module -# DotNetFrameworkVersion = '' - -# Minimum version of the common language runtime (CLR) required by this module -# CLRVersion = '' - -# Processor architecture (None, X86, Amd64) required by this module -# ProcessorArchitecture = '' - -# Modules that must be imported into the global environment prior to importing this module -# RequiredModules = @() - -# Assemblies that must be loaded prior to importing this module -# RequiredAssemblies = @() - -# Script files (.ps1) that are run in the caller's environment prior to importing this module. -# ScriptsToProcess = @() - -# Type files (.ps1xml) to be loaded when importing this module -# TypesToProcess = @() - -# Format files (.ps1xml) to be loaded when importing this module -# FormatsToProcess = @() - -# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess -# NestedModules = @() - -# Functions to export from this module -FunctionsToExport = '*' - -# Cmdlets to export from this module -CmdletsToExport = '*' - -# Variables to export from this module -VariablesToExport = '*' - -# Aliases to export from this module -AliasesToExport = '*' - -# List of all modules packaged with this module -# ModuleList = @() - -# List of all files packaged with this module -# FileList = @() - -# Private data to pass to the module specified in RootModule/ModuleToProcess -# PrivateData = '' - -# HelpInfo URI of this module -# HelpInfoURI = '' - -# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. -# DefaultCommandPrefix = '' - -} - - diff --git a/GitVersion.yml b/GitVersion.yml new file mode 100644 index 0000000..8493c01 --- /dev/null +++ b/GitVersion.yml @@ -0,0 +1,26 @@ +mode: ContinuousDelivery +next-version: 1.5.0 +major-version-bump-message: '\s?(breaking|major|breaking\schange)' +minor-version-bump-message: '\s?(add|feature|minor)' +patch-version-bump-message: '\s?(fix|patch)' +no-bump-message: '\+semver:\s?(none|skip)' +assembly-informational-format: '{NuGetVersionV2}+Sha.{Sha}.Date.{CommitDate}' +branches: + master: + tag: preview + pull-request: + tag: PR + feature: + tag: useBranchName + increment: Minor + regex: f(eature(s)?)?[\/-] + source-branches: ['master'] + hotfix: + tag: fix + increment: Patch + regex: (hot)?fix(es)?[\/-] + source-branches: ['master'] + +ignore: + sha: [] +merge-message-formats: {} diff --git a/LICENSE b/LICENSE index 567fd6a..f4c3ee9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Microsoft Corporation. +Copyright (c) DSC Community contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file +THE SOFTWARE. diff --git a/README.md b/README.md index 967d515..ec54859 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,97 @@ # xSystemSecurity -[![Build status](https://ci.appveyor.com/api/projects/status/u3h1665qqneo98bh/branch/master?svg=true)](https://ci.appveyor.com/project/PowerShell/xsystemsecurity/branch/master) +[![Build Status](https://dev.azure.com/dsccommunity/xSystemSecurity/_apis/build/status/dsccommunity.xSystemSecurity?branchName=master)](https://dev.azure.com/dsccommunity/xSystemSecurity/_build/latest?definitionId={definitionId}&branchName=master) +![Azure DevOps coverage (branch)](https://img.shields.io/azure-devops/coverage/dsccommunity/xSystemSecurity/{definitionId}/master) +[![Azure DevOps tests](https://img.shields.io/azure-devops/tests/dsccommunity/xSystemSecurity/{definitionId}/master)](https://dsccommunity.visualstudio.com/xSystemSecurity/_test/analytics?definitionId={definitionId}&contextType=build) +[![PowerShell Gallery (with prereleases)](https://img.shields.io/powershellgallery/vpre/xSystemSecurity?label=xSystemSecurity%20Preview)](https://www.powershellgallery.com/packages/xSystemSecurity/) +[![PowerShell Gallery](https://img.shields.io/powershellgallery/v/xSystemSecurity?label=xSystemSecurity)](https://www.powershellgallery.com/packages/xSystemSecurity/) -The **xSystemSecurity** module contains the **xUAC** and **xIEEsc** DSC resources for configuring and managing UAC and IE Enhanced Security Configuration. +This module contains DSC resources for configuring and managing computer security. -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. +## Code of Conduct + +This project has adopted this [Code of Conduct](CODE_OF_CONDUCT.md). + +## Releases + +For each merge to the branch `master` a preview release will be +deployed to [PowerShell Gallery](https://www.powershellgallery.com/). +Periodically a release version tag will be pushed which will deploy a +full release to [PowerShell Gallery](https://www.powershellgallery.com/). ## Contributing -Please check out common DSC Resources [contributing guidelines](https://github.com/PowerShell/DscResource.Kit/blob/master/CONTRIBUTING.md). +Please check out common DSC Community [contributing guidelines](https://dsccommunity.org/guidelines/contributing). ## Resources -* **xUAC** handles how and when the User Account Control Windows Prompt shows up or doesn't show up. +* **xUAC** handles how and when the User Account Control Windows Prompt + shows up or doesn't show up. * **xIEEsc** enables or disables IE Enhanced Security Configuration. +* **xFileSystemAccessRule** modifies the rights of file system objects. ### xUAC -* **Setting**: The desired User Account Control Setting: { AlwaysNotify | NotifyChanges | NotifyChangesWithoutDimming | NeverNotify | NeverNotifyAndDisableAll } - * **AlwaysNotify**: You will be notified before programs make changes to your computer or to Windows settings that require the permissions of an administrator. When you're notified, your desktop will be dimmed, and you must either approve or deny the request in the UAC dialog box before you can do anything else on your computer. The dimming of your desktop is referred to as the secure desktop because other programs can't run while it's dimmed. This is the most secure setting. When you are notified, you should carefully read the contents of each dialog box before allowing changes to be made to your computer. - * **NotifyChanges**: You will be notified before programs make changes to your computer that require the permissions of an administrator. You will not be notified if you try to make changes to Windows settings that require the permissions of an administrator. You will be notified if a program outside of Windows tries to make changes to a Windows setting. It's usually safe to allow changes to be made to Windows settings without you being notified. However, certain programs that come with Windows can have commands or data passed to them, and malicious software can take advantage of this by using these programs to install files or change settings on your computer. You should always be careful about which programs you allow to run on your computer. - * **NotifyChangesWithoutDimming**: You will be notified before programs make changes to your computer that require the permissions of an administrator. You will not be notified if you try to make changes to Windows settings that require the permissions of an administrator. You will be notified if a program outside of Windows tries to make changes to a Windows setting. This setting is the same as "NotifyChanges" but you are not notified on the secure desktop. Because the UAC dialog box isn't on the secure desktop with this setting, other programs might be able to interfere with the dialog's visual appearance. This is a small security risk if you already have a malicious program running on your computer. - * **NeverNotify**: You will not be notified before any changes are made to your computer. If you are logged on as an administrator, programs can make changes to your computer without you knowing about it. If you are logged on as a standard user, any changes that require the permissions of an administrator will automatically be denied. If you select this setting, you will need to restart the computer to complete the process of turning off UAC. Once UAC is off, people that log on as administrator will always have the permissions of an administrator. This is the least secure setting. When you set UAC to never notify, you open up your computer to potential security risks. If you set UAC to never notify, you should be careful about which programs you run, because they will have the same access to the computer as you do. This includes reading and making changes to protected system areas, your personal data, saved files, and anything else stored on the computer. Programs will also be able to communicate and transfer information to and from anything your computer connects with, including the Internet. - * **NeverNotifyAndDisableAll**: You will not be notified before any changes are made to your computer. If you are logged on as an administrator, programs can make changes to your computer without you knowing about it. If you are logged on as a standard user, any changes that require the permissions of an administrator will automatically be denied. If you select this setting, you will need to restart the computer to complete the process of turning off UAC. Once UAC is off, people that log on as administrator will always have the permissions of an administrator. This is the least secure setting same as "NeverNotify", but in addition EnableLUA registry key is disabled. EnableLUA controls the behavior of all UAC policy settings for the computer. If you change this policy setting, you must restart your computer. We do not recommend using this setting, but it can be selected for systems that use programs that are not certified for Windows 8, Windows Server 2012, Windows 7 or Windows Server 2008 R2 because they do not support UAC. +* **Setting**: The desired User Account Control Setting: + { AlwaysNotify | NotifyChanges | NotifyChangesWithoutDimming | NeverNotify | + NeverNotifyAndDisableAll } + * **AlwaysNotify**: You will be notified before programs make changes to your + computer or to Windows settings that require the permissions of an administrator. + When you're notified, your desktop will be dimmed, and you must either approve + or deny the request in the UAC dialog box before you can do anything else on + your computer. The dimming of your desktop is referred to as the secure desktop + because other programs can't run while it's dimmed. This is the most secure + setting. When you are notified, you should carefully read the contents of each + dialog box before allowing changes to be made to your computer. + * **NotifyChanges**: You will be notified before programs make changes to your + computer that require the permissions of an administrator. You will not be notified + if you try to make changes to Windows settings that require the permissions of + an administrator. You will be notified if a program outside of Windows tries + to make changes to a Windows setting. It's usually safe to allow changes to be + made to Windows settings without you being notified. However, certain programs + that come with Windows can have commands or data passed to them, and malicious + software can take advantage of this by using these programs to install files + or change settings on your computer. You should always be careful about which + programs you allow to run on your computer. + * **NotifyChangesWithoutDimming**: You will be notified before programs make + changes to your computer that require the permissions of an administrator. + You will not be notified if you try to make changes to Windows settings that + require the permissions of an administrator. You will be notified if a program + outside of Windows tries to make changes to a Windows setting. This setting is + the same as "NotifyChanges" but you are not notified on the secure desktop. + Because the UAC dialog box isn't on the secure desktop with this setting, other + programs might be able to interfere with the dialog's visual appearance. This + is a small security risk if you already have a malicious program running on + your computer. + * **NeverNotify**: You will not be notified before any changes are made to your + computer. If you are logged on as an administrator, programs can make changes + to your computer without you knowing about it. If you are logged on as a + standard user, any changes that require the permissions of an administrator will + automatically be denied. If you select this setting, you will need to restart + the computer to complete the process of turning off UAC. Once UAC is off, people + that log on as administrator will always have the permissions of an administrator. + This is the least secure setting. When you set UAC to never notify, you open + up your computer to potential security risks. If you set UAC to never notify, + you should be careful about which programs you run, because they will have the + same access to the computer as you do. This includes reading and making changes + to protected system areas, your personal data, saved files, and anything else + stored on the computer. Programs will also be able to communicate and transfer + information to and from anything your computer connects with, including the + Internet. + * **NeverNotifyAndDisableAll**: You will not be notified before any changes are + made to your computer. If you are logged on as an administrator, programs can + make changes to your computer without you knowing about it. If you are logged + on as a standard user, any changes that require the permissions of an administrator + will automatically be denied. If you select this setting, you will need to + restart the computer to complete the process of turning off UAC. Once UAC is + off, people that log on as administrator will always have the permissions of + an administrator. This is the least secure setting same as "NeverNotify", but + in addition EnableLUA registry key is disabled. EnableLUA controls the behavior + of all UAC policy settings for the computer. If you change this policy setting, + you must restart your computer. We do not recommend using this setting, but it + can be selected for systems that use programs that are not certified for + Windows 8, Windows Server 2012, Windows 7 or Windows Server 2008 R2 because + they do not support UAC. ### xIEEsc @@ -32,54 +100,37 @@ Please check out common DSC Resources [contributing guidelines](https://github.c ### xFileSystemAccessRule -* **`[String]` Path** _(Key)_: The path to the item that should have permissions set +* **`[String]` Path** _(Key)_: The path to the item that should have + permissions set * **`[String]` Identity** _(Key)_: The identity to set permissions for -* **`[String[]]` Rights** _(Write)_: The permissions to include in this rule. Optional if Ensure is set to value 'Absent'. { ListDirectory | ReadData | WriteData | CreateFiles | CreateDirectories | AppendData | ReadExtendedAttributes | WriteExtendedAttributes | Traverse | ExecuteFile | DeleteSubdirectoriesAndFiles | ReadAttributes | WriteAttributes | Write | Delete | ReadPermissions | Read | ReadAndExecute | Modify | ChangePermissions | TakeOwnership | Synchronize | FullControl } -* **`[String]` Ensure** _(Write)_: Present to create the rule, Absent to remove an existing rule. Default value is 'Present'. { *Present* | Absent } -* **`[Boolean]` ProcessOnlyOnActiveNode** _(Write)_: Specifies that the resource will only determine if a change is needed if the target node is the active host of the filesystem object. The user the configuration is run as must have permission to the Windows Server Failover Cluster. -* **`[Boolean]` IsActiveNode** _(Read)_: Determines if the current node is actively hosting the filesystem object. This will always return $true if ProcessOnlyOnActiveNode is not set or the value of ProcessOnlyOnActiveNode is set to $false. - -Please refer to [this article](http://technet.microsoft.com/en-us/library/dd883248(v=ws.10).aspx) for the effects and security impact of Enhanced Security Configuration. - -## Versions - -### Unreleased - -* Update appveyor.yml to use the default template. -* Added default template files .codecov.yml, .gitattributes, and .gitignore, and - .vscode folder. -* Corrected xFileSystemAccessRule flag handling so that the DSC resources 'Test' passes correctly. -* xFileSystemAccessRule Ensure: Absent with no rights specified will now correctly remove existing ACLs for the specified identity, rather than silently leaving them there. - -### 1.4.0.0 - -* Changes to xFileSystemAccessRule - * Fixed issue when cluster shared disk is not present on the server ([issue #16](https://github.com/PowerShell/xSystemSecurity/issues/16)). [Dan Reist (@randomnote1)](https://github.com/randomnote1) - -### 1.3.0.0 - -* Updated FileSystemACL Set - -### 1.2.0.0 - -* Converted appveyor.yml to install Pester from PSGallery instead of from Chocolatey. -* Added xFileSystemAccessRule resource - -### 1.1.0.0 - -* Fixed encoding - -### 1.0.0.0 - -* Initial release with the following resources - * xUAC - * xIEEsc +* **`[String[]]` Rights** _(Write)_: The permissions to include in this + rule. Optional if Ensure is set to value 'Absent'. { ListDirectory | + ReadData | WriteData | CreateFiles | CreateDirectories | AppendData | + ReadExtendedAttributes | WriteExtendedAttributes | Traverse | ExecuteFile | + DeleteSubdirectoriesAndFiles | ReadAttributes | WriteAttributes | Write | + Delete | ReadPermissions | Read | ReadAndExecute | Modify | ChangePermissions | + TakeOwnership | Synchronize | FullControl } +* **`[String]` Ensure** _(Write)_: Present to create the rule, Absent to + remove an existing rule. Default value is 'Present'. { *Present* | Absent } +* **`[Boolean]` ProcessOnlyOnActiveNode** _(Write)_: Specifies that the resource + will only determine if a change is needed if the target node is the active host + of the filesystem object. The user the configuration is run as must have + permission to the Windows Server Failover Cluster. +* **`[Boolean]` IsActiveNode** _(Read)_: Determines if the current node + is actively hosting the filesystem object. This will always return + $true if ProcessOnlyOnActiveNode is not set or the value of + ProcessOnlyOnActiveNode is set to $false. + +Please refer to [this article](http://technet.microsoft.com/en-us/library/dd883248(v=ws.10).aspx) +for the effects and security impact of Enhanced Security Configuration. ## Examples ### Disable User Account Control -This configuration will never show the UAC prompt and will disable all User Account Control settings. This setting when changed requires a restart of the computer. +This configuration will never show the UAC prompt and will disable all +User Account Control settings. This setting when changed requires a restart +of the computer. ```powershell Configuration NeverNotifyAndDisableAll @@ -118,7 +169,8 @@ Configuration DisableLocalIEEsc ### Sets a permission on a specific folder -This configuration will grant the network service account full control over the directory. +This configuration will grant the network service account full control +over the directory. ```powershell Configuration FullControlExample diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 new file mode 100644 index 0000000..614775b --- /dev/null +++ b/RequiredModules.psd1 @@ -0,0 +1,22 @@ +@{ + PSDependOptions = @{ + AddToPath = $true + Target = 'output\RequiredModules' + Parameters = @{ + Repository = '' + } + } + + InvokeBuild = 'latest' + PSScriptAnalyzer = 'latest' + Pester = 'latest' + Plaster = 'latest' + ModuleBuilder = '1.0.0' + ChangelogManagement = 'latest' + Sampler = 'latest' + MarkdownLinkCheck = 'latest' + 'DscResource.Test' = 'latest' + 'DscResource.AnalyzerRules' = 'latest' + xDscResourceDesigner = 'latest' +} + diff --git a/Resolve-Dependency.ps1 b/Resolve-Dependency.ps1 new file mode 100644 index 0000000..4928e62 --- /dev/null +++ b/Resolve-Dependency.ps1 @@ -0,0 +1,289 @@ +[CmdletBinding()] +param +( + + [Parameter()] + [String] + $DependencyFile = 'RequiredModules.psd1', + + [Parameter()] + [String] + # Path for PSDepend to be bootstrapped and save other dependencies. + # Can also be CurrentUser or AllUsers if you wish to install the modules in such scope + # Default to $PWD.Path/output/modules + $PSDependTarget = (Join-Path $PSScriptRoot './output/RequiredModules'), + + [Parameter()] + [uri] + # URI to use for Proxy when attempting to Bootstrap PackageProvider & PowerShellGet + $Proxy, + + [Parameter()] + # Credential to contact the Proxy when provided + [PSCredential]$ProxyCredential, + + [Parameter()] + [ValidateSet('CurrentUser', 'AllUsers')] + [String] + # Scope to bootstrap the PackageProvider and PSGet if not available + $Scope = 'CurrentUser', + + [Parameter()] + [String] + # Gallery to use when bootstrapping PackageProvider, PSGet and when calling PSDepend (can be overridden in Dependency files) + $Gallery = 'PSGallery', + + [Parameter()] + [PSCredential] + # Credentials to use with the Gallery specified above + $GalleryCredential, + + + [Parameter()] + [switch] + # Allow you to use a locally installed version of PowerShellGet older than 1.6.0 (not recommended, default to $False) + $AllowOldPowerShellGetModule, + + [Parameter()] + [String] + # Allow you to specify a minimum version fo PSDepend, if you're after specific features. + $MinimumPSDependVersion, + + [Parameter()] + [Switch] + $AllowPrerelease, + + [Parameter()] + [Switch] + $WithYAML +) + +# Load Defaults for parameters values from Resolve-Dependency.psd1 if not provided as parameter +try +{ + Write-Verbose -Message "Importing Bootstrap default parameters from '$PSScriptRoot/Resolve-Dependency.psd1'." + $ResolveDependencyDefaults = Import-PowerShellDataFile -Path (Join-Path $PSScriptRoot '.\Resolve-Dependency.psd1' -Resolve -ErrorAction Stop) + $ParameterToDefault = $MyInvocation.MyCommand.ParameterSets.Where{ $_.Name -eq $PSCmdlet.ParameterSetName }.Parameters.Keys + if ($ParameterToDefault.Count -eq 0) + { + $ParameterToDefault = $MyInvocation.MyCommand.Parameters.Keys + } + # Set the parameters available in the Parameter Set, or it's not possible to choose yet, so all parameters are an option + foreach ($ParamName in $ParameterToDefault) + { + if (-Not $PSBoundParameters.Keys.Contains($ParamName) -and $ResolveDependencyDefaults.ContainsKey($ParamName)) + { + Write-Verbose -Message "Setting $ParamName with $($ResolveDependencyDefaults[$ParamName])" + try + { + $variableValue = $ResolveDependencyDefaults[$ParamName] + if ($variableValue -is [string]) + { + $variableValue = $ExecutionContext.InvokeCommand.ExpandString($variableValue) + } + $PSBoundParameters.Add($ParamName, $variableValue) + Set-Variable -Name $ParamName -value $variableValue -Force -ErrorAction SilentlyContinue + } + catch + { + Write-Verbose -Message "Error adding default for $ParamName : $($_.Exception.Message)" + } + } + } +} +catch +{ + Write-Warning -Message "Error attempting to import Bootstrap's default parameters from $(Join-Path $PSScriptRoot '.\Resolve-Dependency.psd1'): $($_.Exception.Message)." +} + +Write-Progress -Activity "Bootstrap:" -PercentComplete 0 -CurrentOperation "NuGet Bootstrap" + +if (!(Get-PackageProvider -Name NuGet -ForceBootstrap -ErrorAction SilentlyContinue)) +{ + $providerBootstrapParams = @{ + Name = 'nuget' + force = $true + ForceBootstrap = $true + ErrorAction = 'Stop' + } + + switch ($PSBoundParameters.Keys) + { + 'Proxy' + { + $providerBootstrapParams.Add('Proxy', $Proxy) + } + 'ProxyCredential' + { + $providerBootstrapParams.Add('ProxyCredential', $ProxyCredential) + } + 'Scope' + { + $providerBootstrapParams.Add('Scope', $Scope) + } + } + + if ($AllowPrerelease) + { + $providerBootstrapParams.Add('AllowPrerelease', $true) + } + + Write-Information "Bootstrap: Installing NuGet Package Provider from the web (Make sure Microsoft addresses/ranges are allowed)" + $null = Install-PackageProvider @providerBootstrapParams + $latestNuGetVersion = (Get-PackageProvider -Name NuGet -ListAvailable | Select-Object -First 1).Version.ToString() + Write-Information "Bootstrap: Importing NuGet Package Provider version $latestNuGetVersion to current session." + $Null = Import-PackageProvider -Name NuGet -RequiredVersion $latestNuGetVersion -Force +} + +Write-Progress -Activity "Bootstrap:" -PercentComplete 10 -CurrentOperation "Ensuring Gallery $Gallery is trusted" + +# Fail if the given PSGallery is not Registered +$Policy = (Get-PSRepository $Gallery -ErrorAction Stop).InstallationPolicy +Set-PSRepository -Name $Gallery -InstallationPolicy Trusted -ErrorAction Ignore +try +{ + Write-Progress -Activity "Bootstrap:" -PercentComplete 25 -CurrentOperation "Checking PowerShellGet" + # Ensure the module is loaded and retrieve the version you have + $PowerShellGetVersion = (Import-Module PowerShellGet -PassThru -ErrorAction SilentlyContinue).Version + + Write-Verbose "Bootstrap: The PowerShellGet version is $PowerShellGetVersion" + # Versions below 1.6.0 are considered old, unreliable & not recommended + if (!$PowerShellGetVersion -or ($PowerShellGetVersion -lt [System.version]'1.6.0' -and !$AllowOldPowerShellGetModule)) + { + Write-Progress -Activity "Bootstrap:" -PercentComplete 40 -CurrentOperation "Installing newer version of PowerShellGet" + $InstallPSGetParam = @{ + Name = 'PowerShellGet' + Force = $True + SkipPublisherCheck = $true + AllowClobber = $true + Scope = $Scope + Repository = $Gallery + } + + switch ($PSBoundParameters.Keys) + { + 'Proxy' + { + $InstallPSGetParam.Add('Proxy', $Proxy) + } + 'ProxyCredential' + { + $InstallPSGetParam.Add('ProxyCredential', $ProxyCredential) + } + 'GalleryCredential' + { + $InstallPSGetParam.Add('Credential', $GalleryCredential) + } + } + + Install-Module @InstallPSGetParam + Remove-Module PowerShellGet -force -ErrorAction SilentlyContinue + Import-Module PowerShellGet -Force + $NewLoadedVersion = (Get-Module PowerShellGet).Version.ToString() + Write-Information "Bootstrap: PowerShellGet version loaded is $NewLoadedVersion" + Write-Progress -Activity "Bootstrap:" -PercentComplete 60 -CurrentOperation "Installing newer version of PowerShellGet" + } + + # Try to import the PSDepend module from the available modules + try + { + $ImportPSDependParam = @{ + Name = 'PSDepend' + ErrorAction = 'Stop' + Force = $true + } + + if ($MinimumPSDependVersion) + { + $ImportPSDependParam.add('MinimumVersion', $MinimumPSDependVersion) + } + $null = Import-Module @ImportPSDependParam + } + catch + { + # PSDepend module not found, installing or saving it + if ($PSDependTarget -in 'CurrentUser', 'AllUsers') + { + Write-Debug "PSDepend module not found. Attempting to install from Gallery $Gallery" + Write-Warning "Installing PSDepend in $PSDependTarget Scope" + $InstallPSDependParam = @{ + Name = 'PSDepend' + Repository = $Gallery + Force = $true + Scope = $PSDependTarget + SkipPublisherCheck = $true + AllowClobber = $true + } + + if ($MinimumPSDependVersion) + { + $InstallPSDependParam.add('MinimumVersion', $MinimumPSDependVersion) + } + + Write-Progress -Activity "Bootstrap:" -PercentComplete 75 -CurrentOperation "Installing PSDepend from $Gallery" + Install-Module @InstallPSDependParam + } + else + { + Write-Debug "PSDepend module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" + $SaveModuleParam = @{ + Name = 'PSDepend' + Repository = $Gallery + Path = $PSDependTarget + } + + if ($MinimumPSDependVersion) + { + $SaveModuleParam.add('MinimumVersion', $MinimumPSDependVersion) + } + + Write-Progress -Activity "Bootstrap:" -PercentComplete 75 -CurrentOperation "Saving & Importing PSDepend from $Gallery to $Scope" + Save-Module @SaveModuleParam + } + } + finally + { + Write-Progress -Activity "Bootstrap:" -PercentComplete 100 -CurrentOperation "Loading PSDepend" + # We should have successfully bootstrapped PSDepend. Fail if not available + Import-Module PSDepend -ErrorAction Stop + } + + if ($WithYAML) + { + if (-Not (Get-Module -ListAvailable -Name 'PowerShell-Yaml')) + { + Write-Verbose "PowerShell-Yaml module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" + $SaveModuleParam = @{ + Name = 'PowerShell-Yaml' + Repository = $Gallery + Path = $PSDependTarget + } + + Save-Module @SaveModuleParam + Import-Module "PowerShell-Yaml" -ErrorAction Stop + } + else + { + Write-Verbose "PowerShell-Yaml is already available" + } + } + + Write-Progress -Activity "PSDepend:" -PercentComplete 0 -CurrentOperation "Restoring Build Dependencies" + if (Test-Path $DependencyFile) + { + $PSDependParams = @{ + Force = $true + Path = $DependencyFile + } + + # TODO: Handle when the Dependency file is in YAML, and -WithYAML is specified + Invoke-PSDepend @PSDependParams + } + Write-Progress -Activity "PSDepend:" -PercentComplete 100 -CurrentOperation "Dependencies restored" -Completed +} +finally +{ + # Reverting the Installation Policy for the given gallery + Set-PSRepository -Name $Gallery -InstallationPolicy $Policy + Write-Verbose "Project Bootstrapped, returning to Invoke-Build" +} diff --git a/Resolve-Dependency.psd1 b/Resolve-Dependency.psd1 new file mode 100644 index 0000000..711cc56 --- /dev/null +++ b/Resolve-Dependency.psd1 @@ -0,0 +1,6 @@ +@{ + Gallery = 'PSGallery' + AllowPrerelease = $false + WithYAML = $true +} + diff --git a/Tests/Integration/MSFT_xFileSystemAccessRule.config.ps1 b/Tests/Integration/MSFT_xFileSystemAccessRule.config.ps1 deleted file mode 100644 index 36dbea2..0000000 --- a/Tests/Integration/MSFT_xFileSystemAccessRule.config.ps1 +++ /dev/null @@ -1,33 +0,0 @@ -configuration MSFT_xFileSystemAccessRule_NewRule { - Import-DscResource -ModuleName 'xSystemSecurity' - node localhost { - xFileSystemAccessRule Integration_Test { - Path = "$($env:SystemDrive)\SampleFolder" - Identity = "NT AUTHORITY\NETWORK SERVICE" - Rights = @("Read","Synchronize") - } - } -} - -configuration MSFT_xFileSystemAccessRule_UpdateRule { - Import-DscResource -ModuleName 'xSystemSecurity' - node localhost { - xFileSystemAccessRule Integration_Test { - Path = "$($env:SystemDrive)\SampleFolder" - Identity = "NT AUTHORITY\NETWORK SERVICE" - Rights = @("FullControl") - } - } -} - -configuration MSFT_xFileSystemAccessRule_RemoveRule { - Import-DscResource -ModuleName 'xSystemSecurity' - node localhost { - xFileSystemAccessRule Integration_Test { - Path = "$($env:SystemDrive)\SampleFolder" - Identity = "NT AUTHORITY\NETWORK SERVICE" - Ensure = "Absent" - } - } -} - diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index bffbe55..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,42 +0,0 @@ -#---------------------------------# -# environment configuration # -#---------------------------------# - -version: 1.2.{build}.0 -environment: - gallery_api: - secure: 9ekJzfsPCDBkyLrfmov83XbbhZ6E2N3z+B/Io8NbDetbHc6hWS19zsDmy7t0Vvxv - -install: - - git clone https://github.com/PowerShell/DscResource.Tests - - ps: Write-Verbose -Message "PowerShell version $($PSVersionTable.PSVersion)" -Verbose - - ps: Import-Module -Name "$env:APPVEYOR_BUILD_FOLDER\DscResource.Tests\AppVeyor.psm1" - - ps: Invoke-AppveyorInstallTask - -#---------------------------------# -# build configuration # -#---------------------------------# - -build: false - -#---------------------------------# -# test configuration # -#---------------------------------# - -test_script: - - ps: | - Invoke-AppveyorTestScriptTask -CodeCoverage -CodeCovIo - -# scripts to run before deployment -after_test: - - ps: | - Import-Module -Name "$env:APPVEYOR_BUILD_FOLDER\DscResource.Tests\AppVeyor.psm1" - Invoke-AppveyorAfterTestTask - -#---------------------------------# -# deployment configuration # -#---------------------------------# - -deploy_script: - - ps: | - Invoke-AppVeyorDeployTask diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..40450e5 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,194 @@ +trigger: + branches: + include: + - master + paths: + exclude: + - CHANGELOG.md + tags: + include: + - "v*" + exclude: + - "*-*" + +stages: + - stage: Build + jobs: + - job: Package_Module + displayName: 'Package Module' + pool: + vmImage: 'ubuntu 16.04' + steps: + - task: GitVersion@5 + name: gitVersion + displayName: 'Evaluate Next Version' + inputs: + runtime: 'core' + configFilePath: 'GitVersion.yml' + - task: PowerShell@2 + name: package + displayName: 'Build & Package Module' + inputs: + filePath: './build.ps1' + arguments: '-ResolveDependency -tasks pack' + pwsh: true + env: + ModuleVersion: $(gitVersion.InformationalVersion) + - task: PublishBuildArtifacts@1 + displayName: 'Publish Build Artifact' + inputs: + pathToPublish: 'output/' + artifactName: 'output' + publishLocation: 'Container' + + - stage: Test + dependsOn: Build + jobs: + - job: Test_HQRM + displayName: 'HQRM' + pool: + vmImage: 'win1803' + timeoutInMinutes: 0 + steps: + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + downloadType: 'single' + artifactName: 'output' + downloadPath: '$(Build.SourcesDirectory)' + - task: PowerShell@2 + name: test + displayName: 'Run HQRM Test' + inputs: + filePath: './build.ps1' + arguments: '-Tasks hqrmtest' + pwsh: false + - task: PublishTestResults@2 + displayName: 'Publish Test Results' + condition: succeededOrFailed() + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: 'output/testResults/NUnit*.xml' + testRunTitle: 'HQRM' + + - job: Test_Unit + displayName: 'Unit' + pool: + vmImage: 'windows-2019' + timeoutInMinutes: 0 + steps: + - powershell: | + $repositoryOwner,$repositoryName = $env:BUILD_REPOSITORY_NAME -split '/' + echo "##vso[task.setvariable variable=RepositoryOwner;isOutput=true]$repositoryOwner" + echo "##vso[task.setvariable variable=RepositoryName;isOutput=true]$repositoryName" + name: dscBuildVariable + displayName: 'Set Environment Variables' + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + downloadType: 'single' + artifactName: 'output' + downloadPath: '$(Build.SourcesDirectory)' + - task: PowerShell@2 + name: test + displayName: 'Run Unit Test' + inputs: + filePath: './build.ps1' + arguments: "-Tasks test -PesterScript 'tests/Unit'" + pwsh: false + - task: PublishTestResults@2 + displayName: 'Publish Test Results' + condition: succeededOrFailed() + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: 'output/testResults/NUnit*.xml' + testRunTitle: 'Unit (Windows Server Core)' + - task: PublishCodeCoverageResults@1 + displayName: 'Publish Code Coverage' + condition: succeededOrFailed() + inputs: + codeCoverageTool: 'JaCoCo' + summaryFileLocation: 'output/testResults/CodeCov*.xml' + pathToSources: '$(Build.SourcesDirectory)/output/$(dscBuildVariable.RepositoryName)' + + - job: Test_Integration + displayName: 'Integration' + pool: + vmImage: 'windows-2019' + timeoutInMinutes: 0 + steps: + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + downloadType: 'single' + artifactName: 'output' + downloadPath: '$(Build.SourcesDirectory)' + - task: PowerShell@2 + name: configureWinRM + displayName: 'Configure WinRM' + inputs: + targetType: 'inline' + script: 'winrm quickconfig -quiet' + pwsh: false + - task: PowerShell@2 + name: test + displayName: 'Run Integration Test' + inputs: + filePath: './build.ps1' + arguments: "-Tasks test -PesterScript 'tests/Integration' -CodeCoverageThreshold 0" + pwsh: false + - task: PublishTestResults@2 + displayName: 'Publish Test Results' + condition: succeededOrFailed() + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: 'output/testResults/NUnit*.xml' + testRunTitle: 'Integration (Windows Server Core)' + + - stage: Deploy + dependsOn: Test + condition: | + and( + succeeded(), + or( + eq(variables['Build.SourceBranch'], 'refs/heads/master'), + startsWith(variables['Build.SourceBranch'], 'refs/tags/') + ), + contains(variables['System.TeamFoundationCollectionUri'], 'dsccommunity') + ) + jobs: + - job: Deploy_Module + displayName: 'Deploy Module' + pool: + vmImage: 'ubuntu 16.04' + steps: + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + downloadType: 'single' + artifactName: 'output' + downloadPath: '$(Build.SourcesDirectory)' + - task: PowerShell@2 + name: publishRelease + displayName: 'Publish Release' + inputs: + filePath: './build.ps1' + arguments: '-tasks publish' + pwsh: true + env: + GitHubToken: $(GitHubToken) + GalleryApiToken: $(GalleryApiToken) + - task: PowerShell@2 + name: sendChangelogPR + displayName: 'Send Changelog PR' + inputs: + filePath: './build.ps1' + arguments: '-tasks Create_ChangeLog_GitHub_PR' + pwsh: true + env: + GitHubToken: $(GitHubToken) + diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..8b20c40 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,365 @@ +<# + +.DESCRIPTION + Bootstrap and build script for PowerShell module pipeline + +#> +[CmdletBinding()] +param +( + [Parameter(Position = 0)] + [string[]]$Tasks = '.', + + [Parameter()] + [String] + $CodeCoverageThreshold = '', + + [Parameter()] + [validateScript( + { Test-Path -Path $_ } + )] + $BuildConfig = './build.yaml', + + [Parameter()] + # A Specific folder to build the artefact into. + $OutputDirectory = 'output', + + [Parameter()] + # Subdirectory name to build the module (under $OutputDirectory) + $BuiltModuleSubdirectory = '', + + # Can be a path (relative to $PSScriptRoot or absolute) to tell Resolve-Dependency & PSDepend where to save the required modules, + # or use CurrentUser, AllUsers to target where to install missing dependencies + # You can override the value for PSDepend in the Build.psd1 build manifest + # This defaults to $OutputDirectory/modules (by default: ./output/modules) + [Parameter()] + $RequiredModulesDirectory = $(Join-Path 'output' 'RequiredModules'), + + [Parameter()] + [string[]] + $PesterScript, + + # Filter which tags to run when invoking Pester tests + # This is used in the Invoke-Pester.pester.build.ps1 tasks + [Parameter()] + [string[]] + $PesterTag, + + # Filter which tags to exclude when invoking Pester tests + # This is used in the Invoke-Pester.pester.build.ps1 tasks + [Parameter()] + [string[]] + $PesterExcludeTag, + + # Filter which tags to run when invoking DSC Resource tests + # This is used in the DscResource.Test.build.ps1 tasks + [Parameter()] + [string[]] + $DscTestTag, + + # Filter which tags to exclude when invoking DSC Resource tests + # This is used in the DscResource.Test.build.ps1 tasks + [Parameter()] + [string[]] + $DscTestExcludeTag, + + [Parameter()] + [Alias('bootstrap')] + [switch]$ResolveDependency, + + [Parameter(DontShow)] + [AllowNull()] + $BuildInfo, + + [Parameter()] + [switch] + $AutoRestore +) + +# The BEGIN block (at the end of this file) handles the Bootstrap of the Environment before Invoke-Build can run the tasks +# if the -ResolveDependency (aka Bootstrap) is specified, the modules are already available, and can be auto loaded + +process +{ + + if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') + { + # Only run the process block through InvokeBuild (Look at the Begin block at the bottom of this script) + return + } + + # Execute the Build Process from the .build.ps1 path. + Push-Location -Path $PSScriptRoot -StackName BeforeBuild + + try + { + Write-Host -ForeGroundColor magenta "[build] Parsing defined tasks" + + # Load Default BuildInfo if not provided as parameter + if (!$PSBoundParameters.ContainsKey('BuildInfo')) + { + try + { + if (Test-Path $BuildConfig) + { + $ConfigFile = (Get-Item -Path $BuildConfig) + Write-Host "[build] Loading Configuration from $ConfigFile" + $BuildInfo = switch -Regex ($ConfigFile.Extension) + { + # Native Support for PSD1 + '\.psd1' + { + Import-PowerShellDataFile -Path $BuildConfig + } + # Support for yaml when module PowerShell-Yaml is available + '\.[yaml|yml]' + { + Import-Module -ErrorAction Stop -Name 'powershell-yaml' + ConvertFrom-Yaml -Yaml (Get-Content -Raw $ConfigFile) + } + # Native Support for JSON and JSONC (by Removing comments) + '\.[json|jsonc]' + { + $JSONC = (Get-Content -Raw -Path $ConfigFile) + $JSON = $JSONC -replace '(?m)\s*//.*?$' -replace '(?ms)/\*.*?\*/' + # This should probably be converted to hashtable for splatting + $JSON | ConvertFrom-Json + } + default + { + Write-Error "Extension '$_' not supported. using @{}" + @{ } + } + } + } + else + { + Write-Host -Object "Configuration file $BuildConfig not found" -ForegroundColor Red + $BuildInfo = @{ } + } + } + catch + { + Write-Host -Object "Error loading Config $ConfigFile.`r`n Are you missing dependencies?" -ForegroundColor Yellow + Write-Host -Object "Make sure you run './build.ps1 -ResolveDependency -tasks noop' to restore the Required modules the first time" -ForegroundColor Yellow + $BuildInfo = @{ } + Write-Error $_.Exception.Message + } + } + + # If the Invoke-Build Task Header is specified in the Build Info, set it + if ($BuildInfo.TaskHeader) + { + Set-BuildHeader ([scriptblock]::Create($BuildInfo.TaskHeader)) + } + + # Import Tasks from modules via their exported aliases when defined in BUild Manifest + # https://github.com/nightroman/Invoke-Build/tree/master/Tasks/Import#example-2-import-from-a-module-with-tasks + if ($BuildInfo.containsKey('ModuleBuildTasks')) + { + foreach ($Module in $BuildInfo['ModuleBuildTasks'].Keys) + { + try + { + Write-Host -ForegroundColor DarkGray -Verbose "Importing tasks from module $Module" + $LoadedModule = Import-Module $Module -PassThru -ErrorAction Stop + foreach ($TaskToExport in $BuildInfo['ModuleBuildTasks'].($Module)) + { + $LoadedModule.ExportedAliases.GetEnumerator().Where{ + # using -like to support wildcard + Write-Host -ForegroundColor DarkGray "`t Loading $($_.Key)..." + $_.Key -like $TaskToExport + }.ForEach{ + # Dot sourcing the Tasks via their exported aliases + . (Get-Alias $_.Key) + } + } + } + catch + { + Write-Host -ForegroundColor Red -Object "Could not load tasks for module $Module." + Write-Error $_ + } + } + } + + # Loading Build Tasks defined in the .build/ folder (will override the ones imported above if same task name) + Get-ChildItem -Path ".build/" -Recurse -Include *.ps1 -ErrorAction Ignore | ForEach-Object { + "Importing file $($_.BaseName)" | Write-Verbose + . $_.FullName + } + + # Synopsis: Empty task, useful to test the bootstrap process + task noop { } + + # Define default task sequence ("."), can be overridden in the $BuildInfo + task . { + Write-Build Yellow "No sequence currently defined for the default task" + } + + # Load Invoke-Build task sequences/workflows from $BuildInfo + Write-Host -ForegroundColor DarkGray "Adding Workflow from configuration:" + foreach ($Workflow in $BuildInfo.BuildWorkflow.keys) + { + Write-Verbose "Creating Build Workflow '$Workflow' with tasks $($BuildInfo.BuildWorkflow.($Workflow) -join ', ')" + $WorkflowItem = $BuildInfo.BuildWorkflow.($Workflow) + if ($WorkflowItem.Trim() -match '^\{(?[\w\W]*)\}$') + { + $WorkflowItem = [ScriptBlock]::Create($Matches['sb']) + } + Write-Host -ForegroundColor DarkGray " +-> $Workflow" + task $Workflow $WorkflowItem + } + + Write-Host -ForeGroundColor magenta "[build] Executing requested workflow: $($Tasks -join ', ')" + + } + finally + { + Pop-Location -StackName BeforeBuild + } +} + +Begin +{ + # Bootstrapping the environment before using Invoke-Build as task runner + + if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') + { + Write-Host -foregroundColor Green "[pre-build] Starting Build Init" + Push-Location $PSScriptRoot -StackName BuildModule + } + + if ($RequiredModulesDirectory -in @('CurrentUser', 'AllUsers')) + { + # Installing modules instead of saving them + Write-Host -foregroundColor Green "[pre-build] Required Modules will be installed for $RequiredModulesDirectory, not saved." + # Tell Resolve-Dependency to use provided scope as the -PSDependTarget if not overridden in Build.psd1 + $PSDependTarget = $RequiredModulesDirectory + } + else + { + if (-Not (Split-Path -IsAbsolute -Path $OutputDirectory)) + { + $OutputDirectory = Join-Path -Path $PSScriptRoot -ChildPath $OutputDirectory + } + + # Resolving the absolute path to save the required modules to + if (-Not (Split-Path -IsAbsolute -Path $RequiredModulesDirectory)) + { + $RequiredModulesDirectory = Join-Path -Path $PSScriptRoot -ChildPath $RequiredModulesDirectory + } + + # Create the output/modules folder if not exists, or resolve the Absolute path otherwise + if (Resolve-Path $RequiredModulesDirectory -ErrorAction SilentlyContinue) + { + Write-Debug "[pre-build] Required Modules path already exist at $RequiredModulesDirectory" + $RequiredModulesPath = Convert-Path $RequiredModulesDirectory + } + else + { + Write-Host -foregroundColor Green "[pre-build] Creating required modules directory $RequiredModulesDirectory." + $RequiredModulesPath = (New-Item -ItemType Directory -Force -Path $RequiredModulesDirectory).FullName + } + + # Prepending $RequiredModulesPath folder to PSModulePath to resolve from this folder FIRST + if ($RequiredModulesDirectory -notIn @('CurrentUser', 'AllUsers') -and + (($Env:PSModulePath -split [io.path]::PathSeparator) -notContains $RequiredModulesDirectory)) + { + Write-Host -foregroundColor Green "[pre-build] Prepending '$RequiredModulesDirectory' folder to PSModulePath" + $Env:PSModulePath = $RequiredModulesDirectory + [io.path]::PathSeparator + $Env:PSModulePath + } + + # Checking if the user should -ResolveDependency + if ((!(Get-Module -ListAvailable powershell-yaml) -or !(Get-Module -ListAvailable InvokeBuild) -or !(Get-Module -ListAvailable PSDepend)) -and !$ResolveDependency) + { + if ($AutoRestore -or !$PSBoundParameters.ContainsKey('Tasks') -or $Tasks -contains 'build') + { + Write-Host -ForegroundColor Yellow "[pre-build] Dependency missing, running './build.ps1 -ResolveDependency -Tasks noop' for you `r`n" + $ResolveDependency = $true + } + else + { + Write-Warning "Some required Modules are missing, make sure you first run with the '-ResolveDependency' parameter." + Write-Warning "Running 'build.ps1 -ResolveDependency -Tasks noop' will pull required modules without running the build task." + } + } + + if ($BuiltModuleSubdirectory) + { + if (-Not (Split-Path -IsAbsolute $BuiltModuleSubdirectory)) + { + $BuildModuleOutput = Join-Path $OutputDirectory $BuiltModuleSubdirectory + } + else + { + $BuildModuleOutput = $BuiltModuleSubdirectory + } + } + else + { + $BuildModuleOutput = $OutputDirectory + } + + # Prepending $BuildModuleOutput folder to PSModulePath to resolve built module from this folder + if (($Env:PSModulePath -split [io.path]::PathSeparator) -notContains $BuildModuleOutput) + { + Write-Host -foregroundColor Green "[pre-build] Prepending '$BuildModuleOutput' folder to PSModulePath" + $Env:PSModulePath = $BuildModuleOutput + [io.path]::PathSeparator + $Env:PSModulePath + } + + # Tell Resolve-Dependency to use $RequiredModulesPath as -PSDependTarget if not overridden in Build.psd1 + $PSDependTarget = $RequiredModulesPath + } + + if ($ResolveDependency) + { + Write-Host -Object "[pre-build] Resolving dependencies." -foregroundColor Green + $ResolveDependencyParams = @{ } + + # If BuildConfig is a Yaml file, bootstrap powershell-yaml via ResolveDependency + if ($BuildConfig -match '\.[yaml|yml]$') + { + $ResolveDependencyParams.add('WithYaml', $True) + } + + $ResolveDependencyAvailableParams = (Get-Command -Name '.\Resolve-Dependency.ps1').parameters.keys + foreach ($CmdParameter in $ResolveDependencyAvailableParams) + { + + # The parameter has been explicitly used for calling the .build.ps1 + if ($MyInvocation.BoundParameters.ContainsKey($CmdParameter)) + { + $ParamValue = $MyInvocation.BoundParameters.ContainsKey($CmdParameter) + Write-Debug " adding $CmdParameter :: $ParamValue [from user-provided parameters to Build.ps1]" + $ResolveDependencyParams.Add($CmdParameter, $ParamValue) + } + # Use defaults parameter value from Build.ps1, if any + else + { + if ($ParamValue = Get-Variable -Name $CmdParameter -ValueOnly -ErrorAction Ignore) + { + Write-Debug " adding $CmdParameter :: $ParamValue [from default Build.ps1 variable]" + $ResolveDependencyParams.add($CmdParameter, $ParamValue) + } + } + } + + Write-Host -foregroundColor Green "[pre-build] Starting bootstrap process." + .\Resolve-Dependency.ps1 @ResolveDependencyParams + } + + if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') + { + Write-Verbose "Bootstrap completed. Handing back to InvokeBuild." + if ($PSBoundParameters.ContainsKey('ResolveDependency')) + { + Write-Verbose "Dependency already resolved. Removing task" + $null = $PSBoundParameters.Remove('ResolveDependency') + } + Write-Host -foregroundColor Green "[build] Starting build with InvokeBuild." + Invoke-Build @PSBoundParameters -Task $Tasks -File $MyInvocation.MyCommand.Path + Pop-Location -StackName BuildModule + return + } +} diff --git a/build.yaml b/build.yaml new file mode 100644 index 0000000..83efd9d --- /dev/null +++ b/build.yaml @@ -0,0 +1,88 @@ +--- +#################################################### +# ModuleBuilder Configuration # +#################################################### +CopyDirectories: + - en-US + - DSCResources +Encoding: UTF8 +VersionedOutputDirectory: true + +#################################################### +# Sampler Pipeline Configuration # +#################################################### +BuildWorkflow: + '.': + - build + - test + + build: + - Clean + - Build_Module_ModuleBuilder + - Build_NestedModules_ModuleBuilder + - Create_changelog_release_output + + pack: + - build + - package_module_nupkg + + hqrmtest: + - DscResource_Tests_Stop_On_Fail + + test: + - Pester_Tests_Stop_On_Fail + - Pester_if_Code_Coverage_Under_Threshold + + publish: + - Publish_release_to_GitHub + - publish_module_to_gallery + + +#################################################### +# PESTER Configuration # +#################################################### + +Pester: + OutputFormat: NUnitXML + ExcludeFromCodeCoverage: + Script: + - tests/Unit + ExcludeTag: + Tag: + CodeCoverageThreshold: 70 +DscTest: + ExcludeTag: + - "Common Tests - Validate Localization" + - "Common Tests - New Error-Level Script Analyzer Rules" + Tag: + ExcludeSourceFile: + - output + ExcludeModuleFile: + + +Resolve-Dependency: + Gallery: '' + AllowPrerelease: false + Verbose: false + +ModuleBuildTasks: + Sampler: + - '*.build.Sampler.ib.tasks' + +TaskHeader: | + param($Path) + "" + "=" * 79 + Write-Build Cyan "`t`t`t$($Task.Name.replace("_"," ").ToUpper())" + Write-Build DarkGray "$(Get-BuildSynopsis $Task)" + "-" * 79 + Write-Build DarkGray " $Path" + Write-Build DarkGray " $($Task.InvocationInfo.ScriptName):$($Task.InvocationInfo.ScriptLineNumber)" + "" +GitHubConfig: + GitHubFilesToAdd: + - 'CHANGELOG.md' + GitHubConfigUserName: dscbot + GitHubConfigUserEmail: dsccommunity@outlook.com + UpdateChangelogOnPrerelease: false + diff --git a/DSCResources/MSFT_xFileSystemAccessRule/MSFT_xFileSystemAccessRule.psm1 b/source/DSCResources/MSFT_xFileSystemAccessRule/MSFT_xFileSystemAccessRule.psm1 similarity index 99% rename from DSCResources/MSFT_xFileSystemAccessRule/MSFT_xFileSystemAccessRule.psm1 rename to source/DSCResources/MSFT_xFileSystemAccessRule/MSFT_xFileSystemAccessRule.psm1 index 5f4ff5a..de1c078 100644 --- a/DSCResources/MSFT_xFileSystemAccessRule/MSFT_xFileSystemAccessRule.psm1 +++ b/source/DSCResources/MSFT_xFileSystemAccessRule/MSFT_xFileSystemAccessRule.psm1 @@ -22,6 +22,7 @@ function Get-TargetResource ) $result = @{ + Ensure = 'Absent' Path = $Path Identity = $Identity Rights = [System.string[]] @() @@ -60,6 +61,7 @@ function Get-TargetResource { $isClusterResource = $true $result.IsActiveNode = $false + $result.Ensure = 'Present' } else { @@ -90,11 +92,13 @@ function Get-TargetResource $matchingRules = $accessRules | Where-Object -FilterScript { $_.IdentityReference -eq $Identity -or $_.IdentityReference -match $regex } if ( $matchingRules ) { + $result.Ensure = 'Present' $result.Rights = @( ( $matchingRules.FileSystemRights -split ', ' ) | Select-Object -Unique ) } } + return $result } diff --git a/DSCResources/MSFT_xFileSystemAccessRule/MSFT_xFileSystemAccessRule.schema.mof b/source/DSCResources/MSFT_xFileSystemAccessRule/MSFT_xFileSystemAccessRule.schema.mof similarity index 100% rename from DSCResources/MSFT_xFileSystemAccessRule/MSFT_xFileSystemAccessRule.schema.mof rename to source/DSCResources/MSFT_xFileSystemAccessRule/MSFT_xFileSystemAccessRule.schema.mof diff --git a/source/DSCResources/xIEEsc/xIEEsc.psd1 b/source/DSCResources/xIEEsc/xIEEsc.psd1 new file mode 100644 index 0000000..b5d6a1d --- /dev/null +++ b/source/DSCResources/xIEEsc/xIEEsc.psd1 @@ -0,0 +1,36 @@ +@{ + # Script module or binary module file associated with this manifest. + RootModule = 'xIEEsc.schema.psm1' + + # Version number of this module. + ModuleVersion = '1.0.0' + + # ID used to uniquely identify this module + GUID = '59cdb269-a864-46ce-a1d1-bed25fabe8b6' + + # Author of this module + Author = 'DSC Community' + + # Company or vendor of this module + CompanyName = 'DSC Community' + + # Copyright statement for this module + Copyright = 'Copyright the DSC Community contributors. All rights reserved.' + + # Description of the functionality provided by this module + Description = 'Enables or Disables IE Enhanced Security Configuration' + + # Functions to export from this module + FunctionsToExport = @() + + # Cmdlets to export from this module + CmdletsToExport = @() + + # Variables to export from this module + VariablesToExport = @() + + # Aliases to export from this module + AliasesToExport = @() +} + + diff --git a/DSCResources/xIEEsc/xIEEsc.schema.psm1 b/source/DSCResources/xIEEsc/xIEEsc.schema.psm1 similarity index 59% rename from DSCResources/xIEEsc/xIEEsc.schema.psm1 rename to source/DSCResources/xIEEsc/xIEEsc.schema.psm1 index d381feb..e08eaee 100644 --- a/DSCResources/xIEEsc/xIEEsc.schema.psm1 +++ b/source/DSCResources/xIEEsc/xIEEsc.schema.psm1 @@ -1,19 +1,19 @@ Configuration xIEEsc -{ +{ param ( - [parameter(Mandatory = $true)] - [ValidateSet("Administrators","Users")] + [Parameter(Mandatory = $true)] + [ValidateSet('Administrators', 'Users')] [System.String] $UserRole, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [System.Boolean] $IsEnabled ) - $key = "" - if ($UserRole -eq "Administrators") + $key = '' + if ($UserRole -eq 'Administrators') { $key = 'HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}' } @@ -24,12 +24,12 @@ Configuration xIEEsc Registry IEEscKey - { - Ensure = "Present" - Key = $key - ValueName = "IsInstalled" + { + Ensure = 'Present' + Key = $key + ValueName = 'IsInstalled' ValueData = [string][int]$IsEnabled - ValueType = "Dword" + ValueType = 'Dword' } } - + diff --git a/source/DSCResources/xUAC/xUAC.psd1 b/source/DSCResources/xUAC/xUAC.psd1 new file mode 100644 index 0000000..ab715e0 --- /dev/null +++ b/source/DSCResources/xUAC/xUAC.psd1 @@ -0,0 +1,33 @@ +@{ + # Script module or binary module file associated with this manifest. + RootModule = 'xUAC.schema.psm1' + + # Version number of this module. + ModuleVersion = '1.0.0' + + # ID used to uniquely identify this module + GUID = '26e39b71-70cf-468e-849d-dd85858fb30c' + + # Author of this module + Author = 'DSC Community' + + # Company or vendor of this module + CompanyName = 'DSC Community' + + # Copyright statement for this module + Copyright = 'Copyright the DSC Community contributors. All rights reserved.' + + # Functions to export from this module + FunctionsToExport = @() + + # Cmdlets to export from this module + CmdletsToExport = @() + + # Variables to export from this module + VariablesToExport = @() + + # Aliases to export from this module + AliasesToExport = @() +} + + diff --git a/DSCResources/xUAC/xUAC.schema.psm1 b/source/DSCResources/xUAC/xUAC.schema.psm1 similarity index 55% rename from DSCResources/xUAC/xUAC.schema.psm1 rename to source/DSCResources/xUAC/xUAC.schema.psm1 index 3466a6b..1e678b5 100644 --- a/DSCResources/xUAC/xUAC.schema.psm1 +++ b/source/DSCResources/xUAC/xUAC.schema.psm1 @@ -2,12 +2,12 @@ Configuration xUac { param ( - [parameter(Mandatory = $true)] - [ValidateSet("AlwaysNotify","NotifyChanges","NotifyChangesWithoutDimming","NeverNotify","NeverNotifyAndDisableAll")] + [Parameter(Mandatory = $true)] + [ValidateSet('AlwaysNotify', 'NotifyChanges', 'NotifyChangesWithoutDimming', 'NeverNotify', 'NeverNotifyAndDisableAll')] [System.String] - $Setting + $Setting ) - + #Initialize variables to default values which is to NotifyChanges. $ConsentPromptBehaviorAdmin = 5 $EnableLua = 1 @@ -15,64 +15,68 @@ Configuration xUac switch ($Setting) { - "AlwaysNotify" + 'AlwaysNotify' { $ConsentPromptBehaviorAdmin = 2 $EnableLua = 1 $PromptOnSecureDesktop = 1 - } - "NotifyChanges" + } + + 'NotifyChanges' { $ConsentPromptBehaviorAdmin = 5 $EnableLua = 1 $PromptOnSecureDesktop = 1 - } - "NotifyChangesWithoutDimming" + } + + 'NotifyChangesWithoutDimming' { $ConsentPromptBehaviorAdmin = 5 $EnableLua = 1 $PromptOnSecureDesktop = 0 - } - "NeverNotify" + } + + 'NeverNotify' { $ConsentPromptBehaviorAdmin = 0 $EnableLua = 1 $PromptOnSecureDesktop = 0 - } - "NeverNotifyAndDisableAll" + } + + 'NeverNotifyAndDisableAll' { $ConsentPromptBehaviorAdmin = 0 $EnableLua = 0 $PromptOnSecureDesktop = 0 - } + } } - $UacKey = "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System" + $UacKey = 'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System' Registry ConsentPromptBehaviorAdmin - { - Ensure = "Present" - Key = $UacKey - ValueName = "ConsentPromptBehaviorAdmin" + { + Ensure = 'Present' + Key = $UacKey + ValueName = 'ConsentPromptBehaviorAdmin' ValueData = [string] $ConsentPromptBehaviorAdmin - ValueType = "Dword" + ValueType = 'Dword' } Registry EnableLua - { - Ensure = "Present" - Key = $UacKey - ValueName = "EnableLUA" + { + Ensure = 'Present' + Key = $UacKey + ValueName = 'EnableLUA' ValueData = [string] $EnableLua - ValueType = "Dword" + ValueType = 'Dword' } Registry PromptOnSecureDesktop - { - Ensure = "Present" - Key = $UacKey - ValueName = "PromptOnSecureDesktop" + { + Ensure = 'Present' + Key = $UacKey + ValueName = 'PromptOnSecureDesktop' ValueData = [string] $PromptOnSecureDesktop - ValueType = "Dword" + ValueType = 'Dword' } } - + diff --git a/source/build.psd1 b/source/build.psd1 new file mode 100644 index 0000000..3efd7d5 --- /dev/null +++ b/source/build.psd1 @@ -0,0 +1,6 @@ +@{ + Path = 'xSystemSecurity.psd1' #or build breaks on Linux +} +# Waiting for ModuleBuilder to do away with this file +# when all parameters are provided to the function + diff --git a/source/en-US/about_xSystemSecurity.help.txt b/source/en-US/about_xSystemSecurity.help.txt new file mode 100644 index 0000000..7dc03e4 --- /dev/null +++ b/source/en-US/about_xSystemSecurity.help.txt @@ -0,0 +1,28 @@ +TOPIC + about_xSystemSecurity + +SHORT DESCRIPTION + The xSystemSecurity module contains DSC resources for configuring and + managing computer security. + +LONG DESCRIPTION + The xSystemSecurity module contains DSC resources for configuring and + managing computer security. + +EXAMPLES + PS C:\> Get-DscResource -Module xSystemSecurity + +NOTE: + Thank you to the DSC Community contributors who contributed to this module by + writing code, sharing opinions, and provided feedback. + +TROUBLESHOOTING NOTE: + Go to the Github repository for read about issues, submit a new issue, and read + about new releases. https://github.com/dsccommunity/xSystemSecurity + +SEE ALSO + - https://github.com/dsccommunity/xSystemSecurity + +KEYWORDS + DSC, DscResource, Security, FileSystem + diff --git a/source/xSystemSecurity.psd1 b/source/xSystemSecurity.psd1 new file mode 100644 index 0000000..08af5a9 --- /dev/null +++ b/source/xSystemSecurity.psd1 @@ -0,0 +1,68 @@ +@{ + # Version number of this module. + moduleVersion = '0.0.1' + + # ID used to uniquely identify this module + GUID = 'e30107af-a22a-48fb-b7bc-7d2b98489ac5' + + # Author of this module + Author = 'DSC Community' + + # Company or vendor of this module + CompanyName = 'DSC Community' + + # Copyright statement for this module + Copyright = 'Copyright the DSC Community contributors. All rights reserved.' + + # Description of the functionality provided by this module + Description = 'This module contains DSC resources for configuring and managing computer security.' + + # Functions to export from this module + FunctionsToExport = @() + + # Cmdlets to export from this module + CmdletsToExport = @() + + # Variables to export from this module + VariablesToExport = @() + + # Aliases to export from this module + AliasesToExport = @() + + DscResourcesToExport = @( + 'xIEEsc' + 'xUAC' + 'xFileSystemAccessRule' + ) + + # Minimum version of the Windows PowerShell engine required by this module + PowerShellVersion = '4.0' + + # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. + PrivateData = @{ + PSData = @{ + Prerelease = '' + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = @('DesiredStateConfiguration', 'DSC', 'DSCResourceKit', 'DSCResource') + + # A URL to the license for this module. + LicenseUri = 'https://github.com/dsccommunity/xSystemSecurity/blob/master/LICENSE' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/dsccommunity/xSystemSecurity' + + # A URL to an icon representing this module. + IconUri = 'https://dsccommunity.org/images/DSC_Logo_300p.png' + + # ReleaseNotes of this module + ReleaseNotes = '' + + } # End of PSData hashtable + + } # End of PrivateData hashtable +} + + + + diff --git a/Tests/Integration/MSFT_xFileSystemAccessRule.integration.tests.ps1 b/tests/Integration/MSFT_xFileSystemAccessRule.Integration.Tests.ps1 similarity index 59% rename from Tests/Integration/MSFT_xFileSystemAccessRule.integration.tests.ps1 rename to tests/Integration/MSFT_xFileSystemAccessRule.Integration.Tests.ps1 index fdec866..94e94c5 100644 --- a/Tests/Integration/MSFT_xFileSystemAccessRule.integration.tests.ps1 +++ b/tests/Integration/MSFT_xFileSystemAccessRule.Integration.Tests.ps1 @@ -1,5 +1,279 @@ -$script:DSCModuleName = 'xSystemSecurity' -$script:DSCResourceName = 'MSFT_xFileSystemAccessRule' +$script:dscModuleName = 'xSystemSecurity' +$script:dscResourceFriendlyName = 'xFileSystemAccessRule' +$script:dscResourceName = "MSFT_$($script:dscResourceFriendlyName)" + +try +{ + Import-Module -Name DscResource.Test -Force -ErrorAction 'Stop' +} +catch [System.IO.FileNotFoundException] +{ + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' +} + +$script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Integration' + +function Wait-ForIdleLcm +{ + [CmdletBinding()] + param () + + while ((Get-DscLocalConfigurationManager).LCMState -ne 'Idle') + { + Write-Verbose -Message 'Waiting for the LCM to become idle' + + Start-Sleep -Seconds 2 + } +} + +try +{ + $configFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dscResourceName).config.ps1" + . $configFile + + Describe "$($script:dscResourceName)_Integration" { + BeforeAll { + $resourceId = "[$($script:dscResourceFriendlyName)]Integration_Test" + + $mockFolderPath1 = "$TestDrive\SampleFolder" + $mockFolderPath2 = "$TestDrive\xFSAR_TestFolder" + + New-Item -Path $mockFolderPath1 -ItemType 'Directory' -Force + New-Item -Path $mockFolderPath2 -ItemType 'Directory' -Force + } + + $configurationName = "$($script:dscResourceName)_Prerequisites_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + } + + Wait-ForIdleLcm + + $configurationName = "$($script:dscResourceName)_NewRule_Config" + + Context ('When using configuration {0}' -f $configurationName) { + BeforeAll { + # The variable $ConfigurationData was dot-sourced above. + $ConfigurationData.AllNodes[0]['Path'] = $mockFolderPath1 + } + + 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 $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Path | Should -Be $ConfigurationData.AllNodes.Path + $resourceCurrentState.Identity | Should -Contain 'NT AUTHORITY\NETWORK SERVICE' + $resourceCurrentState.Rights | Should -Contain 'Read' + $resourceCurrentState.Rights | Should -Contain 'Synchronize' + $resourceCurrentState.IsActiveNode | Should -BeTrue + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + Wait-ForIdleLcm + + $configurationName = "$($script:dscResourceName)_UpdateRule_Config" + + Context ('When using configuration {0}' -f $configurationName) { + BeforeAll { + # The variable $ConfigurationData was dot-sourced above. + $ConfigurationData.AllNodes[0]['Path'] = $mockFolderPath1 + } + + 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 $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Path | Should -Be $ConfigurationData.AllNodes.Path + $resourceCurrentState.Identity | Should -Contain 'NT AUTHORITY\NETWORK SERVICE' + $resourceCurrentState.Rights | Should -Contain 'FullControl' + $resourceCurrentState.IsActiveNode | Should -BeTrue + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + Wait-ForIdleLcm + + $configurationName = "$($script:dscResourceName)_RemoveRule_Config" + + Context ('When using configuration {0}' -f $configurationName) { + BeforeAll { + # The variable $ConfigurationData was dot-sourced above. + $ConfigurationData.AllNodes[0]['Path'] = $mockFolderPath1 + } + + 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 $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Absent' + $resourceCurrentState.Path | Should -Be $ConfigurationData.AllNodes.Path + $resourceCurrentState.Identity | Should -Contain 'NT AUTHORITY\NETWORK SERVICE' + $resourceCurrentState.Rights | Should -BeNullOrEmpty + $resourceCurrentState.IsActiveNode | Should -BeTrue + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dscResourceName)_Cleanup_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + } + } +} +finally +{ + Restore-TestEnvironment -TestEnvironment $script:testEnvironment +} # Basic integration tests setup try @@ -36,7 +310,7 @@ try } #endregion SETUP - Import-Module "$PSScriptRoot\..\..\DSCResources\MSFT_xFileSystemAccessRule\MSFT_xFileSystemAccessRule.psm1" + Import-Module "$PSScriptRoot\..\..\source\DSCResources\MSFT_xFileSystemAccessRule\MSFT_xFileSystemAccessRule.psm1" Describe "MSFT_xFileSystemAccessRule Functional unit tests" { Context "Test-TargetResource when ACL is absent" { BeforeAll { @@ -47,6 +321,7 @@ try # Shouldn't throw when run twice, not necessary for DSC but just verifying my test setup is safe Set-TargetResource -Verbose -Path "$testRoot" -Identity $testIdentity -Rights @() -Ensure Absent } + $absentAclTestCases = @( @{ Rights = @() @@ -85,6 +360,7 @@ try Explanation = "Permissions should have been removed" } ) + It 'Returns for Ensure and Rights with no existing rights' -TestCases $absentAclTestCases { Param( $Ensure, @@ -341,84 +617,3 @@ finally Get-LocalGroup $testIdentity -ErrorAction 'SilentlyContinue' | Remove-LocalGroup -ErrorAction 'Stop' } } - - -[String] $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) -if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` - (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) -{ - & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) -} - -Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force -$initializeTestEnvironmentSplat = @{ - DscResourceName = $script:DSCResourceName - TestType = 'Integration' - DscModuleName = $script:DSCModuleName -} -$TestEnvironment = Initialize-TestEnvironment @initializeTestEnvironmentSplat - -New-Item -Path "$env:SystemDrive\SampleFolder" -ItemType Directory -try -{ - $ConfigFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:DSCResourceName).config.ps1" - . $ConfigFile - - Describe "$($script:DSCResourceName)_Integration" { - - It 'New rule - Should compile without throwing' { - Invoke-Expression -Command "$($script:DSCResourceName)_NewRule -OutputPath `$TestDrive" - } - - It "New rule - Should apply without throwing" { - Start-DscConfiguration -Path $TestDrive -ComputerName localhost -Wait -Verbose -Force -ErrorAction 'Stop' - } - - It 'New rule - Should be able to call Get-DscConfiguration without throwing' { - Get-DscConfiguration -Verbose -ErrorAction Stop - } - - It 'New rule - Should have set the resource and all the parameters should match' { - Test-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' | Should Be $true - } - - - It 'Update rule - Should compile without throwing' { - Invoke-Expression -Command "$($script:DSCResourceName)_UpdateRule -OutputPath `$TestDrive" -ErrorAction 'Stop' - } - - It "Update rule - Should apply without throwing" { - Start-DscConfiguration -Path $TestDrive -ComputerName localhost -Wait -Verbose -Force -ErrorAction 'Stop' - } - - - It 'Update rule - Should be able to call Get-DscConfiguration without throwing' { - Get-DscConfiguration -Verbose -ErrorAction Stop - } - - It 'Remove rule - Should have set the resource and all the parameters should match' { - Test-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' | Should Be $true - } - - It 'Remove rule - Should compile without throwing' { - Invoke-Expression -Command "$($script:DSCResourceName)_RemoveRule -OutputPath `$TestDrive" -ErrorAction 'Stop' - } - - It "Remove rule - Should apply without throwing" { - Start-DscConfiguration -Path $TestDrive -ComputerName localhost -Wait -Verbose -Force -ErrorAction 'Stop' - } - - It 'Remove rule - Should be able to call Get-DscConfiguration without throwing' { - Get-DscConfiguration -Verbose -ErrorAction Stop - } - - It 'New rule - Should have set the resource and all the parameters should match' { - Test-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' | Should Be $true - } - } -} -finally -{ - Remove-Item -Path "$env:SystemDrive\SampleFolder" -Recurse -Force -Confirm:$false -ErrorAction 'SilentlyContinue' - Restore-TestEnvironment -TestEnvironment $TestEnvironment -} diff --git a/tests/Integration/MSFT_xFileSystemAccessRule.config.ps1 b/tests/Integration/MSFT_xFileSystemAccessRule.config.ps1 new file mode 100644 index 0000000..d631323 --- /dev/null +++ b/tests/Integration/MSFT_xFileSystemAccessRule.config.ps1 @@ -0,0 +1,105 @@ +$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' + + <# + Path is set to a correct value in the configuration prior to + running each configuration. + #> + Path = '' + + # Local group temporarily created for testing. + LocalGroupName = 'FSAR_Test' + + CertificateFile = $env:DscPublicCertificatePath + } + ) + } +} + +configuration MSFT_xFileSystemAccessRule_Prerequisites_Config +{ + Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + + node localhost + { + Group 'AddLocalGroup' + { + Ensure = 'Present' + GroupName = $Node.LocalGroupName + Description = 'Group for MSFT_xFileSystemAccessRule tests' + } + } +} + +configuration MSFT_xFileSystemAccessRule_NewRule_Config +{ + Import-DscResource -ModuleName 'xSystemSecurity' + + node localhost + { + xFileSystemAccessRule Integration_Test + { + Path = $Node.Path + Identity = 'NT AUTHORITY\NETWORK SERVICE' + Rights = @('Read', 'Synchronize') + } + } +} + +configuration MSFT_xFileSystemAccessRule_UpdateRule_Config +{ + Import-DscResource -ModuleName 'xSystemSecurity' + + node localhost + { + xFileSystemAccessRule Integration_Test + { + Path = $Node.Path + Identity = 'NT AUTHORITY\NETWORK SERVICE' + Rights = @('FullControl') + } + } +} + +configuration MSFT_xFileSystemAccessRule_RemoveRule_Config +{ + Import-DscResource -ModuleName 'xSystemSecurity' + + node localhost + { + xFileSystemAccessRule Integration_Test + { + Path = $Node.Path + Identity = 'NT AUTHORITY\NETWORK SERVICE' + Ensure = 'Absent' + } + } +} + +configuration MSFT_xFileSystemAccessRule_Cleanup_Config +{ + Import-DscResource -ModuleName 'PSDesiredStateConfiguration' + + node localhost + { + Group 'AddLocalGroup' + { + Ensure = 'Absent' + GroupName = $Node.LocalGroupName + Description = 'Group for MSFT_xFileSystemAccessRule tests' + } + } +} diff --git a/Tests/Unit/MSFT_xFileSystemAccessRule.tests.ps1 b/tests/Unit/MSFT_xFileSystemAccessRule.Tests.ps1 similarity index 74% rename from Tests/Unit/MSFT_xFileSystemAccessRule.tests.ps1 rename to tests/Unit/MSFT_xFileSystemAccessRule.Tests.ps1 index 9768924..96b245c 100644 --- a/Tests/Unit/MSFT_xFileSystemAccessRule.tests.ps1 +++ b/tests/Unit/MSFT_xFileSystemAccessRule.Tests.ps1 @@ -1,36 +1,34 @@ -#region HEADER +$script:dscModuleName = 'xSystemSecurity' +$script:dscResourceName = 'MSFT_xFileSystemAccessRule' -# Unit Test Template Version: 1.2.1 -$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'))) ) +function Invoke-TestSetup { - & 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 'xSystemSecurity' ` - -DSCResourceName 'MSFT_xFileSystemAccessRule' ` - -TestType Unit - -#endregion HEADER + try + { + Import-Module -Name DscResource.Test -Force -ErrorAction 'Stop' + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' + } -function Invoke-TestSetup {} + $script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Unit' +} function Invoke-TestCleanup { - Restore-TestEnvironment -TestEnvironment $TestEnvironment + Restore-TestEnvironment -TestEnvironment $script:testEnvironment } -# Begin Testing +Invoke-TestSetup + try { - Invoke-TestSetup - - InModuleScope 'MSFT_xFileSystemAccessRule' { - + InModuleScope $script:dscResourceName { #region Mock Variables $mockClusterName = 'Cluster1' $mockClusterNodes = @( @@ -40,7 +38,7 @@ try ) $mockIdentity = 'NT AUTHORITY\NETWORK SERVICE' $mockPath = "$($env:SystemDrive)\TestFolder" - $mockRights = @('ReadData','WriteAttributes') + $mockRights = @('ReadData', 'WriteAttributes') # The filesystem doesn't return a string array for ACLs, it returns a bit-flagged [System.Security.AccessControl.FileSystemRights] $mockRightsResult = [System.Security.AccessControl.FileSystemRights] @('ReadData','WriteAttributes') $mockTestPathResult = $true @@ -49,18 +47,18 @@ try #region Cmdlet Mocks $mockGetAcl = { return New-Object -TypeName PsObject | - Add-Member -MemberType NoteProperty -Name Access -Value @( - New-Object -TypeName PsObject | - Add-Member -MemberType NoteProperty -Name IdentityReference -Value $mockIdentity -PassThru | - Add-Member -MemberType NoteProperty -Name FileSystemRights -Value $mockRightsResult -PassThru - ) -PassThru | - Add-Member -MemberType ScriptMethod -Name "SetAccessRule" -Value {} -PassThru | - Add-Member -MemberType ScriptMethod -Name "RemoveAccessRule" -Value {} -PassThru + Add-Member -MemberType NoteProperty -Name Access -Value @( + New-Object -TypeName PsObject | + Add-Member -MemberType NoteProperty -Name IdentityReference -Value $mockIdentity -PassThru | + Add-Member -MemberType NoteProperty -Name FileSystemRights -Value $mockRightsResult -PassThru + ) -PassThru | + Add-Member -MemberType ScriptMethod -Name "SetAccessRule" -Value {} -PassThru | + Add-Member -MemberType ScriptMethod -Name "RemoveAccessRule" -Value {} -PassThru } $mockGetCimAssociatedInstanceMSCluster_Resource = { return @( - New-Object -TypeName Microsoft.Management.Infrastructure.CimInstance -ArgumentList 'MSCluster_Resource','root/MSCluster' | + New-Object -TypeName Microsoft.Management.Infrastructure.CimInstance -ArgumentList 'MSCluster_Resource', 'root/MSCluster' | Add-Member -MemberType NoteProperty -Name Name -Value $env:COMPUTERNAME -PassThru -Force ) } @@ -69,7 +67,7 @@ try return @( $ClusterNodes | ForEach-Object -Process { $node = $_ - New-Object -TypeName Microsoft.Management.Infrastructure.CimInstance -ArgumentList 'MSCluster_ResourceToPossibleOwner','root/MSCluster' | + New-Object -TypeName Microsoft.Management.Infrastructure.CimInstance -ArgumentList 'MSCluster_ResourceToPossibleOwner', 'root/MSCluster' | Add-Member -MemberType NoteProperty -Name Name -Value $node -PassThru -Force } ) @@ -77,32 +75,32 @@ try $mockGetCimInstanceMSCluster_Cluster = { return @( - New-Object -TypeName Microsoft.Management.Infrastructure.CimInstance -ArgumentList 'MSCluster_Cluster','root/MSCluster' | + New-Object -TypeName Microsoft.Management.Infrastructure.CimInstance -ArgumentList 'MSCluster_Cluster', 'root/MSCluster' | Add-Member -MemberType NoteProperty -Name Name -Value $mockClusterName -PassThru -Force ) } - $mockGetCimInstanceMSCluster_ClusterEmpty = {} + $mockGetCimInstanceMSCluster_ClusterEmpty = { } $mockGetCimInstanceMSCluster_ClusterDiskPartition = { return @( - New-Object -TypeName Microsoft.Management.Infrastructure.CimInstance -ArgumentList 'MSCluster_ClusterDiskPartition','root/MSCluster' | + New-Object -TypeName Microsoft.Management.Infrastructure.CimInstance -ArgumentList 'MSCluster_ClusterDiskPartition', 'root/MSCluster' | Add-Member -MemberType NoteProperty -Name MountPoints -Value @($env:SystemDrive) -PassThru -Force ) } $mockGetItem = { return New-Object -TypeName PsObject | - Add-Member -MemberType ScriptMethod -Name GetAccessControl -Value { - return New-Object -TypeName PsObject | - Add-Member -MemberType NoteProperty -Name Access -Value @( - New-Object -TypeName PsObject | - Add-Member -MemberType NoteProperty -Name IdentityReference -Value $mockIdentity -PassThru | - Add-Member -MemberType NoteProperty -Name FileSystemRights -Value $mockRightsResult -PassThru - ) -PassThru | - Add-Member -MemberType ScriptMethod -Name "SetAccessRule" -Value {} -PassThru | - Add-Member -MemberType ScriptMethod -Name "RemoveAccessRule" -Value {} -PassThru - } -PassThru + Add-Member -MemberType ScriptMethod -Name GetAccessControl -Value { + return New-Object -TypeName PsObject | + Add-Member -MemberType NoteProperty -Name Access -Value @( + New-Object -TypeName PsObject | + Add-Member -MemberType NoteProperty -Name IdentityReference -Value $mockIdentity -PassThru | + Add-Member -MemberType NoteProperty -Name FileSystemRights -Value $mockRightsResult -PassThru + ) -PassThru | + Add-Member -MemberType ScriptMethod -Name "SetAccessRule" -Value {} -PassThru | + Add-Member -MemberType ScriptMethod -Name "RemoveAccessRule" -Value {} -PassThru + } -PassThru } $mockTestPath = { @@ -113,149 +111,154 @@ try #region Test Cases $getTargetResourceTestCasesPathExists = @( @{ - Path = $mockPath - Identity = $mockIdentity - RightsResult = $mockRights + Path = $mockPath + Identity = $mockIdentity + RightsResult = $mockRights IsActiveNodeResult = $true - ClusterNodes = $mockClusterNodes + ClusterNodes = $mockClusterNodes } @{ - Path = $mockPath - Identity = 'contoso\bob' - RightsResult = @() + Path = $mockPath + Identity = 'contoso\bob' + RightsResult = @() IsActiveNodeResult = $true - ClusterNodes = $mockClusterNodes + ClusterNodes = $mockClusterNodes } @{ - Path = $mockPath - Identity = $mockIdentity - RightsResult = @() + Path = $mockPath + Identity = $mockIdentity + RightsResult = @() IsActiveNodeResult = $false - ClusterNodes = $mockClusterNodes + ClusterNodes = $mockClusterNodes } ) $getTargetResourceTestCasesPathDoesNotExist = @( @{ - Path = $mockPath - Identity = $mockIdentity - RightsResult = $mockRights - IsActiveNodeResult = $false - ClusterNodes = $mockClusterNodes + Path = $mockPath + Identity = $mockIdentity + RightsResult = $mockRights + IsActiveNodeResult = $false + ClusterNodes = $mockClusterNodes MSCluster_ClusterMock = $mockGetCimInstanceMSCluster_ClusterEmpty } @{ - Path = $mockPath - Identity = $mockIdentity - RightsResult = $mockRights - IsActiveNodeResult = $false - ClusterNodes = ( $mockClusterNodes | Where-Object -FilterScript { $_ -ne $env:COMPUTERNAME } ) + Path = $mockPath + Identity = $mockIdentity + RightsResult = $mockRights + IsActiveNodeResult = $false + ClusterNodes = ( $mockClusterNodes | Where-Object -FilterScript { $_ -ne $env:COMPUTERNAME } ) MSCluster_ClusterMock = $mockGetCimInstanceMSCluster_Cluster } ) $setTargetResourceTestCasesAbsent = @( @{ - Path = $mockPath - Identity = $mockIdentity - Rights = $mockRights - Ensure = 'Absent' + Path = $mockPath + Identity = $mockIdentity + Rights = $mockRights + Ensure = 'Absent' ProcessOnlyOnActiveNode = $false } ) $setTargetResourceTestCasesPresent = @( @{ - Path = $mockPath - Identity = $mockIdentity - Rights = $mockRights - Ensure = 'Present' + Path = $mockPath + Identity = $mockIdentity + Rights = $mockRights + Ensure = 'Present' ProcessOnlyOnActiveNode = $false } ) $testTargetResourceTestCasesAbsent = @( @{ - Path = $mockPath - Identity = $mockIdentity - Rights = $null - Ensure = 'Absent' + Path = $mockPath + Identity = $mockIdentity + Rights = $null + Ensure = 'Absent' ProcessOnlyOnActiveNode = $false - TestResult = $false # Per discussion with Johlju the previous behavior was non-intuitive, and this case implies all ACL permissions should be removed, not a silent pass. - ClusterNodes = $mockClusterNodes + <# + Per discussion with Johlju the previous behavior was non-intuitive, + and this case implies all ACL permissions should be removed, not a + silent pass. + #> + TestResult = $false + ClusterNodes = $mockClusterNodes } @{ - Path = $mockPath - Identity = $mockIdentity - Rights = @('FullControl') - Ensure = 'Absent' + Path = $mockPath + Identity = $mockIdentity + Rights = @('FullControl') + Ensure = 'Absent' ProcessOnlyOnActiveNode = $false - TestResult = $true - ClusterNodes = $mockClusterNodes + TestResult = $true + ClusterNodes = $mockClusterNodes } @{ - Path = $mockPath - Identity = $mockIdentity - Rights = $mockRights - Ensure = 'Absent' + Path = $mockPath + Identity = $mockIdentity + Rights = $mockRights + Ensure = 'Absent' ProcessOnlyOnActiveNode = $false - TestResult = $false - ClusterNodes = $mockClusterNodes + TestResult = $false + ClusterNodes = $mockClusterNodes } @{ - Path = $mockPath - Identity = $mockIdentity - Rights = $mockRights - Ensure = 'Absent' + Path = $mockPath + Identity = $mockIdentity + Rights = $mockRights + Ensure = 'Absent' ProcessOnlyOnActiveNode = $true - TestResult = $true - ClusterNodes = $mockClusterNodes + TestResult = $true + ClusterNodes = $mockClusterNodes } ) $testTargetResourceTestCasesPresent = @( @{ - Path = $mockPath - Identity = $mockIdentity - Rights = @('FullControl') - Ensure = 'Present' + Path = $mockPath + Identity = $mockIdentity + Rights = @('FullControl') + Ensure = 'Present' ProcessOnlyOnActiveNode = $false - TestResult = $false - ClusterNodes = $mockClusterNodes + TestResult = $false + ClusterNodes = $mockClusterNodes } @{ - Path = $mockPath - Identity = $mockIdentity - Rights = $mockRights - Ensure = 'Present' + Path = $mockPath + Identity = $mockIdentity + Rights = $mockRights + Ensure = 'Present' ProcessOnlyOnActiveNode = $false - TestResult = $true - ClusterNodes = $mockClusterNodes + TestResult = $true + ClusterNodes = $mockClusterNodes } @{ - Path = $mockPath - Identity = $mockIdentity - Rights = $mockRights - Ensure = 'Present' + Path = $mockPath + Identity = $mockIdentity + Rights = $mockRights + Ensure = 'Present' ProcessOnlyOnActiveNode = $true - TestResult = $true - ClusterNodes = $mockClusterNodes + TestResult = $true + ClusterNodes = $mockClusterNodes } ) $testTargetResourceTestCasesPresentError = @( @{ - Path = $mockPath - Identity = $mockIdentity - Ensure = 'Present' + Path = $mockPath + Identity = $mockIdentity + Ensure = 'Present' ProcessOnlyOnActiveNode = $false - TestResult = $false - ClusterNodes = $mockClusterNodes + TestResult = $false + ClusterNodes = $mockClusterNodes } ) #endregion Test Cases - Describe 'MSFT_xFileSystemAccessRule\Get-TargetResource' -Tag Get { + Describe 'MSFT_xFileSystemAccessRule\Get-TargetResource' -Tag 'Get' { BeforeAll { Mock -CommandName Get-Acl -MockWith $mockGetAcl -Verifiable Mock -CommandName Get-CimAssociatedInstance -MockWith $mockGetCimAssociatedInstanceMSCluster_Resource -Verifiable -ParameterFilter { $ResultClassName -eq 'MSCluster_Resource' } @@ -299,9 +302,9 @@ try } $getTargetResourceParameters = @{ - Path = $Path + Path = $Path Identity = $Identity - } + } $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters @@ -349,9 +352,9 @@ try } $getTargetResourceParameters = @{ - Path = $Path + Path = $Path Identity = $Identity - } + } { Get-TargetResource @getTargetResourceParameters } | Should -Throw "Unable to get ACL for '$Path' because it does not exist" @@ -365,10 +368,10 @@ try } } - Describe 'MSFT_xFileSystemAccessRule\Set-TargetResource' -Tag Set { + Describe 'MSFT_xFileSystemAccessRule\Set-TargetResource' -Tag 'Set' { BeforeAll { Mock -CommandName Get-Item -MockWith $mockGetItem -Verifiable - Mock -CommandName Set-Acl -MockWith {} -Verifiable + Mock -CommandName Set-Acl -MockWith { } -Verifiable Mock -CommandName Test-Path -MockWith $mockTestPath -Verifiable } @@ -388,10 +391,10 @@ try ) $setTargetResourceParameters = @{ - Path = $Path - Identity = $Identity - Rights = $Rights - Ensure = $Ensure + Path = $Path + Identity = $Identity + Rights = $Rights + Ensure = $Ensure ProcessOnlyOnActiveNode = $ProcessOnlyOnActiveNode } @@ -415,10 +418,10 @@ try $mockTestPathResult = $false $setTargetResourceParameters = @{ - Path = $Path - Identity = $Identity - Rights = $Rights - Ensure = $Ensure + Path = $Path + Identity = $Identity + Rights = $Rights + Ensure = $Ensure ProcessOnlyOnActiveNode = $ProcessOnlyOnActiveNode } @@ -442,10 +445,10 @@ try ) $setTargetResourceParameters = @{ - Path = $Path - Identity = $Identity - Rights = $Rights - Ensure = $Ensure + Path = $Path + Identity = $Identity + Rights = $Rights + Ensure = $Ensure ProcessOnlyOnActiveNode = $ProcessOnlyOnActiveNode } @@ -469,10 +472,10 @@ try $mockTestPathResult = $false $setTargetResourceParameters = @{ - Path = $Path - Identity = $Identity - Rights = $Rights - Ensure = $Ensure + Path = $Path + Identity = $Identity + Rights = $Rights + Ensure = $Ensure ProcessOnlyOnActiveNode = $ProcessOnlyOnActiveNode } @@ -494,9 +497,9 @@ try ) $setTargetResourceParameters = @{ - Path = $Path - Identity = $Identity - Ensure = $Ensure + Path = $Path + Identity = $Identity + Ensure = $Ensure ProcessOnlyOnActiveNode = $ProcessOnlyOnActiveNode } @@ -509,7 +512,7 @@ try } } - Describe 'MSFT_xFileSystemAccessRule\Test-TargetResource' -Tag Test { + Describe 'MSFT_xFileSystemAccessRule\Test-TargetResource' -Tag 'Test' { BeforeAll { Mock -CommandName Get-Acl -MockWith $mockGetAcl -Verifiable Mock -CommandName Get-CimAssociatedInstance -MockWith $mockGetCimAssociatedInstanceMSCluster_Resource -Verifiable -ParameterFilter { $ResultClassName -eq 'MSCluster_Resource' } @@ -551,15 +554,15 @@ try } $testTargetResourceParameters = @{ - Path = $Path - Identity = $Identity - Ensure = $Ensure + Path = $Path + Identity = $Identity + Ensure = $Ensure ProcessOnlyOnActiveNode = $ProcessOnlyOnActiveNode } if ( $Rights ) { - $testTargetResourceParameters.Add('Rights',$Rights) + $testTargetResourceParameters.Add('Rights', $Rights) } Test-TargetResource @testTargetResourceParameters | Should -Be $TestResult @@ -601,10 +604,10 @@ try } $testTargetResourceParameters = @{ - Path = $Path - Identity = $Identity - Rights = $Rights - Ensure = $Ensure + Path = $Path + Identity = $Identity + Rights = $Rights + Ensure = $Ensure ProcessOnlyOnActiveNode = $ProcessOnlyOnActiveNode } @@ -644,9 +647,9 @@ try } $testTargetResourceParameters = @{ - Path = $Path - Identity = $Identity - Ensure = $Ensure + Path = $Path + Identity = $Identity + Ensure = $Ensure ProcessOnlyOnActiveNode = $ProcessOnlyOnActiveNode } @@ -662,7 +665,7 @@ try } } - Describe 'MSFT_xFileSystemAccessRule\Get-AclAccess' -Tag Helper { + Describe 'MSFT_xFileSystemAccessRule\Get-AclAccess' -Tag 'Helper' { BeforeAll { Mock -CommandName Get-Item -MockWith $mockGetItem -Verifiable } @@ -684,4 +687,3 @@ finally { Invoke-TestCleanup } - diff --git a/xSystemSecurity.psd1 b/xSystemSecurity.psd1 deleted file mode 100644 index 3ddf175..0000000 --- a/xSystemSecurity.psd1 +++ /dev/null @@ -1,72 +0,0 @@ -# -# Module manifest for module 'MSFT_xSystemSecurity' -# -# Generated on: 3/19/2014 -# - -@{ - -# Version number of this module. -moduleVersion = '1.4.0.0' - -# ID used to uniquely identify this module -GUID = 'e30107af-a22a-48fb-b7bc-7d2b98489ac5' - -# Author of this module -Author = 'Arun Chandrasekhar' - -# Company or vendor of this module -CompanyName = 'Microsoft' - -# Copyright statement for this module -Copyright = '(c) 2014 Microsoft Corporation. All rights reserved.' - -# Description of the functionality provided by this module -Description = 'Handles Windows related security settings like UAC and IE ESC. xUAC enables or disables the User Account Control prompt, while xIEEsc enables or disables IE Enhanced Security Configuration.' - -# Functions to export from this module -FunctionsToExport = '*' - -# Cmdlets to export from this module -CmdletsToExport = '*' - -# Variables to export from this module -VariablesToExport = '*' - -# Aliases to export from this module -AliasesToExport = '*' - -# Minimum version of the Windows PowerShell engine required by this module -PowerShellVersion = '4.0' - -# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. -PrivateData = @{ - - PSData = @{ - - # Tags applied to this module. These help with module discovery in online galleries. - Tags = @('DesiredStateConfiguration', 'DSC', 'DSCResourceKit', 'DSCResource') - - # A URL to the license for this module. - LicenseUri = 'https://github.com/PowerShell/xSystemSecurity/blob/master/LICENSE' - - # A URL to the main website for this project. - ProjectUri = 'https://github.com/PowerShell/xSystemSecurity' - - # A URL to an icon representing this module. - # IconUri = '' - - # ReleaseNotes of this module - ReleaseNotes = '* Changes to xFileSystemAccessRule - * Fixed issue when cluster shared disk is not present on the server ([issue 16](https://github.com/PowerShell/xSystemSecurity/issues/16)). [Dan Reist (@randomnote1)](https://github.com/randomnote1) - -' - - } # End of PSData hashtable - -} # End of PrivateData hashtable -} - - - -