├── PlasterTemplates └── PSModuleProjectMB │ ├── README.md │ ├── docs │ └── index.md │ ├── header-mkdocs.yml │ ├── Module │ └── Module.psm1 │ ├── appveyor.yml │ ├── LICENSE.md │ ├── Tests │ └── Module.Tests.ps1 │ ├── .github │ ├── ISSUE_TEMPLATE.md │ ├── PULL_REQUEST_TEMPLATE.md │ └── CONTRIBUTING.md │ ├── BuildSettings.ps1 │ ├── PlasterManifest.xml │ ├── .gitignore │ └── build.ps1 ├── CustomPSScriptAnalyzerRules ├── ExampleScript.ps1 ├── Tests │ └── Integration │ │ └── MBAnalyzerRules.Tests.ps1 └── MBAnalyzerRules.psm1 ├── Merge-DscConfigData ├── Merge-DscConfigData.psd1 ├── Tests │ └── Unit │ │ ├── Merge-DscConfigData-NonNodeData.Tests.ps1 │ │ └── Merge-DscConfigData.Tests.ps1 └── Merge-DscConfigData.psm1 ├── README.md ├── .gitignore ├── LICENSE ├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── CONTRIBUTING.md ├── Export-NUnitXml ├── ExampleOutputSuccess.xml ├── ExampleOutputFailures.xml └── Export-NUnitXml.psm1 ├── appveyor.yml ├── DSC └── Testing │ └── Boilerplate.Tests.ps1 ├── Microsoft.PowerShell_profile.ps1 └── VisualStudioCode └── PowerShell.json /PlasterTemplates/PSModuleProjectMB/README.md: -------------------------------------------------------------------------------- 1 | # <%= $PLASTER_PARAM_GitHubRepo %> 2 | 3 | PowerShell module which 4 | 5 | -------------------------------------------------------------------------------- /PlasterTemplates/PSModuleProjectMB/docs/index.md: -------------------------------------------------------------------------------- 1 | # <%= $PLASTER_PARAM_GitHubRepo %> 2 | 3 | This PowerShell project 4 | 5 | -------------------------------------------------------------------------------- /CustomPSScriptAnalyzerRules/ExampleScript.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MathieuBuisson/PowerShell-DevOps/HEAD/CustomPSScriptAnalyzerRules/ExampleScript.ps1 -------------------------------------------------------------------------------- /Merge-DscConfigData/Merge-DscConfigData.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MathieuBuisson/PowerShell-DevOps/HEAD/Merge-DscConfigData/Merge-DscConfigData.psd1 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerShell-DevOps 2 | PowerShell scripts or modules for purposes related to DevOps practices 3 | 4 | [![Build status](https://ci.appveyor.com/api/projects/status/b9c8pi933yir8ex6/branch/master?svg=true)](https://ci.appveyor.com/project/MathieuBuisson/powershell-devops/branch/master) -------------------------------------------------------------------------------- /PlasterTemplates/PSModuleProjectMB/header-mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: <%= $PLASTER_PARAM_ModuleName %> 2 | repo_url: https://github.com/MathieuBuisson/<%= $PLASTER_PARAM_GitHubRepo %> 3 | site_author: <%= $PLASTER_PARAM_FullName %> 4 | edit_uri: edit/master/docs/ 5 | theme: readthedocs 6 | copyright: "<%= $PLASTER_PARAM_GitHubRepo %> is licensed under the MIT license" 7 | pages: 8 | - Home: index.md 9 | -------------------------------------------------------------------------------- /PlasterTemplates/PSModuleProjectMB/Module/Module.psm1: -------------------------------------------------------------------------------- 1 | #Get public and private function definition files. 2 | $Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue ) 3 | $Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue ) 4 | 5 | Foreach ( $Import in @($Public + $Private) ) { 6 | Try { 7 | . $Import.FullName 8 | } 9 | Catch { 10 | Write-Error -Message "Failed to import function $($Import.FullName): $_" 11 | } 12 | } 13 | 14 | Export-ModuleMember -Function $Public.Basename -------------------------------------------------------------------------------- /PlasterTemplates/PSModuleProjectMB/appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.1.{build} 2 | 3 | os: WMF 5 4 | 5 | environment: 6 | Coveralls_Key: 7 | secure: 8 | GitHub_Key: 9 | secure: wtrwAJK+i7Ar5L8TXeXOUtsxmVD+2wXu9u9bOf6GRfPP0Xn2V4yqTatLwaT7VWA6 10 | 11 | before_build: 12 | - ps: Write-Host "Build version :` $env:APPVEYOR_BUILD_VERSION" 13 | - ps: Write-Host "Branch :` $env:APPVEYOR_REPO_BRANCH" 14 | - ps: Install-PackageProvider -Name NuGet -Force 15 | - ps: Set-PSRepository -Name PSGallery -InstallationPolicy Trusted 16 | - ps: Install-Module InvokeBuild -Scope CurrentUser -AllowClobber -Force 17 | - ps: Import-Module InvokeBuild 18 | 19 | build_script: 20 | - ps: Invoke-Build 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | # Build results 9 | [Dd]ebug/ 10 | [Dd]ebugPublic/ 11 | [Rr]elease/ 12 | 13 | # Visual Studio 2015 cache/options directory 14 | .vs/ 15 | 16 | #NUNIT 17 | *.VisualState.xml 18 | TestResult.xml 19 | 20 | # Visual Studio profiler 21 | *.psess 22 | *.vsp 23 | *.vspx 24 | # TFS 2012 Local Workspace 25 | $tf/ 26 | 27 | # ReSharper is a .NET coding add-in 28 | _ReSharper*/ 29 | 30 | # TeamCity is a build add-in 31 | _TeamCity* 32 | 33 | # Folders created by Visual Studio Code 34 | *.vscode/ 35 | 36 | # Backup files created by Notepad++ and other applications 37 | *.orig 38 | 39 | # Windows image file caches 40 | Thumbs.db 41 | ehthumbs.db 42 | -------------------------------------------------------------------------------- /CustomPSScriptAnalyzerRules/Tests/Integration/MBAnalyzerRules.Tests.ps1: -------------------------------------------------------------------------------- 1 | 2 | Describe "Testing rule Measure-PascalCase against ExampleScript.ps1" { 3 | 4 | $ExampleResults = Invoke-ScriptAnalyzer -Path "$($PSScriptRoot)\..\..\ExampleScript.ps1" -CustomRulePath "$($PSScriptRoot)\..\..\MBAnalyzerRules.psm1" 5 | 6 | $VariableNames = $ExampleResults.Extent.Text | ForEach-Object { ($_ -split ' = ')[0].Trim('$') } 7 | $TestCases = @( 8 | @{ ExpectedViolation = 'statusUrl' }, 9 | @{ ExpectedViolation = 'errorUrl' }, 10 | @{ ExpectedViolation = 'NAMESPACE' }, 11 | @{ ExpectedViolation = 'uwfInstance' }, 12 | @{ ExpectedViolation = 'UWFEnabled' } 13 | ) 14 | 15 | It "Detected violations should contain " -TestCases $TestCases { 16 | Param($ExpectedViolation) 17 | $VariableNames -contains $ExpectedViolation | Should Be $True 18 | } 19 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Mathieu BUISSON 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 | -------------------------------------------------------------------------------- /PlasterTemplates/PSModuleProjectMB/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 <%= $PLASTER_PARAM_FullName %> 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 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Expected Behavior 4 | 5 | 6 | 7 | ## Current Behavior 8 | 9 | 10 | 11 | ## Possible Solution 12 | 13 | 14 | 15 | ## Steps to Reproduce (for bugs) 16 | 17 | 18 | 1. 19 | 2. 20 | 3. 21 | 4. 22 | 23 | ## Context 24 | 25 | 26 | 27 | ## Your Environment 28 | 29 | * Module version used: 30 | * Operating System and PowerShell version: -------------------------------------------------------------------------------- /PlasterTemplates/PSModuleProjectMB/Tests/Module.Tests.ps1: -------------------------------------------------------------------------------- 1 | $ModuleName = '<%= $PLASTER_PARAM_ModuleName %>' 2 | Import-Module "$PSScriptRoot\..\..\..\$ModuleName\$($ModuleName).psd1" -Force 3 | 4 | Describe 'General Module behaviour' { 5 | 6 | $ModuleInfo = Get-Module -Name $ModuleName 7 | $ManifestPath = '{0}\{1}.psd1' -f $ModuleInfo.ModuleBase, $ModuleName 8 | 9 | It 'The required modules should be ' { 10 | 11 | Foreach ( $RequiredModule in $ModuleInfo.RequiredModules.Name ) { 12 | $RequiredModule -in @() | Should Be $True 13 | } 14 | } 15 | It 'Has a valid manifest' { 16 | { Test-ModuleManifest -Path $ManifestPath -ErrorAction Stop } | 17 | Should Not Throw 18 | } 19 | It 'Has a valid root module' { 20 | $ModuleInfo.RootModule -like '*{0}.psm1' -f $ModuleName | 21 | Should Be $True 22 | } 23 | It 'Exports all public functions' { 24 | $ExpectedFunctions = (Get-ChildItem -Path "$PSScriptRoot\..\..\..\$ModuleName\Public" -File).BaseName 25 | $ExportedFunctions = $ModuleInfo.ExportedFunctions.Values.Name 26 | 27 | Foreach ( $FunctionName in $ExpectedFunctions ) { 28 | $ExportedFunctions -contains $FunctionName | Should Be $True 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /PlasterTemplates/PSModuleProjectMB/.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Expected Behavior 4 | 5 | 6 | 7 | ## Current Behavior 8 | 9 | 10 | 11 | ## Possible Solution 12 | 13 | 14 | 15 | ## Steps to Reproduce (for bugs) 16 | 17 | 18 | 1. 19 | 2. 20 | 3. 21 | 4. 22 | 23 | ## Context 24 | 25 | 26 | 27 | ## Your Environment 28 | 29 | * Module version used: 30 | * Operating System and PowerShell version: -------------------------------------------------------------------------------- /Export-NUnitXml/ExampleOutputSuccess.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | 6 | ## Related Issue 7 | 8 | 9 | 10 | 11 | 12 | ## Motivation and Context 13 | 14 | 15 | ## How Has This Been Tested? 16 | 17 | 18 | 19 | 20 | ## Screenshots (if appropriate): 21 | 22 | ## Types of changes 23 | 24 | - [ ] Bug fix (non-breaking change which fixes an issue) 25 | - [ ] New feature (non-breaking change which adds functionality) 26 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 27 | 28 | ## Checklist: 29 | 30 | 31 | - [ ] My code follows the code style of this project. 32 | - [ ] My change requires a change to the documentation. 33 | - [ ] I have updated the documentation accordingly. 34 | - [ ] I have read the **CONTRIBUTING** document. 35 | - [ ] I have added tests to cover my changes. 36 | - [ ] All new and existing tests passed. -------------------------------------------------------------------------------- /PlasterTemplates/PSModuleProjectMB/.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | 6 | ## Related Issue 7 | 8 | 9 | 10 | 11 | 12 | ## Motivation and Context 13 | 14 | 15 | ## How Has This Been Tested? 16 | 17 | 18 | 19 | 20 | ## Screenshots (if appropriate): 21 | 22 | ## Types of changes 23 | 24 | - [ ] Bug fix (non-breaking change which fixes an issue) 25 | - [ ] New feature (non-breaking change which adds functionality) 26 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 27 | 28 | ## Checklist: 29 | 30 | 31 | - [ ] My code follows the code style of this project. 32 | - [ ] My change requires a change to the documentation. 33 | - [ ] I have updated the documentation accordingly. 34 | - [ ] I have read the **CONTRIBUTING** document. 35 | - [ ] I have added tests to cover my changes. 36 | - [ ] All new and existing tests passed. -------------------------------------------------------------------------------- /PlasterTemplates/PSModuleProjectMB/BuildSettings.ps1: -------------------------------------------------------------------------------- 1 | # This file stores variables which are used by the build script 2 | 3 | # Storing all values in a single $Settings variable to make it obvious that the values are coming from this BuildSettings file when accessing them. 4 | $Settings = @{ 5 | 6 | BuildOutput = "$PSScriptRoot\BuildOutput" 7 | Dependency = @('Coveralls','Pester','PsScriptAnalyzer','platyPS') 8 | SourceFolder = "$PSScriptRoot\<%= $PLASTER_PARAM_ModuleName %>" 9 | TestUploadUrl = "https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)" 10 | CoverallsKey = $env:Coveralls_Key 11 | Branch = $env:APPVEYOR_REPO_BRANCH 12 | 13 | UnitTestParams = @{ 14 | Script = '.\Tests\Unit' 15 | CodeCoverage = '.\<%= $PLASTER_PARAM_ModuleName %>\P*\*' 16 | OutputFile = "$PSScriptRoot\BuildOutput\UnitTestsResult.xml" 17 | PassThru = $True 18 | } 19 | 20 | IntegrationTestParams = @{ 21 | Script = '.\Tests\Integration' 22 | OutputFile = "$PSScriptRoot\BuildOutput\IntegrationTestsResult.xml" 23 | PassThru = $True 24 | } 25 | 26 | AnalyzeParams = @{ 27 | Path = "$PSScriptRoot\$($env:APPVEYOR_PROJECT_NAME)" 28 | Severity = 'Error' 29 | Recurse = $True 30 | } 31 | 32 | IsPullRequest = ($env:APPVEYOR_PULL_REQUEST_NUMBER -gt 0) 33 | Version = $env:APPVEYOR_BUILD_VERSION 34 | ManifestPath = '{0}\{1}\{1}.psd1' -f $PSScriptRoot, $env:APPVEYOR_PROJECT_NAME 35 | VersionRegex = "ModuleVersion\s=\s'(?\S+)'" -as [regex] 36 | 37 | ModuleName = $env:APPVEYOR_PROJECT_NAME 38 | HeaderPath = "$PSScriptRoot\header-mkdocs.yml" 39 | MkdocsPath = "$PSScriptRoot\mkdocs.yml" 40 | FunctionDocsPath = "$PSScriptRoot\docs\Functions" 41 | PlatyPSParams = @{ 42 | Module = $env:APPVEYOR_PROJECT_NAME 43 | OutputFolder = "$PSScriptRoot\docs\Functions" 44 | NoMetadata = $True 45 | Force = $True 46 | } 47 | 48 | GitHubKey = $env:GitHub_Key 49 | Email = 'MathieuBuisson@users.noreply.github.com' 50 | Name = '<%= $PLASTER_PARAM_FullName %>' 51 | } -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | 3 | os: WMF 5 4 | 5 | # Skip on updates to the readme 6 | skip_commits: 7 | message: /readme*/ 8 | 9 | install: 10 | - ps: Write-Host "Build version :` $env:APPVEYOR_BUILD_VERSION" 11 | - ps: Write-Host "Branch :` $env:APPVEYOR_REPO_BRANCH" 12 | - ps: Install-PackageProvider -Name NuGet -Force 13 | - ps: Install-Module -Name Pester -Force 14 | - ps: Install-Module PsScriptAnalyzer -Force 15 | 16 | build: false 17 | 18 | test_script: 19 | - ps: | 20 | $UnitTestsOutput = Invoke-Pester -Script ".\Merge-DscConfigData" -OutputFile TestsResults.xml -OutputFormat NUnitXml -PassThru 21 | (New-Object 'System.Net.WebClient').UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\TestsResults.xml)) 22 | $FailedCount = $UnitTestsOutput.FailedCount 23 | If ($FailedCount -gt 0) { 24 | Add-AppveyorMessage -Message "$($FailedCount) unit test(s) failed. Check the test results for more information." 25 | 26 | # Failing the build 27 | Throw "Build failed because $($FailedCount) unit test(s) failed. Check the test results for more information." 28 | } 29 | 30 | - ps: | 31 | Import-Module -Name 'PsScriptAnalyzer' -Force 32 | $ScriptAnalyzerRules = Get-ScriptAnalyzerRule -Severity Error 33 | $ScriptAnalyzerResult = Invoke-ScriptAnalyzer -Path $pwd -IncludeRule $ScriptAnalyzerRules -Recurse 34 | If ( $ScriptAnalyzerResult ) { 35 | 36 | $ScriptAnalyzerResultString = $ScriptAnalyzerResult | Out-String 37 | Write-Warning $ScriptAnalyzerResultString 38 | } 39 | Import-Module ".\Export-NUnitXml\Export-NUnitXml.psm1" -Force 40 | Export-NUnitXml -ScriptAnalyzerResult $ScriptAnalyzerResult -Path ".\ScriptAnalyzerResult.xml" 41 | 42 | (New-Object 'System.Net.WebClient').UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\ScriptAnalyzerResult.xml)) 43 | 44 | If ( $ScriptAnalyzerResult ) { 45 | # Failing the build 46 | Throw 'Build failed because there was one or more PSScriptAnalyzer violation. See test results for more information.' 47 | } 48 | 49 | -------------------------------------------------------------------------------- /DSC/Testing/Boilerplate.Tests.ps1: -------------------------------------------------------------------------------- 1 | $Global:DSCResourceName = 'My_DSCResource' #<----- Just change this 2 | 3 | Import-Module "$($PSScriptRoot)\..\..\DSCResources\$($Global:DSCResourceName)\$($Global:DSCResourceName).psm1" -Force 4 | 5 | # Helper function to list the names of mandatory parameters of *-TargetResource functions 6 | Function Get-MandatoryParameter { 7 | [CmdletBinding()] 8 | Param( 9 | [Parameter(Mandatory=$True)] 10 | [string]$CommandName 11 | ) 12 | $GetCommandData = Get-Command "$($Global:DSCResourceName)\$CommandName" 13 | $MandatoryParameters = $GetCommandData.Parameters.Values | Where-Object { $_.Attributes.Mandatory -eq $True } 14 | return $MandatoryParameters.Name 15 | } 16 | 17 | # Getting the names of mandatory parameters for each *-TargetResource function 18 | $GetMandatoryParameter = Get-MandatoryParameter -CommandName "Get-TargetResource" 19 | $TestMandatoryParameter = Get-MandatoryParameter -CommandName "Test-TargetResource" 20 | $SetMandatoryParameter = Get-MandatoryParameter -CommandName "Set-TargetResource" 21 | 22 | # Splatting parameters values for Get, Test and Set-TargetResource functions 23 | $GetParams = @{ 24 | 25 | } 26 | $TestParams = @{ 27 | 28 | } 29 | $SetParams = @{ 30 | 31 | } 32 | 33 | Describe "$($Global:DSCResourceName)\Get-TargetResource" { 34 | 35 | $GetReturn = & "$($Global:DSCResourceName)\Get-TargetResource" @GetParams 36 | 37 | It "Should return a hashtable" { 38 | $GetReturn | Should BeOfType System.Collections.Hashtable 39 | } 40 | Foreach ($MandatoryParameter in $GetMandatoryParameter) { 41 | 42 | It "Should return a hashtable with key named $MandatoryParameter" { 43 | $GetReturn.ContainsKey($MandatoryParameter) | Should Be $True 44 | } 45 | } 46 | } 47 | 48 | Describe "$($Global:DSCResourceName)\Test-TargetResource" { 49 | 50 | $TestReturn = & "$($Global:DSCResourceName)\Test-TargetResource" @TestParams 51 | 52 | It "Should have the same mandatory parameters as Get-TargetResource" { 53 | # Does not check for $True or $False but uses the output of Compare-Object. 54 | # That way, if this test fails Pester will show us the actual difference(s). 55 | (Compare-Object $GetMandatoryParameter $TestMandatoryParameter).InputObject | Should Be $Null 56 | } 57 | It "Should return a boolean" { 58 | $TestReturn | Should BeOfType System.Boolean 59 | } 60 | } 61 | 62 | Describe "$($Global:DSCResourceName)\Set-TargetResource" { 63 | 64 | $SetReturn = & "$($Global:DSCResourceName)\Set-TargetResource" @SetParams 65 | 66 | It "Should have the same mandatory parameters as Test-TargetResource" { 67 | (Compare-Object $TestMandatoryParameter $SetMandatoryParameter).InputObject | Should Be $Null 68 | } 69 | It "Should not return anything" { 70 | $SetReturn | Should Be $Null 71 | } 72 | } -------------------------------------------------------------------------------- /PlasterTemplates/PSModuleProjectMB/PlasterManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | PSModuleProjectMB 6 | 0d01c1b8-ca0c-48cd-9322-920dad8dbbb7 7 | 0.0.1 8 | Template for PowerShell module project 9 | 10 | Mathieu Buisson 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Creating folder structure 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Deploying common files 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | Contributions to PowerShell-DevOps are highly encouraged and desired. Below are some guidelines that will help make the process as smooth as possible. 3 | 4 | # Getting Started 5 | * Make sure you have a [GitHub account](https://github.com/signup/free) 6 | * Submit a new issue, assuming one does not already exist. 7 | * Clearly describe the issue including steps to reproduce when it is a bug. 8 | * Make sure you fill in the earliest version that you know has the issue. 9 | * Fork the repository on GitHub 10 | 11 | # Suggesting Enhancements 12 | I want to know what you think is missing from PowerShell-DevOps and how it can be made better. 13 | * When submitting an issue for an enhancement, please be as clear as possible about why you think the enhancement is needed and what the benefit of 14 | it would be. 15 | 16 | # Making Changes 17 | * From your fork of the repository, create a topic/feature branch where work on your change will take place. 18 | * To quickly create a topic/feature branch based on master; `git checkout -b my_topic_branch master`. Please avoid working directly on the `master` branch. 19 | * Make commits of logical units. 20 | * Check for unnecessary whitespace with `git diff --check` before committing. 21 | * Please follow the prevailing code conventions in the repository. Differences in style make the code harder to understand for everyone. 22 | * Make sure your commit messages are in the proper format. 23 | ```` 24 | Add more cowbell to Get-Something.ps1 25 | 26 | The functionaly of Get-Something would be greatly improved if there was a little 27 | more 'pizzazz' added to it. I propose a cowbell. Adding more cowbell has been 28 | shown in studies to both increase one's mojo, and cement one's status 29 | as a rock legend. 30 | ```` 31 | 32 | * Make sure you have added all the necessary Pester tests for your changes. 33 | * Run the Pester tests in the module and make sure they _all_ pass, to verify that your changes have not broken anything. 34 | 35 | # Documentation 36 | I am infallible and as such my documenation needs no corectoin. In the highly 37 | unlikely event that it is _not_ the case, contributions to update or add documentation 38 | are highly appreciated. 39 | 40 | # Submitting Changes 41 | * Push your changes to a topic/feature branch in your fork of the repository. 42 | * Submit a pull request to the main repository. 43 | * Once the pull request has been reviewed and accepted, it will be merged with the master branch in the main repository. 44 | * Be proud of your awesomeness and celebrate. 45 | 46 | # Additional Resources 47 | * [General GitHub documentation](https://help.github.com/) 48 | * [GitHub forking documentation](https://guides.github.com/activities/forking/) 49 | * [GitHub pull request documentation](https://help.github.com/send-pull-requests/) 50 | * [GitHub Flow guide](https://guides.github.com/introduction/flow/) 51 | * [GitHub's guide to contributing to open source projects](https://guides.github.com/activities/contributing-to-open-source/) -------------------------------------------------------------------------------- /CustomPSScriptAnalyzerRules/MBAnalyzerRules.psm1: -------------------------------------------------------------------------------- 1 | #Requires -Version 3.0 2 | Function Measure-PascalCase { 3 | <# 4 | .SYNOPSIS 5 | The variables names should be in PascalCase. 6 | 7 | .DESCRIPTION 8 | Variable names should use a consistent capitalization style, i.e. : PascalCase. 9 | In PascalCase, only the first letter is capitalized. Or, if the variable name is made of multiple concatenated words, only the first letter of each concatenated word is capitalized. 10 | To fix a violation of this rule, please consider using PascalCase for variable names. 11 | 12 | .EXAMPLE 13 | Measure-PascalCase -ScriptBlockAst $ScriptBlockAst 14 | 15 | .INPUTS 16 | [System.Management.Automation.Language.ScriptBlockAst] 17 | 18 | .OUTPUTS 19 | [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]] 20 | 21 | .NOTES 22 | https://msdn.microsoft.com/en-us/library/dd878270(v=vs.85).aspx 23 | https://msdn.microsoft.com/en-us/library/ms229043(v=vs.110).aspx 24 | #> 25 | 26 | [CmdletBinding()] 27 | [OutputType([Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]])] 28 | Param 29 | ( 30 | [Parameter(Mandatory = $True)] 31 | [ValidateNotNullOrEmpty()] 32 | [System.Management.Automation.Language.ScriptBlockAst] 33 | $ScriptBlockAst 34 | ) 35 | 36 | Process { 37 | 38 | $Results = @() 39 | 40 | try { 41 | #region Define predicates to find ASTs. 42 | 43 | [ScriptBlock]$Predicate = { 44 | Param ([System.Management.Automation.Language.Ast]$Ast) 45 | 46 | [bool]$ReturnValue = $False 47 | If ($Ast -is [System.Management.Automation.Language.AssignmentStatementAst]) { 48 | 49 | [System.Management.Automation.Language.AssignmentStatementAst]$VariableAst = $Ast 50 | If ($VariableAst.Left.VariablePath.UserPath -cnotmatch '^([A-Z][a-z]+)+$') { 51 | $ReturnValue = $True 52 | } 53 | } 54 | return $ReturnValue 55 | } 56 | #endregion 57 | 58 | #region Finds ASTs that match the predicates. 59 | [System.Management.Automation.Language.Ast[]]$Violations = $ScriptBlockAst.FindAll($Predicate, $True) 60 | 61 | If ($Violations.Count -ne 0) { 62 | 63 | Foreach ($Violation in $Violations) { 64 | 65 | $Result = New-Object ` 66 | -Typename "Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord" ` 67 | -ArgumentList "$((Get-Help $MyInvocation.MyCommand.Name).Description.Text)",$Violation.Extent,$PSCmdlet.MyInvocation.InvocationName,Information,$Null 68 | 69 | $Results += $Result 70 | } 71 | } 72 | return $Results 73 | #endregion 74 | } 75 | catch { 76 | $PSCmdlet.ThrowTerminatingError($_) 77 | } 78 | } 79 | } 80 | Export-ModuleMember -Function Measure-* -------------------------------------------------------------------------------- /PlasterTemplates/PSModuleProjectMB/.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | Contributions to <%= $PLASTER_PARAM_GitHubRepo %> are highly encouraged and desired. Below are some guidelines that will help make the process as smooth as possible. 3 | 4 | # Getting Started 5 | * Make sure you have a [GitHub account](https://github.com/signup/free) 6 | * Submit a new issue, assuming one does not already exist. 7 | * Clearly describe the issue including steps to reproduce when it is a bug. 8 | * Make sure you fill in the earliest version that you know has the issue. 9 | * Fork the repository on GitHub 10 | 11 | # Suggesting Enhancements 12 | I want to know what you think is missing from <%= $PLASTER_PARAM_GitHubRepo %> and how it can be made better. 13 | * When submitting an issue for an enhancement, please be as clear as possible about why you think the enhancement is needed and what the benefit of 14 | it would be. 15 | 16 | # Making Changes 17 | * From your fork of the repository, create a topic/feature branch where work on your change will take place. 18 | * To quickly create a topic/feature branch based on master; `git checkout -b my_topic_branch master`. Please avoid working directly on the `master` branch. 19 | * Make commits of logical units. 20 | * Check for unnecessary whitespace with `git diff --check` before committing. 21 | * Please follow the prevailing code conventions in the repository. Differences in style make the code harder to understand for everyone. 22 | * Make sure your commit messages are in the proper format. 23 | ```` 24 | Add more cowbell to Get-Something.ps1 25 | 26 | The functionaly of Get-Something would be greatly improved if there was a little 27 | more 'pizzazz' added to it. I propose a cowbell. Adding more cowbell has been 28 | shown in studies to both increase one's mojo, and cement one's status 29 | as a rock legend. 30 | ```` 31 | 32 | * Make sure you have added all the necessary Pester tests for your changes. 33 | * Run the Pester tests in the module and make sure they _all_ pass, to verify that your changes have not broken anything. 34 | 35 | # Documentation 36 | I am infallible and as such my documenation needs no corectoin. In the highly 37 | unlikely event that it is _not_ the case, contributions to update or add documentation 38 | are highly appreciated. 39 | 40 | # Submitting Changes 41 | * Push your changes to a topic/feature branch in your fork of the repository. 42 | * Submit a pull request to the main repository. 43 | * Once the pull request has been reviewed and accepted, it will be merged with the master branch in the main repository. 44 | * Be proud of your awesomeness and celebrate. 45 | 46 | # Additional Resources 47 | * [General GitHub documentation](https://help.github.com/) 48 | * [GitHub forking documentation](https://guides.github.com/activities/forking/) 49 | * [GitHub pull request documentation](https://help.github.com/send-pull-requests/) 50 | * [GitHub Flow guide](https://guides.github.com/introduction/flow/) 51 | * [GitHub's guide to contributing to open source projects](https://guides.github.com/activities/contributing-to-open-source/) -------------------------------------------------------------------------------- /Export-NUnitXml/ExampleOutputFailures.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | The variable 'ScriptFolder' is assigned but never used. 12 | at line: 49 in C:\GitHub\PowerShell-DevOps\CustomPSScriptAnalyzerRules\ExampleScript.ps1 13 | 49: $ScriptFolder 14 | Rule severity : Warning 15 | 16 | 17 | 18 | 19 | 20 | The variable 'UWFEnabled' is assigned but never used. 21 | at line: 117 in C:\GitHub\PowerShell-DevOps\CustomPSScriptAnalyzerRules\ExampleScript.ps1 22 | 117: $UWFEnabled 23 | Rule severity : Warning 24 | 25 | 26 | 27 | 28 | 29 | File 'ExampleScript.ps1' uses WMI cmdlet. For PowerShell 3.0 and above, use CIM cmdlet which perform the same tasks as the WMI cmdlets. The CIM cmdlets comply with WS-Management (WSMan) standards and with the Common Information Model (CIM) standard, which enables the cmdlets to use the same techniques to manage Windows computers and those running other operating systems. 30 | at line: 88 in C:\GitHub\PowerShell-DevOps\CustomPSScriptAnalyzerRules\ExampleScript.ps1 31 | 88: Get-WmiObject -Namespace $NAMESPACE -Class UWF_Filter -List 32 | Rule severity : Warning 33 | 34 | 35 | 36 | 37 | 38 | File 'ExampleScript.ps1' uses WMI cmdlet. For PowerShell 3.0 and above, use CIM cmdlet which perform the same tasks as the WMI cmdlets. The CIM cmdlets comply with WS-Management (WSMan) standards and with the Common Information Model (CIM) standard, which enables the cmdlets to use the same techniques to manage Windows computers and those running other operating systems. 39 | at line: 94 in C:\GitHub\PowerShell-DevOps\CustomPSScriptAnalyzerRules\ExampleScript.ps1 40 | 94: Get-WmiObject -Namespace $NAMESPACE -Class UWF_Filter 41 | Rule severity : Warning 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Microsoft.PowerShell_profile.ps1: -------------------------------------------------------------------------------- 1 | ####################### Importing the PSReadline module 2 | Import-Module PSReadline 3 | 4 | function prompt { 5 | $origLastExitCode = $LastExitCode 6 | $curPath = $ExecutionContext.SessionState.Path.CurrentLocation.Path 7 | if ($curPath.ToLower().StartsWith($Home.ToLower())) { 8 | $curPath = "~" + $curPath.SubString($Home.Length) 9 | } 10 | Write-Host $curPath -NoNewline -ForegroundColor Black -BackgroundColor DarkGreen 11 | Write-Host "$([char]57520) " -NoNewline -ForegroundColor DarkGreen 12 | 13 | Write-VcsStatus 14 | "`n$('>' * ($nestedPromptLevel + 1)) " 15 | $LastExitCode = $origLastExitCode 16 | } 17 | 18 | ####################### Posh-Git 19 | Import-Module -Name 'posh-git' 20 | 21 | # Background colors 22 | $baseBackgroundColor = 'DarkBlue' 23 | $GitPromptSettings.AfterStashBackgroundColor = $baseBackgroundColor 24 | $GitPromptSettings.BeforeBackgroundColor = $baseBackgroundColor 25 | $GitPromptSettings.BeforeIndexBackgroundColor = $baseBackgroundColor 26 | $GitPromptSettings.BeforeStashBackgroundColor = $baseBackgroundColor 27 | $GitPromptSettings.BranchAheadStatusBackgroundColor = $baseBackgroundColor 28 | $GitPromptSettings.BranchBackgroundColor = $baseBackgroundColor 29 | $GitPromptSettings.BranchBehindAndAheadStatusBackgroundColor = $baseBackgroundColor 30 | $GitPromptSettings.BranchBehindStatusBackgroundColor = $baseBackgroundColor 31 | $GitPromptSettings.BranchGoneStatusBackgroundColor = $baseBackgroundColor 32 | $GitPromptSettings.BranchIdenticalStatusToBackgroundColor = $baseBackgroundColor 33 | $GitPromptSettings.DelimBackgroundColor = $baseBackgroundColor 34 | $GitPromptSettings.IndexBackgroundColor = $baseBackgroundColor 35 | $GitPromptSettings.ErrorBackgroundColor = $baseBackgroundColor 36 | $GitPromptSettings.LocalDefaultStatusBackgroundColor = $baseBackgroundColor 37 | $GitPromptSettings.LocalStagedStatusBackgroundColor = $baseBackgroundColor 38 | $GitPromptSettings.LocalWorkingStatusBackgroundColor = $baseBackgroundColor 39 | $GitPromptSettings.StashBackgroundColor = $baseBackgroundColor 40 | $GitPromptSettings.WorkingBackgroundColor = $baseBackgroundColor 41 | 42 | # Foreground colors 43 | $GitPromptSettings.AfterForegroundColor = $baseBackgroundColor 44 | $GitPromptSettings.BeforeForegroundColor = "Black" 45 | $GitPromptSettings.BranchForegroundColor = "Blue" 46 | $GitPromptSettings.BranchGoneStatusForegroundColor = "Blue" 47 | $GitPromptSettings.BranchIdenticalStatusToForegroundColor = "DarkYellow" 48 | $GitPromptSettings.DefaultForegroundColor = "Gray" 49 | $GitPromptSettings.DelimForegroundColor = "Blue" 50 | $GitPromptSettings.IndexForegroundColor = "Green" 51 | #$GitPromptSettings.WorkingForegroundColor = "Yellow" 52 | 53 | # Prompt shape 54 | $GitPromptSettings.BeforeText = "$([char]57520)" 55 | $GitPromptSettings.AfterText = "$([char]57520) " 56 | $GitPromptSettings.DelimText = " ॥" 57 | $GitPromptSettings.ShowStatusWhenZero = $False 58 | 59 | function Get-FullHelp { 60 | 61 | [cmdletBinding()] 62 | Param( 63 | [Parameter(Mandatory = $true)] 64 | [string]$cmd 65 | ) 66 | 67 | If ((Get-Command -Name $cmd).CommandType -eq "Cmdlet" ) { 68 | $cmdlet = (Get-Command -Name $cmd).Name 69 | } 70 | Elseif ((Get-Command -Name $cmd).CommandType -eq "Function" ) { 71 | $cmdlet = (Get-Command -Name $cmd).Name 72 | } 73 | Elseif ((Get-Command -Name $cmd).CommandType -eq "Alias" ) { 74 | 75 | # Resolving aliases to cmdlet names 76 | $cmdlet = (Get-Command -Name $cmd).Definition 77 | } 78 | Else { 79 | # No support for other command types, like workflows, external commands and external scripts 80 | throw "Could not resolve $cmd to a valid Cmdlet name. Exiting the function." 81 | } # End conditional statements 82 | 83 | Get-Help $cmdlet -Full | more 84 | 85 | } 86 | 87 | ######################### Setting an alias for the function Get-FullHelp 88 | New-Alias -Name gfh -Value Get-FullHelp 89 | 90 | ######################## Aliases for common git commands 91 | function gpo { 92 | & git push origin master 93 | } 94 | function gpr { 95 | & git pull --rebase 96 | } 97 | function gpc { 98 | & git commit -m $args 99 | } 100 | function ga { 101 | & git add --all 102 | } 103 | function gst { 104 | & git status 105 | } 106 | function glp { 107 | & git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %C(bold green)(%cr) %C(bold yellow)<%an>%Creset' --abbrev-commit 108 | } 109 | -------------------------------------------------------------------------------- /Merge-DscConfigData/Tests/Unit/Merge-DscConfigData-NonNodeData.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Ensuring PSScriptAnalyzer ignores the use of Invoke-Expression 2 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingInvokeExpression', '')] 3 | param() 4 | 5 | $ModuleName = 'Merge-DscConfigData' 6 | If ( Get-Module -Name $ModuleName ) { 7 | Remove-Module -Name $ModuleName 8 | } 9 | Import-Module "$($PSScriptRoot)\..\..\$($ModuleName).psm1" -Force 10 | 11 | Describe 'Merge-DscConfigData [NonNodeData]' { 12 | 13 | InModuleScope $ModuleName { 14 | 15 | $TestBaseConfigData = @' 16 | @{ 17 | # Node specific data 18 | AllNodes = @( 19 | # Common settings for all nodes 20 | @{ 21 | NodeName = '*' 22 | PSDscAllowPlainTextPassword = $True 23 | ServicesEndpoint = 'http://localhost/Services/' 24 | DefaultLogLevel = 'Debug' 25 | KeepLatestBuilds = 6 26 | } 27 | ); 28 | NonNodeData = @{ 29 | DomainName = 'example.com' 30 | } 31 | } 32 | '@ 33 | 34 | $TestBaseConfig = $TestBaseConfigData | Invoke-Expression 35 | 36 | 37 | $TestOverrideConfigData = @' 38 | @{ 39 | # Node specific data 40 | AllNodes = @( 41 | @{ 42 | NodeName = '*' 43 | LocalAdministrators = 'MyLocalUser' 44 | DefaultLogLevel = 'Info' 45 | }, 46 | @{ 47 | NodeName = 'Server1' 48 | Role = 'Primary' 49 | }, 50 | @{ 51 | NodeName = 'Server2' 52 | Role = 'Secondary' 53 | DefaultLogLevel = 'Info' 54 | KeepLatestBuilds = 3 55 | } 56 | ); 57 | NonNodeData = @{ 58 | DomainName = 'subdomain.example.com' 59 | OUPath = 'OU=Servers, DC=subdomain, DC=exampple, DC=com' 60 | } 61 | } 62 | '@ 63 | 64 | $TestOverrideConfig = $TestOverrideConfigData | Invoke-Expression 65 | 66 | Mock Get-Content { return [string]::Empty } -ParameterFilter {$Path -eq 'TestEmpty'} 67 | Mock Get-Content { return $TestBaseConfigData } -ParameterFilter {$Path -eq 'TestBase'} 68 | Mock Get-Content { return $TestOverrideConfigData } -ParameterFilter {$Path -eq 'TestsOverride'} 69 | 70 | $Output = Merge-DscConfigData -BaseConfigFilePath 'TestBase' -OverrideConfigFilePath 'TestsOverride' -MergeNonNodeData $true 71 | 72 | It 'Should add NonNodeData settings which are absent in the base config' { 73 | 74 | $Expected = ($TestOverrideConfig.NonNodeData).OUPath 75 | $Actual = ($Output.NonNodeData).OUPath 76 | 77 | $Actual | Should Be $Expected 78 | } 79 | 80 | It 'Should override NonNodeData settings which are already present in the base config' { 81 | 82 | $Expected = ($TestOverrideConfig.NonNodeData).DomainName 83 | $Actual = ($Output.NonNodeData).DomainName 84 | 85 | $Actual | Should Be $Expected 86 | } 87 | 88 | $Output = Merge-DscConfigData -BaseConfigFilePath 'TestBase' -OverrideConfigFilePath 'TestsOverride' -MergeNonNodeData $false 89 | 90 | It 'Should return NonNodeData settings from the base config if the parameter MergeNonNodeData is false' { 91 | 92 | $Expected = $TestBaseConfig.NonNodeData 93 | $Actual = $Output.NonNodeData 94 | 95 | [bool](Compare-Object $Expected $Actual) | Should Be $false 96 | } 97 | 98 | $TestBaseConfigData = @' 99 | @{ 100 | # Node specific data 101 | AllNodes = @( 102 | # Common settings for all nodes 103 | @{ 104 | NodeName = '*' 105 | PSDscAllowPlainTextPassword = $True 106 | } 107 | ); 108 | } 109 | '@ 110 | 111 | $TestBaseConfig = $TestBaseConfigData | Invoke-Expression 112 | 113 | Mock Get-Content { return $TestBaseConfigData } -ParameterFilter {$Path -eq 'TestBase'} 114 | 115 | $Output = Merge-DscConfigData -BaseConfigFilePath 'TestBase' -OverrideConfigFilePath 'TestsOverride' -MergeNonNodeData $true 116 | 117 | It 'Should create NonNodeData in the BaseConfig if it does not already exist' { 118 | 119 | $Expected = $true 120 | $Actual = $Output.NonNodeData 121 | 122 | [bool]($Actual) | Should Be $true 123 | } 124 | 125 | } 126 | 127 | } 128 | 129 | -------------------------------------------------------------------------------- /Merge-DscConfigData/Tests/Unit/Merge-DscConfigData.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Ensuring PSScriptAnalyzer ignores the use of Invoke-Expression 2 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingInvokeExpression', '')] 3 | param() 4 | 5 | $ModuleName = 'Merge-DscConfigData' 6 | If ( Get-Module -Name $ModuleName ) { 7 | Remove-Module -Name $ModuleName 8 | } 9 | Import-Module "$($PSScriptRoot)\..\..\$($ModuleName).psm1" -Force 10 | 11 | Describe 'General Module behaviour' { 12 | 13 | $ModuleInfo = Get-Module -Name $ModuleName 14 | 15 | It 'Exports only the function "Merge-DscConfigData"' { 16 | 17 | $ModuleInfo.ExportedFunctions.Values.Name | 18 | Should Be 'Merge-DscConfigData' 19 | } 20 | } 21 | Describe 'Merge-DscConfigData' { 22 | 23 | InModuleScope $ModuleName { 24 | 25 | $TestBaseConfigData = @' 26 | @{ 27 | # Node specific data 28 | AllNodes = @( 29 | # Common settings for all nodes 30 | @{ 31 | NodeName = '*' 32 | PSDscAllowPlainTextPassword = $True 33 | ServicesEndpoint = 'http://localhost/Services/' 34 | DefaultLogLevel = 'Debug' 35 | KeepLatestBuilds = 6 36 | } 37 | ); 38 | } 39 | '@ 40 | $TestBaseConfig = $TestBaseConfigData | Invoke-Expression 41 | 42 | $TestOverrideConfigData = @' 43 | @{ 44 | # Node specific data 45 | AllNodes = @( 46 | @{ 47 | NodeName = '*' 48 | LocalAdministrators = 'MyLocalUser' 49 | DefaultLogLevel = 'Info' 50 | }, 51 | @{ 52 | NodeName = 'Server1' 53 | Role = 'Primary' 54 | }, 55 | @{ 56 | NodeName = 'Server2' 57 | Role = 'Secondary' 58 | DefaultLogLevel = 'Info' 59 | KeepLatestBuilds = 3 60 | } 61 | ); 62 | } 63 | '@ 64 | $TestOverrideConfig = $TestOverrideConfigData | Invoke-Expression 65 | $NodesNotAlreadyPresentInBaseConfig = Compare-Object $TestBaseConfig.AllNodes.NodeName $TestOverrideConfig.AllNodes.NodeName 66 | 67 | Mock Get-Content { return [string]::Empty } -ParameterFilter {$Path -eq 'TestEmpty'} 68 | Mock Get-Content { return $TestBaseConfigData } -ParameterFilter {$Path -eq 'TestBase'} 69 | Mock Get-Content { return $TestOverrideConfigData } -ParameterFilter {$Path -eq 'TestsOverride'} 70 | 71 | $Output = Merge-DscConfigData -BaseConfigFilePath 'TestBase' -OverrideConfigFilePath 'TestsOverride' 72 | 73 | It 'Should throw if the base config data file is empty' { 74 | { Merge-DscConfigData -BaseConfigFilePath 'TestEmpty' -OverrideConfigFilePath 'TestsOverride' } | 75 | Should Throw 'The base configuration data file is empty. This is not allowed.' 76 | } 77 | It 'Should Add the nodes which are not already present in the base config' { 78 | 79 | $Output.AllNodes.Count | Should Be ($TestBaseConfig.AllNodes.Count + $NodesNotAlreadyPresentInBaseConfig.Count) 80 | } 81 | It 'Should Add the nodenames which are not already present in the base config' { 82 | 83 | Foreach ( $NodeName in $NodesNotAlreadyPresentInBaseConfig.InputObject ) { 84 | 85 | $Output.AllNodes.NodeName | Where-Object { $_ -eq $NodeName } | 86 | Should Not BeNullOrEmpty 87 | } 88 | } 89 | It 'Should keep the nodenames which are already present in the base config' { 90 | 91 | $Output.AllNodes.NodeName | Where-Object { $_ -eq '*' } | 92 | Should Not BeNullOrEmpty 93 | } 94 | It 'Should add settings which are absent in the base config for existing nodes' { 95 | 96 | $Expected = ($TestOverrideConfig.AllNodes | Where-Object { $_.NodeName -eq '*' }).LocalAdministrators 97 | $Actual = ($Output.AllNodes | Where-Object { $_.NodeName -eq '*' }).LocalAdministrators 98 | 99 | $Actual | Should Be $Expected 100 | } 101 | It 'Should override settings which are already present in the base config for existing nodes' { 102 | 103 | $Expected = ($TestOverrideConfig.AllNodes | Where-Object { $_.NodeName -eq '*' }).DefaultLogLevel 104 | $Actual = ($Output.AllNodes | Where-Object { $_.NodeName -eq '*' }).DefaultLogLevel 105 | 106 | $Actual | Should Be $Expected 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /Export-NUnitXml/Export-NUnitXml.psm1: -------------------------------------------------------------------------------- 1 | Function Export-NUnitXml { 2 | <# 3 | .SYNOPSIS 4 | Takes results from PSScriptAnalyzer and exports them as a Pester test results file (NUnitXml format). 5 | 6 | .DESCRIPTION 7 | Takes results from PSScriptAnalyzer and exports them as a Pester test results file (NUnit XML schema). 8 | Because the generated file in NUnit-compatible, it can be consumed and published by most continuous integration tools. 9 | #> 10 | [CmdletBinding()] 11 | Param ( 12 | [Parameter(Mandatory, Position=0)] 13 | [AllowNull()] 14 | [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]]$ScriptAnalyzerResult, 15 | 16 | [Parameter(Mandatory, Position=1)] 17 | [string]$Path 18 | ) 19 | 20 | $TotalNumber = If ($ScriptAnalyzerResult) { $ScriptAnalyzerResult.Count -as [string] } Else { '1' } 21 | $FailedNumber = If ($ScriptAnalyzerResult) { $ScriptAnalyzerResult.Count -as [string] } Else { '0' } 22 | $Now = Get-Date 23 | $FormattedDate = Get-Date $Now -Format 'yyyy-MM-dd' 24 | $FormattedTime = Get-Date $Now -Format 'T' 25 | $User = $env:USERNAME 26 | $MachineName = $env:COMPUTERNAME 27 | $Cwd = $pwd.Path 28 | $UserDomain = $env:USERDOMAIN 29 | $OS = Get-CimInstance -ClassName Win32_OperatingSystem 30 | $Platform = $OS.Caption 31 | $OSVersion = $OS.Version 32 | $ClrVersion = $PSVersionTable.CLRVersion.ToString() 33 | $CurrentCulture = (Get-Culture).Name 34 | $UICulture = (Get-UICulture).Name 35 | 36 | Switch ($ScriptAnalyzerResult) { 37 | $Null { $TestResult = 'Success'; $TestSuccess = 'True'; Break} 38 | Default { $TestResult = 'Failure'; $TestSuccess = 'False'} 39 | } 40 | 41 | $Header = @" 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | `n 50 | "@ 51 | 52 | $Footer = @" 53 | 54 | 55 | 56 | 57 | 58 | "@ 59 | 60 | If ( -not($ScriptAnalyzerResult) ) { 61 | 62 | $TestDescription = 'All PowerShell files pass the specified PSScriptAnalyzer rules' 63 | $TestName = "PSScriptAnalyzer.{0}" -f $TestDescription 64 | 65 | $Body = @" 66 | `n 67 | "@ 68 | } 69 | Else { # $ScriptAnalyzerResult is not null 70 | $Body = [string]::Empty 71 | Foreach ( $Result in $ScriptAnalyzerResult ) { 72 | 73 | $TestDescription = "Rule name : $($Result.RuleName)" 74 | $TestName = "PSScriptAnalyzer.{0} - {1} - Line {2}" -f $TestDescription, $($Result.ScriptName), $($Result.Line.ToString()) 75 | 76 | # Need to Escape these otherwise we can end up with an invalid XML if the Stacktrace has non XML friendly chars like &, etc 77 | $Line = [System.Security.SecurityElement]::Escape($Result.Line) 78 | $ScriptPath = [System.Security.SecurityElement]::Escape($Result.ScriptPath) 79 | $Text = [System.Security.SecurityElement]::Escape($Result.Extent.Text) 80 | $Severity = [System.Security.SecurityElement]::Escape($Result.Severity) 81 | 82 | $TestCase = @" 83 | 84 | 85 | $($Result.Message) 86 | at line: $($Line) in $($ScriptPath) 87 | $($Line): $($Text) 88 | Rule severity : $($Severity) 89 | 90 | 91 | `n 92 | "@ 93 | 94 | $Body += $TestCase 95 | } 96 | } 97 | $OutputXml = $Header + $Body + $Footer 98 | 99 | # Checking our output is a well formed XML document 100 | Try { 101 | $XmlCheck = [xml]$OutputXml 102 | } 103 | Catch { 104 | Throw "There was an problem when attempting to cast the output to XML : $($_.Exception.Message)" 105 | } 106 | $OutputXml | Out-File -FilePath $Path -Encoding utf8 -Force 107 | } 108 | -------------------------------------------------------------------------------- /Merge-DscConfigData/Merge-DscConfigData.psm1: -------------------------------------------------------------------------------- 1 | #Requires -Version 4 2 | Function Merge-DscConfigData { 3 | [CmdletBinding()] 4 | 5 | Param( 6 | [Parameter(Mandatory=$True,Position=0)] 7 | [string]$BaseConfigFilePath, 8 | 9 | [Parameter(Mandatory=$True,Position=1)] 10 | [string]$OverrideConfigFilePath, 11 | 12 | [boolean]$MergeNonNodeData = $false 13 | ) 14 | 15 | $BaseConfigText = (Get-Content $BaseConfigFilePath | Out-String).Trim() 16 | 17 | # The base config should never be null or empty 18 | If ( -not($BaseConfigText) ) { 19 | Throw "The base configuration data file is empty. This is not allowed." 20 | } 21 | $OverrideConfigText = (Get-Content $OverrideConfigFilePath | Out-String).Trim() 22 | 23 | # When we standardize on PowerShell 5.x, we'll be able to use the much safer Import-PowerShellDataFile 24 | $BaseConfig = $BaseConfigText | Invoke-Expression 25 | 26 | # Merge the environment specific config only if it is NOT null or empty 27 | If ( $OverrideConfigText ) { 28 | 29 | Write-Verbose "Content of the override config data file : `r`n$OverrideConfigText" 30 | Try { 31 | $OverrideConfig = $OverrideConfigText | Invoke-Expression -ErrorAction Stop 32 | } 33 | Catch { 34 | throw "An error occurred when evaluating the content of $OverrideConfigFilePath as a PowerShell expression" 35 | } 36 | 37 | # Proceeding with the merge only if $OverrideConfig is not null and has been evaluated as a hashtable 38 | If ( $OverrideConfig -and ($OverrideConfig -is [hashtable]) ) { 39 | 40 | #Casting the AllNodes array to a list in order to add elements to it 41 | $BaseNodes = $BaseConfig.AllNodes -as [System.Collections.ArrayList] 42 | 43 | Foreach ( $Node in $OverrideConfig.AllNodes ) { 44 | 45 | $NodeInBaseConfig = $BaseNodes | Where-Object { $_.NodeName -eq $Node.NodeName } 46 | 47 | If ( $NodeInBaseConfig ) { 48 | 49 | Write-Verbose "The node $($Node.NodeName) is already present in the base config." 50 | 51 | # Removing the NodeName entry from the current Node to keep only the actual settings 52 | $Node.Remove('NodeName') 53 | 54 | Foreach ($OverrideSettingKey in $Node.keys) { 55 | 56 | # Checking if the setting already exists in the Base config 57 | $KeyExistsInBaseNode = $NodeInBaseConfig.ContainsKey($OverrideSettingKey) 58 | 59 | If ( $KeyExistsInBaseNode ) { 60 | Write-Verbose "$($NodeInBaseConfig.NodeName).$OverrideSettingKey, in node $($NodeInBaseConfig.NodeName) is present in the base config, overriding its value." 61 | $NodeInBaseConfig.$($OverrideSettingKey) = $Node.$($OverrideSettingKey) 62 | } 63 | Else { 64 | Write-Verbose "The setting $OverrideSettingKey, in node $($NodeInBaseConfig.NodeName) is absent in the base config, adding it." 65 | $NodeInBaseConfig.add($OverrideSettingKey, $Node.$($OverrideSettingKey)) 66 | } 67 | } 68 | } 69 | Else { # If the node is not already present in the base base config 70 | 71 | Write-Verbose "The node $($Node.NodeName) is absent in the base config, adding it." 72 | $Null = $BaseNodes.Add($Node) 73 | } 74 | } 75 | 76 | if ($MergeNonNodeData) { 77 | 78 | if ($OverrideConfig.NonNodeData -ne $null) { 79 | 80 | if ($BaseConfig.NonNodeData -eq $null) { 81 | 82 | # Use NonNodeData, in its entirety, from the Override configuration if NonNodeData does not exist in the Base configuration 83 | $BaseConfig.NonNodeData = $OverrideConfig.NonNodeData 84 | 85 | } else { 86 | 87 | foreach ($NonNodeSetting in $OverrideConfig.NonNodeData.GetEnumerator()) { 88 | 89 | # Checking if the setting already exists in the Base config 90 | if ($BaseConfig.NonNodeData.ContainsKey($NonNodeSetting.name)) 91 | { 92 | Write-Verbose -Message "The setting $($NonNodeSetting.name) is present in the base config, overring its value." 93 | $BaseConfig.NonNodeData.Set_Item($NonNodeSetting.Name, $NonNodeSetting.value) 94 | } 95 | else 96 | { 97 | Write-Verbose -Message "The setting $($NonNodeSetting.name) is absent in the base config, ading it." 98 | $BaseConfig.NonNodeData.Add($NonNodeSetting.Name, $NonNodeSetting.value) 99 | } 100 | } 101 | } 102 | } 103 | } 104 | $MergedConfig = $BaseConfig 105 | # Converting AllNodes back to an [array] because PSDesiredStateConfiguration doesn't accept an [ArrayList] 106 | $NodesBackToArray = $BaseNodes -as [array] 107 | $MergedConfig.AllNodes = $NodesBackToArray 108 | return $MergedConfig 109 | } 110 | Else { 111 | return $BaseConfig 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /PlasterTemplates/PSModuleProjectMB/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | *TestResult*.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | 254 | # Ignore VS Code workspace settings 255 | .vscode/* 256 | 257 | # Build results 258 | [Bb]uildOutput/ -------------------------------------------------------------------------------- /PlasterTemplates/PSModuleProjectMB/build.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Modules 'InvokeBuild' 2 | 3 | # Importing all build settings into the current scope 4 | . (Get-ChildItem *BuildSettings.ps1).FullName 5 | 6 | Function Write-TaskBanner ( [string]$TaskName ) { 7 | "`n" + ('-' * 79) + "`n" + "`t`t`t $($TaskName.ToUpper()) `n" + ('-' * 79) + "`n" 8 | } 9 | 10 | task Clean { 11 | Write-TaskBanner -TaskName $Task.Name 12 | 13 | If (Test-Path -Path $Settings.BuildOutput) { 14 | "Removing existing files and folders in $($Settings.BuildOutput)\" 15 | Get-ChildItem $Settings.BuildOutput | Remove-Item -Force -Recurse 16 | } 17 | Else { 18 | "$($Settings.BuildOutput) is not present, nothing to clean up." 19 | $Null = New-Item -ItemType Directory -Path $Settings.BuildOutput 20 | } 21 | } 22 | 23 | task Install_Dependencies { 24 | Write-TaskBanner -TaskName $Task.Name 25 | 26 | Foreach ( $Depend in $Settings.Dependency ) { 27 | "Installing build dependency : $Depend" 28 | Install-Module $Depend -Scope CurrentUser -Force 29 | Import-Module $Depend -Force 30 | } 31 | } 32 | 33 | task Unit_Tests { 34 | Write-TaskBanner -TaskName $Task.Name 35 | 36 | $UnitTestSettings = $Settings.UnitTestParams 37 | $Script:UnitTestsResult = Invoke-Pester @UnitTestSettings 38 | } 39 | 40 | task Fail_If_Failed_Unit_Test { 41 | Write-TaskBanner -TaskName $Task.Name 42 | 43 | $FailureMessage = '{0} Unit test(s) failed. Aborting build' -f $UnitTestsResult.FailedCount 44 | assert ($UnitTestsResult.FailedCount -eq 0) $FailureMessage 45 | } 46 | 47 | task Publish_Unit_Tests_Coverage { 48 | Write-TaskBanner -TaskName $Task.Name 49 | 50 | $Coverage = Format-Coverage -PesterResults $UnitTestsResult -CoverallsApiToken $Settings.CoverallsKey -BranchName $Settings.Branch 51 | Publish-Coverage -Coverage $Coverage 52 | } 53 | 54 | task Integration_Tests { 55 | Write-TaskBanner -TaskName $Task.Name 56 | 57 | $IntegrationTestSettings = $Settings.IntegrationTestParams 58 | $Script:IntegrationTestsResult = Invoke-Pester @IntegrationTestSettings 59 | } 60 | 61 | task Fail_If_Failed_Integration_Test { 62 | Write-TaskBanner -TaskName $Task.Name 63 | 64 | $FailureMessage = '{0} Integration test(s) failed. Aborting build' -f $IntegrationTestsResult.FailedCount 65 | assert ($IntegrationTestsResult.FailedCount -eq 0) $FailureMessage 66 | } 67 | 68 | task Upload_Test_Results_To_AppVeyor { 69 | Write-TaskBanner -TaskName $Task.Name 70 | 71 | $TestResultFiles = (Get-ChildItem -Path $Settings.BuildOutput -Filter '*TestsResult.xml').FullName 72 | Foreach ( $TestResultFile in $TestResultFiles ) { 73 | "Uploading test result file : $TestResultFile" 74 | (New-Object 'System.Net.WebClient').UploadFile($Settings.TestUploadUrl, $TestResultFile) 75 | } 76 | } 77 | 78 | task Test Unit_Tests, 79 | Fail_If_Failed_Unit_Test, 80 | Publish_Unit_Tests_Coverage, 81 | # There are no integration tests at the moment 82 | # Integration_Tests, 83 | # Fail_If_Failed_Integration_Test, 84 | Upload_Test_Results_To_AppVeyor 85 | 86 | task Analyze { 87 | Write-TaskBanner -TaskName $Task.Name 88 | 89 | Add-AppveyorTest -Name 'Code Analysis' -Outcome Running 90 | $AnalyzeSettings = $Settings.AnalyzeParams 91 | $Script:AnalyzeFindings = Invoke-ScriptAnalyzer @AnalyzeSettings 92 | 93 | If ( $AnalyzeFindings ) { 94 | $FindingsString = $AnalyzeFindings | Out-String 95 | Write-Warning $FindingsString 96 | Update-AppveyorTest -Name 'Code Analysis' -Outcome Failed -ErrorMessage $FindingsString 97 | } 98 | Else { 99 | Update-AppveyorTest -Name 'Code Analysis' -Outcome Passed 100 | } 101 | } 102 | 103 | task Fail_If_Analyze_Findings { 104 | Write-TaskBanner -TaskName $Task.Name 105 | 106 | $FailureMessage = 'PSScriptAnalyzer found {0} issues. Aborting build' -f $AnalyzeFindings.Count 107 | assert ( -not($AnalyzeFindings) ) $FailureMessage 108 | } 109 | 110 | Task Build_Documentation { 111 | Write-TaskBanner -TaskName $Task.Name 112 | 113 | Remove-Module -Name $Settings.ModuleName -Force -ErrorAction SilentlyContinue 114 | # platyPS + AppVeyor requires the module to be loaded in Global scope 115 | Import-Module $Settings.ManifestPath -Force -Global 116 | 117 | $HeaderContent = Get-Content -Path $Settings.HeaderPath -Raw 118 | $HeaderContent += " - Public Functions:`n" 119 | 120 | If (Test-Path -Path $Settings.FunctionDocsPath) { 121 | Get-ChildItem $Settings.FunctionDocsPath | Remove-Item -Force -Recurse 122 | } 123 | Else { 124 | $Null = New-Item -ItemType Directory -Path $Settings.FunctionDocsPath 125 | } 126 | 127 | $PlatyPSSettings = $Settings.PlatyPSParams 128 | New-MarkdownHelp @PlatyPSSettings | Foreach-Object { 129 | $Part = ' - {0}: Functions/{1}' -f $_.BaseName, $_.Name 130 | "Created markdown help file : $($_.FullName)" 131 | $HeaderContent += "{0}`n" -f $Part 132 | } 133 | $HeaderContent | Set-Content -Path $Settings.MkdocsPath -Force 134 | } 135 | 136 | task Set_Module_Version { 137 | Write-TaskBanner -TaskName $Task.Name 138 | 139 | $ManifestContent = Get-Content -Path $Settings.ManifestPath 140 | $CurrentVersion = $Settings.VersionRegex.Match($ManifestContent).Groups['ModuleVersion'].Value 141 | "Current module version in the manifest : $CurrentVersion" 142 | 143 | $ManifestContent -replace $CurrentVersion,$Settings.Version | Set-Content -Path $Settings.ManifestPath -Force 144 | $NewManifestContent = Get-Content -Path $Settings.ManifestPath 145 | $NewVersion = $Settings.VersionRegex.Match($NewManifestContent).Groups['ModuleVersion'].Value 146 | "Updated module version in the manifest : $NewVersion" 147 | 148 | If ( $NewVersion -ne $Settings.Version ) { 149 | Throw "Module version was not updated correctly to $($Settings.Version) in the manifest." 150 | } 151 | } 152 | 153 | task Push_Build_Changes_To_Repo { 154 | Write-TaskBanner -TaskName $Task.Name 155 | 156 | cmd /c "git config --global credential.helper store 2>&1" 157 | Add-Content "$env:USERPROFILE\.git-credentials" "https://$($Settings.GitHubKey):x-oauth-basic@github.com`n" 158 | cmd /c "git config --global user.email ""$($Settings.Email)"" 2>&1" 159 | cmd /c "git config --global user.name ""$($Settings.Name)"" 2>&1" 160 | cmd /c "git config --global core.autocrlf true 2>&1" 161 | cmd /c "git checkout $($Settings.Branch) 2>&1" 162 | cmd /c "git add -A 2>&1" 163 | cmd /c "git commit -m ""Commit build changes [ci skip]"" 2>&1" 164 | cmd /c "git status 2>&1" 165 | cmd /c "git push origin $($Settings.Branch) 2>&1" 166 | } 167 | 168 | task Copy_Source_To_Build_Output { 169 | Write-TaskBanner -TaskName $Task.Name 170 | 171 | "Copying the source folder [$($Settings.SourceFolder)] into the build output folder : [$($Settings.BuildOutput)]" 172 | Copy-Item -Path $Settings.SourceFolder -Destination $Settings.BuildOutput -Recurse 173 | } 174 | 175 | # Default task : 176 | task . Clean, 177 | Install_Dependencies, 178 | Test, 179 | Analyze, 180 | Fail_If_Analyze_Findings, 181 | Build_Documentation, 182 | Set_Module_Version, 183 | Push_Build_Changes_To_Repo, 184 | Copy_Source_To_Build_Output 185 | -------------------------------------------------------------------------------- /VisualStudioCode/PowerShell.json: -------------------------------------------------------------------------------- 1 | { 2 | "Example-DSC Resource Unit Tests Script": { 3 | "prefix": "ex-DSC Resource Unit Tests Script", 4 | "body": [ 5 | "$Global:DSCResourceName = '${DscResourceName:Enter the name of your DSC resource}'", 6 | "", 7 | "Import-Module \"$($PSScriptRoot)\\..\\..\\DSCResources\\\\$($Global:DSCResourceName)\\\\$($Global:DSCResourceName).psm1\" -Force", 8 | "", 9 | "# Helper function to list the names of mandatory parameters of *-TargetResource functions", 10 | "Function Get-MandatoryParameter {", 11 | "\t[CmdletBinding()]", 12 | "\tParam(", 13 | "\t\t[Parameter(Mandatory=$True)]", 14 | "\t\t[string]$CommandName", 15 | "\t)", 16 | "\t$GetCommandData = Get-Command \"$($Global:DSCResourceName)\\\\$CommandName\"", 17 | "\t$MandatoryParameters = $GetCommandData.Parameters.Values | Where-Object { $_.Attributes.Mandatory -eq $True }", 18 | "\treturn $MandatoryParameters.Name", 19 | "}", 20 | "", 21 | "# Getting the names of mandatory parameters for each *-TargetResource function", 22 | "$GetMandatoryParameter = Get-MandatoryParameter -CommandName \"Get-TargetResource\"", 23 | "$TestMandatoryParameter = Get-MandatoryParameter -CommandName \"Test-TargetResource\"", 24 | "$SetMandatoryParameter = Get-MandatoryParameter -CommandName \"Set-TargetResource\"", 25 | "", 26 | "# Splatting parameters values for Get, Test and Set-TargetResource functions", 27 | "$GetParams = @{", 28 | "\t", 29 | "}", 30 | "$TestParams = @{", 31 | "\t", 32 | "}", 33 | "$SetParams = @{", 34 | "\t", 35 | "}", 36 | "", 37 | "Describe \"$($Global:DSCResourceName)\\Get-TargetResource\" {", 38 | "\t", 39 | "\t$GetReturn = & \"$($Global:DSCResourceName)\\Get-TargetResource\" @GetParams", 40 | "\t", 41 | "\tIt \"Should return a hashtable\" {", 42 | "\t\t$GetReturn | Should BeOfType System.Collections.Hashtable", 43 | "\t}", 44 | "\tForeach ($MandatoryParameter in $GetMandatoryParameter) {", 45 | "\t\t", 46 | "\t\tIt \"Should return a hashtable with key named $MandatoryParameter\" {", 47 | "\t\t\t$GetReturn.ContainsKey($MandatoryParameter) | Should Be $True", 48 | "\t\t}", 49 | "\t}", 50 | "}", 51 | "", 52 | "Describe \"$($Global:DSCResourceName)\\Test-TargetResource\" {", 53 | "\t", 54 | "\t$TestReturn = & \"$($Global:DSCResourceName)\\Test-TargetResource\" @TestParams", 55 | "\t", 56 | "\tIt \"Should have the same mandatory parameters as Get-TargetResource\" {", 57 | "\t\t# Does not check for $True or $False but uses the output of Compare-Object.", 58 | "\t\t# That way, if this test fails Pester will show us the actual difference(s).", 59 | "\t\t(Compare-Object $GetMandatoryParameter $TestMandatoryParameter).InputObject | Should Be $Null", 60 | "\t}", 61 | "\tIt \"Should return a boolean\" {", 62 | "\t\t$TestReturn | Should BeOfType System.Boolean", 63 | "\t}", 64 | "}", 65 | "", 66 | "Describe \"$($Global:DSCResourceName)\\Set-TargetResource\" {", 67 | "\t", 68 | "\t$SetReturn = & \"$($Global:DSCResourceName)\\Set-TargetResource\" @SetParams", 69 | "\t", 70 | "\tIt \"Should have the same mandatory parameters as Test-TargetResource\" {", 71 | "\t\t(Compare-Object $TestMandatoryParameter $SetMandatoryParameter).InputObject | Should Be $Null", 72 | "\t}", 73 | "\tIt \"Should not return anything\" {", 74 | "\t\t$SetReturn | Should Be $Null", 75 | "\t}", 76 | "}" 77 | ], 78 | "description": "Example: snippet for unit testing a DSC resource with Pester" 79 | }, 80 | "Example-Class": { 81 | "prefix": "ex-class", 82 | "body": [ 83 | "class ${classname:MyClass} {", 84 | "\t# Property: Holds name", 85 | "\t[String] $Name", 86 | "", 87 | "\t# Constructor: Creates a new MyClass object, with the specified name", 88 | "\t${classname:MyClass}([String] $NewName) {", 89 | "\t\t# Set name for ${classname:MyClass}", 90 | "\t\t$this.Name = $NewName", 91 | "\t}", 92 | "", 93 | "\t# Method: Method that changes $Name to the default name", 94 | "\t[void] ChangeNameToDefault() {", 95 | "\t\t$this.Name = \"DefaultName\"", 96 | "\t}", 97 | "}" 98 | ], 99 | "description": "Example: class snippet with a constructor, property and a method" 100 | }, 101 | "Example-Cmdlet": { 102 | "prefix": "ex-cmdlet", 103 | "body": [ 104 | "Function ${name:Verb-Noun} {", 105 | "<#", 106 | ".SYNOPSIS", 107 | "\tShort description", 108 | ".DESCRIPTION", 109 | "\tLong description", 110 | ".EXAMPLE", 111 | "\tExample of how to use this cmdlet", 112 | ".OUTPUTS", 113 | "\tOutput from this cmdlet (if any)", 114 | ".NOTES", 115 | "\tGeneral notes", 116 | "#>", 117 | "\t[CmdletBinding()]", 118 | "\t[OutputType([String])]", 119 | "\tParam (", 120 | "\t\t[Parameter(Mandatory=\\$True, Position=0, ValueFromPipeline=\\$True)]", 121 | "\t\t[ValidateSet('sun', 'moon', 'earth')]", 122 | "\t\t[string]\\$Param1,", 123 | "\t\t", 124 | "\t\t[ValidateScript({ \\$True })]", 125 | "\t\t[int]\\$Param2", 126 | "\t)", 127 | "\t", 128 | "\tBegin {", 129 | "\t}", 130 | "\t", 131 | "\tProcess {", 132 | "\t\t\t$0", 133 | "\t}", 134 | "\t", 135 | "\tEnd {", 136 | "\t}", 137 | "}" 138 | ], 139 | "description": "Example: script cmdlet snippet with all attributes and inline help fields" 140 | }, 141 | "Example-DSC Configuration": { 142 | "prefix": "ex-DSC config", 143 | "body": [ 144 | "configuration Name {", 145 | "\t# One can evaluate expressions to get the node list", 146 | "\t# E.g: $AllNodes.Where(\"Role -eq Web\").NodeName", 147 | "\tnode (\"Node1\",\"Node2\",\"Node3\")", 148 | "\t{", 149 | "\t\t# Call Resource Provider", 150 | "\t\t# E.g: WindowsFeature, File", 151 | "\t\tWindowsFeature FriendlyName", 152 | "\t\t{", 153 | "\t\t\tEnsure = \"Present\"", 154 | "\t\t\tName = \"Feature Name\"", 155 | "\t\t}", 156 | "", 157 | "\t\tFile FriendlyName", 158 | "\t\t{", 159 | "\t\t\tEnsure = \"Present\"", 160 | "\t\t\tSourcePath = $SourcePath", 161 | "\t\t\tDestinationPath = $DestinationPath", 162 | "\t\t\tType = \"Directory\"", 163 | "\t\t\tDependsOn = \"[WindowsFeature]FriendlyName\"", 164 | "\t\t}", 165 | "\t}", 166 | "}" 167 | ], 168 | "description": "Example: DSC configuration snippet that uses built-in resource providers" 169 | }, 170 | "Example-DSC Resource Provider (class-based)": { 171 | "prefix": "ex-DSC resource provider (class-based)", 172 | "body": [ 173 | "# Defines the values for the resource's Ensure property.", 174 | "enum Ensure {", 175 | "\t# The resource must be absent.", 176 | "\tAbsent", 177 | "\t# The resource must be present.", 178 | "\tPresent", 179 | "}", 180 | "", 181 | "# [DscResource()] indicates the class is a DSC resource.", 182 | "[DscResource()]", 183 | "class NameOfResource {", 184 | "\t# A DSC resource must define at least one key property.", 185 | "\t[DscProperty(Key)]", 186 | "\t[string] $P1", 187 | "\t", 188 | "\t# Mandatory indicates the property is required and DSC will guarantee it is set.", 189 | "\t[DscProperty(Mandatory)]", 190 | "\t[Ensure] $P2", 191 | "\t", 192 | "\t# NotConfigurable properties return additional information about the state of the resource.", 193 | "\t# For example, a Get() method might return the date a resource was last modified.", 194 | "\t# NOTE: These properties are only used by the Get() method and cannot be set in configuration.", 195 | "\t[DscProperty(NotConfigurable)]", 196 | "\t[Nullable[datetime]] $P3", 197 | "\t", 198 | "\t[DscProperty()]", 199 | "\t[ValidateSet(\"val1\", \"val2\")]", 200 | "\t[string] $P4", 201 | "\t", 202 | "\t# Gets the resource's current state.", 203 | "\t[NameOfResource] Get() {", 204 | "\t\t# NotConfigurable properties are set in the Get method.", 205 | "\t\t$this.P3 = something", 206 | "\t\t# Return this instance or construct a new instance.", 207 | "\t\treturn $this", 208 | "\t}", 209 | "\t", 210 | "\t# Sets the desired state of the resource.", 211 | "\t[void] Set() {", 212 | "\t}", 213 | "\t", 214 | "\t# Tests if the resource is in the desired state.", 215 | "\t[bool] Test() {", 216 | "\t\t return $true", 217 | "\t}", 218 | "}" 219 | ], 220 | "description": "Example: class-based DSC resource provider snippet" 221 | }, 222 | "Example-DSC Resource Provider (function based)": { 223 | "prefix": "ex-DSC resource provider (function based)", 224 | "body": [ 225 | "function Get-TargetResource {", 226 | "\t# TODO: Add parameters here", 227 | "\t# Make sure to use the same parameters for", 228 | "\t# Get-TargetResource, Set-TargetResource, and Test-TargetResource", 229 | "\tparam(", 230 | "\t)", 231 | "}", 232 | "function Set-TargetResource {", 233 | "\t# TODO: Add parameters here", 234 | "\t# Make sure to use the same parameters for", 235 | "\t# Get-TargetResource, Set-TargetResource, and Test-TargetResource", 236 | "\tparam(", 237 | "\t)", 238 | "}", 239 | "function Test-TargetResource {", 240 | "\t# TODO: Add parameters here", 241 | "\t# Make sure to use the same parameters for", 242 | "\t# Get-TargetResource, Set-TargetResource, and Test-TargetResource", 243 | "\tparam(", 244 | "\t)", 245 | "}" 246 | ], 247 | "description": "Example: function-based DSC resource provider snippet" 248 | }, 249 | "Example-Splatting": { 250 | "prefix": "ex-splat", 251 | "body": [ 252 | "$Params = @{", 253 | "\tModule = '*'", 254 | "\tVerb = 'Get'", 255 | "}", 256 | "Get-Command @Params" 257 | ], 258 | "description": "Example: PowerShell splatting technique snippet" 259 | }, 260 | "Example-Switch": { 261 | "prefix": "ex-switch", 262 | "body": [ 263 | "switch (${variable:$x})", 264 | "{", 265 | "\t'${val:value1}' { $1 }", 266 | "\t{$_ -in 'A','B','C'} {}", 267 | "\t'value3' {}", 268 | "\tDefault {}", 269 | "}" 270 | ], 271 | "description": "Example: switch statement snippet" 272 | }, 273 | "Example-Advanced Workflow": { 274 | "prefix": "ex-advanced workflow", 275 | "body": [ 276 | "<#", 277 | ".Synopsis", 278 | "\tShort description", 279 | ".DESCRIPTION", 280 | "\tLong description", 281 | ".EXAMPLE", 282 | "\tExample of how to use this workflow", 283 | ".EXAMPLE", 284 | "\tAnother example of how to use this workflow", 285 | ".INPUTS", 286 | "\tInputs to this workflow (if any)", 287 | ".OUTPUTS", 288 | "\tOutput from this workflow (if any)", 289 | ".NOTES", 290 | "\tGeneral notes", 291 | ".FUNCTIONALITY", 292 | "\tThe functionality that best describes this workflow", 293 | "#>", 294 | "workflow ${name:Verb-Noun} {", 295 | "\t[CmdletBinding(DefaultParameterSetName='Parameter Set 1',", 296 | "\t HelpUri = 'http://www.microsoft.com/',", 297 | "\t ConfirmImpact='Medium')]", 298 | "\t[Alias()]", 299 | "\t[OutputType([String])]", 300 | "\tparam(", 301 | "\t\t# Param1 help description", 302 | "\t\t[Parameter(Mandatory=$true, ", 303 | "\t\t Position=0,", 304 | "\t\t ParameterSetName='Parameter Set 1')]", 305 | "\t\t[ValidateNotNull()]", 306 | "\t\t[Alias(\"p1\")] ", 307 | "\t\t$Param1,", 308 | "", 309 | "\t\t# Param2 help description", 310 | "\t\t[int]", 311 | "\t\t$Param2", 312 | "\t)", 313 | "", 314 | "\t# Saves (persists) the current workflow state and output", 315 | "\t# Checkpoint-Workflow", 316 | "\t# Suspends the workflow", 317 | "\t# Suspend-Workflow", 318 | "", 319 | "\t# Workflow common parameters are available as variables such as:", 320 | "\t$PSPersist ", 321 | "\t$PSComputerName", 322 | "\t$PSCredential", 323 | "\t$PSUseSsl", 324 | "\t$PSAuthentication", 325 | "", 326 | "\t# Workflow runtime information can be accessed by using the following variables:", 327 | "\t$Input", 328 | "\t$PSSenderInfo", 329 | "\t$PSWorkflowRoot", 330 | "\t$JobCommandName", 331 | "\t$ParentCommandName", 332 | "\t$JobId", 333 | "\t$ParentJobId", 334 | "\t$WorkflowInstanceId", 335 | "\t$JobInstanceId", 336 | "\t$ParentJobInstanceId", 337 | "\t$JobName", 338 | "\t$ParentJobName", 339 | "", 340 | "\t# Set the progress message ParentActivityId", 341 | "\t$PSParentActivityId", 342 | "", 343 | "\t# Preference variables that control runtime behavior", 344 | "\t$PSRunInProcessPreference", 345 | "\t$PSPersistPreference", 346 | "}" 347 | ], 348 | "description": "Example: advanced workflow snippet" 349 | }, 350 | "Class": { 351 | "prefix": "class", 352 | "body": [ 353 | "class ${ClassName} {", 354 | "\t$0", 355 | "}" 356 | ], 357 | "description": "Class definition snippet" 358 | }, 359 | "Constructor": { 360 | "prefix": "ctor", 361 | "body": [ 362 | "${ClassName}(${OptionalParameters}) {", 363 | "\t$0", 364 | "}" 365 | ], 366 | "description": "Class constructor definition snippet" 367 | }, 368 | "Hidden Property": { 369 | "prefix": "proph", 370 | "body": [ 371 | "hidden [${string}] $${ProperyName}" 372 | ], 373 | "description": "Class hidden property definition snippet" 374 | }, 375 | "Property": { 376 | "prefix": "prop", 377 | "body": [ 378 | "[${string}] $${PropertyName}" 379 | ], 380 | "description": "Class property definition snippet" 381 | }, 382 | "Method": { 383 | "prefix": "method", 384 | "body": [ 385 | "[${void}] ${MethodName}($${OptionalParameters}) {", 386 | "\t$0", 387 | "}" 388 | ], 389 | "description": "Class method definition snippet" 390 | }, 391 | "Enum": { 392 | "prefix": "enum", 393 | "body": [ 394 | "enum ${EnumName} {", 395 | "\t$0", 396 | "}" 397 | ], 398 | "description": "Enum definition snippet" 399 | }, 400 | "Cmdlet": { 401 | "prefix": "cmdlet", 402 | "body": [ 403 | "function ${name:Verb-Noun} {", 404 | "\t[CmdletBinding()]", 405 | "\tparam(", 406 | "\t\t$0", 407 | "\t)", 408 | "\t", 409 | "\tbegin {", 410 | "\t}", 411 | "\t", 412 | "\tprocess {", 413 | "\t}", 414 | "\t", 415 | "\tend {", 416 | "\t}", 417 | "}" 418 | ], 419 | "description": "Script cmdlet definition snippet" 420 | }, 421 | "Cmdlet-Comment-Help": { 422 | "prefix": "help", 423 | "body": [ 424 | "<#", 425 | ".SYNOPSIS", 426 | "\tShort description", 427 | ".DESCRIPTION", 428 | "\tLong description", 429 | ".EXAMPLE", 430 | "\tC:\\PS> ", 431 | "\tExplanation of what the example does", 432 | ".INPUTS", 433 | "\tInputs (if any)", 434 | ".OUTPUTS", 435 | "\tOutput (if any)", 436 | ".NOTES", 437 | "\tGeneral notes", 438 | "#>" 439 | ], 440 | "description": "Comment-based help snippet" 441 | }, 442 | "Parameter": { 443 | "prefix": "parameter", 444 | "body": [ 445 | "# ${Parameter help description}", 446 | "[Parameter(${AttributeValues})]", 447 | "[${ParameterType}]", 448 | "$${ParameterName}" 449 | ], 450 | "description": "Parameter declaration snippet" 451 | }, 452 | "Parameter-Path": { 453 | "prefix": "parameter-path", 454 | "body": [ 455 | "# Specifies a path to one or more locations.", 456 | "[Parameter(Mandatory=$true,", 457 | " Position=${Position:0},", 458 | " ParameterSetName=\"${ParameterSetName:Path}\",", 459 | " ValueFromPipeline=$true,", 460 | " ValueFromPipelineByPropertyName=$true,", 461 | " HelpMessage=\"Path to one or more locations.\")]", 462 | "[Alias(\"PSPath\")]", 463 | "[ValidateNotNullOrEmpty()]", 464 | "[string[]]", 465 | "$${ParameterName:Path}$0" 466 | ], 467 | "description": "Parameter declaration snippet for Path parameter that does not accept wildcards. Do not use with parameter-literalpath." 468 | }, 469 | "Parameter-Path-Wildcards": { 470 | "prefix": "parameter-path-wildcards", 471 | "body": [ 472 | "# Specifies a path to one or more locations. Wildcards are permitted.", 473 | "[Parameter(Mandatory=$true,", 474 | " Position=${Position:0},", 475 | " ParameterSetName=\"${ParameterSetName:Path}\",", 476 | " ValueFromPipeline=$true,", 477 | " ValueFromPipelineByPropertyName=$true,", 478 | " HelpMessage=\"Path to one or more locations.\")]", 479 | "[ValidateNotNullOrEmpty()]", 480 | "[SupportsWildcards()]", 481 | "[string[]]", 482 | "$${ParameterName:Path}$0" 483 | ], 484 | "description": "Parameter declaration snippet for Path parameter that accepts wildcards. Add parameter-literalpath to handle paths with embedded wildcard chars." 485 | }, 486 | "Parameter-LiteralPath": { 487 | "prefix": "parameter-literalpath", 488 | "body": [ 489 | "# Specifies a path to one or more locations. Unlike the Path parameter, the value of the LiteralPath parameter is", 490 | "# used exactly as it is typed. No characters are interpreted as wildcards. If the path includes escape characters,", 491 | "# enclose it in single quotation marks. Single quotation marks tell Windows PowerShell not to interpret any", 492 | "# characters as escape sequences.", 493 | "[Parameter(Mandatory=$true,", 494 | " Position=${Position:0},", 495 | " ParameterSetName=\"${ParameterSetName:LiteralPath}\",", 496 | " ValueFromPipelineByPropertyName=$true,", 497 | " HelpMessage=\"Literal path to one or more locations.\")]", 498 | "[Alias(\"PSPath\")]", 499 | "[ValidateNotNullOrEmpty()]", 500 | "[string[]]", 501 | "$${ParameterName:LiteralPath}$0" 502 | ], 503 | "description": "Parameter declaration snippet for a LiteralPath parameter" 504 | }, 505 | "DSC Ensure Enum": { 506 | "prefix": "DSC Ensure enum", 507 | "body": [ 508 | "enum Ensure {", 509 | "\tAbsent", 510 | "\tPresent", 511 | "}" 512 | ], 513 | "description": "DSC Ensure enum definition snippet" 514 | }, 515 | "DSC Resource Provider (class-based)": { 516 | "prefix": "DSC resource provider (class-based)", 517 | "body": [ 518 | "[DscResource()]", 519 | "class ${ResourceName:NameOfResource} {", 520 | "\t[DscProperty(Key)]", 521 | "\t[string] $${PropertyName:KeyName}", 522 | "\t", 523 | "\t# Gets the resource's current state.", 524 | "\t[${ResourceName:NameOfResource}] Get() {", 525 | "\t\t$0", 526 | "\t\treturn $this", 527 | "\t}", 528 | "\t", 529 | "\t# Sets the desired state of the resource.", 530 | "\t[void] Set() {", 531 | "\t\t", 532 | "\t}", 533 | "\t", 534 | "\t# Tests if the resource is in the desired state.", 535 | "\t[bool] Test() {", 536 | "\t\t", 537 | "\t}", 538 | "}" 539 | ], 540 | "description": "Class-based DSC resource provider snippet" 541 | }, 542 | "DSC Resource Provider (function-based)": { 543 | "prefix": "DSC resource provier (function-based)", 544 | "body": [ 545 | "function Get-TargetResource {", 546 | "\tparam(", 547 | "\t)", 548 | "\t", 549 | "\t$0", 550 | "}", 551 | "function Set-TargetResource {", 552 | "\tparam(", 553 | "\t)", 554 | "\t", 555 | "}", 556 | "function Test-TargetResource {", 557 | "\tparam(", 558 | "\t)", 559 | "\t", 560 | "}" 561 | ], 562 | "description": "Function-based DSC resource provider snippet" 563 | }, 564 | "comment block": { 565 | "prefix": "comment", 566 | "body": [ 567 | "<#", 568 | " # $0", 569 | " #>" 570 | ], 571 | "description": "Comment block snippet" 572 | }, 573 | "do-until": { 574 | "prefix": "do-until", 575 | "body": [ 576 | "do {", 577 | "\t$0", 578 | "} until (${condition})" 579 | ], 580 | "description": "do-until loop snippet" 581 | }, 582 | "do-while": { 583 | "prefix": "do-while", 584 | "body": [ 585 | "do {", 586 | "\t$0", 587 | "} while (${condition})" 588 | ], 589 | "description": "do-while loop snippet" 590 | }, 591 | "while": { 592 | "prefix": "while", 593 | "body": [ 594 | "while (${condition}) {", 595 | "\t$0", 596 | "}" 597 | ], 598 | "description": "while loop snippet" 599 | }, 600 | "for": { 601 | "prefix": "for", 602 | "body": [ 603 | "for ($${variable:i} = 0; $${variable:i} -lt $${array}.Count; $${variable:i}++) {", 604 | "\t$0", 605 | "}" 606 | ], 607 | "description": "for loop snippet" 608 | }, 609 | "for-reversed": { 610 | "prefix": "forr", 611 | "body": [ 612 | "for ($${variable:i} = $${array}.Count - 1; $${variable:i} -ge 0 ; $${variable:i}--) {", 613 | "\t$0", 614 | "}" 615 | ], 616 | "description": "reversed for loop snippet" 617 | }, 618 | "foreach": { 619 | "prefix": "foreach", 620 | "body": [ 621 | "foreach ($${variable:item} in $${collection:collection}) {", 622 | "\t$0", 623 | "}" 624 | ], 625 | "description": "foreach loop snippet" 626 | }, 627 | "function": { 628 | "prefix": "function", 629 | "body": [ 630 | "function ${FunctionName} (${OptionalParameters}) {", 631 | "\t$0", 632 | "}" 633 | ], 634 | "description": "function definition snippet" 635 | }, 636 | "if": { 637 | "prefix": "if", 638 | "body": [ 639 | "if (${condition}) {", 640 | "\t$0", 641 | "}" 642 | ], 643 | "description": "if statement snippet" 644 | }, 645 | "elseif": { 646 | "prefix": "elseif", 647 | "body": [ 648 | "elseif (${condition}) {", 649 | "\t$0", 650 | "}" 651 | ], 652 | "description": "elseif statement snippet" 653 | }, 654 | "else": { 655 | "prefix": "else", 656 | "body": [ 657 | "else {", 658 | "\t$0", 659 | "}" 660 | ], 661 | "description": "else statement snippet" 662 | }, 663 | "switch": { 664 | "prefix": "switch", 665 | "body": [ 666 | "switch (${variable:$x}) {", 667 | "\t${condition} { $0 }", 668 | "\tDefault {}", 669 | "}" 670 | ], 671 | "description": "switch statement snippet" 672 | }, 673 | "try-catch": { 674 | "prefix": "try", 675 | "body": [ 676 | "try {", 677 | "\t$0", 678 | "}", 679 | "catch [${exception:System.Exception}] {", 680 | "\t", 681 | "}" 682 | ], 683 | "description": "try-catch snippet" 684 | }, 685 | "try-catch-finally": { 686 | "prefix": "trycf", 687 | "body": [ 688 | "try {", 689 | "\t$0", 690 | "}", 691 | "catch [${exception:System.Exception}] {", 692 | "\t", 693 | "}", 694 | "finally {", 695 | "\t", 696 | "}" 697 | ], 698 | "description": "try-catch-finally snippet" 699 | }, 700 | "try-finally": { 701 | "prefix": "tryf", 702 | "body": [ 703 | "try {", 704 | "\t$0", 705 | "}", 706 | "finally {", 707 | "\t", 708 | "}" 709 | ], 710 | "description": "try-finally snippet" 711 | }, 712 | "Workflow": { 713 | "prefix": "workflow", 714 | "body": [ 715 | "workflow ${name:Verb-Noun} {", 716 | "\tparam(", 717 | "\t)", 718 | "\t", 719 | "\t$0", 720 | "}" 721 | ], 722 | "description": "workflow snippet" 723 | }, 724 | "Workflow ForEachParallel": { 725 | "prefix": "workflow foreach-parallel", 726 | "body": [ 727 | "foreach -parallel ($${variable:item} in $${collection:collection}) {", 728 | "\t$0", 729 | "}" 730 | ], 731 | "description": "foreach-parallel snippet (for use inside a workflow)" 732 | }, 733 | "Workflow InlineScript": { 734 | "prefix": "workflow inlinescript", 735 | "body": [ 736 | "inlineScript {", 737 | "\t$0", 738 | "}" 739 | ], 740 | "description": "inlinescript snippet (for use inside a workflow)" 741 | }, 742 | "Workflow Parallel": { 743 | "prefix": "workflow parallel", 744 | "body": [ 745 | "parallel {", 746 | "\t$0", 747 | "}" 748 | ], 749 | "description": "parallel snippet (for use inside a workflow)" 750 | }, 751 | "Workflow Sequence": { 752 | "prefix": "workflow sequence", 753 | "body": [ 754 | "sequence {", 755 | "\t$0", 756 | "}" 757 | ], 758 | "description": "sequence snippet (for use inside a workflow)" 759 | } 760 | } 761 | --------------------------------------------------------------------------------