diff --git a/.MetaTestOptIn.json b/.MetaTestOptIn.json index 7ee31e96a..a1ac19386 100644 --- a/.MetaTestOptIn.json +++ b/.MetaTestOptIn.json @@ -2,5 +2,6 @@ "Common Tests - Validate Module Files", "Common Tests - Validate Script Files", "Common Tests - Validate Example Files", - "Common Tests - Validate Example Files To Be Published" + "Common Tests - Validate Example Files To Be Published", + "Common Tests - Validate Markdown Files" ] diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 84e1ea82f..d28e6a415 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,11 +2,10 @@ Thanks for submitting a Pull Request (PR) to this project. Your contribution to this project is greatly appreciated! - Please prefix the PR title with the resource name, - e.g. 'ResourceName: My short description'. - If this is a breaking change, then also prefix the PR title - with 'BREAKING CHANGE:', - e.g. 'BREAKING CHANGE: ResourceName: My short description'. + Please prefix the PR title with the resource name, e.g. 'ResourceName: My + short description'. If this is a breaking change, then also prefix the PR + title with 'BREAKING CHANGE:', e.g. 'BREAKING CHANGE: ResourceName: My + short description'. You may remove this comment block, and the other comment blocks, but please keep the headers and the task list. @@ -18,9 +17,9 @@ #### This Pull Request (PR) fixes the following issues @@ -34,14 +33,20 @@ 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 under the Unreleased section of the change log in + CHANGELOG.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. +- [ ] 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 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 and Best Practices](https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md) diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..3d0928cd9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,765 @@ +# Versions + +## Unreleased + +- Pull server module publishing + - Removed forced verbose logging from CreateZipFromSource, Publish-DSCModulesAndMof and Publish-MOFToPullServer as it polluted the console +- Corrected GitHub Pull Request template to remove referral to + `BestPractices.MD` which has been combined into `StyleGuidelines.md` + ([issue #520](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/520)). +- xWindowsOptionalFeature + - Suppress useless verbose output from `Import-Module` cmdlet. + ([issue #453](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/453)). +- Changes to xRemoteFile + - Corrected a resource name in the example xRemoteFile_DownloadFileConfig.ps1 +- Fix `MSFT_xDSCWebService` to find + `Microsoft.Powershell.DesiredStateConfiguration.Service.Resources.dll` + when server is configured with pt-BR Locales + ([issue #284](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/284)). +- Changes to xDSCWebService + - Fixed an issue which prevented the removal of the IIS Application Pool + created during deployment of an DSC Pull Server instance. + ([issue #464](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/464)) + - Fixed an issue where a Pull Server cannot be deployed on a machine when IIS + Express is installed aside a full blown IIS + ([issue #191](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/191)) +- Update `CommonResourceHelper` unit tests to meet Pester 4.0.0 + standards + ([issue #473](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/473)). +- Update `ResourceHelper` unit tests to meet Pester 4.0.0 + standards + ([issue #473](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/473)). +- Update `MSFT_xDSCWebService` unit tests to meet Pester 4.0.0 + standards + ([issue #473](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/473)). +- Update `MSFT_xDSCWebService` integration tests to meet Pester 4.0.0 + standards + ([issue #473](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/473)). +- Refactored `MSFT_xDSCWebService` integration tests to meet current + standards and to use Pester TestDrive. +- xArchive + - Fix end-to-end tests + ([issue #457](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/457)). + - Update integration tests to meet Pester 4.0.0 standards. + - Update end-to-end tests to meet Pester 4.0.0 standards. + - Update unit and integration tests to meet Pester 4.0.0 standards. + - Wrapped all path and identifier strings in verbose messages with + quotes to make it easier to identify the limit of the string when + debugging. + - Refactored date/time checksum code to improve testability and ensure + tests can run on machines with localized datetime formats that are not + US. + - Fix 'Get-ArchiveEntryLastWriteTime' to return `[datetime]` + ([issue #471](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/471)). + - Improved verbose logging to make debugging path issues easier. + - Added handling for '/' as a path seperator by backporting code from + PSDscResources - + ([issue #469](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/469)). + - Copied unit tests from + [PSDscResources](https://github.com/PowerShell/PSDscResources). + - Added .gitattributes file and removed git configuration from AppVeyor + to ensure CRLF settings are configured correctly for the repository. +- Updated '.vscode\settings.json' to refer to AnalyzerSettings.psd1 so that + custom syntax problems are highlighted in Visual Studio Code. +- Fixed style guideline violations in `CommonResourceHelper.psm1`. +- Changes to xService + - Fixes issue where Get-TargetResource or Test-TargetResource will throw an + exception if the target service is configured with a non-existent + dependency. + - Refactored Get-TargetResource Unit tests. +- Changes to xPackage + - Fixes an issue where incorrect verbose output was displayed if product + found. + ([issue #446](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/446)) +- Fixes files which are getting triggered for re-encoding after recent pull + request (possibly #472). +- Moves version and change history from README.MD to new file, CHANGELOG.MD. +- Fixes markdown issues in README.MD and HighQualityResourceModulePlan.md. +- Opted in to 'Common Tests - Validate Markdown Files' +- Changes to xPSDesiredStateConfiguration + - In AppVeyor CI the tests are split into three separate jobs, and also + run tests on two different build worker images (Windows Server 2012R2 + and Windows Server 2016). The common tests are only run on the + Windows Server 2016 build worker image. Helps with + [issue #477](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/477). +- xGroup + - Corrected style guideline violations. ([issue #485](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/485)) +- xWindowsProcess + - Corrected style guideline violations. ([issue #496](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/496)) +- Changes to PSWSIISEndpoint.psm1 + - Fixes most PSScriptAnalyzer issues. +- Changes to xRegistry + - Fixed an issue that fails to remove reg key when the `Key` is specified as + common registry path. + ([issue #444](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/444)) +- Changes to xService + - Added support for Group Managed Service Accounts +- Adds new Integration tests for MSFT_xDSCWebService and removes old + Integration test file, MSFT_xDSCWebService.xxx.ps1. +- xRegistry + - Corrected style guideline violations. ([issue #489](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/489)) +- Fixes script analyzer issues in MSFT_xMsiPackage.psm1. + [issue #486](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/486) +- Fixes script analyzer issues in MSFT_xPackageResource.psm1. + [issue #487](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/487) +- Adds spaces between variable types and variables, and changes Type + Accelerators to Fully Qualified Type Names on affected code. +- Fixes script analyzer issues in MSFT_xPSSessionConfiguration.psm1 + and convert Type Accelerators to Fully Qualified Type Names + [issue #488](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/488). +- Adds spaces between array members. +- Fixes script analyzer issues in MSFT_xRemoteFile.psm1 and + correct general style violations. + ([issue #490](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/490)) +- Remove unnecessary whitespace from line endings. +- Add statement to README.md regarding the lack of testing of this module with + PowerShell 4 + [issue #522](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/522). +- Fixes script analyzer issues in MSFT_xWindowsOptionalFeature.psm1 and + correct general style violations. + [issue #494](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/494)) +- Fixes script analyzer issues in MSFT_xRemoteFile.psm1 missed from + [issue #490](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/490). +- Fix script analyzer issues in MSFT_xWindowsFeature.psm1. + [issue #493](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/493) +- Fix script analyzer issues in MSFT_xUserResource.psm1. + [issue #492](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/492) +- Moves calls to set $global:DSCMachineStatus = 1 into a helper function to + reduce the number of locations where we need to suppress PSScriptAnalyzer + rules PSAvoidGlobalVars and PSUseDeclaredVarsMoreThanAssignments. +- Adds spaces between comment hashtags and comments. +- Fixes script analyzer issues in MSFT_xServiceResource.psm1. + [issue #491](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/491) +- Fixes script analyzer issues in MSFT_xWindowsPackageCab.psm1. + [issue #495](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/495) +- xFileUpload: + - Fixes script analyzer issues in xFileUpload.schema.psm1. + [issue #497](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/497) + - Update to meet style guidelines. + - Added Integration tests. + - Updated manifest Author, Company and Copyright to match + standards. +- Updated module manifest Copyright to match standards and remove + year. +- Auto-formatted the module manifest to improve layout. + +## 8.4.0.0 + +- Changes to xPSDesiredStateConfiguration + - Opt-in for the common tests validate module files and script files. + - All files change to encoding UTF-8 (without byte order mark). + - Opt-in for the common test for example validation. + - Added Visual Studio Code workspace settings that helps with formatting + against the style guideline. + - Update all examples for them to be able pass the common test validation. +- xEnvironment path documentation update demonstrating usage with multiple + values + ([issue #415](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/415). + [Alex Kokkinos (@alexkokkinos)](https://github.com/alexkokkinos) +- Changes to xWindowsProcess + - Increased the wait time in the integration tests since the tests + still failed randomly + ([issue #420](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/420)). +- Renamed and updated examples to be able to publish them to PowerShell + Gallery. + - Sample\_xScript.ps1 → xScript\_WatchFileContentConfig.ps1 + - Sample\_xService\_UpdateStartupTypeIgnoreState.ps1 → + xService\_UpdateStartupTypeIgnoreStateConfig.ps1 + - Sample\_xWindowsProcess\_Start.ps1 → + xWindowsProcess\_StartProcessConfig.ps1 + - Sample\_xWindowsProcess\_StartUnderUser.ps1 → + xWindowsProcess\_StartProcessUnderUserConfig.ps1 + - Sample\_xWindowsProcess\_Stop.ps1 → xWindowsProcess\_StopProcessConfig.ps1 + - Sample\_xWindowsProcess\_StopUnderUser.ps1 → + xWindowsProcess\_StopProcessUnderUserConfig.ps1 + - Sample\_xUser\_CreateUser.ps1.ps1 → xUser\_CreateUserConfig.ps1 + - Sample\_xUser\_Generic.ps1.ps1 → xUser\_CreateUserDetailedConfig.ps1 + - Sample\_xWindowsFeature.ps1 → xWindowsFeature\_AddFeatureConfig.ps1 + - Sample\_xWindowsFeatureSet\_Install.ps1 → + xWindowsFeatureSet\_AddFeaturesConfig.ps1 + - Sample\_xWindowsFeatureSet\_Uninstall.ps1 → + xWindowsFeatureSet\_RemoveFeaturesConfig.ps1 + - Sample\_xRegistryResource\_AddKey.ps1 → xRegistryResource\_AddKeyConfig.ps1 + - Sample\_xRegistryResource\_RemoveKey.ps1 → + xRegistryResource\_RemoveKeyConfig.ps1 + - Sample\_xRegistryResource\_AddOrModifyValue.ps1 → + xRegistryResource\_AddOrModifyValueConfig.ps1 + - Sample\_xRegistryResource\_RemoveValue.ps1 → + xRegistryResource\_RemoveValueConfig.ps1 + - Sample\_xService\_CreateService.ps1 → xService\_CreateServiceConfig.ps1 + - Sample\_xService\_DeleteService.ps1 → xService\_RemoveServiceConfig.ps1 + - Sample\_xServiceSet\_StartServices.ps1 → + xServiceSet\_StartServicesConfig.ps1 + - Sample\_xServiceSet\_BuiltInAccount → + xServiceSet\_EnsureBuiltInAccountConfig.ps1 + - Sample\_xWindowsPackageCab → xWindowsPackageCab\_InstallPackageConfig + - Sample\_xWindowsOptionalFeature.ps1 → + xWindowsOptionalFeature\_EnableConfig.ps1 + - Sample\_xWindowsOptionalFeatureSet\_Enable.ps1 → + xWindowsOptionalFeatureSet\_EnableConfig.ps1 + - Sample\_xWindowsOptionalFeatureSet\_Disable.ps1 → + xWindowsOptionalFeatureSet\_DisableConfig.ps1 + - Sample\_xRemoteFileUsingProxy.ps1 → + xRemoteFile\_DownloadFileUsingProxyConfig.ps1 + - Sample\_xRemoteFile.ps1 → xRemoteFile\_DownloadFileConfig.ps1 + - Sample\_xProcessSet\_Start.ps1 → xProcessSet\_StartProcessConfig.ps1 + - Sample\_xProcessSet\_Stop.ps1 → xProcessSet\_StopProcessConfig.ps1 + - Sample\_xMsiPackage\_UninstallPackageFromHttps.ps1 → + xMsiPackage\_UninstallPackageFromHttpsConfig.ps1 + - Sample\_xMsiPackage\_UninstallPackageFromFile.ps1 → + xMsiPackage\_UninstallPackageFromFileConfig.ps1 + - Sample\_xMsiPackage\_InstallPackageFromFile → + xMsiPackage\_InstallPackageConfig.ps1 + - Sample\_xGroup\_SetMembers.ps1 → xGroup\_SetMembersConfig.ps1 + - Sample\_xGroup\_RemoveMembers.ps1 → xGroup\_RemoveMembersConfig.ps1 + - Sample\_xGroupSet\_AddMembers.ps1 → xGroupSet\_AddMembersConfig.ps1 + - Sample\_xFileUpload.ps1 → xFileUpload\_UploadToSMBShareConfig.ps1 + - Sample\_xEnvironment\_CreateMultiplePathVariables.ps1 → + xEnvironment\_AddMultiplePathsConfig.ps1 + - Sample\_xEnvironment\_RemovePathVariables.ps1 → + xEnvironment\_RemoveMultiplePathsConfig.ps1 + - Sample\_xEnvironment\_CreateNonPathVariable.ps1 → + xEnvironment\_CreateNonPathVariableConfig.ps1 + - Sample\_xEnvironment\_Remove.ps1 → xEnvironment\_RemoveVariableConfig.ps1 + - Sample\_xArchive\_ExpandArchiveChecksumAndForce.ps1 → + xArchive\_ExpandArchiveChecksumAndForceConfig.ps1 + - Sample\_xArchive\_ExpandArchiveDefaultValidationAndForce.ps1 → + xArchive\_ExpandArchiveDefaultValidationAndForceConfig.ps1 + - Sample\_xArchive\_ExpandArchiveNoValidation.ps1 → + xArchive\_ExpandArchiveNoValidationConfig.ps1 + - Sample\_xArchive\_ExpandArchiveNoValidationCredential.ps1 → + xArchive\_ExpandArchiveNoValidationCredentialConfig.ps1 + - Sample\_xArchive\_RemoveArchiveChecksum.ps1 → + xArchive\_RemoveArchiveChecksumConfig.ps1 + - Sample\_xArchive\_RemoveArchiveNoValidation.ps1 → + xArchive\_RemoveArchiveNoValidationConfig.ps1 + - Sample\_InstallExeCreds\_xPackage.ps1 → + xPackage\_InstallExeUsingCredentialsConfig.ps1 + - Sample\_InstallExeCredsRegistry\_xPackage.ps1 → + xPackage\_InstallExeUsingCredentialsAndRegistryConfig.ps1 + - Sample\_InstallMSI\_xPackage.ps1 → xPackage\_InstallMsiConfig.ps1 + - Sample\_InstallMSIProductId\_xPackage.ps1 → + xPackage\_InstallMsiUsingProductIdConfig.ps1 +- New examples + - xUser\_RemoveUserConfig.ps1 + - xWindowsFeature\_AddFeatureUsingCredentialConfig.ps1 + - xWindowsFeature\_AddFeatureWithLogPathConfig.ps1 + - xWindowsFeature\_RemoveFeatureConfig.ps1 + - xService\_ChangeServiceStateConfig.ps1 + - xWindowsOptionalFeature\_DisableConfig.ps1 + - xPSEndpoint\_NewConfig.ps1 + - xPSEndpoint\_NewWithDefaultsConfig.ps1 + - xPSEndpoint\_RemoveConfig.ps1 + - xPSEndpoint\_NewCustomConfig.ps1 +- Removed examples + - Sample\_xPSSessionConfiguration.ps1 - This file was split up in several + examples, those starting with 'xPSEndpoint*'. + - Sample\_xMsiPackage\_InstallPackageFromHttp - This was added to the example + xMsiPackage\_InstallPackageConfig.ps1 so the example sows either URI + scheme. + - Sample\_xEnvironment\_CreatePathVariable.ps1 - Same as the new example + xEnvironment\_AddMultiplePaths.ps1 + +## 8.3.0.0 + +- Changes to xPSDesiredStateConfiguration + - README.md: Fixed typo. + [Steve Banik (@stevebanik-ndsc)](https://github.com/stevebanik-ndsc) + - Adding a Branches section to the README.md with Codecov badges for both + master and dev branch + ([issue #416](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/416)). +- Changes to xWindowsProcess + - Integration tests for this resource should no longer fail randomly. A + timing issue made the tests fail in certain scenarios + ([issue #420](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/420)). +- Changes to xDSCWebService + - Added the option to use a certificate based on it's subject and template + name instead of it's thumbprint. Resolves + [issue #205](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/205). + - xDSCWebService: Fixed an issue where Test-WebConfigModulesSetting would + return $true when web.config contains a module and the desired state was + for it to be absent. Resolves + [issue #418](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/418). +- Updated the main DSCPullServerSetup readme to read easier, then updates the + PowerShell comment based help for each function to follow normal help + standards. [James Pogran (@jpogran)](https://github.com/jpogran) +- xRemoteFile: Remove progress bar for file download. This resolves issues + [#165](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/165) + and + [#383](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/383) + [Claudio Spizzi (@claudiospizzi)](https://github.com/claudiospizzi) + +## 8.2.0.0 + +- xDSCWebService: Disable installing + Microsoft.Powershell.Desiredstateconfiguration.Service.Resources.dll as a + temporary workaround since the binary is missing on the latest Windows + builds. + +## 8.1.0.0 + +- xDSCWebService: Enable SQL provider configuration + +## 8.0.0.0 + +- xDSCWebService + - BREAKING CHANGE: The Pull Server will now run in a 64 bit IIS process by + default. Enable32BitAppOnWin64 needs to be set to TRUE for the Pull + Server to run in a 32 bit process. + +## 7.0.0.0 + +- xService + - BREAKING CHANGE: The service will now return as compliant if the service + is not installed and the StartupType is set to Disabled regardless of the + value of the Ensure property. +- Fixed misnamed certificate thumbprint variable in example + Sample_xDscWebServiceRegistrationWithSecurityBestPractices + +## 6.4.0.0 + +- xGroup: + - Added updates from PSDscResources: + - Added support for domain based group members on Nano server + +## 6.3.0.0 + +- xDSCWebService + - Fixed an issue where all 64bit IIS application pools stop working after + installing DSC Pull Server, because IISSelfSignedCertModule(32bit) module + was registered without bitness32 precondition. + +## 6.2.0.0 + +- xMsiPackage: + - Created high quality MSI package manager resource +- xArchive: + - Fixed a minor bug in the unit tests where sometimes the incorrect + DateTime format was used. +- xWindowsFeatureSet: + - Had the wrong parameter name in one test case. + +## 6.1.0.0 + +- Moved DSC pull server setup tests to DSCPullServerSetup folder for new common + tests. +- xArchive: + - Updated the resource to be a high quality resource + - Transferred the existing "unit" tests to integration tests + - Added unit and end-to-end tests + - Updated documentation and examples +- xUser + - Fixed error handling in xUser +- xRegistry + - Fixed bug where an error was thrown when running Get-DscConfiguration if + the registry key already existed +- Updated Test-IsNanoServer cmdlet to properly test for a Nano server rather + than the core version of PowerShell + +## 6.0.0.0 + +- xEnvironment + - Updated resource to follow HQRM guidelines. + - Added examples. + - Added unit and end-to-end tests. + - Significantly cleaned the resource. + - Minor Breaking Change where the resource will now throw an error if no + value is provided, Ensure is set to present, and the variable does not + exist, whereas before it would create an empty registry key on the + machine in this case (if this is the desired outcome then use the + Registry resource). + - Added a new Write property 'Target', which specifies whether the user + wants to set the machine variable, the process variable, or both + (previously it was setting both in most cases). +- xGroup: + - Group members in the "NT Authority", "BuiltIn" and "NT Service" scopes + should now be resolved without an error. If you were seeing the errors + "Exception calling ".ctor" with "4" argument(s): "Server names cannot + contain a space character."" or "Exception calling ".ctor" with "2" + argument(s): "Server names cannot contain a space character."", this fix + should resolve those errors. If you are still seeing one of the errors, + there is probably another local scope we need to add. Please let us know. + - The resource will no longer attempt to resolve group members if Members, + MembersToInclude, and MembersToExclude are not specified. + +## 5.2.0.0 + +- xWindowsProcess + - Minor updates to integration tests because one of the tests was flaky. +- xRegistry: + - Added support for forward slashes in registry key names. This resolves + issue + [#285](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/285). + +## 5.1.0.0 + +- xWindowsFeature: + - Added Catch to ignore RuntimeException when importing ServerManager + module. This resolves issue + [#69](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/69). + - Updated unit tests. +- xPackage: + - No longer checks for package installation when a reboot is required. This + resolves issue + [#52](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/52). + - Ensures a space is added to MSI installation arguments. This resolves + issue + [#195](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/195). + - Adds RunAsCredential parameter to permit installing packages with + specific user account. This resolves issue + [#221](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/221). + - Fixes null verbose log output error. This resolves issue + [#224](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/224). +- xDSCWebService + - Fixed issue where resource would fail to read redirection.config file. + This resolves issue + [#191](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/191) +- xArchive + - Fixed issue where resource would throw exception when file name contains + brackets. This resolves issue + [#255](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/255). +- xScript + - Cleaned resource for high quality requirements + - Added unit tests + - Added integration tests + - Updated documentation and example +- ResourceSetHelper: + - Updated common functions for all 'Set' resources. + - Added unit tests +- xGroupSet: + - Updated resource to use new ResouceSetHelper functions and added + integration tests. +- xGroup: + - Cleaned module imports, fixed PSSA issues, and set ErrorActionPreference + to stop. +- xService: + - Cleaned resource functions to enable StrictMode. + - Fixed bug in which Set-TargetResource would create a service when Ensure + set to Absent and Path specified. + - Added unit tests. + - Added integration tests for BuiltInAccount and Credential. +- xServiceSet: + - Updated resource to use new ResouceSetHelper functions and added + integration tests. + - Updated documentation and example +- xWindowsProcess + - Cleaned resource as per high quality guidelines. + - Added unit tests. + - Added integration tests. + - Updated documentation. + - Updated examples. + - Fixed bug in Get-TargetResource. + - Added a 'Count' value to the hashtable returned by Get-TargetResource so + that the user can see how many instances of the process are running. + - Fixed bug in finding the path to the executable. + - Changed name to be xWindowsProcess everywhere. +- xWindowsOptionalFeatureSet + - Updated resource to use new ResouceSetHelper functions and added + integration tests. + - Updated documentation and examples +- xWindowsFeatureSet + - Updated resource to use new ResouceSetHelper functions and added + integration tests. + - Updated documentation and examples +- xProcessSet + - Updated resource to use new ResouceSetHelper functions and added + integration tests. + - Updated documentation and examples +- xRegistry + - Updated resource to be high-quality + - Fixed bug in which the user could not set a Binary registry value to 0 + - Added unit and integration tests + - Added examples and updated documentation + +## 5.0.0.0 + +- xWindowsFeature: + - Cleaned up resource (PSSA issues, formatting, etc.) + - Added/Updated Tests and Examples + - BREAKING CHANGE: Removed the unused Source parameter + - Updated to a high quality resource +- xDSCWebService: + - Add DatabasePath property to specify a custom database path and enable + multiple pull server instances on one server. + - Rename UseUpToDateSecuritySettings property to UseSecurityBestPractices. + - Add DisableSecurityBestPractices property to specify items that are + excepted from following best practice security settings. +- xGroup: + - Fixed PSSA issues + - Formatting updated as per style guidelines + - Missing comment-based help added for Get-/Set-/Test-TargetResource + - Typos fixed in Unit test script + - Unit test 'Get-TargetResource/Should return hashtable with correct values + when group has no members' updated to handle the expected empty Members + array correctly + - Added a lot of unit tests + - Cleaned resource +- xUser: + - Fixed PSSA/Style violations + - Added/Updated Tests and Examples +- Added xWindowsPackageCab +- xService: + - Fixed PSSA/Style violations + - Updated Tests + - Added 'Ignore' state + +## 4.0.0.0 + +- xDSCWebService: + - Added setting of enhanced security + - Cleaned up Examples + - Cleaned up pull server verification test +- xProcess: + - Fixed PSSA issues + - Corrected most style guideline issues +- xPSSessionConfiguration: + - Fixed PSSA and style issues + - Renamed internal functions to follow verb-noun formats + - Decorated all functions with comment-based help +- xRegistry: + - Fixed PSSA and style issues + - Renamed internal functions to follow verb-noun format + - Decorated all functions with comment-based help + - Merged with in-box Registry + - Fixed registry key and value removal + - Added unit tests +- xService: + - Added descriptions to MOF file. + - Added additional details to parameters in Readme.md in a format that can + be generated from the MOF. + - Added DesktopInteract parameter. + - Added standard help headers to *-TargetResource functions. + - Changed indent/format of all function help headers to be consistent. + - Fixed line length violations. + - Changed localization code so only a single copy of localization strings + are required. + - Removed localization strings from inside module file. + - Updated unit tests to use standard test enviroment configuration and + header. + - Recreated unit tests to be non-destructive. + - Created integration tests. + - Allowed service to be restarted immediately rather than wait for next LCM + run. + - Changed helper function names to valid verb-noun format. + - Removed New-TestService function from + MSFT_xServiceResource.TestHelper.psm1 because it should not be used. + - Fixed error calling Get-TargetResource when service does not exist. + - Fixed bug with Get-TargetResource returning StartupType 'Auto' instead of + 'Automatic'. + - Converted to HQRM standards. + - Removed obfuscation of exception in Get-Win32ServiceObject function. + - Fixed bug where service start mode would be set to auto when it already + was set to auto. + - Fixed error message content when start mode can not be changed. + - Removed shouldprocess from functions as not required. + - Optimized Test-TargetResource and Set-TargetResource by removing repeated + calls to Get-Service and Get-CimInstance. + - Added integration test for testing changes to additional service + properties as well as changing service binary path. + - Modified Set-TargetResource so that newly created service created with + minimal properties and then all additional properties updated + (simplification of code). + - Added support for changing Service Description and DisplayName + parameters. + - Fixed bug when changing binary path of existing service. +- Removed test log output from repo. +- xWindowsOptionalFeature: + - Cleaned up resource (PSSA issues, formatting, etc.) + - Added example script + - Added integration test + - BREAKING CHANGE: Removed the unused Source parameter + - Updated to a high quality resource +- Removed test log output from repo. +- Removed the prefix MSFT_ from all files and folders of the composite + resources in this module because they were unavailable to Get-DscResource and + Import-DscResource. + - xFileUpload + - xGroupSet + - xProcessSet + - xServiceSet + - xWindowsFeatureSet + - xWindowsOptionalFeatureSet + +## 3.13.0.0 + +- Converted appveyor.yml to install Pester from PSGallery instead of from + Chocolatey. +- Updated appveyor.yml to use the default image. +- Merged xPackage with in-box Package resource and added tests. +- xPackage: Re-implemented parameters for installation check from registry key + value. +- xGroup: + - Fixed Verbose output in Get-MembersAsPrincipals function. + - Fixed bug when credential parameter passed does not contain local or + domain context. + - Fixed logic bug in MembersToInclude and MembersToExclude. + - Fixed bug when trying to include the built-in Administrator in Members. + - Fixed bug where Test-TargetResource would check for members when none + specified. + - Fix bug in Test-TargetResourceOnFullSKU function when group being set to + a single member. + - Fix bug in Set-TargetResourceOnFullSKU function when group being set to a + single member. + - Fix bugs in Assert-GroupNameValid to throw correct exception. +- xService + - Updated xService resource to allow empty string for Description + parameter. +- Merged xProcess with in-box Process resource and added tests. +- Fixed PSSA issues in xPackageResource. + +## 3.12.0.0 + +- Removed localization for now so that resources can run on non-English + systems. + +## 3.11.0.0 + +- xRemoteFile: + - Added parameters: + - TimeoutSec + - Proxy + - ProxyCredential + - Added unit tests. + - Corrected Style Guidelines issues. + - Added Localization support. + - URI parameter supports File://. + - Get-TargetResource returns URI parameter. + - Fixed logging of error message reported when download fails. + - Added new example Sample_xRemoteFileUsingProxy.ps1. +- Examples: Fixed missing newline at end of PullServerSetupTests.ps1. +- xFileUpload: Added PSSA rule suppression attribute. +- xPackageResource: Removed hardcoded ComputerName 'localhost' parameter from + Get-WMIObject to eliminate PSSA rule violation. The parameter is not + required. +- Added .gitignore to prevent DSCResource.Tests from being commited to repo. +- Updated AppVeyor.yml to use WMF 5 build OS so that latest test methods work. +- Updated xWebService resource to not deploy Devices.mdb if esent provider is + used +- Fixed $script:netsh parameter initialization in xWebService resource that was + causing CIM exception when EnableFirewall flag was specified. +- xService: + - Fixed a bug where, despite no state specified in the config, the resource + test returns false if the service is not running + - Fixed bug in which Automatice StartupType did not match the 'Auto' + StartMode in Test-TargetResource. +- xPackage: Fixes bug where CreateCheckRegValue was not being removed when + uninstalling packages +- Replaced New-NetFirewallRule cmdlets with netsh as this cmdlet is not + available by default on some downlevel OS such as Windows 2012 R2 Core. +- Added the xEnvironment resource +- Added the xWindowsFeature resource +- Added the xScript resource +- Added the xUser resource +- Added the xGroupSet resource +- Added the xProcessSet resource +- Added the xServiceSet resource +- Added the xWindowsFeatureSet resource +- Added the xWindowsOptionalFeatureSet resource +- Merged the in-box Service resource with xService and added tests for xService +- Merged the in-box Archive resource with xArchive and added tests for xArchive +- Merged the in-box Group resource with xGroup and added tests for xGroup + +## 3.10.0.0 + +- **Publish-ModuleToPullServer** +- **Publish-MOFToPullServer** + +## 3.9.0.0 + +- Added more information how to use Publish-DSCModuleAndMof cmdlet and samples +- Removed compliance server samples + +## 3.8.0.0 + +- Added Pester tests to validate pullserver deployement. +- Removed Compliance Server deployment from xWebservice resource. Fixed + database provider selection issue depending on OS flavor +- Added Publish-DSCModuleAndMof cmdlet to package DSC modules and mof and + publish them on DSC enterprise pull server +- xRemoteFile resource: Added size verification in cache + +## 3.7.0.0 + +- xService: + - Fixed a bug where 'Dependencies' property was not picked up and caused + exception when set. +- xWindowsOptionalFeature: + - Fixed bug where Test-TargetResource method always failed. + - Added support for Windows Server 2012 (and later) SKUs. +- Added xRegistry resource + +## 3.6.0.0 + +- Added CreateCheckRegValue parameter to xPackage resource +- Added MatchSource parameter to xRemoteFile resource + +## 3.5.0.0 + +- MSFT_xPackageResource: Added ValidateSet to Get/Set/Test-TargetResource to + match MSFT_xPackageResource.schema.mof +- Fixed bug causing xService to throw error when service already exists +- Added StartupTimeout to xService resource +- Removed UTF8 BOM +- Added code for pull server removal + +## 3.4.0.0 + +- Added logging inner exception messages in xArchive and xPackage resources +- Fixed hash calculation in Get-CacheEntry +- Fixed issue with PSDSCComplianceServer returning HTTP Error 401.2 + +## 3.3.0.0 + +- Add support to xPackage resource for checking different registry hives +- Added support for new registration properties in xDscWebService resource + +## 3.2.0.0 + +- xArchive: + - Fix problems with file names containing square brackets. +- xDSCWebService: + - Fix default culture issue. +- xPackage: + - Security enhancements. + +## 3.0.3.4 + +- Multiple issues addressed + - Corrected output type for Set- and Test-TargetResource functions in + xWebSite, xPackage, xArchive, xGroup, xProcess, xService + - xRemoteFile modified to support creating a directory that does not exist + when specified, ensuring idempotency. Also improved error messages. + - xDSCWebService updated so that Get-TargetResource returns the OData + Endpoint URL correctly. + - In xWindowsOptionalFeature, fixed Test-TargetResource issue requiring + Ensure = True. Note: this change requires the previous Ensure values of + Enable and Disable to change to Present and Absent + +## 3.0.2.0 + +- Adding following resources: + - xGroup + +## 3.0.1.0 + +- Adding following resources: + - xFileUpload + +## 2.0.0.0 + +- Adding following resources: + - xWindowsProcess + - xService + - xRemoteFile + - xPackage + +## 1.1.0.0 + +- Fix to remove and recreate the SSL bindings when performing a new HTTPS IIS + Endpoint setup. +- Fix in the resource module to consume WebSite Name parameter correctly + +## 1.0.0.0 + +- Initial release with the following resources: + - DscWebService diff --git a/DSCPullServerSetup/PublishModulesAndMofsToPullServer.psm1 b/DSCPullServerSetup/PublishModulesAndMofsToPullServer.psm1 index f433b03ef..894c63d49 100644 --- a/DSCPullServerSetup/PublishModulesAndMofsToPullServer.psm1 +++ b/DSCPullServerSetup/PublishModulesAndMofsToPullServer.psm1 @@ -2,13 +2,13 @@ .SYNOPSIS Package DSC modules and mof configuration document and publish them on an enterprise DSC pull server in the required format. .DESCRIPTION - Uses Publish-DSCModulesAndMof function to package DSC modules into zip files with the version info. + Uses Publish-DSCModulesAndMof function to package DSC modules into zip files with the version info. Publishes the zip modules on "$env:ProgramFiles\WindowsPowerShell\DscService\Modules". Publishes all mof configuration documents that are present in the $Source folder on "$env:ProgramFiles\WindowsPowerShell\DscService\Configuration"- Use $Force to overwrite the version of the module that exists in the PowerShell module path with the version from the $source folder. Use $ModuleNameList to specify the names of the modules to be published if the modules do not exist in $Source folder. .PARAMETER Source - The folder that contains the configuration mof documents and modules to be published on Pull server. + The folder that contains the configuration mof documents and modules to be published on Pull server. Everything in this folder will be packaged and published. .PARAMETER Force Switch to overwrite the module in PSModulePath with the version provided in $Sources. @@ -24,10 +24,17 @@ function Publish-DSCModuleAndMof { [CmdletBinding()] param( - [Parameter(Mandatory=$True)] - [string]$Source = $pwd, - [switch]$Force, - [string[]]$ModuleNameList + [Parameter(Mandatory = $True)] + [System.String] + $Source = $pwd, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force, + + [Parameter()] + [System.String[]] + $ModuleNameList ) # Create working directory @@ -35,7 +42,7 @@ function Publish-DSCModuleAndMof New-Item -Path $tempFolder -ItemType Directory -Force -ErrorAction SilentlyContinue # Copy the mof documents from the $Source to working dir - Copy-Item -Path "$Source\*.mof" -Destination $tempFolder -Force -Verbose + Copy-Item -Path "$Source\*.mof" -Destination $tempFolder -Force # Start Deployment! Log -Scope $MyInvocation -Message 'Start Deployment' @@ -66,32 +73,32 @@ function CreateZipFromPSModulePath param($ListModuleNames, $Destination) # Move all required modules from powershell module path to a temp folder and package them - if ([string]::IsNullOrEmpty($ListModuleNames)) + if ([System.String]::IsNullOrEmpty($ListModuleNames)) { - Log -Scope $MyInvocation -Message "No additional modules are specified to be packaged." + Log -Scope $MyInvocation -Message "No additional modules are specified to be packaged." } - + foreach ($module in $ListModuleNames) { - $allVersions = Get-Module -Name $module -ListAvailable -Verbose + $allVersions = Get-Module -Name $module -ListAvailable # Package all versions of the module foreach ($moduleVersion in $allVersions) { - $name = $moduleVersion.Name + $name = $moduleVersion.Name $source = "$Destination\$name" # Create package zip - $path = $moduleVersion.ModuleBase + $path = $moduleVersion.ModuleBase $version = $moduleVersion.Version.ToString() Log -Scope $MyInvocation -Message "Zipping $name ($version)" - Compress-Archive -Path "$path\*" -DestinationPath "$source.zip" -Verbose -Force + Compress-Archive -Path "$path\*" -DestinationPath "$source.zip" -Force $newName = "$Destination\$name" + "_" + "$version" + ".zip" # Rename the module folder to contain the version info. if (Test-Path $newName) { - Remove-Item $newName -Recurse -Force + Remove-Item $newName -Recurse -Force } - Rename-Item -Path "$source.zip" -NewName $newName -Force - } + Rename-Item -Path "$source.zip" -NewName $newName -Force + } } } @@ -109,19 +116,19 @@ function CreateZipFromPSModulePath function CreateZipFromSource { param($Source, $Destination) - # for each module under $Source folder create a zip package that has the same name as the folder. + # for each module under $Source folder create a zip package that has the same name as the folder. $allModulesInSource = Get-ChildItem -Path $Source -Directory $modules = @() - + foreach ($item in $allModulesInSource) { $name = $Item.Name - $alreadyExists = Get-Module -Name $name -ListAvailable -Verbose + $alreadyExists = Get-Module -Name $name -ListAvailable if (($alreadyExists -eq $null) -or ($Force)) { - # Install the modules into PowerShell module path and overwrite the content - Copy-Item -Path $item.FullName -Recurse -Force -Destination "$env:ProgramFiles\WindowsPowerShell\Modules" -Verbose - } + # Install the modules into PowerShell module path and overwrite the content + Copy-Item -Path $item.FullName -Recurse -Force -Destination "$env:ProgramFiles\WindowsPowerShell\Modules" + } else { Write-Warning "Skipping module overwrite. Module with the name $name already exists." @@ -148,12 +155,12 @@ function PublishModulesAndChecksum if ((Get-Module ServerManager -ListAvailable) -and (Test-Path $moduleRepository)) { Log -Scope $MyInvocation -Message "Copying modules and checksums to [$moduleRepository]." - Copy-Item -Path "$Source\*.zip*" -Destination $moduleRepository -Force -Verbose + Copy-Item -Path "$Source\*.zip*" -Destination $moduleRepository -Force } else { Write-Warning "Copying modules to Pull server module repository skipped because the machine is not a server sku or Pull server endpoint is not deployed." - } + } } @@ -165,13 +172,13 @@ function PublishModulesAndChecksum #> function PublishMofDocuments { - param($Source) + param($Source) # Check if the current machine is a server sku. $mofRepository = "$env:ProgramFiles\WindowsPowerShell\DscService\Configuration" - if ((Get-Module ServerManager -ListAvailable) -and (Test-Path $mofRepository)) + if ((Get-Module ServerManager -ListAvailable) -and (Test-Path $mofRepository)) { Log -Scope $MyInvocation -Message "Copying mofs and checksums to [$mofRepository]." - Copy-Item -Path "$Source\*.mof*" -Destination $mofRepository -Force -Verbose + Copy-Item -Path "$Source\*.mof*" -Destination $mofRepository -Force } else { @@ -183,7 +190,7 @@ Function Log { Param( $Date = $(Get-Date), - $Scope, + $Scope, $Message ) @@ -195,7 +202,7 @@ Function Log .SYNOPSIS Deploy DSC modules to the pullserver. .DESCRIPTION - Publish DSC module using Module Info object as an input. + Publish DSC module using Module Info object as an input. The cmdlet will figure out the location of the module repository using web.config of the pullserver. .PARAMETER Name Name of the module. @@ -217,19 +224,19 @@ function Publish-ModuleToPullServer [OutputType([void])] Param ( - [Parameter(Mandatory=$true, - ValueFromPipelineByPropertyName=$true, - Position=0)] + [Parameter(Mandatory = $true, + ValueFromPipelineByPropertyName = $true, + Position = 0)] $Name, - - [Parameter(Mandatory=$true, - ValueFromPipelineByPropertyName=$true, - Position=1)] + + [Parameter(Mandatory = $true, + ValueFromPipelineByPropertyName = $true, + Position = 1)] $ModuleBase, - - [Parameter(Mandatory=$true, - ValueFromPipelineByPropertyName=$true, - Position=2)] + + [Parameter(Mandatory = $true, + ValueFromPipelineByPropertyName = $true, + Position = 2)] $Version, $PullServerWebConfig = "$env:SystemDrive\inetpub\wwwroot\PSDSCPullServer\web.config", @@ -248,33 +255,33 @@ function Publish-ModuleToPullServer else { # Pull Server exist figure out the module path of the pullserver and use this value as output folder path. - $webConfigXml = [xml](cat $PullServerWebConfig) + $webConfigXml = [System.Xml.XmlDocument] (Get-Content -Path $PullServerWebConfig) $moduleXElement = $webConfigXml.SelectNodes("//appSettings/add[@key = 'ModulePath']") - $OutputFolderPath = $moduleXElement.Value + $OutputFolderPath = $moduleXElement.Value } } } Process { - Write-Verbose "Name: $Name , ModuleBase : $ModuleBase ,Version: $Version" - $targetPath = Join-Path $OutputFolderPath "$($Name)_$($Version).zip" - - if (Test-Path $targetPath) - { - Compress-Archive -DestinationPath $targetPath -Path "$($ModuleBase)\*" -Update -Verbose - } - else - { - Compress-Archive -DestinationPath $targetPath -Path "$($ModuleBase)\*" -Verbose - } + Write-Verbose "Name: $Name , ModuleBase : $ModuleBase ,Version: $Version" + $targetPath = Join-Path $OutputFolderPath "$($Name)_$($Version).zip" + + if (Test-Path $targetPath) + { + Compress-Archive -DestinationPath $targetPath -Path "$($ModuleBase)\*" -Update + } + else + { + Compress-Archive -DestinationPath $targetPath -Path "$($ModuleBase)\*" + } } End { - # Now that all the modules are published generate thier checksum. - New-DscChecksum -Path $OutputFolderPath - + # Now that all the modules are published generate thier checksum. + New-DscChecksum -Path $OutputFolderPath + } -} +} <# @@ -294,38 +301,38 @@ function Publish-MOFToPullServer [OutputType([void])] Param ( - [Parameter(Mandatory=$true, - ValueFromPipelineByPropertyName=$true, - Position=0)] + [Parameter(Mandatory = $true, + ValueFromPipelineByPropertyName = $true, + Position = 0)] $FullName, - + $PullServerWebConfig = "$env:SystemDrive\inetpub\wwwroot\PSDSCPullServer\web.config" ) Begin { - $webConfigXml = [xml](cat $PullServerWebConfig) - $configXElement = $webConfigXml.SelectNodes("//appSettings/add[@key = 'ConfigurationPath']") - $OutputFolderPath = $configXElement.Value + $webConfigXml = [System.Xml.XmlDocument] (Get-Content -Path $PullServerWebConfig) + $configXElement = $webConfigXml.SelectNodes("//appSettings/add[@key = 'ConfigurationPath']") + $OutputFolderPath = $configXElement.Value } Process { - $fileInfo = [System.IO.FileInfo]::new($FullName) + $fileInfo = New-Item -Path $FullName -ItemType File if ($fileInfo.Extension -eq '.mof') { - if (Test-Path $FullName) + if (Test-Path -Path $FullName) { - copy $FullName $OutputFolderPath -Verbose -Force + Copy-Item -Path $FullName -Destination $OutputFolderPath -Force } - else + else { - Throw "File not found at $FullName" - } + throw "File not found at $FullName" + } } else { throw "Invalid file $FullName. Only mof files can be copied to the pullserver configuration repository" - } + } } End { diff --git a/DSCPullServerSetup/PullServerDeploymentVerificationTest/PullServerSetupTests.ps1 b/DSCPullServerSetup/PullServerDeploymentVerificationTest/PullServerSetupTests.ps1 index b0051fa7d..77887fad0 100644 --- a/DSCPullServerSetup/PullServerDeploymentVerificationTest/PullServerSetupTests.ps1 +++ b/DSCPullServerSetup/PullServerDeploymentVerificationTest/PullServerSetupTests.ps1 @@ -20,7 +20,6 @@ Describe PullServerInstallationTests { BeforeAll{ - # UPDATE THE PULLSERVER URL, If it is different from the default value. $DscHostFQDN = [System.Net.Dns]::GetHostEntry([string]$env:computername).HostName $DscPullServerURL = "https://$($DscHostFQDN):8080/PSDSCPullserver.svc" @@ -35,14 +34,14 @@ Describe PullServerInstallationTests { } # Get web.config content as XML - $DscWebConfigXML = [xml](Get-Content $DscWebConfigPath) + $DscWebConfigXML = [System.Xml.XmlDocument] (Get-Content -Path $DscWebConfigPath) # Registration Keys info. $DscRegKeyName = 'RegistrationKeys.txt' $DscRegKeyXMLNode = "//appSettings/add[@key = 'RegistrationKeyPath']" $DscRegKeyParentPath = ($DscWebConfigXML.SelectNodes($DscRegKeyXMLNode)).value $DscRegKeyPath = Join-Path -Path $DscRegKeyParentPath -ChildPath $DscRegKeyName - $DscRegKey = Get-Content $DscRegKeyPath + $DscRegKey = Get-Content -Path $DscRegKeyPath # Configuration repository info. $DscConfigPathXMLNode = "//appSettings/add[@key = 'ConfigurationPath']" @@ -63,10 +62,10 @@ Describe PullServerInstallationTests { $DscRegKeyPath | Should Exist } It "Module repository $DscModulePath exists" { - $DscModulePath | Should Exist + $DscModulePath | Should Exist } It "Configuration repository $DscConfigPath exists" { - $DscConfigPath | Should Exist + $DscConfigPath | Should Exist } It "Verify server $DscPullServerURL is up and running" { $DscPullServerResponse = Invoke-WebRequest -Uri $DscPullServerURL -UseBasicParsing @@ -80,7 +79,7 @@ Describe PullServerInstallationTests { { Settings { - RefreshMode = "PULL" + RefreshMode = 'PULL' } ConfigurationRepositoryWeb ConfigurationManager { @@ -97,15 +96,15 @@ Describe PullServerInstallationTests { $DscLocalConfigNames -contains $DscTestConfigName | Should Be True } It "Creates mof and checksum files in $DscConfigPath" { - # Sample test configuration + # Sample test configuration Configuration NoOpConfig { Import-DscResource -ModuleName PSDesiredStateConfiguration Node ($DscTestConfigName) { Script script { - GetScript = "@{}" - SetScript = "{}" + GetScript = '@{}' + SetScript = '{}' TestScript = { if ($false) { return $true } else {return $false} } @@ -113,16 +112,16 @@ Describe PullServerInstallationTests { } } - # Create a mof file copy it to + # Create a mof file copy it to NoOpConfig -OutputPath $DscConfigPath -Verbose:$VerbosePreference $DscTestMofPath | Should Exist - # Create checksum + # Create checksum New-DscChecksum $DscConfigPath -Verbose:$VerbosePreference -Force "$DscTestMofPath.checksum" | Should Exist } It 'Updates DscConfiguration Successfully' { - Update-DscConfiguration -Wait -Verbose:$VerbosePreference + Update-DscConfiguration -Wait -Verbose:$VerbosePreference (Get-DscConfiguration).ConfigurationName | Should Be "NoOpConfig" } } diff --git a/DSCResources/CommonResourceHelper.psm1 b/DSCResources/CommonResourceHelper.psm1 index 6d15f3041..2bcbd3293 100644 --- a/DSCResources/CommonResourceHelper.psm1 +++ b/DSCResources/CommonResourceHelper.psm1 @@ -176,5 +176,32 @@ function Get-LocalizedData return $localizedData } -Export-ModuleMember -Function @( 'Test-IsNanoServer', 'New-InvalidArgumentException', - 'New-InvalidOperationException', 'Get-LocalizedData' ) +<# + .SYNOPSIS + Sets the Global DSCMachineStatus variable to a value of 1. +#> +function Set-DSCMachineRebootRequired +{ + # Suppressing this rule because $global:DSCMachineStatus is used to trigger a reboot. + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] + <# + Suppressing this rule because $global:DSCMachineStatus is only set, + never used (by design of Desired State Configuration). + #> + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] + [CmdletBinding()] + param + ( + ) + + $global:DSCMachineStatus = 1 +} + +Export-ModuleMember ` + -Function @( + 'Test-IsNanoServer', + 'New-InvalidArgumentException', + 'New-InvalidOperationException', + 'Get-LocalizedData', + 'Set-DSCMachineRebootRequired' + ) diff --git a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 index 5effdce06..1f031e62a 100644 --- a/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 +++ b/DSCResources/MSFT_xDSCWebService/MSFT_xDSCWebService.psm1 @@ -161,11 +161,24 @@ function Get-TargetResource } else { - $certificate = ([Array](Get-ChildItem -Path 'Cert:\LocalMachine\My\')).Where{$_.Thumbprint -eq $webBinding.CertificateHash} + $certificate = ([Array] (Get-ChildItem -Path 'Cert:\LocalMachine\My\')) | Where-Object -FilterScript { + $_.Thumbprint -eq $webBinding.CertificateHash + } + + # Try to parse the Certificate Template Name. The property is not available on all Certificates. + $actualCertificateTemplateName = '' + $certificateTemplateProperty = $certificate.Extensions | Where-Object -FilterScript { + $_.Oid.FriendlyName -eq 'Certificate Template Name' + } + + if ($null -ne $certificateTemplateProperty) + { + $actualCertificateTemplateName = $certificateTemplateProperty.Format($false) + } $output.Add('CertificateThumbPrint', $webBinding.CertificateHash) $output.Add('CertificateSubject', $certificate.Subject) - $output.Add('CertificateTemplateName', $certificate.Extensions.Where{$_.Oid.FriendlyName -eq 'Certificate Template Name'}.Format($false)) + $output.Add('CertificateTemplateName', $actualCertificateTemplateName) } return $output @@ -196,7 +209,8 @@ function Set-TargetResource # Thumbprint of the Certificate in CERT:\LocalMachine\MY\ for Pull Server [Parameter(ParameterSetName = 'CertificateThumbPrint')] [ValidateNotNullOrEmpty()] - [String]$CertificateThumbPrint, + [String] + $CertificateThumbPrint, # Subject of the Certificate in CERT:\LocalMachine\MY\ for Pull Server [Parameter(ParameterSetName = 'CertificateSubject')] @@ -305,7 +319,7 @@ function Set-TargetResource $jet4database = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=$DatabasePath\Devices.mdb;" $eseprovider = "ESENT" $esedatabase = "$DatabasePath\Devices.edb" - + $cultureInfo = Get-Culture $languagePath = $cultureInfo.IetfLanguageTag $language = $cultureInfo.TwoLetterISOLanguageName @@ -465,7 +479,7 @@ function Set-TargetResource if($UseSecurityBestPractices) { - UseSecurityBestPractices\Set-UseSecurityBestPractices -DisableSecurityBestPractices $DisableSecurityBestPractices + UseSecurityBestPractices\Set-UseSecurityBestPractice -DisableSecurityBestPractices $DisableSecurityBestPractices } } @@ -766,7 +780,7 @@ function Test-TargetResource Write-Verbose -Message "Check UseSecurityBestPractices" if ($UseSecurityBestPractices) { - if (-not (UseSecurityBestPractices\Test-UseSecurityBestPractices -DisableSecurityBestPractices $DisableSecurityBestPractices)) + if (-not (UseSecurityBestPractices\Test-UseSecurityBestPractice -DisableSecurityBestPractices $DisableSecurityBestPractices)) { $desiredConfigurationMatch = $false Write-Verbose -Message "The state of security settings does not match the desired state." @@ -826,7 +840,7 @@ function Test-WebConfigAppSetting if (Test-Path -Path $WebConfigFullPath) { - $webConfigXml = [Xml](Get-Content -Path $WebConfigFullPath) + $webConfigXml = [Xml] (Get-Content -Path $WebConfigFullPath) $root = $webConfigXml.get_DocumentElement() foreach ($item in $root.appSettings.add) @@ -864,7 +878,7 @@ function Get-WebConfigAppSetting $appSettingValue = "" if (Test-Path -Path $WebConfigFullPath) { - $webConfigXml = [Xml](Get-Content -Path $WebConfigFullPath) + $webConfigXml = [Xml] (Get-Content -Path $WebConfigFullPath) $root = $webConfigXml.get_DocumentElement() foreach ($item in $root.appSettings.add) @@ -902,7 +916,7 @@ function Test-WebConfigModulesSetting if (Test-Path -Path $WebConfigFullPath) { - $webConfigXml = [Xml](Get-Content -Path $WebConfigFullPath) + $webConfigXml = [Xml] (Get-Content -Path $WebConfigFullPath) $root = $webConfigXml.get_DocumentElement() foreach ($item in $root."system.webServer".modules.add) @@ -934,7 +948,7 @@ function Get-WebConfigModulesSetting $moduleValue = "" if (Test-Path -Path $WebConfigFullPath) { - $webConfigXml = [Xml](Get-Content -Path $WebConfigFullPath) + $webConfigXml = [Xml] (Get-Content -Path $WebConfigFullPath) $root = $webConfigXml.get_DocumentElement() foreach ($item in $root."system.webServer".modules.add) diff --git a/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 b/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 index ca5a44ce8..9cf10d829 100644 --- a/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 +++ b/DSCResources/MSFT_xDSCWebService/PSWSIISEndpoint.psm1 @@ -1,62 +1,101 @@ # This module file contains a utility to perform PSWS IIS Endpoint setup # Module exports New-PSWSEndpoint function to perform the endpoint setup -# -#Copyright (c) Microsoft Corporation, 2014 -# - -# name and description for the Firewall rules. Used in multiple locations -$FireWallRuleDisplayName = "Desired State Configuration - Pull Server Port:{0}" -$FireWallRuleDescription = "Inbound traffic for IIS site on Port:{0} for DSC pull server. Created by DSCWebService resource" - -# Validate supplied configuration to setup the PSWS Endpoint -# Function checks for the existence of PSWS Schema files, IIS config -# Also validate presence of IIS on the target machine -# + +# Name and description for the Firewall rules. Used in multiple locations +$FireWallRuleDisplayName = 'Desired State Configuration - Pull Server Port:{0}' + +<# + .SYNOPSIS + Validate supplied configuration to setup the PSWS Endpoint Function + checks for the existence of PSWS Schema files, IIS config Also validate + presence of IIS on the target machine +#> function Initialize-Endpoint { - param ( + [CmdletBinding()] + param + ( + [Parameter()] + [System.String] $site, + + [Parameter()] + [System.String] $path, + + [Parameter()] + [ValidateScript({Test-Path -Path $_})] + [System.String] $cfgfile, + + [Parameter()] + [System.Int32] $port, + + [Parameter()] + [System.String] $app, + + [Parameter()] + [System.String] $applicationPoolIdentityType, + + [Parameter()] + [ValidateScript({Test-Path -Path $_})] + [System.String] $svc, + + [Parameter()] + [ValidateScript({Test-Path -Path $_})] + [System.String] $mof, + + [Parameter()] + [System.String] $dispatch, + + [Parameter()] + [ValidateScript({Test-Path -Path $_})] + [System.String] $asax, + + [Parameter()] + [System.String[]] $dependentBinaries, + + [Parameter()] + [System.String] $language, + + [Parameter()] + [System.String[]] $dependentMUIFiles, + + [Parameter()] + [System.String[]] $psFiles, + + [Parameter()] + [System.Boolean] $removeSiteFiles = $false, - $certificateThumbPrint, - $enable32BitAppOnWin64) - if (!(Test-Path $cfgfile)) - { - throw "ERROR: $cfgfile does not exist" - } + [Parameter()] + [System.String] + $certificateThumbPrint, - if (!(Test-Path $svc)) - { - throw "ERROR: $svc does not exist" - } + [Parameter()] + [System.Boolean] + $enable32BitAppOnWin64 + ) - if (!(Test-Path $mof)) + if ($certificateThumbPrint -ne 'AllowUnencryptedTraffic') { - throw "ERROR: $mof does not exist" - } + Write-Verbose -Message 'Verify that the certificate with the provided thumbprint exists in CERT:\LocalMachine\MY\' - if (!(Test-Path $asax)) - { - throw "ERROR: $asax does not exist" - } + $certificate = Get-ChildItem -Path CERT:\LocalMachine\MY\ | Where-Object -FilterScript { + $_.Thumbprint -eq $certificateThumbPrint + } - if ($certificateThumbPrint -ne "AllowUnencryptedTraffic") - { - Write-Verbose "Verify that the certificate with the provided thumbprint exists in CERT:\LocalMachine\MY\" - $certificate = Get-childItem CERT:\LocalMachine\MY\ | Where {$_.Thumbprint -eq $certificateThumbPrint} if (!$Certificate) { throw "ERROR: Certificate with thumbprint $certificateThumbPrint does not exist in CERT:\LocalMachine\MY\" @@ -65,63 +104,81 @@ function Initialize-Endpoint Test-IISInstall - $appPool = "PSWS" - + $appPool = 'PSWS' - Write-Verbose "Delete the App Pool if it exists" + Write-Verbose -Message 'Delete the App Pool if it exists' Remove-AppPool -apppool $appPool - Write-Verbose "Remove the site if it already exists" + Write-Verbose -Message 'Remove the site if it already exists' Update-Site -siteName $site -siteAction Remove - # check for existing binding, there should be no binding with the same port - if ((Get-WebBinding | where bindingInformation -eq "*:$($port):").count -gt 0) + # Check for existing binding, there should be no binding with the same port + $allWebBindingsOnPort = Get-WebBinding | Where-Object -FilterScript { + $_.BindingInformation -eq "*:$($port):" + } + + if ($allWebBindingsOnPort.Count -gt 0) { throw "ERROR: Port $port is already used, please review existing sites and change the port to be used." } if ($removeSiteFiles) { - if(Test-Path $path) + if(Test-Path -Path $path) { Remove-Item -Path $path -Recurse -Force } } - Copy-Files -path $path -cfgfile $cfgfile -svc $svc -mof $mof -dispatch $dispatch -asax $asax -dependentBinaries $dependentBinaries -language $language -dependentMUIFiles $dependentMUIFiles -psFiles $psFiles + Copy-PSWSConfigurationToIISEndpointFolder -path $path -cfgfile $cfgfile -svc $svc -mof $mof -dispatch $dispatch -asax $asax -dependentBinaries $dependentBinaries -language $language -dependentMUIFiles $dependentMUIFiles -psFiles $psFiles New-IISWebSite -site $site -path $path -port $port -app $app -apppool $appPool -applicationPoolIdentityType $applicationPoolIdentityType -certificateThumbPrint $certificateThumbPrint -enable32BitAppOnWin64 $enable32BitAppOnWin64 } -# Validate if IIS and all required dependencies are installed on the target machine -# +<# + .SYNOPSIS + Validate if IIS and all required dependencies are installed on the + target machine +#> function Test-IISInstall { - Write-Verbose "Checking IIS requirements" - $iisVersion = (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\InetStp -ErrorAction silentlycontinue).MajorVersion + [CmdletBinding()] + param() - if ($iisVersion -lt 7) - { - throw "ERROR: IIS Version detected is $iisVersion , must be running higher than 7.0" - } + Write-Verbose -Message 'Checking IIS requirements' + $iisVersion = (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\InetStp -ErrorAction silentlycontinue).MajorVersion - $wsRegKey = (Get-ItemProperty hklm:\SYSTEM\CurrentControlSet\Services\W3SVC -ErrorAction silentlycontinue).ImagePath - if ($wsRegKey -eq $null) - { - throw "ERROR: Cannot retrive W3SVC key. IIS Web Services may not be installed" - } + if ($iisVersion -lt 7) + { + throw "ERROR: IIS Version detected is $iisVersion , must be running higher than 7.0" + } - if ((Get-Service w3svc).Status -ne "running") - { - throw "ERROR: service W3SVC is not running" - } + $wsRegKey = (Get-ItemProperty hklm:\SYSTEM\CurrentControlSet\Services\W3SVC -ErrorAction silentlycontinue).ImagePath + if ($null -eq $wsRegKey) + { + throw 'ERROR: Cannot retrive W3SVC key. IIS Web Services may not be installed' + } + + if ((Get-Service w3svc).Status -ne 'running') + { + throw 'ERROR: service W3SVC is not running' + } } -# Verify if a given IIS Site exists -# -function Test-IISSiteExists +<# + .SYNOPSIS + Verify if a given IIS Site exists +#> +function Test-ForIISSite { - param ($siteName) + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter()] + [System.String] + $siteName + ) if (Get-Website -Name $siteName) { @@ -131,23 +188,31 @@ function Test-IISSiteExists return $false } -# Perform an action (such as stop, start, delete) for a given IIS Site -# +<# + .SYNOPSIS + Perform an action (such as stop, start, delete) for a given IIS Site +#> function Update-Site { - param ( - [Parameter(ParameterSetName = 'SiteName', Mandatory, Position = 0)] + param + ( + [Parameter(ParameterSetName = 'SiteName', Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] - [String]$siteName, + [System.String] + $siteName, - [Parameter(ParameterSetName = 'Site', Mandatory, Position = 0)] + [Parameter(ParameterSetName = 'Site', Mandatory = $true, Position = 0)] + [System.Object] $site, - [Parameter(ParameterSetName = 'SiteName', Mandatory, Position = 1)] - [Parameter(ParameterSetName = 'Site', Mandatory, Position = 1)] - [String]$siteAction) + [Parameter(ParameterSetName = 'SiteName', Mandatory = $true, Position = 1)] + [Parameter(ParameterSetName = 'Site', Mandatory = $true, Position = 1)] + [System.String] + $siteAction + ) + + [System.String] $name = $null - [String]$name = $null if ($PSCmdlet.ParameterSetName -eq 'SiteName') { $name = $siteName @@ -157,221 +222,271 @@ function Update-Site $name = $site.Name } - if (Test-IISSiteExists -siteName $name) + if (Test-ForIISSite -siteName $name) { switch ($siteAction) { - "Start" {Start-Website -Name "$name"} - "Stop" {Stop-Website -Name "$name" -ErrorAction SilentlyContinue} - "Remove" {Remove-Website -Name "$name"} + 'Start' {Start-Website -Name "$name"} + 'Stop' {Stop-Website -Name "$name" -ErrorAction SilentlyContinue} + 'Remove' {Remove-Website -Name "$name"} } - Write-Verbose "p11" } } -# Delete the given IIS Application Pool -# This is required to cleanup any existing conflicting apppools before setting up the endpoint -# +<# + .SYNOPSIS + Delete the given IIS Application Pool. This is required to cleanup any + existing conflicting apppools before setting up the endpoint. +#> function Remove-AppPool { - param ($appPool) - - # without this tests we may get a breaking error here, despite SilentlyContinue - if (Test-Path "IIS:\AppPools\$appPool") + [CmdletBinding()] + param + ( + [Parameter()] + [System.String] + $appPool + ) + + # Without this tests we may get a breaking error here, despite SilentlyContinue + if (Test-Path -Path "IIS:\AppPools\$appPool") { Remove-WebAppPool -Name $appPool -ErrorAction SilentlyContinue } } -# Generate an IIS Site Id while setting up the endpoint -# The Site Id will be the max available in IIS config + 1 -# +<# + .SYNOPSIS + Generate an IIS Site Id while setting up the endpoint. The Site Id will + be the max available in IIS config + 1. +#> function New-SiteID { - return ((Get-Website | % { $_.Id } | Measure-Object -Maximum).Maximum + 1) + [CmdletBinding()] + param() + + return ((Get-Website | Foreach-Object -Process { $_.Id } | Measure-Object -Maximum).Maximum + 1) } -# Validate the PSWS config files supplied and copy to the IIS endpoint in inetpub -# -function Copy-Files +<# + .SYNOPSIS + Copies the supplied PSWS config files to the IIS endpoint in inetpub +#> +function Copy-PSWSConfigurationToIISEndpointFolder { - param ( + [CmdletBinding()] + param + ( + [Parameter()] + [System.String] $path, + + [Parameter()] + [ValidateScript({Test-Path -Path $_})] + [System.String] $cfgfile, + + [Parameter()] + [ValidateScript({Test-Path -Path $_})] + [System.String] $svc, + + [Parameter()] + [ValidateScript({Test-Path -Path $_})] + [System.String] $mof, + + [Parameter()] + [System.String] $dispatch, + + [Parameter()] + [ValidateScript({Test-Path -Path $_})] + [System.String] $asax, - $dependentBinaries, - $language, - $dependentMUIFiles, - $psFiles) - if (!(Test-Path $cfgfile)) - { - throw "ERROR: $cfgfile does not exist" - } + [Parameter()] + [System.String[]] + $dependentBinaries, - if (!(Test-Path $svc)) - { - throw "ERROR: $svc does not exist" - } + [Parameter()] + [System.String] + $language, - if (!(Test-Path $mof)) - { - throw "ERROR: $mof does not exist" - } + [Parameter()] + [System.String[]] + $dependentMUIFiles, - if (!(Test-Path $asax)) - { - throw "ERROR: $asax does not exist" - } + [Parameter()] + [System.String[]] + $psFiles + ) - if (!(Test-Path $path)) + if (!(Test-Path -Path $path)) { $null = New-Item -ItemType container -Path $path } foreach ($dependentBinary in $dependentBinaries) { - if (!(Test-Path $dependentBinary)) + if (!(Test-Path -Path $dependentBinary)) { throw "ERROR: $dependentBinary does not exist" } } - <#foreach ($dependentMUIFile in $dependentMUIFiles) - { - if (!(Test-Path $dependentMUIFile)) - { - throw "ERROR: $dependentMUIFile does not exist" - } - }#> - - Write-Verbose "Create the bin folder for deploying custom dependent binaries required by the endpoint" - $binFolderPath = Join-Path $path "bin" - $null = New-Item -path $binFolderPath -itemType "directory" -Force - Copy-Item $dependentBinaries $binFolderPath -Force - - <#if ($language) - { - $muiPath = Join-Path $binFolderPath $language - - if (!(Test-Path $muiPath)) - { - $null = New-Item -ItemType container $muiPath - } - Copy-Item $dependentMUIFiles $muiPath -Force - }#> + Write-Verbose -Message 'Create the bin folder for deploying custom dependent binaries required by the endpoint' + $binFolderPath = Join-Path -Path $path -ChildPath 'bin' + $null = New-Item -Path $binFolderPath -ItemType 'directory' -Force + Copy-Item -Path $dependentBinaries -Destination $binFolderPath -Force foreach ($psFile in $psFiles) { - if (!(Test-Path $psFile)) + if (!(Test-Path -Path $psFile)) { throw "ERROR: $psFile does not exist" } - Copy-Item $psFile $path -Force + Copy-Item -Path $psFile -Destination $path -Force } - Copy-Item $cfgfile (Join-Path $path "web.config") -Force - Copy-Item $svc $path -Force - Copy-Item $mof $path -Force + Copy-Item -Path $cfgfile (Join-Path -Path $path -ChildPath 'web.config') -Force + Copy-Item -Path $svc -Destination $path -Force + Copy-Item -Path $mof -Destination $path -Force if ($dispatch) { - Copy-Item $dispatch $path -Force + Copy-Item -Path $dispatch -Destination $path -Force } if ($asax) { - Copy-Item $asax $path -Force + Copy-Item -Path $asax -Destination $path -Force } } -# Setup IIS Apppool, Site and Application -# +<# + .SYNOPSIS + Setup IIS Apppool, Site and Application +#> function New-IISWebSite { - param ( + [CmdletBinding()] + param + ( + [Parameter()] + [System.String] $site, + + [Parameter()] + [System.String] $path, + + [Parameter()] + [System.Int32] $port, + + [Parameter()] + [System.String] $app, + + [Parameter()] + [System.String] $appPool, + + [Parameter()] + [System.String] $applicationPoolIdentityType, + + [Parameter()] + [System.String] $certificateThumbPrint, - $enable32BitAppOnWin64) + + [Parameter()] + [System.Boolean] + $enable32BitAppOnWin64 + ) $siteID = New-SiteID - Write-Verbose "Adding App Pool" + Write-Verbose -Message 'Adding App Pool' $null = New-WebAppPool -Name $appPool - Write-Verbose "Set App Pool Properties" + Write-Verbose -Message 'Set App Pool Properties' $appPoolIdentity = 4 if ($applicationPoolIdentityType) { # LocalSystem = 0, LocalService = 1, NetworkService = 2, SpecificUser = 3, ApplicationPoolIdentity = 4 - if ($applicationPoolIdentityType -eq "LocalSystem") + if ($applicationPoolIdentityType -eq 'LocalSystem') { $appPoolIdentity = 0 } - elseif ($applicationPoolIdentityType -eq "LocalService") + elseif ($applicationPoolIdentityType -eq 'LocalService') { $appPoolIdentity = 1 } - elseif ($applicationPoolIdentityType -eq "NetworkService") + elseif ($applicationPoolIdentityType -eq 'NetworkService') { $appPoolIdentity = 2 } } $appPoolItem = Get-Item IIS:\AppPools\$appPool - $appPoolItem.managedRuntimeVersion = "v4.0" + $appPoolItem.managedRuntimeVersion = 'v4.0' $appPoolItem.enable32BitAppOnWin64 = $enable32BitAppOnWin64 $appPoolItem.processModel.identityType = $appPoolIdentity $appPoolItem | Set-Item - Write-Verbose "Add and Set Site Properties" - if ($certificateThumbPrint -eq "AllowUnencryptedTraffic") + Write-Verbose -Message 'Add and Set Site Properties' + if ($certificateThumbPrint -eq 'AllowUnencryptedTraffic') { - $webSite = New-WebSite -Name $site -Id $siteID -Port $port -IPAddress "*" -PhysicalPath $path -ApplicationPool $appPool + New-WebSite -Name $site -Id $siteID -Port $port -IPAddress "*" -PhysicalPath $path -ApplicationPool $appPool | Out-Null } else { - $webSite = New-WebSite -Name $site -Id $siteID -Port $port -IPAddress "*" -PhysicalPath $path -ApplicationPool $appPool -Ssl + New-WebSite -Name $site -Id $siteID -Port $port -IPAddress "*" -PhysicalPath $path -ApplicationPool $appPool -Ssl | Out-Null # Remove existing binding for $port Remove-Item IIS:\SSLBindings\0.0.0.0!$port -ErrorAction Ignore # Create a new binding using the supplied certificate - $null = Get-Item CERT:\LocalMachine\MY\$certificateThumbPrint | New-Item IIS:\SSLBindings\0.0.0.0!$port + Get-Item CERT:\LocalMachine\MY\$certificateThumbPrint | New-Item IIS:\SSLBindings\0.0.0.0!$port | Out-Null } Update-Site -siteName $site -siteAction Start } -# Allow Clients outsite the machine to access the setup endpoint on a User Port -# -function New-FirewallRule +<# + .SYNOPSIS + Allow Clients outsite the machine to access the setup endpoint on a + User Port. +#> +function Set-FirewallConfigurationToAllowPullServerAccess { - param ($firewallPort) + [CmdletBinding()] + param + ( + [Parameter()] + [System.String] + $firewallPort + ) $script:netsh = "$env:windir\system32\netsh.exe" - Write-Verbose "Disable Inbound Firewall Notification" + Write-Verbose -Message 'Disable Inbound Firewall Notification' & $script:netsh advfirewall set currentprofile settings inboundusernotification disable # remove all existing rules with that displayName & $script:netsh advfirewall firewall delete rule name=DSCPullServer_IIS_Port protocol=tcp localport=$firewallPort | Out-Null - Write-Verbose "Add Firewall Rule for port $firewallPort" + Write-Verbose -Message "Add Firewall Rule for port $firewallPort" & $script:netsh advfirewall firewall add rule name=DSCPullServer_IIS_Port dir=in action=allow protocol=TCP localport=$firewallPort } -# Enable & Clear PSWS Operational/Analytic/Debug ETW Channels -# +<# + .SYNOPSIS + Enable & Clear PSWS Operational/Analytic/Debug ETW Channels. +#> function Enable-PSWSETW { # Disable Analytic Log @@ -391,98 +506,151 @@ function Enable-PSWSETW } <# -.Synopsis - Create PowerShell WebServices IIS Endpoint -.DESCRIPTION - Creates a PSWS IIS Endpoint by consuming PSWS Schema and related dependent files -.EXAMPLE - New a PSWS Endpoint [@ http://Server:39689/PSWS_Win32Process] by consuming PSWS Schema Files and any dependent scripts/binaries - New-PSWSEndpoint -site Win32Process -path $env:SystemDrive\inetpub\PSWS_Win32Process -cfgfile Win32Process.config -port 39689 -app Win32Process -svc PSWS.svc -mof Win32Process.mof -dispatch Win32Process.xml -dependentBinaries ConfigureProcess.ps1, Rbac.dll -psFiles Win32Process.psm1 + .SYNOPSIS + Create PowerShell WebServices IIS Endpoint + + .DESCRIPTION + Creates a PSWS IIS Endpoint by consuming PSWS Schema and related + dependent files + + .EXAMPLE + New PSWS Endpoint [@ http://Server:39689/PSWS_Win32Process] by + consuming PSWS Schema Files and any dependent scripts/binaries: + + New-PSWSEndpoint + -site Win32Process + -path $env:SystemDrive\inetpub\PSWS_Win32Process + -cfgfile Win32Process.config + -port 39689 + -app Win32Process + -svc PSWS.svc + -mof Win32Process.mof + -dispatch Win32Process.xml + -dependentBinaries ConfigureProcess.ps1, Rbac.dll + -psFiles Win32Process.psm1 #> function New-PSWSEndpoint { -[CmdletBinding()] - param ( - + [CmdletBinding()] + param + ( # Unique Name of the IIS Site - [String] $site = "PSWS", + [Parameter()] + [System.String] + $site = 'PSWS', # Physical path for the IIS Endpoint on the machine (under inetpub) - [String] $path = "$env:SystemDrive\inetpub\PSWS", + [Parameter()] + [System.String] + $path = "$env:SystemDrive\inetpub\PSWS", # Web.config file - [String] $cfgfile = "web.config", + [Parameter()] + [System.String] + $cfgfile = 'web.config', # Port # for the IIS Endpoint - [Int] $port = 8080, + [Parameter()] + [Int] + $port = 8080, # IIS Application Name for the Site - [String] $app = "PSWS", + [Parameter()] + [System.String] + $app = 'PSWS', # IIS App Pool Identity Type - must be one of LocalService, LocalSystem, NetworkService, ApplicationPoolIdentity + [Parameter()] [ValidateSet('LocalService', 'LocalSystem', 'NetworkService', 'ApplicationPoolIdentity')] - [String] $applicationPoolIdentityType, + [System.String] + $applicationPoolIdentityType, # WCF Service SVC file - [String] $svc = "PSWS.svc", + [Parameter()] + [System.String] + $svc = 'PSWS.svc', # PSWS Specific MOF Schema File - [parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] $mof, + [System.String] + $mof, # PSWS Specific Dispatch Mapping File [Optional] + [Parameter()] [ValidateNotNullOrEmpty()] - [String] $dispatch, + [System.String] + $dispatch, # Global.asax file [Optional] + [Parameter()] [ValidateNotNullOrEmpty()] - [String] $asax, + [System.String] + $asax, # Any dependent binaries that need to be deployed to the IIS endpoint, in the bin folder + [Parameter()] [ValidateNotNullOrEmpty()] - [String[]] $dependentBinaries, + [String[]] + $dependentBinaries, # MUI Language [Optional] + [Parameter()] [ValidateNotNullOrEmpty()] - [String] $language, + [System.String] + $language, # Any dependent binaries that need to be deployed to the IIS endpoint, in the bin\mui folder [Optional] + [Parameter()] [ValidateNotNullOrEmpty()] - [String[]] $dependentMUIFiles, + [String[]] + $dependentMUIFiles, # Any dependent PowerShell Scipts/Modules that need to be deployed to the IIS endpoint application root + [Parameter()] [ValidateNotNullOrEmpty()] - [String[]] $psFiles, + [String[]] + $psFiles, # True to remove all files for the site at first, false otherwise - [Boolean]$removeSiteFiles = $false, + [Parameter()] + [System.Boolean] + $removeSiteFiles = $false, # Enable Firewall Exception for the supplied port - [Boolean] $EnableFirewallException, + [Parameter()] + [System.Boolean] + $EnableFirewallException, # Enable and Clear PSWS ETW - [switch] $EnablePSWSETW, + [Parameter()] + [System.Management.Automation.SwitchParameter] + $EnablePSWSETW, # Thumbprint of the Certificate in CERT:\LocalMachine\MY\ for Pull Server - [String] $certificateThumbPrint = "AllowUnencryptedTraffic", + [Parameter()] + [System.String] + $certificateThumbPrint = 'AllowUnencryptedTraffic', # When this property is set to true, Pull Server will run on a 32 bit process on a 64 bit machine - [boolean]$Enable32BitAppOnWin64 = $false) + [Parameter()] + [System.Boolean] + $Enable32BitAppOnWin64 = $false + ) $script:wevtutil = "$env:windir\system32\Wevtutil.exe" $svcName = Split-Path $svc -Leaf - $protocol = "https:" - if ($certificateThumbPrint -eq "AllowUnencryptedTraffic") + $protocol = 'https:' + if ($certificateThumbPrint -eq 'AllowUnencryptedTraffic') { - $protocol = "http:" + $protocol = 'http:' } # Get Machine Name $cimInstance = Get-CimInstance -ClassName Win32_ComputerSystem -Verbose:$false - Write-Verbose ("Setting up endpoint at - $protocol//" + $cimInstance.Name + ":" + $port + "/" + $svcName) + Write-Verbose ("Setting up endpoint at - $protocol//" + $cimInstance.Name + ':' + $port + '/' + $svcName) Initialize-Endpoint -site $site -path $path -cfgfile $cfgfile -port $port -app $app ` -applicationPoolIdentityType $applicationPoolIdentityType -svc $svc -mof $mof ` -dispatch $dispatch -asax $asax -dependentBinaries $dependentBinaries ` @@ -492,8 +660,8 @@ function New-PSWSEndpoint if ($EnableFirewallException -eq $true) { - Write-Verbose "Enabling firewall exception for port $port" - $null = New-FirewallRule $port + Write-Verbose -Message "Enabling firewall exception for port $port" + $null = Set-FirewallConfigurationToAllowPullServerAccess $port } if ($EnablePSWSETW) @@ -503,97 +671,107 @@ function New-PSWSEndpoint } <# -.Synopsis - Removes a DSC WebServices IIS Endpoint -.DESCRIPTION - Removes a PSWS IIS Endpoint -.EXAMPLE - Remove the endpoint with the specified name - Remove-PSWSEndpoint -siteName PSDSCPullServer + .SYNOPSIS + Removes a DSC WebServices IIS Endpoint + + .DESCRIPTION + Removes a PSWS IIS Endpoint + + .EXAMPLE + Remove the endpoint with the specified name: + + Remove-PSWSEndpoint -siteName PSDSCPullServer #> function Remove-PSWSEndpoint { -[CmdletBinding()] - param ( + [CmdletBinding()] + param + ( # Unique Name of the IIS Site - [String] $siteName - ) - - # get the site to remove - $site = Get-Item -Path "IIS:\sites\$siteName" - # and the pool it is using - $pool = $site.applicationPool - - # get the path so we can delete the files - $filePath = $site.PhysicalPath - # get the port number for the Firewall rule - $bindings = (Get-WebBinding -Name $siteName).bindingInformation - $port = [regex]::match($bindings,':(\d+):').Groups[1].Value - - # remove the actual site. - Remove-Website -Name $siteName - # there may be running requests, wait a little - # I had an issue where the files were still in use - # when I tried to delete them - Start-Sleep -Milliseconds 200 - - # remove the files for the site - If (Test-Path $filePath) - { - Get-ChildItem $filePath -Recurse | Remove-Item -Recurse - Remove-Item $filePath - } - - # find out whether any other site is using this pool - $filter = "/system.applicationHost/sites/site/application[@applicationPool='" + $pool + "']" - $apps = (Get-WebConfigurationProperty -Filter $filter -PSPath "machine/webroot/apphost" -name path).ItemXPath - if (-not $apps -or $apps.count -eq 1) - { - # if we are the only site in the pool, remove the pool as well. - Remove-WebAppPool -Name $pool - } - - - # remove all rules with that name - $ruleName = ($($FireWallRuleDisplayName) -f $port) - Get-NetFirewallRule | Where-Object DisplayName -eq "$ruleName" | Remove-NetFirewallRule + [Parameter()] + [System.String] + $siteName + ) + + # Get the site to remove + $site = Get-Item -Path "IIS:\sites\$siteName" + # And the pool it is using + $pool = $site.applicationPool + + # Get the path so we can delete the files + $filePath = $site.PhysicalPath + # Get the port number for the Firewall rule + $bindings = (Get-WebBinding -Name $siteName).bindingInformation + $port = [regex]::match($bindings,':(\d+):').Groups[1].Value + + # Remove the actual site. + Remove-Website -Name $siteName + <# + There may be running requests, wait a little + I had an issue where the files were still in use + when I tried to delete them + #> + Start-Sleep -Milliseconds 200 + + # Remove the files for the site + If (Test-Path -Path $filePath) + { + Get-ChildItem -Path $filePath -Recurse | Remove-Item -Recurse + Remove-Item -Path $filePath + } + + # Find out whether any other site is using this pool + $filter = "/system.applicationHost/sites/site/application[@applicationPool='" + $pool + "']" + $apps = (Get-WebConfigurationProperty -Filter $filter -PSPath 'machine/webroot/apphost' -name path).ItemXPath + if (-not $apps -or $apps.count -eq 1) + { + # If we are the only site in the pool, remove the pool as well. + Remove-WebAppPool -Name $pool + } + # Remove all rules with that name + $ruleName = ($($FireWallRuleDisplayName) -f $port) + Get-NetFirewallRule | Where-Object DisplayName -eq "$ruleName" | Remove-NetFirewallRule } <# -.Synopsis - Set the option into the web.config for an endpoint -.DESCRIPTION - Set the options into the web.config for an endpoint allowing customization. -.EXAMPLE + .SYNOPSIS + Set the option into the web.config for an endpoint + + .DESCRIPTION + Set the options into the web.config for an endpoint allowing + customization. #> function Set-AppSettingsInWebconfig { - param ( - + [CmdletBinding()] + param + ( # Physical path for the IIS Endpoint on the machine (possibly under inetpub) - [parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] $path, + [System.String] + $path, # Key to add/update - [parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] $key, + [System.String] + $key, # Value - [parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] $value - - ) + [System.String] + $value + ) - $webconfig = Join-Path $path "web.config" - [bool] $Found = $false + $webconfig = Join-Path $path 'web.config' + [System.Boolean] $Found = $false - if (Test-Path $webconfig) + if (Test-Path -Path $webconfig) { - $xml = [xml](get-content $webconfig) + $xml = [System.Xml.XmlDocument] (Get-Content -Path $webconfig) $root = $xml.get_DocumentElement() foreach( $item in $root.appSettings.add) @@ -607,16 +785,16 @@ function Set-AppSettingsInWebconfig if( -not $Found) { - $newElement = $xml.CreateElement("add") - $nameAtt1 = $xml.CreateAttribute("key") + $newElement = $xml.CreateElement('add') + $nameAtt1 = $xml.CreateAttribute('key') $nameAtt1.psbase.value = $key; $null = $newElement.SetAttributeNode($nameAtt1) - $nameAtt2 = $xml.CreateAttribute("value") + $nameAtt2 = $xml.CreateAttribute('value') $nameAtt2.psbase.value = $value; $null = $newElement.SetAttributeNode($nameAtt2) - $null = $xml.configuration["appSettings"].AppendChild($newElement) + $null = $xml.configuration['appSettings'].AppendChild($newElement) } } @@ -624,68 +802,75 @@ function Set-AppSettingsInWebconfig } <# -.Synopsis - Set the binding redirect setting in the web.config to redirect 10.0.0.0 version of microsoft.isam.esent.interop to 6.3.0.0. -.DESCRIPTION - This function creates the following section in the web.config: - - - - - - - - + .SYNOPSIS + Set the binding redirect setting in the web.config to redirect 10.0.0.0 + version of microsoft.isam.esent.interop to 6.3.0.0. + + .DESCRIPTION + This function creates the following section in the web.config: + + + + + + + + #> function Set-BindingRedirectSettingInWebConfig { - param ( - + [CmdletBinding()] + param + ( # Physical path for the IIS Endpoint on the machine (possibly under inetpub) - [parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] $path, + [System.String] + $path, # old version of the assembly - [String] $oldVersion = "10.0.0.0", + [Parameter()] + [System.String] + $oldVersion = '10.0.0.0', # new version to redirect to - [String] $newVersion = "6.3.0.0" - - ) + [Parameter()] + [System.String] + $newVersion = '6.3.0.0' + ) - $webconfig = Join-Path $path "web.config" + $webconfig = Join-Path $path 'web.config' - if (Test-Path $webconfig) + if (Test-Path -Path $webconfig) { - $xml = [xml](get-content $webconfig) + $xml = [System.Xml.XmlDocument] (Get-Content -Path $webconfig) if(-not($xml.get_DocumentElement().runtime)) { # Create the section - $runtimeSetting = $xml.CreateElement("runtime") + $runtimeSetting = $xml.CreateElement('runtime') # Create the section - $assemblyBindingSetting = $xml.CreateElement("assemblyBinding") - $xmlnsAttribute = $xml.CreateAttribute("xmlns") - $xmlnsAttribute.Value = "urn:schemas-microsoft-com:asm.v1" + $assemblyBindingSetting = $xml.CreateElement('assemblyBinding') + $xmlnsAttribute = $xml.CreateAttribute('xmlns') + $xmlnsAttribute.Value = 'urn:schemas-microsoft-com:asm.v1' $assemblyBindingSetting.Attributes.Append($xmlnsAttribute) # The section goes inside $null = $runtimeSetting.AppendChild($assemblyBindingSetting) # Create the section - $dependentAssemblySetting = $xml.CreateElement("dependentAssembly") + $dependentAssemblySetting = $xml.CreateElement('dependentAssembly') - #The section goes inside + # The section goes inside $null = $assemblyBindingSetting.AppendChild($dependentAssemblySetting) # Create the section - $assemblyIdentitySetting = $xml.CreateElement("assemblyIdentity") - $nameAttribute = $xml.CreateAttribute("name") - $nameAttribute.Value = "microsoft.isam.esent.interop" - $publicKeyTokenAttribute = $xml.CreateAttribute("publicKeyToken") - $publicKeyTokenAttribute.Value = "31bf3856ad364e35" + $assemblyIdentitySetting = $xml.CreateElement('assemblyIdentity') + $nameAttribute = $xml.CreateAttribute('name') + $nameAttribute.Value = 'microsoft.isam.esent.interop' + $publicKeyTokenAttribute = $xml.CreateAttribute('publicKeyToken') + $publicKeyTokenAttribute.Value = '31bf3856ad364e35' $null = $assemblyIdentitySetting.Attributes.Append($nameAttribute) $null = $assemblyIdentitySetting.Attributes.Append($publicKeyTokenAttribute) @@ -693,9 +878,9 @@ function Set-BindingRedirectSettingInWebConfig $dependentAssemblySetting.AppendChild($assemblyIdentitySetting) # Create the section - $bindingRedirectSetting = $xml.CreateElement("bindingRedirect") - $oldVersionAttribute = $xml.CreateAttribute("oldVersion") - $newVersionAttribute = $xml.CreateAttribute("newVersion") + $bindingRedirectSetting = $xml.CreateElement('bindingRedirect') + $oldVersionAttribute = $xml.CreateAttribute('oldVersion') + $newVersionAttribute = $xml.CreateAttribute('newVersion') $oldVersionAttribute.Value = $oldVersion $newVersionAttribute.Value = $newVersion $null = $bindingRedirectSetting.Attributes.Append($oldVersionAttribute) diff --git a/DSCResources/MSFT_xDSCWebService/UseSecurityBestPractices.psm1 b/DSCResources/MSFT_xDSCWebService/UseSecurityBestPractices.psm1 index a9dc7a7c9..a99f389ec 100644 --- a/DSCResources/MSFT_xDSCWebService/UseSecurityBestPractices.psm1 +++ b/DSCResources/MSFT_xDSCWebService/UseSecurityBestPractices.psm1 @@ -6,15 +6,22 @@ Import-Module $PSScriptRoot\SecureTLSProtocols.psm1 -Verbose:$false # This list corresponds to the ValueMap definition of DisableSecurityBestPractices parameter defined in MSFT_xDSCWebService.Schema.mof -$SecureTLSProtocols = "SecureTLSProtocols"; +$SecureTLSProtocols = 'SecureTLSProtocols' <# .SYNOPSIS This function tests whether the node uses security best practices for non-disabled items #> -function Test-UseSecurityBestPractices +function Test-UseSecurityBestPractice { - param([string[]] $DisableSecurityBestPractices) + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter()] + [System.String[]] + $DisableSecurityBestPractices + ) $usedProtocolsBestPractices = ($DisableSecurityBestPractices -icontains $SecureTLSProtocols) -or (Test-SChannelProtocol) @@ -25,9 +32,15 @@ function Test-UseSecurityBestPractices .SYNOPSIS This function sets the node to use security best practices for non-disabled items #> -function Set-UseSecurityBestPractices +function Set-UseSecurityBestPractice { - param([string[]] $DisableSecurityBestPractices) + [CmdletBinding()] + param + ( + [Parameter()] + [System.String[]] + $DisableSecurityBestPractices + ) if (-not ($DisableSecurityBestPractices -icontains $SecureTLSProtocols)) { @@ -35,4 +48,4 @@ function Set-UseSecurityBestPractices } } -Export-ModuleMember -function *-UseSecurityBestPractices +Export-ModuleMember -Function *-UseSecurityBestPractice diff --git a/DSCResources/MSFT_xEnvironmentResource/MSFT_xEnvironmentResource.psm1 b/DSCResources/MSFT_xEnvironmentResource/MSFT_xEnvironmentResource.psm1 index 52f9bfa8c..42cd0001f 100644 --- a/DSCResources/MSFT_xEnvironmentResource/MSFT_xEnvironmentResource.psm1 +++ b/DSCResources/MSFT_xEnvironmentResource/MSFT_xEnvironmentResource.psm1 @@ -29,25 +29,26 @@ $script:maxUserEnvVariableLength = 255 #> function Get-TargetResource { - [CmdletBinding()] - [OutputType([Hashtable])] + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Name, - + + [Parameter()] [ValidateSet('Process', 'Machine')] [ValidateNotNullOrEmpty()] - [String[]] - $Target = ('Process', 'Machine') + [System.String[]] + $Target = ('Process', 'Machine') ) - + $valueToReturn = $null if ($Target -contains 'Machine') - { + { $environmentVaraible = Get-EnvironmentVariableWithoutExpanding -Name $Name -ErrorAction 'SilentlyContinue' if ($null -ne $environmentVaraible) @@ -59,17 +60,17 @@ function Get-TargetResource { $valueToReturn = Get-ProcessEnvironmentVariable -Name $Name } - + $environmentResource = @{ Name = $Name Value = $null Ensure = 'Absent' } - + if ($null -eq $valueToReturn) - { + { Write-Verbose -Message ($script:localizedData.EnvVarNotFound -f $Name) - } + } else { Write-Verbose -Message ($script:localizedData.EnvVarFound -f $Name, $valueToReturn) @@ -83,7 +84,7 @@ function Get-TargetResource <# .SYNOPSIS Creates, modifies, or removes an environment variable. - + .PARAMETER Name The name of the environment variable to create, modify, or remove. @@ -116,30 +117,34 @@ function Set-TargetResource { [CmdletBinding()] param - ( + ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Name, - + + [Parameter()] [ValidateNotNull()] - [String] - $Value = [String]::Empty, - + [System.String] + $Value = [System.String]::Empty, + + [Parameter()] [ValidateSet('Present', 'Absent')] - [String] + [System.String] $Ensure = 'Present', - - [Boolean] + + [Parameter()] + [System.Boolean] $Path = $false, + [Parameter()] [ValidateSet('Process', 'Machine')] [ValidateNotNullOrEmpty()] - [String[]] + [System.String[]] $Target = ('Process', 'Machine') ) - - $valueSpecified = ($Value -ne [String]::Empty) + + $valueSpecified = ($Value -ne [System.String]::Empty) $currentValueFromMachine = $null $currentValueFromProcess = $null $currentPropertiesFromMachine = $null @@ -157,7 +162,7 @@ function Set-TargetResource { $currentValueFromMachine = $currentPropertiesFromMachine.$Name } - } + } else { $currentPropertiesFromMachine = Get-ItemProperty -Path $script:envVarRegPathMachine -Name $Name -ErrorAction 'SilentlyContinue' @@ -187,12 +192,11 @@ function Set-TargetResource if ($Ensure -eq 'Present') { - $createMachineVariable = ((-not $setMachineVariable) -or ($null -eq $currentPropertiesFromMachine) -or ($currentValueFromMachine -eq [String]::Empty)) - $createProcessVariable = ((-not $setProcessVariable) -or ($null -eq $currentValueFromProcess) -or ($currentValueFromProcess -eq [String]::Empty)) + $createMachineVariable = ((-not $setMachineVariable) -or ($null -eq $currentPropertiesFromMachine) -or ($currentValueFromMachine -eq [System.String]::Empty)) + $createProcessVariable = ((-not $setProcessVariable) -or ($null -eq $currentValueFromProcess) -or ($currentValueFromProcess -eq [System.String]::Empty)) if ($createMachineVariable -and $createProcessVariable) { - if (-not $valueSpecified) { <# @@ -210,7 +214,7 @@ function Set-TargetResource #> Set-EnvironmentVariable -Name $Name -Value $Value -Target $Target - + Write-Verbose -Message ($script:localizedData.EnvVarCreated -f $Name, $Value) return } @@ -230,15 +234,15 @@ function Set-TargetResource # Check if an empty, whitespace or semi-colon only string has been specified. If yes, return unchanged. $trimmedValue = $Value.Trim(';').Trim() - if ([String]::IsNullOrEmpty($trimmedValue)) + if ([System.String]::IsNullOrEmpty($trimmedValue)) { Write-Verbose -Message ($script:localizedData.EnvVarPathUnchanged -f $Name, $currentValueToDisplay) - return + return } if (-not $Path) { - # For non-path variables, simply set the specified $Value as the new value of the specified + # For non-path variables, simply set the specified $Value as the new value of the specified # variable $Name for the given $Target if (($setMachineVariable -and ($Value -cne $currentValueFromMachine)) -or ` @@ -276,7 +280,7 @@ function Set-TargetResource if ($setProcessVariable) { $valueUnchanged = Test-PathsInValue -ExistingPaths $currentValueFromProcess -QueryPaths $trimmedValue -FindCriteria 'All' - + if ($currentValueFromProcess -and -not $valueUnchanged) { $updatedValue = Add-PathsToValue -CurrentValue $currentValueFromProcess -NewValue $trimmedValue @@ -299,10 +303,10 @@ function Set-TargetResource if ($machineVariableRemoved -and $processVariableRemoved) { # Variable not found, condition is satisfied and there is nothing to set/remove, return - Write-Verbose -Message ($script:localizedData.EnvVarNotFound -f $Name) + Write-Verbose -Message ($script:localizedData.EnvVarNotFound -f $Name) return } - + if ((-not $ValueSpecified) -or (-not $Path)) { <# @@ -319,7 +323,7 @@ function Set-TargetResource # Check if an empty string or semi-colon only string has been specified as $Value. If yes, return unchanged as we don't need to remove anything. $trimmedValue = $Value.Trim(';').Trim() - if ([String]::IsNullOrEmpty($trimmedValue)) + if ([System.String]::IsNullOrEmpty($trimmedValue)) { Write-Verbose -Message ($script:localizedData.EnvVarPathUnchanged -f $Name, $currentValueToDisplay) return @@ -330,7 +334,7 @@ function Set-TargetResource if ($setMachineVariable) { $finalPath = $null - + if ($currentValueFromMachine) { <# @@ -342,7 +346,7 @@ function Set-TargetResource $finalPath = Remove-PathsFromValue -CurrentValue $currentValueFromMachine -PathsToRemove $trimmedValue } - if ([String]::IsNullOrEmpty($finalPath)) + if ([System.String]::IsNullOrEmpty($finalPath)) { Remove-EnvironmentVariable -Name $Name -Target @('Machine') Write-Verbose -Message ($script:localizedData.EnvVarRemoved -f $Name) @@ -355,13 +359,13 @@ function Set-TargetResource { Set-EnvironmentVariable -Name $Name -Value $finalPath -Target @('Machine') Write-Verbose -Message ($script:localizedData.EnvVarPathUpdated -f $Name, $currentValueFromMachine, $finalPath) - } + } } if ($setProcessVariable) { $finalPath = $null - + if ($currentValueFromProcess) { <# @@ -373,7 +377,7 @@ function Set-TargetResource $finalPath = Remove-PathsFromValue -CurrentValue $currentValueFromProcess -PathsToRemove $trimmedValue } - if ([String]::IsNullOrEmpty($finalPath)) + if ([System.String]::IsNullOrEmpty($finalPath)) { Remove-EnvironmentVariable -Name $Name -Target @('Process') Write-Verbose -Message ($script:localizedData.EnvVarRemoved -f $Name) @@ -386,7 +390,7 @@ function Set-TargetResource { Set-EnvironmentVariable -Name $Name -Value $finalPath -Target @('Process') Write-Verbose -Message ($script:localizedData.EnvVarPathUpdated -f $Name, $currentValueFromProcess, $finalPath) - } + } } } } @@ -394,7 +398,7 @@ function Set-TargetResource <# .SYNOPSIS Tests if the environment variable is in the desired state. - + .PARAMETER Name The name of the environment variable to test. @@ -422,32 +426,36 @@ function Set-TargetResource function Test-TargetResource { [CmdletBinding()] - [OutputType([Boolean])] + [OutputType([System.Boolean])] param - ( + ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Name, - + + [Parameter()] [ValidateNotNull()] - [String] + [System.String] $Value, + [Parameter()] [ValidateSet('Present', 'Absent')] - [String] + [System.String] $Ensure = 'Present', - - [Boolean] + + [Parameter()] + [System.Boolean] $Path = $false, + [Parameter()] [ValidateSet('Process', 'Machine')] [ValidateNotNullOrEmpty()] - [String[]] + [System.String[]] $Target = ('Process', 'Machine') ) - - $valueSpecified = $PSBoundParameters.ContainsKey('Value') -and ($Value -ne [String]::Empty) + + $valueSpecified = $PSBoundParameters.ContainsKey('Value') -and ($Value -ne [System.String]::Empty) $currentValueFromMachine = $null $currentValueFromProcess = $null $currentPropertiesFromMachine = $null @@ -465,7 +473,7 @@ function Test-TargetResource { $currentValueFromMachine = $currentPropertiesFromMachine.$Name } - } + } else { $currentPropertiesFromMachine = Get-ItemProperty -Path $script:envVarRegPathMachine -Name $Name -ErrorAction 'SilentlyContinue' @@ -492,7 +500,7 @@ function Test-TargetResource { $currentValueToDisplay = $currentValueFromProcess } - + if (($checkMachineTarget -and ($null -eq $currentPropertiesFromMachine)) -or ($checkProcessTarget -and ($null -eq $currentValueFromProcess))) { # Variable not found @@ -505,7 +513,7 @@ function Test-TargetResource Write-Verbose ($script:localizedData.EnvVarFound -f $Name, $currentValueToDisplay) return ($Ensure -eq 'Present') } - + if (-not $Path) { # For this non-path variable, make sure that the specified $Value matches the current value. @@ -525,12 +533,12 @@ function Test-TargetResource # If the control reaches here, the expected environment variable exists, it is a path variable and a $Value is specified to test against if ($Ensure -eq 'Present') - { + { if ($checkMachineTarget) - { + { if (-not (Test-PathsInValue -ExistingPaths $currentValueFromMachine -QueryPaths $Value -FindCriteria 'All')) { - # If the control reached here some part of the specified path ($Value) was not found in the existing variable, return failure + # If the control reached here some part of the specified path ($Value) was not found in the existing variable, return failure Write-Verbose ($script:localizedData.EnvVarFoundWithMisMatchingValue -f $Name, $currentValueToDisplay, $Value) return $false } @@ -540,7 +548,7 @@ function Test-TargetResource { if (-not (Test-PathsInValue -ExistingPaths $currentValueFromProcess -QueryPaths $Value -FindCriteria 'All')) { - # If the control reached here some part of the specified path ($Value) was not found in the existing variable, return failure + # If the control reached here some part of the specified path ($Value) was not found in the existing variable, return failure Write-Verbose ($script:localizedData.EnvVarFoundWithMisMatchingValue -f $Name, $currentValueToDisplay, $Value) return $false } @@ -552,7 +560,7 @@ function Test-TargetResource } # Ensure = 'Absent' else - { + { if ($checkMachineTarget) { if (Test-PathsInValue -ExistingPaths $currentValueFromMachine -QueryPaths $Value -FindCriteria 'Any') @@ -572,7 +580,7 @@ function Test-TargetResource return $false } } - + # If the control reached here, none of the specified paths were found in the existing path-variable, return success Write-Verbose ($script:localizedData.EnvVarFoundWithMisMatchingValue -f $Name, $currentValueToDisplay, $Value) return $true @@ -582,7 +590,7 @@ function Test-TargetResource <# .SYNOPSIS Retrieves the value of the environment variable from the given Target. - + .PARAMETER Name The name of the environment variable to retrieve the value from. @@ -594,23 +602,23 @@ function Test-TargetResource function Get-EnvironmentVariable { [CmdletBinding()] - [OutputType([String])] + [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Name, [Parameter(Mandatory = $true)] [ValidateSet('Process', 'Machine')] - [String] + [System.String] $Target ) $valueToReturn = $null - if ($Target -eq 'Process') + if ($Target -eq 'Process') { $valueToReturn = Get-ProcessEnvironmentVariable -Name $Name } @@ -632,27 +640,26 @@ function Get-EnvironmentVariable $valueToReturn = $retrievedProperty.$Name } } - + return $valueToReturn } <# .SYNOPSIS Wrapper function to retrieve an environment variable from the current process. - + .PARAMETER Name The name of the variable to retrieve - #> function Get-ProcessEnvironmentVariable { [CmdletBinding()] - [OutputType([String])] + [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Name ) @@ -664,28 +671,28 @@ function Get-ProcessEnvironmentVariable If there are any paths in NewPaths that aren't in CurrentValue they will be added to the current paths value and a String will be returned containing all old paths and new paths. Otherwise the original value will be returned unchanged. - + .PARAMETER CurrentValue A semicolon-separated String containing the current path values. .PARAMETER NewPaths A semicolon-separated String containing any paths that should be added to - the current value. If CurrentValue already contains a path, it will not be added. + the current value. If CurrentValue already contains a path, it will not be added. #> function Add-PathsToValue { [CmdletBinding()] - [OutputType([String])] + [OutputType([System.String])] param - ( + ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $CurrentValue, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $NewValue ) @@ -693,8 +700,8 @@ function Add-PathsToValue $currentPaths = $CurrentValue -split ';' $newPaths = $NewValue -split ';' - foreach ($path in $newPaths) - { + foreach ($path in $newPaths) + { if ($currentPaths -notcontains $path) { <# @@ -703,8 +710,8 @@ function Add-PathsToValue #> $finalValue += ($path + ';') - } - } + } + } # Remove any extraneous ';' at the end (and potentially start - as a side-effect) of the value to be set return $finalValue.Trim(';') @@ -717,7 +724,7 @@ function Add-PathsToValue paths that remain, or an empty string will be returned if all paths were removed. If none of the paths in PathsToRemove are in CurrentValue then this function will return CurrentValue since nothing needs to be changed. - + .PARAMETER CurrentValue A semicolon-separated String containing the current path values. @@ -727,18 +734,18 @@ function Add-PathsToValue #> function Remove-PathsFromValue { - [OutputType([String])] + [OutputType([System.String])] [CmdletBinding()] param - ( + ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $CurrentValue, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $PathsToRemove ) @@ -761,12 +768,12 @@ function Remove-PathsFromValue { # the current $subpath was not part of the $specifiedPaths (to be removed) so keep this $subpath in the finalPath $finalPath += $subpath + ';' - } - } - - # Remove any extraneous ';' at the end (and potentially start - as a side-effect) of the $finalPath - $finalPath = $finalPath.Trim(';') - + } + } + + # Remove any extraneous ';' at the end (and potentially start - as a side-effect) of the $finalPath + $finalPath = $finalPath.Trim(';') + if ($varAltered) { return $finalPath @@ -781,14 +788,14 @@ function Remove-PathsFromValue .SYNOPSIS Sets the value of the environment variable with the given name if a value is specified. If no value is specified, then the environment variable will be removed. - + .PARAMETER Name The name of the environment variable to set or remove. .PARAMETER Value The value to set the environment variable to. If not provided, then the variable will be removed. - + .PARAMETER Target Indicates where to set or remove the environment variable: The machine, the process, or both. The logic for User is also included here for future expansion of this resource. @@ -797,18 +804,19 @@ function Set-EnvironmentVariable { [CmdletBinding()] param - ( + ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Name, - [String] + [Parameter()] + [System.String] $Value, [Parameter(Mandatory = $true)] [ValidateSet('Process', 'Machine')] - [String[]] + [System.String[]] $Target ) @@ -816,8 +824,8 @@ function Set-EnvironmentVariable try { - # If the Value is set to [String]::Empty then nothing should be updated for the process - if (($Target -contains 'Process') -and (-not $valueSpecified -or ($Value -ne [String]::Empty))) + # If the Value is set to [System.String]::Empty then nothing should be updated for the process + if (($Target -contains 'Process') -and (-not $valueSpecified -or ($Value -ne [System.String]::Empty))) { if (-not $valueSpecified) { @@ -838,7 +846,7 @@ function Set-EnvironmentVariable $path = $script:envVarRegPathMachine - if (-not $valueSpecified) + if (-not $valueSpecified) { $environmentKey = Get-ItemProperty -Path $path -Name $Name -ErrorAction 'SilentlyContinue' @@ -857,7 +865,7 @@ function Set-EnvironmentVariable Set-ItemProperty -Path $path -Name $Name -Value $Value $environmentKey = Get-ItemProperty -Path $path -Name $Name -ErrorAction 'SilentlyContinue' - if ($null -eq $environmentKey) + if ($null -eq $environmentKey) { $message = ($script:localizedData.GetItemPropertyFailure -f $Name, $path) New-InvalidArgumentException -Message $message -ArgumentName $Name @@ -875,7 +883,7 @@ function Set-EnvironmentVariable $path = $script:envVarRegPathUser - if (-not $valueSpecified) + if (-not $valueSpecified) { $environmentKey = Get-ItemProperty -Path $path -Name $Name -ErrorAction 'SilentlyContinue' @@ -894,7 +902,7 @@ function Set-EnvironmentVariable Set-ItemProperty -Path $path -Name $Name -Value $Value $environmentKey = Get-ItemProperty -Path $path -Name $Name -ErrorAction 'SilentlyContinue' - if ($null -eq $environmentKey) + if ($null -eq $environmentKey) { $message = ($script:localizedData.GetItemPropertyFailure -f $Name, $path) New-InvalidArgumentException -Message $message -ArgumentName $Name @@ -902,7 +910,7 @@ function Set-EnvironmentVariable } } } - catch + catch { New-InvalidOperationException -Message ($script:localizedData.EnvVarSetError -f $Name, $Value) ` -ErrorRecord $_ @@ -913,13 +921,12 @@ function Set-EnvironmentVariable <# .SYNOPSIS Wrapper function to set an environment variable for the current process. - + .PARAMETER Name The name of the environment variable to set. .PARAMETER Value The value to set the environment variable to. - #> function Set-ProcessEnvironmentVariable { @@ -928,11 +935,12 @@ function Set-ProcessEnvironmentVariable ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Name, - [String] - $Value = [String]::Empty + [Parameter()] + [System.String] + $Value = [System.String]::Empty ) [System.Environment]::SetEnvironmentVariable($Name, $Value) @@ -942,7 +950,7 @@ function Set-ProcessEnvironmentVariable .SYNOPSIS Removes an environment variable from the given target(s) by calling Set-EnvironmentVariable with no Value specified. - + .PARAMETER Name The name of the environment variable to remove. @@ -953,23 +961,23 @@ function Remove-EnvironmentVariable { [CmdletBinding()] param - ( + ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Name, [Parameter(Mandatory = $true)] [ValidateSet('Process', 'Machine')] - [String[]] + [System.String[]] $Target ) - + try { Set-EnvironmentVariable -Name $Name -Target $Target } - catch + catch { New-InvalidOperationException -Message ($script:localizedData.EnvVarRemoveError -f $Name) ` -ErrorRecord $_ @@ -983,7 +991,7 @@ function Remove-EnvironmentVariable paths in QueryPaths are in ExistingPaths, otherwise it will return False. If FindCriteria is set to 'Any' then it will return True if any of the paths in QueryPaths are in ExistingPaths, otherwise it will return False. - + .PARAMETER ExistingPaths A semicolon-separated String containing the path values to test against. @@ -997,21 +1005,21 @@ function Remove-EnvironmentVariable #> function Test-PathsInValue { - [OutputType([Boolean])] + [OutputType([System.Boolean])] [CmdletBinding()] param ( - [Parameter(Mandatory = $true)] - [String] + [Parameter(Mandatory = $true)] + [System.String] $ExistingPaths, - + [Parameter(Mandatory = $true)] - [String] + [System.String] $QueryPaths, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateSet('Any', 'All')] - [String] + [System.String] $FindCriteria ) @@ -1023,35 +1031,35 @@ function Test-PathsInValue 'Any' { foreach ($queryPath in $queryPathList) - { + { if ($existingPathList -contains $queryPath) { # Found this $queryPath in the existing paths, return $true return $true - } + } } # If the control reached here, none of the QueryPaths were found in ExistingPaths - return $false + return $false } 'All' { foreach ($queryPath in $queryPathList) { - if ($queryPath) + if ($queryPath) { if ($existingPathList -notcontains $queryPath) { - # The current $queryPath wasn't found in any of the $existingPathList, return false + # The current $queryPath wasn't found in any of the $existingPathList, return false return $false } - } + } } # If the control reached here, all of the QueryPaths were found in ExistingPaths return $true - } + } } } @@ -1062,7 +1070,7 @@ function Test-PathsInValue name and its current value on the machine. This is to most closely represent what the actual API call returns. If an environment variable with the given name is not found, then $null will be returned. - + .PARAMETER Name The name of the environment variable to retrieve the value of. #> @@ -1074,14 +1082,14 @@ function Get-EnvironmentVariableWithoutExpanding ( [Parameter(Mandatory = $true)] [ValidateNotNull()] - [String] + [System.String] $Name ) $path = $script:envVarRegPathMachine $pathTokens = $path.Split('\',[System.StringSplitOptions]::RemoveEmptyEntries) $entry = $pathTokens[1..($pathTokens.Count - 1)] -join '\' - + # Since the target registry path coming to this function is hardcoded for local machine $hive = [Microsoft.Win32.Registry]::LocalMachine @@ -1090,14 +1098,14 @@ function Get-EnvironmentVariableWithoutExpanding try { $key = $hive.OpenSubKey($entry) - + $valueNames = $key.GetValueNames() if ($valueNames -inotcontains $Name) { return $null } - - [String] $value = Get-KeyValue -Name $Name -Key $key + + [System.String] $value = Get-KeyValue -Name $Name -Key $key $noteProperties.Add($Name, $value) } finally @@ -1117,7 +1125,7 @@ function Get-EnvironmentVariableWithoutExpanding .SYNOPSIS Wrapper function to get the value of the environment variable with the given name from the specified registry key. - + .PARAMETER Name The name of the environment variable to retrieve the value of. @@ -1126,13 +1134,13 @@ function Get-EnvironmentVariableWithoutExpanding #> function Get-KeyValue { - [OutputType([String])] + [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNull()] - [String] + [System.String] $Name, [Parameter(Mandatory = $true)] diff --git a/DSCResources/MSFT_xGroupResource/MSFT_xGroupResource.psm1 b/DSCResources/MSFT_xGroupResource/MSFT_xGroupResource.psm1 index 9f7f3835f..17f8fd3ff 100644 --- a/DSCResources/MSFT_xGroupResource/MSFT_xGroupResource.psm1 +++ b/DSCResources/MSFT_xGroupResource/MSFT_xGroupResource.psm1 @@ -96,6 +96,7 @@ function Get-TargetResource [String] $GroupName, + [Parameter()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential @@ -127,7 +128,7 @@ function Get-TargetResource To ensure that the group does exist, set this property to present. To ensure that the group does not exist, set this property to Absent. - + The default value is Present. .PARAMETER Description @@ -138,10 +139,10 @@ function Get-TargetResource This property will replace all the current group members with the specified members. - Members should be specified as strings in the format of their domain qualified name + Members should be specified as strings in the format of their domain qualified name (domain\username), their UPN (username@domainname), their distinguished name (CN=username,DC=...), - or their username (for local machine accounts). - + or their username (for local machine accounts). + Using either the MembersToExclude or MembersToInclude properties in the same configuration as this property will generate an error. @@ -150,7 +151,7 @@ function Get-TargetResource This property will only add members to a group. - Members should be specified as strings in the format of their domain qualified name + Members should be specified as strings in the format of their domain qualified name (domain\username), their UPN (username@domainname), their distinguished name (CN=username,DC=...), or their username (for local machine accounts). @@ -161,7 +162,7 @@ function Get-TargetResource This property will only remove members from a group. - Members should be specified as strings in the format of their domain qualified name + Members should be specified as strings in the format of their domain qualified name (domain\username), their UPN (username@domainname), their distinguished name (CN=username,DC=...), or their username (for local machine accounts). @@ -188,22 +189,28 @@ function Set-TargetResource [String] $GroupName, + [Parameter()] [ValidateSet('Present', 'Absent')] [String] $Ensure = 'Present', + [Parameter()] [String] $Description, + [Parameter()] [String[]] $Members, + [Parameter()] [String[]] $MembersToInclude, + [Parameter()] [String[]] $MembersToExclude, + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] @@ -244,10 +251,10 @@ function Set-TargetResource .PARAMETER Members The list of members the group should have. - - The value of this property is an array of strings of the formats domain qualified name + + The value of this property is an array of strings of the formats domain qualified name (domain\username), UPN (username@domainname), distinguished name (CN=username,DC=...) and/or - a unqualified (username) for local machine accounts. + a unqualified (username) for local machine accounts. If you set this property in a configuration, do not use either the MembersToExclude or MembersToInclude property. Doing so will generate an error. @@ -255,9 +262,9 @@ function Set-TargetResource .PARAMETER MembersToInclude A list of members that should be in the group. - The value of this property is an array of strings of the formats domain qualified name + The value of this property is an array of strings of the formats domain qualified name (domain\username), UPN (username@domainname), distinguished name (CN=username,DC=...) and/or - a unqualified (username) for local machine accounts. + a unqualified (username) for local machine accounts. If you set this property in a configuration, do not use the Members property. Doing so will generate an error. @@ -265,7 +272,7 @@ function Set-TargetResource .PARAMETER MembersToExclude A list of members that should not be in the group. - The value of this property is an array of strings of the formats domain qualified name + The value of this property is an array of strings of the formats domain qualified name (domain\username), UPN (username@domainname), distinguished name (CN=username,DC=...) and/or a unqualified (username) for local machine accounts. @@ -286,22 +293,28 @@ function Test-TargetResource [String] $GroupName, + [Parameter()] [ValidateSet('Present', 'Absent')] [String] $Ensure = 'Present', + [Parameter()] [String] $Description, + [Parameter()] [String[]] $Members, + [Parameter()] [String[]] $MembersToInclude, + [Parameter()] [String[]] $MembersToExclude, + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] @@ -343,6 +356,7 @@ function Get-TargetResourceOnFullSKU [String] $GroupName, + [Parameter()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential @@ -411,6 +425,7 @@ function Get-TargetResourceOnNanoServer [String] $GroupName, + [Parameter()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential @@ -454,10 +469,10 @@ function Get-TargetResourceOnNanoServer .PARAMETER Ensure Indicates if the group should exist or not. - + Set this property to Present to ensure that the group exists. Set this property to Absent to ensure that the group does not exist. - + The default value is Present. .PARAMETER Description @@ -465,31 +480,31 @@ function Get-TargetResourceOnNanoServer .PARAMETER Members Use this property to replace the current group membership with the specified members. - - The value of this property is an array of strings of the formats domain qualified name + + The value of this property is an array of strings of the formats domain qualified name (domain\username), UPN (username@domainname), distinguished name (CN=username,DC=...) and/or an unqualified (username) for local machine accounts. - - If you set this property in a configuration, do not use either the MembersToExclude or + + If you set this property in a configuration, do not use either the MembersToExclude or MembersToInclude property. Doing so will generate an error. .PARAMETER MembersToInclude Use this property to add members to the existing membership of the group. - - The value of this property is an array of strings of the formats domain qualified name + + The value of this property is an array of strings of the formats domain qualified name (domain\username), UPN (username@domainname), distinguished name (CN=username,DC=...) and/or - a unqualified (username) for local machine accounts. - + a unqualified (username) for local machine accounts. + If you set this property in a configuration, do not use the Members property. Doing so will generate an error. .PARAMETER MembersToExclude Use this property to remove members from the existing membership of the group. - - The value of this property is an array of strings of the formats domain qualified name + + The value of this property is an array of strings of the formats domain qualified name (domain\username), UPN (username@domainname), distinguished name (CN=username,DC=...) and/or - a unqualified (username) for local machine accounts. - + a unqualified (username) for local machine accounts. + If you set this property in a configuration, do not use the Members property. Doing so will generate an error. @@ -508,22 +523,28 @@ function Set-TargetResourceOnFullSKU [String] $GroupName, + [Parameter()] [ValidateSet('Present', 'Absent')] [String] $Ensure = 'Present', + [Parameter()] [String] $Description, + [Parameter()] [String[]] $Members, + [Parameter()] [String[]] $MembersToInclude, + [Parameter()] [String[]] $MembersToExclude, + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] @@ -621,7 +642,7 @@ function Set-TargetResourceOnFullSKU if ($Members.Count -eq 0 -and $null -ne $actualMembersAsPrincipals -and $actualMembersAsPrincipals.Count -ne 0) { - Clear-GroupMembers -Group $group + Clear-GroupMember -Group $group $saveChanges = $true } elseif ($Members.Count -ne 0) @@ -806,10 +827,10 @@ function Set-TargetResourceOnFullSKU .PARAMETER Ensure Indicates if the group should exist or not. - + Set this property to Present to ensure that the group exists. Set this property to Absent to ensure that the group does not exist. - + The default value is Present. .PARAMETER Description @@ -817,31 +838,31 @@ function Set-TargetResourceOnFullSKU .PARAMETER Members Use this property to replace the current group membership with the specified members. - - The value of this property is an array of strings of the formats domain qualified name + + The value of this property is an array of strings of the formats domain qualified name (domain\username), UPN (username@domainname), distinguished name (CN=username,DC=...) and/or a unqualified (username) for local machine accounts. - - If you set this property in a configuration, do not use either the MembersToExclude or + + If you set this property in a configuration, do not use either the MembersToExclude or MembersToInclude property. Doing so will generate an error. .PARAMETER MembersToInclude Use this property to add members to the existing membership of the group. - The value of this property is an array of strings of the formats domain qualified name + The value of this property is an array of strings of the formats domain qualified name (domain\username), UPN (username@domainname), distinguished name (CN=username,DC=...) and/or a unqualified (username) for local machine accounts. - + If you set this property in a configuration, do not use the Members property. Doing so will generate an error. .PARAMETER MembersToExclude Use this property to remove members from the existing membership of the group. - The value of this property is an array of strings of the formats domain qualified name + The value of this property is an array of strings of the formats domain qualified name (domain\username), UPN (username@domainname), distinguished name (CN=username,DC=...) and/or a unqualified (username) for local machine accounts. - + If you set this property in a configuration, do not use the Members property. Doing so will generate an error. @@ -859,22 +880,28 @@ function Set-TargetResourceOnNanoServer [String] $GroupName, + [Parameter()] [ValidateSet('Present', 'Absent')] [String] $Ensure = 'Present', + [Parameter()] [String] $Description, + [Parameter()] [String[]] $Members, + [Parameter()] [String[]] $MembersToInclude, + [Parameter()] [String[]] $MembersToExclude, + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] @@ -923,7 +950,7 @@ function Set-TargetResourceOnNanoServer } # Set the group properties. - if ($PSBoundParameters.ContainsKey('Description') -and + if ($PSBoundParameters.ContainsKey('Description') -and ((-not $groupOriginallyExists) -or ($Description -ne $group.Description))) { Set-LocalGroup -Name $GroupName -Description $Description @@ -965,7 +992,7 @@ function Set-TargetResourceOnNanoServer } elseif ($PSBoundParameters.ContainsKey('MembersToInclude') -or $PSBoundParameters.ContainsKey('MembersToExclude')) { - [array]$groupMembers = Get-MembersOnNanoServer -Group $group + [System.Array] $groupMembers = Get-MembersOnNanoServer -Group $group $uniqueMembersToInclude = $MembersToInclude | Select-Object -Unique $uniqueMembersToExclude = $MembersToExclude | Select-Object -Unique @@ -1036,10 +1063,10 @@ function Set-TargetResourceOnNanoServer .PARAMETER Ensure Indicates if the group should exist or not. - + Set this property to Present to ensure that the group exists. Set this property to Absent to ensure that the group does not exist. - + The default value is Present. .PARAMETER Description @@ -1048,8 +1075,8 @@ function Set-TargetResourceOnNanoServer .PARAMETER Members Use this property to test if the existing membership of the group matches the list provided. - - The value of this property is an array of strings of the formats domain qualified name + + The value of this property is an array of strings of the formats domain qualified name (domain\username), UPN (username@domainname), distinguished name (CN=username,DC=...) and/or a unqualified (username) for local machine accounts. @@ -1058,9 +1085,9 @@ function Set-TargetResourceOnNanoServer .PARAMETER MembersToInclude Use this property to test if members need to be added to the existing membership - of the group. + of the group. - The value of this property is an array of strings of the formats domain qualified name + The value of this property is an array of strings of the formats domain qualified name (domain\username), UPN (username@domainname), distinguished name (CN=username,DC=...) and/or a unqualified (username) for local machine accounts. @@ -1071,7 +1098,7 @@ function Set-TargetResourceOnNanoServer Use this property to test if members need to removed from the existing membership of the group. - The value of this property is an array of strings of the formats domain qualified name + The value of this property is an array of strings of the formats domain qualified name (domain\username), UPN (username@domainname), distinguished name (CN=username,DC=...) and/or a unqualified (username) for local machine accounts. @@ -1092,22 +1119,28 @@ function Test-TargetResourceOnFullSKU [String] $GroupName, + [Parameter()] [ValidateSet('Present', 'Absent')] [String] $Ensure = 'Present', + [Parameter()] [String] $Description, + [Parameter()] [String[]] $Members, + [Parameter()] [String[]] $MembersToInclude, + [Parameter()] [String[]] $MembersToExclude, + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] @@ -1297,10 +1330,10 @@ function Test-TargetResourceOnFullSKU .PARAMETER Ensure Indicates if the group should exist or not. - + Set this property to Present to ensure that the group exists. Set this property to Absent to ensure that the group does not exist. - + The default value is Present. .PARAMETER Description @@ -1310,7 +1343,7 @@ function Test-TargetResourceOnFullSKU Use this property to test if the existing membership of the group matches the list provided. - The value of this property is an array of strings of the formats domain qualified name + The value of this property is an array of strings of the formats domain qualified name (domain\username), UPN (username@domainname), distinguished name (CN=username,DC=...) and/or a unqualified (username) for local machine accounts. @@ -1321,7 +1354,7 @@ function Test-TargetResourceOnFullSKU Use this property to test if members need to be added to the existing membership of the group. - The value of this property is an array of strings of the formats domain qualified name + The value of this property is an array of strings of the formats domain qualified name (domain\username), UPN (username@domainname), distinguished name (CN=username,DC=...) and/or a unqualified (username) for local machine accounts. @@ -1332,7 +1365,7 @@ function Test-TargetResourceOnFullSKU Use this property to test if members need to removed from the existing membership of the group. - The value of this property is an array of strings of the formats domain qualified name + The value of this property is an array of strings of the formats domain qualified name (domain\username), UPN (username@domainname), distinguished name (CN=username,DC=...) and/or a unqualified (username) for local machine accounts. @@ -1353,22 +1386,28 @@ function Test-TargetResourceOnNanoServer [String] $GroupName, + [Parameter()] [ValidateSet('Present', 'Absent')] [String] $Ensure = 'Present', + [Parameter()] [String] $Description, + [Parameter()] [String[]] $Members, + [Parameter()] [String[]] $MembersToInclude, + [Parameter()] [String[]] $MembersToExclude, + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] @@ -1419,7 +1458,7 @@ function Test-TargetResourceOnNanoServer } } - [array]$groupMembers = Get-MembersOnNanoServer -Group $group + [System.Array] $groupMembers = Get-MembersOnNanoServer -Group $group # Remove duplicate names as strings. $uniqueMembers = $Members | Select-Object -Unique @@ -1571,6 +1610,7 @@ function Get-MembersOnFullSKU [AllowEmptyCollection()] $Disposables, + [Parameter()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential @@ -1656,6 +1696,7 @@ function Get-MembersAsPrincipalsList [AllowEmptyCollection()] $Disposables, + [Parameter()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential @@ -1833,6 +1874,7 @@ function ConvertTo-UniquePrincipalsList [AllowEmptyCollection()] $Disposables, + [Parameter()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential @@ -1922,6 +1964,7 @@ function ConvertTo-Principal [AllowEmptyCollection()] $Disposables, + [Parameter()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential @@ -2061,6 +2104,7 @@ function Get-PrincipalContext [String] $Scope, + [Parameter()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential, @@ -2117,7 +2161,7 @@ function Get-PrincipalContext } $principalContext = New-Object -TypeName 'System.DirectoryServices.AccountManagement.PrincipalContext' ` - -ArgumentList @( [System.DirectoryServices.AccountManagement.ContextType]::Domain, $Scope, + -ArgumentList @( [System.DirectoryServices.AccountManagement.ContextType]::Domain, $Scope, $principalContextName, $Credential.GetNetworkCredential().Password ) # Cache the PrincipalContext for this scope for subsequent calls. @@ -2329,6 +2373,7 @@ function Find-Principal [String] $IdentityValue, + [Parameter()] [System.DirectoryServices.AccountManagement.IdentityType] $IdentityType ) @@ -2341,7 +2386,7 @@ function Find-Principal { return [System.DirectoryServices.AccountManagement.Principal]::FindByIdentity($PrincipalContext, $IdentityValue) } - + } <# @@ -2425,7 +2470,7 @@ function Get-GroupMembersFromDirectoryEntry .PARAMETER Group The group to clear the members of. #> -function Clear-GroupMembers +function Clear-GroupMember { [CmdletBinding()] param @@ -2435,7 +2480,7 @@ function Clear-GroupMembers [System.DirectoryServices.AccountManagement.GroupPrincipal] $Group ) - + $Group.Members.Clear() } @@ -2517,7 +2562,7 @@ function Remove-Group [System.DirectoryServices.AccountManagement.GroupPrincipal] $Group ) - + $Group.Delete() } diff --git a/DSCResources/MSFT_xMsiPackage/MSFT_xMsiPackage.psm1 b/DSCResources/MSFT_xMsiPackage/MSFT_xMsiPackage.psm1 index e1a88bdf1..a97d62db7 100644 --- a/DSCResources/MSFT_xMsiPackage/MSFT_xMsiPackage.psm1 +++ b/DSCResources/MSFT_xMsiPackage/MSFT_xMsiPackage.psm1 @@ -1,5 +1,3 @@ -# Suppress Global Vars PSSA Error because $global:DSCMachineStatus must be allowed -[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] param() @@ -30,18 +28,18 @@ $script:msiTools = $null #> function Get-TargetResource { - [OutputType([Hashtable])] + [OutputType([System.Collections.Hashtable])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $ProductId, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path ) @@ -122,44 +120,54 @@ function Set-TargetResource ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $ProductId, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path, + [Parameter()] [ValidateSet('Present', 'Absent')] - [String] + [System.String] $Ensure = 'Present', - [String] + [Parameter()] + [System.String] $Arguments, - [PSCredential] + [Parameter()] + [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential, - [String] + [Parameter()] + [System.String] $LogPath, - [String] + [Parameter()] + [System.String] $FileHash, + [Parameter()] [ValidateSet('SHA1', 'SHA256', 'SHA384', 'SHA512', 'MD5', 'RIPEMD160')] - [String] + [System.String] $HashAlgorithm = 'SHA256', - [String] + [Parameter()] + [System.String] $SignerSubject, - [String] + [Parameter()] + [System.String] $SignerThumbprint, - [String] + [Parameter()] + [System.String] $ServerCertificateValidationCallback, + [Parameter()] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $RunAsCredential @@ -213,7 +221,7 @@ function Set-TargetResource if ($uri.IsUnc) { $psDriveArgs = @{ - Name = [Guid]::NewGuid() + Name = [System.Guid]::NewGuid() PSProvider = 'FileSystem' Root = Split-Path -Path $localPath } @@ -289,7 +297,7 @@ function Set-TargetResource # Check if the MSI package specifies the ProductCode, and if so make sure they match $productCode = Get-MsiProductCode -Path $Path - if ((-not [String]::IsNullOrEmpty($identifyingNumber)) -and ($identifyingNumber -ne $productCode)) + if ((-not [System.String]::IsNullOrEmpty($identifyingNumber)) -and ($identifyingNumber -ne $productCode)) { New-InvalidArgumentException -ArgumentName 'ProductId' -Message ($script:localizedData.InvalidId -f $identifyingNumber, $productCode) } @@ -331,7 +339,7 @@ function Set-TargetResource if (($serverFeatureData -and $serverFeatureData.RequiresReboot) -or $rebootRequired) { Write-Verbose $script:localizedData.MachineRequiresReboot - $global:DSCMachineStatus = 1 + Set-DSCMachineRebootRequired } elseif ($Ensure -eq 'Present') { @@ -397,50 +405,60 @@ function Set-TargetResource #> function Test-TargetResource { - [OutputType([Boolean])] + [OutputType([System.Boolean])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $ProductId, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path, + [Parameter()] [ValidateSet('Present', 'Absent')] - [String] + [System.String] $Ensure = 'Present', - [String] + [Parameter()] + [System.String] $Arguments, - [PSCredential] + [Parameter()] + [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential, - [String] + [Parameter()] + [System.String] $LogPath, - [String] + [Parameter()] + [System.String] $FileHash, + [Parameter()] [ValidateSet('SHA1', 'SHA256', 'SHA384', 'SHA512', 'MD5', 'RIPEMD160')] - [String] + [System.String] $HashAlgorithm = 'SHA256', - [String] + [Parameter()] + [System.String] $SignerSubject, - [String] + [Parameter()] + [System.String] $SignerThumbprint, - [String] + [Parameter()] + [System.String] $ServerCertificateValidationCallback, + [Parameter()] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $RunAsCredential @@ -477,7 +495,7 @@ function Assert-PathExtensionValid ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path ) @@ -500,19 +518,19 @@ function Assert-PathExtensionValid #> function Convert-PathToUri { - [OutputType([Uri])] + [OutputType([System.Uri])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path ) try { - $uri = [Uri]$Path + $uri = [System.Uri] $Path } catch { @@ -539,20 +557,20 @@ function Convert-PathToUri #> function Convert-ProductIdToIdentifyingNumber { - [OutputType([String])] + [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $ProductId ) try { Write-Verbose -Message ($script:localizedData.ParsingProductIdAsAnIdentifyingNumber -f $ProductId) - $identifyingNumber = '{{{0}}}' -f [Guid]::Parse($ProductId).ToString().ToUpper() + $identifyingNumber = '{{{0}}}' -f [System.Guid]::Parse($ProductId).ToString().ToUpper() Write-Verbose -Message ($script:localizedData.ParsedProductIdAsIdentifyingNumber -f $ProductId, $identifyingNumber) return $identifyingNumber @@ -578,7 +596,7 @@ function Get-ProductEntry param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $IdentifyingNumber ) @@ -587,7 +605,7 @@ function Get-ProductEntry $productEntry = $null - if (-not [String]::IsNullOrEmpty($IdentifyingNumber)) + if (-not [System.String]::IsNullOrEmpty($IdentifyingNumber)) { $productEntryKeyLocation = Join-Path -Path $uninstallRegistryKey -ChildPath $IdentifyingNumber $productEntry = Get-Item -Path $productEntryKeyLocation -ErrorAction 'SilentlyContinue' @@ -611,7 +629,7 @@ function Get-ProductEntry #> function Get-ProductEntryInfo { - [OutputType([Hashtable])] + [OutputType([System.Collections.Hashtable])] [CmdletBinding()] param ( @@ -626,7 +644,7 @@ function Get-ProductEntryInfo { try { - $installDate = '{0:d}' -f [DateTime]::ParseExact($installDate, 'yyyyMMdd',[System.Globalization.CultureInfo]::CurrentCulture).Date + $installDate = '{0:d}' -f [System.DateTime]::ParseExact($installDate, 'yyyyMMdd',[System.Globalization.CultureInfo]::CurrentCulture).Date } catch { @@ -675,7 +693,7 @@ function Get-ProductEntryInfo #> function Get-ProductEntryValue { - [OutputType([Object])] + [OutputType([System.Object])] [CmdletBinding()] param ( @@ -684,7 +702,7 @@ function Get-ProductEntryValue $ProductEntry, [Parameter(Mandatory = $true)] - [String] + [System.String] $Property ) @@ -705,7 +723,7 @@ function New-LogFile param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $LogPath ) @@ -745,10 +763,11 @@ function Get-WebRequestResponse param ( [Parameter(Mandatory = $true)] - [Uri] + [System.Uri] $Uri, - [String] + [Parameter()] + [System.String] $ServerCertificateValidationCallback ) @@ -769,7 +788,7 @@ function Get-WebRequestResponse Write-Verbose -Message ($script:localizedData.SettingAuthenticationLevel) $webRequest.AuthenticationLevel = [System.Net.Security.AuthenticationLevel]::None } - elseif ($uriScheme -eq 'https' -and -not [String]::IsNullOrEmpty($ServerCertificateValidationCallback)) + elseif ($uriScheme -eq 'https' -and -not [System.String]::IsNullOrEmpty($ServerCertificateValidationCallback)) { Write-Verbose -Message $script:localizedData.SettingCertificateValidationCallback $webRequest.ServerCertificateValidationCallBack = (Get-ScriptBlock -FunctionName $ServerCertificateValidationCallback) @@ -801,7 +820,7 @@ function Get-WebRequest param ( [Parameter(Mandatory = $true)] - [Uri] + [System.Uri] $Uri ) @@ -827,7 +846,7 @@ function Get-WebRequestResponseStream $WebRequest ) - return (([System.Net.HttpWebRequest]$WebRequest).GetResponse()).GetResponseStream() + return (([System.Net.HttpWebRequest] $WebRequest).GetResponse()).GetResponseStream() } <# @@ -840,16 +859,16 @@ function Get-WebRequestResponseStream #> function Get-ScriptBlock { - [OutputType([ScriptBlock])] + [OutputType([System.Management.Automation.ScriptBlock])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $FunctionName ) - return [ScriptBlock]::Create($FunctionName) + return [System.Management.Automation.ScriptBlock]::Create($FunctionName) } <# @@ -938,28 +957,32 @@ function Assert-FileValid param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $Path, - [String] + [Parameter()] + [System.String] $FileHash, - [String] + [Parameter()] + [System.String] $HashAlgorithm = 'SHA256', - [String] + [Parameter()] + [System.String] $SignerThumbprint, - [String] + [Parameter()] + [System.String] $SignerSubject ) - if (-not [String]::IsNullOrEmpty($FileHash)) + if (-not [System.String]::IsNullOrEmpty($FileHash)) { Assert-FileHashValid -Path $Path -Hash $FileHash -Algorithm $HashAlgorithm } - if (-not [String]::IsNullOrEmpty($SignerThumbprint) -or -not [String]::IsNullOrEmpty($SignerSubject)) + if (-not [System.String]::IsNullOrEmpty($SignerThumbprint) -or -not [System.String]::IsNullOrEmpty($SignerSubject)) { Assert-FileSignatureValid -Path $Path -Thumbprint $SignerThumbprint -Subject $SignerSubject } @@ -985,14 +1008,15 @@ function Assert-FileHashValid param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $Path, [Parameter(Mandatory = $true)] - [String] + [System.String] $Hash, - [String] + [Parameter()] + [System.String] $Algorithm = 'SHA256' ) @@ -1025,13 +1049,15 @@ function Assert-FileSignatureValid param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $Path, - [String] + [Parameter()] + [System.String] $Thumbprint, - [String] + [Parameter()] + [System.String] $Subject ) @@ -1048,12 +1074,12 @@ function Assert-FileSignatureValid Write-Verbose -Message ($script:localizedData.FileHasValidSignature -f $Path, $signature.SignerCertificate.Thumbprint, $signature.SignerCertificate.Subject) } - if (-not [String]::IsNullOrEmpty($Subject) -and ($signature.SignerCertificate.Subject -notlike $Subject)) + if (-not [System.String]::IsNullOrEmpty($Subject) -and ($signature.SignerCertificate.Subject -notlike $Subject)) { New-InvalidArgumentException -ArgumentName 'SignerSubject' -Message ($script:localizedData.WrongSignerSubject -f $Path, $Subject) } - if (-not [String]::IsNullOrEmpty($Thumbprint) -and ($signature.SignerCertificate.Thumbprint -ne $Thumbprint)) + if (-not [System.String]::IsNullOrEmpty($Thumbprint) -and ($signature.SignerCertificate.Thumbprint -ne $Thumbprint)) { New-InvalidArgumentException -ArgumentName 'SignerThumbprint' -Message ($script:localizedData.WrongSignerThumbprint -f $Path, $Thumbprint) } @@ -1090,24 +1116,28 @@ function Start-MsiProcess ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $IdentifyingNumber, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path, + [Parameter()] [ValidateSet('Present', 'Absent')] - [String] + [System.String] $Ensure = 'Present', - [String] + [Parameter()] + [System.String] $Arguments, - [String] + [Parameter()] + [System.String] $LogPath, + [Parameter()] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $RunAsCredential @@ -1133,14 +1163,14 @@ function Start-MsiProcess $startInfo.Arguments = ('/x{0}' -f $id) } - if (-not [String]::IsNullOrEmpty($LogPath)) + if (-not [System.String]::IsNullOrEmpty($LogPath)) { $startInfo.Arguments += (' /log "{0}"' -f $LogPath) } $startInfo.Arguments += ' /quiet /norestart' - if (-not [String]::IsNullOrEmpty($Arguments)) + if (-not [System.String]::IsNullOrEmpty($Arguments)) { # Append any specified arguments with a space $startInfo.Arguments += (' {0}' -f $Arguments) @@ -1152,7 +1182,7 @@ function Start-MsiProcess try { - if (-not [String]::IsNullOrEmpty($RunAsCredential)) + if (-not [System.String]::IsNullOrEmpty($RunAsCredential)) { $commandLine = ('"{0}" {1}' -f $startInfo.FileName, $startInfo.Arguments) $exitCode = Invoke-PInvoke -CommandLine $commandLine -RunAsCredential $RunAsCredential @@ -1190,7 +1220,7 @@ function Invoke-PInvoke param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $CommandLine, [Parameter(Mandatory = $true)] @@ -1245,13 +1275,13 @@ function Invoke-Process #> function Get-MsiProductCode { - [OutputType([String])] + [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path ) diff --git a/DSCResources/MSFT_xPSSessionConfiguration/MSFT_xPSSessionConfiguration.psm1 b/DSCResources/MSFT_xPSSessionConfiguration/MSFT_xPSSessionConfiguration.psm1 index f9539faf9..c88b1076a 100644 --- a/DSCResources/MSFT_xPSSessionConfiguration/MSFT_xPSSessionConfiguration.psm1 +++ b/DSCResources/MSFT_xPSSessionConfiguration/MSFT_xPSSessionConfiguration.psm1 @@ -40,13 +40,13 @@ function Get-TargetResource [OutputType([System.Collections.Hashtable])] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Name ) - Write-Verbose ($LocalizedData.GetTargetResourceStartMessage -f $Name) + Write-Verbose -Message ($LocalizedData.GetTargetResourceStartMessage -f $Name) # Try getting the specified endpoint $endpoint = Get-PSSessionConfiguration -Name $Name -ErrorAction SilentlyContinue -Verbose:$false @@ -56,7 +56,6 @@ function Get-TargetResource { $ensure = 'Absent' } - # If endpoint is present, check other properties else { @@ -66,29 +65,33 @@ function Get-TargetResource if ($endpoint.RunAsUser) { $newCimInstanceParams = @{ - ClassName = 'MSFT_Credential' - Property = @{ - Username = [String] $endpoint.RunAsUser - Password = [String] $null + ClassName = 'MSFT_Credential' + + Property = @{ + Username = [System.String] $endpoint.RunAsUser + Password = [System.String] $null } - Namespace = 'root/microsoft/windows/desiredstateconfiguration' + + Namespace = 'root/microsoft/windows/desiredstateconfiguration' ClientOnly = $true } + $convertToCimCredential = New-CimInstance @newCimInstanceParams } + $accessMode = Get-EndpointAccessMode -Endpoint $endpoint } @{ Name = $Name - RunAsCredential = [CimInstance]$convertToCimCredential + RunAsCredential = [CimInstance] $convertToCimCredential SecurityDescriptorSDDL = $endpoint.Permission StartupScript = $endpoint.StartupScript AccessMode = $accessMode Ensure = $ensure } - Write-Verbose ($LocalizedData.GetTargetResourceEndMessage -f $Name) + Write-Verbose -Message ($LocalizedData.GetTargetResourceEndMessage -f $Name) } <# @@ -130,36 +133,41 @@ function Get-TargetResource function Set-TargetResource { [CmdletBinding(SupportsShouldProcess = $true)] - [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars','')] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Name, + [Parameter()] [AllowEmptyString()] - [String] + [System.String] $StartupScript, + [Parameter()] [PSCredential] + [System.Management.Automation.Credential()] $RunAsCredential, - [String] + [Parameter()] + [System.String] $SecurityDescriptorSDDL, - [ValidateSet('Local','Remote', 'Disabled')] - [String] + [Parameter()] + [ValidateSet('Local', 'Remote', 'Disabled')] + [System.String] $AccessMode = 'Remote', - [ValidateSet('Present','Absent')] - [String] + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] $Ensure = 'Present' ) - Write-Verbose ($LocalizedData.SetTargetResourceStartMessage -f $Name) + Write-Verbose -Message ($LocalizedData.SetTargetResourceStartMessage -f $Name) - #Check if the session configuration exists + # Check if the session configuration exists Write-Verbose -Message ($LocalizedData.CheckEndpointMessage -f $Name) # Try to get a named session configuration @@ -186,11 +194,12 @@ function Set-TargetResource $DebugPreference = $VerbosePreference = "SilentlyContinue" $unregisterPSSessionConfigParams = @{ - Name = $Name - Force = $true + Name = $Name + Force = $true NoServiceRestart = $true - ErrorAction = 'Stop' + ErrorAction = 'Stop' } + $null = Unregister-PSSessionConfiguration @unregisterPSSessionConfigParams # Reset the following preference to older values @@ -204,10 +213,11 @@ function Set-TargetResource catch { $invokeThrowErrorHelperParams = @{ - ErrorId = 'UnregisterPSSessionConfigurationFailed' - ErrorMessage = $_.Exception + ErrorId = 'UnregisterPSSessionConfigurationFailed' + ErrorMessage = $_.Exception ErrorCategory = 'InvalidOperation' } + Invoke-ThrowErrorHelper @invokeThrowErrorHelperParams } @@ -221,14 +231,16 @@ function Set-TargetResource { $null = $PSBoundParameters.Remove('Name') } + if ($PSBoundParameters.ContainsKey('Ensure')) { $null = $PSBoundParameters.Remove('Ensure') } - [Hashtable]$validatedProperties = ( - Get-ValidatedResourcePropertyTable -Endpoint $endpoint @PSBoundParameters -Apply) - $null = $validatedProperties.Add('Name',$Name) + [System.Collections.Hashtable] $validatedProperties = ( + Get-ValidatedResourcePropertyTable -Endpoint $endpoint @PSBoundParameters -Apply + ) + $null = $validatedProperties.Add('Name', $Name) # If the $validatedProperties contain more than 1 key, something needs to be changed if ($validatedProperties.count -gt 1) @@ -248,10 +260,11 @@ function Set-TargetResource catch { $invokeThrowErrorHelperParams = @{ - ErrorId = 'SetPSSessionConfigurationFailed' - ErrorMessage = $_.Exception + ErrorId = 'SetPSSessionConfigurationFailed' + ErrorMessage = $_.Exception ErrorCategory = 'InvalidOperation' } + Invoke-ThrowErrorHelper @invokeThrowErrorHelperParams } } @@ -295,26 +308,28 @@ function Set-TargetResource if ($PSBoundParameters.ContainsKey('AccessMode') -and $AccessMode -ne 'Remote') { $setPSSessionConfigurationParams = @{ - Name = $Name - AccessMode = $AccessMode - Force = $true + Name = $Name + AccessMode = $AccessMode + Force = $true NoServiceRestart = $true - Verbose = $false + Verbose = $false } + $null = Set-PSSessionConfiguration @setPSSessionConfigurationParams } $restartNeeded = $true - Write-Verbose -Message ($LocalizedData.EndpointNameMessage -f $Name,'present') + Write-Verbose -Message ($LocalizedData.EndpointNameMessage -f $Name, 'present') } catch { $invokeThrowErrorHelperParams = @{ - ErrorId = 'RegisterOrSetPSSessionConfigurationFailed' - ErrorMessage = $_.Exception + ErrorId = 'RegisterOrSetPSSessionConfigurationFailed' + ErrorMessage = $_.Exception ErrorCategory = 'InvalidOperation' } + Invoke-ThrowErrorHelper @invokeThrowErrorHelperParams } } @@ -327,11 +342,11 @@ function Set-TargetResource #> if ($restartNeeded) { - $global:DscMachineStatus = 1 + Set-DSCMachineRebootRequired } } - Write-Verbose ($LocalizedData.SetTargetResourceEndMessage -f $Name) + Write-Verbose -Message ($LocalizedData.SetTargetResourceEndMessage -f $Name) } <# @@ -376,35 +391,48 @@ function Test-TargetResource [OutputType([System.Boolean])] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String]$Name, + [System.String] + $Name, + [Parameter()] [AllowEmptyString()] - [String]$StartupScript, + [System.String] + $StartupScript, - [PSCredential]$RunAsCredential, + [Parameter()] + [PSCredential] + [System.Management.Automation.Credential()] + $RunAsCredential, - [String]$SecurityDescriptorSDDL, + [Parameter()] + [System.String] + $SecurityDescriptorSDDL, - [ValidateSet('Local','Remote', 'Disabled')] - [String]$AccessMode = 'Remote', + [Parameter()] + [ValidateSet('Local', 'Remote', 'Disabled')] + [System.String] + $AccessMode = 'Remote', - [ValidateSet('Present','Absent')] - [String]$Ensure = 'Present' + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present' ) - Write-Verbose ($LocalizedData.TestTargetResourceStartMessage -f $Name) + Write-Verbose -Message ($LocalizedData.TestTargetResourceStartMessage -f $Name) -#region Input Validation + #region Input Validation # Check if the endpoint name is blank/whitespaced string - if ([String]::IsNullOrWhiteSpace($Name)) + if ([System.String]::IsNullOrWhiteSpace($Name)) { $invokeThrowErrorHelperParams = @{ - ErrorId = 'BlankString' - ErrorMessage = $LocalizedData.WhitespacedStringMessage -f 'name' + ErrorId = 'BlankString' + ErrorMessage = $LocalizedData.WhitespacedStringMessage -f 'name' ErrorCategory = 'SyntaxError' } + Invoke-ThrowErrorHelper @invokeThrowErrorHelperParams } @@ -412,39 +440,43 @@ function Test-TargetResource if ($PSBoundParameters.ContainsKey('StartupScript')) { # Check if startup script path is valid - if (!(Test-Path $StartupScript)) + if (!(Test-Path -Path $StartupScript)) { $invokeThrowErrorHelperParams = @{ - ErrorId = 'PathNotFound' - ErrorMessage = $LocalizedData.StartupPathNotFoundMessage -f $StartupScript + ErrorId = 'PathNotFound' + ErrorMessage = $LocalizedData.StartupPathNotFoundMessage -f $StartupScript ErrorCategory = 'ObjectNotFound' } + Invoke-ThrowErrorHelper @invokeThrowErrorHelperParams } # Check the startup script extension $startupScriptFileExtension = $StartupScript.Split('.')[-1] + if ($startupScriptFileExtension -ne 'ps1') { $invokeThrowErrorHelperParams = @{ - ErrorId = 'WrongFileExtension' - ErrorMessage = - $LocalizedData.WrongStartupScriptExtensionMessage -f $startupScriptFileExtension + ErrorId = 'WrongFileExtension' + ErrorMessage = + $LocalizedData.WrongStartupScriptExtensionMessage -f $startupScriptFileExtension ErrorCategory = 'InvalidData' } + Invoke-ThrowErrorHelper @invokeThrowErrorHelperParams } } # Check if SecurityDescriptorSDDL is whitespaced if ($PSBoundParameters.ContainsKey('SecurityDescriptorSDDL') -and - [String]::IsNullOrWhiteSpace($SecurityDescriptorSDDL)) + [System.String]::IsNullOrWhiteSpace($SecurityDescriptorSDDL)) { $invokeThrowErrorHelperParams = @{ - ErrorId = 'BlankString' - ErrorMessage = $LocalizedData.WhitespacedStringMessage -f 'securityDescriptorSddl' + ErrorId = 'BlankString' + ErrorMessage = $LocalizedData.WhitespacedStringMessage -f 'securityDescriptorSddl' ErrorCategory = 'SyntaxError' } + Invoke-ThrowErrorHelper @invokeThrowErrorHelperParams } @@ -453,15 +485,16 @@ function Test-TargetResource ($RunAsCredential -eq [PSCredential]::Empty)) { $invokeThrowErrorHelperParams = @{ - ErrorId = 'EmptyCredential' - ErrorMessage = $LocalizedData.EmptyCredentialMessage + ErrorId = 'EmptyCredential' + ErrorMessage = $LocalizedData.EmptyCredentialMessage ErrorCategory = 'InvalidArgument' } + Invoke-ThrowErrorHelper @invokeThrowErrorHelperParams } -#endregion + #endregion - #Check if the session configuration exists + # Check if the session configuration exists Write-Verbose -Message ($LocalizedData.CheckEndpointMessage -f $Name) try @@ -469,7 +502,7 @@ function Test-TargetResource # Try to get a named session configuration $endpoint = Get-PSSessionConfiguration -Name $Name -ErrorAction Stop -Verbose:$false - Write-Verbose -Message ($LocalizedData.EndpointNameMessage -f $Name,'present') + Write-Verbose -Message ($LocalizedData.EndpointNameMessage -f $Name, 'present') # If the endpoint shouldn't be present, return false if ($Ensure -eq 'Absent') @@ -484,6 +517,7 @@ function Test-TargetResource { $null = $PSBoundParameters.Remove('Name') } + if ($PSBoundParameters.ContainsKey('Ensure')) { $null = $PSBoundParameters.Remove('Ensure') @@ -494,27 +528,28 @@ function Test-TargetResource } catch [Microsoft.PowerShell.Commands.WriteErrorException] { - Write-Verbose -Message ($LocalizedData.EndpointNameMessage -f $Name,'absent') + Write-Verbose -Message ($LocalizedData.EndpointNameMessage -f $Name, 'absent') return ($Ensure -eq 'Absent') } - Write-Verbose ($LocalizedData.TestTargetResourceEndMessage -f $Name) + Write-Verbose -Message ($LocalizedData.TestTargetResourceEndMessage -f $Name) } <# .SYNOPSIS Helper function to translate the endpoint's accessmode - to the "Disabled","Local","Remote" values + to the 'Disabled', 'Local', 'Remote' values .PARAMETER Endpoint Specifies a valid session configuration endpoint object #> function Get-EndpointAccessMode { + [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] $Endpoint ) @@ -542,22 +577,22 @@ function Get-EndpointAccessMode .PARAMETER KeysToSkip Specifies an array of Hashtable keys to ignore. - #> function Write-EndpointMessage { + [CmdletBinding()] param ( - [Parameter(Mandatory)] - [Hashtable] + [Parameter(Mandatory = $true)] + [System.Collections.Hashtable] $Parameters, - [Parameter(Mandatory)] - [String[]] + [Parameter(Mandatory = $true)] + [System.String[]] $KeysToSkip ) - foreach($key in $Parameters.keys) + foreach ($key in $Parameters.keys) { if ($KeysToSkip -notcontains $key) { @@ -598,24 +633,30 @@ function Write-EndpointMessage #> function Get-ValidatedResourcePropertyTable { + [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] $Endpoint, - [String] + [Parameter()] + [System.String] $StartupScript, + [Parameter()] [PSCredential] $RunAsCredential, - [String] + [Parameter()] + [System.String] $SecurityDescriptorSDDL, - [ValidateSet('Local','Remote','Disabled')] - [String] + [Parameter()] + [ValidateSet('Local', 'Remote', 'Disabled')] + [System.String] $AccessMode, + [Parameter()] [Switch] $Apply ) @@ -628,63 +669,62 @@ function Get-ValidatedResourcePropertyTable # Check if the SDDL is same as specified if ($PSBoundParameters.ContainsKey('SecurityDescriptorSDDL')) { - $querySDDLMessage = $LocalizedData.CheckPropertyMessage -f 'SDDL', - $SecurityDescriptorSDDL - Write-Verbose -Message $querySDDLMessage + Write-Verbose -Message ($LocalizedData.CheckPropertyMessage -f 'SDDL', + $SecurityDescriptorSDDL) - # If endpoint SDDL is not same as specified - if ($endpoint.SecurityDescriptorSddl -and - ($endpoint.SecurityDescriptorSddl -ne $SecurityDescriptorSDDL)) - { - $notDesiredSDDLMessage = $LocalizedData.NotDesiredPropertyMessage -f 'SDDL', - $SecurityDescriptorSDDL, $endpoint.SecurityDescriptorSddl - Write-Verbose -Message $notDesiredSDDLMessage + # If endpoint SDDL is not same as specified + if ($endpoint.SecurityDescriptorSddl -and + ($endpoint.SecurityDescriptorSddl -ne $SecurityDescriptorSDDL)) + { + $notDesiredSDDLMessage = $LocalizedData.NotDesiredPropertyMessage -f 'SDDL', + $SecurityDescriptorSDDL, $endpoint.SecurityDescriptorSddl + Write-Verbose -Message $notDesiredSDDLMessage - if ($Apply) - { - $validatedProperties['SecurityDescriptorSddl'] = $SecurityDescriptorSDDL - } - else - { - return $false - } + if ($Apply) + { + $validatedProperties['SecurityDescriptorSddl'] = $SecurityDescriptorSDDL } - # If endpoint SDDL is same as specified else { - Write-Verbose -Message ($LocalizedData.DesiredPropertyMessage -f 'SDDL', - $SecurityDescriptorSDDL) + return $false } } + # If endpoint SDDL is same as specified + else + { + Write-Verbose -Message ($LocalizedData.DesiredPropertyMessage -f 'SDDL', + $SecurityDescriptorSDDL) + } + } # Check the RunAs user is same as specified if ($PSBoundParameters.ContainsKey('RunAsCredential')) { - Write-Verbose -Message ($LocalizedData.CheckPropertyMessage -f 'RunAs user', - $RunAsCredential.UserName) + Write-Verbose -Message ($LocalizedData.CheckPropertyMessage -f 'RunAs user', + $RunAsCredential.UserName) - # If endpoint RunAsUser is not same as specified - if ($endpoint.RunAsUser -ne $RunAsCredential.UserName) - { - Write-Verbose -Message ($LocalizedData.NotDesiredPropertyMessage -f 'RunAs user', - $RunAsCredential.UserName, $endpoint.RunAsUser) + # If endpoint RunAsUser is not same as specified + if ($endpoint.RunAsUser -ne $RunAsCredential.UserName) + { + Write-Verbose -Message ($LocalizedData.NotDesiredPropertyMessage -f 'RunAs user', + $RunAsCredential.UserName, $endpoint.RunAsUser) - if ($Apply) - { - $validatedProperties['RunAsCredential'] = $RunAsCredential - } - else - { - return $false - } + if ($Apply) + { + $validatedProperties['RunAsCredential'] = $RunAsCredential } - # If endpoint RunAsUser is same as specified else { - Write-Verbose -Message ($LocalizedData.DesiredPropertyMessage -f 'RunAs user', - $RunAsCredential.UserName) + return $false } } + # If endpoint RunAsUser is same as specified + else + { + Write-Verbose -Message ($LocalizedData.DesiredPropertyMessage -f 'RunAs user', + $RunAsCredential.UserName) + } + } # Check if the StartupScript is same as specified if ($PSBoundParameters.ContainsKey('StartupScript')) @@ -771,26 +811,27 @@ function Get-ValidatedResourcePropertyTable #> function Invoke-ThrowErrorHelper { + [CmdletBinding()] param ( - [Parameter(Mandatory)] - [String] + [Parameter(Mandatory = $true)] + [System.String] $ErrorId, - [Parameter(Mandatory)] - [String] + [Parameter(Mandatory = $true)] + [System.String] $ErrorMessage, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [System.Management.Automation.ErrorCategory] $ErrorCategory ) $exception = New-Object System.InvalidOperationException $ErrorMessage $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $ErrorId, - $ErrorCategory, $null + $ErrorCategory, $null throw $errorRecord } -Export-ModuleMember -Function *-TargetResource +Export-ModuleMember -Function Get-TargetResource, Set-TargetResource, Test-TargetResource diff --git a/DSCResources/MSFT_xPackageResource/MSFT_xPackageResource.psm1 b/DSCResources/MSFT_xPackageResource/MSFT_xPackageResource.psm1 index 3e7aabd46..229d0175a 100644 --- a/DSCResources/MSFT_xPackageResource/MSFT_xPackageResource.psm1 +++ b/DSCResources/MSFT_xPackageResource/MSFT_xPackageResource.psm1 @@ -1,5 +1,3 @@ -# Suppress Global Vars PSSA Error because $global:DSCMachineStatus must be allowed -[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] param() @@ -19,47 +17,54 @@ $script:msiTools = $null function Get-TargetResource { - [OutputType([Hashtable])] + [OutputType([System.Collections.Hashtable])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [AllowEmptyString()] - [String] + [System.String] $Name, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path, [Parameter(Mandatory = $true)] [AllowEmptyString()] - [String] + [System.String] $ProductId, - [Boolean] + [Parameter()] + [System.Boolean] $CreateCheckRegValue = $false, - [ValidateSet('LocalMachine','CurrentUser')] - [String] + [Parameter()] + [ValidateSet('LocalMachine', 'CurrentUser')] + [System.String] $InstalledCheckRegHive = 'LocalMachine', - [String] + [Parameter()] + [System.String] $InstalledCheckRegKey, - [String] + [Parameter()] + [System.String] $InstalledCheckRegValueName, - [String] + [Parameter()] + [System.String] $InstalledCheckRegValueData ) + Write-Verbose -Message $script:localizedData.EnteringGetTargetResource + Assert-PathExtensionValid -Path $Path - $uri = Convert-PathToUri -Path $Path - $identifyingNumber = [String]::Empty - if (-not [String]::IsNullOrEmpty($ProductId)) + $identifyingNumber = [System.String]::Empty + + if (-not [System.String]::IsNullOrEmpty($ProductId)) { $identifyingNumber = Convert-ProductIdToIdentifyingNumber -ProductId $ProductId } @@ -173,72 +178,88 @@ function Set-TargetResource [CmdletBinding(SupportsShouldProcess = $true)] param ( + [Parameter()] [ValidateSet('Present', 'Absent')] - [String] + [System.String] $Ensure = 'Present', [Parameter(Mandatory = $true)] [AllowEmptyString()] - [String] + [System.String] $Name, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path, [Parameter(Mandatory = $true)] [AllowEmptyString()] - [String] + [System.String] $ProductId, - [String] + [Parameter()] + [System.String] $Arguments, - [PSCredential] + [Parameter()] + [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential, # Return codes 1641 and 3010 indicate success when a restart is requested per installation + [Parameter()] [ValidateNotNullOrEmpty()] - [UInt32[]] + [System.UInt32[]] $ReturnCode = @( 0, 1641, 3010 ), - [String] + [Parameter()] + [System.String] $LogPath, - [String] + [Parameter()] + [System.String] $FileHash, + [Parameter()] [ValidateSet('SHA1', 'SHA256', 'SHA384', 'SHA512', 'MD5', 'RIPEMD160')] - [String] + [System.String] $HashAlgorithm, - [String] + [Parameter()] + [System.String] $SignerSubject, - [String] + [Parameter()] + [System.String] $SignerThumbprint, - [String] + [Parameter()] + [System.String] $ServerCertificateValidationCallback, - [Boolean] + [Parameter()] + [System.Boolean] $CreateCheckRegValue = $false, - [ValidateSet('LocalMachine','CurrentUser')] - [String] + [Parameter()] + [ValidateSet('LocalMachine', 'CurrentUser')] + [System.String] $InstalledCheckRegHive = 'LocalMachine', - [String] + [Parameter()] + [System.String] $InstalledCheckRegKey, - [String] + [Parameter()] + [System.String] $InstalledCheckRegValueName, - [String] + [Parameter()] + [System.String] $InstalledCheckRegValueData, + [Parameter()] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $RunAsCredential @@ -254,7 +275,7 @@ function Set-TargetResource Assert-PathExtensionValid -Path $Path $uri = Convert-PathToUri -Path $Path - if (-not [String]::IsNullOrEmpty($ProductId)) + if (-not [System.String]::IsNullOrEmpty($ProductId)) { $identifyingNumber = Convert-ProductIdToIdentifyingNumber -ProductId $ProductId } @@ -281,7 +302,7 @@ function Set-TargetResource try { $fileExtension = [System.IO.Path]::GetExtension($Path).ToLower() - if (-not [String]::IsNullOrEmpty($LogPath)) + if (-not [System.String]::IsNullOrEmpty($LogPath)) { try { @@ -319,7 +340,7 @@ function Set-TargetResource if ($uri.IsUnc -and $PSCmdlet.ShouldProcess($script:localizedData.MountSharePath, $null, $null)) { $psDriveArgs = @{ - Name = [Guid]::NewGuid() + Name = [System.Guid]::NewGuid() PSProvider = 'FileSystem' Root = Split-Path -Path $uri.LocalPath } @@ -377,15 +398,15 @@ function Set-TargetResource Write-Verbose -Message ($script:localizedData.SettingAuthenticationLevel) $webRequest.AuthenticationLevel = [System.Net.Security.AuthenticationLevel]::None } - elseif ($uriScheme -eq 'https' -and -not [String]::IsNullOrEmpty($ServerCertificateValidationCallback)) + elseif ($uriScheme -eq 'https' -and -not [System.String]::IsNullOrEmpty($ServerCertificateValidationCallback)) { Write-Verbose -Message 'Assigning user-specified certificate verification callback' - $serverCertificateValidationScriptBlock = [ScriptBlock]::Create($ServerCertificateValidationCallback) + $serverCertificateValidationScriptBlock = [System.Management.Automation.ScriptBlock]::Create($ServerCertificateValidationCallback) $webRequest.ServerCertificateValidationCallBack = $serverCertificateValidationScriptBlock } Write-Verbose -Message ($script:localizedData.Gettingtheschemeresponsestream -f $uriScheme) - $responseStream = (([System.Net.HttpWebRequest]$webRequest).GetResponse()).GetResponseStream() + $responseStream = (([System.Net.HttpWebRequest] $webRequest).GetResponse()).GetResponseStream() } catch { @@ -453,12 +474,12 @@ function Set-TargetResource $productName = Get-MsiProductName -Path $Path $productCode = Get-MsiProductCode -Path $Path - if ((-not [String]::IsNullOrEmpty($Name)) -and ($productName -ne $Name)) + if ((-not [System.String]::IsNullOrEmpty($Name)) -and ($productName -ne $Name)) { New-InvalidArgumentException -ArgumentName 'Name' -Message ($script:localizedData.InvalidNameOrId -f $Name, $identifyingNumber, $productName, $productCode) } - if ((-not [String]::IsNullOrEmpty($identifyingNumber)) -and ($identifyingNumber -ne $productCode)) + if ((-not [System.String]::IsNullOrEmpty($identifyingNumber)) -and ($identifyingNumber -ne $productCode)) { New-InvalidArgumentException -ArgumentName 'ProductId' -Message ($script:localizedData.InvalidNameOrId -f $Name, $identifyingNumber, $productName, $productCode) } @@ -562,10 +583,12 @@ function Set-TargetResource if ($logStream) { - #We have to re-mux these since they appear to us as different streams - #The underlying Win32 APIs prevent this problem, as would constructing a script - #on the fly and executing it, but the former is highly problematic from PowerShell - #and the latter doesn't let us get the return code for UI-based EXEs + <# + We have to re-mux these since they appear to us as different streams + the underlying Win32 APIs prevent this problem, as would constructing a script + on the fly and executing it, but the former is highly problematic from PowerShell + and the latter doesn't let us get the return code for UI-based EXEs + #> $outputEvents = Get-Event -SourceIdentifier $LogPath $errorEvents = Get-Event -SourceIdentifier $errorLogPath $masterEvents = @() + $outputEvents + $errorEvents @@ -648,7 +671,7 @@ function Set-TargetResource if (($serverFeatureData -and $serverFeatureData.RequiresReboot) -or $registryData -or $exitcode -eq 3010 -or $exitcode -eq 1641) { Write-Verbose $script:localizedData.MachineRequiresReboot - $global:DSCMachineStatus = 1 + Set-DSCMachineRebootRequired } elseif ($Ensure -eq 'Present') { @@ -684,85 +707,100 @@ function Set-TargetResource function Test-TargetResource { - [OutputType([Boolean])] + [OutputType([System.Boolean])] [CmdletBinding()] param ( + [Parameter()] [ValidateSet('Present', 'Absent')] - [String] + [System.String] $Ensure = 'Present', [Parameter(Mandatory = $true)] [AllowEmptyString()] - [String] + [System.String] $Name, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path, [Parameter(Mandatory = $true)] [AllowEmptyString()] - [String] + [System.String] $ProductId, - [String] + [Parameter()] + [System.String] $Arguments, - [PSCredential] + [Parameter()] + [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential, # Return codes 1641 and 3010 indicate success when a restart is requested per installation + [Parameter()] [ValidateNotNullOrEmpty()] - [UInt32[]] + [System.UInt32[]] $ReturnCode = @( 0, 1641, 3010 ), - [String] + [Parameter()] + [System.String] $LogPath, - [String] + [Parameter()] + [System.String] $FileHash, + [Parameter()] [ValidateSet('SHA1', 'SHA256', 'SHA384', 'SHA512', 'MD5', 'RIPEMD160')] - [String] + [System.String] $HashAlgorithm, - [String] + [Parameter()] + [System.String] $SignerSubject, - [String] + [Parameter()] + [System.String] $SignerThumbprint, - [String] + [Parameter()] + [System.String] $ServerCertificateValidationCallback, - [Boolean] + [Parameter()] + [System.Boolean] $CreateCheckRegValue = $false, - [ValidateSet('LocalMachine','CurrentUser')] - [String] + [Parameter()] + [ValidateSet('LocalMachine', 'CurrentUser')] + [System.String] $InstalledCheckRegHive = 'LocalMachine', - [String] + [Parameter()] + [System.String] $InstalledCheckRegKey, - [String] + [Parameter()] + [System.String] $InstalledCheckRegValueName, - [String] + [Parameter()] + [System.String] $InstalledCheckRegValueData, - [PSCredential] + [Parameter()] + [System.Management.Automation.PSCredential] $RunAsCredential ) Assert-PathExtensionValid -Path $Path - $uri = Convert-PathToUri -Path $Path $identifyingNumber = $null - if (-not [String]::IsNullOrEmpty($ProductId)) + if (-not [System.String]::IsNullOrEmpty($ProductId)) { $identifyingNumber = Convert-ProductIdToIdentifyingNumber -ProductId $ProductId } @@ -799,7 +837,7 @@ function Test-TargetResource Write-Verbose -Message 'Product installation cannot be determined' } - Write-Verbose -Message ($script:localizedData.ProductAsBooleanIs -f [Boolean]$productEntry) + Write-Verbose -Message ($script:localizedData.ProductAsBooleanIs -f [System.Boolean] $productEntry) if ($null -ne $productEntry) { @@ -817,7 +855,7 @@ function Test-TargetResource { $displayName = $null - if (-not [String]::IsNullOrEmpty($Name)) + if (-not [System.String]::IsNullOrEmpty($Name)) { $displayName = $Name } @@ -846,7 +884,7 @@ function Assert-PathExtensionValid ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path ) @@ -871,19 +909,19 @@ function Assert-PathExtensionValid #> function Convert-PathToUri { - [OutputType([Uri])] + [OutputType([System.Uri])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path ) try { - $uri = [Uri] $Path + $uri = [System.Uri] $Path } catch { @@ -894,7 +932,7 @@ function Convert-PathToUri if ($validUriSchemes -notcontains $uri.Scheme) { - Write-Verbose -Message ($Localized.TheUriSchemeWasUriScheme -f $uri.Scheme) + Write-Verbose -Message ($script:localizedData.TheUriSchemeWasUriScheme -f $uri.Scheme) New-InvalidArgumentException -ArgumentName 'Path' -Message ($script:localizedData.InvalidPath -f $Path) } @@ -910,20 +948,20 @@ function Convert-PathToUri #> function Convert-ProductIdToIdentifyingNumber { - [OutputType([String])] + [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $ProductId ) try { Write-Verbose -Message ($script:localizedData.ParsingProductIdAsAnIdentifyingNumber -f $ProductId) - $identifyingNumber = '{{{0}}}' -f [Guid]::Parse($ProductId).ToString().ToUpper() + $identifyingNumber = '{{{0}}}' -f [System.Guid]::Parse($ProductId).ToString().ToUpper() Write-Verbose -Message ($script:localizedData.ParsedProductIdAsIdentifyingNumber -f $ProductId, $identifyingNumber) return $identifyingNumber @@ -958,19 +996,22 @@ function Assert-RegistryParametersValid [CmdletBinding()] param ( - [String] + [Parameter()] + [System.String] $InstalledCheckRegKey, - [String] + [Parameter()] + [System.String] $InstalledCheckRegValueName, - [String] + [Parameter()] + [System.String] $InstalledCheckRegValueData ) foreach ($parameter in $PSBoundParameters.Keys) { - if ([String]::IsNullOrEmpty($PSBoundParameters[$parameter])) + if ([System.String]::IsNullOrEmpty($PSBoundParameters[$parameter])) { New-InvalidArgumentException -ArgumentName $parameter -Message ($script:localizedData.ProvideParameterForRegistryCheck -f $parameter) } @@ -1007,26 +1048,33 @@ function Get-ProductEntry [CmdletBinding()] param ( - [String] + [Parameter()] + [System.String] $Name, - [String] + [Parameter()] + [System.String] $IdentifyingNumber, - [Switch] + [Parameter()] + [System.Management.Automation.SwitchParameter] $CreateCheckRegValue, + [Parameter()] [ValidateSet('LocalMachine', 'CurrentUser')] - [String] + [System.String] $InstalledCheckRegHive = 'LocalMachine', - [String] + [Parameter()] + [System.String] $InstalledCheckRegKey, - [String] + [Parameter()] + [System.String] $InstalledCheckRegValueName, - [String] + [Parameter()] + [System.String] $InstalledCheckRegValueData ) @@ -1035,7 +1083,7 @@ function Get-ProductEntry $productEntry = $null - if (-not [String]::IsNullOrEmpty($IdentifyingNumber)) + if (-not [System.String]::IsNullOrEmpty($IdentifyingNumber)) { $productEntryKeyLocation = Join-Path -Path $uninstallRegistryKey -ChildPath $IdentifyingNumber $productEntry = Get-Item -Path $productEntryKeyLocation -ErrorAction 'SilentlyContinue' @@ -1114,11 +1162,11 @@ function Get-RegistryValueWithErrorsIgnored param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $Key, [Parameter(Mandatory = $true)] - [String] + [System.String] $Value, [Parameter(Mandatory = $true)] @@ -1166,12 +1214,13 @@ function Get-LocalizedRegistryKeyValue [CmdletBinding()] param ( - [Object] + [Parameter()] + [System.Object] $RegistryKey, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $ValueName ) @@ -1210,28 +1259,32 @@ function Assert-FileValid param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $Path, - [String] + [Parameter()] + [System.String] $FileHash, - [String] + [Parameter()] + [System.String] $HashAlgorithm, - [String] + [Parameter()] + [System.String] $SignerThumbprint, - [String] + [Parameter()] + [System.String] $SignerSubject ) - if (-not [String]::IsNullOrEmpty($FileHash)) + if (-not [System.String]::IsNullOrEmpty($FileHash)) { Assert-FileHashValid -Path $Path -Hash $FileHash -Algorithm $HashAlgorithm } - if (-not [String]::IsNullOrEmpty($SignerThumbprint) -or -not [String]::IsNullOrEmpty($SignerSubject)) + if (-not [System.String]::IsNullOrEmpty($SignerThumbprint) -or -not [System.String]::IsNullOrEmpty($SignerSubject)) { Assert-FileSignatureValid -Path $Path -Thumbprint $SignerThumbprint -Subject $SignerSubject } @@ -1256,18 +1309,20 @@ function Assert-FileHashValid param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $Path, - [Parameter(Mandatory)] - [String] + [Parameter()] + [Parameter(Mandatory = $true)] + [System.String] $Hash, - [String] + [Parameter()] + [System.String] $Algorithm = 'SHA256' ) - if ([String]::IsNullOrEmpty($Algorithm)) + if ([System.String]::IsNullOrEmpty($Algorithm)) { $Algorithm = 'SHA256' } @@ -1301,13 +1356,15 @@ function Assert-FileSignatureValid param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $Path, - [String] + [Parameter()] + [System.String] $Thumbprint, - [String] + [Parameter()] + [System.String] $Subject ) @@ -1344,13 +1401,13 @@ function Assert-FileSignatureValid #> function Get-MsiProductName { - [OutputType([String])] + [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path ) @@ -1370,13 +1427,13 @@ function Get-MsiProductName #> function Get-MsiProductCode { - [OutputType([String])] + [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path ) @@ -1481,11 +1538,11 @@ function Invoke-PInvoke param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $CommandLine, - [Parameter(Mandatory)] - [PSCredential] + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential ) @@ -1521,7 +1578,7 @@ function Invoke-Process [CmdletBinding()] [OutputType([System.Diagnostics.Process])] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [System.Diagnostics.Process] $Process, @@ -1564,11 +1621,11 @@ function Set-RegistryValue param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $Key, [Parameter(Mandatory = $true)] - [String] + [System.String] $Value, [Parameter(Mandatory = $true)] @@ -1577,7 +1634,7 @@ function Set-RegistryValue [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Data ) @@ -1622,11 +1679,11 @@ function Remove-RegistryValue param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $Key, [Parameter(Mandatory = $true)] - [String] + [System.String] $Value, [Parameter(Mandatory = $true)] diff --git a/DSCResources/MSFT_xPackageResource/en-US/MSFT_xPackageResource.strings.psd1 b/DSCResources/MSFT_xPackageResource/en-US/MSFT_xPackageResource.strings.psd1 index ebd8a5604..b8a1de60b 100644 --- a/DSCResources/MSFT_xPackageResource/en-US/MSFT_xPackageResource.strings.psd1 +++ b/DSCResources/MSFT_xPackageResource/en-US/MSFT_xPackageResource.strings.psd1 @@ -65,4 +65,5 @@ ConvertFrom-StringData @' ErrorSettingRegistryValue = An error occured while attempting to set the registry key {0} value {1} to {2} ErrorRemovingRegistryValue = An error occured while attempting to remove the registry key {0} value {1} ExeCouldNotBeUninstalled = The .exe file found at {0} could not be uninstalled. The uninstall functionality may not be implemented in this .exe file. + EnteringGetTargetResource = Entering Get-TargetResource in file MSFT_xPackageResource.psm1. '@ diff --git a/DSCResources/MSFT_xRegistryResource/MSFT_xRegistryResource.psm1 b/DSCResources/MSFT_xRegistryResource/MSFT_xRegistryResource.psm1 index 2ce59a11d..f58d15444 100644 --- a/DSCResources/MSFT_xRegistryResource/MSFT_xRegistryResource.psm1 +++ b/DSCResources/MSFT_xRegistryResource/MSFT_xRegistryResource.psm1 @@ -53,9 +53,11 @@ function Get-TargetResource [AllowEmptyString()] $ValueName, + [Parameter()] [String[]] $ValueData, + [Parameter()] [ValidateSet('String', 'Binary', 'DWord', 'QWord', 'MultiString', 'ExpandString')] [String] $ValueType @@ -142,10 +144,10 @@ function Get-TargetResource .PARAMETER Ensure Specifies whether or not the registry key with the given path and the registry key value with the given name should exist. - + To ensure that the registry key and value exists, set this property to Present. To ensure that the registry key and value do not exist, set this property to Absent. - + The default value is Present. .PARAMETER ValueData @@ -153,7 +155,7 @@ function Get-TargetResource .PARAMETER ValueType The type of the value to set. - + The supported types are: String (REG_SZ) Binary (REG-BINARY) @@ -167,12 +169,12 @@ function Get-TargetResource If specified, DWORD/QWORD value data is presented in hexadecimal format. Not valid for other value types. - + The default value is $false. .PARAMETER Force Specifies whether or not to overwrite the registry key with the given path with the new - value if it is already present. + value if it is already present. #> function Set-TargetResource { @@ -190,21 +192,26 @@ function Set-TargetResource [AllowEmptyString()] $ValueName, + [Parameter()] [ValidateSet('Present', 'Absent')] [String] $Ensure = 'Present', + [Parameter()] [ValidateNotNull()] [String[]] $ValueData = @(), + [Parameter()] [ValidateSet('String', 'Binary', 'DWord', 'QWord', 'MultiString', 'ExpandString')] [String] $ValueType = 'String', + [Parameter()] [Boolean] $Hex = $false, + [Parameter()] [Boolean] $Force = $false ) @@ -288,7 +295,7 @@ function Set-TargetResource Write-Verbose -Message ($script:localizedData.OverwritingRegistryKeyValue -f $valueDisplayName, $Key) $null = Set-RegistryKeyValue -RegistryKeyName $registryKeyName -RegistryKeyValueName $ValueName -RegistryKeyValue $expectedRegistryKeyValue -ValueType $ValueType } - } + } } } else @@ -297,7 +304,7 @@ function Set-TargetResource if ($null -ne $actualRegistryKeyValue) { Write-Verbose -Message ($script:localizedData.RemovingRegistryKeyValue -f $valueDisplayName, $Key) - + # If the specified registry key value exists, check if the user specified a registry key value with a name to remove if (-not [String]::IsNullOrEmpty($ValueName)) { @@ -352,7 +359,7 @@ function Set-TargetResource .PARAMETER Ensure Specifies whether or not the registry key and value should exist. - + To test that they exist, set this property to "Present". To test that they do not exist, set the property to "Absent". The default value is "Present". @@ -362,7 +369,7 @@ function Set-TargetResource .PARAMETER ValueType The type of the value. - + The supported types are: String (REG_SZ) Binary (REG-BINARY) @@ -394,21 +401,26 @@ function Test-TargetResource [String] $ValueName, + [Parameter()] [ValidateSet('Present', 'Absent')] [String] $Ensure = 'Present', + [Parameter()] [ValidateNotNull()] [String[]] $ValueData = @(), + [Parameter()] [ValidateSet('String', 'Binary', 'DWord', 'QWord', 'MultiString', 'ExpandString')] [String] $ValueType = 'String', + [Parameter()] [Boolean] $Hex = $false, + [Parameter()] [Boolean] $Force = $false ) @@ -732,6 +744,7 @@ function Get-RegistryKey [String] $RegistryKeyPath, + [Parameter()] [Switch] $WriteAccessAllowed ) @@ -777,9 +790,9 @@ function Get-RegistryKeyValueDisplayName param ( [Parameter(Mandatory = $true)] - [String] [AllowNull()] [AllowEmptyString()] + [String] $RegistryKeyValueName ) @@ -816,8 +829,8 @@ function Get-RegistryKeyValue [Parameter(Mandatory = $true)] [ValidateNotNull()] - [String] [AllowEmptyString()] + [String] $RegistryKeyValueName ) @@ -850,8 +863,8 @@ function Get-RegistryKeyValueType [Parameter(Mandatory = $true)] [ValidateNotNull()] - [String] [AllowEmptyString()] + [String] $RegistryKeyValueName ) @@ -873,8 +886,8 @@ function Convert-ByteArrayToHexString ( [Parameter(Mandatory = $true)] [ValidateNotNull()] - [Object[]] [AllowEmptyCollection()] + [Object[]] $ByteArray ) @@ -905,9 +918,9 @@ function ConvertTo-ReadableString param ( [Parameter(Mandatory = $true)] - [Object[]] [AllowNull()] [AllowEmptyCollection()] + [Object[]] $RegistryKeyValue, [Parameter(Mandatory = $true)] @@ -925,10 +938,10 @@ function ConvertTo-ReadableString { $RegistryKeyValue = Convert-ByteArrayToHexString -ByteArray $RegistryKeyValue } - + if ($RegistryKeyValueType -ne 'MultiString') { - $RegistryKeyValue = [String[]]@() + $RegistryKeyValue + $RegistryKeyValue = [String[]] @() + $RegistryKeyValue } if ($RegistryKeyValue.Count -eq 1 -and -not [String]::IsNullOrEmpty($RegistryKeyValue[0])) @@ -1049,8 +1062,8 @@ function ConvertTo-Binary ( [Parameter()] [AllowNull()] - [String[]] [AllowEmptyCollection()] + [String[]] $RegistryKeyValue ) @@ -1106,8 +1119,8 @@ function ConvertTo-DWord ( [Parameter()] [AllowNull()] - [String[]] [AllowEmptyCollection()] + [String[]] $RegistryKeyValue, [Parameter()] @@ -1169,8 +1182,8 @@ function ConvertTo-MultiString ( [Parameter()] [AllowNull()] - [String[]] [AllowEmptyCollection()] + [String[]] $RegistryKeyValue ) @@ -1178,7 +1191,7 @@ function ConvertTo-MultiString if (($null -ne $RegistryKeyValue) -and ($RegistryKeyValue.Length -gt 0)) { - $multiStringRegistryKeyValue = [String[]]$RegistryKeyValue + $multiStringRegistryKeyValue = [String[]] $RegistryKeyValue } return $multiStringRegistryKeyValue @@ -1199,8 +1212,8 @@ function ConvertTo-QWord ( [Parameter()] [AllowNull()] - [String[]] [AllowEmptyCollection()] + [String[]] $RegistryKeyValue, [Parameter()] @@ -1262,8 +1275,8 @@ function ConvertTo-String ( [Parameter()] [AllowNull()] - [String[]] [AllowEmptyCollection()] + [String[]] $RegistryKeyValue ) @@ -1276,7 +1289,7 @@ function ConvertTo-String if (($null -ne $RegistryKeyValue) -and ($RegistryKeyValue.Count -eq 1)) { - $registryKeyValueAsString = [String]$RegistryKeyValue[0] + $registryKeyValueAsString = [String] $RegistryKeyValue[0] } return $registryKeyValueAsString @@ -1311,13 +1324,13 @@ function Set-RegistryKeyValue [Parameter(Mandatory = $true)] [ValidateNotNull()] - [String] [AllowEmptyString()] + [String] $RegistryKeyValueName, [Parameter(Mandatory = $true)] - [Object] [AllowNull()] + [Object] $RegistryKeyValue, [Parameter(Mandatory = $true)] @@ -1328,11 +1341,11 @@ function Set-RegistryKeyValue if ($ValueType -eq 'Binary') { - $RegistryKeyValue = [Byte[]]$RegistryKeyValue + $RegistryKeyValue = [Byte[]] $RegistryKeyValue } elseif ($ValueType -eq 'MultiString') { - $RegistryKeyValue = [String[]]$RegistryKeyValue + $RegistryKeyValue = [String[]] $RegistryKeyValue } $null = [Microsoft.Win32.Registry]::SetValue($RegistryKeyName, $RegistryKeyValueName, $RegistryKeyValue, $ValueType) @@ -1358,13 +1371,13 @@ function Test-RegistryKeyValuesMatch param ( [Parameter(Mandatory = $true)] - [Object] [AllowNull()] + [Object] $ExpectedRegistryKeyValue, [Parameter(Mandatory = $true)] - [Object] [AllowNull()] + [Object] $ActualRegistryKeyValue, [Parameter(Mandatory = $true)] @@ -1463,9 +1476,9 @@ function Remove-RegistryKeyValue .SYNOPSIS Removes the default value of the specified registry key. This is a wrapper function for unit testing. - + .PARAMETER RegistryKey - The registry key to remove the default value of. + The registry key to remove the default value of. #> function Remove-DefaultRegistryKeyValue { diff --git a/DSCResources/MSFT_xRegistryResource/en-US/MSFT_xRegistryResource.strings.psd1 b/DSCResources/MSFT_xRegistryResource/en-US/MSFT_xRegistryResource.strings.psd1 index 41e70be8b..fd1cb0b76 100644 --- a/DSCResources/MSFT_xRegistryResource/en-US/MSFT_xRegistryResource.strings.psd1 +++ b/DSCResources/MSFT_xRegistryResource/en-US/MSFT_xRegistryResource.strings.psd1 @@ -30,7 +30,7 @@ ConvertFrom-StringData @' RegistryDriveInvalid = The registry drive specified in the registry key path {0} is missing or invalid. ArrayNotAllowedForExpectedType = The specified value data has been declared as a string array, but the registry key type {0} cannot be converted from an array. Please declare the value data as only one string or use the registry type MultiString. DWordDataNotInHexFormat = The specified registry key value data {0} is not in the correct hex format to parse as an Int32 (dword). - QWordDataNotInHexFormat = The specified registry key value data {0} is not in the correct hex format to parse as an Int64 (qword). + QWordDataNotInHexFormat = The specified registry key value data {0} is not in the correct hex format to parse as an Int64 (qword). BinaryDataNotInHexFormat = The specified registry key value data {0} is not in the correct hex format to parse as a Byte array (Binary). InvalidRegistryDrive = The registry drive {0} is invalid. Please update the Key parameter to include a valid registry drive. InvalidRegistryDriveAbbreviation = The registry drive abbreviation {0} is invalid. Please update the Key parameter to include a valid registry drive. diff --git a/DSCResources/MSFT_xRemoteFile/MSFT_xRemoteFile.psm1 b/DSCResources/MSFT_xRemoteFile/MSFT_xRemoteFile.psm1 index 54c510da5..52ab305ce 100644 --- a/DSCResources/MSFT_xRemoteFile/MSFT_xRemoteFile.psm1 +++ b/DSCResources/MSFT_xRemoteFile/MSFT_xRemoteFile.psm1 @@ -1,26 +1,17 @@ -$moduleRoot = Split-Path ` - -Path $MyInvocation.MyCommand.Path ` - -Parent +# Import CommonResourceHelper +$script:dscResourcesFolderFilePath = Split-Path -Path $PSScriptRoot -Parent +$script:commonResourceHelperFilePath = Join-Path -Path $script:dscResourcesFolderFilePath -ChildPath 'CommonResourceHelper.psm1' +Import-Module -Name $script:commonResourceHelperFilePath -#region LocalizedData -$Culture = 'en-us' -if (Test-Path -Path (Join-Path -Path $moduleRoot -ChildPath $PSUICulture)) -{ - $Culture = $PSUICulture -} -Import-LocalizedData ` - -BindingVariable LocalizedData ` - -Filename MSFT_xRemoteFile.psd1 ` - -BaseDirectory $moduleRoot ` - -UICulture $Culture -#endregion +# Localized messages for verbose and error statements in this resource +$script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xRemoteFile' # Path where cache will be stored. It's cleared whenever LCM gets new configuration. $script:cacheLocation = "$env:ProgramData\Microsoft\Windows\PowerShell\Configuration\BuiltinProvCache\MSFT_xRemoteFile" <# -.Synopsis -The Get-TargetResource function is used to fetch the status of file specified in DestinationPath on the target machine. + .SYNOPSIS + The Get-TargetResource function is used to fetch the status of file specified in DestinationPath on the target machine. #> function Get-TargetResource { @@ -28,185 +19,186 @@ function Get-TargetResource [OutputType([System.Collections.Hashtable])] param ( - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $DestinationPath, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $Uri ) # Check whether DestinationPath is existing file - $ensure = "Absent" - $pathItemType = Get-PathItemType -path $DestinationPath - switch($pathItemType) + $ensure = 'Absent' + $pathItemType = Get-PathItemType -Path $DestinationPath + + switch ($pathItemType) { - "File" + 'File' { - Write-Verbose -Message $($LocalizedData.DestinationPathIsExistingFile ` - -f ${DestinationPath}) - $ensure = "Present" + Write-Verbose -Message ($script:localizedData.DestinationPathIsExistingFile -f $DestinationPath) + $ensure = 'Present' } - "Directory" + 'Directory' { - Write-Verbose -Message $($LocalizedData.DestinationPathIsExistingPath ` - -f ${DestinationPath}) + Write-Verbose -Message ($script:localizedData.DestinationPathIsExistingPath -f $DestinationPath) # If it's existing directory, let's check whether expectedDestinationPath exists - $uriFileName = Split-Path $Uri -Leaf - $expectedDestinationPath = Join-Path $DestinationPath $uriFileName - if (Test-Path $expectedDestinationPath) + $uriFileName = Split-Path -Path $Uri -Leaf + $expectedDestinationPath = Join-Path -Path $DestinationPath -ChildPath $uriFileName + + if (Test-Path -Path $expectedDestinationPath) { - Write-Verbose -Message $($LocalizedData.FileExistsInDestinationPath ` - -f ${uriFileName}) - $ensure = "Present" + Write-Verbose -Message ($script:localizedData.FileExistsInDestinationPath -f $uriFileName) + $ensure = 'Present' } } - "Other" + 'Other' { - Write-Verbose -Message $($LocalizedData.DestinationPathUnknownType ` - -f ${DestinationPath},${pathItemType}) + Write-Verbose -Message ($script:localizedData.DestinationPathUnknownType -f $DestinationPath, $pathItemType) } - "NotExists" + 'NotExists' { - Write-Verbose -Message $($LocalizedData.DestinationPathDoesNotExist ` - -f ${DestinationPath}) + Write-Verbose -Message ($script:localizedData.DestinationPathDoesNotExist -f $DestinationPath) } } - $returnValue = @{ + return @{ DestinationPath = $DestinationPath - Uri = $Uri - Ensure = $ensure + Uri = $Uri + Ensure = $ensure } - - $returnValue } <# -.Synopsis -The Set-TargetResource function is used to download file found under Uri location to DestinationPath -Additional parameters can be specified to configure web request + .SYNOPSIS + The Set-TargetResource function is used to download file found under Uri location to DestinationPath + Additional parameters can be specified to configure web request #> function Set-TargetResource { [CmdletBinding()] param ( - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $DestinationPath, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $Uri, + [Parameter()] [System.String] $UserAgent, + [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $Headers, + [Parameter()] [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] $Credential, - [parameter(Mandatory = $false)] + [Parameter()] [System.Boolean] $MatchSource = $true, - [Uint32] + [Parameter()] + [System.Uint32] $TimeoutSec, + [Parameter()] [System.String] $Proxy, + [Parameter()] [System.Management.Automation.PSCredential] $ProxyCredential ) # Validate Uri - if (-not (Test-UriScheme -uri $Uri -scheme "http|https|file")) + if (-not (Test-UriScheme -Uri $Uri -Scheme 'http|https|file')) { - $errorMessage = $($LocalizedData.InvalidWebUriError) ` - -f ${Uri} + $errorMessage = $script:localizedData.InvalidWebUriError -f $Uri New-InvalidDataException ` - -errorId "UriValidationFailure" ` - -errorMessage $errorMessage + -ErrorId 'UriValidationFailure' ` + -ErrorMessage $errorMessage } # Validate DestinationPath scheme - if (-not (Test-UriScheme -uri $DestinationPath -scheme "file")) + if (-not (Test-UriScheme -Uri $DestinationPath -Scheme 'file')) { - $errorMessage = $($LocalizedData.InvalidDestinationPathSchemeError ` - -f ${DestinationPath}) + $errorMessage = $script:localizedData.InvalidDestinationPathSchemeError -f $DestinationPath New-InvalidDataException ` - -errorId "DestinationPathSchemeValidationFailure" ` - -errorMessage $errorMessage + -ErrorId 'DestinationPathSchemeValidationFailure' ` + -ErrorMessage $errorMessage } # Validate DestinationPath is not UNC path - if ($DestinationPath.StartsWith("\\")) - { - $errorMessage = $($LocalizedData.DestinationPathIsUncError ` - -f ${DestinationPath}) + if ($DestinationPath.StartsWith('\\')) + { + $errorMessage = $script:localizedData.DestinationPathIsUncError -f $DestinationPath New-InvalidDataException ` - -errorId "DestinationPathIsUncFailure" ` - -errorMessage $errorMessage + -ErrorId 'DestinationPathIsUncFailure' ` + -ErrorMessage $errorMessage } # Validate DestinationPath does not contain invalid characters - @('*','?','"','<','>','|') | % { - if ($DestinationPath.Contains($_) ){ - $errorMessage = $($LocalizedData.DestinationPathHasInvalidCharactersError ` - -f ${DestinationPath}) + @('*', '?', '"', '<', '>', '|') | Foreach-Object -Process { + if ($DestinationPath.Contains($_)) + { + $errorMessage = $script:localizedData.DestinationPathHasInvalidCharactersError -f $DestinationPath New-InvalidDataException ` - -errorId "DestinationPathHasInvalidCharactersError" ` - -errorMessage $errorMessage + -ErrorId 'DestinationPathHasInvalidCharactersError' ` + -ErrorMessage $errorMessage } } # Validate DestinationPath does not end with / or \ (Invoke-WebRequest requirement) - if ($DestinationPath.EndsWith('/') -or $DestinationPath.EndsWith('\')){ - $errorMessage = $($LocalizedData.DestinationPathEndsWithInvalidCharacterError ` - -f ${DestinationPath}) + if ($DestinationPath.EndsWith('/') -or $DestinationPath.EndsWith('\')) + { + $errorMessage = $script:localizedData.DestinationPathEndsWithInvalidCharacterError -f $DestinationPath New-InvalidDataException ` - -errorId "DestinationPathEndsWithInvalidCharacterError" ` - -errorMessage $errorMessage + -ErrorId 'DestinationPathEndsWithInvalidCharacterError' ` + -ErrorMessage $errorMessage } # Check whether DestinationPath's parent directory exists. Create if it doesn't. - $destinationPathParent = Split-Path $DestinationPath -Parent + $destinationPathParent = Split-Path -Path $DestinationPath -Parent + if (-not (Test-Path $destinationPathParent)) { $null = New-Item -ItemType Directory -Path $destinationPathParent -Force } # Check whether DestinationPath's leaf is an existing folder - $uriFileName = Split-Path $Uri -Leaf + $uriFileName = Split-Path -Path $Uri -Leaf + if (Test-Path $DestinationPath -PathType Container) { - $DestinationPath = Join-Path $DestinationPath $uriFileName + $DestinationPath = Join-Path -Path $DestinationPath -ChildPath $uriFileName } # Remove DestinationPath and MatchSource from parameters as they are not parameters of Invoke-WebRequest - $null = $PSBoundParameters.Remove("DestinationPath") - $null = $PSBoundParameters.Remove("MatchSource") + $null = $PSBoundParameters.Remove('DestinationPath') + $null = $PSBoundParameters.Remove('MatchSource') # Convert headers to hashtable - $null = $PSBoundParameters.Remove("Headers") + $null = $PSBoundParameters.Remove('Headers') $headersHashtable = $null - if ($Headers -ne $null) + if ($null -ne $Headers) { - $headersHashtable = Convert-KeyValuePairArrayToHashtable -array $Headers + $headersHashtable = Convert-KeyValuePairArrayToHashtable -Array $Headers } # Invoke web request @@ -215,25 +207,23 @@ function Set-TargetResource $currentProgressPreference = $ProgressPreference $ProgressPreference = 'SilentlyContinue' - Write-Verbose -Message $($LocalizedData.DownloadingURI ` - -f ${DestinationPath},${URI}) - Invoke-WebRequest @PSBoundParameters -Headers $headersHashtable -outFile $DestinationPath + Write-Verbose -Message ($script:localizedData.DownloadingURI -f $DestinationPath, $URI) + + Invoke-WebRequest @PSBoundParameters -Headers $headersHashtable -OutFile $DestinationPath } catch [System.OutOfMemoryException] { - $errorMessage = $($LocalizedData.DownloadOutOfMemoryException ` - -f $_) + $errorMessage = $script:localizedData.DownloadOutOfMemoryException -f $_ New-InvalidDataException ` - -errorId "SystemOutOfMemoryException" ` - -errorMessage $errorMessage + -ErrorId 'SystemOutOfMemoryException' ` + -ErrorMessage $errorMessage } catch [System.Exception] { - $errorMessage = $($LocalizedData.DownloadException ` - -f $_) + $errorMessage = $script:localizedData.DownloadException -f $_ New-InvalidDataException ` - -errorId "SystemException" ` - -errorMessage $errorMessage + -ErrorId 'SystemException' ` + -ErrorMessage $errorMessage } finally { @@ -247,15 +237,15 @@ function Set-TargetResource $lastWriteTime = $downloadedFile.LastWriteTimeUtc $filesize = $downloadedFile.Length $inputObject = @{} - $inputObject["LastWriteTime"] = $lastWriteTime - $inputObject["FileSize"] = $filesize + $inputObject['LastWriteTime'] = $lastWriteTime + $inputObject['FileSize'] = $filesize Update-Cache -DestinationPath $DestinationPath -Uri $Uri -InputObject $inputObject } } <# -.Synopsis -The Test-TargetResource function is used to validate if the DestinationPath exists on the machine. + .SYNOPSIS + The Test-TargetResource function is used to validate if the DestinationPath exists on the machine. #> function Test-TargetResource { @@ -263,78 +253,85 @@ function Test-TargetResource [OutputType([System.Boolean])] param ( - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $DestinationPath, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $Uri, + [Parameter()] [System.String] $UserAgent, + [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $Headers, + [Parameter()] [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] $Credential, - [parameter(Mandatory = $false)] + [Parameter()] [System.Boolean] $MatchSource = $true, - [Uint32] + [Parameter()] + [System.Uint32] $TimeoutSec, + [Parameter()] [System.String] $Proxy, + [Parameter()] [System.Management.Automation.PSCredential] $ProxyCredential ) # Check whether DestinationPath points to existing file or directory $fileExists = $false - $uriFileName = Split-Path $Uri -Leaf + $uriFileName = Split-Path -Path $Uri -Leaf $pathItemType = Get-PathItemType -Path $DestinationPath - switch($pathItemType) + + switch ($pathItemType) { - "File" + 'File' { - Write-Verbose -Message $($LocalizedData.DestinationPathIsExistingFile ` - -f ${DestinationPath}) + Write-Verbose -Message ($script:localizedData.DestinationPathIsExistingFile -f $DestinationPath) - if ($MatchSource) { + if ($MatchSource) + { $file = Get-Item -Path $DestinationPath # Getting cache. It's cleared every time user runs Start-DscConfiguration $cache = Get-Cache -DestinationPath $DestinationPath -Uri $Uri - if ($cache -ne $null ` - -and ($cache.LastWriteTime -eq $file.LastWriteTimeUtc) ` - -and ($cache.FileSize -eq $file.Length)) + if ($null -ne $cache ` + -and ($cache.LastWriteTime -eq $file.LastWriteTimeUtc) ` + -and ($cache.FileSize -eq $file.Length)) { - Write-Verbose -Message $($LocalizedData.CacheReflectsCurrentState) + Write-Verbose -Message $script:localizedData.CacheReflectsCurrentState $fileExists = $true } else { - Write-Verbose -Message $($LocalizedData.CacheIsEmptyOrNotMatchCurrentState) + Write-Verbose -Message $script:localizedData.CacheIsEmptyOrNotMatchCurrentState } } else { - Write-Verbose -Message $($LocalizedData.MatchSourceFalse) + Write-Verbose -Message $script:localizedData.MatchSourceFalse $fileExists = $true } } - "Directory" + 'Directory' { - Write-Verbose -Message $($LocalizedData.DestinationPathIsExistingPath ` - -f ${DestinationPath}) + Write-Verbose -Message ($script:localizedData.DestinationPathIsExistingPath -f $DestinationPath) $expectedDestinationPath = Join-Path -Path $DestinationPath -ChildPath $uriFileName @@ -344,102 +341,113 @@ function Test-TargetResource { $file = Get-Item -Path $expectedDestinationPath $cache = Get-Cache -DestinationPath $expectedDestinationPath -Uri $Uri - if ($cache -ne $null -and ($cache.LastWriteTime -eq $file.LastWriteTimeUtc)) + + if ($null -ne $cache -and ($cache.LastWriteTime -eq $file.LastWriteTimeUtc)) { - Write-Verbose -Message $($LocalizedData.CacheReflectsCurrentState) + Write-Verbose -Message $script:localizedData.CacheReflectsCurrentState $fileExists = $true } else { - Write-Verbose -Message $($LocalizedData.CacheIsEmptyOrNotMatchCurrentState) + Write-Verbose -Message $script:localizedData.CacheIsEmptyOrNotMatchCurrentState } } else { - Write-Verbose -Message $($LocalizedData.MatchSourceFalse) + Write-Verbose -Message $script:localizedData.MatchSourceFalse $fileExists = $true } } } - "Other" + 'Other' { - Write-Verbose -Message $($LocalizedData.DestinationPathUnknownType ` - -f ${DestinationPath},${pathItemType}) + Write-Verbose -Message ($script:localizedData.DestinationPathUnknownType -f $DestinationPath, $pathItemType) } - "NotExists" + 'NotExists' { - Write-Verbose -Message $($LocalizedData.DestinationPathDoesNotExist ` - -f ${DestinationPath}) + Write-Verbose -Message ($script:localizedData.DestinationPathDoesNotExist -f $DestinationPath) } } $result = $fileExists - $result + return $result } <# -.Synopsis -Throws terminating error of category InvalidData with specified errorId and errorMessage + .SYNOPSIS + Throws terminating error of category InvalidData with specified errorId and errorMessage #> function New-InvalidDataException { - param( - [parameter(Mandatory = $true)] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] [System.String] - $errorId, + $ErrorId, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [System.String] - $errorMessage + $ErrorMessage ) - + $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidData $exception = New-Object ` -TypeName System.InvalidOperationException ` - -ArgumentList $errorMessage + -ArgumentList $ErrorMessage $errorRecord = New-Object ` -TypeName System.Management.Automation.ErrorRecord ` - -ArgumentList $exception, $errorId, $errorCategory, $null + -ArgumentList $exception, $ErrorId, $errorCategory, $null + throw $errorRecord } <# -.Synopsis -Checks whether given URI represents specific scheme -.Description -Most common schemes: file, http, https, ftp -We can also specify logical expressions like: [http|https] + .SYNOPSIS + Checks whether given URI represents specific scheme + + .DESCRIPTION + Most common schemes: file, http, https, ftp + We can also specify logical expressions like: [http|https] #> function Test-UriScheme { - param ( - [parameter(Mandatory = $true)] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] [System.String] - $uri, + $Uri, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [System.String] - $scheme + $Scheme ) - $newUri = $uri -as [System.URI] - $newUri.AbsoluteURI -ne $null -and $newUri.Scheme -match $scheme + + $newUri = $Uri -as [System.URI] + + return ($null -ne $newUri.AbsoluteURI -and $newUri.Scheme -match $Scheme) } <# -.Synopsis -Gets type of the item which path points to. -.Outputs -File, Directory, Other or NotExists + .SYNOPSIS + Gets type of the item which path points to. + + .OUTPUTS + File, Directory, Other or NotExists #> function Get-PathItemType { - param ( - [parameter(Mandatory = $true)] + [CmdletBinding()] + [OutputType([System.String])] + param + ( + [Parameter(Mandatory = $true)] [System.String] - $path + $Path ) $type = $null @@ -448,63 +456,72 @@ function Get-PathItemType if (Test-Path $path) { # Check type of the path - $pathItem = Get-Item -Path $path + $pathItem = Get-Item -Path $Path $pathItemType = $pathItem.GetType().Name - if ($pathItemType -eq "FileInfo") + + if ($pathItemType -eq 'FileInfo') { - $type = "File" + $type = 'File' } - elseif ($pathItemType -eq "DirectoryInfo") + elseif ($pathItemType -eq 'DirectoryInfo') { - $type = "Directory" + $type = 'Directory' } else { - $type = "Other" + $type = 'Other' } } else { - $type = "NotExists" + $type = 'NotExists' } return $type } <# -.Synopsis -Converts CimInstance array of type KeyValuePair to hashtable + .SYNOPSIS + Converts CimInstance array of type KeyValuePair to hashtable #> function Convert-KeyValuePairArrayToHashtable { - param ( - [parameter(Mandatory = $true)] + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] [Microsoft.Management.Infrastructure.CimInstance[]] - $array + $Array ) $hashtable = @{} - foreach($item in $array) + + foreach ($item in $Array) { - $hashtable += @{$item.Key = $item.Value} + $hashtable += @{ + $item.Key = $item.Value + } } return $hashtable } <# -.Synopsis -Gets cache for specific DestinationPath and Uri + .SYNOPSIS + Gets cache for specific DestinationPath and Uri #> function Get-Cache { - param ( - [parameter(Mandatory = $true)] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $DestinationPath, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $Uri @@ -514,80 +531,81 @@ function Get-Cache $key = Get-CacheKey -DestinationPath $DestinationPath -Uri $Uri $path = Join-Path -Path $script:cacheLocation -ChildPath $key - Write-Verbose -Message $($LocalizedData.CacheLookingForPath ` - -f ${Path}) + Write-Verbose -Message ($script:localizedData.CacheLookingForPath -f $Path) - if(-not (Test-Path -Path $path)) + if (-not (Test-Path -Path $path)) { - Write-Verbose -Message $($LocalizedData.CacheNotFoundForPath ` - -f ${DestinationPath},${Uri},${Key}) + Write-Verbose -Message ($script:localizedData.CacheNotFoundForPath -f $DestinationPath, $Uri, $Key) $cacheContent = $null } else { $cacheContent = Import-CliXml -Path $path - Write-Verbose -Message $($LocalizedData.CacheFoundForPath ` - -f ${DestinationPath},${Uri},${Key}) + Write-Verbose -Message ($script:localizedData.CacheFoundForPath -f $DestinationPath, $Uri, $Key) } return $cacheContent } <# -.Synopsis -Creates or updates cache for specific DestinationPath and Uri + .SYNOPSIS + Creates or updates cache for specific DestinationPath and Uri #> function Update-Cache { - param ( - [parameter(Mandatory = $true)] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $DestinationPath, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $Uri, - - [parameter(Mandatory = $true)] - [Object] + + [Parameter(Mandatory = $true)] + [System.Object] $InputObject ) $key = Get-CacheKey -DestinationPath $DestinationPath -Uri $Uri $path = Join-Path -Path $script:cacheLocation -ChildPath $key - if(-not (Test-Path -Path $script:cacheLocation)) + if (-not (Test-Path -Path $script:cacheLocation)) { $null = New-Item -ItemType Directory -Path $script:cacheLocation } - Write-Verbose -Message $($LocalizedData.UpdatingCache ` - -f ${DestinationPath},${Uri},${Key}) + Write-Verbose -Message ($script:localizedData.UpdatingCache -f $DestinationPath, $Uri, $Key) Export-CliXml -Path $path -InputObject $InputObject -Force } <# -.Synopsis -Returns cache key for given parameters + .SYNOPSIS + Returns cache key for given parameters #> function Get-CacheKey { - param ( - [parameter(Mandatory = $true)] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $DestinationPath, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $Uri ) - return [string]::Join("", @($DestinationPath, $Uri)).GetHashCode().ToString() + + return [System.String]::Join('', @($DestinationPath, $Uri)).GetHashCode().ToString() } -Export-ModuleMember -Function *-TargetResource +Export-ModuleMember -Function Get-TargetResource, Set-TargetResource, Test-TargetResource diff --git a/DSCResources/MSFT_xRemoteFile/en-us/MSFT_xRemoteFile.psd1 b/DSCResources/MSFT_xRemoteFile/en-us/MSFT_xRemoteFile.strings.psd1 similarity index 100% rename from DSCResources/MSFT_xRemoteFile/en-us/MSFT_xRemoteFile.psd1 rename to DSCResources/MSFT_xRemoteFile/en-us/MSFT_xRemoteFile.strings.psd1 diff --git a/DSCResources/MSFT_xScriptResource/MSFT_xScriptResource.psm1 b/DSCResources/MSFT_xScriptResource/MSFT_xScriptResource.psm1 index 441449a96..d71cdb881 100644 --- a/DSCResources/MSFT_xScriptResource/MSFT_xScriptResource.psm1 +++ b/DSCResources/MSFT_xScriptResource/MSFT_xScriptResource.psm1 @@ -26,17 +26,17 @@ $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xScriptResource' .PARAMETER Credential The Credential to run the get script under if needed. #> -function Get-TargetResource +function Get-TargetResource { [OutputType([Hashtable])] [CmdletBinding()] - param + param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $GetScript, - + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] @@ -55,7 +55,7 @@ function Get-TargetResource ) Write-Verbose -Message $script:localizedData.GetTargetResourceStartVerboseMessage - + $invokeScriptParameters = @{ ScriptBlock = [ScriptBlock]::Create($GetScript) } @@ -101,16 +101,16 @@ function Get-TargetResource .PARAMETER Credential The Credential to run the set script under if needed. #> -function Set-TargetResource +function Set-TargetResource { [CmdletBinding()] - param - ( + param + ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $GetScript, - + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] @@ -166,17 +166,17 @@ function Set-TargetResource .PARAMETER Credential The Credential to run the test script under if needed. #> -function Test-TargetResource +function Test-TargetResource { [OutputType([Boolean])] [CmdletBinding()] - param - ( + param + ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $GetScript, - + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] @@ -245,12 +245,12 @@ function Invoke-Script { [OutputType([Object])] [CmdletBinding()] - param + param ( [Parameter(Mandatory = $true)] - [ScriptBlock] + [ScriptBlock] $ScriptBlock, - + [Parameter()] [ValidateNotNull()] [System.Management.Automation.PSCredential] diff --git a/DSCResources/MSFT_xServiceResource/MSFT_xServiceResource.psm1 b/DSCResources/MSFT_xServiceResource/MSFT_xServiceResource.psm1 index 2f78caf4e..26e2e8823 100644 --- a/DSCResources/MSFT_xServiceResource/MSFT_xServiceResource.psm1 +++ b/DSCResources/MSFT_xServiceResource/MSFT_xServiceResource.psm1 @@ -189,7 +189,7 @@ function Get-TargetResource Here are the paths through which Set-TargetResource calls Invoke-CimMethod: Set-TargetResource --> Set-ServicePath --> Invoke-CimMethod - --> Set-ServiceProperty --> Set-ServiceDependencies --> Invoke-CimMethod + --> Set-ServiceProperty --> Set-ServiceDependency --> Invoke-CimMethod --> Set-ServiceAccountProperty --> Invoke-CimMethod --> Set-ServiceStartupType --> Invoke-CimMethod #> @@ -203,50 +203,63 @@ function Set-TargetResource [String] $Name, + [Parameter()] [ValidateSet('Present', 'Absent')] [String] $Ensure = 'Present', + [Parameter()] [ValidateNotNullOrEmpty()] [String] $Path, + [Parameter()] [ValidateSet('Automatic', 'Manual', 'Disabled')] [String] $StartupType, + [Parameter()] [ValidateSet('LocalSystem', 'LocalService', 'NetworkService')] [String] $BuiltInAccount, + [Parameter()] [String] $GroupManagedServiceAccount, + [Parameter()] [ValidateSet('Running', 'Stopped', 'Ignore')] [String] $State = 'Running', + [Parameter()] [Boolean] $DesktopInteract = $false, + [Parameter()] [ValidateNotNullOrEmpty()] [String] $DisplayName, + [Parameter()] [ValidateNotNullOrEmpty()] [String] $Description, + [Parameter()] [String[]] [AllowEmptyCollection()] $Dependencies, + [Parameter()] [UInt32] $StartupTimeout = 30000, + [Parameter()] [UInt32] $TerminateTimeout = 30000, + [Parameter()] [ValidateNotNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] @@ -426,50 +439,63 @@ function Test-TargetResource [String] $Name, + [Parameter()] [ValidateSet('Present', 'Absent')] [String] $Ensure = 'Present', + [Parameter()] [ValidateNotNullOrEmpty()] [String] $Path, + [Parameter()] [ValidateSet('Automatic', 'Manual', 'Disabled')] [String] $StartupType, + [Parameter()] [ValidateSet('LocalSystem', 'LocalService', 'NetworkService')] [String] $BuiltInAccount, + [Parameter()] [String] $GroupManagedServiceAccount, + [Parameter()] [Boolean] $DesktopInteract = $false, + [Parameter()] [ValidateSet('Running', 'Stopped', 'Ignore')] [String] $State = 'Running', + [Parameter()] [ValidateNotNull()] [String] $DisplayName, + [Parameter()] [String] [AllowEmptyString()] $Description, + [Parameter()] [String[]] [AllowEmptyCollection()] $Dependencies, + [Parameter()] [UInt32] $StartupTimeout = 30000, + [Parameter()] [UInt32] $TerminateTimeout = 30000, + [Parameter()] [ValidateNotNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] @@ -864,7 +890,7 @@ function Set-ServicePath SupportsShouldProcess is enabled because Invoke-CimMethod calls ShouldProcess. This function calls Invoke-CimMethod directly. #> -function Set-ServiceDependencies +function Set-ServiceDependency { [CmdletBinding(SupportsShouldProcess = $true)] param @@ -969,7 +995,7 @@ function Grant-LogOnAsServiceRight private const int UNLEN = 256; private const int DNLEN = 15; - // Extra characteres for "\","@" etc. + // Extra characteres for '\', '@' etc. private const int EXTRA_LENGTH = 3; #endregion constants @@ -1547,7 +1573,7 @@ function Set-ServiceStartupType SupportsShouldProcess is enabled because Invoke-CimMethod calls ShouldProcess. Here are the paths through which Set-ServiceProperty calls Invoke-CimMethod: - Set-ServiceProperty --> Set-ServiceDependencies --> Invoke-CimMethod + Set-ServiceProperty --> Set-ServiceDependency --> Invoke-CimMethod --> Set-ServieceAccountProperty --> Invoke-CimMethod --> Set-ServiceStartupType --> Invoke-CimMethod #> @@ -1624,7 +1650,7 @@ function Set-ServiceProperty # Update service dependencies if needed if ($PSBoundParameters.ContainsKey('Dependencies')) { - Set-ServiceDependencies -ServiceName $ServiceName -Dependencies $Dependencies + Set-ServiceDependency -ServiceName $ServiceName -Dependencies $Dependencies } # Update service account properties if needed diff --git a/DSCResources/MSFT_xUserResource/MSFT_xUserResource.psm1 b/DSCResources/MSFT_xUserResource/MSFT_xUserResource.psm1 index aed7515ab..c6d7698cf 100644 --- a/DSCResources/MSFT_xUserResource/MSFT_xUserResource.psm1 +++ b/DSCResources/MSFT_xUserResource/MSFT_xUserResource.psm1 @@ -46,7 +46,7 @@ function Get-TargetResource <# .SYNOPSIS Creates, modifies, or deletes a user. - + .PARAMETER UserName The name of the user to create, modify, or delete. @@ -80,7 +80,7 @@ function Get-TargetResource Specifies whether the user is allowed to change their password or not. By default this is set to $false - .NOTES + .NOTES If Ensure is set to 'Present' then the password parameter is required. #> function Set-TargetResource @@ -95,30 +95,38 @@ function Set-TargetResource [System.String] $UserName, + [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present', + [Parameter()] [System.String] $FullName, + [Parameter()] [System.String] $Description, + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Password, + [Parameter()] [System.Boolean] $Disabled, + [Parameter()] [System.Boolean] $PasswordNeverExpires, + [Parameter()] [System.Boolean] $PasswordChangeRequired, + [Parameter()] [System.Boolean] $PasswordChangeNotAllowed ) @@ -178,30 +186,38 @@ function Test-TargetResource [System.String] $UserName, + [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present', + [Parameter()] [System.String] $FullName, + [Parameter()] [System.String] $Description, + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Password, + [Parameter()] [System.Boolean] $Disabled, + [Parameter()] [System.Boolean] $PasswordNeverExpires, + [Parameter()] [System.Boolean] $PasswordChangeRequired, + [Parameter()] [System.Boolean] $PasswordChangeNotAllowed ) @@ -290,7 +306,7 @@ function Get-TargetResourceOnFullSKU <# .SYNOPSIS Creates, modifies, or deletes a user when on a full server. - + .PARAMETER UserName The name of the user to create, modify, or delete. @@ -324,7 +340,7 @@ function Get-TargetResourceOnFullSKU Specifies whether the user is allowed to change their password or not. By default this is set to $false - .NOTES + .NOTES If Ensure is set to 'Present' then the Password parameter is required. #> function Set-TargetResourceOnFullSKU @@ -337,30 +353,38 @@ function Set-TargetResourceOnFullSKU [System.String] $UserName, + [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present', + [Parameter()] [System.String] $FullName, + [Parameter()] [System.String] $Description, + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Password, + [Parameter()] [System.Boolean] $Disabled, + [Parameter()] [System.Boolean] $PasswordNeverExpires, + [Parameter()] [System.Boolean] $PasswordChangeRequired, + [Parameter()] [System.Boolean] $PasswordChangeNotAllowed ) @@ -500,7 +524,7 @@ function Set-TargetResourceOnFullSKU else { # Ensure is set to 'Absent' - if ($user -ne $null) + if ($null -ne $user) { # The user exists if ($pscmdlet.ShouldProcess($script:localizedData.UserWithName -f $UserName, $script:localizedData.RemoveOperation)) @@ -579,30 +603,38 @@ function Test-TargetResourceOnFullSKU [System.String] $UserName, + [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present', + [Parameter()] [System.String] $FullName, + [Parameter()] [System.String] $Description, + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Password, + [Parameter()] [System.Boolean] $Disabled, + [Parameter()] [System.Boolean] $PasswordNeverExpires, + [Parameter()] [System.Boolean] $PasswordChangeRequired, + [Parameter()] [System.Boolean] $PasswordChangeNotAllowed ) @@ -725,7 +757,7 @@ function Get-TargetResourceOnNanoServer [CmdletBinding()] param ( - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $UserName @@ -780,7 +812,7 @@ function Get-TargetResourceOnNanoServer <# .SYNOPSIS Creates, modifies, or deletes a user when on Nano Server. - + .PARAMETER UserName The name of the user to create, modify, or delete. @@ -814,7 +846,7 @@ function Get-TargetResourceOnNanoServer Specifies whether the user is allowed to change their password or not. By default this is set to $false - .NOTES + .NOTES If Ensure is set to 'Present' then the Password parameter is required. #> function Set-TargetResourceOnNanoServer @@ -826,30 +858,38 @@ function Set-TargetResourceOnNanoServer [System.String] $UserName, + [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present', + [Parameter()] [System.String] $FullName, + [Parameter()] [System.String] $Description, + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Password, + [Parameter()] [System.Boolean] $Disabled, + [Parameter()] [System.Boolean] $PasswordNeverExpires, + [Parameter()] [System.Boolean] $PasswordChangeRequired, + [Parameter()] [System.Boolean] $PasswordChangeNotAllowed ) @@ -862,7 +902,7 @@ function Set-TargetResourceOnNanoServer # Try to find a user by a name. $userExists = $false - + try { [Microsoft.PowerShell.Commands.LocalUser] $user = Get-LocalUser -Name $UserName -ErrorAction Stop @@ -897,7 +937,7 @@ function Set-TargetResourceOnNanoServer { if (-not $userExists -or $FullName -ne $user.FullName) { - if ($FullName -eq $null) + if ($null -eq $FullName) { Set-LocalUser -Name $UserName -FullName ([String]::Empty) } @@ -960,12 +1000,12 @@ function Set-TargetResourceOnNanoServer # NOTE: The parameter name and the property name have opposite meaning. [System.Boolean] $expected = -not $PasswordChangeNotAllowed $actual = $expected - + if ($userExists) { $actual = $user.UserMayChangePassword } - + if ($PSBoundParameters.ContainsKey('PasswordChangeNotAllowed') -and ((-not $userExists) -or ($expected -ne $actual))) { Set-LocalUser -Name $UserName -UserMayChangePassword $expected @@ -1035,30 +1075,38 @@ function Test-TargetResourceOnNanoServer [System.String] $UserName, + [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present', + [Parameter()] [System.String] $FullName, + [Parameter()] [System.String] $Description, + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Password, + [Parameter()] [System.Boolean] $Disabled, + [Parameter()] [System.Boolean] $PasswordNeverExpires, + [Parameter()] [System.Boolean] $PasswordChangeRequired, + [Parameter()] [System.Boolean] $PasswordChangeNotAllowed ) @@ -1171,7 +1219,7 @@ function Assert-UserNameValid # Check if the name consists of only periods and/or white spaces $wrongName = $true - + for ($i = 0; $i -lt $UserName.Length; $i++) { if (-not [Char]::IsWhiteSpace($UserName, $i) -and $UserName[$i] -ne '.') @@ -1181,7 +1229,7 @@ function Assert-UserNameValid } } - $invalidChars = @('\','/','"','[',']',':','|','<','>','+','=',';',',','?','*','@') + $invalidChars = @('\', '/', '"', '[', ']', ':', '|', '<', '>', '+', '=', ';', ',', '?', '*', '@') if ($wrongName) { @@ -1237,7 +1285,7 @@ function New-ConnectionException <# .SYNOPSIS Tests the local user's credentials on the local machine. - + .PARAMETER UserName The username to validate the credentials of. @@ -1255,6 +1303,7 @@ function Test-CredentialsValidOnNanoServer [System.String] $UserName, + [Parameter()] [ValidateNotNullOrEmpty()] [SecureString] $Password diff --git a/DSCResources/MSFT_xWindowsFeature/MSFT_xWindowsFeature.psm1 b/DSCResources/MSFT_xWindowsFeature/MSFT_xWindowsFeature.psm1 index 41074694d..5646bef21 100644 --- a/DSCResources/MSFT_xWindowsFeature/MSFT_xWindowsFeature.psm1 +++ b/DSCResources/MSFT_xWindowsFeature/MSFT_xWindowsFeature.psm1 @@ -1,7 +1,3 @@ -# Global needed to indicate if a restart is required -[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] -param () - Import-Module -Name (Join-Path -Path (Split-Path $PSScriptRoot -Parent) ` -ChildPath 'CommonResourceHelper.psm1') @@ -23,7 +19,7 @@ $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xWindowsFeature' If the specified role or feature does not contain any subfeatures then IncludeAllSubFeature will be set to $false. If the specified feature contains one or more subfeatures then IncludeAllSubFeature will be set to $true only if all the - subfeatures are installed. Otherwise, IncludeAllSubFeature will be set to $false. + subfeatures are installed. Otherwise, IncludeAllSubFeature will be set to $false. #> function Get-TargetResource { @@ -35,19 +31,20 @@ function Get-TargetResource [ValidateNotNullOrEmpty()] [String] $Name, - + + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential ) - + Write-Verbose -Message ($script:localizedData.GetTargetResourceStartMessage -f $Name) - - Import-ServerManager - + + Import-ServerManager + Write-Verbose -Message ($script:localizedData.QueryFeature -f $Name) - + $isWinServer2008R2SP1 = Test-IsWinServer2008R2SP1 if ($isWinServer2008R2SP1 -and $PSBoundParameters.ContainsKey('Credential')) { @@ -59,11 +56,11 @@ function Get-TargetResource { $feature = Get-WindowsFeature @PSBoundParameters } - - Assert-SingleFeatureExists -Feature $feature -Name $Name - + + Assert-SingleInstanceOfFeature -Feature $feature -Name $Name + $includeAllSubFeature = $true - + if ($feature.SubFeatures.Count -eq 0) { $includeAllSubFeature = $false @@ -72,14 +69,13 @@ function Get-TargetResource { foreach ($currentSubFeatureName in $feature.SubFeatures) { - $getWindowsFeatureParameters = @{ Name = $currentSubFeatureName } if ($PSBoundParameters.ContainsKey('Credential')) { - $getWindowsFeatureParameters['Credential'] = $Credential + $getWindowsFeatureParameters['Credential'] = $Credential } if ($isWinServer2008R2SP1 -and $PSBoundParameters.ContainsKey('Credential')) @@ -97,9 +93,9 @@ function Get-TargetResource { $subFeature = Get-WindowsFeature @getWindowsFeatureParameters } - - Assert-SingleFeatureExists -Feature $subFeature -Name $currentSubFeatureName - + + Assert-SingleInstanceOfFeature -Feature $subFeature -Name $currentSubFeatureName + if (-not $subFeature.Installed) { $includeAllSubFeature = $false @@ -118,7 +114,7 @@ function Get-TargetResource } Write-Verbose -Message ($script:localizedData.GetTargetResourceEndMessage -f $Name) - + # Add all feature properties to the hash table return @{ Name = $Name @@ -131,7 +127,7 @@ function Get-TargetResource <# .SYNOPSIS Installs or uninstalls the role or feature with the given name on the target machine - with the option of installing or uninstalling all subfeatures as well. + with the option of installing or uninstalling all subfeatures as well. .PARAMETER Name The name of the role or feature to install or uninstall. @@ -166,18 +162,22 @@ function Set-TargetResource [String] $Name, + [Parameter()] [ValidateSet('Present', 'Absent')] [String] $Ensure = 'Present', + [Parameter()] [Boolean] $IncludeAllSubFeature = $false, + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential, + [Parameter()] [ValidateNotNullOrEmpty()] [String] $LogPath @@ -198,7 +198,7 @@ function Set-TargetResource if ($PSBoundParameters.ContainsKey('LogPath')) { - $addWindowsFeatureParameters['LogPath'] = $LogPath + $addWindowsFeatureParameters['LogPath'] = $LogPath } Write-Verbose -Message ($script:localizedData.InstallFeature -f $Name) @@ -218,7 +218,7 @@ function Set-TargetResource { if ($PSBoundParameters.ContainsKey('Credential')) { - $addWindowsFeatureParameters['Credential'] = $Credential + $addWindowsFeatureParameters['Credential'] = $Credential } $feature = Add-WindowsFeature @addWindowsFeatureParameters @@ -232,7 +232,7 @@ function Set-TargetResource if ($feature.RestartNeeded -eq 'Yes') { Write-Verbose -Message $script:localizedData.RestartNeeded - $global:DSCMachineStatus = 1 + Set-DSCMachineRebootRequired } } else @@ -249,7 +249,7 @@ function Set-TargetResource if ($PSBoundParameters.ContainsKey('LogPath')) { - $removeWindowsFeatureParameters['LogPath'] = $LogPath + $removeWindowsFeatureParameters['LogPath'] = $LogPath } Write-Verbose -Message ($script:localizedData.UninstallFeature -f $Name) @@ -269,7 +269,7 @@ function Set-TargetResource { if ($PSBoundParameters.ContainsKey('Credential')) { - $addWindowsFeatureParameters['Credential'] = $Credential + $addWindowsFeatureParameters['Credential'] = $Credential } $feature = Remove-WindowsFeature @removeWindowsFeatureParameters @@ -283,7 +283,7 @@ function Set-TargetResource if ($feature.RestartNeeded -eq 'Yes') { Write-Verbose -Message $script:localizedData.RestartNeeded - $global:DSCMachineStatus = 1 + Set-DSCMachineRebootRequired } } else @@ -297,7 +297,7 @@ function Set-TargetResource <# .SYNOPSIS - Tests if the role or feature with the given name is in the desired state. + Tests if the role or feature with the given name is in the desired state. .PARAMETER Name The name of the role or feature to test the state of. @@ -336,18 +336,22 @@ function Test-TargetResource [String] $Name, + [Parameter()] [ValidateSet('Present', 'Absent')] [String] $Ensure = 'Present', + [Parameter()] [Boolean] $IncludeAllSubFeature = $false, + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential, + [Parameter()] [ValidateNotNullOrEmpty()] [String] $LogPath @@ -355,7 +359,7 @@ function Test-TargetResource ) Write-Verbose -Message ($script:localizedData.TestTargetResourceStartMessage -f $Name) - + Import-ServerManager $testTargetResourceResult = $false @@ -366,11 +370,11 @@ function Test-TargetResource if ($PSBoundParameters.ContainsKey('Credential')) { - $getWindowsFeatureParameters['Credential'] = $Credential + $getWindowsFeatureParameters['Credential'] = $Credential } - + Write-Verbose -Message ($script:localizedData.QueryFeature -f $Name) - + $isWinServer2008R2SP1 = Test-IsWinServer2008R2SP1 if ($isWinServer2008R2SP1 -and $PSBoundParameters.ContainsKey('Credential')) { @@ -387,22 +391,22 @@ function Test-TargetResource { $feature = Get-WindowsFeature @getWindowsFeatureParameters } - - Assert-SingleFeatureExists -Feature $feature -Name $Name - + + Assert-SingleInstanceOfFeature -Feature $feature -Name $Name + # Check if the feature is in the requested Ensure state. if (($Ensure -eq 'Present' -and $feature.Installed -eq $true) -or ` ($Ensure -eq 'Absent' -and $feature.Installed -eq $false)) { $testTargetResourceResult = $true - + if ($IncludeAllSubFeature) { # Check if each subfeature is in the requested state. foreach ($currentSubFeatureName in $feature.SubFeatures) { $getWindowsFeatureParameters['Name'] = $currentSubFeatureName - + if ($isWinServer2008R2SP1 -and $PSBoundParameters.ContainsKey('Credential')) { <# @@ -418,15 +422,15 @@ function Test-TargetResource { $subFeature = Get-WindowsFeature @getWindowsFeatureParameters } - - Assert-SingleFeatureExists -Feature $subFeature -Name $currentSubFeatureName - + + Assert-SingleInstanceOfFeature -Feature $subFeature -Name $currentSubFeatureName + if (-not $subFeature.Installed -and $Ensure -eq 'Present') { $testTargetResourceResult = $false break } - + if ($subFeature.Installed -and $Ensure -eq 'Absent') { $testTargetResourceResult = $false @@ -440,9 +444,9 @@ function Test-TargetResource # Ensure is not in the correct state $testTargetResourceResult = $false } - + Write-Verbose -Message ($script:localizedData.TestTargetResourceEndMessage -f $Name) - + return $testTargetResourceResult } @@ -456,16 +460,18 @@ function Test-TargetResource .PARAMETER Name The name of the role or feature to include in any error messages that are thrown. - (Not used to assert validity of the feature). + (Not used to assert validity of the feature). #> -function Assert-SingleFeatureExists +function Assert-SingleInstanceOfFeature { [CmdletBinding()] param ( + [Parameter()] [PSObject] $Feature, + [Parameter()] [String] $Name ) @@ -488,7 +494,7 @@ function Assert-SingleFeatureExists #> function Import-ServerManager { - param + param () <# @@ -517,7 +523,8 @@ function Import-ServerManager { Import-Module -Name 'ServerManager' -ErrorAction Stop } - catch [System.Management.Automation.RuntimeException] { + catch [System.Management.Automation.RuntimeException] + { if ($_.Exception.Message -like "*Some or all identity references could not be translated*") { Write-Verbose $_.Exception.Message @@ -538,7 +545,7 @@ function Import-ServerManager <# .SYNOPSIS Tests if the machine is a Windows Server 2008 R2 SP1 machine. - + .NOTES Since Assert-PrequisitesValid ensures that ServerManager is available on the machine, this function only checks the OS version. diff --git a/DSCResources/MSFT_xWindowsOptionalFeature/MSFT_xWindowsOptionalFeature.psm1 b/DSCResources/MSFT_xWindowsOptionalFeature/MSFT_xWindowsOptionalFeature.psm1 index 5736ee56b..d9a0a8e8a 100644 --- a/DSCResources/MSFT_xWindowsOptionalFeature/MSFT_xWindowsOptionalFeature.psm1 +++ b/DSCResources/MSFT_xWindowsOptionalFeature/MSFT_xWindowsOptionalFeature.psm1 @@ -1,5 +1,3 @@ -# PSSA global rule suppression is allowed here because $global:DSCMachineStatus must be set -[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] param () Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'CommonResourceHelper.psm1') @@ -15,11 +13,11 @@ $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xWindowsOptionalFe function Get-TargetResource { [CmdletBinding()] - [OutputType([Hashtable])] + [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $Name ) @@ -28,11 +26,11 @@ function Get-TargetResource Assert-ResourcePrerequisitesValid $windowsOptionalFeature = Dism\Get-WindowsOptionalFeature -FeatureName $Name -Online - + <# $windowsOptionalFeatureProperties and this section of code are needed because an error will be thrown if a property is not found in WMF 4 instead of returning null. - #> + #> $windowsOptionalFeatureProperties = @{} $propertiesNeeded = @( 'LogPath', 'State', 'CustomProperties', 'FeatureName', 'LogLevel', 'Description', 'DisplayName' ) @@ -51,8 +49,8 @@ function Get-TargetResource $windowsOptionalFeatureResource = @{ LogPath = $windowsOptionalFeatureProperties.LogPath Ensure = Convert-FeatureStateToEnsure -State $windowsOptionalFeatureProperties.State - CustomProperties = - Convert-CustomPropertyArrayToStringArray -CustomProperties $windowsOptionalFeatureProperties.CustomProperties + CustomProperties = Convert-CustomPropertyArrayToStringArray ` + -CustomProperties $windowsOptionalFeatureProperties.CustomProperties Name = $windowsOptionalFeatureProperties.FeatureName LogLevel = $windowsOptionalFeatureProperties.LogLevel Description = $windowsOptionalFeatureProperties.Description @@ -81,7 +79,7 @@ function Get-TargetResource being disabled. .PARAMETER NoWindowsUpdateCheck - Specifies whether or not DISM contacts Windows Update (WU) when searching for the source + Specifies whether or not DISM contacts Windows Update (WU) when searching for the source files to enable the feature. If $true, DISM will not contact WU. @@ -102,24 +100,29 @@ function Set-TargetResource param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $Name, + [Parameter()] [ValidateSet('Present', 'Absent')] - [String] + [System.String] $Ensure = 'Present', - [Boolean] + [Parameter()] + [System.Boolean] $RemoveFilesOnDisable, - [Boolean] + [Parameter()] + [System.Boolean] $NoWindowsUpdateCheck, - [String] + [Parameter()] + [System.String] $LogPath, + [Parameter()] [ValidateSet('ErrorsOnly', 'ErrorsAndWarning', 'ErrorsAndWarningAndInformation')] - [String] + [System.String] $LogLevel = 'ErrorsAndWarningAndInformation' ) @@ -129,9 +132,23 @@ function Set-TargetResource $dismLogLevel = switch ($LogLevel) { - 'ErrorsOnly' { 'Errors'; break } - 'ErrorsAndWarning' { 'Warnings'; break } - 'ErrorsAndWarningAndInformation' { 'WarningsInfo'; break } + 'ErrorsOnly' + { + 'Errors' + break + } + + 'ErrorsAndWarning' + { + 'Warnings' + break + } + + 'ErrorsAndWarningAndInformation' + { + 'WarningsInfo' + break + } } # Construct splatting hashtable for DISM cmdlets @@ -179,7 +196,7 @@ function Set-TargetResource <# $restartNeeded and this section of code are needed because an error will be thrown if the RestartNeeded property is not found in WMF 4. - #> + #> try { $restartNeeded = $windowsOptionalFeature.RestartNeeded @@ -193,7 +210,7 @@ function Set-TargetResource if ($restartNeeded) { Write-Verbose -Message $script:localizedData.RestartNeeded - $global:DSCMachineStatus = 1 + Set-DSCMachineRebootRequired } Write-Verbose -Message ($script:localizedData.SetTargetResourceEndMessage -f $Name) @@ -226,28 +243,33 @@ function Set-TargetResource function Test-TargetResource { [CmdletBinding()] - [OutputType([Boolean])] + [OutputType([System.Boolean])] param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $Name, + [Parameter()] [ValidateSet('Present', 'Absent')] - [String] + [System.String] $Ensure = 'Present', - [Boolean] + [Parameter()] + [System.Boolean] $RemoveFilesOnDisable, - [Boolean] + [Parameter()] + [System.Boolean] $NoWindowsUpdateCheck, - [String] + [Parameter()] + [System.String] $LogPath, + [Parameter()] [ValidateSet('ErrorsOnly', 'ErrorsAndWarning', 'ErrorsAndWarningAndInformation')] - [String] + [System.String] $LogLevel = 'ErrorsAndWarningAndInformation' ) @@ -256,7 +278,7 @@ function Test-TargetResource Assert-ResourcePrerequisitesValid $windowsOptionalFeature = Dism\Get-WindowsOptionalFeature -FeatureName $Name -Online - + $featureIsInDesiredState = $false if ($null -eq $windowsOptionalFeature -or $windowsOptionalFeature.State -eq 'Disabled') @@ -267,9 +289,9 @@ function Test-TargetResource { $featureIsInDesiredState = $Ensure -eq 'Present' } - + Write-Verbose -Message ($script:localizedData.TestTargetResourceEndMessage -f $Name) - + return $featureIsInDesiredState } @@ -284,14 +306,15 @@ function Test-TargetResource function Convert-CustomPropertyArrayToStringArray { [CmdletBinding()] - [OutputType([String[]])] + [OutputType([System.String[]])] param ( + [Parameter()] [PSCustomObject[]] $CustomProperties ) - $propertiesAsStrings = [String[]] @() + $propertiesAsStrings = [System.String[]] @() foreach ($customProperty in $CustomProperties) { @@ -315,11 +338,11 @@ function Convert-CustomPropertyArrayToStringArray function Convert-FeatureStateToEnsure { [CmdletBinding()] - [OutputType([String])] + [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] - [String] + [System.String] $State ) @@ -366,8 +389,9 @@ function Assert-ResourcePrerequisitesValid # Check that we are running as an administrator $windowsIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent() $windowsPrincipal = New-Object -TypeName 'System.Security.Principal.WindowsPrincipal' -ArgumentList @( $windowsIdentity ) - + $adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator + if (-not $windowsPrincipal.IsInRole($adminRole)) { New-InvalidOperationException -Message $script:localizedData.ElevationRequired diff --git a/DSCResources/MSFT_xWindowsPackageCab/MSFT_xWindowsPackageCab.psm1 b/DSCResources/MSFT_xWindowsPackageCab/MSFT_xWindowsPackageCab.psm1 index 833b3a16a..f67ac1ac3 100644 --- a/DSCResources/MSFT_xWindowsPackageCab/MSFT_xWindowsPackageCab.psm1 +++ b/DSCResources/MSFT_xWindowsPackageCab/MSFT_xWindowsPackageCab.psm1 @@ -44,6 +44,7 @@ function Get-TargetResource [String] $SourcePath, + [Parameter()] [ValidateNotNullOrEmpty()] [String] $LogPath @@ -126,6 +127,7 @@ function Set-TargetResource [String] $SourcePath, + [Parameter()] [ValidateNotNullOrEmpty()] [String] $LogPath @@ -192,6 +194,7 @@ function Test-TargetResource [String] $SourcePath, + [Parameter()] [ValidateNotNullOrEmpty()] [String] $LogPath diff --git a/DSCResources/MSFT_xWindowsProcess/MSFT_xWindowsProcess.psm1 b/DSCResources/MSFT_xWindowsProcess/MSFT_xWindowsProcess.psm1 index 0700a6b90..79e106651 100644 --- a/DSCResources/MSFT_xWindowsProcess/MSFT_xWindowsProcess.psm1 +++ b/DSCResources/MSFT_xWindowsProcess/MSFT_xWindowsProcess.psm1 @@ -30,20 +30,21 @@ $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xWindowsProcess' #> function Get-TargetResource { - [OutputType([Hashtable])] + [OutputType([System.Collections.Hashtable])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path, [Parameter(Mandatory = $true)] [AllowEmptyString()] - [String] + [System.String] $Arguments, + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] @@ -95,6 +96,7 @@ function Get-TargetResource } Write-Verbose -Message ($script:localizedData.GetTargetResourceEndMessage -f $Path) + return $processToReturn } @@ -149,33 +151,39 @@ function Set-TargetResource ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path, [Parameter(Mandatory = $true)] [AllowEmptyString()] - [String] + [System.String] $Arguments, + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential, + [Parameter()] [ValidateSet('Present', 'Absent')] - [String] + [System.String] $Ensure = 'Present', - [String] + [Parameter()] + [System.String] $StandardOutputPath, - [String] + [Parameter()] + [System.String] $StandardErrorPath, - [String] + [Parameter()] + [System.String] $StandardInputPath, - [String] + [Parameter()] + [System.String] $WorkingDirectory ) @@ -209,6 +217,7 @@ function Set-TargetResource Assert-HashtableDoesNotContainKey @assertHashtableParams $whatIfShouldProcess = $PSCmdlet.ShouldProcess($Path, $script:localizedData.StoppingProcessWhatif) + if ($processCimInstance.Count -gt 0 -and $whatIfShouldProcess) { # If there are multiple process Ids, all will be included to be stopped @@ -229,6 +238,7 @@ function Set-TargetResource New-InvalidOperationException -Message $errorMessage } + <# Before returning from Set-TargetResource we have to ensure a subsequent Test-TargetResource is going to work @@ -256,7 +266,7 @@ function Set-TargetResource foreach ($shouldBeRootedPathArgument in $shouldBeRootedPathArguments) { - if (-not [String]::IsNullOrEmpty($PSBoundParameters[$shouldBeRootedPathArgument])) + if (-not [System.String]::IsNullOrEmpty($PSBoundParameters[$shouldBeRootedPathArgument])) { $assertPathArgumentRootedParams = @{ PathArgumentName = $shouldBeRootedPathArgument @@ -270,7 +280,7 @@ function Set-TargetResource foreach ($shouldExistPathArgument in $shouldExistPathArguments) { - if (-not [String]::IsNullOrEmpty($PSBoundParameters[$shouldExistPathArgument])) + if (-not [System.String]::IsNullOrEmpty($PSBoundParameters[$shouldExistPathArgument])) { $assertPathArgumentValidParams = @{ PathArgumentName = $shouldExistPathArgument @@ -299,13 +309,13 @@ function Set-TargetResource $parameterKey = $startProcessOptionalArgumentMap[$startProcessOptionalArgumentName] $parameterValue = $PSBoundParameters[$parameterKey] - if (-not [String]::IsNullOrEmpty($parameterValue)) + if (-not [System.String]::IsNullOrEmpty($parameterValue)) { $startProcessArguments[$startProcessOptionalArgumentName] = $parameterValue } } - if (-not [String]::IsNullOrEmpty($Arguments)) + if (-not [System.String]::IsNullOrEmpty($Arguments)) { $startProcessArguments['ArgumentList'] = $Arguments } @@ -325,7 +335,7 @@ function Set-TargetResource if (($PSBoundParameters.ContainsKey('Credential')) -and (Test-IsRunFromLocalSystemUser)) { # Throw an exception if any of the below parameters are included with Credential passed - foreach ($key in @('StandardOutputPath','StandardInputPath','WorkingDirectory')) + foreach ($key in @('StandardOutputPath', 'StandardInputPath', 'WorkingDirectory')) { if ($PSBoundParameters.Keys -contains $key) { @@ -336,6 +346,7 @@ function Set-TargetResource New-InvalidArgumentException @newInvalidArgumentExceptionParams } } + try { Start-ProcessAsLocalSystemUser -Path $Path -Arguments $Arguments -Credential $Credential @@ -356,7 +367,7 @@ function Set-TargetResource catch [System.Exception] { $errorMessage = ($script:localizedData.ErrorStarting -f $Path, $_.Exception.Message) - + New-InvalidOperationException -Message $errorMessage } } @@ -420,39 +431,45 @@ function Set-TargetResource #> function Test-TargetResource { - [OutputType([Boolean])] + [OutputType([System.Boolean])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path, [Parameter(Mandatory = $true)] [AllowEmptyString()] - [String] + [System.String] $Arguments, + [Parameter()] [ValidateNotNullOrEmpty()] [PSCredential] [System.Management.Automation.Credential()] $Credential, + [Parameter()] [ValidateSet('Present', 'Absent')] - [String] + [System.String] $Ensure = 'Present', - [String] + [Parameter()] + [System.String] $StandardOutputPath, - [String] + [Parameter()] + [System.String] $StandardErrorPath, - [String] + [Parameter()] + [System.String] $StandardInputPath, - [String] + [Parameter()] + [System.String] $WorkingDirectory ) @@ -496,13 +513,13 @@ function Test-TargetResource #> function Expand-Path { - [OutputType([String])] + [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path ) @@ -521,11 +538,12 @@ function Expand-Path # Check to see if the path to the file exists in the current location. If so, return the full rooted path. $rootedPath = [System.IO.Path]::GetFullPath($Path) + if ([System.IO.File]::Exists($rootedPath)) { return $rootedPath } - + # If the path is not found, throw an exception New-InvalidArgumentException -ArgumentName 'Path' -Message ($script:localizedData.FileNotFound -f $Path) } @@ -551,25 +569,28 @@ function Expand-Path #> function Get-ProcessCimInstance { - [OutputType([CimInstance[]])] + [OutputType([Microsoft.Management.Infrastructure.CimInstance[]])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path, - [String] + [Parameter()] + [System.String] $Arguments, + [Parameter()] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential, - [ValidateRange(0, [Int]::MaxValue)] - [Int] + [Parameter()] + [ValidateRange(0, [System.Int32]::MaxValue)] + [System.Int32] $UseGetCimInstanceThreshold = 8 ) @@ -581,7 +602,6 @@ function Get-ProcessCimInstance if ($getProcessResult.Count -ge $UseGetCimInstanceThreshold) { - $escapedPathForWqlFilter = ConvertTo-EscapedStringForWqlFilter -FilterString $Path $wqlFilter = "ExecutablePath = '$escapedPathForWqlFilter'" @@ -623,7 +643,7 @@ function Get-ProcessCimInstance if ($null -eq $Arguments) { - $Arguments = [String]::Empty + $Arguments = [System.String]::Empty } $processesWithMatchingArguments = @() @@ -649,17 +669,17 @@ function Get-ProcessCimInstance #> function ConvertTo-EscapedStringForWqlFilter { - [OutputType([String])] + [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $FilterString ) - return $FilterString.Replace("\","\\").Replace('"','\"').Replace("'","\'") + return $FilterString.Replace("\", "\\").Replace('"', '\"').Replace("'", "\'") } <# @@ -675,13 +695,13 @@ function ConvertTo-EscapedStringForWqlFilter #> function Get-ProcessOwner { - [OutputType([String])] + [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNull()] - [Object] + [System.Object] $Process ) @@ -700,7 +720,7 @@ function Get-ProcessOwner } } - return '' + return [System.String]::Empty } <# @@ -716,13 +736,13 @@ function Get-ProcessOwner #> function Get-ProcessOwnerCimInstance { - [OutputType([CimInstance])] + [OutputType([Microsoft.Management.Infrastructure.CimInstance])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNull()] - [Object] + [System.Object] $Process ) @@ -742,17 +762,18 @@ function Get-ProcessOwnerCimInstance #> function Get-ArgumentsFromCommandLineInput { - [OutputType([String])] + [OutputType([System.String])] [CmdletBinding()] param ( - [String] + [Parameter()] + [System.String] $CommandLineInput ) - if ([String]::IsNullOrWhitespace($CommandLineInput)) + if ([System.String]::IsNullOrWhitespace($CommandLineInput)) { - return [String]::Empty + return [System.String]::Empty } $CommandLineInput = $CommandLineInput.Trim() @@ -770,7 +791,7 @@ function Get-ArgumentsFromCommandLineInput if ($endofCommandIndex -eq -1) { - return [String]::Empty + return [System.String]::Empty } return $CommandLineInput.Substring($endofCommandIndex + 1).Trim() @@ -792,11 +813,11 @@ function Assert-HashtableDoesNotContainKey param ( [Parameter(Mandatory = $true)] - [Hashtable] + [System.Collections.Hashtable] $Hashtable, [Parameter(Mandatory = $true)] - [String[]] + [System.String[]] $Key ) @@ -828,30 +849,31 @@ function Assert-HashtableDoesNotContainKey #> function Wait-ProcessCount { - [OutputType([Boolean])] + [OutputType([System.Boolean])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [Hashtable] + [System.Collections.Hashtable] $ProcessSettings, [Parameter(Mandatory = $true)] - [ValidateRange(0, [Int]::MaxValue)] - [Int] + [ValidateRange(0, [System.Int32]::MaxValue)] + [System.Int32] $ProcessCount, - [Int] + [Parameter()] + [System.Int32] $WaitTime = 200000 ) - $startTime = [DateTime]::Now + $startTime = [System.DateTime]::Now do { $actualProcessCount = @( Get-ProcessCimInstance @ProcessSettings ).Count - } while ($actualProcessCount -ne $ProcessCount -and ([DateTime]::Now - $startTime).TotalMilliseconds -lt $WaitTime) + } while ($actualProcessCount -ne $ProcessCount -and ([System.DateTime]::Now - $startTime).TotalMilliseconds -lt $WaitTime) return $actualProcessCount -eq $ProcessCount } @@ -873,12 +895,12 @@ function Assert-PathArgumentRooted ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $PathArgumentName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $PathArgument ) @@ -908,19 +930,19 @@ function Assert-PathArgumentValid ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $PathArgumentName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $PathArgument ) if (-not (Test-Path -Path $PathArgument)) { $message = $script:localizedData.PathShouldExist -f $PathArgument, $PathArgumentName - + New-InvalidArgumentException -ArgumentName 'Path' ` -Message $message } @@ -932,9 +954,11 @@ function Assert-PathArgumentValid #> function Test-IsRunFromLocalSystemUser { - [OutputType([Boolean])] + [OutputType([System.Boolean])] [CmdletBinding()] - param () + param + ( + ) $identity = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = New-Object -TypeName Security.Principal.WindowsPrincipal -ArgumentList $identity @@ -958,16 +982,16 @@ function Test-IsRunFromLocalSystemUser function Start-ProcessAsLocalSystemUser { [CmdletBinding()] - param + param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] + [System.String] $Path, [Parameter(Mandatory = $true)] [AllowEmptyString()] - [String] + [System.String] $Arguments, [Parameter(Mandatory = $true)] @@ -989,7 +1013,7 @@ function Start-ProcessAsLocalSystemUser [PSDesiredStateConfiguration.NativeMethods]::CreateProcessAsUser( "$Path $Arguments", $splitCredentialResult.Domain, $splitCredentialResult.UserName, $Credential.Password, - $false, [Ref]$null ) + $false, [Ref] $null ) } <# @@ -1005,7 +1029,7 @@ function Start-ProcessAsLocalSystemUser #> function Split-Credential { - [OutputType([Hashtable])] + [OutputType([System.Collections.Hashtable])] [CmdletBinding()] param ( @@ -1050,7 +1074,7 @@ function Split-Credential } else { - # support for default domain (localhost) + # Support for default domain (localhost) $domain = $env:computerName $userName = $Credential.UserName } @@ -1080,8 +1104,9 @@ function Split-Credential function Assert-PsDscContextNotRunAsUser { [CmdletBinding()] - param - () + param + ( + ) Set-StrictMode -Off @@ -1091,6 +1116,7 @@ function Assert-PsDscContextNotRunAsUser ArgumentName = 'PsDscRunAsCredential' Message = ($script:localizedData.ErrorRunAsCredentialParameterNotSupported -f $PsDscContext.RunAsUser) } + New-InvalidArgumentException @newInvalidArgumentExceptionParams } } @@ -1101,391 +1127,391 @@ function Assert-PsDscContextNotRunAsUser for a user from the local system. Currently Start-Process, which is the command used otherwise, cannot do this. #> -function Import-DscNativeMethods -{ -$dscNativeMethodsSource = @" - -using System; -using System.Collections.Generic; -using System.Text; -using System.Security; -using System.Runtime.InteropServices; -using System.Diagnostics; -using System.Security.Principal; -#if !CORECLR -using System.ComponentModel; -#endif -using System.IO; - -namespace PSDesiredStateConfiguration -{ -#if !CORECLR - [SuppressUnmanagedCodeSecurity] -#endif - public static class NativeMethods - { - //The following structs and enums are used by the various Win32 API's that are used in the code below - - [StructLayout(LayoutKind.Sequential)] - public struct STARTUPINFO - { - public Int32 cb; - public string lpReserved; - public string lpDesktop; - public string lpTitle; - public Int32 dwX; - public Int32 dwY; - public Int32 dwXSize; - public Int32 dwXCountChars; - public Int32 dwYCountChars; - public Int32 dwFillAttribute; - public Int32 dwFlags; - public Int16 wShowWindow; - public Int16 cbReserved2; - public IntPtr lpReserved2; - public IntPtr hStdInput; - public IntPtr hStdOutput; - public IntPtr hStdError; - } - - [StructLayout(LayoutKind.Sequential)] - public struct PROCESS_INFORMATION - { - public IntPtr hProcess; - public IntPtr hThread; - public Int32 dwProcessID; - public Int32 dwThreadID; - } - - [Flags] - public enum LogonType - { - LOGON32_LOGON_INTERACTIVE = 2, - LOGON32_LOGON_NETWORK = 3, - LOGON32_LOGON_BATCH = 4, - LOGON32_LOGON_SERVICE = 5, - LOGON32_LOGON_UNLOCK = 7, - LOGON32_LOGON_NETWORK_CLEARTEXT = 8, - LOGON32_LOGON_NEW_CREDENTIALS = 9 - } - - [Flags] - public enum LogonProvider - { - LOGON32_PROVIDER_DEFAULT = 0, - LOGON32_PROVIDER_WINNT35, - LOGON32_PROVIDER_WINNT40, - LOGON32_PROVIDER_WINNT50 - } - [StructLayout(LayoutKind.Sequential)] - public struct SECURITY_ATTRIBUTES - { - public Int32 Length; - public IntPtr lpSecurityDescriptor; - public bool bInheritHandle; - } - - public enum SECURITY_IMPERSONATION_LEVEL - { - SecurityAnonymous, - SecurityIdentification, - SecurityImpersonation, - SecurityDelegation - } - - public enum TOKEN_TYPE - { - TokenPrimary = 1, - TokenImpersonation - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - internal struct TokPriv1Luid - { - public int Count; - public long Luid; - public int Attr; - } - - public const int GENERIC_ALL_ACCESS = 0x10000000; - public const int CREATE_NO_WINDOW = 0x08000000; - internal const int SE_PRIVILEGE_ENABLED = 0x00000002; - internal const int TOKEN_QUERY = 0x00000008; - internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020; - internal const string SE_INCRASE_QUOTA = "SeIncreaseQuotaPrivilege"; - -#if CORECLR - [DllImport("api-ms-win-core-handle-l1-1-0.dll", -#else - [DllImport("kernel32.dll", -#endif - EntryPoint = "CloseHandle", SetLastError = true, - CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] - public static extern bool CloseHandle(IntPtr handle); - -#if CORECLR - [DllImport("api-ms-win-core-processthreads-l1-1-2.dll", -#else - [DllImport("advapi32.dll", -#endif - EntryPoint = "CreateProcessAsUser", SetLastError = true, - CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] - public static extern bool CreateProcessAsUser( - IntPtr hToken, - string lpApplicationName, - string lpCommandLine, - ref SECURITY_ATTRIBUTES lpProcessAttributes, - ref SECURITY_ATTRIBUTES lpThreadAttributes, - bool bInheritHandle, - Int32 dwCreationFlags, - IntPtr lpEnvrionment, - string lpCurrentDirectory, - ref STARTUPINFO lpStartupInfo, - ref PROCESS_INFORMATION lpProcessInformation - ); - -#if CORECLR - [DllImport("api-ms-win-security-base-l1-1-0.dll", EntryPoint = "DuplicateTokenEx")] -#else - [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")] -#endif - public static extern bool DuplicateTokenEx( - IntPtr hExistingToken, - Int32 dwDesiredAccess, - ref SECURITY_ATTRIBUTES lpThreadAttributes, - Int32 ImpersonationLevel, - Int32 dwTokenType, - ref IntPtr phNewToken - ); - -#if CORECLR - [DllImport("api-ms-win-security-logon-l1-1-1.dll", CharSet = CharSet.Unicode, SetLastError = true)] -#else - [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] -#endif - public static extern Boolean LogonUser( - String lpszUserName, - String lpszDomain, - IntPtr lpszPassword, - LogonType dwLogonType, - LogonProvider dwLogonProvider, - out IntPtr phToken - ); - -#if CORECLR - [DllImport("api-ms-win-security-base-l1-1-0.dll", ExactSpelling = true, SetLastError = true)] -#else - [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] -#endif - internal static extern bool AdjustTokenPrivileges( - IntPtr htok, - bool disall, - ref TokPriv1Luid newst, - int len, - IntPtr prev, - IntPtr relen - ); - -#if CORECLR - [DllImport("api-ms-win-downlevel-kernel32-l1-1-0.dll", ExactSpelling = true)] -#else - [DllImport("kernel32.dll", ExactSpelling = true)] -#endif - internal static extern IntPtr GetCurrentProcess(); - -#if CORECLR - [DllImport("api-ms-win-downlevel-advapi32-l1-1-1.dll", ExactSpelling = true, SetLastError = true)] -#else - [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] -#endif - internal static extern bool OpenProcessToken( - IntPtr h, - int acc, - ref IntPtr phtok - ); - -#if CORECLR - [DllImport("api-ms-win-downlevel-kernel32-l1-1-0.dll", ExactSpelling = true)] -#else - [DllImport("kernel32.dll", ExactSpelling = true)] -#endif - internal static extern int WaitForSingleObject( - IntPtr h, - int milliseconds - ); - -#if CORECLR - [DllImport("api-ms-win-downlevel-kernel32-l1-1-0.dll", ExactSpelling = true)] -#else - [DllImport("kernel32.dll", ExactSpelling = true)] -#endif - internal static extern bool GetExitCodeProcess( - IntPtr h, - out int exitcode - ); - -#if CORECLR - [DllImport("api-ms-win-downlevel-advapi32-l4-1-0.dll", SetLastError = true)] -#else - [DllImport("advapi32.dll", SetLastError = true)] -#endif - internal static extern bool LookupPrivilegeValue( - string host, - string name, - ref long pluid - ); - - internal static void ThrowException( - string message - ) - { -#if CORECLR - throw new Exception(message); -#else - throw new Win32Exception(message); -#endif - } - - public static void CreateProcessAsUser(string strCommand, string strDomain, string strName, SecureString secureStringPassword, bool waitForExit, ref int ExitCode) - { - var hToken = IntPtr.Zero; - var hDupedToken = IntPtr.Zero; - TokPriv1Luid tp; - var pi = new PROCESS_INFORMATION(); - var sa = new SECURITY_ATTRIBUTES(); - sa.Length = Marshal.SizeOf(sa); - Boolean bResult = false; - try - { - IntPtr unmanagedPassword = IntPtr.Zero; - try - { -#if CORECLR - unmanagedPassword = SecureStringMarshal.SecureStringToCoTaskMemUnicode(secureStringPassword); -#else - unmanagedPassword = Marshal.SecureStringToGlobalAllocUnicode(secureStringPassword); -#endif - bResult = LogonUser( - strName, - strDomain, - unmanagedPassword, - LogonType.LOGON32_LOGON_NETWORK_CLEARTEXT, - LogonProvider.LOGON32_PROVIDER_DEFAULT, - out hToken - ); - } - finally - { - Marshal.ZeroFreeGlobalAllocUnicode(unmanagedPassword); - } - if (!bResult) - { - ThrowException("$($script:localizedData.UserCouldNotBeLoggedError)" + Marshal.GetLastWin32Error().ToString()); - } - IntPtr hproc = GetCurrentProcess(); - IntPtr htok = IntPtr.Zero; - bResult = OpenProcessToken( - hproc, - TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, - ref htok - ); - if (!bResult) - { - ThrowException("$($script:localizedData.OpenProcessTokenError)" + Marshal.GetLastWin32Error().ToString()); - } - tp.Count = 1; - tp.Luid = 0; - tp.Attr = SE_PRIVILEGE_ENABLED; - bResult = LookupPrivilegeValue( - null, - SE_INCRASE_QUOTA, - ref tp.Luid - ); - if (!bResult) - { - ThrowException("$($script:localizedData.PrivilegeLookingUpError)" + Marshal.GetLastWin32Error().ToString()); - } - bResult = AdjustTokenPrivileges( - htok, - false, - ref tp, - 0, - IntPtr.Zero, - IntPtr.Zero - ); - if (!bResult) - { - ThrowException("$($script:localizedData.TokenElevationError)" + Marshal.GetLastWin32Error().ToString()); - } - - bResult = DuplicateTokenEx( - hToken, - GENERIC_ALL_ACCESS, - ref sa, - (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, - (int)TOKEN_TYPE.TokenPrimary, - ref hDupedToken - ); - if (!bResult) - { - ThrowException("$($script:localizedData.DuplicateTokenError)" + Marshal.GetLastWin32Error().ToString()); - } - var si = new STARTUPINFO(); - si.cb = Marshal.SizeOf(si); - si.lpDesktop = ""; - bResult = CreateProcessAsUser( - hDupedToken, - null, - strCommand, - ref sa, - ref sa, - false, - 0, - IntPtr.Zero, - null, - ref si, - ref pi - ); - if (!bResult) - { - ThrowException("$($script:localizedData.CouldNotCreateProcessError)" + Marshal.GetLastWin32Error().ToString()); - } - if (waitForExit) { - int status = WaitForSingleObject(pi.hProcess, -1); - if(status == -1) - { - ThrowException("$($script:localizedData.WaitFailedError)" + Marshal.GetLastWin32Error().ToString()); - } - - bResult = GetExitCodeProcess(pi.hProcess, out ExitCode); - if(!bResult) - { - ThrowException("$($script:localizedData.RetriveStatusError)" + Marshal.GetLastWin32Error().ToString()); - } - } - } - finally - { - if (pi.hThread != IntPtr.Zero) - { - CloseHandle(pi.hThread); - } - if (pi.hProcess != IntPtr.Zero) - { - CloseHandle(pi.hProcess); - } - if (hDupedToken != IntPtr.Zero) - { - CloseHandle(hDupedToken); - } - } - } - } -} - +function Import-DscNativeMethods +{ + $dscNativeMethodsSource = @" + +using System; +using System.Collections.Generic; +using System.Text; +using System.Security; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Security.Principal; +#if !CORECLR +using System.ComponentModel; +#endif +using System.IO; + +namespace PSDesiredStateConfiguration +{ +#if !CORECLR + [SuppressUnmanagedCodeSecurity] +#endif + public static class NativeMethods + { + //The following structs and enums are used by the various Win32 API's that are used in the code below + + [StructLayout(LayoutKind.Sequential)] + public struct STARTUPINFO + { + public Int32 cb; + public string lpReserved; + public string lpDesktop; + public string lpTitle; + public Int32 dwX; + public Int32 dwY; + public Int32 dwXSize; + public Int32 dwXCountChars; + public Int32 dwYCountChars; + public Int32 dwFillAttribute; + public Int32 dwFlags; + public Int16 wShowWindow; + public Int16 cbReserved2; + public IntPtr lpReserved2; + public IntPtr hStdInput; + public IntPtr hStdOutput; + public IntPtr hStdError; + } + + [StructLayout(LayoutKind.Sequential)] + public struct PROCESS_INFORMATION + { + public IntPtr hProcess; + public IntPtr hThread; + public Int32 dwProcessID; + public Int32 dwThreadID; + } + + [Flags] + public enum LogonType + { + LOGON32_LOGON_INTERACTIVE = 2, + LOGON32_LOGON_NETWORK = 3, + LOGON32_LOGON_BATCH = 4, + LOGON32_LOGON_SERVICE = 5, + LOGON32_LOGON_UNLOCK = 7, + LOGON32_LOGON_NETWORK_CLEARTEXT = 8, + LOGON32_LOGON_NEW_CREDENTIALS = 9 + } + + [Flags] + public enum LogonProvider + { + LOGON32_PROVIDER_DEFAULT = 0, + LOGON32_PROVIDER_WINNT35, + LOGON32_PROVIDER_WINNT40, + LOGON32_PROVIDER_WINNT50 + } + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_ATTRIBUTES + { + public Int32 Length; + public IntPtr lpSecurityDescriptor; + public bool bInheritHandle; + } + + public enum SECURITY_IMPERSONATION_LEVEL + { + SecurityAnonymous, + SecurityIdentification, + SecurityImpersonation, + SecurityDelegation + } + + public enum TOKEN_TYPE + { + TokenPrimary = 1, + TokenImpersonation + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct TokPriv1Luid + { + public int Count; + public long Luid; + public int Attr; + } + + public const int GENERIC_ALL_ACCESS = 0x10000000; + public const int CREATE_NO_WINDOW = 0x08000000; + internal const int SE_PRIVILEGE_ENABLED = 0x00000002; + internal const int TOKEN_QUERY = 0x00000008; + internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020; + internal const string SE_INCRASE_QUOTA = "SeIncreaseQuotaPrivilege"; + +#if CORECLR + [DllImport("api-ms-win-core-handle-l1-1-0.dll", +#else + [DllImport("kernel32.dll", +#endif + EntryPoint = "CloseHandle", SetLastError = true, + CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] + public static extern bool CloseHandle(IntPtr handle); + +#if CORECLR + [DllImport("api-ms-win-core-processthreads-l1-1-2.dll", +#else + [DllImport("advapi32.dll", +#endif + EntryPoint = "CreateProcessAsUser", SetLastError = true, + CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] + public static extern bool CreateProcessAsUser( + IntPtr hToken, + string lpApplicationName, + string lpCommandLine, + ref SECURITY_ATTRIBUTES lpProcessAttributes, + ref SECURITY_ATTRIBUTES lpThreadAttributes, + bool bInheritHandle, + Int32 dwCreationFlags, + IntPtr lpEnvrionment, + string lpCurrentDirectory, + ref STARTUPINFO lpStartupInfo, + ref PROCESS_INFORMATION lpProcessInformation + ); + +#if CORECLR + [DllImport("api-ms-win-security-base-l1-1-0.dll", EntryPoint = "DuplicateTokenEx")] +#else + [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")] +#endif + public static extern bool DuplicateTokenEx( + IntPtr hExistingToken, + Int32 dwDesiredAccess, + ref SECURITY_ATTRIBUTES lpThreadAttributes, + Int32 ImpersonationLevel, + Int32 dwTokenType, + ref IntPtr phNewToken + ); + +#if CORECLR + [DllImport("api-ms-win-security-logon-l1-1-1.dll", CharSet = CharSet.Unicode, SetLastError = true)] +#else + [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] +#endif + public static extern Boolean LogonUser( + String lpszUserName, + String lpszDomain, + IntPtr lpszPassword, + LogonType dwLogonType, + LogonProvider dwLogonProvider, + out IntPtr phToken + ); + +#if CORECLR + [DllImport("api-ms-win-security-base-l1-1-0.dll", ExactSpelling = true, SetLastError = true)] +#else + [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] +#endif + internal static extern bool AdjustTokenPrivileges( + IntPtr htok, + bool disall, + ref TokPriv1Luid newst, + int len, + IntPtr prev, + IntPtr relen + ); + +#if CORECLR + [DllImport("api-ms-win-downlevel-kernel32-l1-1-0.dll", ExactSpelling = true)] +#else + [DllImport("kernel32.dll", ExactSpelling = true)] +#endif + internal static extern IntPtr GetCurrentProcess(); + +#if CORECLR + [DllImport("api-ms-win-downlevel-advapi32-l1-1-1.dll", ExactSpelling = true, SetLastError = true)] +#else + [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] +#endif + internal static extern bool OpenProcessToken( + IntPtr h, + int acc, + ref IntPtr phtok + ); + +#if CORECLR + [DllImport("api-ms-win-downlevel-kernel32-l1-1-0.dll", ExactSpelling = true)] +#else + [DllImport("kernel32.dll", ExactSpelling = true)] +#endif + internal static extern int WaitForSingleObject( + IntPtr h, + int milliseconds + ); + +#if CORECLR + [DllImport("api-ms-win-downlevel-kernel32-l1-1-0.dll", ExactSpelling = true)] +#else + [DllImport("kernel32.dll", ExactSpelling = true)] +#endif + internal static extern bool GetExitCodeProcess( + IntPtr h, + out int exitcode + ); + +#if CORECLR + [DllImport("api-ms-win-downlevel-advapi32-l4-1-0.dll", SetLastError = true)] +#else + [DllImport("advapi32.dll", SetLastError = true)] +#endif + internal static extern bool LookupPrivilegeValue( + string host, + string name, + ref long pluid + ); + + internal static void ThrowException( + string message + ) + { +#if CORECLR + throw new Exception(message); +#else + throw new Win32Exception(message); +#endif + } + + public static void CreateProcessAsUser(string strCommand, string strDomain, string strName, SecureString secureStringPassword, bool waitForExit, ref int ExitCode) + { + var hToken = IntPtr.Zero; + var hDupedToken = IntPtr.Zero; + TokPriv1Luid tp; + var pi = new PROCESS_INFORMATION(); + var sa = new SECURITY_ATTRIBUTES(); + sa.Length = Marshal.SizeOf(sa); + Boolean bResult = false; + try + { + IntPtr unmanagedPassword = IntPtr.Zero; + try + { +#if CORECLR + unmanagedPassword = SecureStringMarshal.SecureStringToCoTaskMemUnicode(secureStringPassword); +#else + unmanagedPassword = Marshal.SecureStringToGlobalAllocUnicode(secureStringPassword); +#endif + bResult = LogonUser( + strName, + strDomain, + unmanagedPassword, + LogonType.LOGON32_LOGON_NETWORK_CLEARTEXT, + LogonProvider.LOGON32_PROVIDER_DEFAULT, + out hToken + ); + } + finally + { + Marshal.ZeroFreeGlobalAllocUnicode(unmanagedPassword); + } + if (!bResult) + { + ThrowException("$($script:localizedData.UserCouldNotBeLoggedError)" + Marshal.GetLastWin32Error().ToString()); + } + IntPtr hproc = GetCurrentProcess(); + IntPtr htok = IntPtr.Zero; + bResult = OpenProcessToken( + hproc, + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + ref htok + ); + if (!bResult) + { + ThrowException("$($script:localizedData.OpenProcessTokenError)" + Marshal.GetLastWin32Error().ToString()); + } + tp.Count = 1; + tp.Luid = 0; + tp.Attr = SE_PRIVILEGE_ENABLED; + bResult = LookupPrivilegeValue( + null, + SE_INCRASE_QUOTA, + ref tp.Luid + ); + if (!bResult) + { + ThrowException("$($script:localizedData.PrivilegeLookingUpError)" + Marshal.GetLastWin32Error().ToString()); + } + bResult = AdjustTokenPrivileges( + htok, + false, + ref tp, + 0, + IntPtr.Zero, + IntPtr.Zero + ); + if (!bResult) + { + ThrowException("$($script:localizedData.TokenElevationError)" + Marshal.GetLastWin32Error().ToString()); + } + + bResult = DuplicateTokenEx( + hToken, + GENERIC_ALL_ACCESS, + ref sa, + (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, + (int)TOKEN_TYPE.TokenPrimary, + ref hDupedToken + ); + if (!bResult) + { + ThrowException("$($script:localizedData.DuplicateTokenError)" + Marshal.GetLastWin32Error().ToString()); + } + var si = new STARTUPINFO(); + si.cb = Marshal.SizeOf(si); + si.lpDesktop = ""; + bResult = CreateProcessAsUser( + hDupedToken, + null, + strCommand, + ref sa, + ref sa, + false, + 0, + IntPtr.Zero, + null, + ref si, + ref pi + ); + if (!bResult) + { + ThrowException("$($script:localizedData.CouldNotCreateProcessError)" + Marshal.GetLastWin32Error().ToString()); + } + if (waitForExit) { + int status = WaitForSingleObject(pi.hProcess, -1); + if(status == -1) + { + ThrowException("$($script:localizedData.WaitFailedError)" + Marshal.GetLastWin32Error().ToString()); + } + + bResult = GetExitCodeProcess(pi.hProcess, out ExitCode); + if(!bResult) + { + ThrowException("$($script:localizedData.RetriveStatusError)" + Marshal.GetLastWin32Error().ToString()); + } + } + } + finally + { + if (pi.hThread != IntPtr.Zero) + { + CloseHandle(pi.hThread); + } + if (pi.hProcess != IntPtr.Zero) + { + CloseHandle(pi.hProcess); + } + if (hDupedToken != IntPtr.Zero) + { + CloseHandle(hDupedToken); + } + } + } + } +} + "@ # if not on Nano: Add-Type -TypeDefinition $dscNativeMethodsSource -ReferencedAssemblies 'System.ServiceProcess' -} +} Export-ModuleMember -Function *-TargetResource diff --git a/DSCResources/MSFT_xWindowsProcess/en-US/MSFT_xWindowsProcess.strings.psd1 b/DSCResources/MSFT_xWindowsProcess/en-US/MSFT_xWindowsProcess.strings.psd1 index 66d9234cc..89f29574a 100644 --- a/DSCResources/MSFT_xWindowsProcess/en-US/MSFT_xWindowsProcess.strings.psd1 +++ b/DSCResources/MSFT_xWindowsProcess/en-US/MSFT_xWindowsProcess.strings.psd1 @@ -1,8 +1,8 @@ # Localized resources for MSFT_xWindowsProcess ConvertFrom-StringData @' - CouldNotCreateProcessError = Could not create process. Error code: - DuplicateTokenError = Duplicate token. Error code: + CouldNotCreateProcessError = Could not create process. Error code: + DuplicateTokenError = Duplicate token. Error code: FileNotFound = File '{0}' not found in the environment path. ErrorInvalidUserName = Invalid username: {0}. Username cannot contain multiple '@' or multiple '\' ErrorParametersNotSupportedWithCredential = Can't specify StandardOutputPath, StandardInputPath or WorkingDirectory when trying to run a process under a local user. @@ -13,24 +13,24 @@ ConvertFrom-StringData @' FailureWaitingForProcessesToStop = Failed to wait for processes to stop. GetTargetResourceStartMessage = Begin executing Get functionality for the process {0}. GetTargetResourceEndMessage = End executing Get functionality for the process {0}. - OpenProcessTokenError = Error while opening process token. Error code: + OpenProcessTokenError = Error while opening process token. Error code: ParameterShouldNotBeSpecified = Parameter {0} should not be specified. PathShouldBeAbsolute = The path '{0}' should be absolute for argument '{1}'. PathShouldExist = The path '{0}' should exist for argument '{1}'. - PrivilegeLookingUpError = Error while looking up privilege. Error code: + PrivilegeLookingUpError = Error while looking up privilege. Error code: ProcessAlreadyStarted = Process matching path '{0}' found running. No action required. ProcessAlreadyStopped = Process matching path '{0}' not found running. No action required. ProcessesStarted = Processes matching path '{0}' started. ProcessesStopped = Processes matching path '{0}' with IDs '({1})' stopped. - RetriveStatusError = Failed to retrieve status. Error code: + RetriveStatusError = Failed to retrieve status. Error code: SetTargetResourceStartMessage = Begin executing Set functionality for the process {0}. SetTargetResourceEndMessage = End executing Set functionality for the process {0}. StartingProcessWhatif = Start-Process. StoppingProcessWhatIf = Stop-Process. TestTargetResourceStartMessage = Begin executing Test functionality for the process {0}. TestTargetResourceEndMessage = End executing Test functionality for the process {0}. - TokenElevationError = Error while getting token elevation. Error code: - UserCouldNotBeLoggedError = User could not be logged. Error code: - WaitFailedError = Failed while waiting for process. Error code: + TokenElevationError = Error while getting token elevation. Error code: + UserCouldNotBeLoggedError = User could not be logged. Error code: + WaitFailedError = Failed while waiting for process. Error code: VerboseInProcessHandle = In process handle {0}. '@ diff --git a/DSCResources/xFileUpload/xFileUpload.psd1 b/DSCResources/xFileUpload/xFileUpload.psd1 index 76adcf9b0..67f3fb837 100644 --- a/DSCResources/xFileUpload/xFileUpload.psd1 +++ b/DSCResources/xFileUpload/xFileUpload.psd1 @@ -1,89 +1,85 @@ @{ + # Script module or binary module file associated with this manifest. + RootModule = 'xFileUpload.schema.psm1' -# Script module or binary module file associated with this manifest. -RootModule = 'xFileUpload.schema.psm1' + # Version number of this module. + ModuleVersion = '1.0' -# Version number of this module. -ModuleVersion = '1.0' + # ID used to uniquely identify this module + GUID = '1fbfd112-4272-4fb8-b31c-fb5b417484bc' -# ID used to uniquely identify this module -GUID = '1fbfd112-4272-4fb8-b31c-fb5b417484bc' + # Author of this module + Author = 'Microsoft Corporation' -# Author of this module -Author = 'kkaczma' + # Company or vendor of this module + CompanyName = 'Microsoft Corporation' -# Company or vendor of this module -CompanyName = 'Unknown' + # Copyright statement for this module + Copyright = '(c) Microsoft Corporation. All rights reserved.' -# Copyright statement for this module -Copyright = '(c) 2014 Microsoft. All rights reserved.' + # Description of the functionality provided by this module + # Description = '' -# Description of the functionality provided by this module -# Description = '' + # Minimum version of the Windows PowerShell engine required by this module + # PowerShellVersion = '' -# Minimum version of the Windows PowerShell engine required by this module -# PowerShellVersion = '' + # Name of the Windows PowerShell host required by this module + # PowerShellHostName = '' -# 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 the Windows PowerShell host required by this module -# PowerShellHostVersion = '' + # Minimum version of Microsoft .NET Framework required by this module + # DotNetFrameworkVersion = '' -# Minimum version of Microsoft .NET Framework required by this module -# DotNetFrameworkVersion = '' + # Minimum version of the common language runtime (CLR) required by this module + # CLRVersion = '' -# Minimum version of the common language runtime (CLR) required by this module -# CLRVersion = '' + # Processor architecture (None, X86, Amd64) required by this module + # ProcessorArchitecture = '' -# 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 = @() -# 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 = @() -# 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 = @() -# 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 = @() -# Type files (.ps1xml) to be loaded when importing this module -# TypesToProcess = @() + # Format files (.ps1xml) to be loaded when importing this module + # FormatsToProcess = @() -# 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 = @() -# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess -# NestedModules = @() + # Functions to export from this module + FunctionsToExport = '*' -# Functions to export from this module -FunctionsToExport = '*' + # Cmdlets to export from this module + CmdletsToExport = '*' -# Cmdlets to export from this module -CmdletsToExport = '*' + # Variables to export from this module + VariablesToExport = '*' -# Variables to export from this module -VariablesToExport = '*' + # Aliases to export from this module + AliasesToExport = '*' -# Aliases to export from this module -AliasesToExport = '*' + # List of all modules packaged with this module + # ModuleList = @() -# List of all modules packaged with this module -# ModuleList = @() + # List of all files packaged with this module + # FileList = @() -# List of all files packaged with this module -# FileList = @() + # Private data to pass to the module specified in RootModule/ModuleToProcess + # PrivateData = '' -# 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 = '' + # 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/DSCResources/xFileUpload/xFileUpload.schema.psm1 b/DSCResources/xFileUpload/xFileUpload.schema.psm1 index 17c23d33c..149a865a6 100644 --- a/DSCResources/xFileUpload/xFileUpload.schema.psm1 +++ b/DSCResources/xFileUpload/xFileUpload.schema.psm1 @@ -1,142 +1,187 @@ -Configuration xFileUpload -{ - <# - .SYNOPSIS - Configuration uploads file or folder to the smb share +<# + .SYNOPSIS + DSC Composite Resource uploads file or folder to an SMB share. + .DESCRIPTION - .EXAMPLE - xFileUpload -destinationPath "\\machine\share" -sourcePath "C:\folder\file" -username "domain\user" -password "password" - .PARAMETER destinationPath - Upload destination (has to point to a share or it's existing subfolder) e.g. \\machinename\sharename\destinationfolder - .PARAMETER sourcePath - Upload source e.g. C:\folder\file.txt - .PARAMETER credential - Credentials to access share where file/folder should be uploaded - .PARAMETER certificateThumbprint - Thumbprint of the certificate which should be used for encryption/decryption - .NOTES - #> + This is a DSC Composite resource that can be used to upload + a file or folder into an SMB file share. The SMB file share + does not have to be currently mounted. It will be mounted + during the upload process using the optional Credential + and then dismounted after completion of the upload. + + .PARAMETER DestinationPath + The destination SMB share path to upload the file or folder to. + + .PARAMETER SourcePath + The source path of the file or folder to upload. + .PARAMETER Credential + Credentials to access the destination SMB share path where file + or folder should be uploaded. + + .PARAMETER certificateThumbprint + Thumbprint of the certificate which should be used for encryption/decryption. + + .EXAMPLE + $securePassword = ConvertTo-SecureString -String 'password' -AsPlainText -Force + $credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList 'domain\user', $securePassword + xFileUpload ` + -DestinationPath '\\machine\share\destinationfolder' ` + -SourcePath 'C:\folder\file.txt' ` + -Credential $credential +#> +Configuration xFileUpload +{ [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "")] - param ( - [parameter(Mandatory = $true)] + param + ( + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] - $destinationPath, - [parameter(Mandatory = $true)] + $DestinationPath, + + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String] - $sourcePath, + [String] + $SourcePath, + + [Parameter()] + [System.Management.Automation.Credential()] [PSCredential] - $credential, + $Credential, + + [Parameter()] [String] $certificateThumbprint ) $cacheLocation = "$env:ProgramData\Microsoft\Windows\PowerShell\Configuration\BuiltinProvCache\MSFT_xFileUpload" - - if ($credential) + + if ($Credential) { - $username = $credential.UserName + $username = $Credential.UserName # Encrypt password - $password = Invoke-Command -ScriptBlock ([ScriptBlock]::Create($getEncryptedPassword)) -ArgumentList $credential, $certificateThumbprint - + $password = Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($getEncryptedPassword)) ` + -ArgumentList $Credential, $CertificateThumbprint } - - Script FileUpload + + Script FileUpload { # Get script is not implemented cause reusing Script resource's schema does not make sense - GetScript = { - $returnValue = @{ - - } - - $returnValue + GetScript = { + return @{} }; - - SetScript = { + SetScript = { # Generating credential object if password and username are specified - $credential = $null + $Credential = $null + if (($using:password) -and ($using:username)) { # Validate that certificate thumbprint is specified - if(-not $using:certificateThumbprint) + if (-not $using:CertificateThumbprint) { - $errorMessage = "Certificate thumbprint has to be specified if credentials are present." - Invoke-Command -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) -ArgumentList "CertificateThumbprintIsRequired", $errorMessage, "InvalidData" + $errorMessage = 'Certificate thumbprint has to be specified if credentials are present.' + Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) ` + -ArgumentList 'CertificateThumbprintIsRequired', $errorMessage, 'InvalidData' } - Write-Debug "Username and password specified." + Write-Debug -Message 'Username and password specified.' # Decrypt password - $decryptedPassword = Invoke-Command -ScriptBlock ([ScriptBlock]::Create($using:getDecryptedPassword)) -ArgumentList $using:password, $using:certificateThumbprint - + $decryptedPassword = Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($using:getDecryptedPassword)) ` + -ArgumentList $using:password, $using:CertificateThumbprint + # Generate credential - $securePassword = ConvertTo-SecureString $decryptedPassword -AsPlainText -Force - $credential = New-Object System.Management.Automation.PSCredential ($using:username, $securePassword) + $securePassword = ConvertTo-SecureString -String $decryptedPassword -AsPlainText -Force + $Credential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ($using:username, $securePassword) } # Validate DestinationPath is UNC path - if (!($using:destinationPath -as [System.Uri]).isUnc) + if (-not ($using:DestinationPath -as [System.Uri]).isUnc) { - $errorMessage = "Destination path $using:destinationPath is not a valid UNC path." - Invoke-Command -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) -ArgumentList "DestinationPathIsNotUNCFailure", $errorMessage, "InvalidData" + $errorMessage = "Destination path $using:DestinationPath is not a valid UNC path." + Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) ` + -ArgumentList 'DestinationPathIsNotUNCFailure', $errorMessage, 'InvalidData' } # Verify source is localpath - if (!(($using:sourcePath -as [System.Uri]).Scheme -match "file")) + if (-not (($using:SourcePath -as [System.Uri]).Scheme -match 'file')) { - $errorMessage = "Source path $using:sourcePath has to be local path." - Invoke-Command -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) -ArgumentList "SourcePathIsNotLocalFailure", $errorMessage, "InvalidData" + $errorMessage = "Source path $using:SourcePath has to be local path." + Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) ` + -ArgumentList 'SourcePathIsNotLocalFailure', $errorMessage, 'InvalidData' } # Check whether source path is existing file or directory $sourcePathType = $null - if (!(Test-Path $using:sourcePath)) + + if (-not (Test-Path -Path $using:SourcePath)) { - $errorMessage = "Source path $using:sourcePath does not exist." - Invoke-Command -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) -ArgumentList "SourcePathDoesNotExistFailure", $errorMessage, "InvalidData" + $errorMessage = "Source path $using:SourcePath does not exist." + Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) ` + -ArgumentList 'SourcePathDoesNotExistFailure', $errorMessage, 'InvalidData' } else { - $item = Get-Item $using:sourcePath + $item = Get-Item -Path $using:SourcePath + switch ($item.GetType().Name) { - "FileInfo" { - $sourcePathType = "File" + 'FileInfo' + { + $sourcePathType = 'File' } - "DirectoryInfo" { - $sourcePathType = "Directory" + 'DirectoryInfo' + { + $sourcePathType = 'Directory' } } } - Write-Debug "SourcePath $using:sourcePath is of type: $sourcePathType" + + Write-Debug -Message "SourcePath $using:SourcePath is of type: $sourcePathType" $psDrive = $null - # Mount the drive only if credentials are specified and it's currently not accessible - if ($credential) + # Mount the drive only if Credentials are specified and it's currently not accessible + if ($Credential) { - if (Test-Path $using:destinationPath -ErrorAction Ignore) + if (Test-Path -Path $using:DestinationPath -ErrorAction Ignore) { - Write-Debug "Destination path $using:destinationPath is already accessible. No mount needed." + Write-Debug -Message "Destination path $using:DestinationPath is already accessible. No mount needed." } else { - $psDriveArgs = @{ Name = ([guid]::NewGuid()); PSProvider = "FileSystem"; Root = $using:destinationPath; Scope = "Private"; Credential = $credential } + $psDriveArgs = @{ + Name = ([guid]::NewGuid()) + PSProvider = 'FileSystem' + Root = $using:DestinationPath + Scope = 'Private' + Credential = $Credential + } + try { - Write-Debug "Create psdrive with destination path $using:destinationPath..." + Write-Debug -Message "Create psdrive with destination path $using:DestinationPath..." $psDrive = New-PSDrive @psDriveArgs -ErrorAction Stop } catch { - $errorMessage = "Cannot access destination path $using:destinationPath with given Credential" - Invoke-Command -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) -ArgumentList "DestinationPathNotAccessibleFailure", $errorMessage, "InvalidData" + $errorMessage = "Cannot access destination path $using:DestinationPath with given Credential" + Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) ` + -ArgumentList 'DestinationPathNotAccessibleFailure', $errorMessage, 'InvalidData' } } } @@ -145,159 +190,196 @@ Configuration xFileUpload { # Get expected destination path $expectedDestinationPath = $null - if (!(Test-Path $using:destinationPath)) + + if (-not (Test-Path -Path $using:DestinationPath)) { # DestinationPath has to exist - $errorMessage = "Invalid parameter values: DestinationPath doesn't exist, but has to be existing directory." - Throw-TerminatingError -errorMessage $errorMessage -errorCategory "InvalidData" -errorId "DestinationPathDoesNotExistFailure" + $errorMessage = 'Invalid parameter values: DestinationPath does not exist, but has to be existing directory.' + Throw-TerminatingError -ErrorMessage $errorMessage -ErrorCategory 'InvalidData' -ErrorId 'DestinationPathDoesNotExistFailure' } else { - $item = Get-Item $using:destinationPath + $item = Get-Item -Path $using:DestinationPath + switch ($item.GetType().Name) { - "FileInfo" { + 'FileInfo' + { # DestinationPath cannot be file - $errorMessage = "Invalid parameter values: DestinationPath is file, but has to be existing directory." - Invoke-Command -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) -ArgumentList "DestinationPathCannotBeFileFailure", $errorMessage, "InvalidData" + $errorMessage = 'Invalid parameter values: DestinationPath is file, but has to be existing directory.' + Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) ` + -ArgumentList 'DestinationPathCannotBeFileFailure', $errorMessage, 'InvalidData' } - "DirectoryInfo" { - $expectedDestinationPath = Join-Path $using:destinationPath (Split-Path $using:sourcePath -Leaf) + 'DirectoryInfo' + { + $expectedDestinationPath = Join-Path ` + -Path $using:DestinationPath ` + -ChildPath (Split-Path -Path $using:SourcePath -Leaf) } } - Write-Debug "ExpectedDestinationPath is $expectedDestinationPath" + + Write-Debug -Message "ExpectedDestinationPath is $expectedDestinationPath" } # Copy destination path try { - Write-Debug "Copying $using:sourcePath to $using:destinationPath" - Copy-Item -path $using:sourcePath -Destination $using:destinationPath -Recurse -Force -ErrorAction Stop + Write-Debug -Message "Copying $using:SourcePath to $using:DestinationPath" + Copy-Item -Path $using:SourcePath -Destination $using:DestinationPath -Recurse -Force -ErrorAction Stop } catch { - $errorMessage = "Couldn't copy source path $using:sourcePath to $using:destinationPath : $($_.Exception)" - Invoke-Command -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) -ArgumentList "CopyDirectoryOverFileFailure", $errorMessage, "InvalidData" + $errorMessage = "Could not copy source path $using:SourcePath to $using:DestinationPath : $($_.Exception)" + Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) ` + -ArgumentList 'CopyDirectoryOverFileFailure', $errorMessage, 'InvalidData' } # Verify whether expectedDestinationPath was created - if (!(Test-Path $expectedDestinationPath)) + if (-not (Test-Path -Path $expectedDestinationPath)) { - $errorMessage = "Destination path $using:destinationPath could not be created" - Invoke-Command -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) -ArgumentList "DestinationPathNotCreatedFailure", $errorMessage, "InvalidData" + $errorMessage = "Destination path $using:DestinationPath could not be created" + Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) ` + -ArgumentList 'DestinationPathNotCreatedFailure', $errorMessage, 'InvalidData' } # If expectedDestinationPath exists else { - Write-Verbose "$sourcePathType $expectedDestinationPath has been successfully created" + Write-Verbose -Message "$sourcePathType $expectedDestinationPath has been successfully created" # Update cache - $uploadedItem = Get-Item $expectedDestinationPath + $uploadedItem = Get-Item -Path $expectedDestinationPath $lastWriteTime = $uploadedItem.LastWriteTimeUtc $inputObject = @{} - $inputObject["LastWriteTimeUtc"] = $lastWriteTime - $key = [string]::Join("", @($using:destinationPath, $using:sourcePath, $expectedDestinationPath)).GetHashCode().ToString() + $inputObject['LastWriteTimeUtc'] = $lastWriteTime + $key = [string]::Join('', @($using:DestinationPath, $using:SourcePath, $expectedDestinationPath)).GetHashCode().ToString() $path = Join-Path $using:cacheLocation $key - if(-not (Test-Path $using:cacheLocation)) + + if (-not (Test-Path -Path $using:cacheLocation)) { - mkdir $using:cacheLocation | Out-Null + New-Item -Path $using:cacheLocation -ItemType Directory | Out-Null } - Write-Debug "Updating cache for DestinationPath = $using:destinationPath and SourcePath = $using:sourcePath. CacheKey = $key" + Write-Debug -Message "Updating cache for DestinationPath = $using:DestinationPath and SourcePath = $using:SourcePath. CacheKey = $key" Export-CliXml -Path $path -InputObject $inputObject -Force } } finally { # Remove PSDrive - if($psDrive) + if ($psDrive) { - Write-Debug "Removing PSDrive on root $($psDrive.Root)" - Remove-PSDrive $psDrive -Force + Write-Debug -Message "Removing PSDrive on root $($psDrive.Root)" + Remove-PSDrive -Name $psDrive -Force } } }; - - TestScript = { - + + TestScript = { # Generating credential object if password and username are specified - $credential = $null + $Credential = $null + if (($using:password) -and ($using:username)) { # Validate that certificate thumbprint is specified - if(-not $using:certificateThumbprint) + if (-not $using:CertificateThumbprint) { - $errorMessage = "Certificate thumbprint has to be specified if credentials are present." - Invoke-Command -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) -ArgumentList "CertificateThumbprintIsRequired", $errorMessage, "InvalidData" + $errorMessage = 'Certificate thumbprint has to be specified if credentials are present.' + Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) ` + -ArgumentList 'CertificateThumbprintIsRequired', $errorMessage, 'InvalidData' } - Write-Debug "Username and password specified. Generating credential" - + Write-Debug -Message 'Username and password specified. Generating credential' + # Decrypt password - $decryptedPassword = Invoke-Command -ScriptBlock ([ScriptBlock]::Create($using:getDecryptedPassword)) -ArgumentList $using:password, $using:certificateThumbprint + $decryptedPassword = Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($using:getDecryptedPassword)) ` + -ArgumentList $using:password, $using:CertificateThumbprint # Generate credential - $securePassword = ConvertTo-SecureString $decryptedPassword -AsPlainText -Force - $credential = New-Object System.Management.Automation.PSCredential ($using:username, $securePassword) + $securePassword = ConvertTo-SecureString -String $decryptedPassword -AsPlainText -Force + $Credential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList ($using:username, $securePassword) } else { - Write-Debug "No credentials specified" + Write-Debug -Message 'No credentials specified.' } # Validate DestinationPath is UNC path - if (!($using:destinationPath -as [System.Uri]).isUnc) + if (-not ($using:DestinationPath -as [System.Uri]).isUnc) { - $errorMessage = "Destination path $using:destinationPath is not a valid UNC path." - Invoke-Command -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) -ArgumentList "DestinationPathIsNotUNCFailure", $errorMessage, "InvalidData" + $errorMessage = "Destination path $using:DestinationPath is not a valid UNC path." + Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) ` + -ArgumentList 'DestinationPathIsNotUNCFailure', $errorMessage, 'InvalidData' } # Check whether source path is existing file or directory (needed for expectedDestinationPath) $sourcePathType = $null - if (!(Test-Path $using:sourcePath)) + if (-not (Test-Path -Path $using:SourcePath)) { - $errorMessage = "Source path $using:sourcePath does not exist." - Invoke-Command -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) -ArgumentList "SourcePathDoesNotExistFailure", $errorMessage, "InvalidData" + $errorMessage = "Source path $using:SourcePath does not exist." + Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) ` + -ArgumentList 'SourcePathDoesNotExistFailure', $errorMessage, 'InvalidData' } else { - $item = Get-Item $using:sourcePath + $item = Get-Item -Path $using:SourcePath + switch ($item.GetType().Name) { - "FileInfo" { - $sourcePathType = "File" + 'FileInfo' + { + $sourcePathType = 'File' } - "DirectoryInfo" { - $sourcePathType = "Directory" + 'DirectoryInfo' + { + $sourcePathType = 'Directory' } } } - Write-Debug "SourcePath $using:sourcePath is of type: $sourcePathType" + + Write-Debug -Message "SourcePath $using:SourcePath is of type: $sourcePathType" $psDrive = $null # Mount the drive only if credentials are specified and it's currently not accessible - if ($credential) + if ($Credential) { - if (Test-Path $using:destinationPath -ErrorAction Ignore) + if (Test-Path -Path $using:DestinationPath -ErrorAction Ignore) { - Write-Debug "Destination path $using:destinationPath is already accessible. No mount needed." + Write-Debug -Message "Destination path $using:DestinationPath is already accessible. No mount needed." } else { - $psDriveArgs = @{ Name = ([guid]::NewGuid()); PSProvider = "FileSystem"; Root = $using:destinationPath; Scope = "Private"; Credential = $credential } + $psDriveArgs = @{ + Name = ([guid]::NewGuid()) + PSProvider = 'FileSystem' + Root = $using:DestinationPath + Scope = 'Private' + Credential = $Credential + + } try { - Write-Debug "Create psdrive with destination path $using:destinationPath..." + Write-Debug -Message "Create psdrive with destination path $using:DestinationPath..." $psDrive = New-PSDrive @psDriveArgs -ErrorAction Stop } catch { - $errorMessage = "Cannot access destination path $using:destinationPath with given Credential" - Invoke-Command -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) -ArgumentList "DestinationPathNotAccessibleFailure", $errorMessage, "InvalidData" + $errorMessage = "Cannot access destination path $using:DestinationPath with given Credential" + Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) ` + -ArgumentList 'DestinationPathNotAccessibleFailure', $errorMessage, 'InvalidData' } } } @@ -306,119 +388,140 @@ Configuration xFileUpload { # Get expected destination path $expectedDestinationPath = $null - if (!(Test-Path $using:destinationPath)) + + if (-not (Test-Path -Path $using:DestinationPath)) { # DestinationPath has to exist - $errorMessage = "Invalid parameter values: DestinationPath doesn't exist or is not accessible. DestinationPath has to be existing directory." - Invoke-Command -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) -ArgumentList "DestinationPathDoesNotExistFailure", $errorMessage, "InvalidData" + $errorMessage = 'Invalid parameter values: DestinationPath does not exist or is not accessible. DestinationPath has to be existing directory.' + Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) ` + -ArgumentList 'DestinationPathDoesNotExistFailure', $errorMessage, 'InvalidData' } else { - $item = Get-Item $using:destinationPath + $item = Get-Item -Path $using:DestinationPath + switch ($item.GetType().Name) { - "FileInfo" { + 'FileInfo' + { # DestinationPath cannot be file - $errorMessage = "Invalid parameter values: DestinationPath is file, but has to be existing directory." - Invoke-Command -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) -ArgumentList "DestinationPathCannotBeFileFailure", $errorMessage, "InvalidData" + $errorMessage = 'Invalid parameter values: DestinationPath is file, but has to be existing directory.' + Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($using:throwTerminatingError)) ` + -ArgumentList 'DestinationPathCannotBeFileFailure', $errorMessage, 'InvalidData' } - "DirectoryInfo" { - $expectedDestinationPath = Join-Path $using:destinationPath (Split-Path $using:sourcePath -Leaf) + 'DirectoryInfo' + { + $expectedDestinationPath = Join-Path ` + -Path $using:DestinationPath ` + -ChildPath (Split-Path -Path $using:SourcePath -Leaf) } } - Write-Debug "ExpectedDestinationPath is $expectedDestinationPath" + + Write-Debug -Message "ExpectedDestinationPath is $expectedDestinationPath" } # Check whether ExpectedDestinationPath exists and has expected type $itemExists = $false - if (!(Test-Path $expectedDestinationPath)) + + if (-not (Test-Path $expectedDestinationPath)) { - Write-Debug "Expected destination path doesn't exist or is not accessible" + Write-Debug -Message 'Expected destination path does not exist or is not accessible.' } # If expectedDestinationPath exists else { - $expectedItem = Get-Item $expectedDestinationPath + $expectedItem = Get-Item -Path $expectedDestinationPath $expectedItemType = $expectedItem.GetType().Name - + # If expectedDestinationPath has same type as sourcePathType, we need to verify cache to determine whether no upload is needed - if ((($expectedItemType -eq "FileInfo") -and ($sourcePathType -eq "File")) -or (($expectedItemType -eq "DirectoryInfo") -and ($sourcePathType -eq "Directory"))) + if ((($expectedItemType -eq 'FileInfo') -and ($sourcePathType -eq 'File')) -or ` + (($expectedItemType -eq 'DirectoryInfo') -and ($sourcePathType -eq 'Directory'))) { # Get cache - Write-Debug "Getting cache for $expectedDestinationPath" + Write-Debug -Message "Getting cache for $expectedDestinationPath" $cacheContent = $null - $key = [string]::Join("", @($using:destinationPath, $using:sourcePath, $expectedDestinationPath)).GetHashCode().ToString() - $path = Join-Path $using:cacheLocation $key - Write-Debug "Looking for cache under $path" - if (!(Test-Path $path)) + $key = [string]::Join('', @($using:DestinationPath, $using:SourcePath, $expectedDestinationPath)).GetHashCode().ToString() + $path = Join-Path -Path $using:cacheLocation -ChildPath $key + Write-Debug -Message "Looking for cache under $path" + + if (-not (Test-Path -Path $path)) { - Write-Debug "No cache found for DestinationPath = $using:destinationPath and SourcePath = $using:sourcePath. CacheKey = $key" + Write-Debug -Message "No cache found for DestinationPath = $using:DestinationPath and SourcePath = $using:SourcePath. CacheKey = $key" } else { - $cacheContent = Import-CliXml $path - Write-Debug "Found cache for DestinationPath = $using:destinationPath and SourcePath = $using:sourcePath. CacheKey = $key" + $cacheContent = Import-CliXml -Path $path + Write-Debug -Message "Found cache for DestinationPath = $using:DestinationPath and SourcePath = $using:SourcePath. CacheKey = $key" } # Verify whether cache reflects current state or upload is needed if ($cacheContent -ne $null -and ($cacheContent.LastWriteTimeUtc -eq $expectedItem.LastWriteTimeUtc)) { - # No upload needed - Write-Debug "Cache reflects current state. No need for upload." + # No upload needed + Write-Debug -Message 'Cache reflects current state. No need for upload.' $itemExists = $true } else { - Write-Debug "Cache is empty or it doesn't reflect current state. Upload will be performed." - } + Write-Debug -Message 'Cache is empty or it does not reflect current state. Upload will be performed.' + } } else { - Write-Debug "Expected destination path: $expectedDestinationPath is of type $expectedItemType, although source path is $sourcePathType" + Write-Debug -Message "Expected destination path: $expectedDestinationPath is of type $expectedItemType, although source path is $sourcePathType" } } } finally { # Remove PSDrive - if($psDrive) + if ($psDrive) { - Write-Debug "Removing PSDrive on root $($psDrive.Root)" - Remove-PSDrive $psDrive -Force + Write-Debug -Message "Removing PSDrive on root $($psDrive.Root)" + Remove-PSDrive -Name $psDrive -Force } } return $itemExists - }; } } # Encrypts password using the defined public key $getEncryptedPassword = @' - param ( - [Parameter(Mandatory = $true)] - [PSCredential] $credential, - [Parameter(Mandatory = $true)] - [String] $certificateThumbprint - ) - - $value = $credential.GetNetworkCredential().Password - - $cert = Invoke-Command -ScriptBlock ([ScriptBlock]::Create($getCertificate)) -ArgumentList $certificateThumbprint - + param + ( + [Parameter(Mandatory = $true)] + [PSCredential] + $Credential, + + [Parameter(Mandatory = $true)] + [String] + $CertificateThumbprint + ) + + $value = $Credential.GetNetworkCredential().Password + + $cert = Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($getCertificate)) ` + -ArgumentList $CertificateThumbprint + $encryptedPassword = $null - if($cert) + if ($cert) { # Cast the public key correctly - $rsaProvider = [System.Security.Cryptography.RSACryptoServiceProvider]$cert.PublicKey.Key - + $rsaProvider = [System.Security.Cryptography.RSACryptoServiceProvider] $cert.PublicKey.Key + if($rsaProvider -eq $null) { - $errorMessage = "Could not get public key from certificate with thumbprint: $certificateThumbprint . Please verify certificate is valid for encryption." - Invoke-Command -ScriptBlock ([ScriptBlock]::Create($throwTerminatingError)) -ArgumentList "DecryptionCertificateNotFound", $errorMessage, "InvalidOperation" + $errorMessage = "Could not get public key from certificate with thumbprint: $CertificateThumbprint . Please verify certificate is valid for encryption." + Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($throwTerminatingError)) ` + -ArgumentList "DecryptionCertificateNotFound", $errorMessage, "InvalidOperation" } # Convert to a byte array @@ -436,8 +539,10 @@ $getEncryptedPassword = @' } else { - $errorMessage = "Could not find certificate which matches thumbprint: $certificateThumbprint . Could not encrypt password" - Invoke-Command -ScriptBlock ([ScriptBlock]::Create($throwTerminatingError)) -ArgumentList "EncryptionCertificateNot", $errorMessage, "InvalidOperation" + $errorMessage = "Could not find certificate which matches thumbprint: $CertificateThumbprint . Could not encrypt password" + Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($throwTerminatingError)) ` + -ArgumentList "EncryptionCertificateNot", $errorMessage, "InvalidOperation" } return $encryptedPassword @@ -445,16 +550,18 @@ $getEncryptedPassword = @' # Retrieves certificate by thumbprint $getCertificate = @' - param( + param + ( [Parameter(Mandatory = $true)] - [string] $certificateThumbprint + [string] + $CertificateThumbprint ) $cert = $null - foreach($certIndex in Get-Childitem cert:\LocalMachine\My) + foreach ($certIndex in (Get-Childitem -Path Cert:\LocalMachine\My)) { - if($certIndex.Thumbprint -match $certificateThumbprint) + if ($certIndex.Thumbprint -match $CertificateThumbprint) { $cert = $certIndex break @@ -462,9 +569,11 @@ $getCertificate = @' } if(-not $cert) - { - $errorMessage = "Error Reading certificate store for {0}. Please verify thumbprint is correct and certificate belongs to cert:\LocalMachine\My store." -f ${certificateThumbprint}; - Invoke-Command -ScriptBlock ([ScriptBlock]::Create($throwTerminatingError)) -ArgumentList "InvalidPathSpecified", $errorMessage, "InvalidOperation" + { + $errorMessage = "Error Reading certificate store for {0}. Please verify thumbprint is correct and certificate belongs to cert:\LocalMachine\My store." -f ${CertificateThumbprint}; + Invoke-Command ` + -ScriptBlock ([ScriptBlock]::Create($throwTerminatingError)) ` + -ArgumentList "InvalidPathSpecified", $errorMessage, "InvalidOperation" } else { @@ -474,60 +583,67 @@ $getCertificate = @' # Throws terminating error specified errorCategory, errorId and errorMessage $throwTerminatingError = @' - param( - [parameter(Mandatory = $true)] - [System.String] - $errorId, - [parameter(Mandatory = $true)] + param + ( + [Parameter(Mandatory = $true)] [System.String] - $errorMessage, - [parameter(Mandatory = $true)] - $errorCategory + $ErrorId, + + [Parameter(Mandatory = $true)] + [System.String] + $ErrorMessage, + + [Parameter(Mandatory = $true)] + $ErrorCategory ) - $exception = New-Object System.InvalidOperationException $errorMessage - $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $null + $exception = New-Object -TypeName System.InvalidOperationException -ArgumentList $ErrorMessage + $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList ($exception, $ErrorId, $ErrorCategory, $null) throw $errorRecord '@ # Decrypts password using the defined private key $getDecryptedPassword = @' - param ( - [Parameter(Mandatory = $true)] - [String] $value, - [Parameter(Mandatory = $true)] - [String] $certificateThumbprint - ) + param + ( + [Parameter(Mandatory = $true)] + [String] + $Value, + + [Parameter(Mandatory = $true)] + [String] + $CertificateThumbprint + ) $cert = $null - foreach($certIndex in Get-Childitem cert:\LocalMachine\My) + foreach ($certIndex in (Get-Childitem -Path Cert:\LocalMachine\My)) { - if($certIndex.Thumbprint -match $certificateThumbprint) + if ($certIndex.Thumbprint -match $CertificateThumbprint) { $cert = $certIndex break } } - if(-not $cert) - { - $errorMessage = "Error Reading certificate store for {0}. Please verify thumbprint is correct and certificate belongs to cert:\LocalMachine\My store." -f ${certificateThumbprint}; - $exception = New-Object System.InvalidOperationException $errorMessage - $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, "InvalidPathSpecified", "InvalidOperation", $null + if (-not $cert) + { + $errorMessage = "Error Reading certificate store for {0}. Please verify thumbprint is correct and certificate belongs to cert:\LocalMachine\My store." -f ${CertificateThumbprint}; + $exception = New-Object -TypeName System.InvalidOperationException -ArgumentList $errorMessage + $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList ($exception, "InvalidPathSpecified", "InvalidOperation", $null) throw $errorRecord } $decryptedPassword = $null # Get RSA provider - $rsaProvider = [System.Security.Cryptography.RSACryptoServiceProvider]$cert.PrivateKey + $rsaProvider = [System.Security.Cryptography.RSACryptoServiceProvider] $cert.PrivateKey if($rsaProvider -eq $null) { - $errorMessage = "Could not get private key from certificate with thumbprint: $certificateThumbprint . Please verify certificate is valid for decryption." - $exception = New-Object System.InvalidOperationException $errorMessage - $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, "DecryptionCertificateNotFound", "InvalidOperation", $null + $errorMessage = "Could not get private key from certificate with thumbprint: $CertificateThumbprint . Please verify certificate is valid for decryption." + $exception = New-Object -TypeName System.InvalidOperationException -ArgumentList $errorMessage + $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList ($exception, "DecryptionCertificateNotFound", "InvalidOperation", $null) throw $errorRecord } @@ -536,7 +652,7 @@ $getDecryptedPassword = @' # Decrypt bytes $decryptedBytes = $rsaProvider.Decrypt($encBytes, $false) - + # Convert to string $decryptedPassword = [System.Text.Encoding]::Unicode.GetString($decryptedBytes) diff --git a/Examples/Sample_xDscWebServiceRegistration.ps1 b/Examples/Sample_xDscWebServiceRegistration.ps1 index 1e3ef9905..c33d4bea6 100644 --- a/Examples/Sample_xDscWebServiceRegistration.ps1 +++ b/Examples/Sample_xDscWebServiceRegistration.ps1 @@ -1,32 +1,55 @@ -# DSC configuration for Pull Server using registration - -# The Sample_xDscWebServiceRegistration configuration sets up a DSC pull server that is capable for client nodes -# to register with it and retrieve configuration documents with configuration names instead of configuration id - -# Prerequisite: Install a certificate in "CERT:\LocalMachine\MY\" store -# For testing environments, you could use a self-signed certificate. (New-SelfSignedCertificate cmdlet could generate one for you). -# For production environments, you will need a certificate signed by valid CA. -# Registration only works over https protocols. So to use registration feature, a secure pull server setup with certificate is necessary - - -# The Sample_MetaConfigurationToRegisterWithLessSecurePullServer register a DSC client node with the pull server - -# =================================== Section Pull Server =================================== # -configuration Sample_xDscWebServiceRegistration +<# + .SYNOPSIS + The Sample_xDscWebServiceRegistration configuration sets up a DSC pull + server that is capable for client nodes to register with it and + retrieve configuration documents with configuration names instead of + configuration id. + + Prerequisite: Install a certificate in 'CERT:\LocalMachine\MY\' store + For testing environments, you could use a self-signed + certificate. (New-SelfSignedCertificate cmdlet could + generate one for you). For production environments, you + will need a certificate signed by valid CA. Registration + only works over https protocols. So to use registration + feature, a secure pull server setup with certificate is + necessary. + + .PARAMETER NodeName + The name of the node being configured as a DSC Pull Server. + + .PARAMETER CertificateThumbPrint + Certificate thumbprint for creating an HTTPS endpoint. Use + "AllowUnencryptedTraffic" for setting up a non SSL based endpoint. + + .PARAMETER RegistrationKey + This key will be used by client nodes as a shared key to authenticate + during registration. This should be a string with enough entropy + (randomness) to protect the registration of clients to the pull server. + The example creates a new GUID for the registration key. + + .EXAMPLE + $thumbprint = (New-SelfSignedCertificate -DnsName $env:COMPUTERNAME -CertStoreLocation Cert:\LocalMachine\My).Thumbprint + $registrationKey = [System.Guid]::NewGuid() + + Sample_xDscWebServiceRegistration -RegistrationKey $registrationkey -CertificateThumbPrint $thumbprint +#> +Configuration Sample_xDscWebServiceRegistration { param ( - [string[]] + [Parameter()] + [System.String[]] $NodeName = 'localhost', + [Parameter()] [ValidateNotNullOrEmpty()] - [string] - $certificateThumbPrint, + [System.String] + $CertificateThumbPrint, - [Parameter(HelpMessage = 'This should be a string with enough entropy (randomness) to protect the registration of clients to the pull server. We will use new GUID by default.')] + [Parameter()] [ValidateNotNullOrEmpty()] - [string] - $RegistrationKey # A guid that clients use to initiate conversation with pull server + [System.String] + $RegistrationKey ) Import-DSCResource -ModuleName xPSDesiredStateConfiguration @@ -35,21 +58,21 @@ configuration Sample_xDscWebServiceRegistration { WindowsFeature DSCServiceFeature { - Ensure = "Present" - Name = "DSC-Service" + Ensure = 'Present' + Name = 'DSC-Service' } xDscWebService PSDSCPullServer { - Ensure = "Present" - EndpointName = "PSDSCPullServer" + Ensure = 'Present' + EndpointName = 'PSDSCPullServer' Port = 8080 PhysicalPath = "$env:SystemDrive\inetpub\PSDSCPullServer" - CertificateThumbPrint = $certificateThumbPrint + CertificateThumbPrint = $CertificateThumbPrint ModulePath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules" ConfigurationPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration" - State = "Started" - DependsOn = "[WindowsFeature]DSCServiceFeature" + State = 'Started' + DependsOn = '[WindowsFeature]DSCServiceFeature' RegistrationKeyPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService" AcceptSelfSignedCertificates = $true Enable32BitAppOnWin64 = $false @@ -66,30 +89,48 @@ configuration Sample_xDscWebServiceRegistration } } -# Sample use (please change values of parameters according to your scenario): -# $thumbprint = (New-SelfSignedCertificate -Subject "TestPullServer").Thumbprint -# $registrationkey = [guid]::NewGuid() -# Sample_xDscWebServiceRegistration -RegistrationKey $registrationkey -certificateThumbPrint $thumbprint +<# + .SYNOPSIS + The Sample_MetaConfigurationToRegisterWithSecurePullServer registers + a DSC client node with the pull server. -# =================================== Section Pull Server =================================== # + .PARAMETER NodeName + The name of the node being configured as a DSC Pull Server. -# =================================== Section DSC Client =================================== # + .PARAMETER RegistrationKey + This key will be used by client nodes as a shared key to authenticate + during registration. This should be a string with enough entropy + (randomness) to protect the registration of clients to the pull server. + The example creates a new GUID for the registration key. + + .PARAMETER ServerName + The HostName to use when configuring the Pull Server URL on the DSC + client. + + .EXAMPLE + $registrationKey = [System.Guid]::NewGuid() + + Sample_MetaConfigurationToRegisterWithSecurePullServer -RegistrationKey $registrationKey +#> [DSCLocalConfigurationManager()] -configuration Sample_MetaConfigurationToRegisterWithLessSecurePullServer +Configuration Sample_MetaConfigurationToRegisterWithSecurePullServer { param ( + [Parameter()] [ValidateNotNullOrEmpty()] - [string] + [System.String] $NodeName = 'localhost', + [Parameter()] [ValidateNotNullOrEmpty()] - [string] - $RegistrationKey, #same as the one used to setup pull server in previous configuration + [System.String] + $RegistrationKey, + [Parameter()] [ValidateNotNullOrEmpty()] - [string] - $ServerName = 'localhost' #node name of the pull server, same as $NodeName used in previous configuration + [System.String] + $ServerName = 'localhost' ) Node $NodeName @@ -101,20 +142,15 @@ configuration Sample_MetaConfigurationToRegisterWithLessSecurePullServer ConfigurationRepositoryWeb CONTOSO-PullSrv { - ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" # notice it is https + ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" RegistrationKey = $RegistrationKey ConfigurationNames = @('ClientConfig') } ReportServerWeb CONTOSO-PullSrv { - ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" # notice it is https + ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" RegistrationKey = $RegistrationKey } } } - -# Sample use (please change values of parameters according to your scenario): -# Sample_MetaConfigurationToRegisterWithLessSecurePullServer -RegistrationKey $registrationkey - -# =================================== Section DSC Client =================================== # diff --git a/Examples/Sample_xDscWebServiceRegistrationWithSecurityBestPractices.ps1 b/Examples/Sample_xDscWebServiceRegistrationWithSecurityBestPractices.ps1 index a6b86e799..232e7d432 100644 --- a/Examples/Sample_xDscWebServiceRegistrationWithSecurityBestPractices.ps1 +++ b/Examples/Sample_xDscWebServiceRegistrationWithSecurityBestPractices.ps1 @@ -1,79 +1,81 @@ -# DSC configuration for Pull Server using registration with enhanced security settings - -# The Sample_xDscWebServiceRegistrationWithEnhancedSecurity configuration sets up a DSC pull server that is capable for client nodes -# to register with it and retrieve configuration documents with configuration names instead of configuration id - -# Prerequisite: Install a certificate in "CERT:\LocalMachine\MY\" store -# For testing environments, you could use a self-signed certificate. (New-SelfSignedCertificate cmdlet could generate one for you). -# For production environments, you will need a certificate signed by valid CA. -# Registration only works over https protocols. So to use registration feature, a secure pull server setup with certificate is necessary - -# The Sample_MetaConfigurationToRegisterWithSecurePullServer register a DSC client node with the pull server - -# ======================================== Arguments ======================================== # <# - Check if OS major version is higher or equal to 10. - - Note: This check is to pass example validation CI tests, - it has not been tested to run on Windows Server 2012 R2, - please see the following example for a Windows Server 2012 R2 version - of this example; - https://github.com/PowerShell/xPSDesiredStateConfiguration/blob/master/Examples/Sample_xDscWebServiceRegistration_Win2k12and2k12R2.ps1. + .SYNOPSIS + The Sample_xDscWebServiceRegistrationWithEnhancedSecurity configuration + sets up a DSC pull server that is capable for client nodes to register + with it and retrieve configuration documents with configuration names + instead of configuration id. + + Prerequisite: 1 - Install a certificate in 'CERT:\LocalMachine\MY\' + store. For testing environments, you could use a + self-signed certificate. (New-SelfSignedCertificate + cmdlet could generate one for you). For production + environments, you will need a certificate signed by + valid CA. Registration only works over https + protocols. So to use registration feature, a secure + pull server setup with certificate is necessary. + 2 - Install and Configure SQL Server + + .PARAMETER NodeName + The name of the node being configured as a DSC Pull Server. + + .PARAMETER CertificateThumbPrint + Certificate thumbprint for creating an HTTPS endpoint. Use + "AllowUnencryptedTraffic" for setting up a non SSL based endpoint. + + .PARAMETER RegistrationKey + This key will be used by client nodes as a shared key to authenticate + during registration. This should be a string with enough entropy + (randomness) to protect the registration of clients to the pull server. + The example creates a new GUID for the registration key. + + .EXAMPLE + $thumbprint = (New-SelfSignedCertificate -DnsName $env:COMPUTERNAME -CertStoreLocation Cert:\LocalMachine\My).Thumbprint + $registrationKey = [System.Guid]::NewGuid() + + Sample_xDscWebServiceRegistrationWithSecurityBestPractices -RegistrationKey $registrationKey -certificateThumbPrint $thumbprint #> -if ([Environment]::OSVersion.Version.Major -ge '10') -{ - $thumbprint = (New-SelfSignedCertificate -Subject $env:COMPUTERNAME).Thumbprint -} -else -{ - Write-Warning -Message 'Running on operating system older than major version 10, this configuration is not meant to run on OS with a major version older than version 10. Generating certificate using New-SelfSignedCertificate with an alternate method.' - $thumbprint = (New-SelfSignedCertificate -DnsName $env:COMPUTERNAME -CertStoreLocation cert:\LocalMachine\My ).Thumbprint -} - -$registrationKey = [guid]::NewGuid() -# ======================================== Arguments ======================================== # - -# =================================== Section DSC Client =================================== # -configuration Sample_xDscWebServiceRegistrationWithSecurityBestPractices +Configuration Sample_xDscWebServiceRegistrationWithSecurityBestPractices { param ( - [string[]] + [Parameter()] + [System.String[]] $NodeName = 'localhost', + [Parameter()] [ValidateNotNullOrEmpty()] - [string] - $certificateThumbPrint, + [System.String] + $CertificateThumbPrint, - [Parameter(HelpMessage = 'This should be a string with enough entropy (randomness) to protect the registration of clients to the pull server. We will use new GUID by default.')] + [Parameter()] [ValidateNotNullOrEmpty()] - [string] - $RegistrationKey # A guid that clients use to initiate conversation with pull server + [System.String] + $RegistrationKey ) Import-DSCResource -ModuleName xPSDesiredStateConfiguration - # To explicitly import the resource WindowsFesture and File. + # To explicitly import the resource WindowsFeature and File. Import-DscResource -ModuleName PSDesiredStateConfiguration Node $NodeName { WindowsFeature DSCServiceFeature { - Ensure = "Present" - Name = "DSC-Service" + Ensure = 'Present' + Name = 'DSC-Service' } xDscWebService PSDSCPullServer { - Ensure = "Present" - EndpointName = "PSDSCPullServer" + Ensure = 'Present' + EndpointName = 'PSDSCPullServer' Port = 8080 PhysicalPath = "$env:SystemDrive\inetpub\PSDSCPullServer" - CertificateThumbPrint = $certificateThumbPrint + CertificateThumbPrint = $CertificateThumbPrint ModulePath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules" ConfigurationPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration" - State = "Started" - DependsOn = "[WindowsFeature]DSCServiceFeature" + State = 'Started' + DependsOn = '[WindowsFeature]DSCServiceFeature' RegistrationKeyPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService" AcceptSelfSignedCertificates = $true UseSecurityBestPractices = $true @@ -88,26 +90,49 @@ configuration Sample_xDscWebServiceRegistrationWithSecurityBestPractices } } } -Sample_xDscWebServiceRegistrationWithSecurityBestPractices -RegistrationKey $registrationKey -certificateThumbPrint $thumbprint -# =================================== Section Pull Server =================================== # -# =================================== Section DSC Client =================================== # +<# + .SYNOPSIS + The Sample_MetaConfigurationToRegisterWithSecurePullServer registers + a DSC client node with the pull server. + + .PARAMETER NodeName + The name of the node being configured as a DSC Pull Server. + + .PARAMETER RegistrationKey + This key will be used by client nodes as a shared key to authenticate + during registration. This should be a string with enough entropy + (randomness) to protect the registration of clients to the pull server. + The example creates a new GUID for the registration key. + + .PARAMETER ServerName + The HostName to use when configuring the Pull Server URL on the DSC + client. + + .EXAMPLE + $registrationKey = [System.Guid]::NewGuid() + + Sample_MetaConfigurationToRegisterWithSecurePullServer -RegistrationKey $registrationKey +#> [DSCLocalConfigurationManager()] -configuration Sample_MetaConfigurationToRegisterWithSecurePullServer +Configuration Sample_MetaConfigurationToRegisterWithSecurePullServer { param ( + [Parameter()] [ValidateNotNullOrEmpty()] - [string] + [System.String] $NodeName = 'localhost', + [Parameter()] [ValidateNotNullOrEmpty()] - [string] - $RegistrationKey, #same as the one used to setup pull server in previous configuration + [System.String] + $RegistrationKey, + [Parameter()] [ValidateNotNullOrEmpty()] - [string] - $ServerName = 'localhost' #node name of the pull server, same as $NodeName used in previous configuration + [System.String] + $ServerName = 'localhost' ) Node $NodeName @@ -119,18 +144,15 @@ configuration Sample_MetaConfigurationToRegisterWithSecurePullServer ConfigurationRepositoryWeb CONTOSO-PullSrv { - ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" # notice it is https + ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" RegistrationKey = $RegistrationKey ConfigurationNames = @('ClientConfig') } ReportServerWeb CONTOSO-PullSrv { - ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" # notice it is https + ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" RegistrationKey = $RegistrationKey } } } - -Sample_MetaConfigurationToRegisterWithSecurePullServer -RegistrationKey $registrationKey -# =================================== Section DSC Client =================================== # diff --git a/Examples/Sample_xDscWebServiceRegistration_UseSQLProvider.ps1 b/Examples/Sample_xDscWebServiceRegistration_UseSQLProvider.ps1 index e56bce37a..2c9d26873 100644 --- a/Examples/Sample_xDscWebServiceRegistration_UseSQLProvider.ps1 +++ b/Examples/Sample_xDscWebServiceRegistration_UseSQLProvider.ps1 @@ -1,83 +1,80 @@ -# DSC configuration for Pull Server using registration with enhanced security settings - -# The Sample_xDscWebServiceRegistration_UseSQLProvider configuration sets up a DSC pull server that is capable for client nodes -# to register with it and use SQL Server as a backend DB - -# Prerequisite:1- Install a certificate in "CERT:\LocalMachine\MY\" store -# For testing environments, you could use a self-signed certificate. (New-SelfSignedCertificate cmdlet could generate one for you). -# For production environments, you will need a certificate signed by valid CA. -# Registration only works over https protocols. So to use registration feature, a secure pull server setup with certificate is necessary -# 2- Install and Configure SQL Server - -# The Sample_MetaConfigurationToRegisterWithSecurePullServer register a DSC client node with the pull server - -# ======================================== Arguments ======================================== # - <# - Check if OS major version is higher or equal to 10. - - Note: This check is to pass example validation CI tests, - it has not been tested to run on Windows Server 2012 R2, - please see the following example for a Windows Server 2012 R2 version - of this example; - https://github.com/PowerShell/xPSDesiredStateConfiguration/blob/master/Examples/Sample_xDscWebServiceRegistration_Win2k12and2k12R2.ps1. + .SYNOPSIS + The Sample_xDscWebServiceRegistration_UseSQLProvider configuration sets + up a DSC pull server that is capable for client nodes to register with + it and use SQL Server as a backend DB. + + Prerequisite: 1 - Install a certificate in 'CERT:\LocalMachine\MY\' + store. For testing environments, you could use a + self-signed certificate. (New-SelfSignedCertificate + cmdlet could generate one for you). For production + environments, you will need a certificate signed by + valid CA. Registration only works over https + protocols. So to use registration feature, a secure + pull server setup with certificate is necessary. + 2 - Install and Configure SQL Server + + .PARAMETER NodeName + The name of the node being configured as a DSC Pull Server. + + .PARAMETER CertificateThumbPrint + Certificate thumbprint for creating an HTTPS endpoint. Use + "AllowUnencryptedTraffic" for setting up a non SSL based endpoint. + + .PARAMETER RegistrationKey + This key will be used by client nodes as a shared key to authenticate + during registration. This should be a string with enough entropy + (randomness) to protect the registration of clients to the pull server. + The example creates a new GUID for the registration key. + + .EXAMPLE + $thumbprint = (New-SelfSignedCertificate -Subject $env:COMPUTERNAME).Thumbprint + $registrationKey = [System.Guid]::NewGuid() + + Sample_xDscWebServiceRegistration_UseSQLProvider -RegistrationKey $registrationKey -CertificateThumbPrint $thumbprint -Verbose #> -if ([Environment]::OSVersion.Version.Major -ge '10') -{ - $thumbprint = (New-SelfSignedCertificate -Subject $env:COMPUTERNAME).Thumbprint -} -else -{ - Write-Warning -Message 'Running on operating system older than major version 10, this configuration is not meant to run on OS with a major version older than version 10. Generating certificate using New-SelfSignedCertificate with an alternate method.' - $thumbprint = (New-SelfSignedCertificate -DnsName $env:COMPUTERNAME -CertStoreLocation cert:\LocalMachine\My ).Thumbprint -} - -$registrationKey = [guid]::NewGuid() - -# ======================================== Arguments ======================================== # - -# =================================== Section Pull Server =================================== # - -configuration Sample_xDscWebServiceRegistration_UseSQLProvider +Configuration Sample_xDscWebServiceRegistration_UseSQLProvider { param ( - [string[]] + [Parameter()] + [System.String[]] $NodeName = 'localhost', + [Parameter()] [ValidateNotNullOrEmpty()] - [string] + [System.String] $CertificateThumbPrint, - [Parameter(HelpMessage = 'This should be a string with enough entropy (randomness) to protect the registration of clients to the pull server. We will use new GUID by default.')] + [Parameter()] [ValidateNotNullOrEmpty()] - [string] - $RegistrationKey # A guid that clients use to initiate conversation with pull server + [System.String] + $RegistrationKey ) Import-DSCResource -ModuleName xPSDesiredStateConfiguration - # To explicitly import the resource WindowsFesture and File. + # To explicitly import the resource WindowsFeature and File. Import-DscResource -ModuleName PSDesiredStateConfiguration Node $NodeName { WindowsFeature DSCServiceFeature { - Ensure = "Present" - Name = "DSC-Service" + Ensure = 'Present' + Name = 'DSC-Service' } xDscWebService PSDSCPullServer { - Ensure = "Present" - EndpointName = "PSDSCPullServer" + Ensure = 'Present' + EndpointName = 'PSDSCPullServer' Port = 8080 PhysicalPath = "$env:SystemDrive\inetpub\PSDSCPullServer" - CertificateThumbPrint = $certificateThumbPrint + CertificateThumbPrint = $CertificateThumbPrint ModulePath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules" ConfigurationPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration" - State = "Started" - DependsOn = "[WindowsFeature]DSCServiceFeature" + State = 'Started' + DependsOn = '[WindowsFeature]DSCServiceFeature' RegistrationKeyPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService" AcceptSelfSignedCertificates = $true UseSecurityBestPractices = $true @@ -95,27 +92,48 @@ configuration Sample_xDscWebServiceRegistration_UseSQLProvider } } -Sample_xDscWebServiceRegistration_UseSQLProvider -RegistrationKey $registrationKey -CertificateThumbPrint $thumbprint -Verbose -# =================================== Section Pull Server =================================== # +<# + .SYNOPSIS + The Sample_MetaConfigurationToRegisterWithSecurePullServer registers + a DSC client node with the pull server. + + .PARAMETER NodeName + The name of the node being configured as a DSC Pull Server. -# =================================== Section DSC Client =================================== # + .PARAMETER RegistrationKey + This key will be used by client nodes as a shared key to authenticate + during registration. This should be a string with enough entropy + (randomness) to protect the registration of clients to the pull server. + The example creates a new GUID for the registration key. + .PARAMETER ServerName + The HostName to use when configuring the Pull Server URL on the DSC + client. + + .EXAMPLE + $registrationKey = [System.Guid]::NewGuid() + + Sample_MetaConfigurationToRegisterWithSecurePullServer -RegistrationKey $registrationKey +#> [DSCLocalConfigurationManager()] -configuration Sample_MetaConfigurationToRegisterWithSecurePullServer +Configuration Sample_MetaConfigurationToRegisterWithSecurePullServer { param ( + [Parameter()] [ValidateNotNullOrEmpty()] - [string] + [System.String] $NodeName = 'localhost', + [Parameter()] [ValidateNotNullOrEmpty()] - [string] - $RegistrationKey, #same as the one used to setup pull server in previous configuration + [System.String] + $RegistrationKey, + [Parameter()] [ValidateNotNullOrEmpty()] - [string] - $ServerName = 'localhost' #node name of the pull server, same as $NodeName used in previous configuration + [System.String] + $ServerName = 'localhost' ) Node $NodeName @@ -127,19 +145,15 @@ configuration Sample_MetaConfigurationToRegisterWithSecurePullServer ConfigurationRepositoryWeb CONTOSO-PullSrv { - ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" # notice it is https + ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" RegistrationKey = $RegistrationKey ConfigurationNames = @('ClientConfig') } ReportServerWeb CONTOSO-PullSrv { - ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" # notice it is https + ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" RegistrationKey = $RegistrationKey } } } - -Sample_MetaConfigurationToRegisterWithSecurePullServer -RegistrationKey $registrationKey - -# =================================== Section DSC Client =================================== # diff --git a/Examples/Sample_xDscWebServiceRegistration_Win2k12and2k12R2.ps1 b/Examples/Sample_xDscWebServiceRegistration_Win2k12and2k12R2.ps1 index e94f24608..2c8fbdf1a 100644 --- a/Examples/Sample_xDscWebServiceRegistration_Win2k12and2k12R2.ps1 +++ b/Examples/Sample_xDscWebServiceRegistration_Win2k12and2k12R2.ps1 @@ -1,65 +1,79 @@ -# DSC configuration for Pull Server using registration with enhanced security settings - -# The Sample_xDscWebServiceRegistration_Win2k12and2k12R2 configuration sets up a DSC pull server that is capable for client nodes -# to register with it. - -# Prerequisite:1- Install a certificate in "CERT:\LocalMachine\MY\" store -# For testing environments, you could use a self-signed certificate. (New-SelfSignedCertificate cmdlet could generate one for you). -# For production environments, you will need a certificate signed by valid CA. -# Registration only works over https protocols. So to use registration feature, a secure pull server setup with certificate is necessary - -# The Sample_MetaConfigurationToRegisterWithSecurePullServer register a DSC client node with the pull server - - - -# ======================================== Arguments ======================================== # - -$thumbprint = (New-SelfSignedCertificate -DnsName $env:COMPUTERNAME -CertStoreLocation cert:\LocalMachine\My ).Thumbprint -$registrationKey = [guid]::NewGuid() - -# ======================================== Arguments ======================================== # - -# =================================== Section Pull Server =================================== # -configuration Sample_xDscWebServiceRegistration_Win2k12and2k12R2 +<# + .SYNOPSIS + The Sample_xDscWebServiceRegistration_Win2k12and2k12R2 configuration + sets up a DSC pull server that is capable for client nodes to register + with it. + + Prerequisite: Install a certificate in 'CERT:\LocalMachine\MY\' store + For testing environments, you could use a self-signed + certificate. (New-SelfSignedCertificate cmdlet could + generate one for you). For production environments, you + will need a certificate signed by valid CA. Registration + only works over https protocols. So to use registration + feature, a secure pull server setup with certificate is + necessary. + + .PARAMETER NodeName + The name of the node being configured as a DSC Pull Server. + + .PARAMETER CertificateThumbPrint + Certificate thumbprint for creating an HTTPS endpoint. Use + "AllowUnencryptedTraffic" for setting up a non SSL based endpoint. + + .PARAMETER RegistrationKey + This key will be used by client nodes as a shared key to authenticate + during registration. This should be a string with enough entropy + (randomness) to protect the registration of clients to the pull server. + The example creates a new GUID for the registration key. + + .EXAMPLE + $thumbprint = (New-SelfSignedCertificate -DnsName $env:COMPUTERNAME -CertStoreLocation Cert:\LocalMachine\My).Thumbprint + $registrationKey = [System.Guid]::NewGuid() + + Sample_xDscWebServiceRegistration_Win2k12and2k12R2 -RegistrationKey $registrationKey -certificateThumbPrint $thumbprint -Verbose +#> +Configuration Sample_xDscWebServiceRegistration_Win2k12and2k12R2 { param ( - [string[]] + [Parameter()] + [System.String[]] $NodeName = 'localhost', + [Parameter()] [ValidateNotNullOrEmpty()] - [string] - $certificateThumbPrint, + [System.String] + $CertificateThumbPrint, - [Parameter(HelpMessage = 'This should be a string with enough entropy (randomness) to protect the registration of clients to the pull server. We will use new GUID by default.')] + [Parameter()] [ValidateNotNullOrEmpty()] - [string] - $RegistrationKey # A guid that clients use to initiate conversation with pull server + [System.String] + $RegistrationKey ) Import-DSCResource -ModuleName xPSDesiredStateConfiguration - # To explicitly import the resource WindowsFesture and File. + # To explicitly import the resource WindowsFeature and File. Import-DscResource -ModuleName PSDesiredStateConfiguration Node $NodeName { WindowsFeature DSCServiceFeature { - Ensure = "Present" - Name = "DSC-Service" + Ensure = 'Present' + Name = 'DSC-Service' } xDscWebService PSDSCPullServer { - Ensure = "Present" - EndpointName = "PSDSCPullServer" + Ensure = 'Present' + EndpointName = 'PSDSCPullServer' Port = 8080 PhysicalPath = "$env:SystemDrive\inetpub\PSDSCPullServer" - CertificateThumbPrint = $certificateThumbPrint + CertificateThumbPrint = $CertificateThumbPrint ModulePath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules" ConfigurationPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration" - State = "Started" - DependsOn = "[WindowsFeature]DSCServiceFeature" + State = 'Started' + DependsOn = '[WindowsFeature]DSCServiceFeature' RegistrationKeyPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService" AcceptSelfSignedCertificates = $true UseSecurityBestPractices = $true @@ -76,28 +90,48 @@ configuration Sample_xDscWebServiceRegistration_Win2k12and2k12R2 } } -Sample_xDscWebServiceRegistration_Win2k12and2k12R2 -RegistrationKey $registrationKey -certificateThumbPrint $thumbprint -Verbose -# =================================== Section Pull Server =================================== # +<# + .SYNOPSIS + The Sample_MetaConfigurationToRegisterWithSecurePullServer registers + a DSC client node with the pull server. + + .PARAMETER NodeName + The name of the node being configured as a DSC Pull Server. -# Prerequisite:1- Import a the above created certificate to "CERT:\LocalMachine\Trusted Root Certification Authority\" store + .PARAMETER RegistrationKey + This key will be used by client nodes as a shared key to authenticate + during registration. This should be a string with enough entropy + (randomness) to protect the registration of clients to the pull server. + The example creates a new GUID for the registration key. -# =================================== Section DSC Client =================================== # + .PARAMETER ServerName + The HostName to use when configuring the Pull Server URL on the DSC + client. + + .EXAMPLE + $registrationKey = [System.Guid]::NewGuid() + + Sample_MetaConfigurationToRegisterWithSecurePullServer -RegistrationKey $registrationKey +#> [DSCLocalConfigurationManager()] -configuration Sample_MetaConfigurationToRegisterWithSecurePullServer +Configuration Sample_MetaConfigurationToRegisterWithSecurePullServer { param ( + [Parameter()] [ValidateNotNullOrEmpty()] - [string] + [System.String] $NodeName = 'localhost', + [Parameter()] [ValidateNotNullOrEmpty()] - [string] - $RegistrationKey, #same as the one used to setup pull server in previous configuration + [System.String] + $RegistrationKey, + [Parameter()] [ValidateNotNullOrEmpty()] - [string] - $ServerName = 'localhost' #node name of the pull server, same as $NodeName used in previous configuration + [System.String] + $ServerName = 'localhost' ) Node $NodeName @@ -109,18 +143,15 @@ configuration Sample_MetaConfigurationToRegisterWithSecurePullServer ConfigurationRepositoryWeb CONTOSO-PullSrv { - ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" # notice it is https + ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" RegistrationKey = $RegistrationKey ConfigurationNames = @('ClientConfig') } ReportServerWeb CONTOSO-PullSrv { - ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" # notice it is https + ServerURL = "https://$ServerName`:8080/PSDSCPullServer.svc" RegistrationKey = $RegistrationKey } } } - -Sample_MetaConfigurationToRegisterWithSecurePullServer -RegistrationKey $registrationKey -# =================================== Section DSC Client =================================== # diff --git a/Examples/xMsiPackage_InstallPackageConfig.ps1 b/Examples/xMsiPackage_InstallPackageConfig.ps1 index a07e0207a..9128184b3 100644 --- a/Examples/xMsiPackage_InstallPackageConfig.ps1 +++ b/Examples/xMsiPackage_InstallPackageConfig.ps1 @@ -30,7 +30,7 @@ '{DEADBEEF-80C6-41E6-A1B9-8BDB8A05027F}'. .PARAMETER Path - The URI path. Should start with an URI scheme, e.g. 'file://','http://', + The URI path. Should start with an URI scheme, e.g. 'file://', 'http://', 'https://'. .NOTES diff --git a/HighQualityResourceModulePlan.md b/HighQualityResourceModulePlan.md index 3f4fcf608..1b0ab625f 100644 --- a/HighQualityResourceModulePlan.md +++ b/HighQualityResourceModulePlan.md @@ -1,11 +1,18 @@ # PSDesiredStateConfiguration High Quality Resource Module Plan -Any comments or questions about this plan can be submitted under issue [#160](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/160) -## Goals - 1. Port the appropriate in-box DSC Resources to the open-source xPSDesiredStateConfiguration resource module. - 2. Make the open-source xPSDesiredStateConfiuration resource module a **High Quality Resource Module (HQRM)** to present to the community for feedback. +Any comments or questions about this plan can be submitted under issue +[#160](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/160) + +## Goals + +1. Port the appropriate in-box DSC Resources to the open-source + xPSDesiredStateConfiguration resource module. +1. Make the open-source xPSDesiredStateConfiuration resource module a **High + Quality Resource Module (HQRM)** to present to the community for feedback. + +The PSDesiredStateConfiguration High Quality Resource Module will consist of +the following resources: -The PSDesiredStateConfiguration High Quality Resource Module will consist of the following resources: - Archive - Environment - Group @@ -27,7 +34,8 @@ The PSDesiredStateConfiguration High Quality Resource Module will consist of the ## Progress -- [x] [1. Port In-Box Only Resources](#port-in-box-only-resources) +- [x] + [1. Port In-Box Only Resources](#port-in-box-only-resources) - [x] Environment - [x] GroupSet - [x] ProcessSet @@ -38,17 +46,20 @@ The PSDesiredStateConfiguration High Quality Resource Module will consist of the - [x] WindowsFeatureSet - [x] WindowsOptionalFeatureSet - [x] WindowsPackageCab -- [x] [2. Merge In-Box & Open-Source Resources](#merge-in-box-and-open-source-resources) +- [x] + [2. Merge In-Box & Open-Source Resources](#merge-in-box-and-open-source-resources) - [x] Archive - [x] Group - [x] Package - - [X] Process - - [x] Registry + - [X] Process + - [x] Registry - [x] Service - [x] WindowsOptionalFeature -- [x] [3. Resolve Nano Server vs. Full Server Resources](#resolve-nano-server-vs-full-server-resources) - The general consensus is to leave the if-statements for now. -- [ ] [4. Update Each Resource to Be High Quality](#update-each-resource-to-be-high-quality) +- [x] + [3. Resolve Nano Server vs. Full Server Resources](#resolve-nano-server-vs-full-server-resources) + The general consensus is to leave the if-statements for now. +- [ ] + [4. Update Each Resource to Be High Quality](#update-each-resource-to-be-high-quality) - [ ] Archive - [ ] Environment - [x] Group @@ -67,9 +78,11 @@ The PSDesiredStateConfiguration High Quality Resource Module will consist of the - [x] WindowsOptionalFeature - [ ] WindowsOptionalFeatureSet - [x] WindowsPackageCab -- [x] [5. Resolve Name of New High Quality Resource Module](#resolve-name-of-new-high-quality-resource-module) +- [x] + [5. Resolve Name of New High Quality Resource Module](#resolve-name-of-new-high-quality-resource-module) The new high quality module name is PSDscResources. -- [ ] [6. Move Resources to New High Quality Module](#move-resources-to-new-high-quality-module) +- [ ] + [6. Move Resources to New High Quality Module](#move-resources-to-new-high-quality-module) - [ ] Archive - [ ] Environment - [x] Group @@ -90,11 +103,16 @@ The PSDesiredStateConfiguration High Quality Resource Module will consist of the - [x] WindowsPackageCab ## Port In-Box Only Resources -We will port the appropriate in-box resources that will be in the HQRM to the open-source xPSDesiredStateConfiguration resource module. These resources are not currently in the open-source repository. Resources currently both in-box and open-source will be merged in step 2. + +We will port the appropriate in-box resources that will be in the HQRM to the +open-source xPSDesiredStateConfiguration resource module. These resources are +not currently in the open-source repository. Resources currently both in-box +and open-source will be merged in step 2. ### In-Box Only Resources Moving to Open-Source HQRM -These resources and any of their tests, examples, or documentation will be moved to the xPSDSC open-source repository: +These resources and any of their tests, examples, or documentation will be +moved to the xPSDSC open-source repository: - Environment - GroupSet @@ -107,13 +125,20 @@ These resources and any of their tests, examples, or documentation will be moved - WindowsOptionalFeatureSet - WindowsPackageCab (**NEW**) -When these resources are moved to GitHub, they will have 'x' appended before their names for now to indicate that they are still 'experimental' in this stage. This 'x' convention will change in the near future. The 'x' can be removed as part of step 5. +When these resources are moved to GitHub, they will have 'x' appended before +their names for now to indicate that they are still 'experimental' in this +stage. This 'x' convention will change in the near future. The 'x' can be +removed as part of step 5. ## Merge In-Box and Open-Source Resources -We will merge in-box resources that are also currently in the open-source module. The in-box resources and any of their tests, examples, or documentation will be merged into the existing open-source resources in the xPSDesiredStateConfiguration resource module. +We will merge in-box resources that are also currently in the open-source +module. The in-box resources and any of their tests, examples, or documentation +will be merged into the existing open-source resources in the +xPSDesiredStateConfiguration resource module. -Four of the current open-source resources are not provided in-box. The fate of these open-source-only resources has been addressed in this step as well. +Four of the current open-source resources are not provided in-box. The fate of +these open-source-only resources has been addressed in this step as well. ### Open-Source Resources Moving to HQRM @@ -128,7 +153,9 @@ Four of the current open-source resources are not provided in-box. The fate of t - Service - WindowsOptionalFeature -These resources will retain the 'x' appended before their names for now to indicate that they are still 'experimental' in this stage. This 'x' convention will change in the near future. The 'x' can be removed as part of step 5. +These resources will retain the 'x' appended before their names for now to +indicate that they are still 'experimental' in this stage. This 'x' convention +will change in the near future. The 'x' can be removed as part of step 5. ### Open-Source Resources Not Moving to HQRM @@ -140,34 +167,47 @@ These resources will retain the 'x' appended before their names for now to indic ## Resolve Nano Server vs Full Server Resources -Some of the in-box resources (User especially) currently contain all-encompassing if-statements which tells the resource to act differently based on whether it is operating on a Nano server or a full server. These if-statements will make the resources difficult to maintain. +Some of the in-box resources (User especially) currently contain +all-encompassing if-statements which tells the resource to act differently +based on whether it is operating on a Nano server or a full server. These +if-statements will make the resources difficult to maintain. ### Potential Solutions | Solution | Pros | Cons | |----------|------|------| -| Leave the if-statements |
  • No time needed for fix.
|
  • Difficult to maintain.
  • User has to download/store extra code. (minimal)
| -| Use the Nano server version only |
  • Code will be easy to maintain.
  • May be a cleaner, simpler implementation for full server.
  • User does not have to download/store extra code. (minimal)
|
  • May break the resources.
  • Requires fixing time.
  • Requires testing.
| -| Separate the Nano and full server versions into separate resources |
  • User can download only the resource version they need.
|
  • Will have to maintain separate version.
  • Requires fixing time.
  • Requires testing.
| +| Leave the if-statements | - No time needed for fix. | - Difficult to maintain. - User has to download/store extra code. (minimal) | +| Use the Nano server version only | - Code will be easy to maintain. - May be a cleaner, simpler implementation for full server. - User does not have to download/store extra code. (minimal) | - May break the resources. - Requires fixing time. - Requires testing. | +| Separate the Nano and full server versions into separate resources | - User can download only the resource version they need. | - Will have to maintain separate version. - Requires fixing time. - Requires testing. | ## Update Each Resource to Be High Quality -We will update the resouces, tests, exmaples, and documentation to ensure that the xPSDesiredStateConfiguration resource module meets the requirements to be a High Quality Resource Module (HQRM). These requirements can be found in the DSC Resource Kit High Quality Plan (not yet published publicly, sorry). +We will update the resouces, tests, exmaples, and documentation to ensure that +the xPSDesiredStateConfiguration resource module meets the requirements to be a +High Quality Resource Module (HQRM). These requirements can be found in the DSC +Resource Kit High Quality Plan (not yet published publicly, sorry). Here are the basic steps we will have to take based on this plan: -1. Fix PSSA issues per the DSC Resource Kit PSSA Rule Severity List (not yet published publicly, sorry). -2. Ensure unit tests are present for each resource with more than 70% code coverage. -3. Ensure examples run correctly, work as expected, and are documented clearly. -4. Ensure clear documentation is provided. -5. Ensure the PSDesiredStateConfiguration module follows the standard DSC Resource Kit module format. -6. Fix code styling to match the [DSC Resource Kit Style Guidelines](https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md). +1. Fix PSSA issues per the DSC Resource Kit PSSA Rule Severity List (not yet + published publicly, sorry). +1. Ensure unit tests are present for each resource with more than 70% code + coverage. +1. Ensure examples run correctly, work as expected, and are documented clearly. +1. Ensure clear documentation is provided. +1. Ensure the PSDesiredStateConfiguration module follows the standard DSC + Resource Kit module format. +1. Fix code styling to match the + [DSC Resource Kit Style Guidelines](https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md). ## Resolve Name of New High Quality Resource Module -The new High Quality Resource Module (HQRM) for xPSDesiredStateConfiguration is named [PSDscResources](https://github.com/PowerShell/PSDscResources). +The new High Quality Resource Module (HQRM) for xPSDesiredStateConfiguration is +named [PSDscResources](https://github.com/PowerShell/PSDscResources). -It cannot be named PSDesiredStateCongfiguration since that would conflict with the in-box module, but this HQRM will not contain all the resources in the in-box module (File, Log cannot be ported). +It cannot be named PSDesiredStateCongfiguration since that would conflict with +the in-box module, but this HQRM will not contain all the resources in the +in-box module (File, Log cannot be ported). All resources will have the 'x' removed in the HQRM. @@ -176,4 +216,4 @@ All resources will have the 'x' removed in the HQRM. Each resource is transferred to the HQRM when it is ready with these steps: 1. Rename the resource in all files (remove the 'x'). -2. Submit a PR with the new resource to the PSDscResources repository. +1. Submit a PR with the new resource to the PSDscResources repository. diff --git a/README.md b/README.md index 83346c576..5cb769f0e 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,25 @@ # xPSDesiredStateConfiguration -The **xPSDesiredStateConfiguration** module is a more recent, experimental version of the PSDesiredStateConfiguration module that ships in Windows as part of PowerShell 4.0. - -The high quality, supported version of this module is available as [PSDscResources](https://github.com/PowerShell/PSDscResources). - -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. +The **xPSDesiredStateConfiguration** module is a more recent, experimental +version of the PSDesiredStateConfiguration module that ships in Windows as part +of PowerShell 4.0. + +The high quality, supported version of this module is available as +[PSDscResources](https://github.com/PowerShell/PSDscResources). + +This module is automatically tested using PowerShell 5.1 on servers running +Windows 2012 R2 and Windows 2016, and is expected to work on other operating +systems running PowerShell 5.1. While this module may work with PowerShell +versions going back to PowerShell 4, there is no automatic testing performed +for these versions, and thus no guarantee that the module will work as +expected. + +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. ## Branches @@ -29,31 +43,48 @@ and be released to [PowerShell Gallery](https://www.powershellgallery.com/). ## Contributing -If you would like to contribute to this module, please review the common DSC Resources [contributing guidelines](https://github.com/PowerShell/DscResource.Kit/blob/master/CONTRIBUTING.md). +If you would like to contribute to this module, please review the common DSC +Resources +[contributing guidelines](https://github.com/PowerShell/DscResource.Kit/blob/master/CONTRIBUTING.md). ## Resources -* **xArchive** provides a mechanism to expand an archive (.zip) file to a specific path or remove an expanded archive (.zip) file from a specific path on a target node. -* **xDscWebService** configures an OData endpoint for DSC service to make a node a DSC pull server. -* **xEnvironment** provides a mechanism to configure and manage environment variables for a machine or process. -* **xFileUpload** is a composite resource which ensures that local files exist on an SMB share. +* **xArchive** provides a mechanism to expand an archive (.zip) file to a + specific path or remove an expanded archive (.zip) file from a specific path + on a target node. +* **xDscWebService** configures an OData endpoint for DSC service to make a + node a DSC pull server. +* **xEnvironment** provides a mechanism to configure and manage environment + variables for a machine or process. +* **xFileUpload** is a composite resource which ensures that local files exist + on an SMB share. * **xGroup** provides a mechanism to manage local groups on a target node. -* **xGroupSet** provides a mechanism to configure and manage multiple xGroup resources with common settings but different names. +* **xGroupSet** provides a mechanism to configure and manage multiple xGroup + resources with common settings but different names. * **xMsiPackage** provides a mechanism to install and uninstall .msi packages. * **xPackage** manages the installation of .msi and .exe packages. -* **xRegistry** provides a mechanism to manage registry keys and values on a target node. +* **xRegistry** provides a mechanism to manage registry keys and values on a + target node. * **xRemoteFile** ensures the presence of remote files on a local machine. -* **xScript** provides a mechanism to run PowerShell script blocks on a target node. +* **xScript** provides a mechanism to run PowerShell script blocks on a target + node. * **xService** provides a mechanism to configure and manage Windows services. -* **xServiceSet** provides a mechanism to configure and manage multiple xService resources with common settings but different names. +* **xServiceSet** provides a mechanism to configure and manage multiple + xService resources with common settings but different names. * **xUser** provides a mechanism to manage local users on the target node. -* **xWindowsFeature** provides a mechanism to install or uninstall Windows roles or features on a target node. -* **xWindowsFeatureSet** provides a mechanism to configure and manage multiple xWindowsFeature resources on a target node. -* **xWindowsOptionalFeature** provides a mechanism to enable or disable optional features on a target node. -* **xWindowsOptionalFeatureSet** provides a mechanism to configure and manage multiple xWindowsOptionalFeature resources on a target node. -* **xWindowsPackageCab** provides a mechanism to install or uninstall a package from a Windows cabinet (cab) file on a target node. +* **xWindowsFeature** provides a mechanism to install or uninstall Windows + roles or features on a target node. +* **xWindowsFeatureSet** provides a mechanism to configure and manage multiple + xWindowsFeature resources on a target node. +* **xWindowsOptionalFeature** provides a mechanism to enable or disable + optional features on a target node. +* **xWindowsOptionalFeatureSet** provides a mechanism to configure and manage + multiple xWindowsOptionalFeature resources on a target node. +* **xWindowsPackageCab** provides a mechanism to install or uninstall a package + from a Windows cabinet (cab) file on a target node. * **xWindowsProcess** provides a mechanism to start and stop a Windows process. -* **xProcessSet** allows starting and stopping of a group of windows processes with no arguments. +* **xProcessSet** allows starting and stopping of a group of windows processes + with no arguments. Resources that work on Nano Server: @@ -67,22 +98,58 @@ Resources that work on Nano Server: ### xArchive -Provides a mechanism to expand an archive (.zip) file to a specific path or remove an expanded archive (.zip) file from a specific path on a target node. +Provides a mechanism to expand an archive (.zip) file to a specific path or +remove an expanded archive (.zip) file from a specific path on a target node. #### Requirements * The System.IO.Compression type assembly must be available on the machine. -* The System.IO.Compression.FileSystem type assembly must be available on the machine. +* The System.IO.Compression.FileSystem type assembly must be available on the + machine. #### Parameters -* **[String] Path** _(Key)_: The path to the archive file that should be expanded to or removed from the specified destination. -* **[String] Destination** _(Key)_: The path where the specified archive file should be expanded to or removed from. -* **[String] Ensure** _(Write)_: Specifies whether or not the expanded content of the archive file at the specified path should exist at the specified destination. To update the specified destination to have the expanded content of the archive file at the specified path, specify this property as Present. To remove the expanded content of the archive file at the specified path from the specified destination, specify this property as Absent. The default value is Present. { *Present* | Absent }. -* **[Boolean] Validate** _(Write)_: Specifies whether or not to validate that a file at the destination with the same name as a file in the archive actually matches that corresponding file in the archive by the specified checksum method. If the file does not match and Ensure is specified as Present and Force is not specified, the resource will throw an error that the file at the desintation cannot be overwritten. If the file does not match and Ensure is specified as Present and Force is specified, the file at the desintation will be overwritten. If the file does not match and Ensure is specified as Absent, the file at the desintation will not be removed. The default Checksum method is ModifiedDate. The default value is false. -* **[String] Checksum** _(Write)_: The Checksum method to use to validate whether or not a file at the destination with the same name as a file in the archive actually matches that corresponding file in the archive. An invalid argument exception will be thrown if Checksum is specified while Validate is specified as false. ModifiedDate will check that the LastWriteTime property of the file at the destination matches the LastWriteTime property of the file in the archive. CreatedDate will check that the CreationTime property of the file at the destination matches the CreationTime property of the file in the archive. SHA-1, SHA-256, and SHA-512 will check that the hash of the file at the destination by the specified SHA method matches the hash of the file in the archive by the specified SHA method. The default value is ModifiedDate. { *ModifiedDate* | CreatedDate | SHA-1 | SHA-256 | SHA-512 } -* **[System.Management.Automation.PSCredential] Credential** _(Write)_: The credential of a user account with permissions to access the specified archive path and destination if needed. -* **[Boolean] Force** _(Write)_: Specifies whether or not any existing files or directories at the destination with the same name as a file or directory in the archive should be overwritten to match the file or directory in the archive. When this property is false, an error will be thrown if an item at the destination needs to be overwritten. The default value is false. +* **[String] Path** _(Key)_: The path to the archive file that should be + expanded to or removed from the specified destination. +* **[String] Destination** _(Key)_: The path where the specified archive file + should be expanded to or removed from. +* **[String] Ensure** _(Write)_: Specifies whether or not the expanded content + of the archive file at the specified path should exist at the specified + destination. To update the specified destination to have the expanded content + of the archive file at the specified path, specify this property as Present. + To remove the expanded content of the archive file at the specified path from + the specified destination, specify this property as Absent. The default value + is Present. { *Present* | Absent }. +* **[Boolean] Validate** _(Write)_: Specifies whether or not to validate that a + file at the destination with the same name as a file in the archive actually + matches that corresponding file in the archive by the specified checksum + method. If the file does not match and Ensure is specified as Present and + Force is not specified, the resource will throw an error that the file at the + desintation cannot be overwritten. If the file does not match and Ensure is + specified as Present and Force is specified, the file at the desintation will + be overwritten. If the file does not match and Ensure is specified as Absent, + the file at the desintation will not be removed. The default Checksum method + is ModifiedDate. The default value is false. +* **[String] Checksum** _(Write)_: The Checksum method to use to validate + whether or not a file at the destination with the same name as a file in the + archive actually matches that corresponding file in the archive. An invalid + argument exception will be thrown if Checksum is specified while Validate is + specified as false. ModifiedDate will check that the LastWriteTime property + of the file at the destination matches the LastWriteTime property of the file + in the archive. CreatedDate will check that the CreationTime property of the + file at the destination matches the CreationTime property of the file in the + archive. SHA-1, SHA-256, and SHA-512 will check that the hash of the file at + the destination by the specified SHA method matches the hash of the file in + the archive by the specified SHA method. The default value is ModifiedDate. + { *ModifiedDate* | CreatedDate | SHA-1 | SHA-256 | SHA-512 } +* **[System.Management.Automation.PSCredential] Credential** _(Write)_: The + credential of a user account with permissions to access the specified archive + path and destination if needed. +* **[Boolean] Force** _(Write)_: Specifies whether or not any existing files or + directories at the destination with the same name as a file or directory in + the archive should be overwritten to match the file or directory in the + archive. When this property is false, an error will be thrown if an item at + the destination needs to be overwritten. The default value is false. #### Read-Only Properties from Get-TargetResource @@ -100,19 +167,32 @@ None ### xDscWebService * **EndpointName**: The desired web service name. -* **CertificateThumbPrint**: Certificate thumbprint for creating an HTTPS endpoint. Use "AllowUnencryptedTraffic" for setting up a non SSL based endpoint. +* **CertificateThumbPrint**: Certificate thumbprint for creating an HTTPS + endpoint. Use "AllowUnencryptedTraffic" for setting up a non SSL based + endpoint. * **Port**: Port for web service. -* **PhysicalPath**: Folder location where the content of the web service resides. +* **PhysicalPath**: Folder location where the content of the web service + resides. * **Ensure**: Ensures that the web service is **Present** or **Absent** * **State**: State of the web service: { Started | Stopped } * **ModulePath**: Folder location where DSC resources are stored. * **ConfigurationPath**: Folder location where DSC configurations are stored. -* **RegistrationKeyPath**: Folder location where DSC pull server registration key file is stored. -* **AcceptSelfSignedCertificates**: Whether self signed certificate can be used to setup pull server. -* **UseSecurityBestPractices**: Whether to use best practice security settings for the node where pull server resides on. -Caution: Setting this property to $true will reset registry values under "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL". This environment change enforces the use of stronger encryption cypher and may affect legacy applications. More information can be found at https://support.microsoft.com/en-us/kb/245030 and https://technet.microsoft.com/en-us/library/dn786418(v=ws.11).aspx. -* **DisableSecurityBestPractices**: The items that are excepted from following best practice security settings. -* **Enable32BitAppOnWin64**: When this property is set to true, Pull Server will run on a 32 bit process on a 64 bit machine. +* **RegistrationKeyPath**: Folder location where DSC pull server registration + key file is stored. +* **AcceptSelfSignedCertificates**: Whether self signed certificate can be used + to setup pull server. +* **UseSecurityBestPractices**: Whether to use best practice security settings + for the node where pull server resides on. + Caution: Setting this property to $true will reset registry values under + "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL". This + environment change enforces the use of stronger encryption cypher and may + affect legacy applications. More information can be found at + https://support.microsoft.com/en-us/kb/245030 and + https://technet.microsoft.com/en-us/library/dn786418(v=ws.11).aspx. +* **DisableSecurityBestPractices**: The items that are excepted from following + best practice security settings. +* **Enable32BitAppOnWin64**: When this property is set to true, Pull Server + will run on a 32 bit process on a 64 bit machine. ### xGroup @@ -125,13 +205,36 @@ None #### Parameters -* **[String] GroupName** _(Key)_: The name of the group to create, modify, or remove. -* **[String] Ensure** _(Write)_: Indicates if the group should exist or not. To add a group or modify an existing group, set this property to Present. To remove a group, set this property to Absent. The default value is Present. { *Present* | Absent }. +* **[String] GroupName** _(Key)_: The name of the group to create, modify, or + remove. +* **[String] Ensure** _(Write)_: Indicates if the group should exist or not. To + add a group or modify an existing group, set this property to Present. To + remove a group, set this property to Absent. The default value is Present. + { *Present* | Absent }. * **[String] Description** _(Write)_: The description the group should have. -* **[String[]] Members** _(Write)_: The members the group should have. This property will replace all the current group members with the specified members. Members should be specified as strings in the format of their domain qualified name (domain\username), their UPN (username@domainname), their distinguished name (CN=username,DC=...), or their username (for local machine accounts). Using either the MembersToExclude or MembersToInclude properties in the same configuration as this property will generate an error. -* **[String[]] MembersToInclude** _(Write)_: The members the group should include. This property will only add members to a group. Members should be specified as strings in the format of their domain qualified name (domain\username), their UPN (username@domainname), their distinguished name (CN=username,DC=...), or their username (for local machine accounts). Using the Members property in the same configuration as this property will generate an error. -* **[String[]] MembersToExclude** _(Write)_: The members the group should exclude. This property will only remove members from a group. Members should be specified as strings in the format of their domain qualified name (domain\username), their UPN (username@domainname), their distinguished name (CN=username,DC=...), or their username (for local machine accounts). Using the Members property in the same configuration as this property will generate an error. -* **[System.Management.Automation.PSCredential] Credential** _(Write)_: A credential to resolve non-local group members. +* **[String[]] Members** _(Write)_: The members the group should have. This + property will replace all the current group members with the specified + members. Members should be specified as strings in the format of their domain + qualified name (domain\username), their UPN (username@domainname), their + distinguished name (CN=username,DC=...), or their username (for local machine + accounts). Using either the MembersToExclude or MembersToInclude properties + in the same configuration as this property will generate an error. +* **[String[]] MembersToInclude** _(Write)_: The members the group should + include. This property will only add members to a group. Members should be + specified as strings in the format of their domain qualified name + (domain\username), their UPN (username@domainname), their distinguished name + (CN=username,DC=...), or their username (for local machine accounts). Using + the Members property in the same configuration as this property will generate + an error. +* **[String[]] MembersToExclude** _(Write)_: The members the group should + exclude. This property will only remove members from a group. Members should + be specified as strings in the format of their domain qualified name + (domain\username), their UPN (username@domainname), their distinguished name + (CN=username,DC=...), or their username (for local machine accounts). Using + the Members property in the same configuration as this property will generate + an error. +* **[System.Management.Automation.PSCredential] Credential** _(Write)_: A + credential to resolve non-local group members. #### Read-Only Properties from Get-TargetResource @@ -144,7 +247,8 @@ None ### xGroupSet -Provides a mechanism to configure and manage multiple xGroup resources with common settings but different names +Provides a mechanism to configure and manage multiple xGroup resources with +common settings but different names #### Requirements @@ -152,14 +256,26 @@ None #### Parameters -* **[String] GroupName** _(Key)_: The names of the groups to create, modify, or remove. +* **[String] GroupName** _(Key)_: The names of the groups to create, modify, or + remove. The following parameters will be the same for each group in the set: -* **[String] Ensure** _(Write)_: Indicates if the groups should exist or not. To add groups or modify existing groups, set this property to Present. To remove groups, set this property to Absent. { Present | Absent }. -* **[String[]] MembersToInclude** _(Write)_: The members the groups should include. This property will only add members to groups. Members should be specified as strings in the format of their domain qualified name (domain\username), their UPN (username@domainname), their distinguished name (CN=username,DC=...), or their username (for local machine accounts). -* **[String[]] MembersToExclude** _(Write)_: The members the groups should exclude. This property will only remove members groups. Members should be specified as strings in the format of their domain qualified name (domain\username), their UPN (username@domainname), their distinguished name (CN=username,DC=...), or their username (for local machine accounts). -* **[System.Management.Automation.PSCredential] Credential** _(Write)_: A credential to resolve non-local group members. +* **[String] Ensure** _(Write)_: Indicates if the groups should exist or not. + To add groups or modify existing groups, set this property to Present. To + remove groups, set this property to Absent. { Present | Absent }. +* **[String[]] MembersToInclude** _(Write)_: The members the groups should + include. This property will only add members to groups. Members should be + specified as strings in the format of their domain qualified name + (domain\username), their UPN (username@domainname), their distinguished name + (CN=username,DC=...), or their username (for local machine accounts). +* **[String[]] MembersToExclude** _(Write)_: The members the groups should + exclude. This property will only remove members groups. Members should be + specified as strings in the format of their domain qualified name + (domain\username), their UPN (username@domainname), their distinguished name + (CN=username,DC=...), or their username (for local machine accounts). +* **[System.Management.Automation.PSCredential] Credential** _(Write)_: A + credential to resolve non-local group members. #### Read-Only Properties from Get-TargetResource @@ -179,23 +295,45 @@ None #### Parameters -* **[String] Path** _(Key)_: The executable file of the process. This can be defined as either the full path to the file or as the name of the file if it is accessible through the environment path. Relative paths are not supported. -* **[String] Arguments** _(Key)_: A single string containing all the arguments to pass to the process. Pass in an empty string if no arguments are needed. -* **[PSCredential] Credential** _(Write)_: The credential of the user account to run the process under. If this user is from the local system, the StandardOutputPath, StandardInputPath, and WorkingDirectory parameters cannot be provided at the same time. -* **[String] Ensure** _(Write)_: Specifies whether or not the process should be running. To start the process, specify this property as Present. To stop the process, specify this property as Absent. { *Present* | Absent }. -* **[String] StandardOutputPath** _(Write)_: The file path to which to write the standard output from the process. Any existing file at this file path will be overwritten. This property cannot be specified at the same time as Credential when running the process as a local user. -* **[String] StandardErrorPath** _(Write)_: The file path to which to write the standard error output from the process. Any existing file at this file path will be overwritten. -* **[String] StandardInputPath** _(Write)_: The file path from which to receive standard input for the process. This property cannot be specified at the same time as Credential when running the process as a local user. -* **[String] WorkingDirectory** _(Write)_: The file path to the working directory under which to run the process. This property cannot be specified at the same time as Credential when running the process as a local user. +* **[String] Path** _(Key)_: The executable file of the process. This can be + defined as either the full path to the file or as the name of the file if it + is accessible through the environment path. Relative paths are not supported. +* **[String] Arguments** _(Key)_: A single string containing all the arguments + to pass to the process. Pass in an empty string if no arguments are needed. +* **[PSCredential] Credential** _(Write)_: The credential of the user account + to run the process under. If this user is from the local system, the + StandardOutputPath, StandardInputPath, and WorkingDirectory parameters cannot + be provided at the same time. +* **[String] Ensure** _(Write)_: Specifies whether or not the process should be + running. To start the process, specify this property as Present. To stop the + process, specify this property as Absent. { *Present* | Absent }. +* **[String] StandardOutputPath** _(Write)_: The file path to which to write + the standard output from the process. Any existing file at this file path + will be overwritten. This property cannot be specified at the same time as + Credential when running the process as a local user. +* **[String] StandardErrorPath** _(Write)_: The file path to which to write the + standard error output from the process. Any existing file at this file path + will be overwritten. +* **[String] StandardInputPath** _(Write)_: The file path from which to receive + standard input for the process. This property cannot be specified at the same + time as Credential when running the process as a local user. +* **[String] WorkingDirectory** _(Write)_: The file path to the working + directory under which to run the process. This property cannot be specified + at the same time as Credential when running the process as a local user. #### Read-Only Properties from Get-TargetResource -* **[UInt64] PagedMemorySize** _(Read)_: The amount of paged memory, in bytes, allocated for the process. -* **[UInt64] NonPagedMemorySize** _(Read)_: The amount of nonpaged memory, in bytes, allocated for the process. -* **[UInt64] VirtualMemorySize** _(Read)_: The amount of virtual memory, in bytes, allocated for the process. -* **[SInt32] HandleCount** _(Read)_: The number of handles opened by the process. +* **[UInt64] PagedMemorySize** _(Read)_: The amount of paged memory, in bytes, + allocated for the process. +* **[UInt64] NonPagedMemorySize** _(Read)_: The amount of nonpaged memory, in + bytes, allocated for the process. +* **[UInt64] VirtualMemorySize** _(Read)_: The amount of virtual memory, in + bytes, allocated for the process. +* **[SInt32] HandleCount** _(Read)_: The number of handles opened by the + process. * **[SInt32] ProcessId** _(Read)_: The unique identifier of the process. -* **[SInt32] ProcessCount** _(Read)_: The number of instances of the given process that are currently running. +* **[SInt32] ProcessCount** _(Read)_: The number of instances of the given + process that are currently running. #### Examples @@ -206,7 +344,8 @@ None ### xProcessSet -Provides a mechanism to configure and manage multiple xWindowsProcess resources on a target node. +Provides a mechanism to configure and manage multiple xWindowsProcess resources +on a target node. #### Requirements @@ -214,25 +353,47 @@ None #### Parameters -* **[String[]] Path** _(Key)_: The file paths to the executables of the processes to start or stop. Only the names of the files may be specified if they are all accessible through the environment path. Relative paths are not supported. +* **[String[]] Path** _(Key)_: The file paths to the executables of the + processes to start or stop. Only the names of the files may be specified if + they are all accessible through the environment path. Relative paths are not + supported. The following parameters will be the same for each process in the set: -* **[PSCredential] Credential** _(Write)_: The credential of the user account to run the processes under. If this user is from the local system, the StandardOutputPath, StandardInputPath, and WorkingDirectory parameters cannot be provided at the same time. -* **[String] Ensure** _(Write)_: Specifies whether or not the processes should be running. To start the processes, specify this property as Present. To stop the processes, specify this property as Absent. { Present | Absent }. -* **[String] StandardOutputPath** _(Write)_: The file path to which to write the standard output from the processes. Any existing file at this file path will be overwritten. This property cannot be specified at the same time as Credential when running the processes as a local user. -* **[String] StandardErrorPath** _(Write)_: The file path to which to write the standard error output from the processes. Any existing file at this file path will be overwritten. -* **[String] StandardInputPath** _(Write)_: The file path from which to receive standard input for the processes. This property cannot be specified at the same time as Credential when running the processes as a local user. -* **[String] WorkingDirectory** _(Write)_: The file path to the working directory under which to run the process. This property cannot be specified at the same time as Credential when running the processes as a local user. +* **[PSCredential] Credential** _(Write)_: The credential of the user account + to run the processes under. If this user is from the local system, the + StandardOutputPath, StandardInputPath, and WorkingDirectory parameters cannot + be provided at the same time. +* **[String] Ensure** _(Write)_: Specifies whether or not the processes should + be running. To start the processes, specify this property as Present. To stop + the processes, specify this property as Absent. { Present | Absent }. +* **[String] StandardOutputPath** _(Write)_: The file path to which to write + the standard output from the processes. Any existing file at this file path + will be overwritten. This property cannot be specified at the same time as + Credential when running the processes as a local user. +* **[String] StandardErrorPath** _(Write)_: The file path to which to write the + standard error output from the processes. Any existing file at this file path + will be overwritten. +* **[String] StandardInputPath** _(Write)_: The file path from which to receive + standard input for the processes. This property cannot be specified at the + same time as Credential when running the processes as a local user. +* **[String] WorkingDirectory** _(Write)_: The file path to the working + directory under which to run the process. This property cannot be specified + at the same time as Credential when running the processes as a local user. #### Read-Only Properties from Get-TargetResource -* **[UInt64] PagedMemorySize** _(Read)_: The amount of paged memory, in bytes, allocated for the processes. -* **[UInt64] NonPagedMemorySize** _(Read)_: The amount of nonpaged memory, in bytes, allocated for the processes. -* **[UInt64] VirtualMemorySize** _(Read)_: The amount of virtual memory, in bytes, allocated for the processes. -* **[SInt32] HandleCount** _(Read)_: The number of handles opened by the processes. +* **[UInt64] PagedMemorySize** _(Read)_: The amount of paged memory, in bytes, + allocated for the processes. +* **[UInt64] NonPagedMemorySize** _(Read)_: The amount of nonpaged memory, in + bytes, allocated for the processes. +* **[UInt64] VirtualMemorySize** _(Read)_: The amount of virtual memory, in + bytes, allocated for the processes. +* **[SInt32] HandleCount** _(Read)_: The number of handles opened by the + processes. * **[SInt32] ProcessId** _(Read)_: The unique identifier of the processes. -* **[SInt32] ProcessCount** _(Read)_: The number of instances of the given processes that are currently running. +* **[SInt32] ProcessCount** _(Read)_: The number of instances of the given + processes that are currently running. #### Examples @@ -250,20 +411,51 @@ None #### Parameters -* **[String] Name** _(Key)_: Indicates the service name. This may be different from the service's display name. To retrieve a list of all services with their names and current states, use the Get-Service cmdlet. -* **[String] Ensure** _(Write)_: Indicates whether the service is present or absent. { *Present* | Absent }. -* **[String] Path** _(Write)_: The path to the service executable file. Required when creating a service. The user account specified by BuiltInAccount or Credential must have access to this path in order to start the service. +* **[String] Name** _(Key)_: Indicates the service name. This may be different + from the service's display name. To retrieve a list of all services with + their names and current states, use the Get-Service cmdlet. +* **[String] Ensure** _(Write)_: Indicates whether the service is present or + absent. { *Present* | Absent }. +* **[String] Path** _(Write)_: The path to the service executable file. + Required when creating a service. The user account specified by + BuiltInAccount or Credential must have access to this path in order to start + the service. * **[String] DisplayName** _(Write)_: The display name of the service. * **[String] Description** _(Write)_: The description of the service. -* **[String[]] Dependencies** _(Write)_: The names of the dependencies of the service. -* **[String] BuiltInAccount** _(Write)_: The built-in account the service should start under. Cannot be specified at the same time as Credential or GroupManagedServiceAccount. The user account specified by this property must have access to the service executable path defined by Path in order to start the service. { LocalService | LocalSystem | NetworkService }. -* **[String] GroupManagedServiceAccount** _(Write)_: The Group Managed Service Account the service should start under. Cannot be specified at the same time as Credential or BuiltinAccount. The user account specified by this property must have access to the service executable path defined by Path in order to start the service. When specified in a DOMAIN\User$ form, remember to also input the trailing dollar sign. Get-TargetResource outputs the name of the user to the BuiltinAccount property. -* **[PSCredential] Credential** _(Write)_: The credential of the user account the service should start under. Cannot be specified at the same time as BuiltInAccount or GroupManagedServiceAccount. The user specified by this credential will automatically be granted the Log on as a Service right. The user account specified by this property must have access to the service executable path defined by Path in order to start the service. Get-TargetResource outputs the name of the user to the BuiltinAccount property. -* **[Boolean] DesktopInteract** _(Write)_: Indicates whether or not the service should be able to communicate with a window on the desktop. Must be false for services not running as LocalSystem. The default value is False. -* **[String] StartupType** _(Write)_: The startup type of the service. { Automatic | Disabled | Manual }. If StartupType is "Disabled" and Service is not installed the resource will complete as being DSC compliant. -* **[String] State** _(Write)_: The state of the service. { *Running* | Stopped | Ignore }. -* **[Uint32] StartupTimeout** _(Write)_: The time to wait for the service to start in milliseconds. Defaults to 30000 (30 seconds). -* **[Uint32] TerminateTimeout** _(Write)_: The time to wait for the service to stop in milliseconds. Defaults to 30000 (30 seconds). +* **[String[]] Dependencies** _(Write)_: The names of the dependencies of the + service. +* **[String] BuiltInAccount** _(Write)_: The built-in account the service + should start under. Cannot be specified at the same time as Credential or + GroupManagedServiceAccount. The user account specified by this property must + have access to the service executable path defined by Path in order to start + the service. { LocalService | LocalSystem | NetworkService }. +* **[String] GroupManagedServiceAccount** _(Write)_: The Group Managed Service + Account the service should start under. Cannot be specified at the same time + as Credential or BuiltinAccount. The user account specified by this property + must have access to the service executable path defined by Path in order to + start the service. When specified in a DOMAIN\User$ form, remember to also + input the trailing dollar sign. Get-TargetResource outputs the name of the + user to the BuiltinAccount property. +* **[PSCredential] Credential** _(Write)_: The credential of the user account + the service should start under. Cannot be specified at the same time as + BuiltInAccount or GroupManagedServiceAccount. The user specified by this + credential will automatically be granted the Log on as a Service right. The + user account specified by this property must have access to the service + executable path defined by Path in order to start the service. + Get-TargetResource outputs the name of the user to the BuiltinAccount + property. +* **[Boolean] DesktopInteract** _(Write)_: Indicates whether or not the service + should be able to communicate with a window on the desktop. Must be false for + services not running as LocalSystem. The default value is False. +* **[String] StartupType** _(Write)_: The startup type of the service. + { Automatic | Disabled | Manual }. If StartupType is "Disabled" and Service + is not installed the resource will complete as being DSC compliant. +* **[String] State** _(Write)_: The state of the service. + { *Running* | Stopped | Ignore }. +* **[Uint32] StartupTimeout** _(Write)_: The time to wait for the service to + start in milliseconds. Defaults to 30000 (30 seconds). +* **[Uint32] TerminateTimeout** _(Write)_: The time to wait for the service to + stop in milliseconds. Defaults to 30000 (30 seconds). #### Read-Only Properties from Get-TargetResource @@ -277,8 +469,10 @@ None * [Update startup type for a service, and ignoring the current state](https://github.com/PowerShell/xPSDesiredStateConfiguration/blob/dev/Examples/xService_UpdateStartupTypeIgnoreStateConfig.ps1) ### xServiceSet -Provides a mechanism to configure and manage multiple xService resources with common settings but different names. -This resource can only modify or delete existing services. It cannot create services. + +Provides a mechanism to configure and manage multiple xService resources with +common settings but different names. This resource can only modify or delete +existing services. It cannot create services. #### Requirements @@ -286,15 +480,29 @@ None #### Parameters -* **[String[]] Name** _(Key)_: The names of the services to modify or delete. This may be different from the service's display name. To retrieve a list of all services with their names and current states, use the Get-Service cmdlet. +* **[String[]] Name** _(Key)_: The names of the services to modify or delete. + This may be different from the service's display name. To retrieve a list of + all services with their names and current states, use the Get-Service cmdlet. The following parameters will be the same for each service in the set: -* **[String] Ensure** _(Write)_: Indicates whether the services are present or absent. { *Present* | Absent }. -* **[String] BuiltInAccount** _(Write)_: The built-in account the services should start under. Cannot be specified at the same time as Credential. The user account specified by this property must have access to the service executable paths in order to start the services. { LocalService | LocalSystem | NetworkService }. -* **[PSCredential] Credential** _(Write)_: The credential of the user account the services should start under. Cannot be specified at the same time as BuiltInAccount. The user specified by this credential will automatically be granted the Log on as a Service right. The user account specified by this property must have access to the service executable paths in order to start the services. -* **[String] StartupType** _(Write)_: The startup type of the services. { Automatic | Disabled | Manual }. -* **[String] State** _(Write)_: The state the services. { *Running* | Stopped | Ignore }. +* **[String] Ensure** _(Write)_: Indicates whether the services are present or + absent. { *Present* | Absent }. +* **[String] BuiltInAccount** _(Write)_: The built-in account the services + should start under. Cannot be specified at the same time as Credential. The + user account specified by this property must have access to the service + executable paths in order to start the services. + { LocalService | LocalSystem | NetworkService }. +* **[PSCredential] Credential** _(Write)_: The credential of the user account + the services should start under. Cannot be specified at the same time as + BuiltInAccount. The user specified by this credential will automatically be + granted the Log on as a Service right. The user account specified by this + property must have access to the service executable paths in order to start + the services. +* **[String] StartupType** _(Write)_: The startup type of the services. + { Automatic | Disabled | Manual }. +* **[String] State** _(Write)_: The state the services. + { *Running* | Stopped | Ignore }. #### Read-Only Properties from Get-TargetResource @@ -307,16 +515,25 @@ None ### xRemoteFile -* **DestinationPath**: Path where the remote file should be downloaded. Required. -* **Uri**: URI of the file which should be downloaded. It must be a HTTP, HTTPS or FILE resource. Required. +* **DestinationPath**: Path where the remote file should be downloaded. + Required. +* **Uri**: URI of the file which should be downloaded. It must be a HTTP, HTTPS + or FILE resource. Required. * **UserAgent**: User agent for the web request. Optional. * **Headers**: Headers of the web request. Optional. -* **Credential**: Specifies credential of a user which has permissions to send the request. Optional. -* **MatchSource**: Determines whether the remote file should be re-downloaded if file in the DestinationPath was modified locally. Optional. -* **TimeoutSec**: Specifies how long the request can be pending before it times out. Optional. -* **Proxy**: Uses a proxy server for the request, rather than connecting directly to the Internet resource. Should be the URI of a network proxy server (e.g 'http://10.20.30.1'). Optional. -* **ProxyCredential**: Specifies a user account that has permission to use the proxy server that is specified by the Proxy parameter. Optional. -* **Ensure**: Says whether DestinationPath exists on the machine. It's a read only property. +* **Credential**: Specifies credential of a user which has permissions to send + the request. Optional. +* **MatchSource**: Determines whether the remote file should be re-downloaded + if file in the DestinationPath was modified locally. Optional. +* **TimeoutSec**: Specifies how long the request can be pending before it times + out. Optional. +* **Proxy**: Uses a proxy server for the request, rather than connecting + directly to the Internet resource. Should be the URI of a network proxy + server (e.g 'http://10.20.30.1'). Optional. +* **ProxyCredential**: Specifies a user account that has permission to use the + proxy server that is specified by the Proxy parameter. Optional. +* **Ensure**: Says whether DestinationPath exists on the machine. It's a read + only property. #### Examples @@ -330,22 +547,31 @@ None * **Path**: The source path of the package. * **ProductId**: The product ID of the package (usually a GUID). * **Arguments**: Command line arguments passed on the installation command line. - - When installing MSI packages, the `/quiet` and `/norestart` arguments are automatically applied. + * When installing MSI packages, the `/quiet` and `/norestart` arguments are + automatically applied. * **Credential**: PSCredential needed to access Path. -* **ReturnCode**: An array of return codes that are returned after a successful installation. +* **ReturnCode**: An array of return codes that are returned after a successful + installation. * **LogPath**: The destination path of the log. * **FileHash**: The hash that should match the hash of the package file. * **HashAlgorithm**: The algorithm to use to get the hash of the package file. - - Supported values: SHA1, SHA256, SHA384, SHA512, MD5, RIPEMD160 -* **SignerSubject**: The certificate subject that should match that of the package file's signing certificate. -* **SignerThumbprint**: The certificate thumbprint that should match that of the package file's signing certificate. -* **ServerCertificateValidationCallback**: A callback function to validate the server certificate. -* **RunAsCredential**: Credential used to install the package on the local system. + * Supported values: SHA1, SHA256, SHA384, SHA512, MD5, RIPEMD160 +* **SignerSubject**: The certificate subject that should match that of the + package file's signing certificate. +* **SignerThumbprint**: The certificate thumbprint that should match that of + the package file's signing certificate. +* **ServerCertificateValidationCallback**: A callback function to validate the + server certificate. +* **RunAsCredential**: Credential used to install the package on the local + system. * **CreateCheckRegValue**: If a registry value should be created. -* **InstalledCheckRegHive**: The hive in which to create the registry key. Defaults to 'LocalMachine'. { LocalMachine | CurrentUser } -* **InstalledCheckRegKey**: That path in the registry where the value should be created. +* **InstalledCheckRegHive**: The hive in which to create the registry key. + Defaults to 'LocalMachine'. { LocalMachine | CurrentUser } +* **InstalledCheckRegKey**: That path in the registry where the value should + be created. * **InstalledCheckRegValueName**: The name of the registry value to create. -* **InstalledCheckRegValueData**: The data that should be set to the registry value. +* **InstalledCheckRegValueData**: The data that should be set to the registry + value. ### Read-Only Properties from Get-TargetResource @@ -367,12 +593,14 @@ None Creates and registers a new session configuration endpoint. -* **Ensure**: Indicates if the session configuration is **Present** or **Absent**. +* **Ensure**: Indicates if the session configuration is **Present** or + **Absent**. * **Name**: Specifies the name of the session configuration. * **StartupScript**: Specifies the startup script for the configuration. Enter the fully qualified path of a Windows PowerShell script. * **RunAsCredential**: Specifies the credential for commands of this session - configuration. By default, commands run with the permissions of the current user. + configuration. By default, commands run with the permissions of the current + user. * **SecurityDescriptorSDDL**: Specifies the Security Descriptor Definition Language (SDDL) string for the configuration. This string determines the permissions that are required to use the new session configuration. To use a @@ -403,24 +631,40 @@ None #### Parameters -* **[String] ProductId** _(Key)_: The identifying number used to find the package, usually a GUID. -* **[String] Path** _(Required)_: The path to the MSI file that should be installed or uninstalled. -* **[String] Ensure** _(Write)_: Specifies whether or not the MSI file should be installed or not. To install the MSI file, specify this property as Present. To uninstall the .msi file, specify this property as Absent. The default value is Present. { *Present* | Absent }. -* **[String] Arguments** _(Write)_: The arguments to be passed to the MSI package during installation or uninstallation if needed. -* **[System.Management.Automation.PSCredential] Credential** _(Write)_: The credential of a user account to be used to mount a UNC path if needed. -* **[String] LogPath** _(Write)_: The path to the log file to log the output from the MSI execution. -* **[String] FileHash** _(Write)_: The expected hash value of the MSI file at the given path. -* **[String] HashAlgorithm** _(Write)_: The algorithm used to generate the given hash value. -* **[String] SignerSubject** _(Write)_: The subject that should match the signer certificate of the digital signature of the MSI file. -* **[String] SignerThumbprint** _(Write)_: The certificate thumbprint that should match the signer certificate of the digital signature of the MSI file. -* **[String] ServerCertificateValidationCallback** _(Write)_: PowerShell code that should be used to validate SSL certificates for paths using HTTPS. -* **[System.Management.Automation.PSCredential] RunAsCredential** _(Write)_: The credential of a user account under which to run the installation or uninstallation of the MSI package. +* **[String] ProductId** _(Key)_: The identifying number used to find the + package, usually a GUID. +* **[String] Path** _(Required)_: The path to the MSI file that should be + installed or uninstalled. +* **[String] Ensure** _(Write)_: Specifies whether or not the MSI file should + be installed or not. To install the MSI file, specify this property as + Present. To uninstall the .msi file, specify this property as Absent. The + default value is Present. { *Present* | Absent }. +* **[String] Arguments** _(Write)_: The arguments to be passed to the MSI + package during installation or uninstallation if needed. +* **[System.Management.Automation.PSCredential] Credential** _(Write)_: The + credential of a user account to be used to mount a UNC path if needed. +* **[String] LogPath** _(Write)_: The path to the log file to log the output + from the MSI execution. +* **[String] FileHash** _(Write)_: The expected hash value of the MSI file at + the given path. +* **[String] HashAlgorithm** _(Write)_: The algorithm used to generate the + given hash value. +* **[String] SignerSubject** _(Write)_: The subject that should match the + signer certificate of the digital signature of the MSI file. +* **[String] SignerThumbprint** _(Write)_: The certificate thumbprint that + should match the signer certificate of the digital signature of the MSI file. +* **[String] ServerCertificateValidationCallback** _(Write)_: PowerShell code + that should be used to validate SSL certificates for paths using HTTPS. +* **[System.Management.Automation.PSCredential] RunAsCredential** _(Write)_: + The credential of a user account under which to run the installation or + uninstallation of the MSI package. #### Read-Only Properties from Get-TargetResource * **[String] Name** _(Read)_: The display name of the MSI package. * **[String] InstallSource** _(Read)_: The path to the MSI package. -* **[String] InstalledOn** _(Read)_: The date that the MSI package was installed on or serviced on, whichever is later. +* **[String] InstalledOn** _(Read)_: The date that the MSI package was + installed on or serviced on, whichever is later. * **[UInt32] Size** _(Read)_: The size of the MSI package in MB. * **[String] Version** _(Read)_: The version number of the MSI package. * **[String] PackageDescription** _(Read)_: The description of the MSI package. @@ -437,7 +681,8 @@ None * **DestinationPath**: Path where the local file should be uploaded. * **SourcePath**: Path to the local file which should be uploaded. * **Credential**: PSCredential for the user with access to DestinationPath. -* **CertificateThumbprint**: Thumbprint of the certificate which should be used for encryption/decryption. +* **CertificateThumbprint**: Thumbprint of the certificate which should be used + for encryption/decryption. #### Examples @@ -445,7 +690,8 @@ None ### xEnvironment -Provides a mechanism to configure and manage environment variables for a machine or process. +Provides a mechanism to configure and manage environment variables for a +machine or process. #### Requirements @@ -453,11 +699,24 @@ None #### Parameters -* **[String] Name** _(Key)_: The name of the environment variable to create, modify, or remove. -* **[String] Value** _(Write)_: The desired value for the environment variable. The default value is an empty string which either indicates that the variable should be removed entirely or that the value does not matter when testing its existence. Multiple entries can be entered and separated by semicolons (see [Examples](./Examples)). -* **[String] Ensure** _(Write)_: Specifies if the environment varaible should exist. { *Present* | Absent }. -* **[Boolean] Path** _(Write)_: Indicates whether or not the environment variable is a path variable. If the variable being configured is a path variable, the value provided will be appended to or removed from the existing value, otherwise the existing value will be replaced by the new value. When configured as a Path variable, multiple entries separated by semicolons are ensured to be either present or absent without affecting other Path entries (see [Examples](./Examples)). The default value is False. -* **[String[]] Target** _(Write)_: Indicates the target where the environment variable should be set. { Process | Machine | *Process, Machine* }. +* **[String] Name** _(Key)_: The name of the environment variable to create, + modify, or remove. +* **[String] Value** _(Write)_: The desired value for the environment variable. + The default value is an empty string which either indicates that the variable + should be removed entirely or that the value does not matter when testing its + existence. Multiple entries can be entered and separated by semicolons (see + [Examples](./Examples)). +* **[String] Ensure** _(Write)_: Specifies if the environment varaible should + exist. { *Present* | Absent }. +* **[Boolean] Path** _(Write)_: Indicates whether or not the environment + variable is a path variable. If the variable being configured is a path + variable, the value provided will be appended to or removed from the existing + value, otherwise the existing value will be replaced by the new value. When + configured as a Path variable, multiple entries separated by semicolons are + ensured to be either present or absent without affecting other Path entries + (see [Examples](./Examples)). The default value is False. +* **[String[]] Target** _(Write)_: Indicates the target where the environment + variable should be set. { Process | Machine | *Process, Machine* }. #### Read-Only Properties from Get-TargetResource @@ -471,6 +730,7 @@ None * [Remove an environment variable](https://github.com/PowerShell/xPSDesiredStateConfiguration/blob/dev/Examples/xEnvironment_RemoveVariableConfig.ps1) xEnvironment_AddMultiplePaths + ### xScript Provides a mechanism to run PowerShell script blocks on a target node. @@ -482,10 +742,26 @@ None #### Parameters -* **[String] GetScript** _(Key)_: A string that can be used to create a PowerShell script block that retrieves the current state of the resource. This script block runs when the Get-DscConfiguration cmdlet is called. This script block should return a hash table containing one key named Result with a string value. -* **[String] SetScript** _(Key)_: A string that can be used to create a PowerShell script block that sets the resource to the desired state. This script block runs conditionally when the Start-DscConfiguration cmdlet is called. The TestScript script block will run first. If the TestScript block returns False, this script block will run. If the TestScript block returns True, this script block will not run. This script block should not return. -* **[String] TestScript** _(Key)_: A string that can be used to create a PowerShell script block that validates whether or not the resource is in the desired state. This script block runs when the Start-DscConfiguration cmdlet is called or when the Test-DscConfiguration cmdlet is called. This script block should return a boolean with True meaning that the resource is in the desired state and False meaning that the resource is not in the desired state. -* **[PSCredential] Credential** _(Write)_: The credential of the user account to run the script under if needed. +* **[String] GetScript** _(Key)_: A string that can be used to create a + PowerShell script block that retrieves the current state of the resource. + This script block runs when the Get-DscConfiguration cmdlet is called. This + script block should return a hash table containing one key named Result with + a string value. +* **[String] SetScript** _(Key)_: A string that can be used to create a + PowerShell script block that sets the resource to the desired state. This + script block runs conditionally when the Start-DscConfiguration cmdlet is + called. The TestScript script block will run first. If the TestScript block + returns False, this script block will run. If the TestScript block returns + True, this script block will not run. This script block should not return. +* **[String] TestScript** _(Key)_: A string that can be used to create a + PowerShell script block that validates whether or not the resource is in the + desired state. This script block runs when the Start-DscConfiguration cmdlet + is called or when the Test-DscConfiguration cmdlet is called. This script + block should return a boolean with True meaning that the resource is in the + desired state and False meaning that the resource is not in the desired + state. +* **[PSCredential] Credential** _(Write)_: The credential of the user account + to run the script under if needed. #### Read-Only Properties from Get-TargetResource @@ -505,13 +781,29 @@ None #### Parameters -* **[String] Key** _(Key)_: The path of the registry key to add, modify, or remove. This path must include the registry hive/drive (e.g. HKEY_LOCAL_MACHINE, HKLM:). -* **[String] ValueName** _(Key)_: The name of the registry value. To add or remove a registry key, specify this property as an empty string without specifying ValueType or ValueData. To modify or remove the default value of a registry key, specify this property as an empty string while also specifying ValueType or ValueData. -* **[String] Ensure** _(Write)_: Specifies whether or not the registry key or value should exist. To add or modify a registry key or value, set this property to Present. To remove a registry key or value, set this property to Absent. { *Present* | Absent }. -* **[String] ValueData** _(Write)_: The data the specified registry key value should have as a string or an array of strings (MultiString only). -* **[String] ValueType** _(Write)_: The type the specified registry key value should have. { *String* | Binary | DWord | QWord | MultiString | ExpandString } -* **[Boolean] Hex** _(Write)_: Specifies whether or not the specified DWord or QWord registry key data is provided in a hexadecimal format. Not valid for types other than DWord and QWord. The default value is $false. -* **[Boolean] Force** _(Write)_: Specifies whether or not to overwrite the specified registry key value if it already has a value or whether or not to delete a registry key that has subkeys. The default value is $false. +* **[String] Key** _(Key)_: The path of the registry key to add, modify, or + remove. This path must include the registry hive/drive (e.g. + HKEY_LOCAL_MACHINE, HKLM:). +* **[String] ValueName** _(Key)_: The name of the registry value. To add or + remove a registry key, specify this property as an empty string without + specifying ValueType or ValueData. To modify or remove the default value of a + registry key, specify this property as an empty string while also specifying + ValueType or ValueData. +* **[String] Ensure** _(Write)_: Specifies whether or not the registry key or + value should exist. To add or modify a registry key or value, set this + property to Present. To remove a registry key or value, set this property to + Absent. { *Present* | Absent }. +* **[String] ValueData** _(Write)_: The data the specified registry key value + should have as a string or an array of strings (MultiString only). +* **[String] ValueType** _(Write)_: The type the specified registry key value + should have. + { *String* | Binary | DWord | QWord | MultiString | ExpandString } +* **[Boolean] Hex** _(Write)_: Specifies whether or not the specified DWord or + QWord registry key data is provided in a hexadecimal format. Not valid for + types other than DWord and QWord. The default value is $false. +* **[Boolean] Force** _(Write)_: Specifies whether or not to overwrite the + specified registry key value if it already has a value or whether or not to + delete a registry key that has subkeys. The default value is $false. #### Read-Only Properties from Get-TargetResource @@ -534,25 +826,38 @@ None #### Parameters -* **[String] UserName** _(Key)_: Indicates the account name for which you want to ensure a specific state. -* **[String] Description** _(Write)_: Indicates the description you want to use for the user account. -* **[Boolean] Disabled** _(Write)_: Indicates if the account is enabled. Set this property to $true to ensure that this account is disabled, and set it to $false to ensure that it is enabled. - - Suported values: $true, $false - - Default value: $false +* **[String] UserName** _(Key)_: Indicates the account name for which you want + to ensure a specific state. +* **[String] Description** _(Write)_: Indicates the description you want to use + for the user account. +* **[Boolean] Disabled** _(Write)_: Indicates if the account is enabled. Set + this property to $true to ensure that this account is disabled, and set it to + $false to ensure that it is enabled. + * Suported values: $true, $false + * Default value: $false * **[String] Ensure** _(Write)_: Ensures that the feature is present or absent. - - Supported values: Present, Absent - - Default Value: Present -* **[String] FullName** _(Write)_: Represents a string with the full name you want to use for the user account. -* **[PSCredential] Password** _(Write)_: Indicates the password you want to use for this account. -* **[Boolean] PasswordChangeNotAllowed** _(Write)_: Indicates if the user can change the password. Set this property to $true to ensure that the user cannot change the password, and set it to $false to allow the user to change the password. - - Suported values: $true, $false - - Default value: $false -* **[Boolean] PasswordChangeRequired** _(Write)_: Indicates if the user must change the password at the next sign in. Set this property to $true if the user must change the password. - - Suported values: $true, $false - - Default value: $true -* **[Boolean] PasswordNeverExpires** _(Write)_: Indicates if the password will expire. To ensure that the password for this account will never expire, set this property to $true, and set it to $false if the password will expire. - - Suported values: $true, $false - - Default value: $false + * Supported values: Present, Absent + * Default Value: Present +* **[String] FullName** _(Write)_: Represents a string with the full name you + want to use for the user account. +* **[PSCredential] Password** _(Write)_: Indicates the password you want to use + for this account. +* **[Boolean] PasswordChangeNotAllowed** _(Write)_: Indicates if the user can + change the password. Set this property to $true to ensure that the user + cannot change the password, and set it to $false to allow the user to change + the password. + * Suported values: $true, $false + * Default value: $false +* **[Boolean] PasswordChangeRequired** _(Write)_: Indicates if the user must + change the password at the next sign in. Set this property to $true if the + user must change the password. + * Suported values: $true, $false + * Default value: $true +* **[Boolean] PasswordNeverExpires** _(Write)_: Indicates if the password will + expire. To ensure that the password for this account will never expire, set + this property to $true, and set it to $false if the password will expire. + * Suported values: $true, $false + * Default value: $false #### Examples @@ -562,7 +867,8 @@ None ### xWindowsFeature -Provides a mechanism to install or uninstall Windows roles or features on a target node. +Provides a mechanism to install or uninstall Windows roles or features on a +target node. #### Requirements @@ -572,15 +878,24 @@ Provides a mechanism to install or uninstall Windows roles or features on a targ #### Parameters -* **[String] Name** _(Key)_: Indicates the name of the role or feature that you want to ensure is added or removed. This is the same as the Name property from the Get-WindowsFeature cmdlet, and not the display name of the role or feature. -* **[PSCredential] Credential** _(Write)_: Indicates the credential to use to add or remove the role or feature if needed. -* **[String] Ensure** _(Write)_: Specifies whether the feature should be installed (Present) or uninstalled (Absent) { *Present* | Absent }. -* **[Boolean] IncludeAllSubFeature** _(Write)_: Set this property to $true to ensure the state of all required subfeatures with the state of the feature you specify with the Name property. The default value is $false. -* **[String] LogPath** _(Write)_: Indicates the path to a log file to log the operation. +* **[String] Name** _(Key)_: Indicates the name of the role or feature that you + want to ensure is added or removed. This is the same as the Name property + from the Get-WindowsFeature cmdlet, and not the display name of the role or + feature. +* **[PSCredential] Credential** _(Write)_: Indicates the credential to use to + add or remove the role or feature if needed. +* **[String] Ensure** _(Write)_: Specifies whether the feature should be + installed (Present) or uninstalled (Absent) { *Present* | Absent }. +* **[Boolean] IncludeAllSubFeature** _(Write)_: Set this property to $true to + ensure the state of all required subfeatures with the state of the feature + you specify with the Name property. The default value is $false. +* **[String] LogPath** _(Write)_: Indicates the path to a log file to log the + operation. #### Read-Only Properties from Get-TargetResource -* **[String] DisplayName** _(Read)_: The display name of the retrieved role or feature. +* **[String] DisplayName** _(Read)_: The display name of the retrieved role or + feature. #### Examples @@ -591,7 +906,8 @@ Provides a mechanism to install or uninstall Windows roles or features on a targ ### xWindowsFeatureSet -Provides a mechanism to configure and manage multiple xWindowsFeature resources on a target node. +Provides a mechanism to configure and manage multiple xWindowsFeature resources +on a target node. #### Requirements @@ -601,15 +917,29 @@ Provides a mechanism to configure and manage multiple xWindowsFeature resources #### Parameters -* **[String] Name** _(Key)_: The names of the roles or features to install or uninstall. This may be different from the display name of the feature/role. To retrieve the names of features/roles on a machine use the Get-WindowsFeature cmdlet. -* **[String] Ensure** _(Write)_: Specifies whether the feature should be installed or uninstalled. To install features, set this property to Present. To uninstall features, set this property to Absent. { Present | Absent }. -* **[Boolean] IncludeAllSubFeature** _(Write)_: Specifies whether or not all subfeatures should be installed or uninstalled alongside the specified roles or features. If this property is true and Ensure is set to Present, all subfeatures will be installed. If this property is false and Ensure is set to Present, subfeatures will not be installed or uninstalled. If Ensure is set to Absent, all subfeatures will be uninstalled. -* **[PSCredential] Credential** _(Write)_: The credential of the user account under which to install or uninstall the roles or features. -* **[String] LogPath** _(Write)_: The custom file path to which to log this operation. If not passed in, the default log path will be used (%windir%\logs\ServerManager.log). +* **[String] Name** _(Key)_: The names of the roles or features to install or + uninstall. This may be different from the display name of the feature/role. + To retrieve the names of features/roles on a machine use the + Get-WindowsFeature cmdlet. +* **[String] Ensure** _(Write)_: Specifies whether the feature should be + installed or uninstalled. To install features, set this property to Present. + To uninstall features, set this property to Absent. { Present | Absent }. +* **[Boolean] IncludeAllSubFeature** _(Write)_: Specifies whether or not all + subfeatures should be installed or uninstalled alongside the specified roles + or features. If this property is true and Ensure is set to Present, all + subfeatures will be installed. If this property is false and Ensure is set to + Present, subfeatures will not be installed or uninstalled. If Ensure is set + to Absent, all subfeatures will be uninstalled. +* **[PSCredential] Credential** _(Write)_: The credential of the user account + under which to install or uninstall the roles or features. +* **[String] LogPath** _(Write)_: The custom file path to which to log this + operation. If not passed in, the default log path will be used + (%windir%\logs\ServerManager.log). #### Read-Only Properties from Get-TargetResource -* **[String] DisplayName** _(Read)_: The display names of the retrieved roles or features. +* **[String] DisplayName** _(Read)_: The display names of the retrieved roles + or features. #### Examples @@ -623,23 +953,41 @@ This resource works on Nano Server. #### Requirements -* Target machine must be running a Windows client operating system, Windows Server 2012 or later, or Nano Server. +* Target machine must be running a Windows client operating system, Windows + Server 2012 or later, or Nano Server. * Target machine must have access to the DISM PowerShell module. #### Parameters -* **[String] Name** _(Key)_: The name of the Windows optional feature to enable or disable. -* **[String] Ensure** _(Write)_: Specifies whether the feature should be enabled or disabled. To enable the feature, set this property to Present. To disable the feature, set the property to Absent. The default value is Present. { *Present* | Absent }. -* **[Boolean] RemoveFilesOnDisable** _(Write)_: Specifies that all files associated with the feature should be removed if the feature is being disabled. -* **[Boolean] NoWindowsUpdateCheck** _(Write)_: Specifies whether or not DISM contacts Windows Update (WU) when searching for the source files to enable the feature. If $true, DISM will not contact WU. -* **[String] LogPath** _(Write)_: The path to the log file to log this operation. There is no default value, but if not set, the log will appear at %WINDIR%\Logs\Dism\dism.log. -* **[String] LogLevel** _(Write)_: The maximum output level to show in the log. ErrorsOnly will log only errors. ErrorsAndWarning will log only errors and warnings. ErrorsAndWarningAndInformation will log errors, warnings, and debug information). The default value is "ErrorsAndWarningAndInformation". { ErrorsOnly | ErrorsAndWarning | *ErrorsAndWarningAndInformation* }. +* **[String] Name** _(Key)_: The name of the Windows optional feature to enable + or disable. +* **[String] Ensure** _(Write)_: Specifies whether the feature should be + enabled or disabled. To enable the feature, set this property to Present. To + disable the feature, set the property to Absent. The default value is + Present. { *Present* | Absent }. +* **[Boolean] RemoveFilesOnDisable** _(Write)_: Specifies that all files + associated with the feature should be removed if the feature is being + disabled. +* **[Boolean] NoWindowsUpdateCheck** _(Write)_: Specifies whether or not DISM + contacts Windows Update (WU) when searching for the source files to enable + the feature. If $true, DISM will not contact WU. +* **[String] LogPath** _(Write)_: The path to the log file to log this + operation. There is no default value, but if not set, the log will appear at + %WINDIR%\Logs\Dism\dism.log. +* **[String] LogLevel** _(Write)_: The maximum output level to show in the log. + ErrorsOnly will log only errors. ErrorsAndWarning will log only errors and + warnings. ErrorsAndWarningAndInformation will log errors, warnings, and debug + information). The default value is "ErrorsAndWarningAndInformation". + { ErrorsOnly | ErrorsAndWarning | *ErrorsAndWarningAndInformation* }. #### Read-Only Properties from Get-TargetResource -* **[String[]] CustomProperties** _(Read)_: The custom properties retrieved from the Windows optional feature as an array of strings. -* **[String] Description** _(Read)_: The description retrieved from the Windows optional feature. -* **[String] DisplayName** _(Read)_: The display name retrieved from the Windows optional feature. +* **[String[]] CustomProperties** _(Read)_: The custom properties retrieved + from the Windows optional feature as an array of strings. +* **[String] Description** _(Read)_: The description retrieved from the + Windows optional feature. +* **[String] DisplayName** _(Read)_: The display name retrieved from the + Windows optional feature. #### Examples @@ -648,25 +996,36 @@ This resource works on Nano Server. ### xWindowsOptionalFeatureSet -Provides a mechanism to configure and manage multiple xWindowsOptionalFeature resources on a target node. -This resource works on Nano Server. +Provides a mechanism to configure and manage multiple xWindowsOptionalFeature +resources on a target node. This resource works on Nano Server. #### Requirements -* Target machine must be running a Windows client operating system, Windows Server 2012 or later, or Nano Server. +* Target machine must be running a Windows client operating system, Windows + Server 2012 or later, or Nano Server. * Target machine must have access to the DISM PowerShell module. #### Parameters -* **[String[]] Name** _(Key)_: The names of the Windows optional features to enable or disable. - -The following parameters will be the same for each Windows optional feature in the set: - -* **[String] Ensure** _(Write)_: Specifies whether the Windows optional features should be enabled or disabled. To enable the features, set this property to Present. To disable the features, set this property to Absent. { Present | Absent }. -* **[Boolean] RemoveFilesOnDisable** _(Write)_: Specifies whether or not to remove the files associated with the Windows optional features when they are disabled. -* **[Boolean] NoWindowsUpdateCheck** _(Write)_: Specifies whether or not DISM should contact Windows Update (WU) when searching for the source files to restore Windows optional features on an online image. +* **[String[]] Name** _(Key)_: The names of the Windows optional features to + enable or disable. + +The following parameters will be the same for each Windows optional feature in +the set: + +* **[String] Ensure** _(Write)_: Specifies whether the Windows optional + features should be enabled or disabled. To enable the features, set this + property to Present. To disable the features, set this property to Absent. + { Present | Absent }. +* **[Boolean] RemoveFilesOnDisable** _(Write)_: Specifies whether or not to + remove the files associated with the Windows optional features when they are + disabled. +* **[Boolean] NoWindowsUpdateCheck** _(Write)_: Specifies whether or not DISM + should contact Windows Update (WU) when searching for the source files to + restore Windows optional features on an online image. * **[String] LogPath** _(Write)_: The file path to which to log the operation. -* **[String] LogLevel** _(Write)_: The level of detail to include in the log. { ErrorsOnly | ErrorsAndWarning | ErrorsAndWarningAndInformation }. +* **[String] LogLevel** _(Write)_: The level of detail to include in the log. + { ErrorsOnly | ErrorsAndWarning | ErrorsAndWarningAndInformation }. #### Read-Only Properties from Get-TargetResource @@ -678,8 +1037,9 @@ None * [Disable multiple features](https://github.com/PowerShell/xPSDesiredStateConfiguration/blob/dev/Examples/xWindowsOptionalFeatureSet_DisableConfig.ps1) ### xWindowsPackageCab -Provides a mechanism to install or uninstall a package from a windows cabinet (cab) file on a target node. -This resource works on Nano Server. + +Provides a mechanism to install or uninstall a package from a windows cabinet +(cab) file on a target node. This resource works on Nano Server. #### Requirements @@ -688,9 +1048,15 @@ This resource works on Nano Server. #### Parameters * **[String] Name** _(Key)_: The name of the package to install or uninstall. -* **[String] Ensure** _(Required)_: Specifies whether the package should be installed or uninstalled. To install the package, set this property to Present. To uninstall the package, set the property to Absent. { *Present* | Absent }. -* **[String] SourcePath** _(Required)_: The path to the cab file to install or uninstall the package from. -* **[String] LogPath** _(Write)_: The path to a file to log the operation to. There is no default value, but if not set, the log will appear at %WINDIR%\Logs\Dism\dism.log. +* **[String] Ensure** _(Required)_: Specifies whether the package should be + installed or uninstalled. To install the package, set this property to + Present. To uninstall the package, set the property to Absent. { *Present* | + Absent }. +* **[String] SourcePath** _(Required)_: The path to the cab file to install or + uninstall the package from. +* **[String] LogPath** _(Write)_: The path to a file to log the operation to. + There is no default value, but if not set, the log will appear at + %WINDIR%\Logs\Dism\dism.log. #### Read-Only Properties from Get-TargetResource @@ -704,552 +1070,16 @@ None ### Publish-ModuleToPullServer -Publishes a 'ModuleInfo' object(s) to the pullserver module repository or user provided path. It accepts its input from a pipeline so it can be used in conjunction with Get-Module as Get-Module | Publish-Module +Publishes a 'ModuleInfo' object(s) to the pullserver module repository or user +provided path. It accepts its input from a pipeline so it can be used in +conjunction with Get-Module as in 'Get-Module -Name ModuleName' | +Publish-Module ### Publish-MOFToPullServer -Publishes a 'FileInfo' object(s) to the pullserver configuration repository. It accepts FileInfo input from a pipeline so it can be used in conjunction with Get-ChildItem .*.mof | Publish-MOFToPullServer - -## Versions - -### Unreleased - -* xWindowsOptionalFeature - * Suppress useless verbose output from `Import-Module` cmdlet. ([issue 453](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/453)) -* Changes to xRemoteFile - * Corrected a resource name in the example xRemoteFile_DownloadFileConfig.ps1 -* Fix `MSFT_xDSCWebService` to find - `Microsoft.Powershell.DesiredStateConfiguration.Service.Resources.dll` - when server is configured with pt-BR Locales ([issue #284](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/284)). -* Changes to xDSCWebService - * Fixed an issue which prevented the removal of the IIS Application Pool created during deployment of an DSC Pull Server instance. ([issue #464](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/464)) - * Fixed an issue where a Pull Server cannot be deployed on a machine when IIS Express is installed aside a full blown IIS ([issue #191](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/191)) -* Update `CommonResourceHelper` unit tests to meet Pester 4.0.0 - standards ([issue #473](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/473)). -* Update `ResourceHelper` unit tests to meet Pester 4.0.0 - standards ([issue #473](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/473)). -* Update `MSFT_xDSCWebService` unit tests to meet Pester 4.0.0 - standards ([issue #473](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/473)). -* Update `MSFT_xDSCWebService` integration tests to meet Pester 4.0.0 - standards ([issue #473](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/473)). -* Refactored `MSFT_xDSCWebService` integration tests to meet current - standards and to use Pester TestDrive. -* xArchive - * Fix end-to-end tests ([issue #457](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/457)). - * Update integration tests to meet Pester 4.0.0 standards. - * Update end-to-end tests to meet Pester 4.0.0 standards. - * Update unit and integration tests to meet Pester 4.0.0 standards. - * Wrapped all path and identifier strings in verbose messages with - quotes to make it easier to identify the limit of the string when - debugging. - * Refactored date/time checksum code to improve testability and ensure - tests can run on machines with localized datetime formats that are not - US. - * Fix 'Get-ArchiveEntryLastWriteTime' to return `[datetime]` ([issue #471](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/471)). - * Improved verbose logging to make debugging path issues easier. - * Added handling for '/' as a path seperator by backporting code from - PSDscResources - ([issue #469](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/469)). - * Copied unit tests from [PSDscResources](https://github.com/PowerShell/PSDscResources). - * Added .gitattributes file and removed git configuration from AppVeyor - to ensure CRLF settings are configured correctly for the repository. -* Updated '.vscode\settings.json' to refer to AnalyzerSettings.psd1 so that - custom syntax problems are highlighted in Visual Studio Code. -* Fixed style guideline violations in `CommonResourceHelper.psm1`. -* Changes to xService - * Fixes issue where Get-TargetResource or Test-TargetResource will throw an - exception if the target service is configured with a non-existent dependency. - * Refactored Get-TargetResource Unit tests. -* Changes to xPackage - * Fixes an issue where incorrect verbose output was displayed if product found. ([issue #446](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/446)) -* Fixes files which are getting triggered for re-encoding after recent pull - request (possibly #472). - -### 8.5.0.0 - -* Changes to xRegistry - * Fixed an issue that fails to remove reg key when the `Key` is specified as common registry path. ([issue #444](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/444)) -* Changes to xService - * Added support for Group Managed Service Accounts - -### 8.4.0.0 - -* Changes to xPSDesiredStateConfiguration - * Opt-in for the common tests validate module files and script files. - * All files change to encoding UTF-8 (without byte order mark). - * Opt-in for the common test for example validation. - * Added Visual Studio Code workspace settings that helps with formatting - against the style guideline. - * Update all examples for them to be able pass the common test validation. -* xEnvironment path documentation update demonstrating usage with multiple values ([issue #415](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/415). [Alex Kokkinos (@alexkokkinos)](https://github.com/alexkokkinos) -* Changes to xWindowsProcess - * Increased the wait time in the integration tests since the tests - still failed randomly ([issue #420](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/420)). -* Renamed and updated examples to be able to publish them to PowerShell Gallery. - * Sample\_xScript.ps1 → xScript\_WatchFileContentConfig.ps1 - * Sample\_xService\_UpdateStartupTypeIgnoreState.ps1 → xService\_UpdateStartupTypeIgnoreStateConfig.ps1 - * Sample\_xWindowsProcess\_Start.ps1 → xWindowsProcess\_StartProcessConfig.ps1 - * Sample\_xWindowsProcess\_StartUnderUser.ps1 → xWindowsProcess\_StartProcessUnderUserConfig.ps1 - * Sample\_xWindowsProcess\_Stop.ps1 → xWindowsProcess\_StopProcessConfig.ps1 - * Sample\_xWindowsProcess\_StopUnderUser.ps1 → xWindowsProcess\_StopProcessUnderUserConfig.ps1 - * Sample\_xUser\_CreateUser.ps1.ps1 → xUser\_CreateUserConfig.ps1 - * Sample\_xUser\_Generic.ps1.ps1 → xUser\_CreateUserDetailedConfig.ps1 - * Sample\_xWindowsFeature.ps1 → xWindowsFeature\_AddFeatureConfig.ps1 - * Sample\_xWindowsFeatureSet\_Install.ps1 → xWindowsFeatureSet\_AddFeaturesConfig.ps1 - * Sample\_xWindowsFeatureSet\_Uninstall.ps1 → xWindowsFeatureSet\_RemoveFeaturesConfig.ps1 - * Sample\_xRegistryResource\_AddKey.ps1 → xRegistryResource\_AddKeyConfig.ps1 - * Sample\_xRegistryResource\_RemoveKey.ps1 → xRegistryResource\_RemoveKeyConfig.ps1 - * Sample\_xRegistryResource\_AddOrModifyValue.ps1 → xRegistryResource\_AddOrModifyValueConfig.ps1 - * Sample\_xRegistryResource\_RemoveValue.ps1 → xRegistryResource\_RemoveValueConfig.ps1 - * Sample\_xService\_CreateService.ps1 → xService\_CreateServiceConfig.ps1 - * Sample\_xService\_DeleteService.ps1 → xService\_RemoveServiceConfig.ps1 - * Sample\_xServiceSet\_StartServices.ps1 → xServiceSet\_StartServicesConfig.ps1 - * Sample\_xServiceSet\_BuiltInAccount → xServiceSet\_EnsureBuiltInAccountConfig.ps1 - * Sample\_xWindowsPackageCab → xWindowsPackageCab\_InstallPackageConfig - * Sample\_xWindowsOptionalFeature.ps1 → xWindowsOptionalFeature\_EnableConfig.ps1 - * Sample\_xWindowsOptionalFeatureSet\_Enable.ps1 → xWindowsOptionalFeatureSet\_EnableConfig.ps1 - * Sample\_xWindowsOptionalFeatureSet\_Disable.ps1 → xWindowsOptionalFeatureSet\_DisableConfig.ps1 - * Sample\_xRemoteFileUsingProxy.ps1 → xRemoteFile\_DownloadFileUsingProxyConfig.ps1 - * Sample\_xRemoteFile.ps1 → xRemoteFile\_DownloadFileConfig.ps1 - * Sample\_xProcessSet\_Start.ps1 → xProcessSet\_StartProcessConfig.ps1 - * Sample\_xProcessSet\_Stop.ps1 → xProcessSet\_StopProcessConfig.ps1 - * Sample\_xMsiPackage\_UninstallPackageFromHttps.ps1 → xMsiPackage\_UninstallPackageFromHttpsConfig.ps1 - * Sample\_xMsiPackage\_UninstallPackageFromFile.ps1 → xMsiPackage\_UninstallPackageFromFileConfig.ps1 - * Sample\_xMsiPackage\_InstallPackageFromFile → xMsiPackage\_InstallPackageConfig.ps1 - * Sample\_xGroup\_SetMembers.ps1 → xGroup\_SetMembersConfig.ps1 - * Sample\_xGroup\_RemoveMembers.ps1 → xGroup\_RemoveMembersConfig.ps1 - * Sample\_xGroupSet\_AddMembers.ps1 → xGroupSet\_AddMembersConfig.ps1 - * Sample\_xFileUpload.ps1 → xFileUpload\_UploadToSMBShareConfig.ps1 - * Sample\_xEnvironment\_CreateMultiplePathVariables.ps1 → xEnvironment\_AddMultiplePathsConfig.ps1 - * Sample\_xEnvironment\_RemovePathVariables.ps1 → xEnvironment\_RemoveMultiplePathsConfig.ps1 - * Sample\_xEnvironment\_CreateNonPathVariable.ps1 → xEnvironment\_CreateNonPathVariableConfig.ps1 - * Sample\_xEnvironment\_Remove.ps1 → xEnvironment\_RemoveVariableConfig.ps1 - * Sample\_xArchive\_ExpandArchiveChecksumAndForce.ps1 → xArchive\_ExpandArchiveChecksumAndForceConfig.ps1 - * Sample\_xArchive\_ExpandArchiveDefaultValidationAndForce.ps1 → xArchive\_ExpandArchiveDefaultValidationAndForceConfig.ps1 - * Sample\_xArchive\_ExpandArchiveNoValidation.ps1 → xArchive\_ExpandArchiveNoValidationConfig.ps1 - * Sample\_xArchive\_ExpandArchiveNoValidationCredential.ps1 → xArchive\_ExpandArchiveNoValidationCredentialConfig.ps1 - * Sample\_xArchive\_RemoveArchiveChecksum.ps1 → xArchive\_RemoveArchiveChecksumConfig.ps1 - * Sample\_xArchive\_RemoveArchiveNoValidation.ps1 → xArchive\_RemoveArchiveNoValidationConfig.ps1 - * Sample\_InstallExeCreds\_xPackage.ps1 → xPackage\_InstallExeUsingCredentialsConfig.ps1 - * Sample\_InstallExeCredsRegistry\_xPackage.ps1 → xPackage\_InstallExeUsingCredentialsAndRegistryConfig.ps1 - * Sample\_InstallMSI\_xPackage.ps1 → xPackage\_InstallMsiConfig.ps1 - * Sample\_InstallMSIProductId\_xPackage.ps1 → xPackage\_InstallMsiUsingProductIdConfig.ps1 -* New examples - * xUser\_RemoveUserConfig.ps1 - * xWindowsFeature\_AddFeatureUsingCredentialConfig.ps1 - * xWindowsFeature\_AddFeatureWithLogPathConfig.ps1 - * xWindowsFeature\_RemoveFeatureConfig.ps1 - * xService\_ChangeServiceStateConfig.ps1 - * xWindowsOptionalFeature\_DisableConfig.ps1 - * xPSEndpoint\_NewConfig.ps1 - * xPSEndpoint\_NewWithDefaultsConfig.ps1 - * xPSEndpoint\_RemoveConfig.ps1 - * xPSEndpoint\_NewCustomConfig.ps1 -* Removed examples - * Sample\_xPSSessionConfiguration.ps1 - This file was split up in several examples, - those starting with 'xPSEndpoint*'. - * Sample\_xMsiPackage\_InstallPackageFromHttp - This was added to the example - xMsiPackage\_InstallPackageConfig.ps1 so the example sows either URI scheme. - * Sample\_xEnvironment\_CreatePathVariable.ps1 - Same as the new example - xEnvironment\_AddMultiplePaths.ps1 - -### 8.3.0.0 - -* Changes to xPSDesiredStateConfiguration - * README.md: Fixed typo. [Steve Banik (@stevebanik-ndsc)](https://github.com/stevebanik-ndsc) - * Adding a Branches section to the README.md with Codecov badges for both - master and dev branch ([issue #416](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/416)). -* Changes to xWindowsProcess - * Integration tests for this resource should no longer fail randomly. A timing - issue made the tests fail in certain scenarios ([issue #420](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/420)). -* Changes to xDSCWebService - * Added the option to use a certificate based on it's subject and template name instead of it's thumbprint. Resolves [issue #205](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/205). - * xDSCWebService: Fixed an issue where Test-WebConfigModulesSetting would return $true when web.config contains a module and the desired state was for it to be absent. Resolves [issue #418](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/418). -* Updated the main DSCPullServerSetup readme to read easier, then updates the PowerShell comment based help for each function to follow normal help standards. [James Pogran (@jpogran)](https://github.com/jpogran) -* xRemoteFile: Remove progress bar for file download. This resolves issues [#165](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/165) and [#383](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/383) [Claudio Spizzi (@claudiospizzi)](https://github.com/claudiospizzi) - -### 8.2.0.0 - -* xDSCWebService: Disable installing Microsoft.Powershell.Desiredstateconfiguration.Service.Resources.dll as a temporary workaround since the binary is missing on the latest Windows builds - -### 8.1.0.0 - -* xDSCWebService: Enable SQL provider configuration - -### 8.0.0.0 - -* xDSCWebService - * BREAKING CHANGE: The Pull Server will now run in a 64 bit IIS process by default. Enable32BitAppOnWin64 needs to be set to TRUE for the Pull Server to run in a 32 bit process. - -### 7.0.0.0 - -* xService - * BREAKING CHANGE: The service will now return as compliant if the service is not installed and the StartupType is set to Disabled regardless of the value of the Ensure property. -* Fixed misnamed certificate thumbprint variable in example Sample_xDscWebServiceRegistrationWithSecurityBestPractices - -### 6.4.0.0 - -* xGroup: - * Added updates from PSDscResources: - * Added support for domain based group members on Nano server - -### 6.3.0.0 - -* xDSCWebService - * Fixed an issue where all 64bit IIS application pools stop working after installing DSC Pull Server, because IISSelfSignedCertModule(32bit) module was registered without bitness32 precondition. - -### 6.2.0.0 - -* xMsiPackage: - * Created high quality MSI package manager resource -* xArchive: - * Fixed a minor bug in the unit tests where sometimes the incorrect DateTime format was used. -* xWindowsFeatureSet: - * Had the wrong parameter name in one test case. - -### 6.1.0.0 - -* Moved DSC pull server setup tests to DSCPullServerSetup folder for new common tests -* xArchive: - * Updated the resource to be a high quality resource - * Transferred the existing "unit" tests to integration tests - * Added unit and end-to-end tests - * Updated documentation and examples -* xUser - * Fixed error handling in xUser -* xRegistry - * Fixed bug where an error was thrown when running Get-DscConfiguration if the registry key already existed -* Updated Test-IsNanoServer cmdlet to properly test for a Nano server rather than the core version of PowerShell - -### 6.0.0.0 - -* xEnvironment - * Updated resource to follow HQRM guidelines. - * Added examples. - * Added unit and end-to-end tests. - * Significantly cleaned the resource. - * Minor Breaking Change where the resource will now throw an error if no value is provided, Ensure is set to present, and the variable does not exist, whereas before it would create an empty registry key on the machine in this case (if this is the desired outcome then use the Registry resource). - * Added a new Write property 'Target', which specifies whether the user wants to set the machine variable, the process variable, or both (previously it was setting both in most cases). -* xGroup: - * Group members in the "NT Authority", "BuiltIn" and "NT Service" scopes should now be resolved without an error. If you were seeing the errors "Exception calling ".ctor" with "4" argument(s): "Server names cannot contain a space character."" or "Exception calling ".ctor" with "2" argument(s): "Server names cannot contain a space character."", this fix should resolve those errors. If you are still seeing one of the errors, there is probably another local scope we need to add. Please let us know. - * The resource will no longer attempt to resolve group members if Members, MembersToInclude, and MembersToExclude are not specified. - -### 5.2.0.0 - -* xWindowsProcess - * Minor updates to integration tests because one of the tests was flaky. -* xRegistry: - * Added support for forward slashes in registry key names. This resolves issue [#285](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/285). - -### 5.1.0.0 - -* xWindowsFeature: - * Added Catch to ignore RuntimeException when importing ServerManager module. This resolves issue [#69](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/69). - * Updated unit tests. -* xPackage: - * No longer checks for package installation when a reboot is required. This resolves issue [#52](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/52). - * Ensures a space is added to MSI installation arguments. This resolves issue [#195](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/195). - * Adds RunAsCredential parameter to permit installing packages with specific user account. This resolves issue [#221](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/221). - * Fixes null verbose log output error. This resolves issue [#224](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/224). -* xDSCWebService - * Fixed issue where resource would fail to read redirection.config file. This resolves issue [#191] (https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/191) -* xArchive - * Fixed issue where resource would throw exception when file name contains brackets. This resolves issue [#255](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/255). -* xScript - * Cleaned resource for high quality requirements - * Added unit tests - * Added integration tests - * Updated documentation and example -* ResourceSetHelper: - * Updated common functions for all 'Set' resources. - * Added unit tests -* xGroupSet: - * Updated resource to use new ResouceSetHelper functions and added integration tests. -* xGroup: - * Cleaned module imports, fixed PSSA issues, and set ErrorActionPreference to stop. -* xService: - * Cleaned resource functions to enable StrictMode. - * Fixed bug in which Set-TargetResource would create a service when Ensure set to Absent and Path specified. - * Added unit tests. - * Added integration tests for BuiltInAccount and Credential. -* xServiceSet: - * Updated resource to use new ResouceSetHelper functions and added integration tests. - * Updated documentation and example -* xWindowsProcess - * Cleaned resource as per high quality guidelines. - * Added unit tests. - * Added integration tests. - * Updated documentation. - * Updated examples. - * Fixed bug in Get-TargetResource. - * Added a 'Count' value to the hashtable returned by Get-TargetResource so that the user can see how many instances of the process are running. - * Fixed bug in finding the path to the executable. - * Changed name to be xWindowsProcess everywhere. -* xWindowsOptionalFeatureSet - * Updated resource to use new ResouceSetHelper functions and added integration tests. - * Updated documentation and examples -* xWindowsFeatureSet - * Updated resource to use new ResouceSetHelper functions and added integration tests. - * Updated documentation and examples -* xProcessSet - * Updated resource to use new ResouceSetHelper functions and added integration tests. - * Updated documentation and examples -* xRegistry - * Updated resource to be high-quality - * Fixed bug in which the user could not set a Binary registry value to 0 - * Added unit and integration tests - * Added examples and updated documentation - -### 5.0.0.0 - -* xWindowsFeature: - * Cleaned up resource (PSSA issues, formatting, etc.) - * Added/Updated Tests and Examples - * BREAKING CHANGE: Removed the unused Source parameter - * Updated to a high quality resource -* xDSCWebService: - * Add DatabasePath property to specify a custom database path and enable multiple pull server instances on one server. - * Rename UseUpToDateSecuritySettings property to UseSecurityBestPractices. - * Add DisableSecurityBestPractices property to specify items that are excepted from following best practice security settings. -* xGroup: - * Fixed PSSA issues - * Formatting updated as per style guidelines - * Missing comment-based help added for Get-/Set-/Test-TargetResource - * Typos fixed in Unit test script - * Unit test 'Get-TargetResource/Should return hashtable with correct values when group has no members' updated to handle the expected empty Members array correctly - * Added a lot of unit tests - * Cleaned resource -* xUser: - * Fixed PSSA/Style violations - * Added/Updated Tests and Examples -* Added xWindowsPackageCab -* xService: - * Fixed PSSA/Style violations - * Updated Tests - * Added 'Ignore' state - -### 4.0.0.0 - -* xDSCWebService: - * Added setting of enhanced security - * Cleaned up Examples - * Cleaned up pull server verification test -* xProcess: - * Fixed PSSA issues - * Corrected most style guideline issues -* xPSSessionConfiguration: - * Fixed PSSA and style issues - * Renamed internal functions to follow verb-noun formats - * Decorated all functions with comment-based help -* xRegistry: - * Fixed PSSA and style issues - * Renamed internal functions to follow verb-noun format - * Decorated all functions with comment-based help - * Merged with in-box Registry - * Fixed registry key and value removal - * Added unit tests -* xService: - * Added descriptions to MOF file. - * Added additional details to parameters in Readme.md in a format that can be generated from the MOF. - * Added DesktopInteract parameter. - * Added standard help headers to *-TargetResource functions. - * Changed indent/format of all function help headers to be consistent. - * Fixed line length violations. - * Changed localization code so only a single copy of localization strings are required. - * Removed localization strings from inside module file. - * Updated unit tests to use standard test enviroment configuration and header. - * Recreated unit tests to be non-destructive. - * Created integration tests. - * Allowed service to be restarted immediately rather than wait for next LCM run. - * Changed helper function names to valid verb-noun format. - * Removed New-TestService function from MSFT_xServiceResource.TestHelper.psm1 because it should not be used. - * Fixed error calling Get-TargetResource when service does not exist. - * Fixed bug with Get-TargetResource returning StartupType 'Auto' instead of 'Automatic'. - * Converted to HQRM standards. - * Removed obfuscation of exception in Get-Win32ServiceObject function. - * Fixed bug where service start mode would be set to auto when it already was set to auto. - * Fixed error message content when start mode can not be changed. - * Removed shouldprocess from functions as not required. - * Optimized Test-TargetResource and Set-TargetResource by removing repeated calls to Get-Service and Get-CimInstance. - * Added integration test for testing changes to additional service properties as well as changing service binary path. - * Modified Set-TargetResource so that newly created service created with minimal properties and then all additional properties updated (simplification of code). - * Added support for changing Service Description and DisplayName parameters. - * Fixed bug when changing binary path of existing service. -* Removed test log output from repo. -* xWindowsOptionalFeature: - * Cleaned up resource (PSSA issues, formatting, etc.) - * Added example script - * Added integration test - * BREAKING CHANGE: Removed the unused Source parameter - * Updated to a high quality resource -* Removed test log output from repo. -* Removed the prefix MSFT_ from all files and folders of the composite resources in this module -because they were unavailable to Get-DscResource and Import-DscResource. - * xFileUpload - * xGroupSet - * xProcessSet - * xServiceSet - * xWindowsFeatureSet - * xWindowsOptionalFeatureSet - -### 3.13.0.0 - -* Converted appveyor.yml to install Pester from PSGallery instead of from Chocolatey. -* Updated appveyor.yml to use the default image. -* Merged xPackage with in-box Package resource and added tests. -* xPackage: Re-implemented parameters for installation check from registry key value. -* xGroup: - * Fixed Verbose output in Get-MembersAsPrincipals function. - * Fixed bug when credential parameter passed does not contain local or domain context. - * Fixed logic bug in MembersToInclude and MembersToExclude. - * Fixed bug when trying to include the built-in Administrator in Members. - * Fixed bug where Test-TargetResource would check for members when none specified. - * Fix bug in Test-TargetResourceOnFullSKU function when group being set to a single member. - * Fix bug in Set-TargetResourceOnFullSKU function when group being set to a single member. - * Fix bugs in Assert-GroupNameValid to throw correct exception. -* xService - * Updated xService resource to allow empty string for Description parameter. -* Merged xProcess with in-box Process resource and added tests. -* Fixed PSSA issues in xPackageResource. - -### 3.12.0.0 - -* Removed localization for now so that resources can run on non-English systems. - -### 3.11.0.0 - -* xRemoteFile: Added parameters: - - TimeoutSec - - Proxy - - ProxyCredential - Added unit tests. - Corrected Style Guidelines issues. - Added Localization support. - URI parameter supports File://. - Get-TargetResource returns URI parameter. - Fixed logging of error message reported when download fails. - Added new example Sample_xRemoteFileUsingProxy.ps1. -* Examples: Fixed missing newline at end of PullServerSetupTests.ps1. -* xFileUpload: Added PSSA rule suppression attribute. -* xPackageResource: Removed hardcoded ComputerName 'localhost' parameter from Get-WMIObject to eliminate PSSA rule violation. The parameter is not required. -* Added .gitignore to prevent DSCResource.Tests from being commited to repo. -* Updated AppVeyor.yml to use WMF 5 build OS so that latest test methods work. -* Updated xWebService resource to not deploy Devices.mdb if esent provider is used -* Fixed $script:netsh parameter initialization in xWebService resource that was causing CIM exception when EnableFirewall flag was specified. -* xService: - - Fixed a bug where, despite no state specified in the config, the resource test returns false if the service is not running - - Fixed bug in which Automatice StartupType did not match the 'Auto' StartMode in Test-TargetResource. -* xPackage: Fixes bug where CreateCheckRegValue was not being removed when uninstalling packages -* Replaced New-NetFirewallRule cmdlets with netsh as this cmdlet is not available by default on some downlevel OS such as Windows 2012 R2 Core. -* Added the xEnvironment resource -* Added the xWindowsFeature resource -* Added the xScript resource -* Added the xUser resource -* Added the xGroupSet resource -* Added the xProcessSet resource -* Added the xServiceSet resource -* Added the xWindowsFeatureSet resource -* Added the xWindowsOptionalFeatureSet resource -* Merged the in-box Service resource with xService and added tests for xService -* Merged the in-box Archive resource with xArchive and added tests for xArchive -* Merged the in-box Group resource with xGroup and added tests for xGroup - -### 3.10.0.0 - -* **Publish-ModuleToPullServer** -* **Publish-MOFToPullServer** - -### 3.9.0.0 - -* Added more information how to use Publish-DSCModuleAndMof cmdlet and samples -* Removed compliance server samples - -### 3.8.0.0 - -* Added Pester tests to validate pullserver deployement. -* Removed Compliance Server deployment from xWebservice resource. Fixed database provider selection issue depending on OS flavor -* Added Publish-DSCModuleAndMof cmdlet to package DSC modules and mof and publish them on DSC enterprise pull server -* xRemoteFile resource: Added size verification in cache - -### 3.7.0.0 - -* xService: - - Fixed a bug where 'Dependencies' property was not picked up and caused exception when set. -* xWindowsOptionalFeature: - - Fixed bug where Test-TargetResource method always failed. - - Added support for Windows Server 2012 (and later) SKUs. -* Added xRegistry resource - -### 3.6.0.0 - -* Added CreateCheckRegValue parameter to xPackage resource -* Added MatchSource parameter to xRemoteFile resource - -### 3.5.0.0 - -* MSFT_xPackageResource: Added ValidateSet to Get/Set/Test-TargetResource to match MSFT_xPackageResource.schema.mof -* Fixed bug causing xService to throw error when service already exists -* Added StartupTimeout to xService resource -* Removed UTF8 BOM -* Added code for pull server removal - -### 3.4.0.0 - -* Added logging inner exception messages in xArchive and xPackage resources -* Fixed hash calculation in Get-CacheEntry -* Fixed issue with PSDSCComplianceServer returning HTTP Error 401.2 - - -### 3.3.0.0 - -* Add support to xPackage resource for checking different registry hives -* Added support for new registration properties in xDscWebService resource - -### 3.2.0.0 - -* xArchive: - - Fix problems with file names containing square brackets. -* xDSCWebService: - - Fix default culture issue. -* xPackage: - - Security enhancements. - -### 3.0.3.4 - -* Multiple issues addressed - - Corrected output type for Set- and Test-TargetResource functions in xWebSite, xPackage, xArchive, xGroup, xProcess, xService - - xRemoteFile modified to support creating a directory that does not exist when specified, ensuring idempotency. - Also improved error messages. - - xDSCWebService updated so that Get-TargetResource returns the OData Endpoint URL correctly. - - In xWindowsOptionalFeature, fixed Test-TargetResource issue requiring Ensure = True. - + Note: this change requires the previous Ensure values of Enable and Disable to change to Present and Absent - -### 3.0.2.0 - -* Adding following resources: - * xGroup - -### 3.0.1.0 - -* Adding following resources: - * xFileUpload - -### 2.0.0.0 - -* Adding following resources: - * xWindowsProcess - * xService - * xRemoteFile - * xPackage - -### 1.1.0.0 - -* Fix to remove and recreate the SSL bindings when performing a new HTTPS IIS Endpoint setup. -* Fix in the resource module to consume WebSite Name parameter correctly - -### 1.0.0.0 - -* Initial release with the following resources: - * DscWebService +Publishes a 'FileInfo' object(s) to the pullserver configuration repository. It +accepts FileInfo input from a pipeline so it can be used in conjunction with +Get-ChildItem .*.mof | Publish-MOFToPullServer ## Examples @@ -1279,31 +1109,43 @@ Note: this requires a credential. ### Download file from URI, specifying headers and user agent This configuration will download a file from a specific URI to DestinationPath. -The web request will contain specific headers and will be sent using a specified user agent. +The web request will contain specific headers and will be sent using a +specified user agent. ### Upload file to an SMB share -This configuration will upload a file from SourcePath to the remote DestinationPath. -Username and password will be used to access the DestinationPath. +This configuration will upload a file from SourcePath to the remote +DestinationPath. Username and password will be used to access the +DestinationPath. ### Sample1.ps1 installs a package that uses an .exe file -This configuration will install a .exe package, verifying the package using the package name. +This configuration will install a .exe package, verifying the package using the +package name. ### Sample1.ps2 installs a package that uses an .exe file -This configuration will install a .exe package and verify the package using the product ID and package name. +This configuration will install a .exe package and verify the package using the +product ID and package name. ### Sample1.ps3 installs a package that uses an .msi file -This configuration will install a .msi package and verify the package using the product ID and package name and requires credentials to read the share and install the package. +This configuration will install a .msi package and verify the package using the +product ID and package name and requires credentials to read the share and +install the package. ### Sample1.ps4 installs a package that uses an .exe file -This configuration will install a .exe package and verify the package using the product ID and package name and requires credentials to read the share and install the package. It also uses custom registry values to check for the package presence. +This configuration will install a .exe package and verify the package using the +product ID and package name and requires credentials to read the share and +install the package. It also uses custom registry values to check for the +package presence. ### Validate pullserver deployment -If Sample_xDscWebService.ps1 is used to setup a DSC pull and reporting endpoint, the service endpoint can be validated by performing Invoke-WebRequest -URI http://localhost:8080/PSDSCPullServer.svc/$metadata in PowerShell or http://localhost:8080/PSDSCPullServer.svc/ when using InternetExplorer. +If Sample_xDscWebService.ps1 is used to setup a DSC pull and reporting +endpoint, the service endpoint can be validated by performing Invoke-WebRequest +-URI http://localhost:8080/PSDSCPullServer.svc/$metadata in PowerShell or +http://localhost:8080/PSDSCPullServer.svc/ when using InternetExplorer. [Pullserver Validation Pester Tests](DSCPullServerSetup/PullServerDeploymentVerificationTest) diff --git a/ResourceDesignerScripts/GenerateXRemoteFileSchema.ps1 b/ResourceDesignerScripts/GenerateXRemoteFileSchema.ps1 index 6ba3a5ffc..ae9228843 100644 --- a/ResourceDesignerScripts/GenerateXRemoteFileSchema.ps1 +++ b/ResourceDesignerScripts/GenerateXRemoteFileSchema.ps1 @@ -1,10 +1,9 @@ $DestinationPath = New-xDscResourceProperty -Name DestinationPath -Type String -Attribute Key -Description 'Path under which downloaded or copied file should be accessible after operation.' $Uri = New-xDscResourceProperty -Name Uri -Type String -Attribute Required -Description 'Uri of a file which should be copied or downloaded. This parameter supports HTTP and HTTPS values.' $Headers = New-xDscResourceProperty -Name Headers -Type Hashtable[] -Attribute Write -Description 'Headers of the web request.' -$UserAgent = New-xDscResourceProperty -Name UserAgent -Type String -Attribute Write -Description 'User agent for the web request.' +$UserAgent = New-xDscResourceProperty -Name UserAgent -Type String -Attribute Write -Description 'User agent for the web request.' $Ensure = New-xDscResourceProperty -Name Ensure -Type String -Attribute Read -ValidateSet "Present", "Absent" -Description 'Says whether DestinationPath exists on the machine' $Credential = New-xDscResourceProperty -Name Credential -Type PSCredential -Attribute Write -Description 'Specifies a user account that has permission to send the request.' -#$CertificateThumbprint = New-xDscResourceProperty -Name CertificateThumbprint -Type String -Attribute Write -Description 'Digital public key certificate that is used to send the request.' -New-xDscResource -Name MSFT_xRemoteFile -Property @($DestinationPath, $Uri, $Headers, $UserAgent, $Ensure, $Credential, $CertificateThumbprint) -ModuleName xPSDesiredStateConfiguration2 -FriendlyName xRemoteFile +New-xDscResource -Name MSFT_xRemoteFile -Property @($DestinationPath, $Uri, $Headers, $UserAgent, $Ensure, $Credential, $CertificateThumbprint) -ModuleName xPSDesiredStateConfiguration2 -FriendlyName xRemoteFile diff --git a/ResourceDesignerScripts/New-PSSessionConfigurationResource.ps1 b/ResourceDesignerScripts/New-PSSessionConfigurationResource.ps1 index 629a8a217..5b22f12b8 100644 --- a/ResourceDesignerScripts/New-PSSessionConfigurationResource.ps1 +++ b/ResourceDesignerScripts/New-PSSessionConfigurationResource.ps1 @@ -10,9 +10,9 @@ $resProperties = @{ StartupScript = New-xDscResourceProperty -Description 'Path for the startup script. Empty string clears the value'` -Name StartupScriptPath -Type String -Attribute Write Ensure = New-xDscResourceProperty -Description 'Whether to create the endpoint or delete it' ` - -Name Ensure -Type String -Attribute Write -ValidateSet 'Present','Absent' + -Name Ensure -Type String -Attribute Write -ValidateSet @('Present', 'Absent') AccessMode = New-xDscResourceProperty -Description 'Whether the endpoint is remotely accessible or has local access only or no access' ` - -Name AccessMode -Type String -Attribute Write -ValidateSet 'Local','Remote', 'Disabled' + -Name AccessMode -Type String -Attribute Write -ValidateSet @('Local', 'Remote', 'Disabled') } New-xDscResource -Name MSFT_xPSSessionConfiguration -Property $resProperties.Values -Path $home\desktop -ModuleName xPSDesiredStateConfiguration -FriendlyName xPSEndpoint -Force diff --git a/Tests/CommonTestHelper.psm1 b/Tests/CommonTestHelper.psm1 index 7defa6e04..b897c7a55 100644 --- a/Tests/CommonTestHelper.psm1 +++ b/Tests/CommonTestHelper.psm1 @@ -551,7 +551,7 @@ function Test-IsFileLocked try { - $content = Get-Content -Path $Path + Get-Content -Path $Path | Out-Null return $false } catch @@ -674,13 +674,13 @@ function Get-AppVeyorAdministratorCredential while ($password.Length -lt $passwordLength) { - $password = $password + [Char]$randomGenerator.Next(45, 126) + $password = $password + [Char] $randomGenerator.Next(45, 126) } # Change password $appVeyorAdministratorUsername = 'appveyor' - $appVeyorAdministratorUser = [ADSI]("WinNT://$($env:computerName)/$appVeyorAdministratorUsername") + $appVeyorAdministratorUser = [ADSI] ("WinNT://$($env:computerName)/$appVeyorAdministratorUsername") $null = $appVeyorAdministratorUser.SetPassword($password) [Microsoft.Win32.Registry]::SetValue('HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon', 'DefaultPassword', $password) @@ -789,6 +789,90 @@ function Exit-DscResourceTestEnvironment Restore-TestEnvironment -TestEnvironment $TestEnvironment } +<# + .SYNOPSIS + Returns $true if the the environment variable APPVEYOR is set to $true, + and the environment variable CONFIGURATION is set to the value passed + in the parameter Type. + + .PARAMETER Name + Name of the test script that is called. Defaults to the name of the + calling script. + + .PARAMETER Type + Type of tests in the test file. Can be set to Unit or Integration. +#> +function Test-SkipContinuousIntegrationTask +{ + [OutputType([System.Boolean])] + [CmdletBinding()] + param + ( + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $Name = $MyInvocation.PSCommandPath.Split('\')[-1], + + [Parameter(Mandatory = $true)] + [ValidateSet('Unit', 'Integration')] + [System.String] + $Type + ) + + $result = $false + + if ($env:APPVEYOR -eq $true -and $env:CONFIGURATION -ne $Type) + { + Write-Verbose -Message ('{1} tests in {0} will be skipped unless $env:CONFIGURATION is set to ''{1}''.' -f $Name, $Type) -Verbose + $result = $true + } + + return $result +} + +<# + .SYNOPSIS + Verifies that the specified Windows Feature exists and is installed + on the local machine. + + .PARAMETER Name + The name of the Windows Feature to verify installation of. +#> +function Install-WindowsFeatureAndVerify +{ + [OutputType([System.Boolean])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Name + ) + + $featureInstalled = $true + + $targetFeature = Get-WindowsFeature -Name $Name -ErrorAction SilentlyContinue + + if ($null -eq $targetFeature) + { + Write-Warning -Message "Unable to find Windows Feature '$Name'." + $featureInstalled = $false + } + elseif (!$targetFeature.Installed) + { + $installResult = Install-WindowsFeature -Name $Name + + if (!$installResult.Success) + { + Write-Error -Message "Failed to install Windows Feature '$Name'." + $featureInstalled = $false + } + } + + return $featureInstalled +} + Export-ModuleMember -Function @( 'Test-GetTargetResourceResult', ` 'Wait-ScriptBlockReturnTrue', ` @@ -801,5 +885,7 @@ Export-ModuleMember -Function @( 'Invoke-SetTargetResourceUnitTest', ` 'Invoke-TestTargetResourceUnitTest', ` 'Invoke-ExpectedMocksAreCalledTest', ` - 'Invoke-GenericUnitTest' + 'Invoke-GenericUnitTest', + 'Test-SkipContinuousIntegrationTask', + 'Install-WindowsFeatureAndVerify' ) diff --git a/Tests/Integration/MSFT_xArchive.EndToEnd.Tests.ps1 b/Tests/Integration/MSFT_xArchive.EndToEnd.Tests.ps1 index b47815e87..b181f5e36 100644 --- a/Tests/Integration/MSFT_xArchive.EndToEnd.Tests.ps1 +++ b/Tests/Integration/MSFT_xArchive.EndToEnd.Tests.ps1 @@ -1,13 +1,18 @@ $errorActionPreference = 'Stop' Set-StrictMode -Version 'Latest' +# Import CommonTestHelper for Enter-DscResourceTestEnvironment, Exit-DscResourceTestEnvironment +$testsFolderFilePath = Split-Path $PSScriptRoot -Parent +$commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' +Import-Module -Name $commonTestHelperFilePath + +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + Describe 'xArchive End to End Tests' { BeforeAll { - # Import CommonTestHelper for Enter-DscResourceTestEnvironment, Exit-DscResourceTestEnvironment - $testsFolderFilePath = Split-Path $PSScriptRoot -Parent - $commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' - Import-Module -Name $commonTestHelperFilePath - $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xArchive' ` diff --git a/Tests/Integration/MSFT_xArchive.Integration.Tests.ps1 b/Tests/Integration/MSFT_xArchive.Integration.Tests.ps1 index 6006eaad8..d423c2d8d 100644 --- a/Tests/Integration/MSFT_xArchive.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xArchive.Integration.Tests.ps1 @@ -1,6 +1,14 @@ $errorActionPreference = 'Stop' Set-StrictMode -Version 'Latest' +Import-Module -Name (Join-Path -Path (Split-Path $PSScriptRoot -Parent) ` + -ChildPath 'CommonTestHelper.psm1') + +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + if ($PSVersionTable.PSVersion -lt [Version] '5.1') { Write-Warning -Message 'Cannot run PSDscResources integration tests on PowerShell versions lower than 5.1' @@ -9,11 +17,6 @@ if ($PSVersionTable.PSVersion -lt [Version] '5.1') Describe 'xArchive Integration Tests' { BeforeAll { - # Import CommonTestHelper for Enter-DscResourceTestEnvironment, Exit-DscResourceTestEnvironment - $testsFolderFilePath = Split-Path $PSScriptRoot -Parent - $commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' - Import-Module -Name $commonTestHelperFilePath - $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xArchive' ` diff --git a/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 new file mode 100644 index 000000000..5d87af0d3 --- /dev/null +++ b/Tests/Integration/MSFT_xDSCWebService.Integration.tests.ps1 @@ -0,0 +1,154 @@ +$script:dscModuleName = 'xPSDesiredStateConfiguration' +$script:dscResourceFriendlyName = 'xDSCWebService' +$script:dcsResourceName = "MSFT_$($script:dscResourceFriendlyName)" + +#region HEADER +# Integration Test Template Version: 1.3.1 +[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 -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -Force +$TestEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dcsResourceName ` + -TestType Integration + +Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'CommonTestHelper.psm1') + +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + +<# + .SYNOPSIS + Performs common DSC integration tests including compiling, setting, + testing, and getting a configuration. + + .PARAMETER ConfigurationName + The name of the configuration being executed. +#> +function Invoke-CommonResourceTesting +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ConfigurationName + ) + + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be $true + } +} + +<# + .SYNOPSIS + Performs common tests to ensure that the DSC pull server was properly + installed. +#> +function Test-DSCPullServerIsPresent +{ + [CmdletBinding()] + param + ( + ) + + It 'Should create a web.config file at the web site root' { + Test-Path -Path (Join-Path -Path $ConfigurationData.AllNodes.PhysicalPath -ChildPath 'web.config') | Should -Be $true + } + + It 'Should create a firewall rule for the chosen port' { + (Get-NetFirewallRule | Where-Object -FilterScript { + $_.DisplayName -eq 'DSCPullServer_IIS_Port' + } | Measure-Object).Count | Should -Be 1 + } +} +#endregion + +# Using try/finally to always cleanup. +try +{ + # Make sure the DSC-Service and Web-Server Windows features are installed + if (!(Install-WindowsFeatureAndVerify -Name 'DSC-Service') -or + !(Install-WindowsFeatureAndVerify -Name 'Web-Server')) + { + Write-Verbose -Message 'Skipping xDSCWebService Integration tests due to missing Windows Features.' -Verbose + return + } + + # Make sure the w3svc is running before proceeding with tests + Start-Service -Name w3svc -ErrorAction Stop + + #region Integration Tests + $configurationFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dcsResourceName).config.ps1" + . $configurationFile + + Describe "$($script:dcsResourceName)_Integration" { + $ensureAbsentConfigurationName = 'MSFT_xDSCWebService_PullTestRemoval_Config' + + $ensurePresentConfigurationNames = @( + 'MSFT_xDSCWebService_PullTestWithSecurityBestPractices_Config', + 'MSFT_xDSCWebService_PullTestWithoutSecurityBestPractices_Config' + ) + + foreach ($configurationName in $ensurePresentConfigurationNames) + { + Context ('When using configuration {0}' -f $configurationName) { + BeforeAll { + Invoke-CommonResourceTesting -ConfigurationName $ensureAbsentConfigurationName + } + + AfterAll { + Invoke-CommonResourceTesting -ConfigurationName $ensureAbsentConfigurationName + } + + Invoke-CommonResourceTesting -ConfigurationName $configurationName + + Test-DSCPullServerIsPresent + } + } + } + #endregion + +} +finally +{ + #region FOOTER + Restore-TestEnvironment -TestEnvironment $TestEnvironment + #endregion +} diff --git a/Tests/Integration/MSFT_xDSCWebService.config.ps1 b/Tests/Integration/MSFT_xDSCWebService.config.ps1 new file mode 100644 index 000000000..36be086b1 --- /dev/null +++ b/Tests/Integration/MSFT_xDSCWebService.config.ps1 @@ -0,0 +1,111 @@ +#region HEADER +# Integration Test Config Template Version: 1.2.0 +#endregion + +$configFile = [System.IO.Path]::ChangeExtension($MyInvocation.MyCommand.Path, 'json') +if (Test-Path -Path $configFile) +{ + $ConfigurationData = Get-Content -Path $configFile | ConvertFrom-Json +} +else +{ + $ConfigurationData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + CertificateFile = $env:DscPublicCertificatePath + CertificateThumbprint = $env:DscCertificateThumbprint + ConfigurationPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration" + EndpointName = 'PSDSCPullServer' + ModulePath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules" + PhysicalPath = "$env:SystemDrive\inetpub\PSDSCPullServer" + Port = 8080 + RegistrationKeyPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService" + } + ) + } +} + +<# + .SYNOPSIS + Removes a configured DSC pull server +#> +Configuration MSFT_xDSCWebService_PullTestRemoval_Config +{ + Import-DscResource -ModuleName 'xPSDesiredStateConfiguration' + + node $AllNodes.NodeName + { + xDSCWebService Integration_Test + { + Ensure = 'Absent' + AcceptSelfSignedCertificates = $true + CertificateThumbPrint = $Node.CertificateThumbprint + ConfigurationPath = $Node.ConfigurationPath + Enable32BitAppOnWin64 = $false + EndpointName = $Node.EndpointName + ModulePath = $Node.ModulePath + Port = $Node.Port + PhysicalPath = $Node.PhysicalPath + RegistrationKeyPath = $Node.RegistrationKeyPath + State = 'Started' + UseSecurityBestPractices = $true + } + } +} + +<# + .SYNOPSIS + Sets up a DSC pull server using security best practices +#> +Configuration MSFT_xDSCWebService_PullTestWithSecurityBestPractices_Config +{ + Import-DscResource -ModuleName 'xPSDesiredStateConfiguration' + + node $AllNodes.NodeName + { + xDSCWebService Integration_Test + { + Ensure = 'Present' + AcceptSelfSignedCertificates = $true + CertificateThumbPrint = $Node.CertificateThumbprint + ConfigurationPath = $Node.ConfigurationPath + Enable32BitAppOnWin64 = $false + EndpointName = $Node.EndpointName + ModulePath = $Node.ModulePath + Port = $Node.Port + PhysicalPath = $Node.PhysicalPath + RegistrationKeyPath = $Node.RegistrationKeyPath + State = 'Started' + UseSecurityBestPractices = $true + } + } +} + +<# + .SYNOPSIS + Sets up a DSC pull server without using security best practices +#> +Configuration MSFT_xDSCWebService_PullTestWithoutSecurityBestPractices_Config +{ + Import-DscResource -ModuleName 'xPSDesiredStateConfiguration' + + node $AllNodes.NodeName + { + xDSCWebService Integration_Test + { + Ensure = 'Present' + AcceptSelfSignedCertificates = $true + CertificateThumbPrint = $Node.CertificateThumbprint + ConfigurationPath = $Node.ConfigurationPath + Enable32BitAppOnWin64 = $false + EndpointName = $Node.EndpointName + ModulePath = $Node.ModulePath + Port = $Node.Port + PhysicalPath = $Node.PhysicalPath + RegistrationKeyPath = $Node.RegistrationKeyPath + State = 'Started' + UseSecurityBestPractices = $false + } + } +} diff --git a/Tests/Integration/MSFT_xDSCWebService.xxx.ps1 b/Tests/Integration/MSFT_xDSCWebService.xxx.ps1 deleted file mode 100644 index 64be95474..000000000 --- a/Tests/Integration/MSFT_xDSCWebService.xxx.ps1 +++ /dev/null @@ -1,183 +0,0 @@ -###################################################################################### -# Integration Tests for DSC Resource xDSCWebService -# -# There tests will make changes to your system, we are tyring to roll them back, -# but you never know. Best to run this on a throwaway VM. -# Run as an elevated administrator -###################################################################################### - -# Create a unique name that we use for our temp files and folders -[System.String] $tempFolderName = 'xDSCWebServiceTests_' + (Get-Date).ToString("yyyyMMdd_HHmmss") - -Describe 'xDSCWebService' { - function Verify-DSCPullServer - { - param - ( - [Parameter(Mandatory = $true)] - [System.String] - $Protocol, - - [Parameter(Mandatory = $true)] - [System.String] - $Hostname, - - [Parameter(Mandatory = $true)] - [System.String] - $Port - ) - - ([xml](Invoke-WebRequest -Uri "$($Protocol)://$($Hostname):$($Port)/psdscpullserver.svc" | Foreach-Object -Process Content)).service.workspace.collection.href - } - - try - { - # Before doing our changes, create a backup of the current config - Backup-WebConfiguration -Name $tempFolderName - - It 'Installing Service' -test { - { - # Define the configuration - Configuration InstallingService - { - WindowsFeature DSCServiceFeature - { - Ensure = 'Present' - Name = 'DSC-Service' - } - } - - $installingServiceMofPath = '{0}\{1}_InstallingService' -f $TestDrive, $tempFolderName - - # Execute the configuration into a temp location - InstallingService -OutputPath $installingServiceMofPath - - # Run the configuration, it should not throw any errors - Start-DscConfiguration -Path $installingServiceMofPath -Wait -Verbose -ErrorAction Stop -Force - } | Should -Not -Throw - - (Get-WindowsFeature -Name DSC-Service | Where-Object -Property Installed -EQ $true).Count | Should -Be 1 - } - - It 'Creating Sites' -Test { - { - # Define the configuration - Configuration CreatingSites - { - Import-DSCResource -ModuleName xPSDesiredStateConfiguration - - xDscWebService PSDSCPullServer - { - EndpointName = 'TestPSDSCPullServer' - Port = 21001 - CertificateThumbPrint = 'AllowUnencryptedTraffic' - UseSecurityBestPractices = $true - } - } - - $creatingSitesMofPath = '{0}\{1}_CreatingSites' -f $TestDrive, $tempFolderName - - # Execute the configuration into a temp location - CreatingSites -OutputPath $creatingSitesMofPath - - # Run the configuration, it should not throw any errors - Start-DscConfiguration -Path $creatingSitesMofPath -Wait -Verbose -ErrorAction Stop -Force - } | Should -Not -Throw - - # We now expect two sites starting with our prefix - (Get-ChildItem -Path IIS:\sites | Where-Object -Property Name -Match "^TestPSDSC").Count | Should -Be 1 - - # We expect some files in the web root, using the defaults - (Test-Path -Path "$ENV:SystemDrive\inetpub\TestPSDSCPullServer\web.config") | Should -Be $true - - $fireWallRuleDisplayName = 'Desired State Configuration - Pull Server Port:{0}' - $ruleName = ($fireWallRuleDisplayName -f '21001') - (Get-NetFirewallRule | Where-Object -Name DisplayName -EQ $ruleName | Measure-Object).Count | Should -Be 1 - - # We also expect an XML document with certain strings at a certain URI - Verify-DSCPullServer -Protocol 'http' -Hostname 'localhost' -Port '21001' | Should -Match 'Action|Module' - } - - It 'Removing Sites' -Test { - { - # Define the configuration - Configuration RemovingSites - { - Import-DSCResource -ModuleName xPSDesiredStateConfiguration - - xDscWebService PSDSCPullServer - { - Ensure = 'Absent' - EndpointName = 'TestPSDSCPullServer' - CertificateThumbPrint = 'NotUsed' - UseSecurityBestPractices = $true - } - } - - $removingSitesMofPath = '{0}\{1}_RemovingSites' -f $TestDrive, $tempFolderName - - # Execute the configuration into a temp location - RemovingSites -OutputPath $removingSitesMofPath - - # Run the configuration, it should not throw any errors - Start-DscConfiguration -Path $removingSitesMofPath -Wait -Verbose -ErrorAction Stop -Force - } | Should -Not -Throw - - # We now expect two sites starting with our prefix - (Get-ChildItem -Path IIS:\sites | Where-Object Name -match '^TestPSDSC').Count | Should -Be 0 - - Test-Path -Path "$ENV:SystemDrive\inetpub\TestPSDSCPullServer\web.config" | Should -Be $false - - $fireWallRuleDisplayName = 'Desired State Configuration - Pull Server Port:{0}' - $ruleName = ($fireWallRuleDisplayName -f '8081') - (Get-NetFirewallRule | Where-Object -Property DisplayName -EQ $ruleName | Measure-Object).Count | Should -Be 0 - } - - It 'CreatingSitesWithFTP' -Test { - { - # Create a new FTP site on IIS - If (-not (Test-Path -Path IIS:\Sites\DummyFTPSite)) - { - New-WebFtpSite -Name 'DummyFTPSite' -Port '21000' - - # Stop the site, we don't want it, it is just here to check whether setup works - (Get-Website -Name 'DummyFTPSite').ftpserver.stop() - } - - # Define the configuration - Configuration CreatingSitesWithFTP - { - Import-DSCResource -ModuleName xPSDesiredStateConfiguration - - xDscWebService PSDSCPullServer2 - { - EndpointName = 'TestPSDSCPullServer2' - Port = 21003 - CertificateThumbPrint = 'AllowUnencryptedTraffic' - UseSecurityBestPractices = $true - } - } - - $creatingSitesWithFtpMofPath = '{0}\{1}_CreatingSitesWithFTP' -f $TestDrive, $tempFolderName - - # Execute the configuration into a temp location - CreatingSitesWithFTP -OutputPath $creatingSitesWithFtpMofPath - - # Run the configuration, it should not throw any errors - Start-DscConfiguration -Path $creatingSitesWithFtpMofPath -Wait -Verbose -ErrorAction Stop -Force - } | Should -Not -Throw - } - } - finally - { - # Roll back our changes - Restore-WebConfiguration -Name $tempFolderName - Remove-WebConfigurationBackup -Name $tempFolderName - - # Remove the generated MoF files - Get-ChildItem -Path $ENV:TEMP -Filter $tempFolderName | Remove-Item -Recurse -Force - - # Remove all firewall rules starting with port 21* - Get-NetFirewallRule | Where-Object -Property DisplayName -Match '^Desired State Configuration - Pull Server Port:21' | Remove-NetFirewallRule - } -} diff --git a/Tests/Integration/MSFT_xEnvironmentResource.EndToEnd.Tests.ps1 b/Tests/Integration/MSFT_xEnvironmentResource.EndToEnd.Tests.ps1 index 77f585c08..e5364d3b8 100644 --- a/Tests/Integration/MSFT_xEnvironmentResource.EndToEnd.Tests.ps1 +++ b/Tests/Integration/MSFT_xEnvironmentResource.EndToEnd.Tests.ps1 @@ -11,6 +11,11 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xEnvironmentResource' ` diff --git a/Tests/Integration/MSFT_xEnvironmentResource.Integration.Tests.ps1 b/Tests/Integration/MSFT_xEnvironmentResource.Integration.Tests.ps1 index e3ec00477..63a80b0ad 100644 --- a/Tests/Integration/MSFT_xEnvironmentResource.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xEnvironmentResource.Integration.Tests.ps1 @@ -8,6 +8,11 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xEnvironmentResource' ` diff --git a/Tests/Integration/MSFT_xGroupResource.Integration.Tests.ps1 b/Tests/Integration/MSFT_xGroupResource.Integration.Tests.ps1 index 1f657e6e5..48356b4a4 100644 --- a/Tests/Integration/MSFT_xGroupResource.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xGroupResource.Integration.Tests.ps1 @@ -9,6 +9,11 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xGroupResource' ` diff --git a/Tests/Integration/MSFT_xMsiPackage.EndToEnd.Tests.ps1 b/Tests/Integration/MSFT_xMsiPackage.EndToEnd.Tests.ps1 index 09c870e34..6bc425b33 100644 --- a/Tests/Integration/MSFT_xMsiPackage.EndToEnd.Tests.ps1 +++ b/Tests/Integration/MSFT_xMsiPackage.EndToEnd.Tests.ps1 @@ -7,13 +7,18 @@ $errorActionPreference = 'Stop' Set-StrictMode -Version 'Latest' +# Import CommonTestHelper +$testsFolderFilePath = Split-Path $PSScriptRoot -Parent +$commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' +Import-Module -Name $commonTestHelperFilePath + +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + Describe 'xMsiPackage End to End Tests' { BeforeAll { - # Import CommonTestHelper - $testsFolderFilePath = Split-Path $PSScriptRoot -Parent - $commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' - Import-Module -Name $commonTestHelperFilePath - $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xMsiPackage' ` diff --git a/Tests/Integration/MSFT_xMsiPackage.Integration.Tests.ps1 b/Tests/Integration/MSFT_xMsiPackage.Integration.Tests.ps1 index 9b02c3c0b..31e153927 100644 --- a/Tests/Integration/MSFT_xMsiPackage.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xMsiPackage.Integration.Tests.ps1 @@ -5,10 +5,15 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $script:testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $script:commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xMsiPackage' ` - -TestType 'Unit' + -TestType 'Integration' try { diff --git a/Tests/Integration/MSFT_xPackageResource.Integration.Tests.ps1 b/Tests/Integration/MSFT_xPackageResource.Integration.Tests.ps1 index 622062c57..9e2eb3449 100644 --- a/Tests/Integration/MSFT_xPackageResource.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xPackageResource.Integration.Tests.ps1 @@ -1,5 +1,10 @@ Import-Module "$PSScriptRoot\..\CommonTestHelper.psm1" +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xPackageResource' ` diff --git a/Tests/Integration/MSFT_xRegistryResource.EndToEnd.Tests.ps1 b/Tests/Integration/MSFT_xRegistryResource.EndToEnd.Tests.ps1 index 3fc63dc70..094ebc1e2 100644 --- a/Tests/Integration/MSFT_xRegistryResource.EndToEnd.Tests.ps1 +++ b/Tests/Integration/MSFT_xRegistryResource.EndToEnd.Tests.ps1 @@ -13,6 +13,11 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xRegistryResource' ` @@ -158,10 +163,10 @@ try $expectedRegistryKeyValue = switch ($registryKeyValueType) { 'String' { 'TestString2'; break } - 'Binary' { [Byte[]]@( 12, 172, 17, 17 ); break } + 'Binary' { [Byte[]] @( 12, 172, 17, 17 ); break } 'DWord' { [Int32]::MaxValue; break } 'QWord' { [Int64]::MaxValue; break } - 'MultiString' { [String[]]@('MultiString1', 'MultiString2'); break } + 'MultiString' { [String[]] @('MultiString1', 'MultiString2'); break } 'ExpandString' { 'C:\windows'; break } } @@ -215,7 +220,7 @@ try ValueData = '0x00' } - $expectedRegistryKeyValue = [Byte[]]@(0) + $expectedRegistryKeyValue = [Byte[]] @(0) It 'Should compile and run configuration' { { diff --git a/Tests/Integration/MSFT_xRegistryResource.Integration.Tests.ps1 b/Tests/Integration/MSFT_xRegistryResource.Integration.Tests.ps1 index 57b2eedb2..022dc581a 100644 --- a/Tests/Integration/MSFT_xRegistryResource.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xRegistryResource.Integration.Tests.ps1 @@ -13,6 +13,11 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xRegistryResource' ` diff --git a/Tests/Integration/MSFT_xRemoteFile.Tests.ps1 b/Tests/Integration/MSFT_xRemoteFile.Tests.ps1 index b111877d9..13064f44c 100644 --- a/Tests/Integration/MSFT_xRemoteFile.Tests.ps1 +++ b/Tests/Integration/MSFT_xRemoteFile.Tests.ps1 @@ -1,6 +1,13 @@ +Import-Module -Name (Join-Path -Path (Split-Path $PSScriptRoot -Parent) ` + -ChildPath 'CommonTestHelper.psm1') -$Global:DSCModuleName = 'xPSDesiredStateConfiguration' # Example xNetworking -$Global:DSCResourceName = 'MSFT_xRemoteFile' # Example MSFT_xFirewall +$script:DSCModuleName = 'xPSDesiredStateConfiguration' # Example xNetworking +$script:DSCResourceName = 'MSFT_xRemoteFile' # Example MSFT_xFirewall + +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} #region HEADER # Integration Test Template Version: 1.1.0 @@ -8,31 +15,31 @@ $Global:DSCResourceName = 'MSFT_xRemoteFile' # Example MSFT_xFirewall if ( (-not (Test-Path -Path (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` (-not (Test-Path -Path (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) { - & git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $moduleRoot -ChildPath '\DSCResource.Tests\')) + & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $moduleRoot -ChildPath '\DSCResource.Tests\')) } Import-Module (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force $TestEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $Global:DSCModuleName ` - -DSCResourceName $Global:DSCResourceName ` - -TestType Integration + -DSCModuleName $script:DSCModuleName ` + -DSCResourceName $script:DSCResourceName ` + -TestType Integration #endregion # Using try/finally to always cleanup even if something awful happens. try { #region Integration Tests - $ConfigFile = Join-Path -Path $PSScriptRoot -ChildPath "$($Global:DSCResourceName).config.ps1" + $ConfigFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:DSCResourceName).config.ps1" . $ConfigFile # Make sure the file to download doesn't exist Remove-Item -Path $TestDestinationPath -Force -ErrorAction SilentlyContinue - Describe "$($Global:DSCResourceName)_Integration" { + Describe "$($script:DSCResourceName)_Integration" { #region DEFAULT TESTS It 'Should compile without throwing' { { - Invoke-Expression -Command "$($Global:DSCResourceName)_Config -OutputPath `$TestDrive" + Invoke-Expression -Command "$($script:DSCResourceName)_Config -OutputPath `$TestDrive" Start-DscConfiguration -Path $TestDrive ` -ComputerName localhost -Wait -Verbose -Force } | Should not throw diff --git a/Tests/Integration/MSFT_xScriptResource.Integration.Tests.ps1 b/Tests/Integration/MSFT_xScriptResource.Integration.Tests.ps1 index a94199764..123d797bb 100644 --- a/Tests/Integration/MSFT_xScriptResource.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xScriptResource.Integration.Tests.ps1 @@ -6,6 +6,11 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xScriptResource' ` diff --git a/Tests/Integration/MSFT_xServiceResource.Integration.Tests.ps1 b/Tests/Integration/MSFT_xServiceResource.Integration.Tests.ps1 index 8470e1d8e..d089870b2 100644 --- a/Tests/Integration/MSFT_xServiceResource.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xServiceResource.Integration.Tests.ps1 @@ -15,6 +15,11 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xServiceResource' ` @@ -116,7 +121,7 @@ try $resourceParameters = $script:existingServiceProperties It 'Should compile and apply the MOF without throwing' { - { + { . $script:configurationAllExceptCredentialFilePath -ConfigurationName $configurationName & $configurationName -OutputPath $TestDrive @resourceParameters Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force @@ -179,7 +184,7 @@ try $resourceParameters = $script:newServiceProperties It 'Should compile and apply the MOF without throwing' { - { + { . $script:configurationAllExceptCredentialFilePath -ConfigurationName $configurationName & $configurationName -OutputPath $TestDrive @resourceParameters Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force @@ -247,7 +252,7 @@ try } It 'Should compile and apply the MOF without throwing' { - { + { . $script:configurationAllExceptCredentialFilePath -ConfigurationName $configurationName & $configurationName -OutputPath $TestDrive @resourceParameters Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force @@ -297,7 +302,7 @@ try } It 'Should compile and apply the MOF without throwing' { - { + { . $script:configurationCredentialOnlyFilePath -ConfigurationName $configurationName & $configurationName -OutputPath $TestDrive -ConfigurationData $configData @resourceParameters Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force @@ -342,7 +347,7 @@ try } It 'Should compile and apply the MOF without throwing' { - { + { . $script:configurationAllExceptCredentialFilePath -ConfigurationName $configurationName & $configurationName -OutputPath $TestDrive @resourceParameters Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force @@ -380,7 +385,7 @@ try } It 'Should compile and apply the MOF without throwing' { - { + { . $script:configurationAllExceptCredentialFilePath -ConfigurationName $configurationName & $configurationName -OutputPath $TestDrive @resourceParameters Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force diff --git a/Tests/Integration/MSFT_xUserResource.Integration.Tests.ps1 b/Tests/Integration/MSFT_xUserResource.Integration.Tests.ps1 index 5058e063d..7b3f9e75a 100644 --- a/Tests/Integration/MSFT_xUserResource.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xUserResource.Integration.Tests.ps1 @@ -1,9 +1,8 @@ <# To run these tests, the currently logged on user must have rights to create a user. - These integration tests cover creating a brand new user, updating values + These integration tests cover creating a brand new user, updating values of a user that already exists, and deleting a user that exists. -#> - +#> # Suppressing this rule since we need to create a plaintext password to test this resource [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] param () @@ -12,6 +11,11 @@ Import-Module -Name (Join-Path -Path (Split-Path $PSScriptRoot -Parent) ` -ChildPath 'CommonTestHelper.psm1') ` -Force +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xUserResource' ` @@ -34,13 +38,13 @@ try { } ) } - + Context 'Should create a new user' { $configurationName = 'MSFT_xUser_NewUser' $configurationPath = Join-Path -Path $TestDrive -ChildPath $configurationName $logPath = Join-Path -Path $TestDrive -ChildPath 'NewUser.log' - + $testUserName = 'TestUserName12345' $testUserPassword = 'StrongOne7.' $testDescription = 'Test Description' @@ -65,7 +69,7 @@ try { It 'Should be able to call Get-DscConfiguration without throwing' { { Get-DscConfiguration -Verbose -ErrorAction Stop } | Should Not Throw } - + It 'Should return the correct configuration' { $currentConfig = Get-DscConfiguration -Verbose -ErrorAction Stop $currentConfig.UserName | Should Be $testUserName @@ -87,13 +91,13 @@ try { } } } - + Context 'Should update an existing user' { $configurationName = 'MSFT_xUser_UpdateUser' $configurationPath = Join-Path -Path $TestDrive -ChildPath $configurationName $logPath = Join-Path -Path $TestDrive -ChildPath 'UpdateUser.log' - + $testUserName = 'TestUserName12345' $testUserPassword = 'StrongOne7.' $testDescription = 'New Test Description' @@ -118,7 +122,7 @@ try { It 'Should be able to call Get-DscConfiguration without throwing' { { Get-DscConfiguration -Verbose -ErrorAction Stop } | Should Not Throw } - + It 'Should return the correct configuration' { $currentConfig = Get-DscConfiguration -Verbose -ErrorAction Stop $currentConfig.UserName | Should Be $testUserName @@ -140,13 +144,13 @@ try { } } } - + Context 'Should delete an existing user' { $configurationName = 'MSFT_xUser_DeleteUser' $configurationPath = Join-Path -Path $TestDrive -ChildPath $configurationName $logPath = Join-Path -Path $TestDrive -ChildPath 'DeleteUser.log' - + $testUserName = 'TestUserName12345' $testUserPassword = 'StrongOne7.' $secureTestPassword = ConvertTo-SecureString $testUserPassword -AsPlainText -Force @@ -170,7 +174,7 @@ try { It 'Should be able to call Get-DscConfiguration without throwing' { { Get-DscConfiguration -Verbose -ErrorAction Stop } | Should Not Throw } - + It 'Should return the correct configuration' { $currentConfig = Get-DscConfiguration -Verbose -ErrorAction Stop $currentConfig.UserName | Should Be $testUserName @@ -189,7 +193,7 @@ try { } } } - + } } finally diff --git a/Tests/Integration/MSFT_xWindowsFeature.Integration.Tests.ps1 b/Tests/Integration/MSFT_xWindowsFeature.Integration.Tests.ps1 index d0a2635e1..bba78b9cc 100644 --- a/Tests/Integration/MSFT_xWindowsFeature.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xWindowsFeature.Integration.Tests.ps1 @@ -4,7 +4,6 @@ RSAT-File-Services is set as the feature to test installing/uninstalling a feature with subfeatures. #> - # Suppressing this rule since we need to create a plaintext password to test this resource [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] param () @@ -13,6 +12,11 @@ Import-Module -Name (Join-Path -Path (Split-Path $PSScriptRoot -Parent) ` -ChildPath 'CommonTestHelper.psm1') ` -Force +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xWindowsFeature' ` @@ -30,7 +34,7 @@ try { $script:testFeatureName = 'Telnet-Client' $script:testFeatureWithSubFeaturesName = 'RSAT-File-Services' - #Saving the state so we can clean up afterwards + # Saving the state so we can clean up afterwards $testFeature = Get-WindowsFeature -Name $script:testFeatureName $script:installStateOfTestFeature = $testFeature.Installed diff --git a/Tests/Integration/MSFT_xWindowsOptionalFeature.Integration.Tests.ps1 b/Tests/Integration/MSFT_xWindowsOptionalFeature.Integration.Tests.ps1 index c908d2fe2..22ce0b45e 100644 --- a/Tests/Integration/MSFT_xWindowsOptionalFeature.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xWindowsOptionalFeature.Integration.Tests.ps1 @@ -1,5 +1,10 @@ Import-Module -Name (Join-Path -Path (Split-Path $PSScriptRoot -Parent) -ChildPath 'CommonTestHelper.psm1') +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xWindowsOptionalFeature' ` diff --git a/Tests/Integration/MSFT_xWindowsPackageCab.Integration.Tests.ps1 b/Tests/Integration/MSFT_xWindowsPackageCab.Integration.Tests.ps1 index cad1fa06d..a393055ae 100644 --- a/Tests/Integration/MSFT_xWindowsPackageCab.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xWindowsPackageCab.Integration.Tests.ps1 @@ -1,5 +1,10 @@ Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'CommonTestHelper.psm1') +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xWindowsPackageCab' ` diff --git a/Tests/Integration/MSFT_xWindowsProcess.Integration.Tests.ps1 b/Tests/Integration/MSFT_xWindowsProcess.Integration.Tests.ps1 index 12d14614c..ffe5f8a25 100644 --- a/Tests/Integration/MSFT_xWindowsProcess.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xWindowsProcess.Integration.Tests.ps1 @@ -13,6 +13,11 @@ Import-Module -Name (Join-Path -Path (Split-Path $PSScriptRoot -Parent) ` -ChildPath 'CommonTestHelper.psm1') ` -Force +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xWindowsProcess' ` diff --git a/Tests/Integration/xFileUpload.Integration.Tests.ps1 b/Tests/Integration/xFileUpload.Integration.Tests.ps1 new file mode 100644 index 000000000..559d1c007 --- /dev/null +++ b/Tests/Integration/xFileUpload.Integration.Tests.ps1 @@ -0,0 +1,129 @@ +$errorActionPreference = 'Stop' +Set-StrictMode -Version 'Latest' + +# Import CommonTestHelper for Enter-DscResourceTestEnvironment, Exit-DscResourceTestEnvironment +$script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent +$script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' +Import-Module -Name $commonTestHelperFilePath + +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + +$script:testEnvironment = Enter-DscResourceTestEnvironment ` + -DscResourceModuleName 'xPSDesiredStateConfiguration' ` + -DscResourceName 'xFileUpload.schema' ` + -TestType 'Integration' + +try +{ + Describe 'xFileUpload Integration Tests' { + BeforeAll { + $script:configurationName = 'xFileUpload_Config' + $script:configurationFilePath = Join-Path -Path $PSScriptRoot -ChildPath 'xFileUpload.config.ps1' + + # File content to use for testing + $script:testFileContent = 'Test Content' + + # Create a folder to be used as the destination SMB Share + $script:smbShareName = 'xFileUploadSMBShare' + $script:smbSharePath = Join-Path -Path $TestDrive -ChildPath 'xFileUploadFolder' + $null = New-Item -Path $script:smbSharePath -ItemType Directory -ErrorAction SilentlyContinue + $null = New-SmbShare -Name $script:smbShareName -Path $script:smbSharePath -FullAccess 'Everyone' -Temporary + $script:destinationPath = "\\localhost\$script:smbShareName\" + + # Create a file to be used as the source + $script:sourceFileName = 'testfile.txt' + $script:sourcePathFile = Join-Path -Path $TestDrive -ChildPath $script:sourceFileName + $null = Set-Content -Path $script:sourcePathFile -Value $script:testFileContent -Force + + # Create a folder and file to be used as the source + $script:sourceFolderName = 'testfolder' + $script:sourcePathFolder = Join-Path -Path $TestDrive -ChildPath $script:sourceFolderName + $null = New-Item -Path $script:sourcePathFolder -ItemType Directory -ErrorAction SilentlyContinue + $script:sourcePathFolderFile = Join-Path -Path $script:sourcePathFolder -ChildPath $script:sourceFileName + $null = Set-Content -Path $script:sourcePathFolderFile -Value $script:testFileContent -Force + } + + AfterAll { + $null = Remove-SmbShare -Name $script:smbShareName -Force + } + + Context 'When uploading a single file to an SMB file share' { + It 'Should compile and run configuration' { + { + $ConfigurationData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + CertificateFile = $env:DscPublicCertificatePath + DestinationPath = $script:destinationPath + SourcePath = $script:sourcePathFile + } + ) + } + + . $script:configurationFilePath -ConfigurationName $script:configurationName + & $script:configurationName ` + -OutputPath $TestDrive ` + -ConfigurationData $ConfigurationData + + Start-DscConfiguration ` + -Path $TestDrive ` + -ErrorAction 'Stop' ` + -Wait ` + -Force ` + -Verbose + } | Should -Not -Throw + } + + It 'Should have copied the file to the destination' { + $uploadedFilePath = Join-Path -Path $script:smbSharePath -ChildPath $script:sourceFileName + Test-Path -Path $uploadedFilePath | Should -Be $true + Get-Content -Path $uploadedFilePath | Should -Be $script:testFileContent + } + } + + Context 'When uploading a folder to an SMB file share' { + It 'Should compile and run configuration' { + { + $ConfigurationData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + CertificateFile = $env:DscPublicCertificatePath + DestinationPath = $script:destinationPath + SourcePath = $script:sourcePathFolder + } + ) + } + + . $script:configurationFilePath -ConfigurationName $script:configurationName + & $script:configurationName ` + -OutputPath $TestDrive ` + -ConfigurationData $ConfigurationData + + Start-DscConfiguration ` + -Path $TestDrive ` + -ErrorAction 'Stop' ` + -Wait ` + -Force ` + -Verbose + } | Should -Not -Throw + } + + It 'Should have copied the folder to the destination' { + $uploadedFolderPath = Join-Path -Path $script:smbSharePath -ChildPath $script:sourceFolderName + $uploadedFilePath = Join-Path -Path $uploadedFolderPath -ChildPath $script:sourceFileName + Test-Path -Path $uploadedFolderPath | Should -Be $true + Test-Path -Path $uploadedFilePath | Should -Be $true + Get-Content -Path $uploadedFilePath | Should -Be $script:testFileContent + } + } + } +} +finally +{ + Exit-DscResourceTestEnvironment -TestEnvironment $script:testEnvironment +} diff --git a/Tests/Integration/xFileUpload.config.ps1 b/Tests/Integration/xFileUpload.config.ps1 new file mode 100644 index 000000000..96593bb91 --- /dev/null +++ b/Tests/Integration/xFileUpload.config.ps1 @@ -0,0 +1,22 @@ +#region HEADER +# Integration Test Config Template Version: 1.2.0 +#endregion + +<# + .SYNOPSIS + Integration test configuration that uploads a file or folder + containined in the SourcePath into the SMB Share in the DestinationPath. +#> +Configuration xFileUpload_Config +{ + Import-DscResource -ModuleName 'xPSDesiredStateConfiguration' + + node $AllNodes.NodeName + { + xFileUpload Integration_Test + { + DestinationPath = $Node.DestinationPath + SourcePath = $Node.SourcePath + } + } +} diff --git a/Tests/Integration/xGroupSet.Integration.Tests.ps1 b/Tests/Integration/xGroupSet.Integration.Tests.ps1 index 5577c34ae..da906896d 100644 --- a/Tests/Integration/xGroupSet.Integration.Tests.ps1 +++ b/Tests/Integration/xGroupSet.Integration.Tests.ps1 @@ -9,6 +9,11 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'xGroupSet' ` diff --git a/Tests/Integration/xProcessSet.Integration.Tests.ps1 b/Tests/Integration/xProcessSet.Integration.Tests.ps1 index 310fa145e..962fd5f19 100644 --- a/Tests/Integration/xProcessSet.Integration.Tests.ps1 +++ b/Tests/Integration/xProcessSet.Integration.Tests.ps1 @@ -6,6 +6,11 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'xProcessSet' ` diff --git a/Tests/Integration/xServiceSet.Integration.Tests.ps1 b/Tests/Integration/xServiceSet.Integration.Tests.ps1 index 2fe6e0c2c..2ff9b0968 100644 --- a/Tests/Integration/xServiceSet.Integration.Tests.ps1 +++ b/Tests/Integration/xServiceSet.Integration.Tests.ps1 @@ -15,6 +15,11 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'xServiceSet' ` diff --git a/Tests/Integration/xWindowsFeatureSet.Integration.Tests.ps1 b/Tests/Integration/xWindowsFeatureSet.Integration.Tests.ps1 index d4c94f77d..781f3fa73 100644 --- a/Tests/Integration/xWindowsFeatureSet.Integration.Tests.ps1 +++ b/Tests/Integration/xWindowsFeatureSet.Integration.Tests.ps1 @@ -6,6 +6,11 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'xWindowsFeatureSet' ` diff --git a/Tests/Integration/xWindowsOptionalFeatureSet.Integration.Tests.ps1 b/Tests/Integration/xWindowsOptionalFeatureSet.Integration.Tests.ps1 index 46899f142..4814dcc2a 100644 --- a/Tests/Integration/xWindowsOptionalFeatureSet.Integration.Tests.ps1 +++ b/Tests/Integration/xWindowsOptionalFeatureSet.Integration.Tests.ps1 @@ -6,6 +6,11 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Integration') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'xWindowsOptionalFeatureSet' ` diff --git a/Tests/MSFT_xGroupResource.TestHelper.psm1 b/Tests/MSFT_xGroupResource.TestHelper.psm1 index 17cf1e6ae..ae6ad13a7 100644 --- a/Tests/MSFT_xGroupResource.TestHelper.psm1 +++ b/Tests/MSFT_xGroupResource.TestHelper.psm1 @@ -1,7 +1,7 @@ $errorActionPreference = 'Stop' Set-StrictMode -Version 'Latest' -#Import CommonResourceHelper for Test-IsNanoServer +# Import CommonResourceHelper for Test-IsNanoServer $moduleRootFilePath = Split-Path -Path $PSScriptRoot -Parent $dscResourcesFolderFilePath = Join-Path -Path $moduleRootFilePath -ChildPath 'DSCResources' $commonResourceHelperFilePath = Join-Path -Path $dscResourcesFolderFilePath -ChildPath 'CommonResourceHelper.psm1' @@ -440,7 +440,7 @@ function New-GroupOnFullSKU if ($PSBoundParameters.ContainsKey("Members")) { - $adsiGroupEntry = [ADSI]"WinNT://$env:computerName/$GroupName,group" + $adsiGroupEntry = [ADSI] "WinNT://$env:computerName/$GroupName,group" foreach ($memberUserName in $Members) { @@ -545,7 +545,7 @@ function Remove-GroupOnFullSKU $GroupName ) - $adsiComputerEntry = [ADSI]("WinNT://$env:computerName") + $adsiComputerEntry = [ADSI] ("WinNT://$env:computerName") $null = $adsiComputerEntry.Delete('Group', $GroupName) } @@ -720,7 +720,7 @@ function New-UserOnFullSKU $userName = $Credential.UserName $password = $Credential.GetNetworkCredential().Password - $adsiComputerEntry = [ADSI]("WinNT://$env:computerName") + $adsiComputerEntry = [ADSI] ("WinNT://$env:computerName") $adsiUserEntry = $adsiComputerEntry.Create('User', $userName) $null = $adsiUserEntry.SetPassword($password) $null = $adsiUserEntry.SetInfo() @@ -799,7 +799,7 @@ function Remove-UserOnFullSKU $UserName ) - $adsiComputerEntry = [ADSI]("WinNT://$env:computerName") + $adsiComputerEntry = [ADSI] ("WinNT://$env:computerName") $null = $adsiComputerEntry.Delete('User', $UserName) } diff --git a/Tests/MSFT_xPackageResource.TestHelper.psm1 b/Tests/MSFT_xPackageResource.TestHelper.psm1 index 035351d07..621851367 100644 --- a/Tests/MSFT_xPackageResource.TestHelper.psm1 +++ b/Tests/MSFT_xPackageResource.TestHelper.psm1 @@ -388,7 +388,7 @@ function Start-Server Write-Log -LogFile $LogPath -Message 'Starting request listener' $asyncState = $Result.AsyncState - [System.Net.HttpListener]$listener = $asyncState.Listener + [System.Net.HttpListener] $listener = $asyncState.Listener $filepath = $asyncState.FilePath Write-Log -LogFile $LogPath -Message (ConvertTo-Json $asyncState) @@ -1263,7 +1263,8 @@ function New-TestExecutable ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [String]$DestinationPath + [String] + $DestinationPath ) if (Test-Path -Path $DestinationPath) diff --git a/Tests/MSFT_xRegistryResource.TestHelper.psm1 b/Tests/MSFT_xRegistryResource.TestHelper.psm1 index edf6da98b..75dfbb8e8 100644 --- a/Tests/MSFT_xRegistryResource.TestHelper.psm1 +++ b/Tests/MSFT_xRegistryResource.TestHelper.psm1 @@ -3,14 +3,14 @@ Tests if a registry key exists. .PARAMETER KeyPath - The path to the registry key to test for existence. + The path to the registry key to test for existence. Must include the registry hive. #> function Test-RegistryKeyExists { [CmdletBinding()] [OutputType([Boolean])] - param + param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -26,7 +26,7 @@ function Test-RegistryKeyExists Tests if a registry key value exists. .PARAMETER KeyPath - The path to the registry key that should contain the value to test for existence. + The path to the registry key that should contain the value to test for existence. Must include the registry hive. .PARAMETER ValueName @@ -62,7 +62,7 @@ function Test-RegistryValueExists $ValueType ) - try + try { $registryValue = Get-ItemProperty -Path $KeyPath -Name $ValueName -ErrorAction 'SilentlyContinue' Write-Verbose -Message "Test-RegistryValueExists - Registry key value: $registryKeyValue" @@ -87,7 +87,7 @@ function Test-RegistryValueExists $registryValueExists = $registryValueExists -and ($registryValue.GetType().Name -eq 'Byte[]') $registryValue = Convert-ByteArrayToHexString -Data $registryValue } - else + else { $registryValueExists = $registryValueExists -and ($registryValue.GetType().Name -eq $ValueType) } @@ -113,7 +113,7 @@ function Test-RegistryValueExists Creates a registry key. .PARAMETER KeyPath - The path to the registry key to be created. + The path to the registry key to be created. Must include the registry hive. #> function New-TestRegistryKey @@ -128,12 +128,12 @@ function New-TestRegistryKey ) $parentPath = Split-Path -Path $KeyPath -Parent - + if (-not (Test-RegistryKeyExists -KeyPath $parentPath)) { New-TestRegistryKey -KeyPath $parentPath } - + Write-Verbose -Message "New-TestRegistryKey - Creating new registry key at: $KeyPath" $null = New-Item -Path $KeyPath @@ -144,7 +144,7 @@ function New-TestRegistryKey Creates a registry key. .PARAMETER KeyPath - The path to the registry key to be created. + The path to the registry key to be created. Must include the registry hive. .PARAMETER ValueName @@ -203,7 +203,7 @@ function New-RegistryValue Write-Verbose -Message "New-RegistryValue - Binary data: $ValueData" } - + $null = New-ItemProperty -Path $KeyPath -Name $ValueName -Value $ValueData -PropertyType $ValueType } @@ -212,13 +212,13 @@ function New-RegistryValue Removes a registry key. .PARAMETER KeyPath - The path to the registry key to remove + The path to the registry key to remove Must include the registry hive. #> function Remove-TestRegistryKey { [CmdletBinding()] - param + param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -234,7 +234,7 @@ function Remove-TestRegistryKey Removes a registry value. .PARAMETER KeyPath - The path to the registry key that contains the value to remove. + The path to the registry key that contains the value to remove. Must include the registry hive. .PARAMETER ValueName @@ -243,7 +243,7 @@ function Remove-TestRegistryKey function Remove-TestRegistryValue { [CmdletBinding()] - param + param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -270,7 +270,7 @@ function Remove-TestRegistryValue function Mount-RegistryDrive { [CmdletBinding()] - param + param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -327,7 +327,7 @@ function Mount-RegistryDrive function Dismount-RegistryDrive { [CmdletBinding()] - param + param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] diff --git a/Tests/Unit/CommonResourceHelper.Tests.ps1 b/Tests/Unit/CommonResourceHelper.Tests.ps1 index d04c594ed..e74713e6d 100644 --- a/Tests/Unit/CommonResourceHelper.Tests.ps1 +++ b/Tests/Unit/CommonResourceHelper.Tests.ps1 @@ -1,6 +1,15 @@ $errorActionPreference = 'Stop' Set-StrictMode -Version 'Latest' +$script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent +$script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' +Import-Module -Name $commonTestHelperFilePath + +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + Describe 'CommonResourceHelper Unit Tests' { BeforeAll { # Import the CommonResourceHelper module to test @@ -177,5 +186,24 @@ Describe 'CommonResourceHelper Unit Tests' { } } } + + Describe 'Set-DSCMachineRebootRequired' { + Context 'When called' { + It 'Should set the desired DSCMachineStatus value' { + # Store the previous $global:DSCMachineStatus value + $prevDSCMachineStatus = $global:DSCMachineStatus + + # Make sure DSCMachineStatus is set to a value that will have to be updated + $global:DSCMachineStatus = 0 + + # Set and test for the new value + Set-DSCMachineRebootRequired + $global:DSCMachineStatus | Should -Be 1 + + # Revert to previous $global:DSCMachineStatus value + $global:DSCMachineStatus = $prevDSCMachineStatus + } + } + } } } diff --git a/Tests/Unit/MSFT_xArchive.Tests.ps1 b/Tests/Unit/MSFT_xArchive.Tests.ps1 index f81acbbfb..0e4dc6eb2 100644 --- a/Tests/Unit/MSFT_xArchive.Tests.ps1 +++ b/Tests/Unit/MSFT_xArchive.Tests.ps1 @@ -1,6 +1,15 @@ $errorActionPreference = 'Stop' Set-StrictMode -Version 'Latest' +$script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent +$script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' +Import-Module -Name $commonTestHelperFilePath + +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + Describe 'xArchive Unit Tests' { BeforeAll { # Import CommonTestHelper for Enter-DscResourceTestEnvironment, Exit-DscResourceTestEnvironment diff --git a/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 b/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 index b19c0fc88..aa06d8070 100644 --- a/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 +++ b/Tests/Unit/MSFT_xDSCWebService.Tests.ps1 @@ -1,13 +1,22 @@ +$script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent +$script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' +Import-Module -Name $commonTestHelperFilePath + $script:dscModuleName = 'xPSDesiredStateConfiguration' $script:dscResourceName = 'MSFT_xDSCWebService' +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + #region HEADER # Integration Test Template Version: 1.1.0 [String] $script:moduleRoot = Split-Path -Parent -Path (Split-Path -Parent -Path $PSScriptRoot) if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) { - & git.exe @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests')) + & git.exe @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests')) } Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force @@ -683,7 +692,7 @@ try Context -Name 'Ensure is Present - CertificateThumbprint and UseSecurityBestPractices is $true' -Fixture { #region Mocks - Mock -CommandName Set-UseSecurityBestPractices + Mock -CommandName Set-UseSecurityBestPractice #endregion $altTestParameters = $testParameters.Clone() @@ -703,7 +712,7 @@ try It 'Should call expected mocks' { Assert-MockCalled -Exactly -Times 0 -CommandName Find-CertificateThumbprintWithSubjectAndTemplateName - Assert-MockCalled -Exactly -Times 1 -CommandName Set-UseSecurityBestPractices + Assert-MockCalled -Exactly -Times 1 -CommandName Set-UseSecurityBestPractice } } @@ -919,7 +928,7 @@ try Mock -CommandName Get-WebConfigAppSetting -MockWith {'ESENT'} -Verifiable Mock -CommandName Test-WebConfigAppSetting -MockWith {$true} -ParameterFilter {$AppSettingName -eq 'dbconnectionstr'} -Verifiable Mock -CommandName Test-WebConfigAppSetting -MockWith {$true} -ParameterFilter {$AppSettingName -eq 'ModulePath'} -Verifiable - Mock -CommandName Test-UseSecurityBestPractices -MockWith {$false} -Verifiable + Mock -CommandName Test-UseSecurityBestPractice -MockWith {$false} -Verifiable Test-TargetResource @altTestParameters -Ensure Present | Should -Be $false diff --git a/Tests/Unit/MSFT_xEnvironmentResource.Tests.ps1 b/Tests/Unit/MSFT_xEnvironmentResource.Tests.ps1 index c605e463a..1765b3afb 100644 --- a/Tests/Unit/MSFT_xEnvironmentResource.Tests.ps1 +++ b/Tests/Unit/MSFT_xEnvironmentResource.Tests.ps1 @@ -1,6 +1,15 @@ $errorActionPreference = 'Stop' Set-StrictMode -Version 'Latest' +$script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent +$script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' +Import-Module -Name $commonTestHelperFilePath + +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + # Import CommonTestHelper for Enter-DscResourceTestEnvironment, Exit-DscResourceTestEnvironment $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' diff --git a/Tests/Unit/MSFT_xGroupResource.Tests.ps1 b/Tests/Unit/MSFT_xGroupResource.Tests.ps1 index ddb20e07b..bf92528a0 100644 --- a/Tests/Unit/MSFT_xGroupResource.Tests.ps1 +++ b/Tests/Unit/MSFT_xGroupResource.Tests.ps1 @@ -9,6 +9,11 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xGroupResource' ` @@ -96,7 +101,7 @@ try } <# - Get-Group, Add-GroupMember, Remove-GroupMember, Clear-GroupMembers, Save-Group, + Get-Group, Add-GroupMember, Remove-GroupMember, Clear-GroupMember, Save-Group, Remove-Group, Find-Principal, and Remove-DisposableObject cannot be unit tested because they are wrapper functions for .NET class function calls. #> @@ -114,7 +119,7 @@ try It 'Should return output Get-TargetResourceOnFullSKU with all parameters when not on Nano Server' { $getTargetResourceResult = Get-TargetResource -GroupName $script:testGroupName -Credential $script:testCredential - + Assert-MockCalled -CommandName 'Test-IsNanoServer' Assert-MockCalled -CommandName 'Get-TargetResourceOnFullSKU' -ParameterFilter { $GroupName -eq $script:testGroupName -and $Credential -eq $script:testCredential } $getTargetResourceResult.TestResult | Should Be 'OnFullSKU' @@ -122,9 +127,9 @@ try It 'Should call Get-TargetResourceOnNanoServer with all parameters when on Nano Server' { Mock -CommandName 'Test-IsNanoServer' -MockWith { return $true } - + $getTargetResourceResult = Get-TargetResource -GroupName $script:testGroupName -Credential $script:testCredential - + Assert-MockCalled -CommandName 'Test-IsNanoServer' Assert-MockCalled -CommandName 'Get-TargetResourceOnNanoServer' -ParameterFilter { $GroupName -eq $script:testGroupName -and $Credential -eq $script:testCredential } $getTargetResourceResult.TestResult | Should Be 'OnNanoServer' @@ -144,16 +149,16 @@ try It 'Should call Set-TargetResourceOnFullSKU with all parameters when not on Nano Server' { Set-TargetResource -GroupName $script:testGroupName -Credential $script:testCredential - + Assert-MockCalled -CommandName 'Test-IsNanoServer' Assert-MockCalled -CommandName 'Set-TargetResourceOnFullSKU' -ParameterFilter { $GroupName -eq $script:testGroupName -and $Credential -eq $script:testCredential } } It 'Should call Set-TargetResourceOnNanoServer with all parameters when on Nano Server' { Mock -CommandName 'Test-IsNanoServer' -MockWith { return $true } - + Set-TargetResource -GroupName $script:testGroupName -Credential $script:testCredential - + Assert-MockCalled -CommandName 'Test-IsNanoServer' Assert-MockCalled -CommandName 'Set-TargetResourceOnNanoServer' -ParameterFilter { $GroupName -eq $script:testGroupName -and $Credential -eq $script:testCredential } } @@ -172,16 +177,16 @@ try It 'Should call Test-TargetResourceOnFullSKU with all parameters when not on Nano Server' { $testTargetResourceResult = Test-TargetResource -GroupName $script:testGroupName -Credential $script:testCredential - + Assert-MockCalled -CommandName 'Test-IsNanoServer' Assert-MockCalled -CommandName 'Test-TargetResourceOnFullSKU' -ParameterFilter { $GroupName -eq $script:testGroupName -and $Credential -eq $script:testCredential } } It 'Should call Test-TargetResourceOnNanoServer with all parameters when on Nano Server' { Mock -CommandName 'Test-IsNanoServer' -MockWith { return $true } - + $testTargetResourceResult = Test-TargetResource -GroupName $script:testGroupName -Credential $script:testCredential - + Assert-MockCalled -CommandName 'Test-IsNanoServer' Assert-MockCalled -CommandName 'Test-TargetResourceOnNanoServer' -ParameterFilter { $GroupName -eq $script:testGroupName -and $Credential -eq $script:testCredential } } @@ -189,7 +194,7 @@ try Context 'Assert-GroupNameValid' { $invalidCharacters = @( '\', '/', '"', '[', ']', ':', '|', '<', '>', '+', '=', ';', ',', '?', '*', '@' ) - + foreach ($invalidCharacter in $invalidCharacters) { It "Should throw error if name contains invalid character '$invalidCharacter'" { @@ -221,7 +226,7 @@ try Context 'Test-IsLocalMachine' { Mock -CommandName 'Get-CimInstance' -MockWith { } - + $localMachineScopes = @( '.', $env:computerName, 'localhost', '127.0.0.1' ) foreach ($localMachineScope in $localMachineScopes) @@ -239,7 +244,7 @@ try It 'Should return true if custom local IP address provided and Get-CimInstance contains matching IP address' { Mock -CommandName 'Get-CimInstance' -MockWith { return @{ IPAddress = @($customLocalIPAddress, '789.1.2.3')} } - + Test-IsLocalMachine -Scope $customLocalIPAddress | Should Be $true } @@ -308,12 +313,12 @@ try $testMembers = @('User1', 'User2') Mock -CommandName 'Get-MembersOnNanoServer' -MockWith { return @() } - + It 'Should return Ensure as Absent when Get-LocalGroup throws a GroupNotFound exception' { Mock -CommandName 'Get-LocalGroup' -MockWith { Write-Error -Message 'Test error message' -CategoryReason 'GroupNotFoundException' } - + $getTargetResourceResult = Get-TargetResourceOnNanoServer -GroupName $script:testGroupName - + Assert-MockCalled -CommandName 'Get-LocalGroup' -ParameterFilter { $Name -eq $script:testGroupName } $getTargetResourceResult -is [Hashtable] | Should Be $true @@ -324,17 +329,17 @@ try It 'Should throw an error when Get-LocalGroup throws an exception other than GroupNotFound' { Mock -CommandName 'Get-LocalGroup' -MockWith { Write-Error -Message $script:testErrorMessage -CategoryReason 'OtherException' } - + { $getTargetResourceResult = Get-TargetResourceOnNanoServer -GroupName $script:testGroupName } | Should Throw $script:testErrorMessage - + Assert-MockCalled -CommandName 'Get-LocalGroup' -ParameterFilter { $Name -eq $script:testGroupName } } It 'Should return correct hashtable values when Get-LocalGroup returns a valid, existing group without members' { $script:testLocalGroup.Description = $script:testGroupDescription - + Mock -CommandName 'Get-LocalGroup' -MockWith { return $script:testLocalGroup } - + $getTargetResourceResult = Get-TargetResourceOnNanoServer -GroupName $script:testGroupName Assert-MockCalled -CommandName 'Get-LocalGroup' -ParameterFilter { $Name -eq $script:testGroupName } @@ -350,12 +355,12 @@ try It 'Should return correct hashtable values when Get-LocalGroup returns a valid, existing group with members' { $script:testLocalGroup.Description = $script:testGroupDescription - + Mock -CommandName 'Get-LocalGroup' -MockWith { return $script:testLocalGroup } Mock -CommandName 'Get-MembersOnNanoServer' -MockWith { return $testMembers } - + $getTargetResourceResult = Get-TargetResourceOnNanoServer -GroupName $script:testGroupName - + Assert-MockCalled -CommandName 'Get-LocalGroup' -ParameterFilter { $Name -eq $script:testGroupName } Assert-MockCalled -CommandName 'Get-MembersOnNanoServer' -ParameterFilter { $Group -eq $script:testLocalGroup } @@ -376,7 +381,7 @@ try Mock -CommandName 'Get-MembersOnNanoServer' -MockWith { } Mock -CommandName 'Add-LocalGroupMember' -MockWith { } Mock -CommandName 'Remove-LocalGroupMember' -MockWith { } - + It 'Should not attempt to remove an absent group when Ensure is Absent' { Set-TargetResourceOnNanoServer -GroupName $script:testGroupName -Ensure 'Absent' @@ -529,7 +534,7 @@ try It 'Should remove a member from an existing group using MembersToExclude' { $testMembers = @( $script:testMemberName2 ) - + Set-TargetResourceOnNanoServer -GroupName $script:testGroupName -MembersToExclude $testMembers -Ensure 'Present' Assert-MockCalled -CommandName 'Get-LocalGroup' -ParameterFilter { $Name -eq $script:testGroupName } @@ -660,13 +665,13 @@ try Context 'Test-TargetResourceOnNanoServer' { Mock -CommandName 'Get-LocalGroup' -MockWith { Write-Error -Message 'Test error message' -CategoryReason 'GroupNotFoundException' } Mock -CommandName 'Get-MembersOnNanoServer' -MockWith { } - + It 'Should return true for an absent group when Ensure is Absent' { Test-TargetResourceOnNanoServer -GroupName $script:testGroupName -Ensure 'Absent' | Should Be $true Assert-MockCalled -CommandName 'Get-LocalGroup' -ParameterFilter { $Name -eq $script:testGroupName } } - + It 'Should return false for an absent group when Ensure is Present' { Test-TargetResourceOnNanoServer -GroupName $script:testGroupName -Ensure 'Present' | Should Be $false @@ -695,7 +700,7 @@ try It 'Should return true for an existing group with a matching description' { $script:testLocalGroup.Description = $script:testGroupDescription - + Test-TargetResourceOnNanoServer -GroupName $script:testGroupName -Description $script:testGroupDescription -Ensure 'Present' | Should Be $true Assert-MockCalled -CommandName 'Get-LocalGroup' -ParameterFilter { $Name -eq $script:testGroupName } @@ -703,7 +708,7 @@ try It 'Should return false for an existing group with a mismatching description' { $script:testLocalGroup.Description = $script:testGroupDescription - + Test-TargetResourceOnNanoServer -GroupName $script:testGroupName -Description 'Wrong description' -Ensure 'Present' | Should Be $false Assert-MockCalled -CommandName 'Get-LocalGroup' -ParameterFilter { $Name -eq $script:testGroupName } @@ -847,10 +852,10 @@ try Mock -CommandName 'Get-Group' -MockWith { } Mock -CommandName 'Get-MembersOnFullSKU' -MockWith { return @() } Mock -CommandName 'Remove-DisposableObject' -MockWith { } - + It 'Should return Ensure as Absent when Get-Group returns null' { $getTargetResourceResult = Get-TargetResourceOnFullSKU -GroupName $script:testGroupName - + Assert-MockCalled -CommandName 'Get-Group' -ParameterFilter { $GroupName -eq $script:testGroupName } Assert-MockCalled -CommandName 'Remove-DisposableObject' @@ -862,9 +867,9 @@ try It 'Should return correct hashtable values when Get-Group returns a valid, existing group without members' { $script:testGroup.Description = $script:testGroupDescription - + Mock -CommandName 'Get-Group' -MockWith { return $script:testGroup } - + $getTargetResourceResult = Get-TargetResourceOnFullSKU -GroupName $script:testGroupName Assert-MockCalled -CommandName 'Get-Group' -ParameterFilter { $GroupName -eq $script:testGroupName } @@ -881,12 +886,12 @@ try It 'Should return correct hashtable values when Get-Group returns a valid, existing group with members' { $testGroup.Description = $script:testGroupDescription - + Mock -CommandName 'Get-Group' -MockWith { return $script:testGroup } Mock -CommandName 'Get-MembersOnFullSKU' -MockWith { return $testMembers } - + $getTargetResourceResult = Get-TargetResourceOnFullSKU -GroupName $script:testGroupName - + Assert-MockCalled -CommandName 'Get-Group' -ParameterFilter { $GroupName -eq $script:testGroupName } Assert-MockCalled -CommandName 'Get-MembersOnFullSKU' -ParameterFilter { $Group -eq $script:testGroup } Assert-MockCalled -CommandName 'Remove-DisposableObject' @@ -903,36 +908,36 @@ try Context 'Set-TargetResourceOnFullSKU' { Mock -CommandName 'Get-Group' -MockWith { } Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { } - Mock -CommandName 'ConvertTo-UniquePrincipalsList' -MockWith { + Mock -CommandName 'ConvertTo-UniquePrincipalsList' -MockWith { $memberPrincipals = @() if ($MemberNames -contains $script:testUserPrincipal1.Name) { $memberPrincipals += @( $script:testUserPrincipal1` ) } - + if ($MemberNames -contains $script:testUserPrincipal2.Name) { $memberPrincipals += @( $script:testUserPrincipal2 ) } - + if ($MemberNames -contains $script:testUserPrincipal3.Name) { $memberPrincipals += @( $script:testUserPrincipal3 ) } return $memberPrincipals - } - - Mock -CommandName 'Clear-GroupMembers' -MockWith { } + } + + Mock -CommandName 'Clear-GroupMember' -MockWith { } Mock -CommandName 'Add-GroupMember' -MockWith { } Mock -CommandName 'Remove-GroupMember' -MockWith { } Mock -CommandName 'Remove-Group' -MockWith { } Mock -CommandName 'Save-Group' -MockWith { } - + Mock -CommandName 'Remove-DisposableObject' -MockWith { } Mock -CommandName 'Get-PrincipalContext' -MockWith { return $script:testPrincipalContext } - + It 'Should not attempt to remove an absent group when Ensure is Absent' { Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -Ensure 'Absent' @@ -941,7 +946,7 @@ try Assert-MockCalled -CommandName 'Remove-Group' -ParameterFilter { $Group.Name -eq $script:testGroupName } -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Remove-DisposableObject' -Scope 'It' } - + It 'Should create an empty group' { Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -Ensure 'Present' @@ -962,7 +967,7 @@ try It 'Should create a group with one local member using Members' { $testMembers = @( $script:testUserPrincipal1.Name ) - + Mock -CommandName 'New-Object' -ParameterFilter { $TypeName -eq 'System.DirectoryServices.AccountManagement.GroupPrincipal' } -MockWith { return $testGroup } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -Members $testMembers -Ensure 'Present' @@ -978,8 +983,8 @@ try It 'Should create a group with two local members using Members' { $testMembers = @( $script:testUserPrincipal1.Name, $script:testUserPrincipal2.Name ) - - Mock -CommandName 'New-Object' -ParameterFilter { $TypeName -eq 'System.DirectoryServices.AccountManagement.GroupPrincipal' } -MockWith { return $testGroup } + + Mock -CommandName 'New-Object' -ParameterFilter { $TypeName -eq 'System.DirectoryServices.AccountManagement.GroupPrincipal' } -MockWith { return $testGroup } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -Members $testMembers -Ensure 'Present' @@ -995,7 +1000,7 @@ try It 'Should create a group with one local member using MembersToInclude' { $testMembers = @( $script:testUserPrincipal1.Name ) - + Mock -CommandName 'New-Object' -ParameterFilter { $TypeName -eq 'System.DirectoryServices.AccountManagement.GroupPrincipal' } -MockWith { return $testGroup } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -MembersToInclude $testMembers -Ensure 'Present' @@ -1011,7 +1016,7 @@ try It 'Should create a group with two local members using MembersToInclude' { $testMembers = @( $script:testUserPrincipal1.Name, $script:testUserPrincipal2.Name ) - + Mock -CommandName 'New-Object' -ParameterFilter { $TypeName -eq 'System.DirectoryServices.AccountManagement.GroupPrincipal' } -MockWith { return $testGroup } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -MembersToInclude $testMembers -Ensure 'Present' @@ -1030,7 +1035,7 @@ try It 'Should add a member to an existing group with no members using Members' { $testMembers = @( $script:testUserPrincipal1.Name ) - + Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { return @() } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -Members $testMembers -Ensure 'Present' @@ -1046,7 +1051,7 @@ try It 'Should add two members to an existing group with one of the members using Members' { $testMembers = @( $script:testUserPrincipal1.Name, $script:testUserPrincipal2.Name ) - + Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { return @( $script:testUserPrincipal1 ) } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -Members $testMembers -Ensure 'Present' @@ -1063,7 +1068,7 @@ try It 'Should add a member to an existing group with no members using MembersToInclude' { $testMembers = @( $script:testUserPrincipal1.Name ) - + Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { return @() } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -MembersToInclude $testMembers -Ensure 'Present' @@ -1079,7 +1084,7 @@ try It 'Should add two members to an existing group with one of the members using MembersToInclude' { $testMembers = @( $script:testUserPrincipal1.Name, $script:testUserPrincipal2.Name ) - + Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { return @( $script:testUserPrincipal1 ) } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -MembersToInclude $testMembers -Ensure 'Present' @@ -1096,7 +1101,7 @@ try It 'Should remove a member from an existing group using Members' { $testMembers = @( $script:testUserPrincipal1.Name ) - + Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { return @( $script:testUserPrincipal1, $script:testUserPrincipal2 ) } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -Members $testMembers -Ensure 'Present' @@ -1112,7 +1117,7 @@ try It 'Should clear group members from an existing group using Members' { $testMembers = @( ) - + Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { return @( $script:testUserPrincipal1, $script:testUserPrincipal2 ) } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -Members $testMembers -Ensure 'Present' @@ -1120,14 +1125,14 @@ try Assert-MockCalled -CommandName 'Get-PrincipalContext' Assert-MockCalled -CommandName 'Get-Group' -ParameterFilter { $GroupName -eq $script:testGroupName } Assert-MockCalled -CommandName 'Get-MembersAsPrincipalsList' -ParameterFilter { $Group.Name -eq $script:testGroupName } - Assert-MockCalled -CommandName 'Clear-GroupMembers' -ParameterFilter { $Group.Name -eq $script:testGroupName } + Assert-MockCalled -CommandName 'Clear-GroupMember' -ParameterFilter { $Group.Name -eq $script:testGroupName } Assert-MockCalled -CommandName 'Save-Group' -ParameterFilter { $Group.Name -eq $script:testGroupName } Assert-MockCalled -CommandName 'Remove-DisposableObject' } It 'Should remove a member from an existing group using MembersToExclude' { $testMembers = @( $script:testUserPrincipal2.Name ) - + Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { return @( $script:testUserPrincipal1, $script:testUserPrincipal2 ) } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -MembersToExclude $testMembers -Ensure 'Present' @@ -1143,7 +1148,7 @@ try It 'Should add a user and remove a user using Members' { $testMembers = @( $script:testUserPrincipal1.Name, $script:testUserPrincipal3.Name ) - + Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { return @( $script:testUserPrincipal1, $script:testUserPrincipal2 ) } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -Members $testMembers -Ensure 'Present' @@ -1161,9 +1166,9 @@ try It 'Should add a user and remove a user using MembersToInclude and MembersToExclude at the same time' { $testMembersToInclude = @( $script:testUserPrincipal3.Name ) $testMembersToExclude = @( $script:testUserPrincipal2.Name ) - + Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { return @( $script:testUserPrincipal1, $script:testUserPrincipal2 ) } - + Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -MembersToInclude $testMembersToInclude -MembersToExclude $testMembersToExclude -Ensure 'Present' Assert-MockCalled -CommandName 'Get-PrincipalContext' @@ -1206,12 +1211,12 @@ try It 'Should not modify group if member specified by MembersToInclude is already in group' { $testMembers = @( $script:testUserPrincipal1.Name, $script:testUserPrincipal2.Name ) - - Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { return @( $script:testUserPrincipal1, $script:testUserPrincipal2 ) } + + Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { return @( $script:testUserPrincipal1, $script:testUserPrincipal2 ) } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -MembersToInclude $testMembers -Ensure 'Present' - Assert-MockCalled -CommandName 'Clear-GroupMembers' -Times 0 -Scope 'It' + Assert-MockCalled -CommandName 'Clear-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Add-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Remove-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Save-Group' -Times 0 -Scope 'It' @@ -1220,12 +1225,12 @@ try It 'Should not modify group if member specified by MembersToExclude is not in group' { $testMembers = @( $script:testUserPrincipal3.Name ) - + Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { return @( $script:testUserPrincipal1, $script:testUserPrincipal2 ) } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -MembersToExclude $testMembers -Ensure 'Present' - Assert-MockCalled -CommandName 'Clear-GroupMembers' -Times 0 -Scope 'It' + Assert-MockCalled -CommandName 'Clear-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Add-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Remove-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Save-Group' -Times 0 -Scope 'It' @@ -1234,12 +1239,12 @@ try It 'Should not modify group if members specified by Members match group members' { $testMembers = @( $script:testUserPrincipal1.Name, $script:testUserPrincipal2.Name ) - + Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { return @( $script:testUserPrincipal1, $script:testUserPrincipal2 ) } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -Members $testMembers -Ensure 'Present' - Assert-MockCalled -CommandName 'Clear-GroupMembers' -Times 0 -Scope 'It' + Assert-MockCalled -CommandName 'Clear-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Add-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Remove-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Save-Group' -Times 0 -Scope 'It' @@ -1248,12 +1253,12 @@ try It 'Should not modify group if MembersToInclude is empty' { $testMembers = @( ) - + Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { return @( ) } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -MembersToInclude $testMembers -Ensure 'Present' - Assert-MockCalled -CommandName 'Clear-GroupMembers' -Times 0 -Scope 'It' + Assert-MockCalled -CommandName 'Clear-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Add-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Remove-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Save-Group' -Times 0 -Scope 'It' @@ -1267,7 +1272,7 @@ try Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -MembersToExclude $testMembers -Ensure 'Present' - Assert-MockCalled -CommandName 'Clear-GroupMembers' -Times 0 -Scope 'It' + Assert-MockCalled -CommandName 'Clear-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Add-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Remove-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Save-Group' -Times 0 -Scope 'It' @@ -1276,12 +1281,12 @@ try It 'Should not modify group if both MembersToInclude and MembersToExclude are empty' { $testMembers = @( ) - + Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { return @( ) } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -MembersToInclude $testMembers -MembersToExclude $testMembers -Ensure 'Present' - Assert-MockCalled -CommandName 'Clear-GroupMembers' -Times 0 -Scope 'It' + Assert-MockCalled -CommandName 'Clear-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Add-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Remove-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Save-Group' -Times 0 -Scope 'It' @@ -1290,12 +1295,12 @@ try It 'Should not modify group with no members if Members is empty' { $testMembers = @( ) - + Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -Members $testMembers -Ensure 'Present' - Assert-MockCalled -CommandName 'Clear-GroupMembers' -Times 0 -Scope 'It' + Assert-MockCalled -CommandName 'Clear-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Add-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Remove-GroupMember' -Times 0 -Scope 'It' Assert-MockCalled -CommandName 'Save-Group' -Times 0 -Scope 'It' @@ -1322,7 +1327,7 @@ try It 'Should pass Credential to all appropriate functions when using Members' { $testMembers = @( $script:testUserPrincipal1.Name ) - + Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { return @() } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -Members $testMembers -Credential $script:testCredential -Ensure 'Present' @@ -1333,7 +1338,7 @@ try It 'Should pass Credential to all appropriate functions when using MembersToInclude and MembersToExclude' { $testMembers = @( $script:testUserPrincipal1.Name ) - + Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { return @() } Set-TargetResourceOnFullSKU -GroupName $script:testGroupName -Members $testMembers -Credential $script:testCredential -Ensure 'Present' @@ -1348,19 +1353,19 @@ try Context 'Test-TargetResourceOnFullSKU' { Mock -CommandName 'Get-Group' -MockWith { } Mock -CommandName 'Get-MembersAsPrincipalsList' -MockWith { } - Mock -CommandName 'ConvertTo-UniquePrincipalsList' -MockWith { + Mock -CommandName 'ConvertTo-UniquePrincipalsList' -MockWith { $memberPrincipals = @() if ($MemberNames -contains $script:testUserPrincipal1.Name) { $memberPrincipals += @( $script:testUserPrincipal1` ) } - + if ($MemberNames -contains $script:testUserPrincipal2.Name) { $memberPrincipals += @( $script:testUserPrincipal2 ) } - + if ($MemberNames -contains $script:testUserPrincipal3.Name) { $memberPrincipals += @( $script:testUserPrincipal3 ) @@ -1368,10 +1373,10 @@ try return $memberPrincipals } - + Mock -CommandName 'Remove-DisposableObject' -MockWith { } Mock -CommandName 'Get-PrincipalContext' -MockWith { return $script:testPrincipalContext } - + It 'Should return true for an absent group when Ensure is Absent' { Test-TargetResourceOnFullSKU -GroupName $script:testGroupName -Ensure 'Absent' | Should Be $true @@ -1379,7 +1384,7 @@ try Assert-MockCalled -CommandName 'Get-Group' -ParameterFilter { $GroupName -eq $script:testGroupName } -Scope 'It' Assert-MockCalled -CommandName 'Remove-DisposableObject' } - + It 'Should return false for an absent group when Ensure is Present' { Test-TargetResourceOnFullSKU -GroupName $script:testGroupName -Ensure 'Present' | Should Be $false @@ -1408,7 +1413,7 @@ try It 'Should return true for an existing group with a matching description' { $script:testGroup.Description = $script:testGroupDescription - + Test-TargetResourceOnFullSKU -GroupName $script:testGroupName -Description $script:testGroupDescription -Ensure 'Present' | Should Be $true Assert-MockCalled -CommandName 'Get-PrincipalContext' @@ -1418,7 +1423,7 @@ try It 'Should return false for an existing group with a mismatching description' { $script:testGroup.Description = $script:testGroupDescription - + Test-TargetResourceOnFullSKU -GroupName $script:testGroupName -Description 'Wrong description' -Ensure 'Present' | Should Be $false Assert-MockCalled -CommandName 'Get-PrincipalContext' @@ -1561,7 +1566,7 @@ try { Test-TargetResourceOnFullSKU -GroupName $script:testGroupName -MembersToInclude $testMembersToInclude -MembersToExclude $testMembersToExclude -Ensure 'Present' } | Should Throw $errorMessage } } - + Context 'Get-MembersOnFullSKU' { $principalContextCache = @{} $disposables = New-Object -TypeName 'System.Collections.ArrayList' @@ -1613,7 +1618,7 @@ try $expectedGetMembersResult = @( $expectedName1, $expectedName2 ) $getMembersResult = Get-MembersOnFullSKU -Group $script:testGroup -PrincipalContextCache $principalContextCache -Disposables $disposables - + (Compare-Object -ReferenceObject $expectedGetMembersResult -DifferenceObject $getMembersResult) | Should Be $null Assert-MockCalled -CommandName 'Get-MembersAsPrincipalsList' -ParameterFilter { $Group.Name -eq $script:testGroupName } @@ -1623,16 +1628,16 @@ try Context 'Get-MembersAsPrincipalsList' { $principalContextCache = @{} $disposables = New-Object -TypeName 'System.Collections.ArrayList' - + Mock -CommandName 'Get-GroupMembersFromDirectoryEntry' -MockWith { } - + Mock -CommandName 'New-Object' -ParameterFilter { $TypeName -eq 'System.DirectoryServices.DirectoryEntry' } -MockWith { return $ArgumentList[0] } - + Mock -CommandName 'Get-PrincipalContext' -MockWith { return $script:testPrincipalContext } Mock -CommandName 'Test-IsLocalMachine' -MockWith { return $true } - + Mock -CommandName 'New-Object' -ParameterFilter { $TypeName -eq 'System.Security.Principal.SecurityIdentifier' } -MockWith { return 'S-1-0-0' } @@ -1714,7 +1719,7 @@ try It 'Should pass Credential to appropriate functions' { $getMembersResult = Get-MembersAsPrincipalsList -Group $script:testGroup -Credential $script:testCredential -PrincipalContextCache $principalContextCache -Disposables $disposables - + Assert-MockCalled -CommandName 'Get-PrincipalContext' -ParameterFilter { $Credential -eq $script:testCredential } Assert-MockCalled -CommandName 'Get-PrincipalContext' -ParameterFilter { $Credential -eq $script:testCredential} } @@ -1731,7 +1736,7 @@ try Context 'ConvertTo-UniquePrincipalsList' { $principalContextCache = @{} $disposables = New-Object -TypeName 'System.Collections.ArrayList' - + $testDomainUser1 = @{ Name = 'TestDomainUser1' SamAccountName = 'TestSamAccountName1' @@ -1751,11 +1756,11 @@ try $script:testUserPrincipal3.Name { return $script:testUserPrincipal3 } $testDomainUser1.Name { return $testDomainUser1 } } - } - + } + It 'Should not return duplicate local prinicpals' { $memberNames = @( $script:testUserPrincipal1.Name, $script:testUserPrincipal1.Name, $script:testUserPrincipal2.Name ) - + $uniquePrincipalsList = ConvertTo-UniquePrincipalsList -MemberNames $memberNames -PrincipalContextCache $principalContextCache -Disposables $disposables $uniquePrincipalsList | Should Be @( $script:testUserPrincipal1, $script:testUserPrincipal2 ) @@ -1787,7 +1792,7 @@ try Context 'ConvertTo-Principal' { $principalContextCache = @{} $disposables = New-Object -TypeName 'System.Collections.ArrayList' - + Mock -CommandName 'Split-MemberName' -MockWith { return $script:localDomain, $MemberName } Mock -CommandName 'Test-IsLocalMachine' -MockWith { return $true } Mock -CommandName 'Get-PrincipalContext' -MockWith { return $script:testPrincipalContext } @@ -1840,7 +1845,7 @@ try It 'Should throw if principal cannot be found' { $errorMessage = ($script:localizedData.CouldNotFindPrincipal -f $script:testUserPrincipal1.Name) - + { $convertToPrincipalResult = ConvertTo-Principal ` -MemberName $script:testUserPrincipal1.Name ` -PrincipalContextCache $principalContextCache ` @@ -1886,7 +1891,7 @@ try Context 'Get-PrincipalContext' { $fakePrincipalContext = 'FakePrincipalContext' - + Mock -CommandName 'Test-IsLocalMachine' -MockWith { return $true } Mock -CommandName 'New-Object' -ParameterFilter { $TypeName -eq 'System.DirectoryServices.AccountManagement.PrincipalContext' } -MockWith { $fakePrincipalContext } @@ -1912,12 +1917,12 @@ try It 'Should return the local principal context from the cache' { $principalContextCache = @{ $script:localDomain = $script:testPrincipalContext } $disposables = New-Object -TypeName 'System.Collections.ArrayList' - + Get-PrincipalContext -Scope $script:localDomain -PrincipalContextCache $principalContextCache -Disposables $disposables Assert-MockCalled -CommandName 'Test-IsLocalMachine' -ParameterFilter { $Scope -eq $script:localDomain } Assert-MockCalled -CommandName 'New-Object' -ParameterFilter { $TypeName -eq 'System.DirectoryServices.AccountManagement.PrincipalContext' } -Times 0 -Scope 'It' - + $principalContextCache.$script:localDomain | Should Not Be $fakePrincipalContext $disposables.Contains($fakePrincipalContext) | Should Be $false } @@ -1929,7 +1934,7 @@ try $disposables = New-Object -TypeName 'System.Collections.ArrayList' $customDomain = 'CustomDomain' - + Get-PrincipalContext -Scope $customDomain -PrincipalContextCache $principalContextCache -Disposables $disposables Assert-MockCalled -CommandName 'Test-IsLocalMachine' -ParameterFilter { $Scope -eq $customDomain } @@ -1948,7 +1953,7 @@ try $disposables = New-Object -TypeName 'System.Collections.ArrayList' $customDomain = 'CustomDomain' - + Get-PrincipalContext -Scope $customDomain -Credential $script:testCredential -PrincipalContextCache $principalContextCache -Disposables $disposables Assert-MockCalled -CommandName 'Test-IsLocalMachine' -ParameterFilter { $Scope -eq $customDomain } @@ -1967,7 +1972,7 @@ try $disposables = New-Object -TypeName 'System.Collections.ArrayList' $customDomain = 'CustomDomain' - + $userNameWithDomain = 'CustomDomain\username' $testPassword = 'TestPassword' $secureTestPassword = ConvertTo-SecureString -String $testPassword -AsPlainText -Force @@ -1989,22 +1994,22 @@ try It 'Should return a custom principal context from the cache' { $customDomain = 'CustomDomain' - + $principalContextCache = @{ $customDomain = $script:testPrincipalContext } $disposables = New-Object -TypeName 'System.Collections.ArrayList' - + Get-PrincipalContext -Scope $customDomain -PrincipalContextCache $principalContextCache -Disposables $disposables Assert-MockCalled -CommandName 'Test-IsLocalMachine' -ParameterFilter { $Scope -eq $customDomain } Assert-MockCalled -CommandName 'New-Object' -ParameterFilter { $TypeName -eq 'System.DirectoryServices.AccountManagement.PrincipalContext' } -Times 0 -Scope 'It' - + $principalContextCache.$customDomain | Should Not Be $fakePrincipalContext $disposables.Contains($fakePrincipalContext) | Should Be $false } } } - + } } } diff --git a/Tests/Unit/MSFT_xMsiPackage.Tests.ps1 b/Tests/Unit/MSFT_xMsiPackage.Tests.ps1 index 28729fc10..ebdb83c0b 100644 --- a/Tests/Unit/MSFT_xMsiPackage.Tests.ps1 +++ b/Tests/Unit/MSFT_xMsiPackage.Tests.ps1 @@ -1,6 +1,15 @@ $errorActionPreference = 'Stop' Set-StrictMode -Version 'Latest' +$script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent +$script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' +Import-Module -Name $commonTestHelperFilePath + +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + Describe 'xMsiPackage Unit Tests' { BeforeAll { # Import CommonTestHelper for Enter-DscResourceTestEnvironment, Exit-DscResourceTestEnvironment @@ -144,13 +153,16 @@ Describe 'xMsiPackage Unit Tests' { Mock -CommandName 'New-PSDrive' -MockWith { return $script:mockPSDrive } Mock -CommandName 'Test-Path' -MockWith { return $true } Mock -CommandName 'New-Item' -MockWith {} - Mock -CommandName 'New-Object' -MockWith { return $script:mockStream } #-ParameterFilter { $TypeName -eq 'System.IO.FileStream' } + Mock -CommandName 'New-Object' -MockWith { return $script:mockStream } Mock -CommandName 'Get-WebRequestResponse' -MockWith { return $script:mockStream } Mock -CommandName 'Copy-ResponseStreamToFileStream' -MockWith {} Mock -CommandName 'Close-Stream' -MockWith {} Mock -CommandName 'Assert-FileValid' -MockWith {} Mock -CommandName 'Get-MsiProductCode' -MockWith { return $script:testIdentifyingNumber } - Mock -CommandName 'Start-MsiProcess' -MockWith { return 0 } # returns the exit code + Mock -CommandName 'Start-MsiProcess' -MockWith { + # Returns the exit code + return 0 + } Mock -CommandName 'Remove-PSDrive' -MockWith {} Mock -CommandName 'Remove-Item' -MockWith {} Mock -CommandName 'Invoke-CimMethod' -MockWith {} diff --git a/Tests/Unit/MSFT_xPackageResource.Tests.ps1 b/Tests/Unit/MSFT_xPackageResource.Tests.ps1 index 665bf4e8b..2e16d0518 100644 --- a/Tests/Unit/MSFT_xPackageResource.Tests.ps1 +++ b/Tests/Unit/MSFT_xPackageResource.Tests.ps1 @@ -2,6 +2,11 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $script:testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $script:commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xPackageResource' ` @@ -11,6 +16,9 @@ try { InModuleScope 'MSFT_xPackageResource' { Describe 'MSFT_xPackageResource Unit Tests' { + # Override helper functions from CommonResourceHelper.psm1 + function Set-DSCMachineRebootRequired {} + BeforeAll { $testsFolderFilePath = Split-Path $PSScriptRoot -Parent $packageTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'MSFT_xPackageResource.TestHelper.psm1' @@ -495,6 +503,7 @@ try Mock Invoke-Process { return [PSCustomObject] @{ ExitCode = 3010 } } Mock Test-TargetResource { return $false } Mock Get-ProductEntry { return $null } + Mock -CommandName Set-DSCMachineRebootRequired $packageParameters = @{ Path = $script:msiLocation @@ -503,6 +512,8 @@ try } { Set-TargetResource -Ensure 'Present' @packageParameters } | Should Not Throw + + Assert-MockCalled -CommandName Set-DSCMachineRebootRequired -Times 1 } It 'Should install package using user credentials when specified' { diff --git a/Tests/Unit/MSFT_xRegistryResource.Tests.ps1 b/Tests/Unit/MSFT_xRegistryResource.Tests.ps1 index d8ea95eb7..cbf1ab451 100644 --- a/Tests/Unit/MSFT_xRegistryResource.Tests.ps1 +++ b/Tests/Unit/MSFT_xRegistryResource.Tests.ps1 @@ -6,6 +6,11 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xRegistryResource' ` @@ -18,7 +23,7 @@ try $script:validRegistryDriveRoots = @( 'HKEY_CLASSES_ROOT', 'HKEY_CURRENT_USER', 'HKEY_LOCAL_MACHINE', 'HKEY_USERS', 'HKEY_CURRENT_CONFIG' ) $script:validRegistryDriveNames = @( 'HKCR', 'HKCU', 'HKLM', 'HKUS', 'HKCC' ) - + # This registry key is used ONLY for its type (Microsoft.Win32.RegistryKey). It is not actually accessed in any way during these tests. $script:testRegistryKey = [Microsoft.Win32.Registry]::CurrentConfig @@ -37,7 +42,7 @@ try Key = 'TestRegistryKey' ValueName = '' } - + It 'Should not throw' { { $null = Get-TargetResource @getTargetResourceParameters } | Should Not Throw } @@ -97,7 +102,7 @@ try $getTargetResourceResult.ValueData | Should Be $null } } - + Mock -CommandName 'Get-RegistryKey' -MockWith { return $script:testRegistryKey } Context 'Specified registry key exists, registry key value name specified as an empty string, and registry key value data and type not specified' { @@ -105,7 +110,7 @@ try Key = 'TestRegistryKey' ValueName = '' } - + It 'Should not throw' { { $null = Get-TargetResource @getTargetResourceParameters } | Should Not Throw } @@ -171,7 +176,7 @@ try Key = 'TestRegistryKey' ValueName = 'TestValueName' } - + It 'Should not throw' { { $null = Get-TargetResource @getTargetResourceParameters } | Should Not Throw } @@ -254,7 +259,7 @@ try Key = 'TestRegistryKey' ValueName = 'TestValueName' } - + It 'Should not throw' { { $null = Get-TargetResource @getTargetResourceParameters } | Should Not Throw } @@ -347,7 +352,7 @@ try ValueType = 'String' ValueData = 'TestValueData' } - + It 'Should not throw' { { $null = Get-TargetResource @getTargetResourceParameters } | Should Not Throw } @@ -1016,7 +1021,7 @@ try Set-TargetResource @setTargetResourceParameters | Should Be $null } } - + Context 'Registry key exists, Ensure specified as Present, specified registry value does not exist, and registry value type and data not specified' { $setTargetResourceParameters = @{ Key = 'TestRegistryKey' @@ -1061,7 +1066,7 @@ try It 'Should convert the specified registry key value to a string' { $convertToStringParameterFilter = { - $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:defaultValueData -DifferenceObject $RegistryKeyValue) + $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $script:defaultValueData -DifferenceObject $RegistryKeyValue) return $registryKeyValueParameterCorrect } @@ -1181,7 +1186,7 @@ try It 'Should convert the specified registry key value to binary data' { $convertToBinaryParameterFilter = { - $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $RegistryKeyValue) + $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $RegistryKeyValue) return $registryKeyValueParameterCorrect } @@ -1311,7 +1316,7 @@ try It 'Should convert the specified registry key value to a multi-string' { $convertToMultiStringParameterFilter = { - $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $RegistryKeyValue) + $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $RegistryKeyValue) return $registryKeyValueParameterCorrect } @@ -1438,9 +1443,9 @@ try It 'Should convert the specified registry key value to a dword' { $convertToDwordParameterFilter = { - $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $RegistryKeyValue) + $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $RegistryKeyValue) $hexParameterCorrect = $Hex -eq $setTargetResourceParameters.Hex - + return $registryKeyValueParameterCorrect -and $hexParameterCorrect } @@ -1570,9 +1575,9 @@ try It 'Should convert the specified registry key value to a qword' { $convertToQwordParameterFilter = { - $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $RegistryKeyValue) + $registryKeyValueParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.ValueData -DifferenceObject $RegistryKeyValue) $hexParameterCorrect = $Hex -eq $setTargetResourceParameters.Hex - + return $registryKeyValueParameterCorrect -and $hexParameterCorrect } @@ -1846,7 +1851,7 @@ try } } } - + Describe 'xRegistry\Test-TargetResource' { Mock -CommandName 'Get-RegistryKeyValueDisplayName' -MockWith { return $RegistryKeyValueName } Mock -CommandName 'Test-RegistryKeyValuesMatch' -MockWith { return $true } @@ -2783,7 +2788,7 @@ try $mountRegistryDriveParameters = @{ RegistryDriveName = 'TestRegistryDriveName' } - + It 'Should throw error for unmountable registry drive' { $errorMessage = $script:localizedData.RegistryDriveCouldNotBeMounted -f $mountRegistryDriveParameters.RegistryDriveName @@ -2797,7 +2802,7 @@ try $mountRegistryDriveParameters = @{ RegistryDriveName = 'TestRegistryDriveName' } - + It 'Should throw error for unmountable registry drive' { $errorMessage = $script:localizedData.RegistryDriveCouldNotBeMounted -f $mountRegistryDriveParameters.RegistryDriveName @@ -2811,7 +2816,7 @@ try $mountRegistryDriveParameters = @{ RegistryDriveName = 'TestRegistryDriveName' } - + It 'Should throw error for unmountable registry drive' { $errorMessage = $script:localizedData.RegistryDriveCouldNotBeMounted -f $mountRegistryDriveParameters.RegistryDriveName @@ -2825,7 +2830,7 @@ try $mountRegistryDriveParameters = @{ RegistryDriveName = 'HKCR' } - + $expectedRegistryDriveRoot = 'HKEY_CLASSES_ROOT' It 'Should not throw' { @@ -2847,7 +2852,7 @@ try $rootParameterCorrect = $Root -eq $expectedRegistryDriveRoot $psProviderParameterCorrect = $PSProvider -eq 'Registry' $scopeParameterCorrect = $Scope -eq 'Script' - + return $nameParameterCorrect -and $rootParameterCorrect -and $psProviderParameterCorrect -and $scopeParameterCorrect } @@ -2865,7 +2870,7 @@ try $mountRegistryDriveParameters = @{ RegistryDriveName = 'TestRegistryDriveName' } - + It 'Should throw error for unmountable registry drive' { $errorMessage = $script:localizedData.RegistryDriveCouldNotBeMounted -f $mountRegistryDriveParameters.RegistryDriveName @@ -2879,7 +2884,7 @@ try $mountRegistryDriveParameters = @{ RegistryDriveName = 'TestRegistryDriveName' } - + It 'Should throw error for unmountable registry drive' { $errorMessage = $script:localizedData.RegistryDriveCouldNotBeMounted -f $mountRegistryDriveParameters.RegistryDriveName @@ -2893,7 +2898,7 @@ try $mountRegistryDriveParameters = @{ RegistryDriveName = 'HKCR' } - + $expectedRegistryDriveRoot = 'HKEY_CLASSES_ROOT' It 'Should not throw' { @@ -3223,7 +3228,7 @@ try $convertToReadableStringResult | Should Be ([String]::Empty) } } - + Context "Registry key value specified as an empty array and registry key type specified as $registryKeyValueType" { $convertToReadableStringParameters = @{ RegistryKeyValue = @() @@ -3455,7 +3460,7 @@ try $expectedRegistryKeyValue = switch ($registryKeyValueType) { 'String' { 'String1' } - 'Binary' { [Byte[]]@( 12, 172, 17, 17 ) } + 'Binary' { [Byte[]] @( 12, 172, 17, 17 ) } 'DWord' { 169 } 'QWord' { 92 } 'MultiString' { @( 'String1', 'String2' ) } @@ -3465,7 +3470,7 @@ try $mismatchingActualRegistryKeyValue = switch ($registryKeyValueType) { 'String' { 'String2' } - 'Binary' { [Byte[]]@( 11, 172, 17, 1 ) } + 'Binary' { [Byte[]] @( 11, 172, 17, 1 ) } 'DWord' { 12 } 'QWord' { 64 } 'MultiString' { @( 'String3', 'String2' ) } @@ -3561,7 +3566,7 @@ try Context 'Specified registry key value is an array containing a valid single string of an odd length' { $validBinaryString = '0xCAC1111' - $expectedByteArray = [Byte[]]@( 12, 172, 17, 17 ) + $expectedByteArray = [Byte[]] @( 12, 172, 17, 17 ) $convertToBinaryParameters = @{ RegistryKeyValue = @( $validBinaryString ) @@ -3580,7 +3585,7 @@ try Context 'Specified registry key value is an array containing a valid single string of an even length' { $validBinaryString = '0x0CAC1111' - $expectedByteArray = [Byte[]]@( 12, 172, 17, 17 ) + $expectedByteArray = [Byte[]] @( 12, 172, 17, 17 ) $convertToBinaryParameters = @{ RegistryKeyValue = @( $validBinaryString ) @@ -3599,7 +3604,7 @@ try Context 'Specified registry key value is an array containing a valid single string of an even length not starting with 0x' { $validBinaryString = '0CAC1111' - $expectedByteArray = [Byte[]]@( 12, 172, 17, 17 ) + $expectedByteArray = [Byte[]] @( 12, 172, 17, 17 ) $convertToBinaryParameters = @{ RegistryKeyValue = @( $validBinaryString ) @@ -3618,7 +3623,7 @@ try Context 'Specified registry key value is an array containing a valid single string of 0x00' { $validBinaryString = '0x00' - $expectedByteArray = [Byte[]]@( 0 ) + $expectedByteArray = [Byte[]] @( 0 ) $convertToBinaryParameters = @{ RegistryKeyValue = @( $validBinaryString ) @@ -3843,7 +3848,7 @@ try $convertToMultiStringResult = ConvertTo-MultiString @convertToMultiStringParameters It 'Should return an array containing null' { - Compare-Object -ReferenceObject ([String[]]@($null)) -DifferenceObject $convertToMultiStringResult | Should Be $null + Compare-Object -ReferenceObject ([String[]] @($null)) -DifferenceObject $convertToMultiStringResult | Should Be $null } } diff --git a/Tests/Unit/MSFT_xRemoteFile.Tests.ps1 b/Tests/Unit/MSFT_xRemoteFile.Tests.ps1 index 1ae20fe59..963a53ece 100644 --- a/Tests/Unit/MSFT_xRemoteFile.Tests.ps1 +++ b/Tests/Unit/MSFT_xRemoteFile.Tests.ps1 @@ -1,20 +1,29 @@ +$script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent +$script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' +Import-Module -Name $commonTestHelperFilePath + $Global:DSCModuleName = 'xPSDesiredStateConfiguration' $Global:DSCResourceName = 'MSFT_xRemoteFile' +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + #region HEADER # Unit Test Template Version: 1.1.0 [String] $moduleRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $Script:MyInvocation.MyCommand.Path)) if ( (-not (Test-Path -Path (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` (-not (Test-Path -Path (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) { - & git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $moduleRoot -ChildPath '\DSCResource.Tests\')) + & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $moduleRoot -ChildPath '\DSCResource.Tests\')) } Import-Module (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force $TestEnvironment = Initialize-TestEnvironment ` -DSCModuleName $Global:DSCModuleName ` -DSCResourceName $Global:DSCResourceName ` - -TestType Unit + -TestType Unit #endregion HEADER # Create a working folder that all files will be created in @@ -39,11 +48,11 @@ try [System.String] $errorMessage ) - + $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidData $exception = New-Object ` -TypeName System.InvalidOperationException ` - -ArgumentList $errorMessage + -ArgumentList $errorMessage $errorRecord = New-Object ` -TypeName System.Management.Automation.ErrorRecord ` -ArgumentList $exception, $errorId, $errorCategory, $null diff --git a/Tests/Unit/MSFT_xScriptResource.Tests.ps1 b/Tests/Unit/MSFT_xScriptResource.Tests.ps1 index 7e45dbe43..dfd2f275e 100644 --- a/Tests/Unit/MSFT_xScriptResource.Tests.ps1 +++ b/Tests/Unit/MSFT_xScriptResource.Tests.ps1 @@ -6,6 +6,11 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DSCResourceModuleName 'xPSDesiredStateConfiguration' ` -DSCResourceName 'MSFT_xScriptResource' ` @@ -77,7 +82,7 @@ try { TestScript = 'NotUsed' SetScript = 'NotUsed' } - + It 'Should not throw' { { $null = Get-TargetResource @getTargetResourceParameters } | Should Not Throw } @@ -94,7 +99,7 @@ try { Assert-MockCalled -CommandName 'Invoke-Script' -ParameterFilter $invokeScriptParameterFilter -Times 1 -Scope 'It' } - + It 'Should return a hashtable' { $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters $getTargetResourceResult -is [Hashtable] | Should Be $true @@ -113,7 +118,7 @@ try { SetScript = 'NotUsed' Credential = $script:testCredenital } - + It 'Should not throw' { { $null = Get-TargetResource @getTargetResourceParameters } | Should Not Throw } @@ -126,13 +131,13 @@ try { $invokeScriptParameterFilter = { $scriptBlockParameterCorrect = $null -eq (Compare-Object -ReferenceObject $expectedScriptBlock.Ast -DifferenceObject $ScriptBlock.Ast) $credentialParameterCorrect = $null -eq (Compare-Object -ReferenceObject $getTargetResourceParameters.Credential -DifferenceObject $Credential) - - return $scriptBlockParameterCorrect -and $credentialParameterCorrect + + return $scriptBlockParameterCorrect -and $credentialParameterCorrect } Assert-MockCalled -CommandName 'Invoke-Script' -ParameterFilter $invokeScriptParameterFilter -Times 1 -Scope 'It' } - + It 'Should return a hashtable' { $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters $getTargetResourceResult -is [Hashtable] | Should Be $true @@ -193,8 +198,8 @@ try { $invokeScriptParameterFilter = { $scriptBlockParameterCorrect = $null -eq (Compare-Object -ReferenceObject $expectedScriptBlock.Ast -DifferenceObject $ScriptBlock.Ast) $credentialParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.Credential -DifferenceObject $Credential) - - return $scriptBlockParameterCorrect -and $credentialParameterCorrect + + return $scriptBlockParameterCorrect -and $credentialParameterCorrect } Assert-MockCalled -CommandName 'Invoke-Script' -ParameterFilter $invokeScriptParameterFilter -Times 1 -Scope 'It' @@ -280,7 +285,7 @@ try { Assert-MockCalled -CommandName 'Invoke-Script' -ParameterFilter $invokeScriptParameterFilter -Times 1 -Scope 'It' } - + It 'Should return the expected boolean' { $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters $testTargetResourceResult | Should Be $expectedBoolean @@ -307,13 +312,13 @@ try { $invokeScriptParameterFilter = { $scriptBlockParameterCorrect = $null -eq (Compare-Object -ReferenceObject $expectedScriptBlock.Ast -DifferenceObject $ScriptBlock.Ast) $credentialParameterCorrect = $null -eq (Compare-Object -ReferenceObject $testTargetResourceParameters.Credential -DifferenceObject $Credential) - - return $scriptBlockParameterCorrect -and $credentialParameterCorrect + + return $scriptBlockParameterCorrect -and $credentialParameterCorrect } Assert-MockCalled -CommandName 'Invoke-Script' -ParameterFilter $invokeScriptParameterFilter -Times 1 -Scope 'It' } - + It 'Should return the expected boolean' { $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters $testTargetResourceResult | Should Be $expectedBoolean @@ -346,7 +351,7 @@ try { Assert-MockCalled -CommandName 'Invoke-Script' -ParameterFilter $invokeScriptParameterFilter -Times 1 -Scope 'It' } - + It 'Should return the expected boolean' { $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters $testTargetResourceResult | Should Be $expectedBoolean @@ -443,7 +448,7 @@ try { It 'Should return result of script' { $scriptExecutionHelperResult = Invoke-Script @scriptExecutionHelperParameters $scriptExecutionHelperResult | Should Be $testScriptResult - } + } } } } diff --git a/Tests/Unit/MSFT_xServiceResource.Tests.ps1 b/Tests/Unit/MSFT_xServiceResource.Tests.ps1 index ec95c9f68..9d7ae554e 100644 --- a/Tests/Unit/MSFT_xServiceResource.Tests.ps1 +++ b/Tests/Unit/MSFT_xServiceResource.Tests.ps1 @@ -10,6 +10,11 @@ $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $script:commonTestHelperFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $commonTestHelperFilePath +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DSCResourceModuleName 'xPSDesiredStateConfiguration' ` -DSCResourceName 'MSFT_xServiceResource' ` @@ -1975,7 +1980,7 @@ try } } - Describe 'xService\Set-ServiceDependencies' { + Describe 'xService\Set-ServiceDependency' { $testServiceCimInstance = New-CimInstance -ClassName 'Win32_Service' -ClientOnly try { @@ -1999,7 +2004,7 @@ try } It 'Should not throw' { - { Set-ServiceDependencies @setServiceDependenciesParameters } | Should Not Throw + { Set-ServiceDependency @setServiceDependenciesParameters } | Should Not Throw } It 'Should retrieve the service' { @@ -2022,7 +2027,7 @@ try } It 'Should not throw' { - { Set-ServiceDependencies @setServiceDependenciesParameters } | Should Not Throw + { Set-ServiceDependency @setServiceDependenciesParameters } | Should Not Throw } It 'Should retrieve the service' { @@ -2045,7 +2050,7 @@ try } It 'Should not throw' { - { Set-ServiceDependencies @setServiceDependenciesParameters } | Should Not Throw + { Set-ServiceDependency @setServiceDependenciesParameters } | Should Not Throw } It 'Should retrieve the service' { @@ -2074,7 +2079,7 @@ try } It 'Should not throw' { - { Set-ServiceDependencies @setServiceDependenciesParameters } | Should Not Throw + { Set-ServiceDependency @setServiceDependenciesParameters } | Should Not Throw } It 'Should retrieve the service' { @@ -2097,7 +2102,7 @@ try } It 'Should not throw' { - { Set-ServiceDependencies @setServiceDependenciesParameters } | Should Not Throw + { Set-ServiceDependency @setServiceDependenciesParameters } | Should Not Throw } It 'Should retrieve the service' { @@ -2128,7 +2133,7 @@ try It 'Should throw error for failed service path change' { $errorMessage = $script:localizedData.InvokeCimMethodFailed -f 'Change', $setServiceDependenciesParameters.ServiceName, 'ServiceDependencies', $invokeCimMethodFailResult.ReturnValue - { Set-ServiceDependencies @setServiceDependenciesParameters } | Should Throw $errorMessage + { Set-ServiceDependency @setServiceDependenciesParameters } | Should Throw $errorMessage } } } @@ -2541,7 +2546,7 @@ try Mock -CommandName 'Get-ServiceCimInstance' -MockWith { return $testServiceCimInstance } Mock -CommandName 'Set-Service' -MockWith { } - Mock -CommandName 'Set-ServiceDependencies' -MockWith { } + Mock -CommandName 'Set-ServiceDependency' -MockWith { } Mock -CommandName 'Set-ServiceAccountProperty' -MockWith { } Mock -CommandName 'Set-ServiceStartupType' -MockWith { } @@ -2563,7 +2568,7 @@ try } It 'Should not set service dependencies' { - Assert-MockCalled -CommandName 'Set-ServiceDependencies' -Times 0 -Scope 'Context' + Assert-MockCalled -CommandName 'Set-ServiceDependency' -Times 0 -Scope 'Context' } It 'Should not set service account properties' { @@ -2594,7 +2599,7 @@ try } It 'Should not set service dependencies' { - Assert-MockCalled -CommandName 'Set-ServiceDependencies' -Times 0 -Scope 'Context' + Assert-MockCalled -CommandName 'Set-ServiceDependency' -Times 0 -Scope 'Context' } It 'Should not set service account properties' { @@ -2625,7 +2630,7 @@ try } It 'Should not set service dependencies' { - Assert-MockCalled -CommandName 'Set-ServiceDependencies' -Times 0 -Scope 'Context' + Assert-MockCalled -CommandName 'Set-ServiceDependency' -Times 0 -Scope 'Context' } It 'Should not set service account properties' { @@ -2657,7 +2662,7 @@ try } It 'Should not set service dependencies' { - Assert-MockCalled -CommandName 'Set-ServiceDependencies' -Times 0 -Scope 'Context' + Assert-MockCalled -CommandName 'Set-ServiceDependency' -Times 0 -Scope 'Context' } It 'Should not set service account properties' { @@ -2680,7 +2685,13 @@ try } It 'Should retrieve service CIM instance' { - Assert-MockCalled -CommandName 'Get-ServiceCimInstance' -ParameterFilter { $ServiceName -eq $setServicePropertyParameters.ServiceName } -Times 1 -Scope 'Context' + Assert-MockCalled ` + -CommandName 'Get-ServiceCimInstance' ` + -ParameterFilter { + $ServiceName -eq $setServicePropertyParameters.ServiceName + } ` + -Times 1 ` + -Scope 'Context' } It 'Should not set service description or display name' { @@ -2688,7 +2699,14 @@ try } It 'Should set service dependencies' { - Assert-MockCalled -CommandName 'Set-ServiceDependencies' -ParameterFilter { $ServiceName -eq $setServicePropertyParameters.ServiceName -and $null -eq (Compare-Object -ReferenceObject $setServicePropertyParameters.Dependencies -DifferenceObject $Dependencies) } -Times 1 -Scope 'Context' + Assert-MockCalled ` + -CommandName 'Set-ServiceDependency' ` + -ParameterFilter { + $ServiceName -eq $setServicePropertyParameters.ServiceName -and ` + $null -eq (Compare-Object -ReferenceObject $setServicePropertyParameters.Dependencies -DifferenceObject $Dependencies) + } ` + -Times 1 ` + -Scope 'Context' } It 'Should not set service account properties' { @@ -2719,7 +2737,7 @@ try } It 'Should not set service dependencies' { - Assert-MockCalled -CommandName 'Set-ServiceDependencies' -Times 0 -Scope 'Context' + Assert-MockCalled -CommandName 'Set-ServiceDependency' -Times 0 -Scope 'Context' } It 'Should set service account properties' { @@ -2750,7 +2768,7 @@ try } It 'Should not set service dependencies' { - Assert-MockCalled -CommandName 'Set-ServiceDependencies' -Times 0 -Scope 'Context' + Assert-MockCalled -CommandName 'Set-ServiceDependency' -Times 0 -Scope 'Context' } It 'Should set service account properties' { @@ -2781,7 +2799,7 @@ try } It 'Should not set service dependencies' { - Assert-MockCalled -CommandName 'Set-ServiceDependencies' -Times 0 -Scope 'Context' + Assert-MockCalled -CommandName 'Set-ServiceDependency' -Times 0 -Scope 'Context' } It 'Should set service account properties' { @@ -2813,7 +2831,7 @@ try } It 'Should not set service dependencies' { - Assert-MockCalled -CommandName 'Set-ServiceDependencies' -Times 0 -Scope 'Context' + Assert-MockCalled -CommandName 'Set-ServiceDependency' -Times 0 -Scope 'Context' } It 'Should set service account properties' { @@ -2844,7 +2862,7 @@ try } It 'Should not set service dependencies' { - Assert-MockCalled -CommandName 'Set-ServiceDependencies' -Times 0 -Scope 'Context' + Assert-MockCalled -CommandName 'Set-ServiceDependency' -Times 0 -Scope 'Context' } It 'Should not set service account properties' { diff --git a/Tests/Unit/MSFT_xUserResource.Tests.ps1 b/Tests/Unit/MSFT_xUserResource.Tests.ps1 index 8e2c0f2a9..01dee4f3b 100644 --- a/Tests/Unit/MSFT_xUserResource.Tests.ps1 +++ b/Tests/Unit/MSFT_xUserResource.Tests.ps1 @@ -6,6 +6,11 @@ Import-Module -Name (Join-Path -Path (Split-Path $PSScriptRoot -Parent) ` -ChildPath 'CommonTestHelper.psm1') ` -Force +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DSCResourceModuleName 'xPSDesiredStateConfiguration' ` -DSCResourceName 'MSFT_xUserResource' ` @@ -21,31 +26,31 @@ try { InModuleScope 'MSFT_xUserResource' { # Used to skip the Nano server tests for the time being since they are not working on AppVeyor - + $script:skipMe = $true - + $existingUserName = 'TestUserName12345' $existingUserPassword = 'StrongOne7.' $existingDescription = 'Some Description' $existingSecurePassword = ConvertTo-SecureString $existingUserPassword -AsPlainText -Force $existingTestCredential = New-Object PSCredential ($existingUserName, $existingSecurePassword) - + New-User -Credential $existingTestCredential -Description $existingDescription - + $newUserName1 = 'NewTestUserName12345' $newUserPassword1 = 'NewStrongOne123.' $newFullName1 = 'Fullname1' $newUserDescription1 = 'New Description1' $newSecurePassword1 = ConvertTo-SecureString $newUserPassword1 -AsPlainText -Force $newCredential1 = New-Object PSCredential ($newUserName1, $newSecurePassword1) - + $newUserName2 = 'newUser1234' $newPassword2 = 'ThisIsAStrongPassword543!' $newFullName2 = 'Fullname2' $newUserDescription2 = 'New Description2' $newSecurePassword2 = ConvertTo-SecureString $newPassword2 -AsPlainText -Force $newCredential2 = New-Object PSCredential ($newUserName2, $newSecurePassword2) - + try { Describe 'xUserResource/Get-TargetResource' { @@ -94,28 +99,28 @@ try { Describe 'xUserResource/Set-TargetResource' { Context 'Tests on FullSKU' { Mock -CommandName Test-IsNanoServer -MockWith { return $false } - + try { New-User -Credential $newCredential1 -Description $newUserDescription1 - + It 'Should remove the user' { Test-User -UserName $newUserName1 | Should Be $true Set-TargetResource -UserName $newUserName1 -Ensure 'Absent' Test-User -UserName $newUserName1 | Should Be $false } - + It 'Should add the new user' { Set-TargetResource -UserName $newUserName2 -Password $newCredential2 -Ensure 'Present' Test-User -UserName $newUserName2 | Should Be $true } - + It 'Should update the user' { $disabled = $false $passwordNeverExpires = $true $passwordChangeRequired = $false $passwordChangeNotAllowed = $true - + Set-TargetResource -UserName $newUserName2 ` -Password $newCredential2 ` -Ensure 'Present' ` @@ -125,9 +130,9 @@ try { -PasswordNeverExpires $passwordNeverExpires ` -PasswordChangeRequired $passwordChangeRequired ` -PasswordChangeNotAllowed $passwordChangeNotAllowed - + Test-User -UserName $newUserName2 | Should Be $true - $testTargetResourceResult1 = + $testTargetResourceResult1 = Test-TargetResource -UserName $newUserName2 ` -Password $newCredential2 ` -Ensure 'Present' ` @@ -143,7 +148,7 @@ try { $passwordNeverExpires = $false $passwordChangeRequired = $true $passwordChangeNotAllowed = $false - + Set-TargetResource -UserName $newUserName2 ` -Password $newCredential1 ` -Ensure 'Present' ` @@ -153,9 +158,9 @@ try { -PasswordNeverExpires $passwordNeverExpires ` -PasswordChangeRequired $passwordChangeRequired ` -PasswordChangeNotAllowed $passwordChangeNotAllowed - + Test-User -UserName $newUserName2 | Should Be $true - $testTargetResourceResult2 = + $testTargetResourceResult2 = Test-TargetResource -UserName $newUserName2 ` -Password $newCredential1 ` -Ensure 'Present' ` @@ -177,28 +182,28 @@ try { Context 'Tests on Nano Server' { Mock -CommandName Test-IsNanoServer -MockWith { return $true } Mock -CommandName Test-CredentialsValidOnNanoServer { return $true } - + try { New-User -Credential $newCredential1 -Description $newUserDescription1 - + It 'Should remove the user' -Skip:$script:skipMe { Test-User -UserName $newUserName1 | Should Be $true Set-TargetResource -UserName $newUserName1 -Ensure 'Absent' Test-User -UserName $newUserName1 | Should Be $false } - + It 'Should add the new user' -Skip:$script:skipMe { Set-TargetResource -UserName $newUserName2 -Password $newCredential2 -Ensure 'Present' Test-User -UserName $newUserName2 | Should Be $true } - + It 'Should update the user' -Skip:$script:skipMe { $disabled = $false $passwordNeverExpires = $true $passwordChangeRequired = $false $passwordChangeNotAllowed = $true - + Set-TargetResource -UserName $newUserName2 ` -Password $newCredential2 ` -Ensure 'Present' ` @@ -208,9 +213,9 @@ try { -PasswordNeverExpires $passwordNeverExpires ` -PasswordChangeRequired $passwordChangeRequired ` -PasswordChangeNotAllowed $passwordChangeNotAllowed - + Test-User -UserName $newUserName2 | Should Be $true - $testTargetResourceResult1 = + $testTargetResourceResult1 = Test-TargetResource -UserName $newUserName2 ` -Password $newCredential2 ` -Ensure 'Present' ` @@ -226,7 +231,7 @@ try { $passwordNeverExpires = $false $passwordChangeRequired = $true $passwordChangeNotAllowed = $false - + Set-TargetResource -UserName $newUserName2 ` -Password $newCredential1 ` -Ensure 'Present' ` @@ -236,9 +241,9 @@ try { -PasswordNeverExpires $passwordNeverExpires ` -PasswordChangeRequired $passwordChangeRequired ` -PasswordChangeNotAllowed $passwordChangeNotAllowed - + Test-User -UserName $newUserName2 | Should Be $true - $testTargetResourceResult2 = + $testTargetResourceResult2 = Test-TargetResource -UserName $newUserName2 ` -Password $newCredential1 ` -Ensure 'Present' ` @@ -262,7 +267,7 @@ try { Context 'Tests on FullSKU' { Mock -CommandName Test-IsNanoServer -MockWith { return $false } $absentUserName = 'AbsentUserUserName123456789' - + It 'Should return true when user Present and correct values' { $testTargetResourceResult = Test-TargetResource -UserName $existingUserName ` -Description $existingDescription ` @@ -272,7 +277,7 @@ try { -PasswordChangeNotAllowed $false $testTargetResourceResult | Should Be $true } - + It 'Should return true when user Absent and Ensure = Absent' { $testTargetResourceResult = Test-TargetResource -UserName $absentUserName ` -Ensure 'Absent' @@ -284,23 +289,23 @@ try { -Ensure 'Present' $testTargetResourceResult | Should Be $false } - + It 'Should return false when user Present and Ensure = Absent' { $testTargetResourceResult = Test-TargetResource -UserName $existingUserName ` -Ensure 'Absent' $testTargetResourceResult | Should Be $false } - + It 'Should return false when Password is wrong' { $badPassword = 'WrongPassword' $secureBadPassword = ConvertTo-SecureString $badPassword -AsPlainText -Force $badTestCredential = New-Object PSCredential ($existingUserName, $secureBadPassword) - + $testTargetResourceResult = Test-TargetResource -UserName $existingUserName ` -Password $badTestCredential $testTargetResourceResult | Should Be $false } - + It 'Should return false when user Present and wrong Description' { $testTargetResourceResult = Test-TargetResource -UserName $existingUserName ` -Description 'Wrong description' @@ -310,36 +315,36 @@ try { It 'Should return false when FullName is incorrect' { $testTargetResourceResult = Test-TargetResource -UserName $existingUserName ` -FullName 'Wrong FullName' - $testTargetResourceResult | Should Be $false + $testTargetResourceResult | Should Be $false } - + It 'Should return false when Disabled is incorrect' { $testTargetResourceResult = Test-TargetResource -UserName $existingUserName ` -Disabled $true - $testTargetResourceResult | Should Be $false + $testTargetResourceResult | Should Be $false } - + It 'Should return false when PasswordNeverExpires is incorrect' { $testTargetResourceResult = Test-TargetResource -UserName $existingUserName ` -PasswordNeverExpires $true - $testTargetResourceResult | Should Be $false + $testTargetResourceResult | Should Be $false } - + It 'Should return false when PasswordChangeNotAllowed is incorrect' { $testTargetResourceResult = Test-TargetResource -UserName $existingUserName ` -PasswordChangeNotAllowed $true - $testTargetResourceResult | Should Be $false + $testTargetResourceResult | Should Be $false } } - + Context 'Tests on Nano Server' { Mock -CommandName Test-IsNanoServer -MockWith { return $true } - + $absentUserName = 'AbsentUserUserName123456789' - + It 'Should return true when user Present and correct values' -Skip:$script:skipMe { Mock -CommandName Test-CredentialsValidOnNanoServer { return $true } - + $testTargetResourceResult = Test-TargetResource -UserName $existingUserName ` -Description $existingDescription ` -Password $existingTestCredential ` @@ -348,7 +353,7 @@ try { -PasswordChangeNotAllowed $false $testTargetResourceResult | Should Be $true } - + It 'Should return true when user Absent and Ensure = Absent' -Skip:$script:skipMe { $testTargetResourceResult = Test-TargetResource -UserName $absentUserName ` -Ensure 'Absent' @@ -360,25 +365,25 @@ try { -Ensure 'Present' $testTargetResourceResult | Should Be $false } - + It 'Should return false when user Present and Ensure = Absent' -Skip:$script:skipMe { $testTargetResourceResult = Test-TargetResource -UserName $existingUserName ` -Ensure 'Absent' $testTargetResourceResult | Should Be $false } - + It 'Should return false when Password is wrong' -Skip:$script:skipMe { Mock -CommandName Test-CredentialsValidOnNanoServer { return $false } - + $badPassword = 'WrongPassword' $secureBadPassword = ConvertTo-SecureString $badPassword -AsPlainText -Force $badTestCredential = New-Object PSCredential ($existingUserName, $secureBadPassword) - + $testTargetResourceResult = Test-TargetResource -UserName $existingUserName ` -Password $badTestCredential $testTargetResourceResult | Should Be $false } - + It 'Should return false when user Present and wrong Description' -Skip:$script:skipMe { $testTargetResourceResult = Test-TargetResource -UserName $existingUserName ` -Description 'Wrong description' @@ -388,34 +393,34 @@ try { It 'Should return false when FullName is incorrect' -Skip:$script:skipMe { $testTargetResourceResult = Test-TargetResource -UserName $existingUserName ` -FullName 'Wrong FullName' - $testTargetResourceResult | Should Be $false + $testTargetResourceResult | Should Be $false } - + It 'Should return false when Disabled is incorrect' -Skip:$script:skipMe { $testTargetResourceResult = Test-TargetResource -UserName $existingUserName ` -Disabled $true - $testTargetResourceResult | Should Be $false + $testTargetResourceResult | Should Be $false } - + It 'Should return false when PasswordNeverExpires is incorrect' -Skip:$script:skipMe { $testTargetResourceResult = Test-TargetResource -UserName $existingUserName ` -PasswordNeverExpires $true - $testTargetResourceResult | Should Be $false + $testTargetResourceResult | Should Be $false } - + It 'Should return false when PasswordChangeNotAllowed is incorrect' -Skip:$script:skipMe { $testTargetResourceResult = Test-TargetResource -UserName $existingUserName ` -PasswordChangeNotAllowed $true - $testTargetResourceResult | Should Be $false + $testTargetResourceResult | Should Be $false } } } - + Describe 'xUserResource/Assert-UserNameValid' { It 'Should not throw when username contains all valid chars' { { Assert-UserNameValid -UserName 'abc123456!f_t-l098s' } | Should Not Throw } - + It 'Should throw InvalidArgumentError when username contains only whitespace and dots' { $invalidName = ' . .. . ' $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument @@ -425,7 +430,7 @@ try { $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $null { Assert-UserNameValid -UserName $invalidName } | Should Throw $errorRecord } - + It 'Should throw InvalidArgumentError when username contains an invalid char' { $invalidName = 'user|name' $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument diff --git a/Tests/Unit/MSFT_xWindowsFeature.Tests.ps1 b/Tests/Unit/MSFT_xWindowsFeature.Tests.ps1 index defbc825a..3a3a83284 100644 --- a/Tests/Unit/MSFT_xWindowsFeature.Tests.ps1 +++ b/Tests/Unit/MSFT_xWindowsFeature.Tests.ps1 @@ -6,6 +6,11 @@ Import-Module -Name (Join-Path -Path (Split-Path $PSScriptRoot -Parent) ` -ChildPath 'CommonTestHelper.psm1') ` -Force +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DSCResourceModuleName 'xPSDesiredStateConfiguration' ` -DSCResourceName 'MSFT_xWindowsFeature' ` @@ -27,19 +32,19 @@ try { $mockWindowsFeatures = @{ - Test1 = @{ + Test1 = @{ Name = 'Test1' DisplayName = 'Test Feature 1' Description = 'Test Feature with 3 subfeatures' - Installed = $false - InstallState = 'Available' + Installed = $false + InstallState = 'Available' FeatureType = 'Role Service' Path = 'Test1' Depth = 1 DependsOn = @() Parent = '' ServerComponentDescriptor = 'ServerComponent_Test_Cert_Authority' - Subfeatures = @('SubTest1','SubTest2','SubTest3') + Subfeatures = @('SubTest1', 'SubTest2', 'SubTest3') SystemService = @() Notification = @() BestPracticesModelId = $null @@ -48,7 +53,7 @@ try { AdditionalInfo = @('MajorVersion', 'MinorVersion', 'NumericId', 'InstallName') } - SubTest1 = @{ + SubTest1 = @{ Name = 'SubTest1' DisplayName = 'Sub Test Feature 1' Description = 'Sub Test Feature with parent as test1' @@ -69,7 +74,7 @@ try { AdditionalInfo = @('MajorVersion', 'MinorVersion', 'NumericId', 'InstallName') } - SubTest2 = @{ + SubTest2 = @{ Name = 'SubTest2' DisplayName = 'Sub Test Feature 2' Description = 'Sub Test Feature with parent as test1' @@ -111,12 +116,12 @@ try { AdditionalInfo = @('MajorVersion', 'MinorVersion', 'NumericId', 'InstallName') } - Test2 = @{ + Test2 = @{ Name = 'Test2' DisplayName = 'Test Feature 2' Description = 'Test Feature with 0 subfeatures' - Installed = $true - InstallState = 'Available' + Installed = $true + InstallState = 'Available' FeatureType = 'Role Service' Path = 'Test2' Depth = 1 @@ -141,7 +146,7 @@ try { $windowsFeature = $mockWindowsFeatures[$testWindowsFeatureName2] $windowsFeatureObject = New-Object -TypeName PSObject -Property $windowsFeature $windowsFeatureObject.PSTypeNames[0] = 'Microsoft.Windows.ServerManager.Commands.Feature' - + return $windowsFeatureObject } @@ -149,7 +154,7 @@ try { $windowsFeature = $mockWindowsFeatures[$testWindowsFeatureName1] $windowsFeatureObject = New-Object -TypeName PSObject -Property $windowsFeature $windowsFeatureObject.PSTypeNames[0] = 'Microsoft.Windows.ServerManager.Commands.Feature' - + return $windowsFeatureObject } @@ -157,7 +162,7 @@ try { $windowsFeature = $mockWindowsFeatures[$testSubFeatureName1] $windowsFeatureObject = New-Object -TypeName PSObject -Property $windowsFeature $windowsFeatureObject.PSTypeNames[0] = 'Microsoft.Windows.ServerManager.Commands.Feature' - + return $windowsFeatureObject } @@ -165,7 +170,7 @@ try { $windowsFeature = $mockWindowsFeatures[$testSubFeatureName2] $windowsFeatureObject = New-Object -TypeName PSObject -Property $windowsFeature $windowsFeatureObject.PSTypeNames[0] = 'Microsoft.Windows.ServerManager.Commands.Feature' - + return $windowsFeatureObject } @@ -173,13 +178,13 @@ try { $windowsFeature = $mockWindowsFeatures[$testSubFeatureName3] $windowsFeatureObject = New-Object -TypeName PSObject -Property $windowsFeature $windowsFeatureObject.PSTypeNames[0] = 'Microsoft.Windows.ServerManager.Commands.Feature' - + return $windowsFeatureObject } - + Context 'Windows Feature exists with no sub features' { - + It 'Should return the correct hashtable when not on a 2008 Server' { Mock -CommandName Test-IsWinServer2008R2SP1 -MockWith { return $false } @@ -202,11 +207,11 @@ try { It 'Should return the correct hashtable when on a 2008 Server and Credential is passed' { Mock -CommandName Test-IsWinServer2008R2SP1 -MockWith { return $true } - Mock -CommandName Invoke-Command -MockWith { + Mock -CommandName Invoke-Command -MockWith { $windowsFeature = $mockWindowsFeatures[$testWindowsFeatureName2] $windowsFeatureObject = New-Object -TypeName PSObject -Property $windowsFeature $windowsFeatureObject.PSTypeNames[0] = 'Microsoft.Windows.ServerManager.Commands.Feature' - + return $windowsFeatureObject } @@ -259,7 +264,7 @@ try { } } } - + Describe 'xWindowsFeature/Set-TargetResource' { Mock -CommandName Import-ServerManager -MockWith {} @@ -275,7 +280,7 @@ try { } $windowsFeatureObject = New-Object -TypeName PSObject -Property $windowsFeature $windowsFeatureObject.PSTypeNames[0] = 'Microsoft.Windows.ServerManager.Commands.Feature' - + return $windowsFeatureObject } @@ -288,7 +293,7 @@ try { } $windowsFeatureObject = New-Object -TypeName PSObject -Property $windowsFeature $windowsFeatureObject.PSTypeNames[0] = 'Microsoft.Windows.ServerManager.Commands.Feature' - + return $windowsFeatureObject } @@ -316,7 +321,7 @@ try { } $windowsFeatureObject = New-Object -TypeName PSObject -Property $windowsFeature $windowsFeatureObject.PSTypeNames[0] = 'Microsoft.Windows.ServerManager.Commands.Feature' - + return $windowsFeatureObject } @@ -329,18 +334,18 @@ try { } $windowsFeatureObject = New-Object -TypeName PSObject -Property $windowsFeature $windowsFeatureObject.PSTypeNames[0] = 'Microsoft.Windows.ServerManager.Commands.Feature' - + return $windowsFeatureObject } It 'Should throw invalid operation exception when Ensure set to Present' { - { Set-TargetResource -Name $testWindowsFeatureName2 -Ensure 'Present' } | + { Set-TargetResource -Name $testWindowsFeatureName2 -Ensure 'Present' } | Should Throw ($script:localizedData.FeatureInstallationFailureError -f $testWindowsFeatureName2) Assert-MockCalled -CommandName Add-WindowsFeature -Times 1 -Exactly -Scope It } It 'Should throw invalid operation exception when Ensure set to Absent' { - { Set-TargetResource -Name $testWindowsFeatureName2 -Ensure 'Absent' } | + { Set-TargetResource -Name $testWindowsFeatureName2 -Ensure 'Absent' } | Should Throw ($script:localizedData.FeatureUninstallationFailureError -f $testWindowsFeatureName2) Assert-MockCalled -CommandName Remove-WindowsFeature -Times 1 -Exactly -Scope It } @@ -359,7 +364,7 @@ try { } $windowsFeatureObject = New-Object -TypeName PSObject -Property $windowsFeature $windowsFeatureObject.PSTypeNames[0] = 'Microsoft.Windows.ServerManager.Commands.Feature' - + return $windowsFeatureObject } @@ -368,7 +373,7 @@ try { It 'Should install the feature when Ensure set to Present and Credential passed in' { - { + { Set-TargetResource -Name $testWindowsFeatureName2 ` -Ensure 'Present' ` -Credential $testCredential @@ -379,7 +384,7 @@ try { It 'Should uninstall the feature when Ensure set to Absent and Credential passed in' { - { + { Set-TargetResource -Name $testWindowsFeatureName2 ` -Ensure 'Absent' ` -Credential $testCredential @@ -397,7 +402,7 @@ try { $windowsFeature = $mockWindowsFeatures[$testWindowsFeatureName1] $windowsFeatureObject = New-Object -TypeName PSObject -Property $windowsFeature $windowsFeatureObject.PSTypeNames[0] = 'Microsoft.Windows.ServerManager.Commands.Feature' - + return $windowsFeatureObject } @@ -405,7 +410,7 @@ try { $windowsFeature = $mockWindowsFeatures[$testSubFeatureName1] $windowsFeatureObject = New-Object -TypeName PSObject -Property $windowsFeature $windowsFeatureObject.PSTypeNames[0] = 'Microsoft.Windows.ServerManager.Commands.Feature' - + return $windowsFeatureObject } @@ -413,7 +418,7 @@ try { $windowsFeature = $mockWindowsFeatures[$testSubFeatureName2] $windowsFeatureObject = New-Object -TypeName PSObject -Property $windowsFeature $windowsFeatureObject.PSTypeNames[0] = 'Microsoft.Windows.ServerManager.Commands.Feature' - + return $windowsFeatureObject } @@ -421,7 +426,7 @@ try { $windowsFeature = $mockWindowsFeatures[$testSubFeatureName3] $windowsFeatureObject = New-Object -TypeName PSObject -Property $windowsFeature $windowsFeatureObject.PSTypeNames[0] = 'Microsoft.Windows.ServerManager.Commands.Feature' - + return $windowsFeatureObject } @@ -430,7 +435,7 @@ try { $windowsFeature = $mockWindowsFeatures[$testWindowsFeatureName1] $windowsFeatureObject = New-Object -TypeName PSObject -Property $windowsFeature $windowsFeatureObject.PSTypeNames[0] = 'Microsoft.Windows.ServerManager.Commands.Feature' - + return $windowsFeatureObject } @@ -554,7 +559,7 @@ try { } } - Describe 'xWindowsFeature/Assert-SingleFeatureExists' { + Describe 'xWindowsFeature/Assert-SingleInstanceOfFeature' { $multipleFeature = @{ Name = 'MultiFeatureName' Count = 2 @@ -562,12 +567,12 @@ try { It 'Should throw invalid operation when feature equals null' { $nonexistentName = 'NonexistentFeatureName' - { Assert-SingleFeatureExists -Feature $null -Name $nonexistentName } | + { Assert-SingleInstanceOfFeature -Feature $null -Name $nonexistentName } | Should Throw ($script:localizedData.FeatureNotFoundError -f $nonexistentName) } It 'Should throw invalid operation when there are multiple features with the given name' { - { Assert-SingleFeatureExists -Feature $multipleFeature -Name $multipleFeature.Name } | + { Assert-SingleInstanceOfFeature -Feature $multipleFeature -Name $multipleFeature.Name } | Should Throw ($script:localizedData.MultipleFeatureInstancesError -f $multipleFeature.Name) } } @@ -584,14 +589,14 @@ try { Mock -CommandName Import-Module -MockWith {} { Import-ServerManager } | Should Not Throw } - + It 'Should Not Throw when exception is Identity Reference Runtime Exception' { $mockIdentityReferenceRuntimeException = New-Object -TypeName System.Management.Automation.RuntimeException -ArgumentList 'Some or all identity references could not be translated' Mock -CommandName Import-Module -MockWith { Throw $mockIdentityReferenceRuntimeException } { Import-ServerManager } | Should Not Throw } - + It 'Should throw invalid operation exception when exception is not Identity Reference Runtime Exception' { $mockOtherRuntimeException = New-Object -TypeName System.Management.Automation.RuntimeException -ArgumentList 'Other error' Mock -CommandName Import-Module -MockWith { Throw $mockOtherRuntimeException } diff --git a/Tests/Unit/MSFT_xWindowsOptionalFeature.Tests.ps1 b/Tests/Unit/MSFT_xWindowsOptionalFeature.Tests.ps1 index 995801664..e811361e9 100644 --- a/Tests/Unit/MSFT_xWindowsOptionalFeature.Tests.ps1 +++ b/Tests/Unit/MSFT_xWindowsOptionalFeature.Tests.ps1 @@ -1,5 +1,10 @@ Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'CommonTestHelper.psm1') +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xWindowsOptionalFeature' ` @@ -14,7 +19,7 @@ try $script:testFeatureName = 'TestFeature' - $script:fakeEnabledFeature = [PSCustomObject] @{ + $script:fakeEnabledFeature = [PSCustomObject] @{ Name = $testFeatureName State = 'Enabled' } @@ -27,7 +32,7 @@ try <# This context block needs to stay at the top because of a bug in Pester on Nano server. - + Assert-ResourcePrerequisitesValid is mocked in most of the other contexts blocks. This causes errors to throw from the script blocks in this context since this function does not take any parameters, but Pester tries to pipe something into it. @@ -57,7 +62,7 @@ try BuildNumber = 9600 } } - + It 'Should throw when the DISM module is not available' { Mock Import-Module -ParameterFilter { $Name -eq 'Dism' } -MockWith { Write-Error 'Cannot find module' } { Assert-ResourcePrerequisitesValid } | Should Throw $script:localizedData.DismNotAvailable @@ -111,7 +116,7 @@ try } } - + Context 'Get-TargetResource - Feature Disabled' { Mock Assert-ResourcePrerequisitesValid -MockWith { } Mock Dism\Get-WindowsOptionalFeature { $FeatureName -eq $script:testFeatureName } -MockWith { return $script:fakeDisabledFeature } @@ -167,9 +172,9 @@ try It 'Should call Enable-WindowsOptionalFeature with NoRestart set to true by default when Ensure set to Present' { Mock Dism\Enable-WindowsOptionalFeature -ParameterFilter { $NoRestart -eq $true } -MockWith { } - + Set-TargetResource -Name $script:testFeatureName - + Assert-MockCalled Dism\Enable-WindowsOptionalFeature -ParameterFilter { $NoRestart -eq $true } -Scope It } @@ -306,7 +311,7 @@ try It 'Should return the correct string for each object' { $propertiesAsStrings = Convert-CustomPropertyArrayToStringArray -CustomProperties $psCustomObjects - + foreach ($objectNumber in @(1, 2, 3)) { $propertiesAsStrings.Contains("Name = Object $objectNumber, Value = Value $objectNumber, Path = Path $objectNumber") | Should Be $true diff --git a/Tests/Unit/MSFT_xWindowsPackageCab.Tests.ps1 b/Tests/Unit/MSFT_xWindowsPackageCab.Tests.ps1 index 1284b7d39..06109baa6 100644 --- a/Tests/Unit/MSFT_xWindowsPackageCab.Tests.ps1 +++ b/Tests/Unit/MSFT_xWindowsPackageCab.Tests.ps1 @@ -1,5 +1,10 @@ Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'CommonTestHelper.psm1') +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xWindowsPackageCab' ` diff --git a/Tests/Unit/MSFT_xWindowsProcess.Tests.ps1 b/Tests/Unit/MSFT_xWindowsProcess.Tests.ps1 index abd19f8b8..cdc6f1e70 100644 --- a/Tests/Unit/MSFT_xWindowsProcess.Tests.ps1 +++ b/Tests/Unit/MSFT_xWindowsProcess.Tests.ps1 @@ -7,6 +7,11 @@ Set-StrictMode -Version 'Latest' Import-Module -Name (Join-Path -Path (Split-Path $PSScriptRoot -Parent) ` -ChildPath 'CommonTestHelper.psm1') +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xWindowsProcess' ` @@ -89,10 +94,10 @@ try Describe 'xWindowsProcess\Get-TargetResource' { Mock -CommandName Expand-Path -MockWith { return $Path } - Mock -CommandName Get-ProcessCimInstance -MockWith { + Mock -CommandName Get-ProcessCimInstance -MockWith { if ($Path -eq $script:validPath1) { - return @($script:mockProcess1, $script:mockProcess3) + return @($script:mockProcess1, $script:mockProcess3) } elseif ($Path -eq $script:validPath2) { @@ -110,7 +115,7 @@ try Mock -CommandName Get-Process -MockWith { if ($ID -eq $script:mockProcess1.Id) { - return $script:mockProcess1 + return $script:mockProcess1 } elseif ($script:mockProcess2.Id) { @@ -127,27 +132,27 @@ try } Mock -CommandName New-InvalidOperationException -MockWith { Throw $script:exceptionMessage } Mock -CommandName New-InvalidArgumentException -MockWith { Throw $script:exceptionMessage } - + It 'Should return the correct properties for a process that is Absent' { $processArguments = 'TestGetProperties' - + $getTargetResourceResult = Get-TargetResource -Path $invalidPath ` -Arguments $processArguments - + $getTargetResourceResult.Arguments | Should Be $processArguments $getTargetResourceResult.Ensure | Should Be 'Absent' $getTargetResourceResult.Path | Should Be $invalidPath - + Assert-MockCalled -CommandName Expand-Path -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-ProcessCimInstance -Exactly 1 -Scope It } - + It 'Should return the correct properties for one process with a credential' { - + $getTargetResourceResult = Get-TargetResource -Path $script:validPath2 ` -Arguments $script:mockProcess2.Arguments ` -Credential $script:testCredential - + $getTargetResourceResult.VirtualMemorySize | Should Be $script:mockProcess2.VirtualMemorySize64 $getTargetResourceResult.Arguments | Should Be $script:mockProcess2.Arguments $getTargetResourceResult.Ensure | Should Be 'Present' @@ -156,17 +161,17 @@ try $getTargetResourceResult.NonPagedMemorySize | Should Be $script:mockProcess2.NonpagedSystemMemorySize64 $getTargetResourceResult.HandleCount | Should Be $script:mockProcess2.HandleCount $getTargetResourceResult.ProcessId | Should Be $script:mockProcess2.ProcessId - + Assert-MockCalled -CommandName Expand-Path -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-ProcessCimInstance -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-Process -Exactly 1 -Scope It } - + It 'Should return the correct properties when there are multiple processes' { - + $getTargetResourceResult = Get-TargetResource -Path $script:validPath1 ` -Arguments $script:mockProcess1.Arguments - + $getTargetResourceResult.VirtualMemorySize | Should Be $script:mockProcess1.VirtualMemorySize64 $getTargetResourceResult.Arguments | Should Be $script:mockProcess1.Arguments $getTargetResourceResult.Ensure | Should Be 'Present' @@ -175,19 +180,19 @@ try $getTargetResourceResult.NonPagedMemorySize | Should Be $script:mockProcess1.NonpagedSystemMemorySize64 $getTargetResourceResult.HandleCount | Should Be $script:mockProcess1.HandleCount $getTargetResourceResult.ProcessId | Should Be $script:mockProcess1.ProcessId - + Assert-MockCalled -CommandName Expand-Path -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-ProcessCimInstance -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-Process -Exactly 1 -Scope It } } - + Describe 'xWindowsProcess\Set-TargetResource' { Mock -CommandName Expand-Path -MockWith { return $Path } - Mock -CommandName Get-ProcessCimInstance -MockWith { + Mock -CommandName Get-ProcessCimInstance -MockWith { if ($Path -eq $script:validPath1) { - return @($script:mockProcess1, $script:mockProcess3) + return @($script:mockProcess1, $script:mockProcess3) } elseif ($Path -eq $script:validPath2) { @@ -219,38 +224,38 @@ try -Credential $script:testCredential ` -Ensure 'Absent' } | Should Not Throw - + Assert-MockCalled -CommandName Expand-Path -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-ProcessCimInstance -Exactly 1 -Scope It Assert-MockCalled -CommandName Stop-Process -Exactly 1 -Scope It Assert-MockCalled -CommandName Wait-ProcessCount -Exactly 1 -Scope It } - + It 'Should not throw when Ensure set to Absent and processes are not running' { { Set-TargetResource -Path $script:invalidPath ` -Arguments '' ` -Ensure 'Absent' } | Should Not Throw - + Assert-MockCalled -CommandName Expand-Path -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-ProcessCimInstance -Exactly 1 -Scope It Assert-MockCalled -CommandName Stop-Process -Exactly 0 -Scope It Assert-MockCalled -CommandName Wait-ProcessCount -Exactly 0 -Scope It } - + It 'Should throw an invalid operation exception when Stop-Process throws an error' { { Set-TargetResource -Path $script:errorProcess.Path ` -Arguments '' ` -Ensure 'Absent' } | Should Throw $script:exceptionMessage - + Assert-MockCalled -CommandName Expand-Path -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-ProcessCimInstance -Exactly 1 -Scope It Assert-MockCalled -CommandName Stop-Process -Exactly 1 -Scope It Assert-MockCalled -CommandName New-InvalidOperationException -Exactly 1 -Scope It Assert-MockCalled -CommandName Wait-ProcessCount -Exactly 0 -Scope It } - + Mock -CommandName Wait-ProcessCount -MockWith { return $false } It 'Should throw an invalid operation exception when there is a problem waiting for the processes' { @@ -258,14 +263,14 @@ try -Arguments $script:mockProcess1.Arguments ` -Ensure 'Absent' } | Should Throw $script:exceptionMessage - + Assert-MockCalled -CommandName Expand-Path -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-ProcessCimInstance -Exactly 1 -Scope It Assert-MockCalled -CommandName Stop-Process -Exactly 1 -Scope It Assert-MockCalled -CommandName Wait-ProcessCount -Exactly 1 -Scope It Assert-MockCalled -CommandName New-InvalidOperationException -Exactly 1 -Scope It } - + Mock -CommandName Wait-ProcessCount -MockWith { return $true } Mock -CommandName Start-ProcessAsLocalSystemUser -MockWith {} @@ -275,14 +280,14 @@ try -Credential $script:testCredential ` -Ensure 'Present' } | Should Not Throw - + Assert-MockCalled -CommandName Expand-Path -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-ProcessCimInstance -Exactly 1 -Scope It Assert-MockCalled -CommandName Test-IsRunFromLocalSystemUser -Exactly 1 -Scope It Assert-MockCalled -CommandName Start-ProcessAsLocalSystemUser -Exactly 1 -Scope It Assert-MockCalled -CommandName Wait-ProcessCount -Exactly 1 -Scope It } - + Mock -CommandName Start-ProcessAsLocalSystemUser -MockWith {} Mock -CommandName Assert-PathArgumentRooted -MockWith {} Mock -CommandName Assert-PathArgumentValid -MockWith {} @@ -294,7 +299,7 @@ try -WorkingDirectory 'test working directory' ` -Ensure 'Present' } | Should Throw $script:exceptionMessage - + Assert-MockCalled -CommandName Expand-Path -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-ProcessCimInstance -Exactly 1 -Scope It Assert-MockCalled -CommandName Test-IsRunFromLocalSystemUser -Exactly 1 -Scope It @@ -304,7 +309,7 @@ try Assert-MockCalled -CommandName Assert-PathArgumentValid -Exactly 1 -Scope It Assert-MockCalled -CommandName New-InvalidArgumentException -Exactly 1 -Scope It } - + $testErrorRecord = 'test Start-ProcessAsLocalSystemUser error record' Mock -CommandName Start-ProcessAsLocalSystemUser -MockWith { Throw $testErrorRecord } @@ -314,14 +319,14 @@ try -Credential $script:testCredential ` -Ensure 'Present' } | Should Throw $testErrorRecord - + Assert-MockCalled -CommandName Expand-Path -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-ProcessCimInstance -Exactly 1 -Scope It Assert-MockCalled -CommandName Test-IsRunFromLocalSystemUser -Exactly 1 -Scope It Assert-MockCalled -CommandName Start-ProcessAsLocalSystemUser -Exactly 1 -Scope It Assert-MockCalled -CommandName Wait-ProcessCount -Exactly 0 -Scope It } - + Mock -CommandName Start-Process -MockWith {} It 'Should not throw when Ensure set to Present and processes are not running and no credential passed' { @@ -329,13 +334,13 @@ try -Arguments $script:mockProcess1.Arguments ` -Ensure 'Present' } | Should Not Throw - + Assert-MockCalled -CommandName Expand-Path -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-ProcessCimInstance -Exactly 1 -Scope It Assert-MockCalled -CommandName Start-Process -Exactly 1 -Scope It Assert-MockCalled -CommandName Wait-ProcessCount -Exactly 1 -Scope It } - + $mockStartProcessException = New-Object -TypeName 'InvalidOperationException' ` -ArgumentList @('Start-Process test exception') Mock -CommandName Start-Process -MockWith { Throw $mockStartProcessException } @@ -345,14 +350,14 @@ try -Arguments $script:mockProcess1.Arguments ` -Ensure 'Present' } | Should Throw $script:exceptionMessage - + Assert-MockCalled -CommandName Expand-Path -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-ProcessCimInstance -Exactly 1 -Scope It Assert-MockCalled -CommandName Start-Process -Exactly 1 -Scope It Assert-MockCalled -CommandName Wait-ProcessCount -Exactly 0 -Scope It Assert-MockCalled -CommandName New-InvalidOperationException -Exactly 1 -Scope It } - + Mock -CommandName Wait-ProcessCount -MockWith { return $false } Mock -CommandName Start-Process -MockWith {} @@ -361,13 +366,13 @@ try -Arguments $script:mockProcess1.Arguments ` -Ensure 'Present' } | Should Throw $script:exceptionMessage - + Assert-MockCalled -CommandName Expand-Path -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-ProcessCimInstance -Exactly 1 -Scope It Assert-MockCalled -CommandName Start-Process -Exactly 1 -Scope It Assert-MockCalled -CommandName Wait-ProcessCount -Exactly 1 -Scope It } - + Mock -CommandName Wait-ProcessCount -MockWith { return $true } It 'Should not throw when Ensure set to Present and processes are already running' { @@ -375,20 +380,20 @@ try -Arguments $script:mockProcess1.Arguments ` -Ensure 'Present' } | Should Not Throw - + Assert-MockCalled -CommandName Expand-Path -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-ProcessCimInstance -Exactly 1 -Scope It Assert-MockCalled -CommandName Start-Process -Exactly 0 -Scope It Assert-MockCalled -CommandName Wait-ProcessCount -Exactly 0 -Scope It } } - + Describe 'xWindowsProcess\Test-TargetResource' { Mock -CommandName Expand-Path -MockWith { return $Path } - Mock -CommandName Get-ProcessCimInstance -MockWith { + Mock -CommandName Get-ProcessCimInstance -MockWith { if ($Path -eq $script:validPath1) { - return @($script:mockProcess1, $script:mockProcess3) + return @($script:mockProcess1, $script:mockProcess3) } elseif ($Path -eq $script:validPath2) { @@ -410,14 +415,14 @@ try -Ensure 'Present' $testTargetResourceResult | Should Be $true } - + It 'Should return false when Ensure set to Present and process is not running' { $testTargetResourceResult = Test-TargetResource -Path $script:invalidPath ` -Arguments $script:mockProcess1.Arguments ` -Ensure 'Present' $testTargetResourceResult | Should Be $false } - + It 'Should return true when Ensure set to Absent and process is not running and Credential passed' { $testTargetResourceResult = Test-TargetResource -Path $script:invalidPath ` -Arguments $script:mockProcess1.Arguments ` @@ -425,46 +430,46 @@ try -Ensure 'Absent' $testTargetResourceResult | Should Be $true } - + It 'Should return false when Ensure set to Absent and process is running' { $testTargetResourceResult = Test-TargetResource -Path $script:validPath1 ` -Arguments $script:mockProcess1.Arguments ` -Ensure 'Absent' $testTargetResourceResult | Should Be $false } - + } - + Describe 'xWindowsProcess\Expand-Path' { Mock -CommandName New-InvalidArgumentException -MockWith { Throw $script:exceptionMessage } Mock -CommandName Test-Path -MockWith { return $true } It 'Should return the original path when path is rooted' { $rootedPath = 'C:\testProcess.exe' - + $expandPathResult = Expand-Path -Path $rootedPath $expandPathResult | Should Be $rootedPath } - + Mock -CommandName Test-Path -MockWith { return $false } It 'Should throw an invalid argument exception when Path is rooted and does not exist' { $rootedPath = 'C:\invalidProcess.exe' - + { Expand-Path -Path $rootedPath} | Should Throw $script:exceptionMessage - + Assert-MockCalled -CommandName New-InvalidArgumentException -Exactly 1 -Scope It } - + It 'Should throw an invalid argument exception when Path is unrooted and does not exist' { $unrootedPath = 'invalidfile.txt' - + { Expand-Path -Path $unrootedPath} | Should Throw $script:exceptionMessage - + Assert-MockCalled -CommandName New-InvalidArgumentException -Exactly 1 -Scope It } } - + Describe 'xWindowsProcess\Get-ProcessCimInstance' { Mock -CommandName Get-Process -MockWith { return @($script:mockProcess2) } Mock -CommandName Get-CimInstance -MockWith { return $script:mockProcess2 } @@ -472,11 +477,11 @@ try It 'Should return the correct process when it exists and no arguments passed' { $resultProcess = Get-ProcessCimInstance -Path $script:mockProcess2.Path $resultProcess | Should Be @($script:mockProcess2) - + Assert-MockCalled -CommandName Get-Process -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-CimInstance -Exactly 1 -Scope It } - + Mock -CommandName Get-Process -MockWith { return @($script:mockProcess1) } Mock -CommandName Get-CimInstance -MockWith { return $script:mockProcess1 } @@ -484,11 +489,11 @@ try $resultProcess = Get-ProcessCimInstance -Path $script:mockProcess1.Path ` -Arguments $script:mockProcess1.Arguments $resultProcess | Should Be @($script:mockProcess1) - + Assert-MockCalled -CommandName Get-Process -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-CimInstance -Exactly 1 -Scope It } - + $expectedProcesses = @($script:mockProcess1, $script:mockProcess1, $script:mockProcess1) Mock -CommandName Get-Process -MockWith { return $expectedProcesses } Mock -CommandName Get-CimInstance -MockWith { return $script:mockProcess1 } @@ -496,13 +501,13 @@ try It 'Should return the correct processes when multiple exist' { $resultProcess = Get-ProcessCimInstance -Path $script:mockProcess1.Path ` -Arguments $script:mockProcess1.Arguments - + Compare-Object -ReferenceObject $expectedProcesses -DifferenceObject $resultProcess | Should Be $null - + Assert-MockCalled -CommandName Get-Process -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-CimInstance -Exactly 3 -Scope It } - + Mock -CommandName Get-Process -MockWith { return @($script:mockProcess2, $script:mockProcess2) } Mock -CommandName Get-CimInstance -MockWith { return @($script:mockProcess2, $script:mockProcess2) } @@ -511,11 +516,11 @@ try -Arguments $script:mockProcess2.Arguments ` -UseGetCimInstanceThreshold 1 $resultProcess | Should Be @($script:mockProcess2, $script:mockProcess2) - + Assert-MockCalled -CommandName Get-Process -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-CimInstance -Exactly 1 -Scope It } - + Mock -CommandName Get-Process -MockWith { return @($script:mockProcess2) } Mock -CommandName Get-CimInstance -MockWith { return $script:mockProcess2 } Mock -CommandName Get-ProcessOwner -MockWith { return ($env:computerName + '\' + $script:testUsername) } ` @@ -525,12 +530,12 @@ try $resultProcess = Get-ProcessCimInstance -Path $script:mockProcess2.Path ` -Credential $script:testCredential $resultProcess | Should Be @($script:mockProcess2) - + Assert-MockCalled -CommandName Get-Process -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-CimInstance -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-ProcessOwner -Exactly 1 -Scope It } - + Mock -CommandName Get-Process -MockWith { return @($script:mockProcess3, $script:mockProcess3, $script:mockProcess4, $script:mockProcess2) } Mock -CommandName Get-CimInstance -MockWith { return @($script:mockProcess3, $script:mockProcess3, $script:mockProcess4, $script:mockProcess2) } Mock -CommandName Get-ProcessOwner -MockWith { return ($env:computerName + '\' + $script:testUsername) } ` @@ -544,11 +549,11 @@ try -Arguments $script:mockProcess3.Arguments ` -UseGetCimInstanceThreshold 1 $resultProcess | Should Be @($script:mockProcess3, $script:mockProcess3) - + Assert-MockCalled -CommandName Get-Process -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-CimInstance -Exactly 1 -Scope It } - + Mock -CommandName Get-ProcessOwner -MockWith { return ($env:computerName + '\' + $script:testUsername) } ` -ParameterFilter { ($Process -eq $script:mockProcess3) -or ($Process -eq $script:mockProcess2) } Mock -CommandName Get-ProcessOwner -MockWith { return ('wrongDomain' + '\' + $script:testUsername) } ` @@ -560,26 +565,26 @@ try -Arguments $script:mockProcess3.Arguments ` -UseGetCimInstanceThreshold 1 $resultProcess | Should Be @($script:mockProcess3, $script:mockProcess3) - + Assert-MockCalled -CommandName Get-Process -Exactly 1 -Scope It Assert-MockCalled -CommandName Get-CimInstance -Exactly 1 -Scope It } } - + Describe 'xWindowsProcess\ConvertTo-EscapedStringForWqlFilter' { It 'Should return the same string when there are no escaped characters' { $inputString = 'testString%$.@123' $convertedString = ConvertTo-EscapedStringForWqlFilter -FilterString $inputString $convertedString | Should Be $inputString } - + It 'Should return a string with escaped characters: ("\)' { $inputString = '\test"string"\123' $expectedString = '\\test\"string\"\\123' $convertedString = ConvertTo-EscapedStringForWqlFilter -FilterString $inputString $convertedString | Should Be $expectedString } - + It "Should return a string with escaped characters: ('\)" { $inputString = "\test'string'\123" $expectedString = "\\test\'string\'\\123" @@ -587,47 +592,47 @@ try $convertedString | Should Be $expectedString } } - + Describe 'xWindowsProcess\Get-ProcessOwner' { $mockOwner = @{ Domain = 'Mock Domain' User = 'Mock User' - } + } Mock -CommandName Get-ProcessOwnerCimInstance -MockWith { return $mockOwner } It 'Should return the correct string with domain\user' { $owner = Get-ProcessOwner -Process $script:mockProcess1 $owner | Should Be ($mockOwner.Domain + '\' + $mockOwner.User) } - + It 'Should return the correct string with default-domain\user when domain is not there' { $mockOwner.Domain = $null $owner = Get-ProcessOwner -Process $script:mockProcess1 $owner | Should Be ($env:computerName + '\' + $mockOwner.User) } - + } - - Describe 'xWindowsProcess\Get-ArgumentsFromCommandLineInput' { + + Describe 'xWindowsProcess\Get-ArgumentsFromCommandLineInput' { It 'Should return the correct arguments when single quotes are used' { $inputString = 'test.txt a b c' $argumentsReturned = Get-ArgumentsFromCommandLineInput -CommandLineInput $inputString $argumentsReturned | Should Be 'a b c' } - + It 'Should return the correct arguments when double quotes are used' { $inputString = '"test file test" a b c' $argumentsReturned = Get-ArgumentsFromCommandLineInput -CommandLineInput $inputString $argumentsReturned | Should Be 'a b c' } - + It 'Should return an empty string when an empty string is passed in' { $inputString = $null $resultString = [String]::Empty $argumentsReturned = Get-ArgumentsFromCommandLineInput -CommandLineInput $inputString $argumentsReturned | Should Be $resultString } - + It 'Should return an empty string when there are no arguments' { $inputString = 'test.txt' $resultString = [String]::Empty @@ -635,7 +640,7 @@ try $argumentsReturned | Should Be $resultString } } - + Describe 'xWindowsProcess\Assert-HashtableDoesNotContainKey' { $mockHashtable = @{ Key1 = 'test key1' @@ -643,23 +648,23 @@ try Key3 = 'test key3' } Mock -CommandName New-InvalidArgumentException -MockWith { Throw $script:exceptionMessage } - + It 'Should not throw an exception if the hashtable does not contain a key' { $mockKey = @('k1', 'k2', 'k3', 'k4', 'k5') { Assert-HashTableDoesNotContainKey -Hashtable $mockHashtable -Key $mockKey } | Should Not Throw } - + It 'Should throw an exception if the hashtable contains a key' { $mockKey = @('k1', 'k2', 'Key3', 'k4', 'k5') { Assert-HashTableDoesNotContainKey -Hashtable $mockHashtable -Key $mockKey } | Should Throw $script:exceptionMessage - + Assert-MockCalled -CommandName New-InvalidArgumentException -Exactly 1 -Scope It } } - + Describe 'xWindowsProcess\Wait-ProcessCount' { - $mockProcessSettings = @{ - Path = 'mockPath' + $mockProcessSettings = @{ + Path = 'mockPath' Arguments = 'mockArguments' } Mock -CommandName Get-ProcessCimInstance -MockWith { return @($script:mockProcess1, $script:mockProcess3) } @@ -668,7 +673,7 @@ try $processCountResult = Wait-ProcessCount -ProcessSettings $mockProcessSettings -ProcessCount 2 $processCountResult | Should Be $true } - + It 'Should return false when not all processes are returned' { $processCountResult = Wait-ProcessCount -ProcessSettings $mockProcessSettings ` -ProcessCount 3 ` @@ -676,48 +681,48 @@ try $processCountResult | Should Be $false } } - + Describe 'xWindowsProcess\Assert-PathArgumentRooted' { Mock -CommandName New-InvalidArgumentException -MockWith { Throw $script:exceptionMessage } It 'Should not throw when path is rooted' { $rootedPath = 'C:\testProcess.exe' - + { Assert-PathArgumentRooted -PathArgumentName 'mock test name' ` -PathArgument $rootedPath } | Should Not Throw } - + It 'Should throw an invalid argument exception when Path is unrooted' { $unrootedPath = 'invalidfile.txt' - - + + { Assert-PathArgumentRooted -PathArgumentName 'mock test name' ` -PathArgument $unrootedPath } | Should Throw $script:exceptionMessage - + Assert-MockCalled -CommandName New-InvalidArgumentException -Exactly 1 -Scope It } } - + Describe 'xWindowsProcess\Assert-PathArgumentValid' { Mock -CommandName New-InvalidArgumentException -MockWith { Throw $script:exceptionMessage } It 'Should not throw when path is valid' { Mock -CommandName Test-Path -MockWith { return $true } - + { Assert-PathArgumentValid -PathArgumentName 'test name' ` -PathArgument 'validPath' } | Should Not Throw } - + It 'Should throw an invalid argument exception when Path is not valid' { Mock -CommandName Test-Path -MockWith { return $false } - + { Assert-PathArgumentValid -PathArgumentName 'test name' ` -PathArgument 'invalidPath' } | Should Throw $script:exceptionMessage - + Assert-MockCalled -CommandName New-InvalidArgumentException -Exactly 1 -Scope It } } - + Describe 'xWindowsProcess\Split-Credential' { Mock -CommandName New-InvalidArgumentException -MockWith { Throw $script:exceptionMessage } @@ -725,57 +730,57 @@ try $testUsername = 'user@domain' $testPassword = ConvertTo-SecureString -String 'dummy' -AsPlainText -Force $testCredential = New-Object -TypeName 'PSCredential' -ArgumentList @($testUsername, $testPassword) - + $splitCredentialResult = Split-Credential -Credential $testCredential - + $splitCredentialResult.Domain | Should Be 'domain' $splitCredentialResult.Username | Should Be 'user' } - + It 'Should return correct domain and username with \ seperator' { $testUsername = 'domain\user' $testPassword = ConvertTo-SecureString -String 'dummy' -AsPlainText -Force $testCredential = New-Object -TypeName 'PSCredential' -ArgumentList @($testUsername, $testPassword) - + $splitCredentialResult = Split-Credential -Credential $testCredential - + $splitCredentialResult.Domain | Should Be 'domain' $splitCredentialResult.Username | Should Be 'user' } - + It 'Should return correct domain and username with a local user' { $testUsername = 'localuser' $testPassword = ConvertTo-SecureString -String 'dummy' -AsPlainText -Force $testCredential = New-Object -TypeName 'PSCredential' -ArgumentList @($testUsername, $testPassword) - + $splitCredentialResult = Split-Credential -Credential $testCredential - + $splitCredentialResult.Domain | Should Be $env:computerName $splitCredentialResult.Username | Should Be 'localuser' } - + It 'Should throw an invalid argument exception when more than one \ in username' { $testUsername = 'user\domain\foo' $testPassword = ConvertTo-SecureString -String 'dummy' -AsPlainText -Force $testCredential = New-Object -TypeName 'PSCredential' -ArgumentList @($testUsername, $testPassword) - + { $splitCredentialResult = Split-Credential -Credential $testCredential } | Should Throw $script:exceptionMessage - + Assert-MockCalled -CommandName New-InvalidArgumentException -Exactly 1 -Scope It } - + It 'Should throw an invalid argument exception when more than one @ in username' { $testUsername = 'user@domain@foo' $testPassword = ConvertTo-SecureString -String 'dummy' -AsPlainText -Force $testCredential = New-Object -TypeName 'PSCredential' -ArgumentList @($testUsername, $testPassword) - + { $splitCredentialResult = Split-Credential -Credential $testCredential } | Should Throw $script:exceptionMessage - + Assert-MockCalled -CommandName New-InvalidArgumentException -Exactly 1 -Scope It } } - } -} + } +} finally { Exit-DscResourceTestEnvironment -TestEnvironment $script:testEnvironment diff --git a/Tests/Unit/ResourceSetHelper.Tests.ps1 b/Tests/Unit/ResourceSetHelper.Tests.ps1 index 0e2faef9a..51851644c 100644 --- a/Tests/Unit/ResourceSetHelper.Tests.ps1 +++ b/Tests/Unit/ResourceSetHelper.Tests.ps1 @@ -4,6 +4,14 @@ param () $errorActionPreference = 'Stop' Set-StrictMode -Version 'Latest' +Import-Module -Name (Join-Path -Path (Split-Path $PSScriptRoot -Parent) ` + -ChildPath 'CommonTestHelper.psm1') + +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + $script:testsFolderFilePath = Split-Path -Path $PSScriptRoot -Parent $script:moduleRootFilePath = Split-Path -Path $script:testsFolderFilePath -Parent $script:dscResourcesFolderFilePath = Join-Path -Path $script:moduleRootFilePath -ChildPath 'DscResources' diff --git a/appveyor.yml b/appveyor.yml index 24642527f..ff0881da1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,6 +5,32 @@ version: 6.0.{build}.0 environment: gallery_api: secure: 9ekJzfsPCDBkyLrfmov83XbbhZ6E2N3z+B/Io8NbDetbHc6hWS19zsDmy7t0Vvxv + # The job where deploy step should run (normally the last job) + DeployInJobNumber: 5 + +# The images that will be used when building the job matrix. +image: +- Visual Studio 2017 +- Visual Studio 2015 + +# The configuration that will be used when building the job matrix. +# Meta = Test Framework Common Test +# Unit = Module Unit Tests. +# Integration = Module Integration Tests. +configuration: +- Meta +- Unit +- Integration + +# - If one job in the matrix fails, the build will stop (fast_finish). +# - The jobs that will excluded based on the combination of configuration name +# and image name. This will make sure the meta tests (common tests) are only +# run on one of the images. +matrix: + fast_finish: true + exclude: + - configuration: Meta + image: Visual Studio 2015 install: - git clone https://github.com/PowerShell/DscResource.Tests @@ -23,15 +49,78 @@ build: false # test configuration # #---------------------------------# -test_script: +# This will build the job matrix (exception is what is excluded from the matrix +# above). The jobs will be order by the order of key 'configuration:' and then +# by the key 'images:'. +# +# Job 1: Run meta test (common tests) on image Visual Studio 2017. +# Job 2: Run unit tests on image Visual Studio 2017, including Codecov report. +# Job 3: Run integration tests on image Visual Studio 2017. +# Job 4: Run unit tests on image Visual Studio 2015, without Codecov report. +# Job 5: Run integration tests on image Visual Studio 2015. + +for: +- + # Job 1. + matrix: + only: + - configuration: Meta + + test_script: + - ps: | + Invoke-AppveyorTestScriptTask ` + -Type 'Default' ` + -ExcludeTag @() + +- + # Job 3 and job 5. + matrix: + only: + - configuration: Integration + environment: + SkipAllCommonTests: True + + test_script: + - ps: | + Invoke-AppveyorTestScriptTask ` + -Type 'Default' ` + -DisableConsistency ` + -ExcludeTag @() + +- + # Job 4. + matrix: + only: + - configuration: Unit + image: Visual Studio 2015 + environment: + SkipAllCommonTests: True + + test_script: + - ps: | + Invoke-AppveyorTestScriptTask ` + -Type 'Default' ` + -CodeCoverage ` + -ExcludeTag @() + +- + # Job 2. + matrix: + only: + - configuration: Unit + image: Visual Studio 2017 + environment: + SkipAllCommonTests: True + + test_script: - ps: | Invoke-AppveyorTestScriptTask ` -Type 'Default' ` -CodeCoverage ` -CodeCovIo ` - -DisableConsistency ` -ExcludeTag @() +# Runs for all jobs. after_test: - ps: | Import-Module -Name "$env:APPVEYOR_BUILD_FOLDER\DscResource.Tests\AppVeyor.psm1" @@ -42,6 +131,14 @@ after_test: # deployment configuration # #---------------------------------# +# Runs only in a branch (not in PR). deploy_script: - ps: | - Invoke-AppVeyorDeployTask + if ($env:APPVEYOR_JOB_NUMBER -eq $env:DeployInJobNumber) + { + Invoke-AppVeyorDeployTask + } + else + { + Write-Verbose -Message ('Skipping deploy step. Deploy step was requested to run in job number {0}. Current job number is {1}.' -f $env:DeployInJobNumber, $env:APPVEYOR_JOB_NUMBER) -Verbose + } diff --git a/xPSDesiredStateConfiguration.psd1 b/xPSDesiredStateConfiguration.psd1 index b1adb083b..9dde069ab 100644 --- a/xPSDesiredStateConfiguration.psd1 +++ b/xPSDesiredStateConfiguration.psd1 @@ -1,58 +1,58 @@ @{ -# Version number of this module. -moduleVersion = '8.5.0.0' + # Version number of this module. + moduleVersion = '8.5.0.0' -# ID used to uniquely identify this module -GUID = 'cc8dc021-fa5f-4f96-8ecf-dfd68a6d9d48' + # ID used to uniquely identify this module + GUID = 'cc8dc021-fa5f-4f96-8ecf-dfd68a6d9d48' -# Author of this module -Author = 'Microsoft Corporation' + # Author of this module + Author = 'Microsoft Corporation' -# Company or vendor of this module -CompanyName = 'Microsoft Corporation' + # Company or vendor of this module + CompanyName = 'Microsoft Corporation' -# Copyright statement for this module -Copyright = '(c) 2014 Microsoft Corporation. All rights reserved.' + # Copyright statement for this module + Copyright = '(c) Microsoft Corporation. All rights reserved.' -# Description of the functionality provided by this module -Description = 'The xPSDesiredStateConfiguration module is a part of the Windows PowerShell Desired State Configuration (DSC) Resource Kit, which is a collection of DSC Resources produced by the PowerShell Team. This module contains the xDscWebService, xWindowsProcess, xService, xPackage, xArchive, xRemoteFile, xPSEndpoint and xWindowsOptionalFeature resources. Please see the Details section for more information on the functionalities provided by these resources. + # Description of the functionality provided by this module + Description = 'The xPSDesiredStateConfiguration module is a part of the Windows PowerShell Desired State Configuration (DSC) Resource Kit, which is a collection of DSC Resources produced by the PowerShell Team. This module contains the xDscWebService, xWindowsProcess, xService, xPackage, xArchive, xRemoteFile, xPSEndpoint and xWindowsOptionalFeature resources. Please see the Details section for more information on the functionalities provided by these resources. All of the resources in the DSC Resource Kit are provided AS IS, and are not supported through any Microsoft standard support program or service. The "x" in xPSDesiredStateConfiguration stands for experimental, which means that these resources will be fix forward and monitored by the module owner(s).' -# Minimum version of the Windows PowerShell engine required by this module -PowerShellVersion = '4.0' + # Minimum version of the Windows PowerShell engine required by this module + PowerShellVersion = '4.0' -# Minimum version of the common language runtime (CLR) required by this module -CLRVersion = '4.0' + # Minimum version of the common language runtime (CLR) required by this module + CLRVersion = '4.0' -# Functions to export from this module -FunctionsToExport = '*' + # Functions to export from this module + FunctionsToExport = '*' -# Cmdlets to export from this module -CmdletsToExport = '*' + # Cmdlets to export from this module + CmdletsToExport = '*' -#Root module -RootModule = 'DSCPullServerSetup\PublishModulesAndMofsToPullServer.psm1' + # Root module + RootModule = 'DSCPullServerSetup\PublishModulesAndMofsToPullServer.psm1' -# 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 = @{ + # 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 = @{ + PSData = @{ - # Tags applied to this module. These help with module discovery in online galleries. - Tags = @('DesiredStateConfiguration', 'DSC', 'DSCResourceKit', 'DSCResource') + # 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/xPSDesiredStateConfiguration/blob/master/LICENSE' + # A URL to the license for this module. + LicenseUri = 'https://github.com/PowerShell/xPSDesiredStateConfiguration/blob/master/LICENSE' - # A URL to the main website for this project. - ProjectUri = 'https://github.com/PowerShell/xPSDesiredStateConfiguration' + # A URL to the main website for this project. + ProjectUri = 'https://github.com/PowerShell/xPSDesiredStateConfiguration' - # A URL to an icon representing this module. - # IconUri = '' + # A URL to an icon representing this module. + # IconUri = '' - # ReleaseNotes of this module - ReleaseNotes = '* Changes to xRegistry + # ReleaseNotes of this module + ReleaseNotes = '* Changes to xRegistry * Fixed an issue that fails to remove reg key when the `Key` is specified as common registry path. ([issue 444](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/444)) * Changes to xService @@ -60,8 +60,6 @@ PrivateData = @{ ' - } # End of PSData hashtable - -} # End of PrivateData hashtable + } # End of PSData hashtable + } # End of PrivateData hashtable } -