├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── General.md │ ├── Problem_with_resource.yml │ ├── Resource_proposal.yml │ └── config.yml └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .markdownlint.json ├── .vscode ├── analyzersettings.psd1 ├── launch.json ├── settings.json └── tasks.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── GitVersion.yml ├── LICENSE ├── README.md ├── RequiredModules.psd1 ├── Resolve-Dependency.ps1 ├── Resolve-Dependency.psd1 ├── SECURITY.md ├── azure-pipelines.yml ├── build.ps1 ├── build.yaml ├── codecov.yml ├── source ├── Public │ ├── Get-DatumNodesRecursive.ps1 │ ├── Get-DscErrorMessage.ps1 │ ├── Get-DscMofEnvironment.ps1 │ ├── Get-DscMofVersion.ps1 │ ├── Get-FilteredConfigurationData.ps1 │ └── Split-Array.ps1 ├── Sampler.DscPipeline.psd1 ├── Sampler.DscPipeline.psm1 ├── Scripts │ ├── CompileRootConfiguration.ps1 │ ├── RootConfiguration.ps1 │ └── RootMetaMof.ps1 ├── Tasks │ ├── CompileDatumRsop.build.ps1 │ ├── CompileRootConfiguration.build.ps1 │ ├── CompileRootMetaMof.build.ps1 │ ├── CompressArtifactCollections.build.ps1 │ ├── CompressModulesWithChecksum.build.ps1 │ ├── LoadDatumConfigData.build.ps1 │ ├── NewMofChecksums.build.ps1 │ ├── TestBuildAcceptance.build.ps1 │ ├── TestConfigData.build.ps1 │ └── TestDscResources.build.ps1 ├── WikiSource │ └── Home.md ├── en-US │ ├── Sampler.DscPipeline.strings.psd1 │ └── about_Sampler.DscPipeline.help.txt └── suffix.ps1 └── tests └── Integration ├── Assets └── ConfigData │ ├── AllNodes │ ├── Dev │ │ ├── DSCFile01.yml │ │ ├── DSCWeb01.yml │ │ └── ReferenceConfigurationDev.yml │ ├── Prod │ │ ├── DSCFile03.yml │ │ ├── DSCWeb03.yml │ │ └── ReferenceConfigurationProd.yml │ └── Test │ │ ├── DSCFile02.yml │ │ ├── DSCWeb02.yml │ │ └── ReferenceConfigurationTest.yml │ ├── Baselines │ ├── DscLcm.yml │ ├── Security.yml │ └── Server.yml │ ├── Datum.yml │ ├── Environment │ ├── Dev.yml │ ├── Prod.yml │ └── Test.yml │ ├── Global │ ├── Azure.yml │ └── Domain.yml │ ├── Locations │ ├── Frankfurt.yml │ ├── London.yml │ ├── Singapore.yml │ └── Tokio.yml │ ├── Roles │ ├── DomainController.yml │ ├── FileServer.yml │ └── WebServer.yml │ └── TestRsopReferences │ ├── ReferenceConfigurationDev.yml │ ├── ReferenceConfigurationProd.yml │ └── ReferenceConfigurationTest.yml └── Public ├── CompileDatumRsop.tests.ps1 ├── Get-FilteredConfigurationData.tests.ps1 └── LoadDatumConfigData.tests.ps1 /.gitattributes: -------------------------------------------------------------------------------- 1 | # Needed for publishing of examples, build worker defaults to core.autocrlf=input. 2 | * text eol=autocrlf 3 | 4 | *.mof text eol=crlf 5 | *.sh text eol=lf 6 | *.svg eol=lf 7 | 8 | # Ensure any exe files are treated as binary 9 | *.exe binary 10 | *.jpg binary 11 | *.xl* binary 12 | *.pfx binary 13 | *.png binary 14 | *.dll binary 15 | *.so binary 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/General.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: General question or documentation update 3 | about: If you have a general question or documentation update suggestion around the resource module. 4 | --- 5 | 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Problem_with_resource.yml: -------------------------------------------------------------------------------- 1 | name: Problem with a resource 2 | description: If you have a problem, bug, or enhancement with a resource in this resource module. 3 | labels: [] 4 | assignees: [] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Please prefix the issue title (above) with the resource name, e.g. 'ResourceName: Short description of my issue'! 10 | 11 | Your feedback and support is greatly appreciated, thanks for contributing! 12 | - type: textarea 13 | id: description 14 | attributes: 15 | label: Problem description 16 | description: Details of the scenario you tried and the problem that is occurring. 17 | validations: 18 | required: true 19 | - type: textarea 20 | id: logs 21 | attributes: 22 | label: Verbose logs 23 | description: | 24 | Verbose logs showing the problem. **NOTE! Sensitive information should be obfuscated.** _Will be automatically formatted as plain text._ 25 | placeholder: | 26 | Paste verbose logs here 27 | render: text 28 | validations: 29 | required: true 30 | - type: textarea 31 | id: configuration 32 | attributes: 33 | label: DSC configuration 34 | description: | 35 | The DSC configuration that is used to reproduce the issue (as detailed as possible). **NOTE! Sensitive information should be obfuscated.** _Will be automatically formatted as PowerShell code._ 36 | placeholder: | 37 | Paste DSC configuration here 38 | render: powershell 39 | validations: 40 | required: true 41 | - type: textarea 42 | id: suggestedSolution 43 | attributes: 44 | label: Suggested solution 45 | description: Do you have any suggestions how to solve the issue? 46 | validations: 47 | required: true 48 | - type: textarea 49 | id: targetNodeOS 50 | attributes: 51 | label: Operating system the target node is running 52 | description: | 53 | Please provide as much as possible about the target node, for example edition, version, build, and language. _Will be automatically formatted as plain text._ 54 | 55 | On OS with WMF 5.1 the following command can help get this information: `Get-ComputerInfo -Property @('OsName','OsOperatingSystemSKU','OSArchitecture','WindowsVersion','WindowsBuildLabEx','OsLanguage','OsMuiLanguages')` 56 | placeholder: | 57 | Add operating system information here 58 | render: text 59 | validations: 60 | required: true 61 | - type: textarea 62 | id: targetNodePS 63 | attributes: 64 | label: PowerShell version and build the target node is running 65 | description: | 66 | Please provide the version and build of PowerShell the target node is running. _Will be automatically formatted as plain text._ 67 | 68 | To help with this information, please run this command: `$PSVersionTable` 69 | placeholder: | 70 | Add PowerShell information here 71 | render: text 72 | validations: 73 | required: true 74 | - type: textarea 75 | id: moduleVersion 76 | attributes: 77 | label: Sampler.DscPipeline version 78 | description: | 79 | Please provide the version of the Sampler.DscPipeline module that was used. _Will be automatically formatted as plain text._ 80 | 81 | To help with this information, please run this command: `Get-Module -Name 'Sampler.DscPipeline' -ListAvailable | ft Name,Version,Path` 82 | placeholder: | 83 | Add module information here 84 | render: text 85 | validations: 86 | required: true 87 | 88 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Resource_proposal.yml: -------------------------------------------------------------------------------- 1 | name: New resource proposal 2 | description: If you have a new resource proposal that you think should be added to this resource module. 3 | title: "NewResourceName: New resource proposal" 4 | labels: [] 5 | assignees: [] 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Please replace `NewResourceName` in the issue title (above) with your proposed resource name. 11 | 12 | Thank you for contributing and making this resource module better! 13 | - type: textarea 14 | id: description 15 | attributes: 16 | label: Resource proposal 17 | description: Provide information how this resource will/should work and how it will help users. 18 | validations: 19 | required: true 20 | - type: textarea 21 | id: proposedProperties 22 | attributes: 23 | label: Proposed properties 24 | description: | 25 | List all the proposed properties that the resource should have (key, required, write, and/or read). For each property provide a detailed description, the data type, if a default value should be used, and if the property is limited to a set of values. 26 | value: | 27 | Property | Type qualifier | Data type | Description | Default value | Allowed values 28 | --- | --- | --- | --- | --- | --- 29 | PropertyName | Key | String | Detailed description | None | None 30 | validations: 31 | required: true 32 | - type: textarea 33 | id: considerations 34 | attributes: 35 | label: Special considerations or limitations 36 | description: | 37 | Provide any considerations or limitations you can think of that a contributor should take in account when coding the proposed resource, and or what limitations a user will encounter or should consider when using the proposed resource. 38 | validations: 39 | required: true 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: "Virtual PowerShell User Group #DSC channel" 4 | url: https://dsccommunity.org/community/contact/ 5 | about: "To talk to the community and maintainers of DSC Community, please visit the #DSC channel." 6 | 7 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | #### Pull Request (PR) description 14 | 15 | 19 | 20 | #### This Pull Request (PR) fixes the following issues 21 | 22 | 30 | 31 | #### Task list 32 | 33 | 40 | 41 | - [ ] Added an entry to the change log under the Unreleased section of the file CHANGELOG.md. 42 | Entry should say what was changed and how that affects users (if applicable), and 43 | reference the issue being resolved (if applicable). 44 | - [ ] Resource documentation added/updated in README.md. 45 | - [ ] Resource parameter descriptions added/updated in README.md, schema.mof and comment-based 46 | help. 47 | - [ ] Comment-based help added/updated. 48 | - [ ] Localization strings added/updated in all localization files as appropriate. 49 | - [ ] Examples appropriately added/updated. 50 | - [ ] Unit tests added/updated. See [DSC Community Testing Guidelines](https://dsccommunity.org/guidelines/testing-guidelines). 51 | - [ ] Integration tests added/updated (where possible). See [DSC Community Testing Guidelines](https://dsccommunity.org/guidelines/testing-guidelines). 52 | - [ ] New/changed code adheres to [DSC Community Style Guidelines](https://dsccommunity.org/styleguidelines). 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | output/ 2 | 3 | **.bak 4 | *.local.* 5 | !**/README.md 6 | .kitchen/ 7 | 8 | *.suo 9 | *.user 10 | *.coverage 11 | .vs 12 | .psproj 13 | .sln 14 | markdownissues.txt 15 | node_modules 16 | package-lock.json 17 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "MD029": { 4 | "style": "one" 5 | }, 6 | "MD013": false, 7 | "MD024": false, 8 | "MD034": false, 9 | "no-hard-tabs": true 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/analyzersettings.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | CustomRulePath = '.\output\RequiredModules\DscResource.AnalyzerRules' 3 | includeDefaultRules = $true 4 | IncludeRules = @( 5 | # DSC Resource Kit style guideline rules. 6 | 'PSAvoidDefaultValueForMandatoryParameter', 7 | 'PSAvoidDefaultValueSwitchParameter', 8 | 'PSAvoidInvokingEmptyMembers', 9 | 'PSAvoidNullOrEmptyHelpMessageAttribute', 10 | 'PSAvoidUsingCmdletAliases', 11 | 'PSAvoidUsingComputerNameHardcoded', 12 | 'PSAvoidUsingDeprecatedManifestFields', 13 | 'PSAvoidUsingEmptyCatchBlock', 14 | 'PSAvoidUsingInvokeExpression', 15 | 'PSAvoidUsingPositionalParameters', 16 | 'PSAvoidShouldContinueWithoutForce', 17 | 'PSAvoidUsingWMICmdlet', 18 | 'PSAvoidUsingWriteHost', 19 | 'PSDSCReturnCorrectTypesForDSCFunctions', 20 | 'PSDSCStandardDSCFunctionsInResource', 21 | 'PSDSCUseIdenticalMandatoryParametersForDSC', 22 | 'PSDSCUseIdenticalParametersForDSC', 23 | 'PSMisleadingBacktick', 24 | 'PSMissingModuleManifestField', 25 | 'PSPossibleIncorrectComparisonWithNull', 26 | 'PSProvideCommentHelp', 27 | 'PSReservedCmdletChar', 28 | 'PSReservedParams', 29 | 'PSUseApprovedVerbs', 30 | 'PSUseCmdletCorrectly', 31 | 'PSUseOutputTypeCorrectly', 32 | 'PSAvoidGlobalVars', 33 | 'PSAvoidUsingConvertToSecureStringWithPlainText', 34 | 'PSAvoidUsingPlainTextForPassword', 35 | 'PSAvoidUsingUsernameAndPasswordParams', 36 | 'PSDSCUseVerboseMessageInDSCResource', 37 | 'PSShouldProcess', 38 | 'PSUseDeclaredVarsMoreThanAssignments', 39 | 'PSUsePSCredentialType', 40 | 41 | 'Measure-*' 42 | ) 43 | 44 | } 45 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "PowerShell Launch Current File", 9 | "type": "PowerShell", 10 | "request": "launch", 11 | "script": "${file}", 12 | "cwd": "${file}" 13 | }, 14 | { 15 | "name": "PowerShell: Interactive Session", 16 | "type": "PowerShell", 17 | "request": "launch", 18 | "cwd": "" 19 | }, 20 | { 21 | "type": "PowerShell", 22 | "request": "launch", 23 | "name": "Launch Build", 24 | "script": "${workspaceRoot}/Build.ps1", 25 | "args": [], 26 | "cwd": "${workspaceRoot}/" 27 | }, 28 | { 29 | "type": "PowerShell", 30 | "request": "launch", 31 | "name": "Launch Test", 32 | "script": "${workspaceRoot}/Build.ps1", 33 | "args": ["-Tasks test"], 34 | "cwd": "${workspaceRoot}/" 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "powershell.codeFormatting.openBraceOnSameLine": false, 3 | "powershell.codeFormatting.newLineAfterOpenBrace": true, 4 | "powershell.codeFormatting.newLineAfterCloseBrace": true, 5 | "powershell.codeFormatting.whitespaceBeforeOpenBrace": true, 6 | "powershell.codeFormatting.whitespaceBeforeOpenParen": true, 7 | "powershell.codeFormatting.whitespaceAroundOperator": true, 8 | "powershell.codeFormatting.whitespaceAfterSeparator": true, 9 | "powershell.codeFormatting.ignoreOneLineBlock": false, 10 | "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationAfterEveryPipeline", 11 | "powershell.codeFormatting.preset": "Custom", 12 | "powershell.codeFormatting.alignPropertyValuePairs": true, 13 | "powershell.developer.bundledModulesPath": "${cwd}/output/RequiredModules", 14 | "powershell.scriptAnalysis.settingsPath": ".vscode\\analyzersettings.psd1", 15 | "powershell.scriptAnalysis.enable": true, 16 | "files.trimTrailingWhitespace": true, 17 | "files.trimFinalNewlines": true, 18 | "files.insertFinalNewline": true, 19 | "files.associations": { 20 | "*.ps1xml": "xml" 21 | }, 22 | "cSpell.words": [ 23 | "COMPANYNAME", 24 | "ICONURI", 25 | "LICENSEURI", 26 | "PROJECTURI", 27 | "RELEASENOTES", 28 | "buildhelpers", 29 | "endregion", 30 | "gitversion", 31 | "icontains", 32 | "keepachangelog", 33 | "notin", 34 | "pscmdlet", 35 | "steppable" 36 | ], 37 | "[markdown]": { 38 | "files.trimTrailingWhitespace": false, 39 | "files.encoding": "utf8" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "_runner": "terminal", 4 | "windows": { 5 | "options": { 6 | "shell": { 7 | "executable": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", 8 | "args": [ 9 | "-NoProfile", 10 | "-ExecutionPolicy", 11 | "Bypass", 12 | "-Command" 13 | ] 14 | } 15 | } 16 | }, 17 | "linux": { 18 | "options": { 19 | "shell": { 20 | "executable": "/usr/bin/pwsh", 21 | "args": [ 22 | "-NoProfile", 23 | "-Command" 24 | ] 25 | } 26 | } 27 | }, 28 | "osx": { 29 | "options": { 30 | "shell": { 31 | "executable": "/usr/local/bin/pwsh", 32 | "args": [ 33 | "-NoProfile", 34 | "-Command" 35 | ] 36 | } 37 | } 38 | }, 39 | "tasks": [ 40 | { 41 | "label": "build", 42 | "type": "shell", 43 | "command": "&${cwd}/build.ps1", 44 | "args": [], 45 | "presentation": { 46 | "echo": true, 47 | "reveal": "always", 48 | "focus": true, 49 | "panel": "new", 50 | "clear": false 51 | }, 52 | "runOptions": { 53 | "runOn": "default" 54 | }, 55 | "problemMatcher": [ 56 | { 57 | "owner": "powershell", 58 | "fileLocation": [ 59 | "absolute" 60 | ], 61 | "severity": "error", 62 | "pattern": [ 63 | { 64 | "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", 65 | "message": 1 66 | }, 67 | { 68 | "regexp": "(.*)", 69 | "code": 1 70 | }, 71 | { 72 | "regexp": "" 73 | }, 74 | { 75 | "regexp": "^.*,\\s*(.*):\\s*line\\s*(\\d+).*", 76 | "file": 1, 77 | "line": 2 78 | } 79 | ] 80 | } 81 | ] 82 | }, 83 | { 84 | "label": "test", 85 | "type": "shell", 86 | "command": "&${cwd}/build.ps1", 87 | "args": ["-AutoRestore","-Tasks","test"], 88 | "presentation": { 89 | "echo": true, 90 | "reveal": "always", 91 | "focus": true, 92 | "panel": "dedicated", 93 | "showReuseMessage": true, 94 | "clear": false 95 | }, 96 | "problemMatcher": [ 97 | { 98 | "owner": "powershell", 99 | "fileLocation": [ 100 | "absolute" 101 | ], 102 | "severity": "error", 103 | "pattern": [ 104 | { 105 | "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", 106 | "message": 1 107 | }, 108 | { 109 | "regexp": "(.*)", 110 | "code": 1 111 | }, 112 | { 113 | "regexp": "" 114 | }, 115 | { 116 | "regexp": "^.*,\\s*(.*):\\s*line\\s*(\\d+).*", 117 | "file": 1, 118 | "line": 2 119 | } 120 | ] 121 | } 122 | ] 123 | } 124 | ] 125 | } 126 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog for Sampler.DscPipeline 2 | 3 | The format is based on and uses the types of changes according to [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 4 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 5 | 6 | ## [Unreleased] 7 | 8 | ### Added 9 | 10 | - Updated build scripts to latest Sampler version. 11 | - Integration tests for 'LoadDatumConfigData' task. 12 | - Integration tests for 'CompileDatumRsop' task. 13 | - Integration tests for 'Get-DscResourceProperty' function. 14 | 15 | ### Fixed 16 | 17 | - Fix parsing of UseEnvironment parameter in CompileDatumRsop task ([#39](https://github.com/SynEdgy/Sampler.DscPipeline/issues/39)) 18 | 19 | ## [0.2.0] - 2024-11-09 20 | 21 | ### Added 22 | 23 | - Adding pipeline tasks and commands from DSC Workshop. 24 | - Small changes to support easier deployment for individual environments. 25 | - Added scripts for compiling MOF and Meta MOF files without the need for the `rootConfig.ps1` script. It is now a self-contained task that takes parameters from the `Build.yml`. 26 | - Having modules available more than once results in: ImportCimAndScriptKeywordsFromModule : "A second CIM class definition 27 | for 'MSFT_PSRepository' was found while processing the schema file". Fixed that by using function 'Get-DscResourceFromModuleInFolder'. 28 | This usually happens with 'PackageManagement' and 'PowerShellGet' 29 | - The handling of the DSC MOF compilation has changed. The file 'RootConfiguration.ps1' is still used when present in the source of 30 | the DSC project that uses 'Sampler.DscPipeline'. Same applies to the Meta MOF compilation script 'RootMetaMof.ps1'. If these 31 | files don't exist, 'Sampler.DscPipeline' uses the scripts in 'ModuleRoot\Scripts'. To control which DSC composite and resource modules should be imported within the DSC configuration, add the section 'Sampler.DscPipeline' to the 'build.yml' as described 32 | on top of the file 'CompileRootConfiguration.ps1'. 33 | - Added error handling discovering 'CompileRootConfiguration.ps1' and 'RootMetaMof.ps1' 34 | - Test cases updated to Pester 5. 35 | - Fixing issue with ZipFile class not being present. 36 | - Fixing calculation of checksum if attribute NodeName is different to attribute Name (of YAML file). 37 | - Increase build speed of root configuration by only importing required Composites/Resources. 38 | - Added ''UseEnvironment'' parameter to cater for RSOP for identical node names in different environments. 39 | - Adding Home.md to wikiSource and correct casing. 40 | - Removed PSModulePath manipulation from task `CompileRootConfiguration.build.ps1`. This is now handled by the Sampler task `Set_PSModulePath`. 41 | - Redesign of the function Split-Array. Most of the time it was not working as expected, especially when requesting larger ChunkCounts (see AutomatedLab/AutomatedLab.Common/#118) 42 | - Redesign of the function Split-Array. Most of the time it was not working as expected, especially when requesting larger ChunkCounts (see AutomatedLab/AutomatedLab.Common/#118). 43 | - Improved error handling when compiling MOF files and when calling 'Get-DscResource'. 44 | - Redesign of the function 'Split-Array'. Most of the time it was not working as expected, especially when requesting larger ChunkCounts (see AutomatedLab/AutomatedLab.Common/#118). 45 | - Improved error handling when compiling MOF files. 46 | - Removed name check when creating Meta MOF files. 47 | - Update GitVersion.Tool installation to use version 5.* 48 | 49 | ### Fixed 50 | 51 | - Fixed regex for commit message `--Added new node` 52 | - Fixed task `Compress_Artifact_Collections` fails when node is filtered 53 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This project has adopted the [DSC Community Code of Conduct](https://dsccommunity.org/code_of_conduct). 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Please check out common DSC Community [contributing guidelines](https://dsccommunity.org/guidelines/contributing). 4 | 5 | ## Running the Tests 6 | 7 | If want to know how to run this module's tests you can look at the [Testing Guidelines](https://dsccommunity.org/guidelines/testing-guidelines/#running-tests). 8 | -------------------------------------------------------------------------------- /GitVersion.yml: -------------------------------------------------------------------------------- 1 | mode: ContinuousDelivery 2 | next-version: 0.0.1 3 | major-version-bump-message: '(breaking\schange|breaking|major)\b' 4 | minor-version-bump-message: '(adds?|features?|minor)\b' 5 | patch-version-bump-message: '\s?(fix|patch)' 6 | no-bump-message: '\+semver:\s?(none|skip)' 7 | assembly-informational-format: '{NuGetVersionV2}+Sha.{Sha}.Date.{CommitDate}' 8 | branches: 9 | master: 10 | tag: preview 11 | regex: ^main$ 12 | pull-request: 13 | tag: PR 14 | feature: 15 | tag: useBranchName 16 | increment: Minor 17 | regex: f(eature(s)?)?[\/-] 18 | source-branches: ['master'] 19 | hotfix: 20 | tag: fix 21 | increment: Patch 22 | regex: (hot)?fix(es)?[\/-] 23 | source-branches: ['master'] 24 | 25 | ignore: 26 | sha: [] 27 | merge-message-formats: {} 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 GaelColas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sampler.DscPipeline 2 | 3 | 4 | This module contains DSC resources for the management and 5 | configuration of PRODUCT. 6 | 7 | 8 | [![Build Status](https://dev.azure.com/dsccommunity/Sampler.DscPipeline/_apis/build/status/dsccommunity.Sampler.DscPipeline?branchName=main)](https://dev.azure.com/dsccommunity/Sampler.DscPipeline/_build/latest?definitionId=9999&branchName=main) 9 | ![Azure DevOps coverage (branch)](https://img.shields.io/azure-devops/coverage/dsccommunity/Sampler.DscPipeline/9999/main) 10 | [![codecov](https://codecov.io/gh/dsccommunity/Sampler.DscPipeline/branch/main/graph/badge.svg)](https://codecov.io/gh/dsccommunity/Sampler.DscPipeline) 11 | [![Azure DevOps tests](https://img.shields.io/azure-devops/tests/dsccommunity/Sampler.DscPipeline/9999/main)](https://dsccommunity.visualstudio.com/Sampler.DscPipeline/_test/analytics?definitionId=9999&contextType=build) 12 | [![PowerShell Gallery (with prereleases)](https://img.shields.io/powershellgallery/vpre/Sampler.DscPipeline?label=Sampler.DscPipeline%20Preview)](https://www.powershellgallery.com/packages/Sampler.DscPipeline/) 13 | [![PowerShell Gallery](https://img.shields.io/powershellgallery/v/Sampler.DscPipeline?label=Sampler.DscPipeline)](https://www.powershellgallery.com/packages/Sampler.DscPipeline/) 14 | 15 | ## Code of Conduct 16 | 17 | This project has adopted this [Code of Conduct](CODE_OF_CONDUCT.md). 18 | 19 | ## Releases 20 | 21 | For each merge to the branch `main` a preview release will be 22 | deployed to [PowerShell Gallery](https://www.powershellgallery.com/). 23 | Periodically a release version tag will be pushed which will deploy a 24 | full release to [PowerShell Gallery](https://www.powershellgallery.com/). 25 | 26 | ## Contributing 27 | 28 | Please check out common DSC Community [contributing guidelines](https://dsccommunity.org/guidelines/contributing). 29 | 30 | ## Change log 31 | 32 | A full list of changes in each version can be found in the [change log](CHANGELOG.md). 33 | 34 | ## Documentation 35 | 36 | The documentation can be found in the [Sampler.DscPipeline Wiki](https://github.com/dsccommunity/Sampler.DscPipeline/wiki). 37 | The DSC resources schema files is used to automatically update the 38 | documentation on each PR merge. 39 | 40 | ### Examples 41 | 42 | You can review the [DSC Pipeline project](https://github.com/SynEdgy/DscPipeline) directory in the Sampler.DscPipeline module 43 | for some general use scenarios for all of the resources that are in the module. 44 | 45 | The resource examples are also available in the [Sampler.DscPipeline Wiki](https://github.com/dsccommunity/Sampler.DscPipeline/wiki). 46 | -------------------------------------------------------------------------------- /RequiredModules.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | PSDependOptions = @{ 3 | AddToPath = $true 4 | Target = 'output\RequiredModules' 5 | Parameters = @{ 6 | Repository = 'PSGallery' 7 | } 8 | } 9 | 10 | InvokeBuild = 'latest' 11 | PSScriptAnalyzer = 'latest' 12 | Pester = 'latest' 13 | Plaster = 'latest' 14 | ModuleBuilder = 'latest' 15 | ChangelogManagement = 'latest' 16 | Sampler = 'latest' 17 | 'Sampler.GitHubTasks' = 'latest' 18 | MarkdownLinkCheck = 'latest' 19 | 'DscResource.Common' = 'latest' 20 | 'DscResource.Test' = 'latest' 21 | 'DscResource.AnalyzerRules' = 'latest' 22 | xDSCResourceDesigner = 'latest' 23 | 'DscResource.DocGenerator' = 'latest' 24 | Datum = 'latest' 25 | 'Datum.ProtectedData' = 'latest' 26 | 'Datum.InvokeCommand' = 'latest' 27 | DscBuildHelpers = 'latest' 28 | ProtectedData = 'latest' 29 | PlatyPS = 'latest' 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Resolve-Dependency.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Gallery = 'PSGallery' 3 | 4 | AllowPrerelease = $false 5 | WithYAML = $true # Will also bootstrap PowerShell-Yaml to read other config files 6 | 7 | UsePSResourceGet = $true 8 | PSResourceGetVersion = '1.0.1' 9 | 10 | UsePowerShellGetCompatibilityModule = $true 11 | UsePowerShellGetCompatibilityModuleVersion = '3.0.23-beta23' 12 | } 13 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Security 2 | 3 | The DSC Community takes the security of our modules seriously, which includes all source code repositories managed through our GitHub organization. 4 | 5 | If you believe you have found a security vulnerability in any DSC Community owned repository, please report it to us as described below. 6 | 7 | ## Reporting Security Issues 8 | 9 | **Please do not report security vulnerabilities through public GitHub issues.** 10 | 11 | Instead, please report them to one or several members of the DSC Community organization. 12 | The easiest way to do so is to send us a direct message via twitter or slack. 13 | 14 | You should receive a response within 48 hours. If for some reason you do not, please follow up to other member of the community. 15 | 16 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 17 | 18 | * Type of issue 19 | * Full paths of source file(s) related to the manifestation of the issue 20 | * The location of the affected source code (tag/branch/commit or direct URL) 21 | * Any special configuration required to reproduce the issue 22 | * Step-by-step instructions to reproduce the issue 23 | * Proof-of-concept or exploit code (if possible) 24 | * Impact of the issue, including how an attacker might exploit the issue 25 | 26 | This information will help us triage your report more quickly. 27 | 28 | ## Preferred Languages 29 | 30 | We prefer all communications to be in English. 31 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | branches: 3 | include: 4 | - main 5 | paths: 6 | include: 7 | - source/* 8 | tags: 9 | include: 10 | - "v*" 11 | exclude: 12 | - "*-*" 13 | 14 | variables: 15 | buildFolderName: output 16 | buildArtifactName: output 17 | testResultFolderName: testResults 18 | testArtifactName: testResults 19 | sourceFolderName: source 20 | defaultBranch: main 21 | Agent.Source.Git.ShallowFetchDepth: 0 22 | 23 | stages: 24 | - stage: Build 25 | jobs: 26 | - job: Build_Module 27 | displayName: 'Build Module' 28 | pool: 29 | vmImage: 'windows-latest' 30 | steps: 31 | - pwsh: | 32 | dotnet tool install --global GitVersion.Tool --version 5.* 33 | $gitVersionObject = dotnet-gitversion | ConvertFrom-Json 34 | $gitVersionObject.psobject.properties.Foreach{ 35 | Write-Host -Object "Setting Task Variable '$($_.Name)' with value '$($_.Value)'." 36 | Write-Host -Object "##vso[task.setvariable variable=$($_.Name);]$($_.Value)" 37 | } 38 | Write-Host -Object "##vso[build.updatebuildnumber]$($gitVersionObject.FullSemVer)" 39 | displayName: Calculate ModuleVersion (GitVersion) 40 | name: gitVersion 41 | 42 | - task: PowerShell@2 43 | name: build 44 | displayName: 'Build Module' 45 | inputs: 46 | filePath: './build.ps1' 47 | arguments: '-ResolveDependency' 48 | pwsh: true 49 | env: 50 | ModuleVersion: $(NuGetVersionV2) 51 | 52 | - task: PublishPipelineArtifact@1 53 | displayName: 'Publish Build Artifact' 54 | inputs: 55 | targetPath: '$(buildFolderName)/' 56 | artifact: $(buildArtifactName) 57 | publishLocation: 'pipeline' 58 | parallel: true 59 | 60 | - stage: Test 61 | dependsOn: Build 62 | jobs: 63 | - job: Test_HQRM 64 | displayName: 'HQRM' 65 | pool: 66 | vmImage: 'windows-latest' 67 | timeoutInMinutes: 0 68 | steps: 69 | 70 | - task: DownloadPipelineArtifact@2 71 | displayName: 'Download Build Artifact' 72 | inputs: 73 | buildType: 'current' 74 | artifactName: $(buildArtifactName) 75 | targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' 76 | 77 | - task: PowerShell@2 78 | name: test 79 | displayName: 'Run HQRM Test' 80 | inputs: 81 | filePath: './build.ps1' 82 | arguments: '-Tasks hqrmtest' 83 | pwsh: false 84 | 85 | - task: PublishTestResults@2 86 | displayName: 'Publish Test Results' 87 | condition: succeededOrFailed() 88 | inputs: 89 | testResultsFormat: 'NUnit' 90 | testResultsFiles: 'output/testResults/NUnit*.xml' 91 | testRunTitle: 'HQRM' 92 | 93 | # If several pipeline jobs are running test, consider renaming this job: 94 | - job: Test_Unit 95 | displayName: 'Unit' 96 | pool: 97 | vmImage: 'windows-latest' 98 | timeoutInMinutes: 0 99 | steps: 100 | 101 | - task: DownloadPipelineArtifact@2 102 | displayName: 'Download Build Artifact' 103 | inputs: 104 | buildType: 'current' 105 | artifactName: $(buildArtifactName) 106 | targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' 107 | 108 | - task: PowerShell@2 109 | name: test 110 | displayName: Run Unit Test 111 | inputs: 112 | filePath: ./build.ps1 113 | arguments: -Tasks test 114 | pwsh: true 115 | 116 | - task: PublishTestResults@2 117 | displayName: 'Publish Test Results' 118 | condition: succeededOrFailed() 119 | inputs: 120 | testResultsFormat: 'NUnit' 121 | testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml' 122 | # If several pipeline jobs are generating test result, consider renaming this title: 123 | testRunTitle: 'Unit' 124 | 125 | - task: PublishPipelineArtifact@1 126 | displayName: 'Publish Test Artifact' 127 | inputs: 128 | targetPath: '$(buildFolderName)/$(testResultFolderName)/' 129 | artifactName: $(testArtifactName) 130 | # If several pipeline jobs are generating code coverage, replace above with this: 131 | #artifactName: 'CodeCoverageWindows' # Can be any, in the pipeline, unique name 132 | parallel: true 133 | 134 | - job: Code_Coverage 135 | displayName: 'Publish Code Coverage' 136 | dependsOn: Test_Unit 137 | pool: 138 | vmImage: 'ubuntu-latest' 139 | timeoutInMinutes: 0 140 | steps: 141 | 142 | - pwsh: | 143 | $repositoryOwner,$repositoryName = $env:BUILD_REPOSITORY_NAME -split '/' 144 | echo "##vso[task.setvariable variable=RepositoryOwner;isOutput=true]$repositoryOwner" 145 | echo "##vso[task.setvariable variable=RepositoryName;isOutput=true]$repositoryName" 146 | name: dscBuildVariable 147 | displayName: 'Set Environment Variables' 148 | 149 | - task: DownloadPipelineArtifact@2 150 | displayName: 'Download Build Artifact' 151 | inputs: 152 | buildType: 'current' 153 | artifactName: $(buildArtifactName) 154 | targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' 155 | 156 | - task: DownloadPipelineArtifact@2 157 | # If several pipeline jobs are generating code coverage, consider renaming this display name: 158 | displayName: 'Download Test Artifact' 159 | inputs: 160 | buildType: 'current' 161 | # If several pipeline jobs are generating code coverage, set the correct artifact name: 162 | artifactName: $(testArtifactName) 163 | targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)' 164 | 165 | - task: PublishCodeCoverageResults@1 166 | displayName: 'Publish Code Coverage to Azure DevOps' 167 | inputs: 168 | codeCoverageTool: 'JaCoCo' 169 | summaryFileLocation: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml' 170 | pathToSources: '$(Build.SourcesDirectory)/$(sourceFolderName)/' 171 | - script: | 172 | bash <(curl -s https://codecov.io/bash) -f "./$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml" 173 | displayName: 'Publish Code Coverage to Codecov.io' 174 | 175 | - stage: Deploy 176 | dependsOn: Test 177 | condition: | 178 | and( 179 | succeeded(), 180 | or( 181 | eq(variables['Build.SourceBranch'], 'refs/heads/main'), 182 | startsWith(variables['Build.SourceBranch'], 'refs/tags/') 183 | ), 184 | contains(variables['System.TeamFoundationCollectionUri'], 'synedgy') 185 | ) 186 | jobs: 187 | - job: Deploy_Module 188 | displayName: 'Deploy Module' 189 | pool: 190 | vmImage: 'ubuntu-latest' 191 | steps: 192 | 193 | - task: DownloadPipelineArtifact@2 194 | displayName: 'Download Pipeline Artifact' 195 | inputs: 196 | buildType: 'current' 197 | artifactName: $(buildArtifactName) 198 | targetPath: '$(Build.SourcesDirectory)/$(buildArtifactName)' 199 | 200 | - task: PowerShell@2 201 | name: publishRelease 202 | displayName: 'Publish Release' 203 | inputs: 204 | filePath: './build.ps1' 205 | arguments: '-tasks publish' 206 | pwsh: true 207 | env: 208 | GitHubToken: $(GitHubToken) 209 | GalleryApiToken: $(GalleryApiToken) 210 | ReleaseBranch: $(defaultBranch) 211 | MainGitBranch: $(defaultBranch) 212 | 213 | - task: PowerShell@2 214 | name: sendChangelogPR 215 | displayName: 'Send Changelog PR' 216 | inputs: 217 | filePath: './build.ps1' 218 | arguments: '-tasks Create_ChangeLog_GitHub_PR' 219 | pwsh: true 220 | env: 221 | GitHubToken: $(GitHubToken) 222 | ReleaseBranch: $(defaultBranch) 223 | MainGitBranch: $(defaultBranch) 224 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .DESCRIPTION 3 | Bootstrap and build script for PowerShell module CI/CD pipeline. 4 | 5 | .PARAMETER Tasks 6 | The task or tasks to run. The default value is '.' (runs the default task). 7 | 8 | .PARAMETER CodeCoverageThreshold 9 | The code coverage target threshold to uphold. Set to 0 to disable. 10 | The default value is '' (empty string). 11 | 12 | .PARAMETER BuildConfig 13 | Not yet written. 14 | 15 | .PARAMETER OutputDirectory 16 | Specifies the folder to build the artefact into. The default value is 'output'. 17 | 18 | .PARAMETER BuiltModuleSubdirectory 19 | Subdirectory name to build the module (under $OutputDirectory). The default 20 | value is '' (empty string). 21 | 22 | .PARAMETER RequiredModulesDirectory 23 | Can be a path (relative to $PSScriptRoot or absolute) to tell Resolve-Dependency 24 | and PSDepend where to save the required modules. It is also possible to use 25 | 'CurrentUser' och 'AllUsers' to install missing dependencies. You can override 26 | the value for PSDepend in the Build.psd1 build manifest. The default value is 27 | 'output/RequiredModules'. 28 | 29 | .PARAMETER PesterScript 30 | One or more paths that will override the Pester configuration in build 31 | configuration file when running the build task Invoke_Pester_Tests. 32 | 33 | If running Pester 5 test, use the alias PesterPath to be future-proof. 34 | 35 | .PARAMETER PesterTag 36 | Filter which tags to run when invoking Pester tests. This is used in the 37 | Invoke-Pester.pester.build.ps1 tasks. 38 | 39 | .PARAMETER PesterExcludeTag 40 | Filter which tags to exclude when invoking Pester tests. This is used in 41 | the Invoke-Pester.pester.build.ps1 tasks. 42 | 43 | .PARAMETER DscTestTag 44 | Filter which tags to run when invoking DSC Resource tests. This is used 45 | in the DscResource.Test.build.ps1 tasks. 46 | 47 | .PARAMETER DscTestExcludeTag 48 | Filter which tags to exclude when invoking DSC Resource tests. This is 49 | used in the DscResource.Test.build.ps1 tasks. 50 | 51 | .PARAMETER ResolveDependency 52 | Not yet written. 53 | 54 | .PARAMETER BuildInfo 55 | The build info object from ModuleBuilder. Defaults to an empty hashtable. 56 | 57 | .PARAMETER AutoRestore 58 | Not yet written. 59 | 60 | .PARAMETER UseModuleFast 61 | Specifies to use ModuleFast instead of PowerShellGet to resolve dependencies 62 | faster. 63 | 64 | .PARAMETER UsePSResourceGet 65 | Specifies to use PSResourceGet instead of PowerShellGet to resolve dependencies 66 | faster. This can also be configured in Resolve-Dependency.psd1. 67 | 68 | .PARAMETER UsePowerShellGetCompatibilityModule 69 | Specifies to use the compatibility module PowerShellGet. This parameter 70 | only works then the method of downloading dependencies is PSResourceGet. 71 | This can also be configured in Resolve-Dependency.psd1. 72 | #> 73 | [CmdletBinding()] 74 | param 75 | ( 76 | [Parameter(Position = 0)] 77 | [System.String[]] 78 | $Tasks = '.', 79 | 80 | [Parameter()] 81 | [System.String] 82 | $CodeCoverageThreshold = '', 83 | 84 | [Parameter()] 85 | [System.String] 86 | [ValidateScript( 87 | { Test-Path -Path $_ } 88 | )] 89 | $BuildConfig, 90 | 91 | [Parameter()] 92 | [System.String] 93 | $OutputDirectory = 'output', 94 | 95 | [Parameter()] 96 | [System.String] 97 | $BuiltModuleSubdirectory = '', 98 | 99 | [Parameter()] 100 | [System.String] 101 | $RequiredModulesDirectory = $(Join-Path 'output' 'RequiredModules'), 102 | 103 | [Parameter()] 104 | # This alias is to prepare for the rename of this parameter to PesterPath when Pester 4 support is removed 105 | [Alias('PesterPath')] 106 | [System.Object[]] 107 | $PesterScript, 108 | 109 | [Parameter()] 110 | [System.String[]] 111 | $PesterTag, 112 | 113 | [Parameter()] 114 | [System.String[]] 115 | $PesterExcludeTag, 116 | 117 | [Parameter()] 118 | [System.String[]] 119 | $DscTestTag, 120 | 121 | [Parameter()] 122 | [System.String[]] 123 | $DscTestExcludeTag, 124 | 125 | [Parameter()] 126 | [Alias('bootstrap')] 127 | [System.Management.Automation.SwitchParameter] 128 | $ResolveDependency, 129 | 130 | [Parameter(DontShow)] 131 | [AllowNull()] 132 | [System.Collections.Hashtable] 133 | $BuildInfo, 134 | 135 | [Parameter()] 136 | [System.Management.Automation.SwitchParameter] 137 | $AutoRestore, 138 | 139 | [Parameter()] 140 | [System.Management.Automation.SwitchParameter] 141 | $UseModuleFast, 142 | 143 | [Parameter()] 144 | [System.Management.Automation.SwitchParameter] 145 | $UsePSResourceGet, 146 | 147 | [Parameter()] 148 | [System.Management.Automation.SwitchParameter] 149 | $UsePowerShellGetCompatibilityModule 150 | ) 151 | 152 | <# 153 | The BEGIN block (at the end of this file) handles the Bootstrap of the Environment 154 | before Invoke-Build can run the tasks if the parameter ResolveDependency (or 155 | parameter alias Bootstrap) is specified. 156 | #> 157 | 158 | process 159 | { 160 | if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') 161 | { 162 | # Only run the process block through InvokeBuild (look at the Begin block at the bottom of this script). 163 | return 164 | } 165 | 166 | # Execute the Build process from the .build.ps1 path. 167 | Push-Location -Path $PSScriptRoot -StackName 'BeforeBuild' 168 | 169 | try 170 | { 171 | Write-Host -Object "[build] Parsing defined tasks" -ForeGroundColor Magenta 172 | 173 | # Load the default BuildInfo if the parameter BuildInfo is not set. 174 | if (-not $PSBoundParameters.ContainsKey('BuildInfo')) 175 | { 176 | try 177 | { 178 | if (Test-Path -Path $BuildConfig) 179 | { 180 | $configFile = Get-Item -Path $BuildConfig 181 | 182 | Write-Host -Object "[build] Loading Configuration from $configFile" 183 | 184 | $BuildInfo = switch -Regex ($configFile.Extension) 185 | { 186 | # Native Support for PSD1 187 | '\.psd1' 188 | { 189 | if (-not (Get-Command -Name Import-PowerShellDataFile -ErrorAction SilentlyContinue)) 190 | { 191 | Import-Module -Name Microsoft.PowerShell.Utility -RequiredVersion 3.1.0.0 192 | } 193 | 194 | Import-PowerShellDataFile -Path $BuildConfig 195 | } 196 | 197 | # Support for yaml when module PowerShell-Yaml is available 198 | '\.[yaml|yml]' 199 | { 200 | Import-Module -Name 'powershell-yaml' -ErrorAction Stop 201 | 202 | ConvertFrom-Yaml -Yaml (Get-Content -Raw $configFile) 203 | } 204 | 205 | # Support for JSON and JSONC (by Removing comments) when module PowerShell-Yaml is available 206 | '\.[json|jsonc]' 207 | { 208 | $jsonFile = Get-Content -Raw -Path $configFile 209 | 210 | $jsonContent = $jsonFile -replace '(?m)\s*//.*?$' -replace '(?ms)/\*.*?\*/' 211 | 212 | # Yaml is superset of JSON. 213 | ConvertFrom-Yaml -Yaml $jsonContent 214 | } 215 | 216 | # Unknown extension, return empty hashtable. 217 | default 218 | { 219 | Write-Error -Message "Extension '$_' not supported. using @{}" 220 | 221 | @{ } 222 | } 223 | } 224 | } 225 | else 226 | { 227 | Write-Host -Object "Configuration file '$($BuildConfig.FullName)' not found" -ForegroundColor Red 228 | 229 | # No config file was found, return empty hashtable. 230 | $BuildInfo = @{ } 231 | } 232 | } 233 | catch 234 | { 235 | $logMessage = "Error loading Config '$($BuildConfig.FullName)'.`r`nAre you missing dependencies?`r`nMake sure you run './build.ps1 -ResolveDependency -tasks noop' before running build to restore the required modules." 236 | 237 | Write-Host -Object $logMessage -ForegroundColor Yellow 238 | 239 | $BuildInfo = @{ } 240 | 241 | Write-Error -Message $_.Exception.Message 242 | } 243 | } 244 | 245 | # If the Invoke-Build Task Header is specified in the Build Info, set it. 246 | if ($BuildInfo.TaskHeader) 247 | { 248 | Set-BuildHeader -Script ([scriptblock]::Create($BuildInfo.TaskHeader)) 249 | } 250 | 251 | <# 252 | Add BuildModuleOutput to PSModule Path environment variable. 253 | Moved here (not in begin block) because build file can contains BuiltSubModuleDirectory value. 254 | #> 255 | if ($BuiltModuleSubdirectory) 256 | { 257 | if (-not (Split-Path -IsAbsolute -Path $BuiltModuleSubdirectory)) 258 | { 259 | $BuildModuleOutput = Join-Path -Path $OutputDirectory -ChildPath $BuiltModuleSubdirectory 260 | } 261 | else 262 | { 263 | $BuildModuleOutput = $BuiltModuleSubdirectory 264 | } 265 | } # test if BuiltModuleSubDirectory set in build config file 266 | elseif ($BuildInfo.ContainsKey('BuiltModuleSubDirectory')) 267 | { 268 | $BuildModuleOutput = Join-Path -Path $OutputDirectory -ChildPath $BuildInfo['BuiltModuleSubdirectory'] 269 | } 270 | else 271 | { 272 | $BuildModuleOutput = $OutputDirectory 273 | } 274 | 275 | # Pre-pending $BuildModuleOutput folder to PSModulePath to resolve built module from this folder. 276 | if ($powerShellModulePaths -notcontains $BuildModuleOutput) 277 | { 278 | Write-Host -Object "[build] Pre-pending '$BuildModuleOutput' folder to PSModulePath" -ForegroundColor Green 279 | 280 | $env:PSModulePath = $BuildModuleOutput + [System.IO.Path]::PathSeparator + $env:PSModulePath 281 | } 282 | 283 | <# 284 | Import Tasks from modules via their exported aliases when defined in Build Manifest. 285 | https://github.com/nightroman/Invoke-Build/tree/master/Tasks/Import#example-2-import-from-a-module-with-tasks 286 | #> 287 | if ($BuildInfo.ContainsKey('ModuleBuildTasks')) 288 | { 289 | foreach ($module in $BuildInfo['ModuleBuildTasks'].Keys) 290 | { 291 | try 292 | { 293 | Write-Host -Object "Importing tasks from module $module" -ForegroundColor DarkGray 294 | 295 | $loadedModule = Import-Module -Name $module -PassThru -ErrorAction Stop 296 | 297 | foreach ($TaskToExport in $BuildInfo['ModuleBuildTasks'].($module)) 298 | { 299 | $loadedModule.ExportedAliases.GetEnumerator().Where{ 300 | Write-Host -Object "`t Loading $($_.Key)..." -ForegroundColor DarkGray 301 | 302 | # Using -like to support wildcard. 303 | $_.Key -like $TaskToExport 304 | }.ForEach{ 305 | # Dot-sourcing the Tasks via their exported aliases. 306 | . (Get-Alias $_.Key) 307 | } 308 | } 309 | } 310 | catch 311 | { 312 | Write-Host -Object "Could not load tasks for module $module." -ForegroundColor Red 313 | 314 | Write-Error -Message $_ 315 | } 316 | } 317 | } 318 | 319 | # Loading Build Tasks defined in the .build/ folder (will override the ones imported above if same task name). 320 | Get-ChildItem -Path '.build/' -Recurse -Include '*.ps1' -ErrorAction Ignore | 321 | ForEach-Object { 322 | "Importing file $($_.BaseName)" | Write-Verbose 323 | 324 | . $_.FullName 325 | } 326 | 327 | # Synopsis: Empty task, useful to test the bootstrap process. 328 | task noop { } 329 | 330 | # Define default task sequence ("."), can be overridden in the $BuildInfo. 331 | task . { 332 | Write-Build -Object 'No sequence currently defined for the default task' -ForegroundColor Yellow 333 | } 334 | 335 | Write-Host -Object 'Adding Workflow from configuration:' -ForegroundColor DarkGray 336 | 337 | # Load Invoke-Build task sequences/workflows from $BuildInfo. 338 | foreach ($workflow in $BuildInfo.BuildWorkflow.keys) 339 | { 340 | Write-Verbose -Message "Creating Build Workflow '$Workflow' with tasks $($BuildInfo.BuildWorkflow.($Workflow) -join ', ')." 341 | 342 | $workflowItem = $BuildInfo.BuildWorkflow.($workflow) 343 | 344 | if ($workflowItem.Trim() -match '^\{(?[\w\W]*)\}$') 345 | { 346 | $workflowItem = [ScriptBlock]::Create($Matches['sb']) 347 | } 348 | 349 | Write-Host -Object " +-> $workflow" -ForegroundColor DarkGray 350 | 351 | task $workflow $workflowItem 352 | } 353 | 354 | Write-Host -Object "[build] Executing requested workflow: $($Tasks -join ', ')" -ForeGroundColor Magenta 355 | 356 | } 357 | finally 358 | { 359 | Pop-Location -StackName 'BeforeBuild' 360 | } 361 | } 362 | 363 | begin 364 | { 365 | # Find build config if not specified. 366 | if (-not $BuildConfig) 367 | { 368 | $config = Get-ChildItem -Path "$PSScriptRoot\*" -Include 'build.y*ml', 'build.psd1', 'build.json*' -ErrorAction Ignore 369 | 370 | if (-not $config -or ($config -is [System.Array] -and $config.Length -le 0)) 371 | { 372 | throw 'No build configuration found. Specify path via parameter BuildConfig.' 373 | } 374 | elseif ($config -is [System.Array]) 375 | { 376 | if ($config.Length -gt 1) 377 | { 378 | throw 'More than one build configuration found. Specify which path to use via parameter BuildConfig.' 379 | } 380 | 381 | $BuildConfig = $config[0] 382 | } 383 | else 384 | { 385 | $BuildConfig = $config 386 | } 387 | } 388 | 389 | # Bootstrapping the environment before using Invoke-Build as task runner 390 | 391 | if ($MyInvocation.ScriptName -notlike '*Invoke-Build.ps1') 392 | { 393 | Write-Host -Object "[pre-build] Starting Build Init" -ForegroundColor Green 394 | 395 | Push-Location $PSScriptRoot -StackName 'BuildModule' 396 | } 397 | 398 | if ($RequiredModulesDirectory -in @('CurrentUser', 'AllUsers')) 399 | { 400 | # Installing modules instead of saving them. 401 | Write-Host -Object "[pre-build] Required Modules will be installed to the PowerShell module path that is used for $RequiredModulesDirectory." -ForegroundColor Green 402 | 403 | <# 404 | The variable $PSDependTarget will be used below when building the splatting 405 | variable before calling Resolve-Dependency.ps1, unless overridden in the 406 | file Resolve-Dependency.psd1. 407 | #> 408 | $PSDependTarget = $RequiredModulesDirectory 409 | } 410 | else 411 | { 412 | if (-not (Split-Path -IsAbsolute -Path $OutputDirectory)) 413 | { 414 | $OutputDirectory = Join-Path -Path $PSScriptRoot -ChildPath $OutputDirectory 415 | } 416 | 417 | # Resolving the absolute path to save the required modules to. 418 | if (-not (Split-Path -IsAbsolute -Path $RequiredModulesDirectory)) 419 | { 420 | $RequiredModulesDirectory = Join-Path -Path $PSScriptRoot -ChildPath $RequiredModulesDirectory 421 | } 422 | 423 | # Create the output/modules folder if not exists, or resolve the Absolute path otherwise. 424 | if (Resolve-Path -Path $RequiredModulesDirectory -ErrorAction SilentlyContinue) 425 | { 426 | Write-Debug -Message "[pre-build] Required Modules path already exist at $RequiredModulesDirectory" 427 | 428 | $requiredModulesPath = Convert-Path -Path $RequiredModulesDirectory 429 | } 430 | else 431 | { 432 | Write-Host -Object "[pre-build] Creating required modules directory $RequiredModulesDirectory." -ForegroundColor Green 433 | 434 | $requiredModulesPath = (New-Item -ItemType Directory -Force -Path $RequiredModulesDirectory).FullName 435 | } 436 | 437 | $powerShellModulePaths = $env:PSModulePath -split [System.IO.Path]::PathSeparator 438 | 439 | # Pre-pending $requiredModulesPath folder to PSModulePath to resolve from this folder FIRST. 440 | if ($RequiredModulesDirectory -notin @('CurrentUser', 'AllUsers') -and 441 | ($powerShellModulePaths -notcontains $RequiredModulesDirectory)) 442 | { 443 | Write-Host -Object "[pre-build] Pre-pending '$RequiredModulesDirectory' folder to PSModulePath" -ForegroundColor Green 444 | 445 | $env:PSModulePath = $RequiredModulesDirectory + [System.IO.Path]::PathSeparator + $env:PSModulePath 446 | } 447 | 448 | $powerShellYamlModule = Get-Module -Name 'powershell-yaml' -ListAvailable 449 | $invokeBuildModule = Get-Module -Name 'InvokeBuild' -ListAvailable 450 | $psDependModule = Get-Module -Name 'PSDepend' -ListAvailable 451 | 452 | # Checking if the user should -ResolveDependency. 453 | if (-not ($powerShellYamlModule -and $invokeBuildModule -and $psDependModule) -and -not $ResolveDependency) 454 | { 455 | if ($AutoRestore -or -not $PSBoundParameters.ContainsKey('Tasks') -or $Tasks -contains 'build') 456 | { 457 | Write-Host -Object "[pre-build] Dependency missing, running './build.ps1 -ResolveDependency -Tasks noop' for you `r`n" -ForegroundColor Yellow 458 | 459 | $ResolveDependency = $true 460 | } 461 | else 462 | { 463 | Write-Warning -Message "Some required Modules are missing, make sure you first run with the '-ResolveDependency' parameter. Running 'build.ps1 -ResolveDependency -Tasks noop' will pull required modules without running the build task." 464 | } 465 | } 466 | 467 | <# 468 | The variable $PSDependTarget will be used below when building the splatting 469 | variable before calling Resolve-Dependency.ps1, unless overridden in the 470 | file Resolve-Dependency.psd1. 471 | #> 472 | $PSDependTarget = $requiredModulesPath 473 | } 474 | 475 | if ($ResolveDependency) 476 | { 477 | Write-Host -Object "[pre-build] Resolving dependencies using preferred method." -ForegroundColor Green 478 | 479 | $resolveDependencyParams = @{ } 480 | 481 | # If BuildConfig is a Yaml file, bootstrap powershell-yaml via ResolveDependency. 482 | if ($BuildConfig -match '\.[yaml|yml]$') 483 | { 484 | $resolveDependencyParams.Add('WithYaml', $true) 485 | } 486 | 487 | $resolveDependencyAvailableParams = (Get-Command -Name '.\Resolve-Dependency.ps1').Parameters.Keys 488 | 489 | foreach ($cmdParameter in $resolveDependencyAvailableParams) 490 | { 491 | # The parameter has been explicitly used for calling the .build.ps1 492 | if ($MyInvocation.BoundParameters.ContainsKey($cmdParameter)) 493 | { 494 | $paramValue = $MyInvocation.BoundParameters.Item($cmdParameter) 495 | 496 | Write-Debug " adding $cmdParameter :: $paramValue [from user-provided parameters to Build.ps1]" 497 | 498 | $resolveDependencyParams.Add($cmdParameter, $paramValue) 499 | } 500 | # Use defaults parameter value from Build.ps1, if any 501 | else 502 | { 503 | $paramValue = Get-Variable -Name $cmdParameter -ValueOnly -ErrorAction Ignore 504 | 505 | if ($paramValue) 506 | { 507 | Write-Debug " adding $cmdParameter :: $paramValue [from default Build.ps1 variable]" 508 | 509 | $resolveDependencyParams.Add($cmdParameter, $paramValue) 510 | } 511 | } 512 | } 513 | 514 | Write-Host -Object "[pre-build] Starting bootstrap process." -ForegroundColor Green 515 | 516 | .\Resolve-Dependency.ps1 @resolveDependencyParams 517 | } 518 | 519 | if ($MyInvocation.ScriptName -notlike '*Invoke-Build.ps1') 520 | { 521 | Write-Verbose -Message "Bootstrap completed. Handing back to InvokeBuild." 522 | 523 | if ($PSBoundParameters.ContainsKey('ResolveDependency')) 524 | { 525 | Write-Verbose -Message "Dependency already resolved. Removing task." 526 | 527 | $null = $PSBoundParameters.Remove('ResolveDependency') 528 | } 529 | 530 | Write-Host -Object "[build] Starting build with InvokeBuild." -ForegroundColor Green 531 | 532 | Invoke-Build @PSBoundParameters -Task $Tasks -File $MyInvocation.MyCommand.Path 533 | 534 | Pop-Location -StackName 'BuildModule' 535 | 536 | return 537 | } 538 | } 539 | -------------------------------------------------------------------------------- /build.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | #################################################### 3 | # ModuleBuilder Configuration # 4 | #################################################### 5 | CopyPaths: 6 | - en-US 7 | - Tasks 8 | - Scripts 9 | Encoding: UTF8 10 | suffix: suffix.ps1 11 | VersionedOutputDirectory: true 12 | 13 | ModuleBuildTasks: 14 | Sampler: 15 | - '*.build.Sampler.ib.tasks' 16 | Sampler.GitHubTasks: 17 | - '*.ib.tasks' 18 | DscResource.DocGenerator: 19 | - 'Task.*' 20 | DscResource.Test: 21 | - 'Task.*' 22 | 23 | #################################################### 24 | # Pipeline Configuration # 25 | #################################################### 26 | BuildWorkflow: 27 | '.': 28 | - build 29 | - test 30 | 31 | build: 32 | - Clean 33 | - Build_Module_ModuleBuilder 34 | - Build_NestedModules_ModuleBuilder 35 | - Create_Changelog_Release_Output 36 | 37 | docs: 38 | - Generate_Conceptual_Help 39 | - Generate_Wiki_Content 40 | - Generate_Wiki_Sidebar 41 | - Clean_Markdown_Metadata 42 | - Package_Wiki_Content 43 | 44 | pack: 45 | - build 46 | - docs 47 | - package_module_nupkg 48 | 49 | hqrmtest: 50 | - Invoke_HQRM_Tests_Stop_On_Fail 51 | 52 | test: 53 | - Pester_Tests_Stop_On_Fail 54 | - Convert_Pester_Coverage 55 | - Pester_If_Code_Coverage_Under_Threshold 56 | 57 | publish: 58 | - publish_module_to_gallery 59 | - Publish_Release_To_GitHub 60 | - Publish_GitHub_Wiki_Content 61 | - Create_ChangeLog_GitHub_PR 62 | 63 | #################################################### 64 | # Pester Configuration (Sampler) # 65 | #################################################### 66 | Pester: 67 | Configuration: 68 | Run: 69 | Path: 70 | - tests/Integration 71 | Output: 72 | Verbosity: Detailed 73 | StackTraceVerbosity: Full 74 | CIFormat: Auto 75 | CodeCoverage: 76 | CoveragePercentTarget: 10 77 | OutputPath: JaCoCo_coverage.xml 78 | OutputEncoding: ascii 79 | UseBreakpoints: false 80 | TestResult: 81 | OutputFormat: NUnitXML 82 | OutputEncoding: ascii 83 | 84 | #################################################### 85 | # Pester Configuration (DscResource.Test) # 86 | #################################################### 87 | DscTest: 88 | Pester: 89 | Configuration: 90 | Filter: 91 | ExcludeTag: 92 | - "Common Tests - New Error-Level Script Analyzer Rules" 93 | Output: 94 | Verbosity: Detailed 95 | CIFormat: Auto 96 | TestResult: 97 | Enabled: true 98 | OutputFormat: NUnitXML 99 | OutputEncoding: ascii 100 | OutputPath: ./output/testResults/NUnitXml_HQRM_Tests.xml 101 | Script: 102 | ExcludeSourceFile: 103 | - output 104 | ExcludeModuleFile: 105 | MainGitBranch: main 106 | 107 | # Invoke-Build Header to be used to 'decorate' the terminal output of the tasks. 108 | TaskHeader: | 109 | param($Path) 110 | "" 111 | "=" * 79 112 | Write-Build Cyan "`t`t`t$($Task.Name.replace("_"," ").ToUpper())" 113 | Write-Build DarkGray "$(Get-BuildSynopsis $Task)" 114 | "-" * 79 115 | Write-Build DarkGray " $Path" 116 | Write-Build DarkGray " $($Task.InvocationInfo.ScriptName):$($Task.InvocationInfo.ScriptLineNumber)" 117 | "" 118 | 119 | #################################################### 120 | # GitHub Configuration # 121 | #################################################### 122 | GitHubConfig: 123 | GitHubFilesToAdd: 124 | - 'CHANGELOG.md' 125 | GitHubConfigUserName: dscbot 126 | GitHubConfigUserEmail: dsccommunity@outlook.com 127 | UpdateChangelogOnPrerelease: false 128 | 129 | #################################################### 130 | # DscResource.DocGenerator Configuration # 131 | #################################################### 132 | DscResource.DocGenerator: 133 | Generate_Conceptual_Help: 134 | MarkdownCodeRegularExpression: 135 | - '\`(.+?)\`' # Match inline code-block 136 | - '\\(\\)' # Match escaped backslash 137 | - '\[[^\[]+\]\((.+?)\)' # Match markdown URL 138 | - '_(.+?)_' # Match Italic (underscore) 139 | - '\*\*(.+?)\*\*' # Match bold 140 | - '\*(.+?)\*' # Match Italic (asterisk) 141 | Publish_GitHub_Wiki_Content: 142 | Debug: false 143 | Generate_Wiki_Content: 144 | MofResourceMetadata: 145 | Type: MofResource 146 | Category: Resources 147 | ClassResourceMetadata: 148 | Type: ClassResource 149 | Category: Resources 150 | CompositeResourceMetadata: 151 | Type: CompositeResource 152 | Category: Resources 153 | Generate_Wiki_Sidebar: 154 | Debug: false 155 | AlwaysOverwrite: true 156 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: no 3 | # master should be the baseline for reporting 4 | branch: main 5 | 6 | comment: 7 | layout: "reach, diff, flags, files" 8 | behavior: default 9 | 10 | coverage: 11 | range: 50..80 12 | round: down 13 | precision: 0 14 | 15 | status: 16 | project: 17 | default: 18 | # Set the overall project code coverage requirement to 70% 19 | target: 70 20 | patch: 21 | default: 22 | # Set the pull request requirement to not regress overall coverage by more than 5% 23 | # and let codecov.io set the goal for the code changed in the patch. 24 | target: auto 25 | threshold: 5 26 | 27 | # Use this if there are paths that should not be part of the code coverage, for 28 | # example a deprecated function where tests has been removed. 29 | #ignore: 30 | # - 'source/Public/Get-Deprecated.ps1' 31 | -------------------------------------------------------------------------------- /source/Public/Get-DatumNodesRecursive.ps1: -------------------------------------------------------------------------------- 1 | using module datum 2 | 3 | function Get-DatumNodesRecursive 4 | { 5 | [CmdletBinding()] 6 | param 7 | ( 8 | [Parameter()] 9 | [object] 10 | $AllDatumNodes = (Get-Variable -Name Datum -ValueOnly).AllNodes 11 | ) 12 | 13 | $datumContainers = [System.Collections.Queue]::new() 14 | 15 | Write-Verbose -Message "Inspecting [$($AllDatumNodes.PSObject.Properties.Where({$_.MemberType -eq 'ScriptProperty'}).Name -join ', ')]" 16 | $AllDatumNodes.PSObject.Properties.Where({ $_.MemberType -eq 'ScriptProperty' }).ForEach({ 17 | Write-Verbose -Message "Working on '$($_.Name)'." 18 | $val = $_.Value | Add-Member -MemberType NoteProperty -Name Name -Value $_.Name -PassThru -ErrorAction Ignore -Force 19 | if ($val -is [FileProvider]) 20 | { 21 | Write-Verbose -Message "Adding '$($val.Name)' to the queue." 22 | $datumContainers.Enqueue($val) 23 | } 24 | else 25 | { 26 | Write-Verbose -Message "Adding Node '$($_.Name)'." 27 | $val['Name'] = $_.Name 28 | $val 29 | } 30 | }) 31 | 32 | while ($datumContainers.Count -gt 0) 33 | { 34 | $currentContainer = $datumContainers.Dequeue() 35 | Write-Debug -Message "Working on Container '$($currentContainer.Name)'." 36 | 37 | $currentContainer.PSObject.Properties.Where({ $_.MemberType -eq 'ScriptProperty' }).ForEach({ 38 | $val = $currentContainer.($_.Name) 39 | $val | Add-Member -MemberType NoteProperty -Name Name -Value $_.Name -ErrorAction Ignore 40 | if ($val -is [FileProvider]) 41 | { 42 | Write-Verbose -Message "Found Container '$($_.Name).'" 43 | $datumContainers.Enqueue($val) 44 | } 45 | else 46 | { 47 | Write-Verbose -Message "Found Node '$($_.Name)'." 48 | $val['Name'] = $_.Name 49 | $val 50 | } 51 | }) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /source/Public/Get-DscErrorMessage.ps1: -------------------------------------------------------------------------------- 1 | function Get-DscErrorMessage 2 | { 3 | param 4 | ( 5 | [Parameter()] 6 | [System.Exception] 7 | $Exception 8 | ) 9 | 10 | switch ($Exception) 11 | { 12 | { $_ -is [System.Management.Automation.ItemNotFoundException] } 13 | { 14 | #can be ignored, very likely caused by Get-Item within the PSDesiredStateConfiguration module 15 | break 16 | } 17 | 18 | { $_.Message -match "Unable to find repository 'PSGallery" } 19 | { 20 | 'Error in Package Management' 21 | break 22 | } 23 | 24 | { $_.Message -match 'A second CIM class definition' } 25 | { 26 | # This happens when several versions of same module are available. 27 | # Mainly a problem when when $Env:PSModulePath is polluted or 28 | # DscResources or DSC_Configuration are not clean 29 | 'Multiple version of the same module exist' 30 | break 31 | } 32 | 33 | { $_ -is [System.Management.Automation.ParentContainsErrorRecordException] } 34 | { 35 | "Compilation Error: $_.Message" 36 | break 37 | } 38 | 39 | { $_.Message -match ([regex]::Escape("Cannot find path 'HKLM:\SOFTWARE\Microsoft\Powershell\3\DSC'")) } 40 | { 41 | if ($_.InvocationInfo.PositionMessage -match 'PSDscAllowDomainUser') 42 | { 43 | # This tend to be repeated for all nodes even if only 1 is affected 44 | 'Domain user credentials are used and PSDscAllowDomainUser is not set' 45 | break 46 | } 47 | elseif ($_.InvocationInfo.PositionMessage -match 'PSDscAllowPlainTextPassword') 48 | { 49 | "It is not recommended to use plain text password. Use PSDscAllowPlainTextPassword = `$false" 50 | break 51 | } 52 | else 53 | { 54 | #can be ignored 55 | break 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /source/Public/Get-DscMofEnvironment.ps1: -------------------------------------------------------------------------------- 1 | function Get-DscMofEnvironment 2 | { 3 | param 4 | ( 5 | [Parameter(Mandatory = $true, ValueFromPipeline = $true)] 6 | [string] 7 | $Path 8 | ) 9 | 10 | process 11 | { 12 | if (-not (Test-Path -Path $Path)) 13 | { 14 | Write-Error -Message "The MOF file '$Path' cannot be found." 15 | return 16 | } 17 | 18 | $content = Get-Content -Path $Path 19 | 20 | $xRegistryDscEnvironment = $content | Select-String -Pattern '\[xRegistry\]DscEnvironment' -Context 0, 10 21 | if (-not $xRegistryDscEnvironment) 22 | { 23 | Write-Error -Message "No environment information found in MOF file '$Path'. The environment information must be added using the 'xRegistryx' named 'DscEnvironment'." 24 | return 25 | } 26 | 27 | $valueData = $xRegistryDscEnvironment.Context.PostContext | Select-String -Pattern 'ValueData' -Context 0, 1 28 | if (-not $valueData) 29 | { 30 | Write-Error -Message "Found the resource 'xRegistry' named 'DscEnvironment' in '$Path' but no ValueData in the expected range (10 lines after defining '[xRegistry]DscEnvironment'." 31 | return 32 | } 33 | 34 | $valueData.Context.PostContext[0].Trim().Replace('"', '') 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /source/Public/Get-DscMofVersion.ps1: -------------------------------------------------------------------------------- 1 | function Get-DscMofVersion 2 | { 3 | [CmdletBinding()] 4 | [OutputType([string])] 5 | param 6 | ( 7 | [Parameter(Mandatory = $true, ValueFromPipeline = $true)] 8 | [string] 9 | $Path 10 | ) 11 | 12 | process 13 | { 14 | if (-not (Test-Path -Path $Path)) 15 | { 16 | Write-Error -Message "The MOF file '$Path' cannot be found." 17 | return 18 | } 19 | 20 | $content = Get-Content -Path $Path 21 | 22 | $xRegistryDscVersion = $content | Select-String -Pattern '\[xRegistry\]DscVersion' -Context 0, 10 23 | 24 | if (-not $xRegistryDscVersion) 25 | { 26 | Write-Error -Message "No version information found in MOF file '$Path'. The version information must be added using the 'xRegistry' named 'DscVersion'." 27 | return 28 | } 29 | 30 | $valueData = $xRegistryDscVersion.Context.PostContext | Select-String -Pattern 'ValueData' -Context 0, 1 31 | if (-not $valueData) 32 | { 33 | Write-Error -Message "Found the resource 'xRegistry' named 'DscVersion' in '$Path' but no ValueData in the expected range (10 lines after defining '[xRegistry]DscVersion'." 34 | return 35 | } 36 | 37 | try 38 | { 39 | $value = $valueData.Context.PostContext[0].Trim().Replace('"', '') 40 | [String]$value 41 | } 42 | catch 43 | { 44 | Write-Error -Message "ValueData could not be converted into 'System.Version'. The value taken from the MOF file was '$value'" 45 | return 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /source/Public/Get-FilteredConfigurationData.ps1: -------------------------------------------------------------------------------- 1 | function Get-FilteredConfigurationData 2 | { 3 | [CmdletBinding()] 4 | [OutputType([hashtable])] 5 | param 6 | ( 7 | [Parameter()] 8 | [ScriptBlock] 9 | $Filter = {}, 10 | 11 | [Parameter()] 12 | [int] 13 | $CurrentJobNumber = 1, 14 | 15 | [Parameter()] 16 | [int] 17 | $TotalJobCount = 1, 18 | 19 | [Parameter()] 20 | [Object] 21 | $Datum = $(Get-Variable -Name Datum -ValueOnly -ErrorAction Stop) 22 | ) 23 | 24 | if ($null -eq $Filter) 25 | { 26 | $Filter = {} 27 | } 28 | 29 | try 30 | { 31 | $allDatumNodes = [System.Collections.Hashtable[]]@(Get-DatumNodesRecursive -AllDatumNodes $Datum.AllNodes -ErrorAction Stop) 32 | } 33 | catch 34 | { 35 | Write-Error -Message "Could not get datum nodes. Pretty likely there is a syntax error in one of the node's yaml definitions." -Exception $_.Exception 36 | } 37 | $totalNodeCount = $allDatumNodes.Count 38 | 39 | Write-Verbose -Message "Node count: $($allDatumNodes.Count)" 40 | 41 | if ($Filter.ToString() -ne {}.ToString()) 42 | { 43 | Write-Verbose -Message "Filter: $($Filter.ToString())" 44 | $allDatumNodes = [System.Collections.Hashtable[]]$allDatumNodes.Where($Filter) 45 | Write-Verbose -Message "Node count after applying filter: $($allDatumNodes.Count)" 46 | } 47 | 48 | if (-not $allDatumNodes.Count) 49 | { 50 | Write-Error -Message "No node data found. There are in total $totalNodeCount nodes defined, but no node was selected. You may want to verify the filter: '$Filter'." 51 | } 52 | 53 | $CurrentJobNumber-- 54 | if ($TotalJobCount -gt 1) 55 | { 56 | try 57 | { 58 | $allDatumNodes = Split-Array -List $allDatumNodes -ChunkCount $TotalJobCount -ErrorAction Stop 59 | } 60 | catch 61 | { 62 | Write-Error -Exception $_.Exception -Message "Error calling 'Split-Array': $($_.Exception.Message). Please make sure the 'TotalJobCount' is not greater than the number of nodes." -ErrorAction Stop 63 | } 64 | $allDatumNodes = @($allDatumNodes[$CurrentJobNumber].ToArray()) 65 | } 66 | 67 | return @{ 68 | AllNodes = $allDatumNodes 69 | Datum = $Datum 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /source/Public/Split-Array.ps1: -------------------------------------------------------------------------------- 1 | function Split-Array 2 | { 3 | param ( 4 | [Parameter(Mandatory = $true)] 5 | [System.Collections.IEnumerable] 6 | $List, 7 | 8 | [Parameter(Mandatory = $true, ParameterSetName = 'MaxChunkSize')] 9 | [Alias('ChunkSize')] 10 | [int] 11 | $MaxChunkSize, 12 | 13 | [Parameter(Mandatory = $true, ParameterSetName = 'ChunkCount')] 14 | [ValidateRange(2, [long]::MaxValue)] 15 | [int] 16 | $ChunkCount, 17 | 18 | [Parameter()] 19 | [switch] 20 | $AllowEmptyChunks 21 | ) 22 | 23 | if (-not $AllowEmptyChunks -and ($list.Count -lt $ChunkCount)) 24 | { 25 | Write-Error "List count ($($List.Count)) is smaller than ChunkCount ($ChunkCount)." 26 | return 27 | } 28 | 29 | if ($PSCmdlet.ParameterSetName -eq 'MaxChunkSize') 30 | { 31 | $ChunkCount = [Math]::Ceiling($List.Count / $MaxChunkSize) 32 | } 33 | $containers = foreach ($i in 1..$ChunkCount) 34 | { 35 | New-Object System.Collections.Generic.List[object] 36 | } 37 | 38 | $iContainer = 0 39 | foreach ($item in $List) 40 | { 41 | $containers[$iContainer].Add($item) 42 | $iContainer++ 43 | if ($iContainer -ge $ChunkCount) 44 | { 45 | $iContainer = 0 46 | } 47 | } 48 | 49 | $containers 50 | } 51 | -------------------------------------------------------------------------------- /source/Sampler.DscPipeline.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | 3 | # Script module or binary module file associated with this manifest. 4 | RootModule = 'Sampler.DscPipeline.psm1' 5 | 6 | # Version number of this module. 7 | ModuleVersion = '0.0.1' 8 | 9 | # Supported PSEditions 10 | # CompatiblePSEditions = @() 11 | 12 | # ID used to uniquely identify this module 13 | GUID = 'a1afa85a-8f1a-4735-956c-d917a4582ec7' 14 | 15 | # Author of this module 16 | Author = 'Gael Colas' 17 | 18 | # Company or vendor of this module 19 | CompanyName = 'SynEdgy Limited' 20 | 21 | # Copyright statement for this module 22 | Copyright = '(c) SynEdgy Limited. All rights reserved.' 23 | 24 | # Description of the functionality provided by this module 25 | Description = 'Samper tasks for a DSC Pipeline using a Datum Yaml hierarchy.' 26 | 27 | # Minimum version of the PowerShell engine required by this module 28 | PowerShellVersion = '5.1' 29 | 30 | # Name of the PowerShell host required by this module 31 | # PowerShellHostName = '' 32 | 33 | # Minimum version of the PowerShell host required by this module 34 | # PowerShellHostVersion = '' 35 | 36 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 37 | # DotNetFrameworkVersion = '' 38 | 39 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 40 | # ClrVersion = '' 41 | 42 | # Processor architecture (None, X86, Amd64) required by this module 43 | # ProcessorArchitecture = '' 44 | 45 | # Modules that must be imported into the global environment prior to importing this module 46 | RequiredModules = @( 47 | 'Plaster' 48 | 'Sampler' 49 | 'DscBuildHelpers' 50 | ) 51 | 52 | # Assemblies that must be loaded prior to importing this module 53 | # RequiredAssemblies = @() 54 | 55 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 56 | # ScriptsToProcess = @() 57 | 58 | # Type files (.ps1xml) to be loaded when importing this module 59 | # TypesToProcess = @() 60 | 61 | # Format files (.ps1xml) to be loaded when importing this module 62 | # FormatsToProcess = @() 63 | 64 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 65 | # NestedModules = @() 66 | 67 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. 68 | FunctionsToExport = '*' 69 | 70 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 71 | CmdletsToExport = '*' 72 | 73 | # Variables to export from this module 74 | VariablesToExport = '*' 75 | 76 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. 77 | AliasesToExport = '*' 78 | 79 | # DSC resources to export from this module 80 | # DscResourcesToExport = @() 81 | 82 | # List of all modules packaged with this module 83 | # ModuleList = @() 84 | 85 | # List of all files packaged with this module 86 | # FileList = @() 87 | 88 | # 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. 89 | PrivateData = @{ 90 | 91 | PSData = @{ 92 | 93 | # Prerelease string of this module 94 | Prerelease = '' 95 | 96 | # Tags applied to this module. These help with module discovery in online galleries. 97 | Tags = @('DSC', 'Sampler', 'InvokeBuild', 'Tasks') 98 | 99 | # A URL to the license for this module. 100 | LicenseUri = 'https://github.com/SynEdgy/Sampler.DscPipeline/blob/main/LICENSE' 101 | 102 | # A URL to the main website for this project. 103 | ProjectUri = 'https://github.com/SynEdgy/Sampler.DscPipeline' 104 | 105 | # A URL to an icon representing this module. 106 | # IconUri = '' 107 | 108 | # ReleaseNotes of this module 109 | ReleaseNotes = '' 110 | 111 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save 112 | # RequireLicenseAcceptance = $false 113 | 114 | # External dependent modules of this module 115 | # ExternalModuleDependencies = @() 116 | 117 | } # End of PSData hashtable 118 | 119 | } # End of PrivateData hashtable 120 | 121 | # HelpInfo URI of this module 122 | # HelpInfoURI = '' 123 | 124 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 125 | # DefaultCommandPrefix = '' 126 | 127 | } 128 | -------------------------------------------------------------------------------- /source/Sampler.DscPipeline.psm1: -------------------------------------------------------------------------------- 1 | <# 2 | ##Import Classes 3 | if (Test-Path "$PSScriptRoot\Classes\classes.psd1") { 4 | $ClassLoadOrder = Import-PowerShellDataFile -Path "$PSScriptRoot\Classes\classes.psd1" -ErrorAction SilentlyContinue 5 | } 6 | else { 7 | $ClassLoadOrder = @{ order=@() } 8 | $ClassLoadOrder.order = (get-childItem "$PSScriptRoot\Classes\*" -Filter *.ps1 -ErrorAction SilentlyContinue).BaseName 9 | } 10 | 11 | foreach ($class in $ClassLoadOrder.order) { 12 | $path = '{0}\classes\{1}.ps1' -f $PSScriptRoot, $class 13 | if (Test-Path $path) { 14 | . $path 15 | } 16 | } 17 | 18 | #Get public and private function definition files. 19 | $Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue ) 20 | $Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue ) 21 | 22 | #Dot source the files 23 | foreach($import in @($Public + $Private)) 24 | { 25 | try 26 | { 27 | Write-Verbose "Importing $($Import.FullName)" 28 | . $import.fullname 29 | } 30 | catch 31 | { 32 | Write-Error -Message "Failed to import function $($import.fullname): $_" 33 | } 34 | } 35 | 36 | Export-ModuleMember -Function $Public.Basename 37 | #> 38 | 39 | -------------------------------------------------------------------------------- /source/Scripts/CompileRootConfiguration.ps1: -------------------------------------------------------------------------------- 1 | Import-Module -Name DscBuildHelpers 2 | $Error.Clear() 3 | 4 | if (-not $ModuleVersion) 5 | { 6 | $ModuleVersion = '0.0.0' 7 | } 8 | 9 | $environment = $node.Environment 10 | if (-not $environment) 11 | { 12 | $environment = 'NA' 13 | } 14 | 15 | #Compiling MOF from RSOP cache 16 | $rsopCache = Get-DatumRsopCache 17 | 18 | <# 19 | This information is taken from build.yaml 20 | 21 | Sampler.DscPipeline: 22 | DscCompositeResourceModules: 23 | - Name: CommonTasks 24 | Version: 0.3.259 25 | - PSDesiredStateConfiguration 26 | #> 27 | 28 | if (-not $BuildInfo.'Sampler.DscPipeline') 29 | { 30 | Write-Error -Message "There are no modules to import defined in the 'build.yml'. Expected the element 'Sampler.DscPipeline'" 31 | } 32 | if (-not $BuildInfo.'Sampler.DscPipeline'.DscCompositeResourceModules) 33 | { 34 | Write-Error -Message "There are no modules to import defined in the 'build.yml'. Expected the element 'Sampler.DscPipeline'.DscCompositeResourceModules" 35 | } 36 | if ($BuildInfo.'Sampler.DscPipeline'.DscCompositeResourceModules.Count -lt 1) 37 | { 38 | Write-Error -Message "There are no modules to import defined in the 'build.yml'. Expected at least one module defined under 'Sampler.DscPipeline'.DscCompositeResourceModules" 39 | } 40 | 41 | Write-Host -Object "RootConfiguration will import these composite resource modules as defined in 'build.yaml':" 42 | foreach ($module in $BuildInfo.'Sampler.DscPipeline'.DscCompositeResourceModules) 43 | { 44 | if ($module -is [hashtable]) 45 | { 46 | Write-Host -Object "`t- $($module.Name) ($($module.Version))" 47 | } 48 | else 49 | { 50 | Write-Host -Object "`t- $module" 51 | } 52 | } 53 | 54 | Write-Host -Object '' 55 | Write-Host -Object 'Preloading available resources' 56 | 57 | # An emptu path in the PSModulePath causes an error when loading DSC resources. Only then the PSModulePath is modified to remove the empty path. 58 | # If you want to remove 'Program Files' or 'Documents' from the PSModulePath, please add the Sampler task 'Set_PsModulePath' to the task sequence. 59 | if ($env:PSModulePath -like '*;;*') 60 | { 61 | $previousPSModulePath = $env:PSModulePath 62 | $env:PSModulePath = $env:PSModulePath -replace "$([System.IO.Path]::PathSeparator)$([System.IO.Path]::PathSeparator)", [System.IO.Path]::PathSeparator 63 | } 64 | 65 | try 66 | { 67 | $availableResources = Get-DscResource 68 | } 69 | catch 70 | { 71 | if ($_.Exception -is [System.Management.Automation.ParameterBindingException] -and $_.Exception.ParameterName -eq 'Path') 72 | { 73 | Write-Error -Message "There was error while loading DSC resources because the 'PSModulePath' contained a path that does not exist. The error was: $($_.Exception.Message)" -Exception $_.Exception 74 | } 75 | else 76 | { 77 | Write-Error -Message "There was error while loading DSC resources. The error was: $($_.Exception.Message)" -Exception $_.Exception 78 | } 79 | } 80 | 81 | if ($previousPSModulePath) 82 | { 83 | $env:PSModulePath = $previousPSModulePath 84 | } 85 | 86 | Write-Host -Object '' 87 | 88 | $configData = @{} 89 | $configData.Datum = $ConfigurationData.Datum 90 | 91 | if (-not $rsopCache) 92 | { 93 | Write-Error -Message "No RSOP cache found. The task 'CompileDatumRsop' must be run before this task." 94 | } 95 | 96 | foreach ($node in $rsopCache.GetEnumerator()) 97 | { 98 | $importStatements = foreach ($configurationItem in $node.Value.Configurations) 99 | { 100 | $resource = $availableResources.Where({ $_.Name -eq $configurationItem }) 101 | if ($null -eq $resource) 102 | { 103 | Write-Debug -Message "No DSC resource found for configuration '$configurationItem'" 104 | continue 105 | } 106 | 107 | "Import-DscResource -ModuleName $($resource.ModuleName) -ModuleVersion $($resource.Version) -Name $($resource.Name)`n" 108 | } 109 | 110 | $rootConfiguration = Get-Content -Path $PSScriptRoot\RootConfiguration.ps1 -Raw 111 | $rootConfiguration = $rootConfiguration -replace '#', ($importStatements | Select-Object -Unique) 112 | 113 | Invoke-Expression -Command $rootConfiguration 114 | 115 | $configData.AllNodes = @([hashtable]$node.Value) 116 | try 117 | { 118 | $path = Join-Path -Path MOF -ChildPath $node.Value.Environment 119 | RootConfiguration -ConfigurationData $configData -OutputPath (Join-Path -Path $BuildOutput -ChildPath $path) 120 | } 121 | catch 122 | { 123 | Write-Host -Object "Error occured during compilation of node '$($node.NodeName)' : $($_.Exception.Message)" -ForegroundColor Red 124 | $relevantErrors = $Error | Where-Object Exception -IsNot [System.Management.Automation.ItemNotFoundException] 125 | foreach ($relevantError in ($relevantErrors | Select-Object -First 3)) 126 | { 127 | Write-Error -ErrorRecord $relevantError 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /source/Scripts/RootConfiguration.ps1: -------------------------------------------------------------------------------- 1 | configuration RootConfiguration 2 | { 3 | # 4 | 5 | #Compiling MOF from RSOP cache 6 | $rsopCache = Get-DatumRsopCache 7 | 8 | $module = Get-Module -Name PSDesiredStateConfiguration 9 | & $module { 10 | param( 11 | [string]$BuildVersion, 12 | [string]$Environment 13 | ) 14 | $Script:PSTopConfigurationName = "MOF_$($Environment)_$($BuildVersion)" 15 | } $buildVersion, $environment 16 | 17 | node $ConfigurationData.AllNodes.NodeName { 18 | Write-Host -Object "`r`n$('-'*75)`r`n$($Node.Name) : $($Node.NodeName) : $(&$module { $Script:PSTopConfigurationName })" -ForegroundColor Yellow 19 | 20 | $configurationNames = $rsopCache."$($Node.Name)".Configurations 21 | $global:node = $node #this makes the node variable being propagated into the configurations 22 | 23 | foreach ($configurationName in $configurationNames) 24 | { 25 | Write-Debug -Message "`tLooking up params for $configurationName" 26 | $dscError = [System.Collections.ArrayList]::new() 27 | 28 | $clonedProperties = $rsopCache."$($Node.Name)".$configurationName 29 | 30 | (Get-DscSplattedResource -ResourceName $configurationName -ExecutionName $configurationName -Properties $clonedProperties -NoInvoke).Invoke($clonedProperties) 31 | 32 | if ($Error[0] -and $lastError -ne $Error[0]) 33 | { 34 | $lastIndex = [Math]::Max(($Error.LastIndexOf($lastError) - 1), -1) 35 | if ($lastIndex -gt 0) 36 | { 37 | $Error[0..$lastIndex].Foreach{ 38 | if ($message = Get-DscErrorMessage -Exception $_.Exception) 39 | { 40 | $null = $dscError.Add($message) 41 | } 42 | } 43 | } 44 | else 45 | { 46 | if ($message = Get-DscErrorMessage -Exception $Error[0].Exception) 47 | { 48 | $null = $dscError.Add($message) 49 | } 50 | } 51 | $lastError = $Error[0] 52 | } 53 | 54 | if ($dscError.Count -gt 0) 55 | { 56 | $warningMessage = " $($Node.Name) : $($Node.Role) ::> $configurationName " 57 | $n = [System.Math]::Max(1, 100 - $warningMessage.Length) 58 | Write-Host -Object "$warningMessage$('.' * $n)FAILED" -ForegroundColor Yellow 59 | $dscError.Foreach{ 60 | Write-Host -Object "`t$message" -ForegroundColor Yellow 61 | } 62 | } 63 | else 64 | { 65 | $okMessage = " $($Node.Name) : $($Node.Role) ::> $configurationName " 66 | $n = [System.Math]::Max(1, 100 - $okMessage.Length) 67 | Write-Host -Object "$okMessage$('.' * $n)OK" -ForegroundColor Green 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /source/Scripts/RootMetaMof.ps1: -------------------------------------------------------------------------------- 1 | Import-Module DscBuildHelpers 2 | 3 | [DscLocalConfigurationManager()] 4 | Configuration RootMetaMOF { 5 | 6 | #Compiling Meta MOF from RSOP cache 7 | $rsopCache = Get-DatumRsopCache 8 | 9 | Node $ConfigurationData.AllNodes.NodeName { 10 | 11 | $lcmConfigKeyName = $datum.__Definition.DscLocalConfigurationManagerKeyName 12 | $clonedProperties = $rsopCache."$($Node.Name)".$lcmConfigKeyName 13 | 14 | if (-not $clonedProperties) 15 | { 16 | Write-Error "LCM configuration key not found for node $($Node.Name). You can define one in the 'datum.yml' using the key 'DscLocalConfigurationManagerKeyName'." -ErrorAction Stop 17 | } 18 | 19 | $lcmConfig = $clonedProperties.Settings 20 | 21 | #If the Nodename is a GUID, use Config ID instead Named config, as per SMB Pull requirements 22 | if ($Node.Nodename -as [Guid]) 23 | { 24 | $lcmConfig['ConfigurationID'] = $Node.Nodename 25 | } 26 | (Get-DscSplattedResource -ResourceName Settings -ExecutionName '' -Properties $lcmConfig -NoInvoke).Invoke($lcmConfig) 27 | 28 | if ($configurationRepositoryShare = $clonedProperties.ConfigurationRepositoryShare) 29 | { 30 | (Get-DscSplattedResource -ResourceName ConfigurationRepositoryShare -ExecutionName ConfigurationRepositoryShare -Properties $configurationRepositoryShare -NoInvoke).Invoke($configurationRepositoryShare) 31 | } 32 | 33 | if ($resourceRepositoryShare = $clonedProperties.ResourceRepositoryShare) 34 | { 35 | (Get-DscSplattedResource -ResourceName ResourceRepositoryShare -ExecutionName ResourceRepositoryShare -Properties $resourceRepositoryShare -NoInvoke).Invoke($resourceRepositoryShare) 36 | } 37 | 38 | if ($configurationRepositoryWeb = $clonedProperties.ConfigurationRepositoryWeb) 39 | { 40 | foreach ($configRepoName in $configurationRepositoryWeb.Keys) 41 | { 42 | (Get-DscSplattedResource -ResourceName ConfigurationRepositoryWeb -ExecutionName $configRepoName -Properties $configurationRepositoryWeb[$configRepoName] -NoInvoke).Invoke($configurationRepositoryWeb[$configRepoName]) 43 | } 44 | } 45 | 46 | if ($resourceRepositoryWeb = $clonedProperties.ResourceRepositoryWeb) 47 | { 48 | foreach ($resourceRepoName in $resourceRepositoryWeb.Keys) 49 | { 50 | (Get-DscSplattedResource -ResourceName ResourceRepositoryWeb -ExecutionName $resourceRepoName -Properties $resourceRepositoryWeb[$resourceRepoName] -NoInvoke).Invoke($resourceRepositoryWeb[$resourceRepoName]) 51 | } 52 | } 53 | 54 | if ($reportServerWeb = $clonedProperties.ReportServerWeb) 55 | { 56 | (Get-DscSplattedResource -ResourceName ReportServerWeb -ExecutionName ReportServerWeb -Properties $reportServerWeb -NoInvoke).Invoke($reportServerWeb) 57 | } 58 | 59 | if ($partialConfiguration = $clonedProperties.PartialConfiguration) 60 | { 61 | foreach ($partialConfigurationName in $partialConfiguration.Keys) 62 | { 63 | (Get-DscSplattedResource -ResourceName PartialConfiguration -ExecutionName $partialConfigurationName -Properties $partialConfiguration[$partialConfigurationName] -NoInvoke).Invoke($partialConfiguration[$partialConfigurationName]) 64 | } 65 | } 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /source/Tasks/CompileDatumRsop.build.ps1: -------------------------------------------------------------------------------- 1 | param 2 | ( 3 | # Project path 4 | [Parameter()] 5 | [System.String] 6 | $ProjectPath = (property ProjectPath $BuildRoot), 7 | 8 | [Parameter()] 9 | # Base directory of all output (default to 'output') 10 | [System.String] 11 | $OutputDirectory = (property OutputDirectory (Join-Path $BuildRoot 'output')), 12 | 13 | [Parameter()] 14 | [string] 15 | $DatumConfigDataDirectory = (property DatumConfigDataDirectory 'source'), 16 | 17 | [Parameter()] 18 | [int] 19 | $CurrentJobNumber = (property CurrentJobNumber 1), 20 | 21 | [Parameter()] 22 | [int] 23 | $TotalJobCount = (property TotalJobCount 1), 24 | 25 | # Build Configuration object 26 | [Parameter()] 27 | [System.Collections.Hashtable] 28 | $BuildInfo = (property BuildInfo @{ }), 29 | 30 | [Parameter()] 31 | [string] 32 | $RsopFolder = (property RsopFolder 'RSOP'), 33 | 34 | [Parameter()] 35 | [string] 36 | $RsopWithSourceFolder = (property RsopFolderWithSource 'RsopWithSource'), 37 | 38 | [Parameter()] 39 | [string] 40 | $ModuleVersion = (property ModuleVersion ''), 41 | 42 | [Parameter()] 43 | [switch] 44 | $UseEnvironment = ([bool]::Parse((property UseEnvironment $false))) 45 | ) 46 | 47 | task CompileDatumRsop { 48 | 49 | Clear-DatumRsopCache #otherwise this task will not generate new RSOP data 50 | 51 | # Get the vales for task variables, see https://github.com/gaelcolas/Sampler#task-variables. 52 | . Set-SamplerTaskVariable -AsNewBuild 53 | 54 | $DatumConfigDataDirectory = Get-SamplerAbsolutePath -Path $DatumConfigDataDirectory -RelativeTo $ProjectPath 55 | $RsopFolder = Get-SamplerAbsolutePath -Path $RsopFolder -RelativeTo $OutputDirectory 56 | $RsopWithSourceFolder = Get-SamplerAbsolutePath -Path $RsopWithSourceFolder -RelativeTo $OutputDirectory 57 | 58 | if (-not (Test-Path -Path $RsopFolder)) 59 | { 60 | $null = New-Item -ItemType Directory -Path $RsopFolder -Force 61 | } 62 | if (-not (Test-Path -Path $RsopWithSourceFolder)) 63 | { 64 | $null = New-Item -ItemType Directory -Path $RsopWithSourceFolder -Force 65 | } 66 | 67 | $rsopOutputPathVersion = Join-Path -Path $RsopFolder -ChildPath $ModuleVersion 68 | if (-not (Test-Path -Path $rsopOutputPathVersion)) 69 | { 70 | $null = New-Item -ItemType Directory -Path $rsopOutputPathVersion -Force 71 | } 72 | $rsopWithSourceOutputPathVersion = Join-Path -Path $RsopWithSourceFolder -ChildPath $ModuleVersion 73 | if (-not (Test-Path -Path $rsopWithSourceOutputPathVersion)) 74 | { 75 | $null = New-Item -ItemType Directory -Path $rsopWithSourceOutputPathVersion -Force 76 | } 77 | 78 | if ($configurationData.AllNodes) 79 | { 80 | Write-Build Green "Generating RSOP output for $($configurationData.AllNodes.Count) nodes." 81 | $configurationData.AllNodes.Where({ $_['Name'] -ne '*' }) | ForEach-Object -Process { 82 | Write-Build Green "`tBuilding RSOP for $($_['Name'])..." 83 | $outPath = $rsopOutputPathVersion 84 | 85 | $nodeRsop = Get-DatumRsop -Datum $datum -AllNodes ([ordered]@{ } + $_) -RemoveSource 86 | $nodeRsopWithSource = Get-DatumRsop -Datum $datum -AllNodes ([ordered]@{ } + $_) -IncludeSource 87 | 88 | if ($UseEnvironment.IsPresent) 89 | { 90 | $finalRsopOutputPathVersion = Join-Path -Path $rsopOutputPathVersion -ChildPath $nodeRsop.Environment 91 | $finalRsopWithSourceOutputPathVersion = Join-Path -Path $rsopWithSourceOutputPathVersion -ChildPath $nodeRsop.Environment 92 | $null = New-Item -ItemType Directory -Path $finalRsopOutputPathVersion, $finalRsopWithSourceOutputPathVersion -Force 93 | } 94 | else 95 | { 96 | $finalRsopOutputPathVersion = $rsopOutputPathVersion 97 | $finalRsopWithSourceOutputPathVersion = $rsopWithSourceOutputPathVersion 98 | } 99 | 100 | $nodeRsop | ConvertTo-Json -Depth 40 | ConvertFrom-Json | ConvertTo-Yaml -OutFile (Join-Path -Path $finalRsopOutputPathVersion -ChildPath "$($nodeRsop.NodeName).yml") -Force 101 | $nodeRsopWithSource | ConvertTo-Json -Depth 40 | ConvertFrom-Json | ConvertTo-Yaml -OutFile (Join-Path -Path $finalRsopWithSourceOutputPathVersion -ChildPath "$($nodeRsop.NodeName).yml") -Force 102 | } 103 | } 104 | else 105 | { 106 | Write-Build Green 'No data for generating RSOP output.' 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /source/Tasks/CompileRootConfiguration.build.ps1: -------------------------------------------------------------------------------- 1 | param 2 | ( 3 | # Project path 4 | [Parameter()] 5 | [System.String] 6 | $ProjectPath = (property ProjectPath $BuildRoot), 7 | 8 | # Source path 9 | [Parameter()] 10 | [System.String] 11 | $SourcePath = (property SourcePath 'source'), 12 | 13 | [Parameter()] 14 | # Base directory of all output (default to 'output') 15 | [System.String] 16 | $OutputDirectory = (property OutputDirectory (Join-Path -Path $BuildRoot -ChildPath output)), 17 | 18 | [Parameter()] 19 | [string] 20 | $RequiredModulesDirectory = (property RequiredModulesDirectory 'RequiredModules'), 21 | 22 | [Parameter()] 23 | [string] 24 | $DatumConfigDataDirectory = (property DatumConfigDataDirectory 'source'), 25 | 26 | [Parameter()] 27 | [string] 28 | $MofOutputFolder = (property MofOutputFolder 'MOF'), 29 | 30 | [Parameter()] 31 | [int] 32 | $CurrentJobNumber = (property CurrentJobNumber 1), 33 | 34 | [Parameter()] 35 | [int] 36 | $TotalJobCount = (property TotalJobCount 1), 37 | 38 | # Build Configuration object 39 | [Parameter()] 40 | [System.Collections.Hashtable] 41 | $BuildInfo = (property BuildInfo @{ }) 42 | ) 43 | 44 | task CompileRootConfiguration { 45 | . Set-SamplerTaskVariable -AsNewBuild 46 | 47 | $RequiredModulesDirectory = Get-SamplerAbsolutePath -Path $RequiredModulesDirectory -RelativeTo $OutputDirectory 48 | 49 | Write-Build DarkGray 'Reading DSC Resource metadata for supporting CIM based DSC parameters...' 50 | Initialize-DscResourceMetaInfo -ModulePath $RequiredModulesDirectory 51 | Write-Build DarkGray 'Done' 52 | 53 | $MofOutputFolder = Get-SamplerAbsolutePath -Path $MofOutputFolder -RelativeTo $OutputDirectory 54 | 55 | if (-not (Test-Path -Path $MofOutputFolder)) 56 | { 57 | $null = New-Item -ItemType Directory -Path $MofOutputFolder -Force 58 | } 59 | 60 | Start-Transcript -Path "$BuildOutput\Logs\CompileRootConfiguration.log" 61 | try 62 | { 63 | Write-Build Green '' 64 | if ((Test-Path -Path (Join-Path -Path $SourcePath -ChildPath RootConfiguration.ps1)) -and 65 | (Test-Path -Path (Join-Path -Path $SourcePath -ChildPath CompileRootConfiguration.ps1))) 66 | { 67 | Write-Build Green "Found 'RootConfiguration.ps1' and 'CompileRootConfiguration.ps1' in '$SourcePath' and using these files" 68 | $rootConfigurationPath = Join-Path -Path $SourcePath -ChildPath CompileRootConfiguration.ps1 69 | } 70 | else 71 | { 72 | Write-Build Green "Did not find 'RootConfiguration.ps1' and 'CompileRootConfiguration.ps1' in '$SourcePath', using the ones in 'Sampler.DscPipeline'" 73 | $rootConfigurationPath = Split-Path -Path $PSScriptRoot -Parent 74 | $rootConfigurationPath = Join-Path -Path $rootConfigurationPath -ChildPath Scripts 75 | $rootConfigurationPath = Join-Path -Path $rootConfigurationPath -ChildPath CompileRootConfiguration.ps1 76 | } 77 | 78 | $mofs = . $rootConfigurationPath 79 | if ($ConfigurationData.AllNodes.Count -ne $mofs.Count) 80 | { 81 | Write-Warning -Message "Compiled MOF file count <> node count. Node count: $($ConfigurationData.AllNodes.Count), MOF file count: $($($mofs.Count))." 82 | } 83 | 84 | Write-Build Green "Successfully compiled $($mofs.Count) MOF files" 85 | } 86 | catch 87 | { 88 | Write-Build Red 'Error(s) occured during the compilation. Details will be shown below' 89 | 90 | $relevantErrors = $Error | Where-Object -FilterScript { 91 | $_.Exception -isnot [System.Management.Automation.ItemNotFoundException] 92 | } 93 | 94 | foreach ($relevantError in ($relevantErrors | Select-Object -First 3)) 95 | { 96 | Write-Error -ErrorRecord $relevantError 97 | } 98 | } 99 | finally 100 | { 101 | Stop-Transcript 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /source/Tasks/CompileRootMetaMof.build.ps1: -------------------------------------------------------------------------------- 1 | param 2 | ( 3 | # Project path 4 | [Parameter()] 5 | [System.String] 6 | $ProjectPath = (property ProjectPath $BuildRoot), 7 | 8 | # Source path 9 | [Parameter()] 10 | [System.String] 11 | $SourcePath = (property SourcePath 'source'), 12 | 13 | [Parameter()] 14 | # Base directory of all output (default to 'output') 15 | [System.String] 16 | $OutputDirectory = (property OutputDirectory (Join-Path -Path $BuildRoot -ChildPath output)), 17 | 18 | [Parameter()] 19 | [string] 20 | $DatumConfigDataDirectory = (property DatumConfigDataDirectory 'source'), 21 | 22 | [Parameter()] 23 | [string] 24 | $MetaMofOutputFolder = (property MetaMofOutputFolder 'MetaMOF'), 25 | 26 | # Build Configuration object 27 | [Parameter()] 28 | [System.Collections.Hashtable] 29 | $BuildInfo = (property BuildInfo @{ }) 30 | ) 31 | 32 | task CompileRootMetaMof { 33 | . Set-SamplerTaskVariable -AsNewBuild 34 | 35 | $MetaMofOutputFolder = Get-SamplerAbsolutePath -Path $MetaMofOutputFolder -RelativeTo $OutputDirectory 36 | 37 | #Compiling Meta MOF from RSOP cache 38 | $rsopCache = Get-DatumRsopCache 39 | 40 | $cd = @{} 41 | foreach ($node in $rsopCache.GetEnumerator()) 42 | { 43 | $cd.AllNodes += @([hashtable]$node.Value) 44 | } 45 | 46 | $originalPSModulePath = $env:PSModulePath 47 | try 48 | { 49 | $env:PSModulePath = ($env:PSModulePath -split [System.IO.Path]::PathSeparator).Where({ 50 | $_ -notmatch ([regex]::Escape('powershell\7\Modules')) -and 51 | $_ -notmatch ([regex]::Escape('Program Files\WindowsPowerShell\Modules')) -and 52 | $_ -notmatch ([regex]::Escape('Documents\PowerShell\Modules')) 53 | }) -join [System.IO.Path]::PathSeparator 54 | 55 | if (-not (Test-Path -Path $MetaMofOutputFolder)) 56 | { 57 | $null = New-Item -ItemType Directory -Path $MetaMofOutputFolder 58 | } 59 | 60 | if ($cd.AllNodes) 61 | { 62 | Write-Build Green '' 63 | if (Test-Path -Path (Join-Path -Path $SourcePath -ChildPath RootMetaMof.ps1)) 64 | { 65 | Write-Build Green "Found and using 'RootMetaMof.ps1' in '$SourcePath'" 66 | $rootMetaMofPath = Join-Path -Path $SourcePath -ChildPath RootMetaMof.ps1 67 | } 68 | else 69 | { 70 | Write-Build Green "Did not find 'RootMetaMof.ps1' in '$SourcePath', using 'RootMetaMof.ps1' the one in module 'Sampler.DscPipeline'" 71 | $rootMetaMofPath = Split-Path -Path $PSScriptRoot -Parent 72 | $rootMetaMofPath = Join-Path -Path $rootMetaMofPath -ChildPath Scripts 73 | $rootMetaMofPath = Join-Path -Path $rootMetaMofPath -ChildPath RootMetaMof.ps1 74 | } 75 | . $rootMetaMofPath 76 | 77 | $metaMofs = RootMetaMOF -ConfigurationData $cd -OutputPath $MetaMofOutputFolder 78 | Write-Build Green "Successfully compiled $($metaMofs.Count) Meta MOF files." 79 | if ($cd.AllNodes.Count -ne $metaMofs.Count) 80 | { 81 | Write-Warning -Message 'Compiled Meta MOF file count <> node count' 82 | } 83 | 84 | Write-Build Green "Successfully compiled $($metaMofs.Count) Meta MOF files." 85 | } 86 | else 87 | { 88 | Write-Build Green 'No data to compile Meta MOF files' 89 | } 90 | } 91 | finally 92 | { 93 | $env:PSModulePath = $originalPSModulePath 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /source/Tasks/CompressArtifactCollections.build.ps1: -------------------------------------------------------------------------------- 1 | param 2 | ( 3 | # Project path 4 | [Parameter()] 5 | [System.String] 6 | $ProjectPath = (property ProjectPath $BuildRoot), 7 | 8 | [Parameter()] 9 | # Base directory of all output (default to 'output') 10 | [System.String] 11 | $OutputDirectory = (property OutputDirectory (Join-Path $BuildRoot 'output')), 12 | 13 | [Parameter()] 14 | [string] 15 | $MofOutputFolder = (property MofOutputFolder 'MOF'), 16 | 17 | [Parameter()] 18 | [string] 19 | $RsopFolder = (property RsopFolder 'RSOP'), 20 | 21 | [Parameter()] 22 | [string] 23 | $MetaMofOutputFolder = (property MetaMofOutputFolder 'MetaMOF'), 24 | 25 | [Parameter()] 26 | [string] 27 | $CompressedModulesFolder = (property CompressedModulesFolder 'CompressedModules'), 28 | 29 | [Parameter()] 30 | [string] 31 | $CompressedArtifactsFolder = (property CompressedArtifactsFolder 'CompressedArtifacts'), 32 | 33 | [Parameter()] 34 | [string] 35 | $ModuleVersion = (property ModuleVersion ''), 36 | 37 | # Build Configuration object 38 | [Parameter()] 39 | [System.Collections.Hashtable] 40 | $BuildInfo = (property BuildInfo @{ }) 41 | ) 42 | 43 | Task Compress_Artifact_Collections { 44 | . Set-SamplerTaskVariable -AsNewBuild 45 | 46 | $RsopFolder = Get-SamplerAbsolutePath -Path $RsopFolder -RelativeTo $OutputDirectory 47 | $MofOutputFolder = Get-SamplerAbsolutePath -Path $MofOutputFolder -RelativeTo $OutputDirectory 48 | $MetaMofOutputFolder = Get-SamplerAbsolutePath -Path $MetaMofOutputFolder -RelativeTo $OutputDirectory 49 | $CompressedArtifactsFolder = Get-SamplerAbsolutePath -Path $CompressedArtifactsFolder -RelativeTo $OutputDirectory 50 | $CompressedModulesFolder = Get-SamplerAbsolutePath -Path $CompressedModulesFolder -RelativeTo $OutputDirectory 51 | 52 | "`tRsopFolder = $RsopFolder" 53 | "`tMofOutputFolder = $MofOutputFolder" 54 | "`tMetaMofOutputFolder = $MetaMofOutputFolder" 55 | "`tCompressedArtifactsFolder = $CompressedArtifactsFolder" 56 | "`tCompressedModulesFolder = $CompressedModulesFolder" 57 | 58 | if (-not (Test-Path -Path $CompressedArtifactsFolder)) 59 | { 60 | $null = New-Item -ItemType Directory $CompressedArtifactsFolder 61 | } 62 | 63 | Write-Build White "Starting deployment with files from '$OutputDirectory'" 64 | 65 | $MOFZip = Join-Path -Path $CompressedArtifactsFolder -ChildPath 'MOF.zip' 66 | $MetaMOFZip = Join-Path -Path $CompressedArtifactsFolder -ChildPath 'MetaMOF.zip' 67 | $RSOPZip = Join-Path -Path $CompressedArtifactsFolder -ChildPath 'RSOP.zip' 68 | $CompressedModulesZip = Join-Path -Path $CompressedArtifactsFolder -ChildPath 'CompressedModules.zip' 69 | 70 | Compress-Archive -Path $MofOutputFolder -DestinationPath $MOFZip -Force 71 | Compress-Archive -Path $MetaMofOutputFolder -DestinationPath $MetaMOFZip -Force 72 | Compress-Archive -Path $RsopFolder -DestinationPath $RSOPZip -Force 73 | if ($SkipCompressedModulesBuild -eq $false) 74 | { 75 | Compress-Archive -Path $CompressedModulesFolder -DestinationPath $CompressedModulesZip -Force 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /source/Tasks/CompressModulesWithChecksum.build.ps1: -------------------------------------------------------------------------------- 1 | param 2 | ( 3 | # Project path 4 | [Parameter()] 5 | [System.String] 6 | $ProjectPath = (property ProjectPath $BuildRoot), 7 | 8 | [Parameter()] 9 | # Base directory of all output (default to 'output') 10 | [System.String] 11 | $OutputDirectory = (property OutputDirectory (Join-Path $BuildRoot 'output')), 12 | 13 | [Parameter()] 14 | [string] 15 | $RequiredModulesDirectory = (property RequiredModulesDirectory 'RequiredModules'), 16 | 17 | [Parameter()] 18 | [string] 19 | $CompressedModulesFolder = (property CompressedModulesFolder 'CompressedModules'), 20 | 21 | [Parameter()] 22 | [int] 23 | $CurrentJobNumber = (property CurrentJobNumber 1), 24 | 25 | [Parameter()] 26 | [int] 27 | $TotalJobCount = (property TotalJobCount 1), 28 | 29 | # Build Configuration object 30 | [Parameter()] 31 | [System.Collections.Hashtable] 32 | $BuildInfo = (property BuildInfo @{ }) 33 | ) 34 | 35 | task CompressModulesWithChecksum { 36 | . Set-SamplerTaskVariable -AsNewBuild 37 | 38 | $CompressedModulesFolder = Get-SamplerAbsolutePath -Path $CompressedModulesFolder -RelativeTo $OutputDirectory 39 | $RequiredModulesDirectory = Get-SamplerAbsolutePath -Path $RequiredModulesDirectory -RelativeTo $OutputDirectory 40 | 41 | if ($SkipCompressedModulesBuild) 42 | { 43 | Write-Host 'Skipping preparation of Compressed Modules as $SkipCompressedModulesBuild is set' 44 | return 45 | } 46 | 47 | if (-not (Test-Path -Path $CompressedModulesFolder)) 48 | { 49 | $null = New-Item -Path $CompressedModulesFolder -ItemType Directory -Force 50 | } 51 | 52 | if ($SkipCompressedModulesBuild) 53 | { 54 | Write-Build Yellow 'Skipping preparation of Compressed Modules as $SkipCompressedModulesBuild is set' 55 | return 56 | } 57 | 58 | if ($configurationData.AllNodes -and $CurrentJobNumber -eq 1) 59 | { 60 | $previousProgressPreference = $ProgressPreference 61 | $ProgressPreference = 'SilentlyContinue' 62 | 63 | #Only zip up the Modules that have Exported DSC Resources 64 | $allModules = Get-ModuleFromFolder -ModuleFolder $RequiredModulesDirectory 65 | $modulesWithDscResources = Get-DscResourceFromModuleInFolder -ModuleFolder $RequiredModulesDirectory -Modules $allModules | 66 | Select-Object -ExpandProperty ModuleName -Unique 67 | $modulesWithDscResources = $allModules | Where-Object Name -In $modulesWithDscResources 68 | #TODO: be more selective and maybe check based on the MOFs (but that's a lot of MOF to parse) 69 | 70 | # As outlined here: https://docs.microsoft.com/en-us/dotnet/api/system.io.compression.zipfile?view=net-6.0#remarks 71 | Add-Type -AssemblyName System.IO.Compression.FileSystem 72 | 73 | foreach ($module in $modulesWithDscResources) 74 | { 75 | $destinationPath = Join-Path -Path $CompressedModulesFolder -ChildPath "$($module.Name)_$($module.Version).zip" 76 | 77 | Write-Host "Compressing module '$($module.Name)' to '$destinationPath'" 78 | [System.IO.Compression.ZipFile]::CreateFromDirectory($module.ModuleBase, $destinationPath, 'Fastest', $false) 79 | $hash = (Get-FileHash -Path $destinationPath).Hash 80 | 81 | try 82 | { 83 | $stream = New-Object -TypeName System.IO.StreamWriter("$destinationPath.checksum", $false) 84 | [void] $stream.Write($hash) 85 | } 86 | finally 87 | { 88 | if ($stream) 89 | { 90 | $stream.Close() 91 | } 92 | } 93 | } 94 | 95 | $ProgressPreference = $previousProgressPreference 96 | } 97 | else 98 | { 99 | Write-Build Green "No data in 'ConfigurationData.AllNodes', skipping task 'CompressModulesWithChecksum'." 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /source/Tasks/LoadDatumConfigData.build.ps1: -------------------------------------------------------------------------------- 1 | param 2 | ( 3 | # Project path 4 | [Parameter()] 5 | [System.String] 6 | $ProjectPath = (property ProjectPath $BuildRoot), 7 | 8 | [Parameter()] 9 | [string] 10 | $DatumConfigDataDirectory = (property DatumConfigDataDirectory 'source'), 11 | 12 | [Parameter()] 13 | [scriptblock] 14 | $Filter = (property Filter {}), 15 | 16 | [Parameter()] 17 | [int] 18 | $CurrentJobNumber = (property CurrentJobNumber 1), 19 | 20 | [Parameter()] 21 | [int] 22 | $TotalJobCount = (property TotalJobCount 1), 23 | 24 | # Build Configuration object 25 | [Parameter()] 26 | [System.Collections.Hashtable] 27 | $BuildInfo = (property BuildInfo @{ }) 28 | ) 29 | 30 | task LoadDatumConfigData { 31 | 32 | $DatumConfigDataDirectory = Get-SamplerAbsolutePath -Path $DatumConfigDataDirectory -RelativeTo $ProjectPath 33 | if ($null -eq $Filter) 34 | { 35 | $Filter = {} 36 | } 37 | 38 | Import-Module -Name PowerShell-Yaml -Scope Global 39 | Import-Module -Name Datum -Scope Global 40 | 41 | # Fix Import issue of Datum.InvokeCommand from vscode integrated terminal 42 | if (-not (Get-Command -Name Import-PowerShellDataFile -ErrorAction SilentlyContinue)) 43 | { 44 | Import-Module -Name Microsoft.PowerShell.Utility -RequiredVersion 3.1.0.0 45 | } 46 | 47 | $global:node = $null #very imporant, otherwise the 2nd build in the same session won't work 48 | $node = $null 49 | 50 | $datumDefinitionFile = Join-Path -Resolve -Path $DatumConfigDataDirectory -ChildPath 'Datum.yml' 51 | Write-Build Green "Loading Datum Definition from '$datumDefinitionFile'" 52 | $global:datum = New-DatumStructure -DefinitionFile $datumDefinitionFile 53 | 54 | if (-not ($datum.AllNodes)) 55 | { 56 | Write-Error 'No nodes found in the solution' 57 | } 58 | 59 | $getFilteredConfigurationDataParams = @{ 60 | CurrentJobNumber = $CurrentJobNumber 61 | TotalJobCount = $TotalJobCount 62 | Filter = $Filter 63 | } 64 | 65 | if ($message = (&git log -1) -and $message -match "--Added new node '(?(\w|\.|-)+)'") 66 | { 67 | $global:Filter = $Filter = [scriptblock]::Create('$_.NodeName -eq "{0}"' -f $Matches.NodeName) 68 | $global:SkipCompressedModulesBuild = $true 69 | 70 | $getFilteredConfigurationDataParams['Filter'] = $Filter 71 | } 72 | 73 | $global:configurationData = Get-FilteredConfigurationData @getFilteredConfigurationDataParams 74 | } 75 | -------------------------------------------------------------------------------- /source/Tasks/NewMofChecksums.build.ps1: -------------------------------------------------------------------------------- 1 | param 2 | ( 3 | # Project path 4 | [Parameter()] 5 | [System.String] 6 | $ProjectPath = (property ProjectPath $BuildRoot), 7 | 8 | [Parameter()] 9 | # Base directory of all output (default to 'output') 10 | [System.String] 11 | $OutputDirectory = (property OutputDirectory (Join-Path -Path $BuildRoot -ChildPath output)), 12 | 13 | [Parameter()] 14 | [string] 15 | $MofOutputFolder = (property MofOutputFolder 'MOF'), 16 | 17 | # Build Configuration object 18 | [Parameter()] 19 | [System.Collections.Hashtable] 20 | $BuildInfo = (property BuildInfo @{ }) 21 | ) 22 | 23 | task NewMofChecksums { 24 | . Set-SamplerTaskVariable -AsNewBuild 25 | 26 | $MofOutputFolder = Get-SamplerAbsolutePath -Path $MofOutputFolder -RelativeTo $OutputDirectory 27 | 28 | $mofs = Get-ChildItem -Path $MofOutputFolder -Filter *.mof -File -Recurse -ErrorAction SilentlyContinue 29 | foreach ($mof in $mofs) 30 | { 31 | New-DscChecksum -Path $mof.FullName -Verbose:$false -Force 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/Tasks/TestBuildAcceptance.build.ps1: -------------------------------------------------------------------------------- 1 | param 2 | ( 3 | # Project path 4 | [Parameter()] 5 | [System.String] 6 | $ProjectPath = (property ProjectPath $BuildRoot), 7 | 8 | [Parameter()] 9 | # Base directory of all output (default to 'output') 10 | [System.String] 11 | $OutputDirectory = (property OutputDirectory (Join-Path $BuildRoot 'output')), 12 | 13 | [Parameter()] 14 | [string] 15 | $DatumConfigDataDirectory = (property DatumConfigDataDirectory 'source'), 16 | 17 | [Parameter()] 18 | [System.Object[]] 19 | $PesterScript = (property PesterScript 'tests'), 20 | 21 | [Parameter()] 22 | [System.Object[]] 23 | $AcceptancePesterScript = (property AcceptancePesterScript 'Acceptance'), 24 | 25 | [Parameter()] 26 | [string[]] 27 | $excludeTag = (property excludeTag @()), 28 | 29 | [Parameter()] 30 | [int] 31 | $CurrentJobNumber = (property CurrentJobNumber 1), 32 | 33 | [Parameter()] 34 | [int] 35 | $TotalJobCount = (property TotalJobCount 1), 36 | 37 | # Build Configuration object 38 | [Parameter()] 39 | [System.Collections.Hashtable] 40 | $BuildInfo = (property BuildInfo @{ }) 41 | ) 42 | 43 | task TestBuildAcceptance { 44 | $PesterOutputFolder = Get-SamplerAbsolutePath -Path $PesterOutputFolder -RelativeTo $OutputDirectory 45 | "`tPester Output Folder = '$PesterOutputFolder" 46 | if (-not (Test-Path -Path $PesterOutputFolder)) 47 | { 48 | Write-Build -Color 'Yellow' -Text "Creating folder $PesterOutputFolder" 49 | 50 | $null = New-Item -Path $PesterOutputFolder -ItemType 'Directory' -Force -ErrorAction 'Stop' 51 | } 52 | 53 | $DatumConfigDataDirectory = Get-SamplerAbsolutePath -Path $DatumConfigDataDirectory -RelativeTo $ProjectPath 54 | $PesterScript = $PesterScript.Foreach({ 55 | Get-SamplerAbsolutePath -Path $_ -RelativeTo $ProjectPath 56 | }) 57 | 58 | $AcceptancePesterScript = $AcceptancePesterScript.Foreach({ 59 | Get-SamplerAbsolutePath -Path $_ -RelativeTo $PesterScript[0] 60 | }) 61 | 62 | Write-Build Green "Acceptance Data Pester Scripts = [$($AcceptancePesterScript -join ';')]" 63 | 64 | if (-not (Test-Path -Path $AcceptancePesterScript)) 65 | { 66 | Write-Build Yellow "Path for tests '$AcceptancePesterScript' does not exist" 67 | return 68 | } 69 | 70 | $testResultsPath = Get-SamplerAbsolutePath -Path AcceptanceTestResults.xml -RelativeTo $PesterOutputFolder 71 | 72 | Write-Build DarkGray "TestResultsPath is: $testResultsPath" 73 | Write-Build DarkGray "BuildOutput is: $OutputDirectory" 74 | 75 | Import-Module -Name Pester 76 | $po = New-PesterConfiguration 77 | $po.Run.PassThru = $true 78 | $po.Run.Path = [string[]]$AcceptancePesterScript 79 | $po.Output.Verbosity = 'Detailed' 80 | if ($excludeTag) 81 | { 82 | $po.Filter.ExcludeTag = $excludeTag 83 | } 84 | $po.Filter.Tag = 'BuildAcceptance' 85 | $po.TestResult.Enabled = $true 86 | $po.TestResult.OutputFormat = 'NUnitXml' 87 | $po.TestResult.OutputPath = $testResultsPath 88 | $testResults = Invoke-Pester -Configuration $po 89 | 90 | assert ($testResults.FailedCount -eq 0 -and $testResults.FailedBlocksCount -eq 0 -and $testResults.FailedContainersCount -eq 0) 91 | } 92 | -------------------------------------------------------------------------------- /source/Tasks/TestConfigData.build.ps1: -------------------------------------------------------------------------------- 1 | param 2 | ( 3 | # Project path 4 | [Parameter()] 5 | [System.String] 6 | $ProjectPath = (property ProjectPath $BuildRoot), 7 | 8 | [Parameter()] 9 | # Base directory of all output (default to 'output') 10 | [System.String] 11 | $OutputDirectory = (property OutputDirectory (Join-Path $BuildRoot 'output')), 12 | 13 | [Parameter()] 14 | [System.String] 15 | $PesterOutputFolder = (property PesterOutputFolder 'TestResults'), 16 | 17 | [Parameter()] 18 | [System.String] 19 | $PesterOutputFormat = (property PesterOutputFormat ''), 20 | 21 | [Parameter()] 22 | [System.Object[]] 23 | $PesterScript = (property PesterScript ''), 24 | 25 | [Parameter()] 26 | [System.Object[]] 27 | $ConfigDataPesterScript = (property ConfigDataPesterScript 'ConfigData'), 28 | 29 | [Parameter()] 30 | [int] 31 | $CurrentJobNumber = (property CurrentJobNumber 1), 32 | 33 | [Parameter()] 34 | [int] 35 | $TotalJobCount = (property TotalJobCount 1), 36 | 37 | # Build Configuration object 38 | [Parameter()] 39 | [System.Collections.Hashtable] 40 | $BuildInfo = (property BuildInfo @{ }) 41 | ) 42 | 43 | task TestConfigData -if ($CurrentJobNumber -eq 1) { 44 | 45 | $isWrongPesterVersion = (Get-Module -Name 'Pester' -ListAvailable | Select-Object -First 1).Version -lt [System.Version] '5.0.0' 46 | 47 | # If the correct module is not imported, then exit. 48 | if ($isWrongPesterVersion) 49 | { 50 | "Pester 5 is not used in the pipeline, skipping task.`n" 51 | 52 | return 53 | } 54 | 55 | . Set-SamplerTaskVariable -AsNewBuild 56 | 57 | $PesterOutputFolder = Get-SamplerAbsolutePath -Path $PesterOutputFolder -RelativeTo $OutputDirectory 58 | "`tPester Output Folder = '$PesterOutputFolder" 59 | if (-not (Test-Path -Path $PesterOutputFolder)) 60 | { 61 | Write-Build -Color 'Yellow' -Text "Creating folder $PesterOutputFolder" 62 | 63 | $null = New-Item -Path $PesterOutputFolder -ItemType 'Directory' -Force -ErrorAction 'Stop' 64 | } 65 | 66 | $PesterScript = $PesterScript.Foreach( { 67 | Get-SamplerAbsolutePath -Path $_ -RelativeTo $ProjectPath 68 | }) 69 | 70 | $ConfigDataPesterScript = $ConfigDataPesterScript.Foreach( { 71 | Get-SamplerAbsolutePath -Path $_ -RelativeTo $PesterScript[0] 72 | }) 73 | 74 | Write-Build Green "Config Data Pester Scripts = [$($ConfigDataPesterScript -join ';')]" 75 | 76 | if (-not (Test-Path -Path $ConfigDataPesterScript)) 77 | { 78 | Write-Build Yellow "Path for tests '$ConfigDataPesterScript' does not exist" 79 | return 80 | } 81 | 82 | $testResultsPath = Get-SamplerAbsolutePath -Path IntegrationTestResults.xml -RelativeTo $PesterOutputFolder 83 | 84 | Write-Build DarkGray "TestResultsPath is: $TestResultsPath" 85 | Write-Build DarkGray "OutputDirectory is: $PesterOutputFolder" 86 | 87 | Import-Module -Name Pester 88 | $po = New-PesterConfiguration 89 | $po.Run.PassThru = $true 90 | $po.Run.Path = [string[]]$ConfigDataPesterScript 91 | $po.Output.Verbosity = 'Detailed' 92 | $po.Filter.Tag = 'Integration' 93 | $po.TestResult.Enabled = $true 94 | $po.TestResult.OutputFormat = 'NUnitXml' 95 | $po.TestResult.OutputPath = $testResultsPath 96 | $testResults = Invoke-Pester -Configuration $po 97 | 98 | assert ($testResults.FailedCount -eq 0 -and $testResults.FailedBlocksCount -eq 0 -and $testResults.FailedContainersCount -eq 0) 99 | } 100 | -------------------------------------------------------------------------------- /source/Tasks/TestDscResources.build.ps1: -------------------------------------------------------------------------------- 1 | task TestDscResources { 2 | Write-Build Yellow "Not implemented yet. We don't separate Composites from Resources or build dependencies anymore." 3 | return 4 | try 5 | { 6 | Start-Transcript -Path "$BuildOutput\Logs\TestDscResources.log" 7 | 8 | foreach ($configModule in (Get-Dependency -Path $ProjectPath/RequiredModules.psd1).DependencyName) 9 | { 10 | Write-Build DarkGray '------------------------------------------------------------' 11 | Write-Build DarkGray 'Currently loaded modules:' 12 | $env:PSModulePath -split ';' | Write-Build DarkGray 13 | Write-Build DarkGray '------------------------------------------------------------' 14 | Write-Build DarkGray "The '$configModule' module provides the following configurations (DSC Composite Resources)" 15 | $m = Get-Module -Name $configModule -ListAvailable 16 | if (-not $m) 17 | { 18 | Write-Error "The module '$configModule' containing the configurations could not be found. Please check the file 'PSDepend.DscConfigurations.psd1' and verify if the module is available in the given repository" -ErrorAction Stop 19 | } 20 | 21 | $resources = Get-ChildItem -Path "$($m.ModuleBase)\DscResources" 22 | $resourceCount = $resources.Count 23 | Write-Build DarkGray "ResourceCount $resourceCount" 24 | 25 | $maxIterations = 5 26 | while ($resourceCount -ne (Get-DscResource -Module $configModule).Count -and $maxIterations -gt 0) 27 | { 28 | $dscResources = Get-DscResource -Module $configModule 29 | Write-Build DarkGray "ResourceCount DOES NOT match, currently '$($dscResources.Count)'. Resources missing:" 30 | Write-Build DarkGray (Compare-Object -ReferenceObject $resources.Name -DifferenceObject $dscResources.Name).InputObject 31 | Start-Sleep -Seconds 5 32 | $maxIterations-- 33 | } 34 | if ($maxIterations -eq 0) 35 | { 36 | throw 'Could not get the expected DSC Resource count' 37 | } 38 | 39 | Write-Build White "ResourceCount matches ($resourceCount)" 40 | Write-Build DarkGray ------------------------------------------------------------ 41 | Write-Build White 'Known DSC Composite Resources' 42 | Write-Build DarkGray ------------------------------------------------------------ 43 | Get-DscResource -Module $configModule | Out-String | Write-Build DarkGray 44 | 45 | Write-Build DarkGray ------------------------------------------------------------ 46 | Write-Build DarkGray 'Known DSC Resources' 47 | Write-Build DarkGray ------------------------------------------------------------ 48 | Write-Build DarkGray 49 | Import-LocalizedData -BindingVariable requiredResources -FileName PSDepend.DscResources.psd1 -BaseDirectory $ProjectPath 50 | $requiredResources = @($requiredResources.GetEnumerator() | Where-Object { $_.Name -ne 'PSDependOptions' }) 51 | $requiredResources.GetEnumerator() | ForEach-Object { 52 | $rr = $_ 53 | try 54 | { 55 | Get-DscResource -Module $rr.Name -WarningAction Stop 56 | } 57 | catch 58 | { 59 | Write-Error "DSC Resource '$($rr.Name)' cannot be found" -ErrorAction Stop 60 | } 61 | } | Group-Object -Property ModuleName, Version | 62 | Select-Object -Property Name, Count | Write-Build DarkGray 63 | Write-Build DarkGray ------------------------------------------------------------ 64 | } 65 | } 66 | catch 67 | { 68 | Write-Error -ErrorRecord $_ 69 | } 70 | finally 71 | { 72 | Stop-Transcript 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /source/WikiSource/Home.md: -------------------------------------------------------------------------------- 1 | # Welcome to the Sampler DSC Pipeline wiki 2 | 3 | *Sampler.DscPipeline v0.2.0-preview0013* 4 | 5 | The `Sampler.DscPipeline` PowerShell module is an extension to [Sampler](https://github.com/gaelcolas/Sampler), providing tasks to implement a DSC pipeline to create DSC artefacts as used in the [DSC Workshop](https://github.com/dsccommunity/DscWorkshop). 6 | 7 | Please leave comments, feature requests, and bug reports in then [issues section](https://github.com/SynEdgy/Sampler.DscPipeline/issues) for this module. 8 | 9 | ## Getting started 10 | 11 | To get started, please have a look at the [DscWorkshop's exercises](https://github.com/dsccommunity/DscWorkshop/tree/main/Exercises). ## Change Log A full list of changes in each version can be found in the [change log](../../CHANGELOG.md). 12 | -------------------------------------------------------------------------------- /source/en-US/Sampler.DscPipeline.strings.psd1: -------------------------------------------------------------------------------- 1 | # Localized resources for helper module Sampler.DscPipeline. 2 | 3 | ConvertFrom-StringData @' 4 | '@ 5 | -------------------------------------------------------------------------------- /source/en-US/about_Sampler.DscPipeline.help.txt: -------------------------------------------------------------------------------- 1 | TOPIC 2 | Sampler.DscPipeline 3 | 4 | SHORT DESCRIPTION 5 | Sampler tasks for a DSC Pipeline using a Datum Yaml hierarchy. 6 | 7 | LONG DESCRIPTION 8 | This module provides a set of tasks for managing a Desired State Configuration (DSC) pipeline using a Datum Yaml hierarchy. It includes functions for compiling configurations, testing configurations, and managing the pipeline process. 9 | 10 | EXAMPLES 11 | Please see how the module is used in https://github.com/dsccommunity/DscWorkshop 12 | 13 | TROUBLESHOOTING NOTE: 14 | Look out on the Github repository for issues and new releases. 15 | 16 | SEE ALSO 17 | - https://github.com/SynEdgy/Sampler.DscPipeline 18 | 19 | KEYWORDS 20 | DSC, Pipeline, Datum, Yaml, Configuration, Automation 21 | -------------------------------------------------------------------------------- /source/suffix.ps1: -------------------------------------------------------------------------------- 1 | # Inspired from https://github.com/nightroman/Invoke-Build/blob/64f3434e1daa806814852049771f4b7d3ec4d3a3/Tasks/Import/README.md#example-2-import-from-a-module-with-tasks 2 | Get-ChildItem -Path (Join-Path -Path $PSScriptRoot -ChildPath 'Tasks\*') -Include '*.build.*' | 3 | ForEach-Object -Process { 4 | $ModuleName = ([System.IO.FileInfo] $MyInvocation.MyCommand.Name).BaseName 5 | $taskFileAliasName = "$($_.BaseName).$ModuleName.ib.tasks" 6 | 7 | Set-Alias -Name $taskFileAliasName -Value $_.FullName 8 | 9 | Export-ModuleMember -Alias $taskFileAliasName 10 | } 11 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/AllNodes/Dev/DSCFile01.yml: -------------------------------------------------------------------------------- 1 | NodeName: '[x={ $Node.Name }=]' 2 | Environment: Dev 3 | Role: FileServer 4 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 5 | Location: Frankfurt 6 | Baseline: Server 7 | 8 | ComputerSettings: 9 | Name: '[x={ $Node.NodeName }=]' 10 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 11 | 12 | NetworkIpConfiguration: 13 | Interfaces: 14 | - InterfaceAlias: DscWorkshop 0 15 | IpAddress: 192.168.111.100 16 | 17 | PSDscAllowPlainTextPassword: True 18 | PSDscAllowDomainUser: True 19 | 20 | LcmConfig: 21 | ConfigurationRepositoryWeb: 22 | Server: 23 | ConfigurationNames: '[x={ $Node.NodeName }=]' 24 | 25 | DscTagging: 26 | Layers: 27 | - '[x={ Get-DatumSourceFile -Path $File } =]' 28 | NodeVersion: '[x={ $datum.Baselines.DscLcm.DscTagging.Version } =]' 29 | NodeRole: '[x={ $Node.Role } =]' 30 | 31 | FileSystemObjects: 32 | Items: 33 | - DestinationPath: Z:\DoesNotWork 34 | Type: Directory 35 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/AllNodes/Dev/DSCWeb01.yml: -------------------------------------------------------------------------------- 1 | NodeName: '[x={ $Node.Name }=]' 2 | Environment: Dev 3 | Role: WebServer 4 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 5 | Location: Singapore 6 | Baseline: Server 7 | 8 | ComputerSettings: 9 | Name: '[x={ $Node.NodeName }=]' 10 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 11 | 12 | NetworkIpConfiguration: 13 | Interfaces: 14 | - InterfaceAlias: DscWorkshop 0 15 | IpAddress: 192.168.111.101 16 | 17 | PSDscAllowPlainTextPassword: True 18 | PSDscAllowDomainUser: True 19 | 20 | LcmConfig: 21 | ConfigurationRepositoryWeb: 22 | Server: 23 | ConfigurationNames: '[x={ $Node.NodeName }=]' 24 | 25 | DscTagging: 26 | Layers: 27 | - '[x={ Get-DatumSourceFile -Path $File } =]' 28 | NodeVersion: '[x={ $datum.Baselines.DscLcm.DscTagging.Version } =]' 29 | NodeRole: '[x={ $Node.Role } =]' 30 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/AllNodes/Dev/ReferenceConfigurationDev.yml: -------------------------------------------------------------------------------- 1 | NodeName: '[x={ $Node.Name }=]' 2 | Environment: Dev 3 | Role: FileServer 4 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 5 | Location: Frankfurt 6 | Baseline: Server 7 | 8 | ComputerSettings: 9 | Name: '[x={ $Node.NodeName }=]' 10 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 11 | 12 | NetworkIpConfiguration: 13 | Interfaces: 14 | - InterfaceAlias: DscWorkshop 0 15 | IpAddress: 127.0.0.1 16 | Gateway: 127.0.0.1 17 | 18 | PSDscAllowPlainTextPassword: True 19 | PSDscAllowDomainUser: True 20 | 21 | LcmConfig: 22 | ConfigurationRepositoryWeb: 23 | Server: 24 | ConfigurationNames: '[x={ $Node.NodeName }=]' 25 | 26 | DscTagging: 27 | Layers: 28 | - '[x={ Get-DatumSourceFile -Path $File } =]' 29 | 30 | FileSystemObjects: 31 | Items: 32 | - DestinationPath: Z:\DoesNotWork 33 | Type: Directory 34 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/AllNodes/Prod/DSCFile03.yml: -------------------------------------------------------------------------------- 1 | NodeName: '[x={ $Node.Name }=]' 2 | Environment: Prod 3 | Role: FileServer 4 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 5 | Location: Frankfurt 6 | Baseline: Server 7 | 8 | ComputerSettings: 9 | Name: '[x={ $Node.NodeName }=]' 10 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 11 | 12 | NetworkIpConfiguration: 13 | Interfaces: 14 | - InterfaceAlias: DscWorkshop 0 15 | IpAddress: 192.168.111.120 16 | 17 | PSDscAllowPlainTextPassword: True 18 | PSDscAllowDomainUser: True 19 | 20 | LcmConfig: 21 | ConfigurationRepositoryWeb: 22 | Server: 23 | ConfigurationNames: '[x={ $Node.NodeName }=]' 24 | 25 | DscTagging: 26 | Layers: 27 | - '[x={ Get-DatumSourceFile -Path $File } =]' 28 | NodeVersion: '[x={ $datum.Baselines.DscLcm.DscTagging.Version } =]' 29 | NodeRole: '[x={ $Node.Role } =]' 30 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/AllNodes/Prod/DSCWeb03.yml: -------------------------------------------------------------------------------- 1 | NodeName: '[x={ $Node.Name }=]' 2 | Environment: Prod 3 | Role: WebServer 4 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 5 | Location: Singapore 6 | Baseline: Server 7 | 8 | ComputerSettings: 9 | Name: '[x={ $Node.NodeName }=]' 10 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 11 | 12 | NetworkIpConfiguration: 13 | Interfaces: 14 | - InterfaceAlias: DscWorkshop 0 15 | IpAddress: 192.168.111.121 16 | 17 | PSDscAllowPlainTextPassword: True 18 | PSDscAllowDomainUser: True 19 | 20 | LcmConfig: 21 | ConfigurationRepositoryWeb: 22 | Server: 23 | ConfigurationNames: '[x={ $Node.NodeName }=]' 24 | 25 | DscTagging: 26 | Layers: 27 | - '[x={ Get-DatumSourceFile -Path $File } =]' 28 | NodeVersion: '[x={ $datum.Baselines.DscLcm.DscTagging.Version } =]' 29 | NodeRole: '[x={ $Node.Role } =]' 30 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/AllNodes/Prod/ReferenceConfigurationProd.yml: -------------------------------------------------------------------------------- 1 | NodeName: '[x={ $Node.Name }=]' 2 | Environment: Prod 3 | Role: FileServer 4 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 5 | Location: Frankfurt 6 | Baseline: Server 7 | 8 | ComputerSettings: 9 | Name: '[x={ $Node.NodeName }=]' 10 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 11 | 12 | NetworkIpConfiguration: 13 | Interfaces: 14 | - InterfaceAlias: DscWorkshop 0 15 | IpAddress: 127.0.0.3 16 | Gateway: 127.0.0.3 17 | 18 | PSDscAllowPlainTextPassword: True 19 | PSDscAllowDomainUser: True 20 | 21 | LcmConfig: 22 | ConfigurationRepositoryWeb: 23 | Server: 24 | ConfigurationNames: '[x={ $Node.NodeName }=]' 25 | 26 | DscTagging: 27 | Layers: 28 | - '[x={ Get-DatumSourceFile -Path $File } =]' 29 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/AllNodes/Test/DSCFile02.yml: -------------------------------------------------------------------------------- 1 | NodeName: '[x={ $Node.Name }=]' 2 | Environment: Test 3 | Role: FileServer 4 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 5 | Location: Frankfurt 6 | Baseline: Server 7 | 8 | ComputerSettings: 9 | Name: '[x={ $Node.NodeName }=]' 10 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 11 | 12 | NetworkIpConfiguration: 13 | Interfaces: 14 | - InterfaceAlias: DscWorkshop 0 15 | IpAddress: 192.168.111.110 16 | 17 | PSDscAllowPlainTextPassword: True 18 | PSDscAllowDomainUser: True 19 | 20 | LcmConfig: 21 | ConfigurationRepositoryWeb: 22 | Server: 23 | ConfigurationNames: '[x={ $Node.NodeName }=]' 24 | 25 | DscTagging: 26 | Layers: 27 | - '[x={ Get-DatumSourceFile -Path $File } =]' 28 | NodeVersion: '[x={ $datum.Baselines.DscLcm.DscTagging.Version } =]' 29 | NodeRole: '[x={ $Node.Role } =]' 30 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/AllNodes/Test/DSCWeb02.yml: -------------------------------------------------------------------------------- 1 | NodeName: '[x={ $Node.Name }=]' 2 | Environment: Test 3 | Role: WebServer 4 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 5 | Location: Singapore 6 | Baseline: Server 7 | 8 | ComputerSettings: 9 | Name: '[x={ $Node.NodeName }=]' 10 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 11 | 12 | NetworkIpConfiguration: 13 | Interfaces: 14 | - InterfaceAlias: DscWorkshop 0 15 | IpAddress: 192.168.111.111 16 | 17 | PSDscAllowPlainTextPassword: True 18 | PSDscAllowDomainUser: True 19 | 20 | LcmConfig: 21 | ConfigurationRepositoryWeb: 22 | Server: 23 | ConfigurationNames: '[x={ $Node.NodeName }=]' 24 | 25 | DscTagging: 26 | Layers: 27 | - '[x={ Get-DatumSourceFile -Path $File } =]' 28 | NodeVersion: '[x={ $datum.Baselines.DscLcm.DscTagging.Version } =]' 29 | NodeRole: '[x={ $Node.Role } =]' 30 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/AllNodes/Test/ReferenceConfigurationTest.yml: -------------------------------------------------------------------------------- 1 | NodeName: '[x={ $Node.Name }=]' 2 | Environment: Test 3 | Role: WebServer 4 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 5 | Location: Singapore 6 | Baseline: Server 7 | 8 | ComputerSettings: 9 | Name: '[x={ $Node.NodeName }=]' 10 | Description: '[x= "$($Node.Role) in $($Node.Environment)" =]' 11 | 12 | NetworkIpConfiguration: 13 | Interfaces: 14 | - InterfaceAlias: DscWorkshop 0 15 | IpAddress: 127.0.0.2 16 | Gateway: 127.0.0.2 17 | 18 | PSDscAllowPlainTextPassword: True 19 | PSDscAllowDomainUser: True 20 | 21 | LcmConfig: 22 | ConfigurationRepositoryWeb: 23 | Server: 24 | ConfigurationNames: '[x={ $Node.NodeName }=]' 25 | 26 | DscTagging: 27 | Layers: 28 | - '[x={ Get-DatumSourceFile -Path $File } =]' 29 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/Baselines/DscLcm.yml: -------------------------------------------------------------------------------- 1 | Configurations: 2 | #- DscLcmController 3 | #- DscLcmMaintenanceWindows 4 | - DscTagging 5 | #- DscDiagnostic 6 | 7 | DscTagging: 8 | Version: 0.3.0 9 | Layers: 10 | - '[x={ Get-DatumSourceFile -Path $File } =]' 11 | 12 | DscLcmMaintenanceWindows: 13 | MaintenanceWindows: 14 | - Name: Always 15 | StartTime: 00:00:00 16 | Timespan: 24:00:00 17 | #- Name: FirstWednesdayNight 18 | # DayOfWeek: Wednesday 19 | # StartTime: 00:30:00 20 | # Timespan: 02:00:00 21 | # On: 1st 22 | #- Name: SundayAllDay 23 | # DayOfWeek: Sunday 24 | # StartTime: 00:00:00 25 | # Timespan: 24:00:00 26 | DependsOn: '[DscLcmController]DscLcmController' 27 | 28 | DscLcmController: 29 | MaintenanceWindowMode: AutoCorrect # or Monitor. In Monitor mode the LCM won't do changes in maintenance window. 30 | MonitorInterval: 00:15:00 # 31 | AutoCorrectInterval: 00:30:00 # 32 | AutoCorrectIntervalOverride: false # THESE VALUES ARE VERY LOW FOR EASY TESTING AND DEMONSTRATIONS 33 | RefreshInterval: 00:30:00 # 34 | RefreshIntervalOverride: false # PLEASE INCREASE THE VALUES TO PREVENT OVERLOADING THE PULL SERVER 35 | ControllerInterval: 00:05:00 # AS WELL AS THE NODES. ALSO THE SQL DATABASE GROWS PRETTY FAST IF A 36 | MaintenanceWindowOverride: false # REPORT IS SENT EVERY 5 MINUTES. 37 | WriteTranscripts: true # 38 | SendDscTaggingData: true # 39 | MaxLcmRuntime: 00:30:00 # 40 | LogHistoryTimeSpan: 7.00:00:00 # 41 | 42 | LcmConfig: 43 | Settings: 44 | RefreshMode: Pull 45 | RefreshFrequencyMins: 30 46 | RebootNodeIfNeeded: true 47 | ActionAfterReboot: ContinueConfiguration 48 | AllowModuleOverwrite: true 49 | #CertificateID: ... 50 | ConfigurationMode: ApplyAndMonitor 51 | ConfigurationModeFrequencyMins: 30 52 | ConfigurationRepositoryWeb: 53 | Server: 54 | ServerURL: https://dscpull01.contoso.com:8080/PSDSCPullServer.svc 55 | RegistrationKey: ec717ee9-b343-49ee-98a2-26e53939eecf 56 | ReportServerWeb: 57 | ServerURL: https://dscpull01.contoso.com:8080/PSDSCPullServer.svc 58 | RegistrationKey: ec717ee9-b343-49ee-98a2-26e53939eecf 59 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/Baselines/Security.yml: -------------------------------------------------------------------------------- 1 | Configurations: 2 | - SecurityBase 3 | - WindowsFeatures 4 | 5 | WindowsFeatures: 6 | Names: 7 | - -Telnet-Client 8 | 9 | SecurityBase: 10 | Role: Baseline 11 | DependsOn: '[WindowsFeatures]WindowsFeatures' 12 | 13 | DscTagging: 14 | Layers: 15 | - '[x={ Get-DatumSourceFile -Path $File } =]' 16 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/Baselines/Server.yml: -------------------------------------------------------------------------------- 1 | Configurations: 2 | - ComputerSettings 3 | - NetworkIpConfiguration 4 | - WindowsEventLogs 5 | 6 | NetworkIpConfiguration: 7 | Interfaces: 8 | - InterfaceAlias: DscWorkshop 0 9 | Prefix: 24 10 | Gateway: 192.168.111.50 11 | DnsServer: 12 | - 192.168.111.10 13 | DisableNetbios: true 14 | 15 | WindowsEventLogs: 16 | Logs: 17 | - LogName: System 18 | LogMode: Circular 19 | MaximumSizeInBytes: 20971520 20 | IsEnabled: true 21 | - LogName: Application 22 | LogMode: Circular 23 | MaximumSizeInBytes: 20971520 24 | IsEnabled: true 25 | - LogName: Security 26 | LogMode: Circular 27 | MaximumSizeInBytes: 134217728 28 | IsEnabled: true 29 | DependsOn: '[ComputerSettings]ComputerSettings' 30 | 31 | ComputerSettings: 32 | DomainName: '[x={ $Datum.Global.Domain.DomainFqdn }=]' 33 | JoinOU: '[x= "CN=Computers,$($Datum.Global.Domain.DomainDn)" =]' 34 | Credential: '[x={ $Datum.Global.Domain.DomainJoinCredentials }=]' 35 | TimeZone: Greenwich Standard Time 36 | 37 | DscTagging: 38 | Layers: 39 | - '[x={ Get-DatumSourceFile -Path $File } =]' 40 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/Datum.yml: -------------------------------------------------------------------------------- 1 | ResolutionPrecedence: 2 | - AllNodes\$($Node.Environment)\$($Node.NodeName) 3 | - Environment\$($Node.Environment) 4 | - Locations\$($Node.Location) 5 | - Roles\$($Node.Role) 6 | - Baselines\Security 7 | - Baselines\$($Node.Baseline) 8 | - Baselines\DscLcm 9 | 10 | DatumHandlersThrowOnError: true 11 | DatumHandlers: 12 | Datum.ProtectedData::ProtectedDatum: 13 | CommandOptions: 14 | PlainTextPassword: SomeSecret 15 | Datum.InvokeCommand::InvokeCommand: 16 | SkipDuringLoad: true 17 | 18 | DscLocalConfigurationManagerKeyName: LcmConfig 19 | 20 | default_lookup_options: MostSpecific 21 | 22 | lookup_options: 23 | Configurations: 24 | merge_basetype_array: Unique 25 | 26 | Baseline: 27 | merge_hash: deep 28 | 29 | DscTagging: 30 | merge_hash: deep 31 | DscTagging\Layers: 32 | merge_basetype_array: Unique 33 | 34 | WindowsFeatures: 35 | merge_hash: deep 36 | 37 | WindowsFeatures\Names: 38 | merge_basetype_array: Unique 39 | 40 | FileSystemObjects: 41 | merge_hash: deep 42 | FileSystemObjects\Items: 43 | merge_hash_array: UniqueKeyValTuples 44 | merge_options: 45 | tuple_keys: 46 | - DestinationPath 47 | 48 | RegistryValues: 49 | merge_hash: deep 50 | RegistryValues\Values: 51 | merge_hash_array: UniqueKeyValTuples 52 | merge_options: 53 | tuple_keys: 54 | - Key 55 | 56 | NetworkIpConfiguration: 57 | merge_hash: deep 58 | NetworkIpConfiguration\Interfaces: 59 | #merge_basetype_array: Deep 60 | merge_hash_array: DeepTuple 61 | merge_options: 62 | tuple_keys: 63 | - InterfaceAlias 64 | 65 | ComputerSettings: 66 | merge_hash: deep 67 | 68 | LcmConfig: 69 | merge_hash: deep 70 | LcmConfig\Settings: 71 | merge_hash: deep 72 | LcmConfig\ReportServerWeb: deep 73 | LcmConfig\ConfigurationRepositoryWeb: 74 | merge_hash: deep 75 | LcmConfig\ConfigurationRepositoryWeb\Server: 76 | merge_hash: deep 77 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/Environment/Dev.yml: -------------------------------------------------------------------------------- 1 | DscTagging: 2 | Environment: '[x={ $Node.Environment } =]' 3 | Layers: 4 | - '[x={ Get-DatumSourceFile -Path $File } =]' 5 | 6 | FileSystemObjects: 7 | Items: 8 | - DestinationPath: '[x= "C:\Test\$($File.BaseName)-Environment" =]' 9 | Type: Directory 10 | 11 | RegistryValues: 12 | Values: 13 | - Key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters 14 | ValueName: DBFlag 15 | ValueData: 545325055 16 | ValueType: DWORD 17 | Ensure: Present 18 | Force: true 19 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/Environment/Prod.yml: -------------------------------------------------------------------------------- 1 | DscTagging: 2 | Environment: '[x={ $Node.Environment } =]' 3 | Layers: 4 | - '[x={ Get-DatumSourceFile -Path $File } =]' 5 | 6 | FileSystemObjects: 7 | Items: 8 | - DestinationPath: '[x= "C:\Test\$($File.BaseName)-Environment" =]' 9 | Type: Directory 10 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/Environment/Test.yml: -------------------------------------------------------------------------------- 1 | DscTagging: 2 | Environment: '[x={ $Node.Environment } =]' 3 | Layers: 4 | - '[x={ Get-DatumSourceFile -Path $File } =]' 5 | 6 | FileSystemObjects: 7 | Items: 8 | - DestinationPath: '[x= "C:\Test\$($File.BaseName)-Environment" =]' 9 | Type: Directory 10 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/Global/Azure.yml: -------------------------------------------------------------------------------- 1 | TenantId: 85a26452-f44d-4090-b62b-ae668d70bdeb 2 | SubscriptionId: da78bd51-11ab-418d-8222-db661c2aa8d9 3 | ResourceGroupName: GCLab1 4 | StorageAccountName: gclab1sa67 5 | LocationName: uksouth 6 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/Global/Domain.yml: -------------------------------------------------------------------------------- 1 | DomainFqdn: contoso.com 2 | DomainDn: dc=contoso,dc=com 3 | #contoso\install : Somepass1 4 | DomainAdminCredentials: '[ENC=PE9ianMgVmVyc2lvbj0iMS4xLjAuMSIgeG1sbnM9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vcG93ZXJzaGVsbC8yMDA0LzA0Ij4NCiAgPE9iaiBSZWZJZD0iMCI+DQogICAgPFROIFJlZklkPSIwIj4NCiAgICAgIDxUPlN5c3RlbS5NYW5hZ2VtZW50LkF1dG9tYXRpb24uUFNDdXN0b21PYmplY3Q8L1Q+DQogICAgICA8VD5TeXN0ZW0uT2JqZWN0PC9UPg0KICAgIDwvVE4+DQogICAgPE1TPg0KICAgICAgPE9iaiBOPSJLZXlEYXRhIiBSZWZJZD0iMSI+DQogICAgICAgIDxUTiBSZWZJZD0iMSI+DQogICAgICAgICAgPFQ+U3lzdGVtLk9iamVjdFtdPC9UPg0KICAgICAgICAgIDxUPlN5c3RlbS5BcnJheTwvVD4NCiAgICAgICAgICA8VD5TeXN0ZW0uT2JqZWN0PC9UPg0KICAgICAgICA8L1ROPg0KICAgICAgICA8TFNUPg0KICAgICAgICAgIDxPYmogUmVmSWQ9IjIiPg0KICAgICAgICAgICAgPFROUmVmIFJlZklkPSIwIiAvPg0KICAgICAgICAgICAgPE1TPg0KICAgICAgICAgICAgICA8UyBOPSJIYXNoIj4xRUZBNTk3QUVERTAyNkNCMDg2RTY4NTIwRjVDNjM2QTc3Q0U1QTNDMDA4RjhENkUxQjBEQjYyMkM5NzFCRkEyPC9TPg0KICAgICAgICAgICAgICA8STMyIE49Ikl0ZXJhdGlvbkNvdW50Ij41MDAwMDwvSTMyPg0KICAgICAgICAgICAgICA8QkEgTj0iS2V5Ij5KbHZqZDBsc2F0VDZxdFNBRXlQSGFEelhrRVJLVS82Z29rQ3QzZm4zRE1VNndSbTFiMy9RcFRBTDRBNk9BSXlsPC9CQT4NCiAgICAgICAgICAgICAgPEJBIE49Ikhhc2hTYWx0Ij5IMzYxc2ZzUENEVXk1Ri92NUN2eno1VTVQVk5KNjhuUWtQTFd5RVltRU44PTwvQkE+DQogICAgICAgICAgICAgIDxCQSBOPSJTYWx0Ij5jcHE3V1ZFRHVwWVhVbVlDbGVRWE0yVWkxN2lFa0s2ZTFrWTYyT2ZONXRJPTwvQkE+DQogICAgICAgICAgICAgIDxCQSBOPSJJViI+Q1VsZWIrbStjbHUvYVk0MmE1aWVTNWlwQWl5NEo1T2hZTmVUR1BSUkhCST08L0JBPg0KICAgICAgICAgICAgPC9NUz4NCiAgICAgICAgICA8L09iaj4NCiAgICAgICAgPC9MU1Q+DQogICAgICA8L09iaj4NCiAgICAgIDxCQSBOPSJDaXBoZXJUZXh0Ij5oLzlodnE5S2ViSnROUmE2NWc5NDVmUDlLU0FwRmYxZ3loaHBYc0s2TFBPZTF3WVRRaFFaTGRxN2lnYlJhWloydU9EeWF0d3diUFVGbFZRVWd2Tm5YWTUxNjUrbjhGYkZOUjd6bTlBWHVNVT08L0JBPg0KICAgICAgPEJBIE49IkhNQUMiPnE5R3BaZlp5U3pHaDByME1uaE1RM3V3S3ZudkdobnJxeFVneG9jS0RRUkU9PC9CQT4NCiAgICAgIDxTIE49IlR5cGUiPlN5c3RlbS5NYW5hZ2VtZW50LkF1dG9tYXRpb24uUFNDcmVkZW50aWFsPC9TPg0KICAgIDwvTVM+DQogIDwvT2JqPg0KPC9PYmpzPg==]' 5 | #contoso\install : Somepass1 6 | DomainJoinCredentials: '[ENC=PE9ianMgVmVyc2lvbj0iMS4xLjAuMSIgeG1sbnM9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vcG93ZXJzaGVsbC8yMDA0LzA0Ij4NCiAgPE9iaiBSZWZJZD0iMCI+DQogICAgPFROIFJlZklkPSIwIj4NCiAgICAgIDxUPlN5c3RlbS5NYW5hZ2VtZW50LkF1dG9tYXRpb24uUFNDdXN0b21PYmplY3Q8L1Q+DQogICAgICA8VD5TeXN0ZW0uT2JqZWN0PC9UPg0KICAgIDwvVE4+DQogICAgPE1TPg0KICAgICAgPE9iaiBOPSJLZXlEYXRhIiBSZWZJZD0iMSI+DQogICAgICAgIDxUTiBSZWZJZD0iMSI+DQogICAgICAgICAgPFQ+U3lzdGVtLk9iamVjdFtdPC9UPg0KICAgICAgICAgIDxUPlN5c3RlbS5BcnJheTwvVD4NCiAgICAgICAgICA8VD5TeXN0ZW0uT2JqZWN0PC9UPg0KICAgICAgICA8L1ROPg0KICAgICAgICA8TFNUPg0KICAgICAgICAgIDxPYmogUmVmSWQ9IjIiPg0KICAgICAgICAgICAgPFROUmVmIFJlZklkPSIwIiAvPg0KICAgICAgICAgICAgPE1TPg0KICAgICAgICAgICAgICA8UyBOPSJIYXNoIj4xRUZBNTk3QUVERTAyNkNCMDg2RTY4NTIwRjVDNjM2QTc3Q0U1QTNDMDA4RjhENkUxQjBEQjYyMkM5NzFCRkEyPC9TPg0KICAgICAgICAgICAgICA8STMyIE49Ikl0ZXJhdGlvbkNvdW50Ij41MDAwMDwvSTMyPg0KICAgICAgICAgICAgICA8QkEgTj0iS2V5Ij5KbHZqZDBsc2F0VDZxdFNBRXlQSGFEelhrRVJLVS82Z29rQ3QzZm4zRE1VNndSbTFiMy9RcFRBTDRBNk9BSXlsPC9CQT4NCiAgICAgICAgICAgICAgPEJBIE49Ikhhc2hTYWx0Ij5IMzYxc2ZzUENEVXk1Ri92NUN2eno1VTVQVk5KNjhuUWtQTFd5RVltRU44PTwvQkE+DQogICAgICAgICAgICAgIDxCQSBOPSJTYWx0Ij5jcHE3V1ZFRHVwWVhVbVlDbGVRWE0yVWkxN2lFa0s2ZTFrWTYyT2ZONXRJPTwvQkE+DQogICAgICAgICAgICAgIDxCQSBOPSJJViI+Q1VsZWIrbStjbHUvYVk0MmE1aWVTNWlwQWl5NEo1T2hZTmVUR1BSUkhCST08L0JBPg0KICAgICAgICAgICAgPC9NUz4NCiAgICAgICAgICA8L09iaj4NCiAgICAgICAgPC9MU1Q+DQogICAgICA8L09iaj4NCiAgICAgIDxCQSBOPSJDaXBoZXJUZXh0Ij5oLzlodnE5S2ViSnROUmE2NWc5NDVmUDlLU0FwRmYxZ3loaHBYc0s2TFBPZTF3WVRRaFFaTGRxN2lnYlJhWloydU9EeWF0d3diUFVGbFZRVWd2Tm5YWTUxNjUrbjhGYkZOUjd6bTlBWHVNVT08L0JBPg0KICAgICAgPEJBIE49IkhNQUMiPnE5R3BaZlp5U3pHaDByME1uaE1RM3V3S3ZudkdobnJxeFVneG9jS0RRUkU9PC9CQT4NCiAgICAgIDxTIE49IlR5cGUiPlN5c3RlbS5NYW5hZ2VtZW50LkF1dG9tYXRpb24uUFNDcmVkZW50aWFsPC9TPg0KICAgIDwvTVM+DQogIDwvT2JqPg0KPC9PYmpzPg==]' 7 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/Locations/Frankfurt.yml: -------------------------------------------------------------------------------- 1 | Configurations: 2 | - FileSystemObjects 3 | 4 | FileSystemObjects: 5 | Items: 6 | - DestinationPath: '[x= "C:\Test\$($File.BaseName)" =]' 7 | Type: Directory 8 | 9 | DscTagging: 10 | Layers: 11 | - '[x={ Get-DatumSourceFile -Path $File } =]' 12 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/Locations/London.yml: -------------------------------------------------------------------------------- 1 | Configurations: 2 | - FileSystemObjects 3 | 4 | FileSystemObjects: 5 | Items: 6 | - DestinationPath: '[x= "C:\Test\$($File.BaseName)" =]' 7 | Type: Directory 8 | 9 | DscTagging: 10 | Layers: 11 | - '[x={ Get-DatumSourceFile -Path $File } =]' 12 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/Locations/Singapore.yml: -------------------------------------------------------------------------------- 1 | Configurations: 2 | - FileSystemObjects 3 | 4 | FileSystemObjects: 5 | Items: 6 | - DestinationPath: '[x= "C:\Test\$($File.BaseName)" =]' 7 | Type: Directory 8 | 9 | DscTagging: 10 | Layers: 11 | - '[x={ Get-DatumSourceFile -Path $File } =]' 12 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/Locations/Tokio.yml: -------------------------------------------------------------------------------- 1 | Configurations: 2 | - FileSystemObjects 3 | 4 | FileSystemObjects: 5 | Items: 6 | - DestinationPath: '[x= "C:\Test\$($File.BaseName)" =]' 7 | Type: Directory 8 | 9 | DscTagging: 10 | Layers: 11 | - '[x={ Get-DatumSourceFile -Path $File } =]' 12 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/Roles/DomainController.yml: -------------------------------------------------------------------------------- 1 | Configurations: 2 | - AddsDomainController 3 | 4 | AddsDomainController: 5 | DomainName: '[x={ $Datum.Global.Domain.DomainFqdn }=]' 6 | Credential: '[x={ $Datum.Global.Domain.DomainJoinCredentials }=]' 7 | SafeModeAdministratorPassword: '[x={ $Datum.Global.Domain.DomainJoinCredentials }=]' 8 | DatabasePath: C:\Windows\NTDS 9 | LogPath: C:\Windows\Logs 10 | SysvolPath: C:\Windows\SYSVOL 11 | SiteName: Default-First-Site-Name 12 | IsGlobalCatalog: True 13 | 14 | DscTagging: 15 | Layers: 16 | - '[x={ Get-DatumSourceFile -Path $File } =]' 17 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/Roles/FileServer.yml: -------------------------------------------------------------------------------- 1 | Configurations: 2 | - FileSystemObjects 3 | - RegistryValues 4 | 5 | WindowsFeatures: 6 | Names: 7 | - File-Services 8 | 9 | FileSystemObjects: 10 | Items: 11 | - DestinationPath: C:\Test 12 | Type: Directory 13 | - DestinationPath: C:\Test\Test1File1.txt 14 | Type: File 15 | Contents: Some test data 16 | DependsOn: '[FileSystemObject]FileSystemObject_C__Test' 17 | - DestinationPath: C:\Test\Test1File2.txt 18 | Type: File 19 | Contents: Some test data 20 | DependsOn: '[FileSystemObject]FileSystemObject_C__Test' 21 | - DestinationPath: C:\GpoBackup 22 | #SourcePath: '[x= "\\DSCDC01\SYSVOL\$($Datum.Global.Domain.DomainFqdn)\Policies" =]' 23 | SourcePath: C:\Windows\Media 24 | Type: Directory 25 | 26 | RegistryValues: 27 | Values: 28 | - Key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Parameters 29 | ValueName: NtpServer 30 | ValueData: pool.ntp.org,0x9 31 | ValueType: String 32 | Ensure: Present 33 | Force: true 34 | DependsOn: '[FileSystemObjects]FileSystemObjects' 35 | 36 | SecurityBaseline: 37 | Role: FileServer 38 | DependsOn: '[RegistryValues]RegistryValues' 39 | 40 | DscTagging: 41 | Layers: 42 | - '[x={ Get-DatumSourceFile -Path $File } =]' 43 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/Roles/WebServer.yml: -------------------------------------------------------------------------------- 1 | Configurations: 2 | - WindowsServices 3 | - RegistryValues 4 | - FileSystemObjects 5 | - WebApplicationPools 6 | - WebApplications 7 | 8 | FileSystemObjects: 9 | Items: 10 | - DestinationPath: C:\Inetpub\TestApp1 11 | Type: Directory 12 | - DestinationPath: C:\Inetpub\TestApp2 13 | Type: Directory 14 | - DestinationPath: C:\Inetpub\TestApp1\default.html 15 | Type: File 16 | Contents: This is TestApp1 17 | DependsOn: '[FileSystemObject]FileSystemObject_C__Inetpub_TestApp1' 18 | - DestinationPath: C:\Inetpub\TestApp2\default.html 19 | Type: File 20 | Contents: This is TestApp2 21 | DependsOn: '[FileSystemObject]FileSystemObject_C__Inetpub_TestApp2' 22 | 23 | WebApplicationPools: 24 | Items: 25 | - Name: TestAppPool1 26 | Ensure: Present 27 | IdentityType: ApplicationPoolIdentity 28 | State: Started 29 | - Name: TestAppPool2 30 | Ensure: Present 31 | IdentityType: ApplicationPoolIdentity 32 | State: Started 33 | DependsOn: '[WebAppPool]TestAppPool1' 34 | DependsOn: 35 | - '[FileSystemObjects]FileSystemObjects' 36 | - '[WindowsFeatures]WindowsFeatures' 37 | 38 | WebApplications: 39 | Items: 40 | - Name: TestApp1 41 | PhysicalPath: C:\Inetpub\TestApp1 42 | WebAppPool: TestAppPool1 43 | Website: Default Web Site 44 | Ensure: Present 45 | - Name: TestApp2 46 | PhysicalPath: C:\Inetpub\TestApp2 47 | WebAppPool: TestAppPool2 48 | Website: Default Web Site 49 | Ensure: Present 50 | DependsOn: '[WebApplication]webapp_TestApp1' 51 | DependsOn: '[WebApplicationPools]WebApplicationPools' 52 | 53 | RegistryValues: 54 | Values: 55 | - Key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Parameters 56 | ValueName: NtpServer 57 | ValueData: time.windows.com,0x9; 58 | ValueType: String 59 | Ensure: Present 60 | Force: true 61 | DependsOn: '[WebApplications]WebApplications' 62 | 63 | SecurityBase: 64 | Role: WebServer 65 | 66 | WindowsFeatures: 67 | Names: 68 | - +Web-Server 69 | - -WoW64-Support 70 | 71 | WindowsServices: 72 | Services: 73 | - Name: FileDummy 74 | DisplayName: Dummy Service on Web Servers 75 | StartupType: Automatic 76 | Path: C:\DummyService.exe 77 | DependsOn: '[RegistryValues]RegistryValues' 78 | DscTagging: 79 | Layers: 80 | - '[x={ Get-DatumSourceFile -Path $File } =]' 81 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/TestRsopReferences/ReferenceConfigurationDev.yml: -------------------------------------------------------------------------------- 1 | WindowsFeatures: 2 | Names: 3 | - File-Services 4 | - -Telnet-Client 5 | Description: 'FileServer in ' 6 | DscDiagnostic: {} 7 | Name: ReferenceConfigurationDev 8 | DscLcmController: 9 | MonitorInterval: 00:15:00 10 | SendDscTaggingData: true 11 | AutoCorrectInterval: 00:30:00 12 | MaintenanceWindowMode: AutoCorrect 13 | RefreshIntervalOverride: false 14 | MaintenanceWindowOverride: false 15 | ControllerInterval: 00:05:00 16 | RefreshInterval: 00:30:00 17 | MaxLcmRuntime: 00:30:00 18 | LogHistoryTimeSpan: 7.00:00:00 19 | WriteTranscripts: true 20 | AutoCorrectIntervalOverride: false 21 | PSDscAllowPlainTextPassword: true 22 | PSDscAllowDomainUser: true 23 | ComputerSettings: 24 | Name: ReferenceConfigurationDev 25 | Description: 'FileServer in ' 26 | TimeZone: Greenwich Standard Time 27 | DomainName: contoso.com 28 | JoinOU: CN=Computers,dc=contoso,dc=com 29 | Credential: install@contoso:********* 30 | DscLcmMaintenanceWindows: 31 | MaintenanceWindows: 32 | Name: Always 33 | StartTime: 00:00:00 34 | Timespan: 24:00:00 35 | DependsOn: '[DscLcmController]DscLcmController' 36 | Configurations: 37 | - FileSystemObjects 38 | - RegistryValues 39 | - SecurityBase 40 | - WindowsFeatures 41 | - ComputerSettings 42 | - NetworkIpConfiguration 43 | - WindowsEventLogs 44 | - DscLcmController 45 | - DscLcmMaintenanceWindows 46 | - DscTagging 47 | - DscDiagnostic 48 | SecurityBase: 49 | DependsOn: '[WindowsFeatures]WindowsFeatures' 50 | Role: Baseline 51 | Baseline: Server 52 | NetworkIpConfiguration: 53 | Interfaces: 54 | DnsServer: 192.168.111.10 55 | Gateway: 127.0.0.1 56 | DisableNetbios: true 57 | Prefix: 24 58 | InterfaceAlias: DscWorkshop 0 59 | IpAddress: 127.0.0.1 60 | DscTagging: 61 | Environment: Dev 62 | Version: 0.3.0 63 | Layers: 64 | - AllNodes\Dev\ReferenceConfigurationDev 65 | - Environment\Dev 66 | - Locations\Frankfurt 67 | - Roles\FileServer 68 | - Baselines\Security 69 | - Baselines\Server 70 | - Baselines\DscLcm 71 | FileSystemObjects: 72 | Items: 73 | - DestinationPath: Z:\DoesNotWork 74 | Type: Directory 75 | - DestinationPath: C:\Test\Dev-Environment 76 | Type: Directory 77 | - DestinationPath: C:\Test\Frankfurt 78 | Type: Directory 79 | - DestinationPath: C:\Test 80 | Type: Directory 81 | - Contents: Some test data 82 | Type: File 83 | DependsOn: '[FileSystemObject]FileSystemObject_C__Test' 84 | DestinationPath: C:\Test\Test1File1.txt 85 | - Contents: Some test data 86 | Type: File 87 | DependsOn: '[File]file_C__Test' 88 | DestinationPath: C:\Test\Test1File2.txt 89 | - DestinationPath: C:\GpoBackup 90 | Type: Directory 91 | SourcePath: '\\DSCDC01\SYSVOL\contoso.com\Policies' 92 | NodeName: ReferenceConfigurationDev 93 | Environment: Dev 94 | Location: Frankfurt 95 | RegistryValues: 96 | DependsOn: '[FileSystemObjects]FileSystemObjects' 97 | Values: 98 | - ValueName: DBFlag 99 | ValueType: DWORD 100 | ValueData: 545325055 101 | Ensure: Present 102 | Force: true 103 | Key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters 104 | - ValueName: NtpServer 105 | ValueType: String 106 | ValueData: pool.ntp.org,0x9 107 | Ensure: Present 108 | Force: true 109 | Key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Parameters 110 | LcmConfig: 111 | ConfigurationRepositoryWeb: 112 | Server: 113 | ConfigurationNames: ReferenceConfigurationDev 114 | RegistrationKey: ec717ee9-b343-49ee-98a2-26e53939eecf 115 | ServerURL: https://dscpull01.contoso.com:8080/PSDSCPullServer.svc 116 | ReportServerWeb: 117 | RegistrationKey: ec717ee9-b343-49ee-98a2-26e53939eecf 118 | ServerURL: https://dscpull01.contoso.com:8080/PSDSCPullServer.svc 119 | Settings: 120 | RefreshFrequencyMins: 30 121 | AllowModuleOverwrite: true 122 | ActionAfterReboot: ContinueConfiguration 123 | RefreshMode: Pull 124 | RebootNodeIfNeeded: true 125 | ConfigurationMode: ApplyAndMonitor 126 | ConfigurationModeFrequencyMins: 30 127 | Role: FileServer 128 | WindowsEventLogs: 129 | Logs: 130 | - LogMode: Circular 131 | IsEnabled: true 132 | LogName: System 133 | MaximumSizeInBytes: 20971520 134 | - LogMode: Circular 135 | IsEnabled: true 136 | LogName: Application 137 | MaximumSizeInBytes: 20971520 138 | - LogMode: Circular 139 | IsEnabled: true 140 | LogName: Security 141 | MaximumSizeInBytes: 134217728 142 | DependsOn: '[ComputerSettings]ComputerSettings' 143 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/TestRsopReferences/ReferenceConfigurationProd.yml: -------------------------------------------------------------------------------- 1 | WindowsFeatures: 2 | Names: 3 | - File-Services 4 | - -Telnet-Client 5 | Description: 'FileServer in ' 6 | DscDiagnostic: {} 7 | Name: ReferenceConfigurationProd 8 | DscLcmController: 9 | MonitorInterval: 00:15:00 10 | SendDscTaggingData: true 11 | AutoCorrectInterval: 00:30:00 12 | MaintenanceWindowMode: AutoCorrect 13 | RefreshIntervalOverride: false 14 | MaintenanceWindowOverride: false 15 | ControllerInterval: 00:05:00 16 | RefreshInterval: 00:30:00 17 | MaxLcmRuntime: 00:30:00 18 | LogHistoryTimeSpan: 7.00:00:00 19 | WriteTranscripts: true 20 | AutoCorrectIntervalOverride: false 21 | PSDscAllowPlainTextPassword: true 22 | PSDscAllowDomainUser: true 23 | ComputerSettings: 24 | Name: ReferenceConfigurationProd 25 | Description: 'FileServer in ' 26 | TimeZone: Greenwich Standard Time 27 | DomainName: contoso.com 28 | JoinOU: CN=Computers,dc=contoso,dc=com 29 | Credential: install@contoso:********* 30 | DscLcmMaintenanceWindows: 31 | MaintenanceWindows: 32 | Name: Always 33 | StartTime: 00:00:00 34 | Timespan: 24:00:00 35 | DependsOn: '[DscLcmController]DscLcmController' 36 | Configurations: 37 | - FileSystemObjects 38 | - RegistryValues 39 | - SecurityBase 40 | - WindowsFeatures 41 | - ComputerSettings 42 | - NetworkIpConfiguration 43 | - WindowsEventLogs 44 | - DscLcmController 45 | - DscLcmMaintenanceWindows 46 | - DscTagging 47 | - DscDiagnostic 48 | SecurityBase: 49 | DependsOn: '[WindowsFeatures]WindowsFeatures' 50 | Role: Baseline 51 | Baseline: Server 52 | NetworkIpConfiguration: 53 | Interfaces: 54 | DnsServer: 192.168.111.10 55 | Gateway: 127.0.0.3 56 | DisableNetbios: true 57 | Prefix: 24 58 | InterfaceAlias: DscWorkshop 0 59 | IpAddress: 127.0.0.3 60 | DscTagging: 61 | Environment: Prod 62 | Version: 0.3.0 63 | Layers: 64 | - AllNodes\Prod\ReferenceConfigurationProd 65 | - Environment\Prod 66 | - Locations\Frankfurt 67 | - Roles\FileServer 68 | - Baselines\Security 69 | - Baselines\Server 70 | - Baselines\DscLcm 71 | FileSystemObjects: 72 | Items: 73 | - DestinationPath: C:\Test\Prod-Environment 74 | Type: Directory 75 | - DestinationPath: C:\Test\Frankfurt 76 | Type: Directory 77 | - DestinationPath: C:\Test 78 | Type: Directory 79 | - Contents: Some test data 80 | Type: File 81 | DependsOn: '[File]file_C__Test' 82 | DestinationPath: C:\Test\Test1File1.txt 83 | - Contents: Some test data 84 | Type: File 85 | DependsOn: '[File]file_C__Test' 86 | DestinationPath: C:\Test\Test1File2.txt 87 | - DestinationPath: C:\GpoBackup 88 | Type: Directory 89 | SourcePath: '\\DSCDC01\SYSVOL\contoso.com\Policies' 90 | NodeName: ReferenceConfigurationProd 91 | Environment: Prod 92 | Location: Frankfurt 93 | RegistryValues: 94 | DependsOn: '[FileSystemObjects]FileSystemObjects' 95 | Values: 96 | ValueName: NtpServer 97 | ValueType: String 98 | ValueData: pool.ntp.org,0x9 99 | Ensure: Present 100 | Force: true 101 | Key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Parameters 102 | LcmConfig: 103 | ConfigurationRepositoryWeb: 104 | Server: 105 | ConfigurationNames: ReferenceConfigurationProd 106 | RegistrationKey: ec717ee9-b343-49ee-98a2-26e53939eecf 107 | ServerURL: https://dscpull01.contoso.com:8080/PSDSCPullServer.svc 108 | ReportServerWeb: 109 | RegistrationKey: ec717ee9-b343-49ee-98a2-26e53939eecf 110 | ServerURL: https://dscpull01.contoso.com:8080/PSDSCPullServer.svc 111 | Settings: 112 | RefreshFrequencyMins: 30 113 | AllowModuleOverwrite: true 114 | ActionAfterReboot: ContinueConfiguration 115 | RefreshMode: Pull 116 | RebootNodeIfNeeded: true 117 | ConfigurationMode: ApplyAndMonitor 118 | ConfigurationModeFrequencyMins: 30 119 | Role: FileServer 120 | WindowsEventLogs: 121 | Logs: 122 | - LogMode: Circular 123 | IsEnabled: true 124 | LogName: System 125 | MaximumSizeInBytes: 20971520 126 | - LogMode: Circular 127 | IsEnabled: true 128 | LogName: Application 129 | MaximumSizeInBytes: 20971520 130 | - LogMode: Circular 131 | IsEnabled: true 132 | LogName: Security 133 | MaximumSizeInBytes: 134217728 134 | DependsOn: '[ComputerSettings]ComputerSettings' 135 | -------------------------------------------------------------------------------- /tests/Integration/Assets/ConfigData/TestRsopReferences/ReferenceConfigurationTest.yml: -------------------------------------------------------------------------------- 1 | WindowsFeatures: 2 | Names: 3 | - +Web-Server 4 | - -WoW64-Support 5 | - -Telnet-Client 6 | Description: 'WebServer in ' 7 | DscDiagnostic: {} 8 | Name: ReferenceConfigurationTest 9 | DscLcmController: 10 | MonitorInterval: 00:15:00 11 | SendDscTaggingData: true 12 | AutoCorrectInterval: 00:30:00 13 | MaintenanceWindowMode: AutoCorrect 14 | RefreshIntervalOverride: false 15 | MaintenanceWindowOverride: false 16 | ControllerInterval: 00:05:00 17 | RefreshInterval: 00:30:00 18 | MaxLcmRuntime: 00:30:00 19 | LogHistoryTimeSpan: 7.00:00:00 20 | WriteTranscripts: true 21 | AutoCorrectIntervalOverride: false 22 | PSDscAllowPlainTextPassword: true 23 | PSDscAllowDomainUser: true 24 | ComputerSettings: 25 | Name: ReferenceConfigurationTest 26 | Description: 'WebServer in ' 27 | TimeZone: Greenwich Standard Time 28 | DomainName: contoso.com 29 | JoinOU: CN=Computers,dc=contoso,dc=com 30 | Credential: install@contoso:********* 31 | DscLcmMaintenanceWindows: 32 | MaintenanceWindows: 33 | Name: Always 34 | StartTime: 00:00:00 35 | Timespan: 24:00:00 36 | DependsOn: '[DscLcmController]DscLcmController' 37 | Configurations: 38 | - FileSystemObjects 39 | - WindowsServices 40 | - RegistryValues 41 | - WebApplicationPools 42 | - WebApplications 43 | - SecurityBase 44 | - WindowsFeatures 45 | - ComputerSettings 46 | - NetworkIpConfiguration 47 | - WindowsEventLogs 48 | - DscLcmController 49 | - DscLcmMaintenanceWindows 50 | - DscTagging 51 | - DscDiagnostic 52 | SecurityBase: 53 | Role: WebServer 54 | Location: Singapore 55 | Baseline: Server 56 | NetworkIpConfiguration: 57 | Interfaces: 58 | DnsServer: 192.168.111.10 59 | Gateway: 127.0.0.2 60 | DisableNetbios: true 61 | Prefix: 24 62 | InterfaceAlias: DscWorkshop 0 63 | IpAddress: 127.0.0.2 64 | DscTagging: 65 | Environment: Test 66 | Version: 0.3.0 67 | Layers: 68 | - AllNodes\Test\ReferenceConfigurationTest 69 | - Environment\Test 70 | - Locations\Singapore 71 | - Roles\WebServer 72 | - Baselines\Security 73 | - Baselines\Server 74 | - Baselines\DscLcm 75 | WindowsServices: 76 | Services: 77 | Path: C:\DummyService.exe 78 | Name: FileDummy 79 | StartupType: Automatic 80 | DisplayName: Dummy Service on Web Servers 81 | DependsOn: '[RegistryValues]RegistryValues' 82 | WebApplicationPools: 83 | Items: 84 | - Name: TestAppPool1 85 | Ensure: Present 86 | IdentityType: ApplicationPoolIdentity 87 | State: Started 88 | - DependsOn: '[WebAppPool]TestAppPool1' 89 | Name: TestAppPool2 90 | Ensure: Present 91 | IdentityType: ApplicationPoolIdentity 92 | State: Started 93 | DependsOn: 94 | - '[FileSystemObjects]FileSystemObjects' 95 | - '[WindowsFeatures]WindowsFeatures' 96 | FileSystemObjects: 97 | Items: 98 | - DestinationPath: C:\Test\Test-Environment 99 | Type: Directory 100 | - DestinationPath: C:\Test\Singapore 101 | Type: Directory 102 | - DestinationPath: C:\Inetpub\TestApp1 103 | Type: Directory 104 | - DestinationPath: C:\Inetpub\TestApp2 105 | Type: Directory 106 | - Contents: This is TestApp1 107 | Type: File 108 | DependsOn: '[File]file_C__Inetpub_TestApp1' 109 | DestinationPath: C:\Inetpub\TestApp1\default.html 110 | - Contents: This is TestApp2 111 | Type: File 112 | DependsOn: '[File]file_C__Inetpub_TestApp2' 113 | DestinationPath: C:\Inetpub\TestApp2\default.html 114 | NodeName: ReferenceConfigurationTest 115 | Environment: Test 116 | WebApplications: 117 | Items: 118 | - WebAppPool: TestAppPool1 119 | Website: Default Web Site 120 | PhysicalPath: C:\Inetpub\TestApp1 121 | Ensure: Present 122 | Name: TestApp1 123 | - PhysicalPath: C:\Inetpub\TestApp2 124 | Ensure: Present 125 | Name: TestApp2 126 | Website: Default Web Site 127 | WebAppPool: TestAppPool2 128 | DependsOn: '[WebApplication]webapp_TestApp1' 129 | DependsOn: '[WebApplicationPools]WebApplicationPools' 130 | RegistryValues: 131 | DependsOn: '[WebApplications]WebApplications' 132 | Values: 133 | ValueName: NtpServer 134 | ValueType: String 135 | ValueData: time.windows.com,0x9; 136 | Ensure: Present 137 | Force: true 138 | Key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Parameters 139 | LcmConfig: 140 | ConfigurationRepositoryWeb: 141 | Server: 142 | ConfigurationNames: ReferenceConfigurationTest 143 | RegistrationKey: ec717ee9-b343-49ee-98a2-26e53939eecf 144 | ServerURL: https://dscpull01.contoso.com:8080/PSDSCPullServer.svc 145 | ReportServerWeb: 146 | RegistrationKey: ec717ee9-b343-49ee-98a2-26e53939eecf 147 | ServerURL: https://dscpull01.contoso.com:8080/PSDSCPullServer.svc 148 | Settings: 149 | RefreshFrequencyMins: 30 150 | AllowModuleOverwrite: true 151 | ActionAfterReboot: ContinueConfiguration 152 | RefreshMode: Pull 153 | RebootNodeIfNeeded: true 154 | ConfigurationMode: ApplyAndMonitor 155 | ConfigurationModeFrequencyMins: 30 156 | Role: WebServer 157 | WindowsEventLogs: 158 | Logs: 159 | - LogMode: Circular 160 | IsEnabled: true 161 | LogName: System 162 | MaximumSizeInBytes: 20971520 163 | - LogMode: Circular 164 | IsEnabled: true 165 | LogName: Application 166 | MaximumSizeInBytes: 20971520 167 | - LogMode: Circular 168 | IsEnabled: true 169 | LogName: Security 170 | MaximumSizeInBytes: 134217728 171 | DependsOn: '[ComputerSettings]ComputerSettings' 172 | -------------------------------------------------------------------------------- /tests/Integration/Public/CompileDatumRsop.tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll { 2 | 3 | Import-Module -Name datum -Force 4 | Import-Module -Name Sampler.DscPipeline -Force 5 | 6 | $datumPath = (Resolve-Path $ProjectPath\tests\Integration\Assets\ConfigData\Datum.yml).Path 7 | 8 | $datum = New-DatumStructure -DefinitionFile $datumPath 9 | $environments = $datum.AllNodes | Get-Member -MemberType ScriptProperty | Select-Object -ExpandProperty Name 10 | $allNodes = foreach ($environment in $environments) 11 | { 12 | $nodes = $datum.AllNodes.$environment | Get-Member -MemberType ScriptProperty | Select-Object -ExpandProperty Name 13 | foreach ($node in $nodes) 14 | { 15 | $datum.AllNodes.$environment.$node 16 | } 17 | } 18 | } 19 | 20 | Describe "'CompileDatumRsop' Tests" -Tags FunctionalQuality { 21 | 22 | BeforeAll { 23 | $configDataDirectory = 'tests\Integration\Assets\ConfigData' 24 | Invoke-Build -File $BuiltModuleBase\Tasks\LoadDatumConfigData.build.ps1 -DatumConfigDataDirectory $configDataDirectory 25 | 26 | $rsopPath = Join-Path -Path $OutputDirectory -ChildPath RSOP 27 | Remove-Item -Path $rsopPath -Recurse -Force -ErrorAction SilentlyContinue 28 | } 29 | 30 | AfterEach { 31 | 32 | $rsopPath = Join-Path -Path $OutputDirectory -ChildPath RSOP 33 | Remove-Item -Path $rsopPath -Recurse -Force -ErrorAction SilentlyContinue 34 | 35 | } 36 | 37 | It "'CompileDatumRsop' created same number of RSOP files as nodes in Datum" { 38 | 39 | Invoke-Build -File $BuiltModuleBase\Tasks\CompileDatumRsop.build.ps1 -Task CompileDatumRsop -DatumConfigDataDirectory $configDataDirectory 40 | 41 | $rsopPath = Join-Path -Path $OutputDirectory -ChildPath RSOP -AdditionalChildPath $ModuleVersion 42 | dir -Path $rsopPath | Should -HaveCount $allNodes.Count 43 | 44 | } 45 | 46 | It "'CompileDatumRsop' created same stores the RSOP files in the correct environment subfolder when UseEnvironment variable is set to '`$true'" { 47 | 48 | $UseEnvironment = $true 49 | Invoke-Build -File $BuiltModuleBase\Tasks\CompileDatumRsop.build.ps1 -Task CompileDatumRsop -DatumConfigDataDirectory $configDataDirectory 50 | 51 | foreach ($environment in $environments) 52 | { 53 | $rsopPath = Join-Path -Path $OutputDirectory -ChildPath RSOP -AdditionalChildPath $ModuleVersion, $environment 54 | $nodes = $allNodes | Where-Object Environment -EQ $environment 55 | dir -Path $rsopPath | Should -HaveCount $nodes.Count 56 | } 57 | 58 | Remove-Variable -Name UseEnvironment 59 | 60 | } 61 | 62 | It "'CompileDatumRsop' throws if UseEnvironment is used with a non-boolean value" { 63 | 64 | $UseEnvironment = 'somevalue' 65 | { 66 | Invoke-Build -File $BuiltModuleBase\Tasks\CompileDatumRsop.build.ps1 -Task CompileDatumRsop -DatumConfigDataDirectory $configDataDirectory 67 | } | Should -Throw 68 | 69 | Remove-Variable -Name UseEnvironment 70 | 71 | } 72 | 73 | It "'CompileDatumRsop' created same stores the RSOP files in the correct environment subfolder when UseEnvironment environment variable is set to '`$true'" { 74 | 75 | $env:UseEnvironment = $true 76 | Invoke-Build -File $BuiltModuleBase\Tasks\CompileDatumRsop.build.ps1 -Task CompileDatumRsop -DatumConfigDataDirectory $configDataDirectory 77 | 78 | foreach ($environment in $environments) 79 | { 80 | $rsopPath = Join-Path -Path $OutputDirectory -ChildPath RSOP -AdditionalChildPath $ModuleVersion, $environment 81 | $nodes = $allNodes | Where-Object Environment -EQ $environment 82 | dir -Path $rsopPath | Should -HaveCount $nodes.Count 83 | } 84 | 85 | Remove-Item -Path Env:\UseEnvironment 86 | 87 | } 88 | 89 | It "'CompileDatumRsop' created same stores the RSOP files in the correct environment subfolder when UseEnvironment environment variable is set to 'true'" { 90 | 91 | $env:UseEnvironment = 'True' 92 | Invoke-Build -File $BuiltModuleBase\Tasks\CompileDatumRsop.build.ps1 -Task CompileDatumRsop -DatumConfigDataDirectory $configDataDirectory 93 | 94 | foreach ($environment in $environments) 95 | { 96 | $rsopPath = Join-Path -Path $OutputDirectory -ChildPath RSOP -AdditionalChildPath $ModuleVersion, $environment 97 | $nodes = $allNodes | Where-Object Environment -EQ $environment 98 | dir -Path $rsopPath | Should -HaveCount $nodes.Count 99 | } 100 | 101 | Remove-Item -Path Env:\UseEnvironment 102 | 103 | } 104 | 105 | It "'CompileDatumRsop' throws if UseEnvironment is used with a non-boolean value" { 106 | 107 | $env:UseEnvironment = 'somevalue' 108 | { 109 | Invoke-Build -File $BuiltModuleBase\Tasks\CompileDatumRsop.build.ps1 -Task CompileDatumRsop -DatumConfigDataDirectory $configDataDirectory 110 | } | Should -Throw 111 | 112 | Remove-Item -Path Env:\UseEnvironment 113 | 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /tests/Integration/Public/Get-FilteredConfigurationData.tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll { 2 | 3 | Import-Module -Name Sampler.DscPipeline -Force 4 | 5 | $datumPath = (Resolve-Path $ProjectPath\tests\Integration\Assets\ConfigData\Datum.yml).Path 6 | 7 | $datum = New-DatumStructure -DefinitionFile $datumPath 8 | $environments = $datum.AllNodes | Get-Member -MemberType ScriptProperty | Select-Object -ExpandProperty Name 9 | $allNodes = foreach ($environment in $environments) 10 | { 11 | $nodes = $datum.AllNodes.$environment | Get-Member -MemberType ScriptProperty | Select-Object -ExpandProperty Name 12 | foreach ($node in $nodes) 13 | { 14 | $datum.AllNodes.$environment.$node 15 | } 16 | } 17 | } 18 | 19 | Describe "'Get-DscResourceProperty' Tests" -Tags FunctionalQuality { 20 | 21 | It "'Get-FilteredConfigurationData' returns data" { 22 | 23 | Get-FilteredConfigurationData -Datum $datum | Should -Not -BeNullOrEmpty 24 | 25 | } 26 | 27 | It "'Get-FilteredConfigurationData' should not throw an error" { 28 | 29 | { Get-FilteredConfigurationData -Datum $datum } | Should -Not -Throw 30 | 31 | } 32 | 33 | It "'Get-FilteredConfigurationData' gets the correct number of nodes when not using a filter" { 34 | 35 | $configurationData = Get-FilteredConfigurationData -Datum $datum 36 | $configurationData.AllNodes.Count | Should -Be $allNodes.Count 37 | 38 | } 39 | 40 | It "'Get-FilteredConfigurationData' gets the correct number of nodes when using a filter on the node's name" { 41 | 42 | $configurationData = Get-FilteredConfigurationData -Datum $datum -Filter { $_.Name -like 'DSCFile*' } 43 | $configurationData.AllNodes.Count | Should -Be 3 44 | 45 | } 46 | 47 | It "'Get-FilteredConfigurationData' gets the correct number of nodes when using a filter on the environment" { 48 | 49 | $configurationData = Get-FilteredConfigurationData -Datum $datum -Filter { $_.Environment -eq 'Dev' } 50 | $configurationData.AllNodes.Count | Should -Be 3 51 | 52 | } 53 | 54 | It "'Get-FilteredConfigurationData' gets no nodes if filtered on a non-existing environment" { 55 | 56 | $configurationData = Get-FilteredConfigurationData -Datum $datum -Filter { $_.Environment -eq 'DoesNotExist' } -ErrorAction SilentlyContinue 57 | $configurationData.AllNodes.Count | Should -Be 0 58 | 59 | } 60 | 61 | It "'Get-FilteredConfigurationData' with a filter on a non-existing environment should throw an error" { 62 | 63 | { Get-FilteredConfigurationData -Datum $datum -Filter { $_.Environment -eq 'DoesNotExist' } -ErrorAction Stop } | Should -Throw 64 | 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /tests/Integration/Public/LoadDatumConfigData.tests.ps1: -------------------------------------------------------------------------------- 1 | BeforeAll { 2 | 3 | Import-Module -Name datum -Force 4 | Import-Module -Name Sampler.DscPipeline -Force 5 | 6 | $datumPath = (Resolve-Path $ProjectPath\tests\Integration\Assets\ConfigData\Datum.yml).Path 7 | 8 | $datum = New-DatumStructure -DefinitionFile $datumPath 9 | $environments = $datum.AllNodes | Get-Member -MemberType ScriptProperty | Select-Object -ExpandProperty Name 10 | $allNodes = foreach ($environment in $environments) 11 | { 12 | $nodes = $datum.AllNodes.$environment | Get-Member -MemberType ScriptProperty | Select-Object -ExpandProperty Name 13 | foreach ($node in $nodes) 14 | { 15 | $datum.AllNodes.$environment.$node 16 | } 17 | } 18 | } 19 | 20 | Describe "'LoadDatumConfigData' Tests" -Tags FunctionalQuality { 21 | 22 | BeforeEach { 23 | Remove-Variable -Name configurationData -Scope Global -ErrorAction SilentlyContinue 24 | } 25 | 26 | It "'LoadDatumConfigData' should fill the variable '`$global:configurationData' when using the right path" { 27 | 28 | $configDataDirectory = 'tests\Integration\Assets\ConfigData' 29 | Invoke-Build -File $BuiltModuleBase\Tasks\LoadDatumConfigData.build.ps1 -DatumConfigDataDirectory $configDataDirectory 30 | 31 | $configurationData | Should -Not -BeNullOrEmpty 32 | 33 | } 34 | 35 | It "'LoadDatumConfigData' should throw an error when using the wrong path" { 36 | 37 | $configDataDirectory = 'tests\Integration\Assets\WrongConfigData' 38 | { Invoke-Build -File $BuiltModuleBase\Tasks\LoadDatumConfigData.build.ps1 -DatumConfigDataDirectory $configDataDirectory } | Should -Throw 39 | 40 | } 41 | 42 | It "'LoadDatumConfigData' returns the right number of nodes" { 43 | 44 | $configDataDirectory = 'tests\Integration\Assets\ConfigData' 45 | Invoke-Build -File $BuiltModuleBase\Tasks\LoadDatumConfigData.build.ps1 -DatumConfigDataDirectory $configDataDirectory 46 | 47 | $configurationData.AllNodes.Count | Should -Be $allNodes.Count 48 | 49 | } 50 | 51 | } 52 | --------------------------------------------------------------------------------