├── Tooling
├── DscOperations
│ ├── DscOperations.psm1
│ ├── Invoke-DscApply.ps1
│ ├── Clear-DscTemporaryModule.ps1
│ ├── Invoke-DscPull.ps1
│ ├── Clear-DscEventLog.ps1
│ ├── DscOperations.psd1
│ └── Set-DscClient.ps1
├── DscConfiguration
│ ├── New-Credential.ps1
│ ├── Remove-PlainTextPassword.ps1
│ ├── New-ConfigurationDataStore.ps1
│ ├── Get-Hashtable.ps1
│ ├── Get-ApplicationConfigurationData.ps1
│ ├── Get-CredentialConfigurationData.ps1
│ ├── Add-NodeRoleFromServiceConfigurationData.Tests.ps1
│ ├── Get-ServiceConfigurationData.ps1
│ ├── Get-SiteDataConfigurationData.ps1
│ ├── Get-AllNodesConfigurationData.ps1
│ ├── ConvertTo-CredentialLookup.ps1
│ ├── Test-LocalCertificate.ps1
│ ├── Add-EncryptedPassword.ps1
│ ├── Add-NodeRoleFromServiceConfigurationData.ps1
│ ├── Get-EncryptedPassword.ps1
│ ├── DscConfiguration.psm1
│ ├── Get-ConfigurationData.ps1
│ ├── DscConfiguration.psd1
│ ├── New-DscNodeMetadata.ps1
│ ├── ConvertTo-EncryptedFile.ps1
│ └── ConvertFrom-EncryptedFile.ps1
├── dscbuild
│ ├── Test-DscResourceIsValid.ps1
│ ├── Invoke-DscResourceUnitTest.ps1
│ ├── Clear-CachedDscResource.ps1
│ ├── Clear-InstalledDscResource.ps1
│ ├── Compress-DscResourceModule.ps1
│ ├── New-DscChecksumFile.ps1
│ ├── Publish-DscToolModule.ps1
│ ├── Publish-DscConfiguration.ps1
│ ├── Publish-DscResourceModule.ps1
│ ├── Copy-CurrentDscResource.ps1
│ ├── DscResourceWmiClass.ps1
│ ├── New-DscZipFile.ps1
│ ├── Assert-DestinationDirectory.ps1
│ ├── Get-DscResourceVersion.ps1
│ ├── Where-DscResource.ps1
│ ├── DscBuild.psd1
│ ├── Invoke-DscConfiguration.ps1
│ ├── Where-DscResource.Tests.ps1
│ ├── DscBuild.psm1
│ ├── Invoke-DscBuild.ps1
│ ├── Update-ModuleMetadataVersion.ps1
│ ├── Resolve-ConfigurationProperty.Tests.ps1
│ └── Resolve-ConfigurationProperty.ps1
├── cDscDiagnostics
│ ├── cDscDiagnostics.psd1
│ ├── cDscDiagnosticsFormat.ps1xml
│ └── cDscDiagnostics.psm1.Tests.ps1
├── DscDevelopment
│ ├── Test-MofFile.ps1
│ ├── DscDevelopment.psm1
│ ├── DscResourceTemplate.Tests.ps1
│ ├── DscResourceTemplate.psm1
│ ├── New-DscResourceFromModule.ps1
│ ├── DscDevelopment.psd1
│ ├── New-DscCompositeResource.ps1
│ ├── Test-DscBuild.ps1
│ ├── Deserializer.ps1
│ └── New-MofFile.ps1
└── cDscResourceDesigner
│ └── cDscResourceDesigner.psd1
├── .gitignore
├── .gitattributes
├── LICENSE.txt
├── README.md
└── README.old.md
/Tooling/DscOperations/DscOperations.psm1:
--------------------------------------------------------------------------------
1 | . $psscriptroot\Set-DscClient.ps1
2 | . $psscriptroot\Invoke-DscPull.ps1
3 | . $psscriptroot\Clear-DscEventLog.ps1
4 | . $psscriptroot\Clear-DscTemporaryModule.ps1
5 | . $psscriptroot\Invoke-DscApply.ps1
6 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/New-Credential.ps1:
--------------------------------------------------------------------------------
1 | function New-Credential
2 | {
3 | param ($username, $password)
4 | $securepassword = $password | ConvertTo-SecureString -AsPlainText -Force
5 | return (New-Object System.Management.Automation.PSCredential -ArgumentList $username, $securepassword)
6 | }
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/Remove-PlainTextPassword.ps1:
--------------------------------------------------------------------------------
1 | function Remove-PlainTextPassword
2 | {
3 | param (
4 | [parameter()]
5 | [string]
6 | $path
7 | )
8 |
9 | Start-Sleep -seconds 2
10 | Write-Verbose "Removing plain text credentials from $path"
11 | Remove-Item $path -Confirm:$false -Force
12 | }
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .hg/**
2 | .hgignore
3 | Configuration/**
4 | **.sublime-workspace
5 | **.sublime-project
6 | Resources/StackExchangeConfigurations/
7 | Resources/StackExchangeResources/DSCResources/StackExchange_IISKeys/
8 | Resources/StackExchangeResources/DSCResources/StackExchange_MetaConfiguration/
9 | Resources/StackExchangeResources/DSCResources/StackExchange_ScollectorInstaller/
10 | Tooling/Pester/
--------------------------------------------------------------------------------
/Tooling/dscbuild/Test-DscResourceIsValid.ps1:
--------------------------------------------------------------------------------
1 | function Test-DscResourceIsValid {
2 | [cmdletbinding(SupportsShouldProcess=$true)]
3 | param ()
4 |
5 |
6 | if ( Test-BuildResource ) {
7 | if ($pscmdlet.shouldprocess("modules from $($script:DscBuildParameters.ProgramFilesModuleDirectory)")) {
8 | dir $script:DscBuildParameters.ProgramFilesModuleDirectory |
9 | Where-DscResource -IsValid |
10 | Out-Null
11 | }
12 | }
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/Invoke-DscResourceUnitTest.ps1:
--------------------------------------------------------------------------------
1 | function Invoke-DscResourceUnitTest {
2 | [cmdletbinding(SupportsShouldProcess=$true)]
3 | param ()
4 |
5 | if ( Test-BuildResource ) {
6 | if ($pscmdlet.shouldprocess($script:DscBuildParameters.SourceResourceDirectory)) {
7 | Write-Verbose 'Running Resource Unit Tests.'
8 | Invoke-Pester -relative_path $script:DscBuildParameters.SourceResourceDirectory
9 | }
10 | }
11 | }
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/New-ConfigurationDataStore.ps1:
--------------------------------------------------------------------------------
1 | function New-DscConfigurationDataStore {
2 | param (
3 | [parameter(mandatory)]
4 | $Path
5 | )
6 | $Path = (resolve-path $path).Path
7 |
8 | mkdir (join-path $Path 'AllNodes') | out-null
9 | mkdir (join-path $Path 'Credentials') | out-null
10 | mkdir (join-path $Path 'Services') | out-null
11 | mkdir (join-path $Path 'SiteData') | out-null
12 | mkdir (join-path $Path 'Applications') | out-null
13 |
14 | $script:ConfigurationDataPath = $Path
15 | }
16 |
--------------------------------------------------------------------------------
/Tooling/cDscDiagnostics/cDscDiagnostics.psd1:
--------------------------------------------------------------------------------
1 |
2 |
3 | @{
4 |
5 |
6 | ModuleVersion = '1.0.2'
7 | GUID = 'ef098cb4-f7e9-4763-b636-0cd9799e1c9a'
8 |
9 | Author = 'Microsoft Corporation'
10 | CompanyName = 'Microsoft Corporation'
11 | Copyright = '(c) 2013 Microsoft Corporation. All rights reserved.'
12 |
13 | Description = 'Module to help in reading details from DSC events'
14 |
15 | PowerShellVersion = '4.0'
16 |
17 | CLRVersion = '4.0'
18 |
19 | FunctionsToExport = @("*")
20 |
21 | NestedModules = @('cDscDiagnostics.psm1')
22 | }
23 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/Clear-CachedDscResource.ps1:
--------------------------------------------------------------------------------
1 | function Clear-CachedDscResource {
2 | [cmdletbinding(SupportsShouldProcess=$true)]
3 | param()
4 |
5 | if ($pscmdlet.ShouldProcess($env:computername)) {
6 | Write-Verbose 'Stopping any existing WMI processes to clear cached resources.'
7 | Get-process -Name WmiPrvSE -erroraction silentlycontinue | stop-process -force
8 |
9 | Write-Verbose 'Clearing out any tmp WMI classes from tested resources.'
10 | Get-DscResourceWmiClass -class tmp* | remove-DscResourceWmiClass
11 | }
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/Get-Hashtable.ps1:
--------------------------------------------------------------------------------
1 | function Get-Hashtable
2 | {
3 | # Path to PSD1 file to evaluate
4 | param (
5 | [parameter(
6 | Position = 0,
7 | ValueFromPipelineByPropertyName,
8 | Mandatory
9 | )]
10 | [ValidateNotNullOrEmpty()]
11 | [Alias('FullName')]
12 | [string]
13 | $Path
14 | )
15 | process
16 | {
17 | Write-Verbose "Loading data from $Path."
18 | invoke-expression "DATA { $(get-content -raw -path $path) }"
19 | }
20 | }
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/Get-ApplicationConfigurationData.ps1:
--------------------------------------------------------------------------------
1 | function Get-ApplicationConfigurationData {
2 | [cmdletbinding()]
3 | param ()
4 | if (($script:ConfigurationData.Applications.Keys.Count -eq 0))
5 | {
6 | Write-Verbose "Processing Applications from $($script:ConfigurationDataPath))."
7 | foreach ( $item in (dir (join-path $script:ConfigurationDataPath 'Applications\*.psd1')) )
8 | {
9 | Write-Verbose "Loading data for site $($item.basename) from $($item.fullname)."
10 | $script:ConfigurationData.Applications += (Get-Hashtable $item.FullName)
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Tooling/DscDevelopment/Test-MofFile.ps1:
--------------------------------------------------------------------------------
1 | function Test-MofFile {
2 | param (
3 | [parameter(Mandatory,Position=0)]
4 | $NodeId,
5 | [parameter(Position=1)]
6 | $ConfigurationPath = 'd:\temp\cimschema'
7 | )
8 | Get-Process WmiPrvSE | Stop-Process -Force
9 | start-job -argumentlist $NodeId -scriptblock {
10 | param ($NodeId)
11 | ipmo dscdevelopment
12 | reset-cimclasscache
13 | dir 'C:\Program Files\WindowsPowerShell\Modules\' -Recurse -Include *.schema.mof | Add-CachedCimClass
14 | Import-CimInstances -MofInstanceFilePath (join-path $ConfigurationPath "$NodeId.mof")
15 | } | receive-job -wait
16 | }
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/Get-CredentialConfigurationData.ps1:
--------------------------------------------------------------------------------
1 | function Get-CredentialConfigurationData
2 | {
3 | [cmdletbinding()]
4 | param ()
5 | if (($script:ConfigurationData.Credentials.Keys.Count -eq 0) )
6 | {
7 | Write-Verbose "Processing Credentials from $($script:ConfigurationDataPath))."
8 |
9 | $script:ConfigurationData.Credentials = dir (join-path $script:ConfigurationDataPath 'Credentials\*.psd1.encrypted') |
10 | Get-DscEncryptedPassword -StoreName {$_.Name -replace '\.encrypted' -replace '\.psd1'} |
11 | ConvertTo-CredentialLookup
12 |
13 | }
14 | }
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/Add-NodeRoleFromServiceConfigurationData.Tests.ps1:
--------------------------------------------------------------------------------
1 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path
2 | $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.ps1", ".psm1")
3 | if (-not (Test-Path $sut))
4 | {
5 | $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.ps1", ".ps1")
6 | }
7 | $pathtosut = join-path $here $sut
8 | if (-not (Test-Path $pathtosut))
9 | {
10 | Write-Error "Failed to find script to test at $pathtosut"
11 | }
12 |
13 |
14 | iex ( gc $pathtosut -Raw )
15 |
16 | describe 'how Add-NodeRoleFromServiceConfigurationData works' {
17 | context 'when ' {}
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/Get-ServiceConfigurationData.ps1:
--------------------------------------------------------------------------------
1 | function Get-ServiceConfigurationData
2 | {
3 | [cmdletbinding()]
4 | param ()
5 | if (($script:ConfigurationData.Services.Keys.Count -eq 0))
6 | {
7 | Write-Verbose "Processing Services from $($script:ConfigurationDataPath))."
8 | foreach ( $item in (dir (join-path $script:ConfigurationDataPath 'Services\*.psd1')) )
9 | {
10 | Write-Verbose "Loading data for site $($item.basename) from $($item.fullname)."
11 | $script:ConfigurationData.Services.Add($item.BaseName, (Get-Hashtable $item.FullName))
12 | }
13 | }
14 | }
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/Get-SiteDataConfigurationData.ps1:
--------------------------------------------------------------------------------
1 | function Get-SiteDataConfigurationData
2 | {
3 | [cmdletbinding()]
4 | param ()
5 | if (($script:ConfigurationData.SiteData.Keys.Count -eq 0))
6 | {
7 | Write-Verbose "Processing SiteData from $($script:ConfigurationDataPath))."
8 | foreach ( $item in (dir (join-path $script:ConfigurationDataPath 'SiteData\*.psd1')) )
9 | {
10 | Write-Verbose "Loading data for site $($item.basename) from $($item.fullname)."
11 | $script:ConfigurationData.SiteData.Add($item.BaseName, (Get-Hashtable $item.FullName))
12 | }
13 | }
14 | }
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/Get-AllNodesConfigurationData.ps1:
--------------------------------------------------------------------------------
1 | function Get-AllNodesConfigurationData
2 | {
3 | [cmdletbinding()]
4 | param ()
5 | if (($script:ConfigurationData.AllNodes.Count -eq 0))
6 | {
7 | Write-Verbose "Processing AllNodes from $($script:ConfigurationDataPath)."
8 | $script:ConfigurationData.AllNodes = @()
9 | dir (join-path $script:ConfigurationDataPath 'AllNodes\*.psd1') |
10 | Get-Hashtable |
11 | foreach-object {
12 | Write-Verbose "Adding Name: $($_.Name)"
13 | $script:ConfigurationData.AllNodes += $_
14 | }
15 | }
16 | }
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
24 | *.ps1 -text diff merge
25 | *.psm1 -text diff merge
26 | *.psd1 -text diff merge
27 | *.mof -text diff merge
28 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/Clear-InstalledDscResource.ps1:
--------------------------------------------------------------------------------
1 | function Clear-InstalledDscResource {
2 | [cmdletbinding(SupportsShouldProcess=$true)]
3 | param ()
4 |
5 | if ( Test-BuildResource ) {
6 | if ($pscmdlet.shouldprocess("$($script:DscBuildParameters.ProgramFilesModuleDirectory)) based on modules in $($script:DscBuildParameters.SourceResourceDirectory)")) {
7 | Write-Verbose 'Clearing Program Files from old configuration modules.'
8 | dir $script:DscBuildParameters.SourceResourceDirectory |
9 | Foreach { dir (join-path $script:DscBuildParameters.ProgramFilesModuleDirectory $_.Name) -erroraction SilentlyContinue } |
10 | Remove-Item -Force -Recurse
11 | }
12 | }
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/ConvertTo-CredentialLookup.ps1:
--------------------------------------------------------------------------------
1 | function ConvertTo-CredentialLookup
2 | {
3 | param (
4 | [parameter(
5 | ValueFromPipeline,
6 | Mandatory
7 | )]
8 | [System.Collections.Hashtable]
9 | $PasswordHashtable
10 | )
11 | begin
12 | {
13 | $CredentialHashtable = @{}
14 | }
15 | Process
16 | {
17 | foreach ($key in $PasswordHashtable.Keys)
18 | {
19 | Write-Verbose "Creating new credential for $key"
20 | $CredentialHashtable.Add($key, (New-Credential -username $key -password $PasswordHashtable[$key]))
21 | }
22 | }
23 | end
24 | {
25 | $CredentialHashtable
26 | }
27 | }
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/Test-LocalCertificate.ps1:
--------------------------------------------------------------------------------
1 | function Test-LocalCertificate
2 | {
3 | param ()
4 | if (-not [string]::IsNullOrEmpty($LocalCertificateThumbprint))
5 | {
6 | Write-Verbose 'LocalCertificateThumbprint is present.'
7 | if (Test-Path $LocalCertificatePath)
8 | {
9 | Write-Verbose 'Certficate is present in the local certificate store.'
10 | return $true
11 | }
12 | else
13 | {
14 | Write-Warning 'Certficate specified is not in the certificate store.'
15 | return $false
16 | }
17 | }
18 | else
19 | {
20 | Write-Warning 'No local certificate supplied or configured with the DSC Local Configuration Manager.'
21 | return $false
22 | }
23 | }
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Tooling/DscDevelopment/DscDevelopment.psm1:
--------------------------------------------------------------------------------
1 | . $PSScriptRoot\New-DscResourceFromModule.ps1
2 | . $PSScriptRoot\New-MofFile.ps1
3 | . $PSScriptRoot\Test-DscBuild.ps1
4 | . $PSScriptRoot\Test-MofFile.ps1
5 | . $PSScriptRoot\New-DscCompositeResource.ps1
6 | . $PSScriptRoot\Deserializer.ps1
7 |
8 |
9 |
10 | function Get-Hashtable
11 | {
12 | # Path to PSD1 file to evaluate
13 | param (
14 | [parameter(
15 | Position = 0,
16 | ValueFromPipelineByPropertyName,
17 | Mandatory
18 | )]
19 | [ValidateNotNullOrEmpty()]
20 | [Alias('FullName')]
21 | [string]
22 | $Path
23 | )
24 | process
25 | {
26 | Write-Verbose "Loading data from $Path."
27 | invoke-expression "DATA { $(get-content -raw -path $path) }"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/Compress-DscResourceModule.ps1:
--------------------------------------------------------------------------------
1 | function Compress-DscResourceModule {
2 | [cmdletbinding(SupportsShouldProcess=$true)]
3 | param ()
4 |
5 | if ( Test-BuildResource ) {
6 | if ($pscmdlet.shouldprocess("from $($script:DscBuildParameters.ProgramFilesModuleDirectory) to $($script:DscBuildParameters.ModuleOutputPath)")) {
7 | Write-Verbose "Compressing tested modules: "
8 | foreach ($module in $script:DscBuildParameters.TestedModules) {
9 | Write-Verbose "`t$module"
10 | }
11 |
12 | Get-Item $script:DscBuildParameters.TestedModules |
13 | New-DscZipFile -ZipFile { join-path $script:DscBuildParameters.ModuleOutputPath "$($_.Name)" } -Force |
14 | Foreach-Object {Write-Verbose "New compressed resource module $($_.fullname)"}
15 | }
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/New-DscChecksumFile.ps1:
--------------------------------------------------------------------------------
1 | function New-DscChecksumFile
2 | {
3 | param (
4 | [parameter(ValueFromPipeline)]
5 | [IO.FileSystemInfo]
6 | $InputObject
7 | )
8 | process
9 | {
10 | $checksumfile = "$($inputobject.fullname).checksum"
11 | $hash = (Get-FileHash -path $inputobject.fullname).hash
12 | Write-Debug "Hash for $($InputObject.fullname) is $hash."
13 | if (test-path $checksumfile)
14 | {
15 | Write-verbose "Removing previous checksum file $checksumfile"
16 | remove-item $checksumfile -Force
17 | }
18 | [io.file]::AppendallText($checksumfile, $hash)
19 | Write-Debug "Hash written to file is $(Get-Content $checksumfile -Raw)."
20 | Write-Verbose "Wrote hash for $($InputObject.FullName) to $checksumfile"
21 | }
22 | }
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/Publish-DscToolModule.ps1:
--------------------------------------------------------------------------------
1 | function Publish-DscToolModule {
2 | [cmdletbinding(SupportsShouldProcess=$true)]
3 | param ()
4 |
5 | if (Test-BuildTools) {
6 | if ($pscmdlet.shouldprocess($script:DscBuildParameters.SourceToolDirectory)) {
7 | dir $script:DscBuildParameters.SourceToolDirectory -exclude '.g*', '.hg' |
8 | Test-ModuleVersion -Destination $script:DscBuildParameters.DestinationToolsDirectory |
9 | copy-item -recurse -force -destination $script:DscBuildParameters.DestinationToolsDirectory
10 | }
11 | }
12 | <#
13 | if (Test-BuildResource) {
14 | if ($pscmdlet.shouldprocess($script:DscBuildParameters.TestedModules)) {
15 | Get-Item $script:DscBuildParameters.TestedModules |
16 | Test-ModuleVersion -Destination $script:DscBuildParameters.DestinationToolsDirectory |
17 | copy-item -recurse -force -destination $script:DscBuildParameters.DestinationToolsDirectory
18 | }
19 | }
20 | #>
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/Tooling/DscDevelopment/DscResourceTemplate.Tests.ps1:
--------------------------------------------------------------------------------
1 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path
2 | $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.ps1", ".psm1")
3 | $pathtosut = join-path $here $sut
4 |
5 | iex (gc $pathtosut -Raw)
6 |
7 | Describe 'how Test-TargetResource responds' {
8 | Context 'when ' {
9 | $expected = ''
10 | $result = ''
11 |
12 | It "should call all the mocks" {
13 | Assert-VerifiableMocks
14 | }
15 | It 'should ' {
16 | $result | should be ($expected)
17 | }
18 |
19 | }
20 | }
21 |
22 | Describe 'how Set-TargetResource responds' {
23 | Context 'when ' {
24 | $expected = ''
25 | $result = ''
26 |
27 | It "should call all the mocks" {
28 | Assert-VerifiableMocks
29 | }
30 | It 'should ' {
31 | $result | should be ($expected)
32 | }
33 |
34 | }
35 | }
36 |
37 |
38 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 PowerShell.Org
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | 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, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/Tooling/dscbuild/Publish-DscConfiguration.ps1:
--------------------------------------------------------------------------------
1 | function Publish-DscConfiguration {
2 | [cmdletbinding(SupportsShouldProcess=$true)]
3 | param ()
4 |
5 | if ( Test-BuildConfiguration ) {
6 |
7 | Write-Verbose 'Moving Processed Resource Modules from '
8 | Write-Verbose "`t$($script:DscBuildParameters.ConfigurationOutputPath) to"
9 | Write-Verbose "`t$($script:DscBuildParameters.DestinationConfigurationDirectory)"
10 | if ($pscmdlet.shouldprocess("$($script:DscBuildParameters.ConfigurationOutputPath) to $($script:DscBuildParameters.DestinationConfigurationDirectory)")) {
11 | dir (join-path $script:DscBuildParameters.ConfigurationOutputPath '*.mof') |
12 | foreach-object { Write-Verbose "Moving $($_.name) to $($script:DscBuildParameters.DestinationConfigurationDirectory)"; $_ } |
13 | Move-Item -Destination $script:DscBuildParameters.DestinationConfigurationDirectory -force -PassThru |
14 | New-DscChecksumFile -Verbose:$false
15 | }
16 | }
17 | else {
18 | Write-Warning "Skipping publishing configurations."
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/Tooling/DscOperations/Invoke-DscApply.ps1:
--------------------------------------------------------------------------------
1 | function Invoke-DscApply
2 | {
3 | <#
4 | .Synopsis
5 | Uses the Configuration Agent to apply the configuration that is pending.
6 | .Description
7 | Uses the Configuration Agent to apply the configuration that is pending.
8 | If there is no configuration pending, this method reapplies the current configuration.
9 | .Example
10 | Invoke-DscApply -CimSession SERVER01 -Verbose
11 | .Example
12 | 1..7 | Invoke-DscApply -CimSession {"SERVER0$_"}
13 |
14 | #>
15 | param (
16 | [parameter(ValueFromPipeline=$true, Position=0)]
17 | [alias('ComputerName', 'Name', '__Server')]
18 | $CimSession = $null
19 | )
20 |
21 | Process {
22 |
23 | $parameters = @{
24 | Namespace = 'root/microsoft/windows/desiredstateconfiguration'
25 | Class = 'MSFT_DscLocalConfigurationManager'
26 | MethodName = 'ApplyConfiguration'
27 | }
28 |
29 | Write-Verbose ""
30 | Write-Verbose "Forcing an application of configuration on $CimSession"
31 | Invoke-CimMethod @parameters @PSBoundParameters
32 | }
33 | }
34 |
35 |
36 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/Publish-DscResourceModule.ps1:
--------------------------------------------------------------------------------
1 | function Publish-DscResourceModule {
2 | [cmdletbinding(SupportsShouldProcess=$true)]
3 | param ()
4 |
5 | if ( Test-BuildResource ) {
6 | Write-Verbose 'Moving Processed Resource Modules from '
7 | Write-Verbose "`t$($script:DscBuildParameters.ModuleOutputPath) to"
8 | Write-Verbose "`t$($script:DscBuildParameters.DestinationModuleDirectory)"
9 |
10 | if ($pscmdlet.shouldprocess("$($script:DscBuildParameters.ModuleOutputPath) to $($script:DscBuildParameters.DestinationModuleDirectory)")) {
11 |
12 | Dir (join-path $script:DscBuildParameters.ModuleOutputPath '*.zip') |
13 | foreach-object { Write-Verbose "Checking if $($_.name) is already at $($script:DscBuildParameters.DestinationModuleDirectory)"; $_ } |
14 | Where-Object {-not (Test-Path (Join-Path $script:DscBuildParameters.DestinationModuleDirectory $_.name))} |
15 | foreach-object { Write-Verbose "Moving $($_.name) to $($script:DscBuildParameters.DestinationModuleDirectory)"; $_ } |
16 | Move-Item -Destination $script:DscBuildParameters.DestinationModuleDirectory -PassThru |
17 | New-DscChecksumFile -Verbose:$false
18 | }
19 | }
20 | }
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Tooling/cDscResourceDesigner/cDscResourceDesigner.psd1:
--------------------------------------------------------------------------------
1 | @{
2 | # Version number of this module.
3 | ModuleVersion = '1.0.1'
4 |
5 | # ID used to uniquely identify this module
6 | GUID = '74951b31-1aa5-472b-9109-738de1bca38f'
7 |
8 | # Author of this module
9 | Author = 'Microsoft Corporation'
10 |
11 | # Company or vendor of this module
12 | CompanyName = 'Microsoft Corporation'
13 |
14 | # Copyright statement for this module
15 | Copyright = '(c) 2014 Microsoft Corporation. All rights reserved.'
16 |
17 | # Description of the functionality provided by this module
18 | Description = 'This module is meant to assist with the development and testing of DSC Resources.'
19 |
20 | # Minimum version of the Windows PowerShell engine required by this module
21 | PowerShellVersion = '4.0'
22 |
23 | # Minimum version of the common language runtime (CLR) required by this module
24 | CLRVersion = '4.0'
25 |
26 | # Script module or binary module file associated with this manifest.
27 | RootModule = 'cDscResourceDesigner.psm1'
28 |
29 | # Functions to export from this module
30 | FunctionsToExport = @('New-cDscResourceProperty',
31 | 'New-cDscResource',
32 | 'Update-cDscResource',
33 | 'Test-cDscResource',
34 | 'Test-cDscSchema',
35 | 'Import-cDscSchema')
36 |
37 | # Cmdlets to export from this module
38 | CmdletsToExport = '*'
39 | }
40 |
--------------------------------------------------------------------------------
/Tooling/DscOperations/Clear-DscTemporaryModule.ps1:
--------------------------------------------------------------------------------
1 | function Clear-DscTemporaryModule {
2 | <#
3 | .Synopsis
4 | Deletes any directories in $env:programfiles\windowspowershell\modules\ whose names end in "_tmp".
5 | .Description
6 | Deletes any directories in $env:programfiles\windowspowershell\modules\ whose names end in "_tmp".
7 | This is due to a failure of the LCM in WMF4 to properly clean up modules with resources when a newer module is retrieved from a pull server.
8 | .Example
9 | Clear-DscTemporaryModule -ComputerName OR-WEB01
10 | #>
11 | param (
12 | #Name of the computer(s) to target.
13 | [Parameter(
14 | ValueFromPipeline=$true,
15 | ValueFromPipelineByPropertyName=$true,
16 | Position='0'
17 | )]
18 | [ValidateNotNullOrEmpty()]
19 | [Alias('__Server', 'Name')]
20 | [string[]]
21 | $ComputerName = $env:COMPUTERNAME,
22 |
23 | #Alternate credentials to use in connecting to the remote computer(s).
24 | [Parameter(
25 | Position=2
26 | )]
27 | [System.Management.Automation.Credential()]
28 | $Credential = [System.Management.Automation.PSCredential]::Empty
29 | )
30 |
31 | process {
32 | invoke-command @psboundparameters {
33 | dir $env:ProgramFiles\windowspowershell\modules\*_tmp |
34 | remove-item -recurse -force
35 | get-process WmiPrvSE | stop-process -force
36 | }
37 | }
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/Tooling/DscDevelopment/DscResourceTemplate.psm1:
--------------------------------------------------------------------------------
1 | # Fallback message strings in en-US
2 | DATA localizedData
3 | {
4 | # same as culture = "en-US"
5 | ConvertFrom-StringData @'
6 | '@
7 | }
8 |
9 | if (Test-Path $PSScriptRoot\en-us)
10 | {
11 | $ModuleName = Split-Path $PSScriptRoot -Leaf
12 | Import-LocalizedData LocalizedData -filename "$($ModuleName)Provider.psd1"
13 | }
14 |
15 | function Get-TargetResource
16 | {
17 | [OutputType([Hashtable])]
18 | param (
19 | [parameter(Mandatory = $true)]
20 | [ValidateNotNullOrEmpty()]
21 | [string]
22 | $Name,
23 | [ValidateSet('Present','Absent')]
24 | [string]
25 | $Ensure = 'Present'
26 | )
27 |
28 | #Needs to return a hashtable that returns the current
29 | #status of the configuration component
30 | $Configuration = @{
31 | Name = $Name
32 | }
33 | return $Configuration
34 | }
35 |
36 | function Set-TargetResource
37 | {
38 | param (
39 | [parameter(Mandatory = $true)]
40 | [ValidateNotNullOrEmpty()]
41 | [string]
42 | $Name,
43 | [parameter()]
44 | [ValidateSet('Present','Absent')]
45 | [string]
46 | $Ensure = 'Present'
47 | )
48 |
49 |
50 | }
51 |
52 | function Test-TargetResource
53 | {
54 | [OutputType([boolean])]
55 | param (
56 | [parameter(Mandatory = $true)]
57 | [ValidateNotNullOrEmpty()]
58 | [string]
59 | $Name,
60 | [parameter()]
61 | [ValidateSet('Present','Absent')]
62 | [string]
63 | $Ensure = 'Present'
64 | )
65 |
66 | #Needs to return a boolean
67 | return $true
68 | }
69 |
70 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/Copy-CurrentDscResource.ps1:
--------------------------------------------------------------------------------
1 | function Copy-CurrentDscResource {
2 | [cmdletbinding(SupportsShouldProcess=$true)]
3 | param ()
4 |
5 | Write-Verbose ''
6 | Write-Verbose "Pushing new configuration modules from $($script:DscBuildParameters.SourceResourceDirectory) to $($script:DscBuildParameters.ProgramFilesModuleDirectory)."
7 |
8 | if ($pscmdlet.shouldprocess("$($script:DscBuildParameters.SourceResourceDirectory) to $($script:DscBuildParameters.ProgramFilesModuleDirectory)")) {
9 | dir $script:DscBuildParameters.SourceResourceDirectory -exclude '.g*', '.hg' |
10 | Where-Object {$script:DscBuildParameters.ExcludedModules -notcontains $_.name} |
11 | Test-ModuleVersion -Destination $script:DscBuildParameters.ProgramFilesModuleDirectory |
12 | Copy-Item -Destination $script:DscBuildParameters.ProgramFilesModuleDirectory -Recurse -Force
13 | }
14 |
15 |
16 | }
17 |
18 | function Copy-CurrentDscTools {
19 | [cmdletbinding(SupportsShouldProcess=$true)]
20 | param ()
21 |
22 | Write-Verbose ''
23 | Write-Verbose "Pushing new tools modules from $($script:DscBuildParameters.SourceToolDirectory) to $($script:DscBuildParameters.CurrentToolsDirectory)."
24 |
25 | if ($pscmdlet.shouldprocess("$($script:DscBuildParameters.SourceToolDirectory) to $($script:DscBuildParameters.CurrentToolsDirectory)")) {
26 | dir $script:DscBuildParameters.SourceToolDirectory -exclude '.g*', '.hg' |
27 | Test-ModuleVersion -Destination $script:DscBuildParameters.CurrentToolsDirectory |
28 | Copy-Item -Destination $script:DscBuildParameters.CurrentToolsDirectory -Recurse -Force
29 | }
30 | Write-Verbose ''
31 | }
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/Add-EncryptedPassword.ps1:
--------------------------------------------------------------------------------
1 | function Add-DscEncryptedPassword
2 | {
3 | param (
4 | [parameter(mandatory)]
5 | [string]
6 | $StoreName,
7 | [parameter()]
8 | [string]
9 | $Path = (Join-path $script:ConfigurationDataPath 'Credentials'),
10 | [parameter()]
11 | [string]
12 | $UserName,
13 | [parameter()]
14 | [string]
15 | $Password
16 | )
17 |
18 | $EncryptedFilePath = Join-Path $Path "$StoreName.psd1.encrypted"
19 | $FilePath = Join-Path $Path "$StoreName.psd1"
20 |
21 | $Credentials = @{}
22 | if (Test-Path $EncryptedFilePath)
23 | {
24 | $Credentials += Get-DscEncryptedPassword -StoreName $StoreName -Path $Path
25 | Remove-Item $EncryptedFilePath
26 | foreach ($key in $Credentials.Keys)
27 | {
28 | Write-Verbose "Found credentials for $username."
29 | }
30 | }
31 |
32 | $Credentials.Add($UserName, $Password)
33 | Write-Verbose "Adding credentials for $Username. ($($credentials.keys.count) total.)"
34 |
35 | if (Test-Path $FilePath)
36 | {
37 | Remove-Item $FilePath -Confirm:$false
38 | }
39 |
40 | '@{' | Out-File $FilePath
41 | foreach ($key in $Credentials.Keys)
42 | {
43 | Write-Verbose "Persisting credentials for $key to disk."
44 | "'$key' = '$($Credentials[$key])'" | Out-File $FilePath -Append
45 | }
46 | '}' | Out-File $FilePath -Append
47 |
48 | Write-Verbose 'Encrypting credentials.'
49 | ConvertTo-EncryptedFile -Path $FilePath -CertificatePath $LocalCertificatePath
50 | Remove-PlainTextPassword $FilePath
51 | }
52 |
53 | Set-Alias -Name 'Add-EncryptedPassword' -value 'Add-DscEncryptedPassword'
54 |
55 |
--------------------------------------------------------------------------------
/Tooling/DscDevelopment/New-DscResourceFromModule.ps1:
--------------------------------------------------------------------------------
1 | function New-DscResourceFromModule
2 | {
3 | param
4 | (
5 | [ValidateScript({Test-Path $_ -PathType 'Container'})]
6 | [parameter(Position = 0, Mandatory = $true)]
7 | [string] $ModulePath,
8 | [string] $Author,
9 | [string] $CompanyName
10 | )
11 | $ModuleName = Split-Path -Path $ModulePath -Leaf
12 | $psd1 = Join-Path -Path $ModulePath -ChildPath "$ModuleName.psd1"
13 | $psm1 = "$ModuleName.psm1"
14 | New-ModuleManifest -Path $psd1 -RootModule $psm1 -Author $Author -CompanyName $CompanyName -FunctionsToExport 'Get-TargetResource', 'Set-TargetResource', 'Test-TargetResource'
15 | New-MofFile -Path $ModulePath -Verbose
16 | }
17 |
18 | function New-DscResourceShell
19 | {
20 | param
21 | (
22 | [ValidateScript({Test-Path $_ -PathType 'Container'})]
23 | [parameter(Position = 0, Mandatory = $true)]
24 | [string] $Path
25 | )
26 |
27 | $ModuleName = Split-Path -Path $Path -Leaf
28 | $ModuleFile = Join-Path -Path $Path -ChildPath "$ModuleName.psm1"
29 | $ModuleFileTests = Join-Path -Path $Path -ChildPath "$ModuleName.Tests.ps1"
30 |
31 | if (-not (Test-Path $Path))
32 | {
33 | mkdir $Path
34 | }
35 | if (-not (Test-Path $ModuleFile))
36 | {
37 | Write-Verbose "Copying template for the module from $PSScriptRoot\DscResourceTemplate.psm1."
38 | Write-Verbose "`tto $ModuleFile"
39 | Copy-Item -Path "$PSScriptRoot\DscResourceTemplate.psm1" -Destination $ModuleFile -Force
40 | }
41 | if (-not (Test-Path $ModuleFileTests))
42 | {
43 | Write-Verbose "Copying template for the module tests from $PSScriptRoot\DscResourceTemplate.Tests.ps1."
44 | Write-Verbose "`tto $ModuleFileTests"
45 | Copy-Item -Path "$PSScriptRoot\DscResourceTemplate.Tests.ps1" -Destination $ModuleFileTests -Force
46 | }
47 | }
--------------------------------------------------------------------------------
/Tooling/dscbuild/DscResourceWmiClass.ps1:
--------------------------------------------------------------------------------
1 | function Get-DscResourceWmiClass {
2 | <#
3 | .Synopsis
4 | Retrieves WMI classes from the DSC namespace.
5 | .Description
6 | Retrieves WMI classes from the DSC namespace.
7 | .Example
8 | Get-DscResourceWmiClass -Class tmp*
9 | .Example
10 | Get-DscResourceWmiClass -Class 'MSFT_UserResource'
11 | #>
12 | param (
13 | #The WMI Class name search for. Supports wildcards.
14 | [parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
15 | [Alias('Name')]
16 | [string]
17 | $Class
18 | )
19 | begin {
20 | $DscNamespace = "root/Microsoft/Windows/DesiredStateConfiguration"
21 | }
22 | process {
23 | Get-wmiobject -Namespace $DscNamespace -list @psboundparameters
24 | }
25 | }
26 |
27 | function Remove-DscResourceWmiClass {
28 | <#
29 | .Synopsis
30 | Removes a WMI class from the DSC namespace.
31 | .Description
32 | Removes a WMI class from the DSC namespace.
33 | .Example
34 | Get-DscResourceWmiClass -Class tmp* | Remove-DscResourceWmiClass
35 | .Example
36 | Remove-DscResourceWmiClass -Class 'tmpD460'
37 |
38 | #>
39 | param (
40 | #The WMI Class name to remove. Supports wildcards.
41 | [parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
42 | [alias('Name')]
43 | [string]
44 | $ResourceType
45 | )
46 | begin {
47 | $DscNamespace = "root/Microsoft/Windows/DesiredStateConfiguration"
48 | }
49 | process {
50 | #Have to use WMI here because I can't find how to delete a WMI instance via the CIM cmdlets.
51 | (Get-wmiobject -Namespace $DscNamespace -list -Class $ResourceType).psbase.delete()
52 | }
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/Add-NodeRoleFromServiceConfigurationData.ps1:
--------------------------------------------------------------------------------
1 | function Add-NodeRoleFromServiceConfigurationData {
2 | [cmdletbinding()]
3 | param()
4 |
5 | Write-Verbose 'Adding Roles to service node.'
6 | $UpdatedNodes = $script:ConfigurationData.AllNodes
7 | foreach ($Service in $script:ConfigurationData.Services.Keys) {
8 |
9 | $UpdatedNodes = $UpdatedNodes |
10 | Add-ServiceConfigurationData -ServiceName $Service
11 |
12 | }
13 | $script:ConfigurationData.AllNodes = $UpdatedNodes
14 | }
15 |
16 | function Add-ServiceConfigurationData {
17 | [cmdletbinding()]
18 | param (
19 | [string]
20 | $ServiceName,
21 | [parameter(ValueFromPipeline)]
22 | [System.Collections.Hashtable]
23 | $InputObject
24 | )
25 | process {
26 | Write-Verbose "`tProcessing $($InputObject.Name) for Service $ServiceName"
27 |
28 | if ($script:ConfigurationData.Services[$ServiceName].Nodes -contains $InputObject.Name) {
29 | $InputObject = $InputObject | Assert-RolesConfigurationData -ServiceName $ServiceName
30 | }
31 |
32 | Write-Verbose "`t`tRoles on $($InputObject.Name) are: $($InputObject.Roles.Keys)"
33 | $InputObject
34 | }
35 | }
36 |
37 |
38 |
39 | function Assert-RolesConfigurationData {
40 | [cmdletbinding()]
41 | param (
42 | [string]
43 | $ServiceName,
44 | [parameter(ValueFromPipeline)]
45 | [System.Collections.Hashtable]
46 | $InputObject
47 | )
48 |
49 | process {
50 | if (-not ($InputObject.ContainsKey('Roles'))) {
51 | $InputObject.Roles = @{}`
52 | }
53 | foreach ($Role in $script:ConfigurationData.Services[$ServiceName].Roles)
54 | {
55 | if ($InputObject.Roles.ContainsKey($Role)) {
56 | $InputObject.Roles[$Role] += $ServiceName
57 | }
58 | else {
59 | $InputObject.Roles.Add($Role, [string[]]$ServiceName)
60 | }
61 | }
62 | $InputObject
63 | }
64 | }
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/Get-EncryptedPassword.ps1:
--------------------------------------------------------------------------------
1 | function Get-DscEncryptedPassword
2 | {
3 | [cmdletbinding(DefaultParameterSetName='ByStoreName')]
4 | param (
5 | [parameter(
6 | ParameterSetName = 'ByStoreName',
7 | ValueFromPipelineByPropertyName,
8 | Mandatory
9 | )]
10 | [Alias('BaseName')]
11 | [string]
12 | $StoreName,
13 | [parameter(
14 | ParameterSetName = 'ByStoreName'
15 | )]
16 | [string]
17 | $Path = (Join-path $script:ConfigurationDataPath 'Credentials'),
18 | [parameter(
19 | ParameterSetName = 'ByPipeline',
20 | ValueFromPipelineByPropertyName,
21 | Mandatory
22 | )]
23 | [Alias('FullName')]
24 | [string]
25 | $EncryptedFilePath,
26 | [parameter()]
27 | [string[]]
28 | $UserName
29 | )
30 | process
31 | {
32 | if (Test-LocalCertificate)
33 | {
34 | if (-not $PSBoundParameters.ContainsKey('EncryptedFilePath'))
35 | {
36 | $EncryptedFilePath = Join-Path $Path "$StoreName.psd1.encrypted"
37 | }
38 |
39 | Write-Verbose "Decrypting $EncryptedFilePath."
40 | $DecryptedDataFile = ConvertFrom-EncryptedFile -path $EncryptedFilePath -CertificatePath $LocalCertificatePath
41 |
42 | Write-Verbose "Loading $($DecryptedDataFile.BaseName) into Credentials."
43 | $Credentials = (Get-Hashtable $DecryptedDataFile.FullName)
44 |
45 | Remove-PlainTextPassword $DecryptedDataFile.FullName
46 |
47 | if ($PSBoundParameters.ContainsKey('UserName'))
48 | {
49 | $CredentialsToReturn = @{}
50 | foreach ($User in $UserName)
51 | {
52 | $CredentialsToReturn.Add($User,$Credentials[$User])
53 | }
54 | return $CredentialsToReturn
55 | }
56 | else
57 | {
58 | return $Credentials
59 | }
60 | }
61 | }
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/Tooling/DscOperations/Invoke-DscPull.ps1:
--------------------------------------------------------------------------------
1 | function Invoke-DscPull
2 | {
3 | <#
4 | .Synopsis
5 | Forces a consistency check on a targeted server.
6 | .Description
7 | Forces a consistency check on a targeted server. The LCM will run local consistency checks.
8 | In order to force the LCM to pull a new configuration, you many need to run this command multiple times,
9 | as the configuration will pull from the pull server on every "x" local check (where the minimum number of checks is 2).
10 | .Example
11 | Invoke-DscPull -ComputerName OR-WEB01 -Verbose
12 | .Example
13 | 1..7 | Invoke-DscPull -ComputerName {"or-web0$_"}
14 | .Example
15 | Invoke-DscPull -Computer OR-WEB01 -Verbose -Force
16 | .Example
17 | Invoke-DscPull OR-WEB01 -verbose
18 |
19 | #>
20 | param (
21 | #
22 | [parameter(ValueFromPipeline=$true, Position=0)]
23 | [alias('ComputerName', 'Name', '__Server')]
24 | $CimSession = $null,
25 | [switch]
26 | $Force
27 | )
28 | Begin {
29 | $Flag = 3
30 | if ($PSBoundParameters.ContainsKey('Flag')) {
31 | $PSBoundParameters.Remove('Flag') | Out-Null
32 | }
33 | if ($PSBoundParameters.ContainsKey('Force')) {
34 | $PSBoundParameters.Remove('Force') | Out-Null
35 | }
36 | }
37 | Process {
38 |
39 | if ($Force)
40 | {
41 | Write-Verbose ""
42 | Write-Verbose "Terminating any existing WmiPrvSE processes to make sure that there are no cached resources mucking about."
43 | Get-CimInstance -ClassName Win32_Process -Filter 'Name like "WmiPrvSE.exe"' @parameters |
44 | Invoke-CimMethod -MethodName 'Terminate' |
45 | Out-Null
46 | }
47 |
48 | $parameters = @{
49 | Namespace = 'root/microsoft/windows/desiredstateconfiguration'
50 | Class = 'MSFT_DscLocalConfigurationManager'
51 | MethodName = 'PerformRequiredConfigurationChecks'
52 | Arguments = @{Flags=[uint32]$flag}
53 | }
54 |
55 | Write-Verbose ""
56 | Write-Verbose "Forcing a consistency check on $CimSession"
57 | Invoke-CimMethod @parameters @PSBoundParameters
58 | }
59 | }
60 |
61 |
62 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/New-DscZipFile.ps1:
--------------------------------------------------------------------------------
1 | function New-DscZipFile
2 | {
3 | param(
4 | ## The name of the zip archive to create
5 | [parameter(ValueFromPipelineByPropertyName)]
6 | [alias('Name')]
7 | [string]
8 | $ZipFile,
9 |
10 | ## The name of the folder to archive
11 | [parameter(ValueFromPipelineByPropertyName)]
12 | [alias('FullName')]
13 | [string]
14 | $Path,
15 |
16 | ## Switch to delete the zip archive if it already exists.
17 | [Switch] $Force
18 | )
19 |
20 |
21 | begin
22 | {
23 | [Byte[]] $zipHeader = 0x50,0x4B,0x05,0x06,0x00,0x00,0x00,0x00,
24 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
25 | 0x00,0x00,0x00,0x00,0x00,0x00
26 | }
27 | process
28 | {
29 | ## Create the Zip File
30 | $Version = Get-DscResourceVersion $path
31 | $folderName = $ZipFile + "_"+ $Version
32 |
33 | $ZipName = $executionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("$folderName.zip")
34 | Write-Verbose "Packing $path to to $ZipName."
35 |
36 | ## Check if the file exists already. If it does, check
37 | ## for -Force - generate an error if not specified.
38 | if(Test-Path $zipName)
39 | {
40 | if($Force)
41 | {
42 | Write-Verbose "Removing previous $zipname"
43 | Remove-Item $zipName -Force
44 | }
45 | else
46 | {
47 | throw "Item with specified name $zipName already exists."
48 | }
49 | }
50 |
51 | try
52 | {
53 | $shellObject = New-Object -comobject "Shell.Application"
54 |
55 | Write-Verbose "Creating new zip file $ZipName."
56 | $Writer = New-Object System.IO.FileStream $ZipName, "Create"
57 | $Writer.Write($zipheader, 0, 22)
58 | $Writer.Close();
59 |
60 | Start-Sleep -Seconds 1
61 | $ZipFileObject = $shellObject.namespace($ZipName)
62 |
63 | Write-Verbose "Loading the zip file contents."
64 | $ZipFileObject.CopyHere($Path)
65 | Start-Sleep -Seconds 5
66 | }
67 | finally
68 | {
69 | ## Release the shell object
70 | $shellObject = $null
71 | $ZipFileObject = $null
72 | }
73 | get-item $ZipName
74 | }
75 | }
76 |
77 |
78 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/Assert-DestinationDirectory.ps1:
--------------------------------------------------------------------------------
1 | function Assert-DestinationDirectory {
2 | [cmdletbinding(SupportsShouldProcess=$true)]
3 | param ()
4 |
5 | if ( Test-BuildResource ) {
6 |
7 | if ($pscmdlet.shouldprocess('module folders')) {}
8 | Add-DscBuildParameter -Name DestinationModuleDirectory -value (join-path $script:DscBuildParameters.DestinationRootDirectory 'Modules') @psboundparameters
9 | Add-DscBuildParameter -Name ModuleOutputPath -value (join-path $script:DscBuildParameters.WorkingDirectory 'BuildOutput\Modules') @psboundparameters
10 | Add-DscBuildParameter -Name DestinationToolsDirectory -value $script:DscBuildParameters.DestinationToolDirectory @psboundparameters
11 |
12 | Assert-Directory -path $script:DscBuildParameters.DestinationToolsDirectory @psboundparameters
13 | Assert-Directory -path $script:DscBuildParameters.DestinationModuleDirectory @psboundparameters
14 | Assert-Directory -path $script:DscBuildParameters.ModuleOutputPath @psboundparameters
15 | }
16 |
17 | if ( Test-BuildConfiguration) {
18 | if ($pscmdlet.shouldprocess('configuration folders')) {}
19 | Add-DscBuildParameter -Name DestinationConfigurationDirectory -value (join-path $script:DscBuildParameters.DestinationRootDirectory 'Configuration') @psboundparameters
20 | Add-DscBuildParameter -Name ConfigurationOutputPath -value (join-path $script:DscBuildParameters.WorkingDirectory 'BuildOutput\Configuration') @psboundparameters
21 |
22 | Assert-Directory -path $script:DscBuildParameters.DestinationConfigurationDirectory @psboundparameters
23 | Assert-Directory -path $script:DscBuildParameters.ConfigurationOutputPath @psboundparameters
24 | }
25 |
26 | if ( Test-BuildTools ) {
27 | if ($pscmdlet.shouldprocess('tools folders')) {}
28 | Add-DscBuildParameter -Name DestinationToolsDirectory -value $script:DscBuildParameters.DestinationToolDirectory @psboundparameters
29 | Assert-Directory -path $script:DscBuildParameters.DestinationToolsDirectory @psboundparameters
30 | }
31 |
32 | }
33 |
34 | function Assert-Directory {
35 | [cmdletbinding(SupportsShouldProcess=$true)]
36 | param (
37 | $Path
38 | )
39 |
40 | try {
41 | if (-not (Test-Path $path -ea Stop)) {
42 | $output = mkdir @psboundparameters
43 | }
44 | }
45 | catch {
46 | Write-Warning "Failed to validate $path"
47 | throw $_.Exception
48 | }
49 | }
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/DscConfiguration.psm1:
--------------------------------------------------------------------------------
1 | param
2 | (
3 | [string]
4 | $ConfigurationDataPath,
5 | [string]
6 | $LocalCertificateThumbprint = "$((Get-DscLocalConfigurationManager).CertificateId)"
7 | )
8 |
9 |
10 | $LocalCertificatePath = "cert:\LocalMachine\My\$LocalCertificateThumbprint"
11 | $ConfigurationData = @{AllNodes=@(); Credentials=@{}; Applications=@{}; Services=@{}; SiteData =@{}}
12 |
13 | . $psscriptroot\Get-Hashtable.ps1
14 | . $psscriptroot\Test-LocalCertificate.ps1
15 | . $psscriptroot\Add-NodeRoleFromServiceConfigurationData.ps1
16 |
17 | . $psscriptroot\New-ConfigurationDataStore
18 | . $psscriptroot\New-DscNodeMetadata.ps1
19 |
20 | . $psscriptroot\Get-AllNodesConfigurationData.ps1
21 | . $psscriptroot\Get-ConfigurationData.ps1
22 | . $psscriptroot\Get-CredentialConfigurationData.ps1
23 | . $psscriptroot\Get-ApplicationConfigurationData.ps1
24 | . $psscriptroot\Get-ServiceConfigurationData.ps1
25 | . $psscriptroot\Get-SiteDataConfigurationData.ps1
26 | . $psscriptroot\Get-EncryptedPassword.ps1
27 |
28 | . $psscriptroot\Add-EncryptedPassword.ps1
29 | . $psscriptroot\ConvertFrom-EncryptedFile.ps1
30 | . $psscriptroot\ConvertTo-CredentialLookup.ps1
31 | . $psscriptroot\ConvertTo-EncryptedFile.ps1
32 | . $psscriptroot\New-Credential.ps1
33 | . $psscriptroot\Remove-PlainTextPassword.ps1
34 |
35 |
36 | function Set-DscConfigurationDataPath {
37 | param (
38 | [parameter(Mandatory)]
39 | [ValidateNotNullOrEmpty()]
40 | [string]
41 | $Path
42 | )
43 |
44 | $script:ConfigurationDataPath = (Resolve-path $Path).Path
45 | }
46 | Set-Alias -Name 'Set-ConfigurationDataPath' -Value 'Set-DscConfigurationDataPath'
47 |
48 | function Get-DscConfigurationDataPath {
49 |
50 | $script:ConfigurationDataPath
51 | }
52 | Set-Alias -Name 'Get-ConfigurationDataPath' -Value 'Get-DscConfigurationDataPath'
53 |
54 | function Resolve-DscConfigurationDataPath {
55 | param (
56 | [parameter()]
57 | [string]
58 | $Path
59 | )
60 |
61 | if ( -not ($psboundparameters.containskey('Path')) ) {
62 | if ([string]::isnullorempty($script:ConfigurationDataPath)) {
63 | if (test-path $env:ConfigurationDataPath) {
64 | $path = $env:ConfigurationDataPath
65 | }
66 | }
67 | else {
68 | $path = $script:ConfigurationDataPath
69 | }
70 | }
71 |
72 | if ( -not ([string]::isnullorempty($path)) ) {
73 | Set-DscConfigurationDataPath -path $path
74 | }
75 |
76 | }
77 | Set-Alias -Name 'Resolve-ConfigurationDataPath' -Value 'Resolve-DscConfigurationDataPath'
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/Get-ConfigurationData.ps1:
--------------------------------------------------------------------------------
1 | function Get-DscConfigurationData
2 | {
3 | [cmdletbinding(DefaultParameterSetName='NoFilter')]
4 | param (
5 | [parameter()]
6 | [ValidateNotNullOrEmpty()]
7 | [string] $Path,
8 |
9 | [parameter(ParameterSetName = 'NameFilter')]
10 | [string] $Name,
11 |
12 | [parameter(ParameterSetName = 'NodeNameFilter')]
13 | [string] $NodeName,
14 |
15 | [parameter(ParameterSetName = 'RoleFilter')]
16 | [string] $Role,
17 |
18 | [parameter()]
19 | [switch] $Force
20 | )
21 |
22 | begin {
23 |
24 | if (($script:ConfigurationData -eq $null) -or $force) {
25 | $script:ConfigurationData = @{ AllNodes = @(); SiteData = @{}; Applications = @{}; Services=@{}; Credentials = @{} }
26 | }
27 |
28 | $ResolveConfigurationDataPathParams = @{}
29 | if ($psboundparameters.containskey('path')) {
30 | $ResolveConfigurationDataPathParams.Path = $path
31 | }
32 | Resolve-ConfigurationDataPath @ResolveConfigurationDataPathParams
33 |
34 | }
35 | end {
36 |
37 | Get-AllNodesConfigurationData
38 |
39 | Write-Verbose 'Checking for filters of AllNodes.'
40 | switch ($PSCmdlet.ParameterSetName)
41 | {
42 | 'NameFilter' {
43 | Write-Verbose "Filtering for nodes with the Name $Name"
44 | $script:ConfigurationData.AllNodes = $script:ConfigurationData.AllNodes.Where({$_.Name -like $Name})
45 | }
46 | 'NodeNameFilter' {
47 | Write-Verbose "Filtering for nodes with the GUID of $NodeName"
48 | $script:ConfigurationData.AllNodes = $script:ConfigurationData.AllNodes.Where({$_.NodeName -like $NodeName})
49 | }
50 | 'RoleFilter' {
51 | Write-Verbose "Filtering for nodes with the Role of $Role"
52 | $script:ConfigurationData.AllNodes = $script:ConfigurationData.AllNodes.Where({ $_.roles -contains $Role})
53 | }
54 | default {
55 | Write-Verbose 'Loading Site Data'
56 | Get-SiteDataConfigurationData
57 | Write-Verbose 'Loading Services Data'
58 | Get-ServiceConfigurationData
59 | Write-Verbose 'Loading Credential Data'
60 | Get-CredentialConfigurationData
61 | Write-Verbose 'Loading Application Data'
62 | Get-ApplicationConfigurationData
63 | }
64 | }
65 |
66 | Add-NodeRoleFromServiceConfigurationData
67 | return $script:ConfigurationData
68 | }
69 | }
70 |
71 | Set-Alias -Name 'Get-ConfigurationData' -Value 'Get-DscConfigurationData'
72 |
--------------------------------------------------------------------------------
/Tooling/DscOperations/Clear-DscEventLog.ps1:
--------------------------------------------------------------------------------
1 | function Clear-DSCEventLog
2 | {
3 | param ($session)
4 | icm $session {
5 | function Clear-WinEvent
6 | {
7 | [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='High',DefaultParameterSetName="LogName")]
8 |
9 | param(
10 | [Parameter(
11 | Position=0,
12 | Mandatory=$true,
13 | ParameterSetName="LogName",
14 | ValueFromPipeline=$true,
15 | ValueFromPipelineByPropertyName=$true
16 | )]
17 | [String[]]$LogName,
18 |
19 | [Parameter(
20 | Position=0,
21 | Mandatory=$true,
22 | ParameterSetName="EventLogConfiguration",
23 | ValueFromPipeline=$true
24 | )]
25 | [System.Diagnostics.Eventing.Reader.EventLogConfiguration[]]$EventLog,
26 |
27 | [switch]$Force
28 |
29 | )
30 |
31 |
32 | process
33 | {
34 | switch($PSCmdlet.ParameterSetName)
35 | {
36 | 'LogName'
37 | {
38 | Write-Verbose "ParameterSetName=LogName"
39 | foreach($l in $LogName)
40 | {
41 | if($Force -or $PSCmdlet.ShouldProcess($env:COMPUTERNAME,"Clear Event log '$l'"))
42 | {
43 | [System.Diagnostics.Eventing.Reader.EventLogSession]::GlobalSession.ClearLog($l)
44 | }
45 | }
46 | }
47 |
48 | 'EventLogConfiguration'
49 | {
50 | Write-Verbose "ParameterSetName=EventLogConfiguration"
51 | foreach($l in $EventLog)
52 | {
53 | if($Force -or $PSCmdlet.ShouldProcess($env:COMPUTERNAME,"Clear Event log '$($l.LogName)'"))
54 | {
55 | [System.Diagnostics.Eventing.Reader.EventLogSession]::GlobalSession.ClearLog($l.LogName)
56 | }
57 | }
58 |
59 | }
60 | }
61 | }
62 | }
63 | Get-WinEvent -ListLog Microsoft-Windows-DSC/Operational -Force |
64 | clear-winevent -force
65 |
66 | }
67 | }
68 |
69 |
70 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/Get-DscResourceVersion.ps1:
--------------------------------------------------------------------------------
1 | function Get-ModuleVersion
2 | {
3 | param (
4 | [parameter(mandatory)]
5 | [validatenotnullorempty()]
6 | [string]
7 | $path,
8 | [switch]
9 | $asVersion
10 | )
11 | $ModuleName = split-path $path -Leaf
12 | $ModulePSD1 = join-path $path "$ModuleName.psd1"
13 |
14 | Write-Verbose ''
15 | Write-Verbose "Checking for $ModulePSD1"
16 | if (Test-Path $ModulePSD1)
17 | {
18 | $psd1 = get-content $ModulePSD1 -Raw
19 | $Version = (Invoke-Expression -Command $psd1)['ModuleVersion']
20 | Write-Verbose "Found version $Version for $ModuleName."
21 | Write-Verbose ''
22 | if ($asVersion) {
23 | [Version]::parse($Version)
24 | }
25 | else {
26 | return $Version
27 | }
28 | }
29 | else
30 | {
31 | Write-Warning "Could not find a PSD1 for $modulename at $ModulePSD1."
32 | }
33 | }
34 |
35 |
36 | function Get-ModuleAuthor
37 | {
38 | param (
39 | [parameter(mandatory)]
40 | [validatenotnullorempty()]
41 | [string]
42 | $path
43 | )
44 | $ModuleName = split-path $path -Leaf
45 | $ModulePSD1 = join-path $path "$ModuleName.psd1"
46 |
47 | if (Test-Path $ModulePSD1)
48 | {
49 | $psd1 = get-content $ModulePSD1 -Raw
50 | $Author = (Invoke-Expression -Command $psd1)['Author']
51 | Write-Verbose "Found author $Author for $ModuleName."
52 | return $Author
53 | }
54 | else
55 | {
56 | Write-Warning "Could not find a PSD1 for $modulename at $ModulePSD1."
57 | }
58 | }
59 |
60 | New-Alias -Name Get-DscResourceVersion -Value Get-ModuleVersion -Force
61 |
62 |
63 | function Test-ModuleVersion {
64 | param (
65 | [parameter(ValueFromPipeline, Mandatory)]
66 | [object]
67 | $InputObject,
68 | [parameter(Mandatory, position = 0)]
69 | [string]
70 | $Destination
71 | )
72 | process {
73 | $DestinationModule = join-path $Destination $InputObject.name
74 |
75 | if (test-path $DestinationModule) {
76 | $CurrentModuleVersion = Get-ModuleVersion -Path $DestinationModule -asVersion
77 | $NewModuleVersion = Get-ModuleVersion -Path $InputObject.fullname -asVersion
78 | if (($CurrentModuleVersion -eq $null) -or ($NewModuleVersion -gt $CurrentModuleVersion)) {
79 | Write-Verbose "New module version is higher the the currently deployed module. Replacing it."
80 | $InputObject
81 | }
82 | else {
83 | Write-Verbose "The current module is the same version or newer than the one in source control. Not replacing it."
84 | }
85 | }
86 | else {
87 | Write-Verbose "No existing module at $DestinationModule."
88 | $InputObject
89 | }
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/Tooling/DscOperations/DscOperations.psd1:
--------------------------------------------------------------------------------
1 | #
2 | # Module manifest for module 'DscOperations'
3 | #
4 | # Generated by: Steven
5 | #
6 | # Generated on: 4/9/2014
7 | #
8 |
9 | @{
10 |
11 | # Script module or binary module file associated with this manifest.
12 | RootModule = 'DscOperations.psm1'
13 |
14 | # Version number of this module.
15 | ModuleVersion = '1.5.5.0'
16 |
17 | # ID used to uniquely identify this module
18 | GUID = 'c5e2c892-c16d-4649-9d65-dc14c0bddcb9'
19 |
20 | # Author of this module
21 | Author = 'Steven Murawski'
22 |
23 | # Company or vendor of this module
24 | CompanyName = 'Stack Exchange'
25 |
26 | # Copyright statement for this module
27 | Copyright = '(c) 2014 Steven Murawski. All rights reserved.'
28 |
29 | # Description of the functionality provided by this module
30 | Description = 'Commands for operating a DSC Pull Server environment.'
31 |
32 | # Minimum version of the Windows PowerShell engine required by this module
33 | # PowerShellVersion = ''
34 |
35 | # Name of the Windows PowerShell host required by this module
36 | # PowerShellHostName = ''
37 |
38 | # Minimum version of the Windows PowerShell host required by this module
39 | # PowerShellHostVersion = ''
40 |
41 | # Minimum version of Microsoft .NET Framework required by this module
42 | # DotNetFrameworkVersion = ''
43 |
44 | # Minimum version of the common language runtime (CLR) required by this module
45 | # CLRVersion = ''
46 |
47 | # Processor architecture (None, X86, Amd64) required by this module
48 | # ProcessorArchitecture = ''
49 |
50 | # Modules that must be imported into the global environment prior to importing this module
51 | RequiredModules = @('DscConfiguration', 'DscBuild')
52 |
53 | # Assemblies that must be loaded prior to importing this module
54 | # RequiredAssemblies = @()
55 |
56 | # Script files (.ps1) that are run in the caller's environment prior to importing this module.
57 | # ScriptsToProcess = @()
58 |
59 | # Type files (.ps1xml) to be loaded when importing this module
60 | # TypesToProcess = @()
61 |
62 | # Format files (.ps1xml) to be loaded when importing this module
63 | # FormatsToProcess = @()
64 |
65 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
66 | # NestedModules = @()
67 |
68 | # Functions to export from this module
69 | FunctionsToExport = 'Clear-DscEventLog', 'Clear-DscTemporaryModule', 'Invoke-DscPull', 'Set-DscClient', 'Invoke-DscApply'
70 |
71 | # Cmdlets to export from this module
72 | #CmdletsToExport = '*'
73 |
74 | # Variables to export from this module
75 | #VariablesToExport = '*'
76 |
77 | # Aliases to export from this module
78 | #AliasesToExport = '*'
79 |
80 | # List of all modules packaged with this module
81 | # ModuleList = @()
82 |
83 | # List of all files packaged with this module
84 | # FileList = @()
85 |
86 | # Private data to pass to the module specified in RootModule/ModuleToProcess
87 | # PrivateData = ''
88 |
89 | # HelpInfo URI of this module
90 | # HelpInfoURI = ''
91 |
92 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
93 | # DefaultCommandPrefix = ''
94 |
95 | }
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/Where-DscResource.ps1:
--------------------------------------------------------------------------------
1 | function Where-DscResource
2 | {
3 | param (
4 | [parameter(ValueFromPipeline)]
5 | [IO.FileSystemInfo]
6 | $InputObject,
7 | [parameter()]
8 | [string]
9 | $Destination,
10 | [switch]
11 | [alias('IsValid')]
12 | $CheckIfIsValid
13 | )
14 | begin
15 | {
16 | if ($CheckIfIsValid)
17 | {
18 | $AllResources = Get-DscResource |
19 | Where-Object {$_.implementedas -like 'PowerShell'}
20 |
21 | Add-DscBuildParameter -Name TestedModules -value @()
22 | }
23 | }
24 | process
25 | {
26 | Write-Verbose "Checking $($inputobject.Name)."
27 | if ( $CheckIfIsValid -and (Test-DscModuleResourceIsValid @psboundparameters) )
28 | {
29 | $InputObject
30 | }
31 | }
32 | }
33 |
34 | function Test-DscModuleResourceIsValid
35 | {
36 | [cmdletbinding()]
37 | param (
38 | [parameter(ValueFromPipeline)]
39 | [IO.FileSystemInfo]
40 | $InputObject,
41 | [parameter()]
42 | [string]
43 | $Destination,
44 | [switch]
45 | [alias('Changed')]
46 | $CheckIfChanged,
47 | [switch]
48 | [alias('IsValid')]
49 | $CheckIfIsValid
50 | )
51 |
52 | Write-Verbose "Testing for valid resources."
53 | $FailedDscResources = Get-FailedDscResource -AllModuleResources (Get-DscResourceForModule -InputObject $InputObject)
54 |
55 | if ($FailedDscResources)
56 | {
57 | Write-Verbose "Found failed resources."
58 | foreach ($resource in $FailedDscResources)
59 | {
60 | Write-Warning "`t`tFailed Resource - $($resource.Name)"
61 | }
62 | throw "Fix invalid resources in $($InputObject.Name)."
63 | }
64 |
65 | return $true
66 | }
67 |
68 | function Get-DscResourceForModule
69 | {
70 | [cmdletbinding()]
71 | param ($InputObject)
72 |
73 | $Name = $inputobject.Name
74 | Write-Verbose "Retrieving all resources and filtering for $Name."
75 |
76 | $ResourcesInModule = $AllResources |
77 | Where-Object {
78 | Write-Verbose "`tChecking for $($_.name) in $name."
79 | $_.module -like $name
80 | } |
81 | ForEach-Object {
82 | Write-Verbose "`t$Name contains $($_.Name)."
83 | $_
84 | }
85 | if ($ResourcesInModule.count -eq 0)
86 | {
87 | Write-Warning "$Name does not contain any resources."
88 | }
89 | else {
90 | $script:DscBuildParameters.TestedModules += $InputObject.FullName
91 | }
92 | $ResourcesInModule
93 | }
94 |
95 | function Get-FailedDscResource
96 | {
97 | [cmdletbinding()]
98 | param ($AllModuleResources)
99 |
100 | foreach ($resource in $AllModuleResources)
101 | {
102 | if (-not (Test-cDscResource -Name $Resource.Name))
103 | {
104 | Write-Warning "`tResources $($_.name) is invalid."
105 | $resource
106 | }
107 | }
108 | }
109 |
110 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/DscBuild.psd1:
--------------------------------------------------------------------------------
1 | #
2 | # Module manifest for module 'DscBuild'
3 | #
4 | # Generated by: Steven Murawski
5 | #
6 | # Generated on: 3/27/2014
7 | #
8 |
9 | @{
10 |
11 | # Script module or binary module file associated with this manifest.
12 | RootModule = 'DscBuild.psm1'
13 |
14 | # Version number of this module.
15 | ModuleVersion = '1.6.5.5'
16 |
17 | # ID used to uniquely identify this module
18 | GUID = '23ccd4bf-0a52-4077-986f-c153893e5a6a'
19 |
20 | # Author of this module
21 | Author = 'Steven Murawski'
22 |
23 | # Company or vendor of this module
24 | CompanyName = 'Stack Exchange'
25 |
26 | # Copyright statement for this module
27 | Copyright = '(c) 2014 Steven Murawski. All rights reserved.'
28 |
29 | # Description of the functionality provided by this module
30 | Description = 'Build tools for DSC Resources and Configurations'
31 |
32 | # Minimum version of the Windows PowerShell engine required by this module
33 | PowerShellVersion = '4.0'
34 |
35 | # Name of the Windows PowerShell host required by this module
36 | # PowerShellHostName = ''
37 |
38 | # Minimum version of the Windows PowerShell host required by this module
39 | # PowerShellHostVersion = ''
40 |
41 | # Minimum version of Microsoft .NET Framework required by this module
42 | # DotNetFrameworkVersion = ''
43 |
44 | # Minimum version of the common language runtime (CLR) required by this module
45 | # CLRVersion = ''
46 |
47 | # Processor architecture (None, X86, Amd64) required by this module
48 | # ProcessorArchitecture = ''
49 |
50 | # Modules that must be imported into the global environment prior to importing this module
51 | RequiredModules = @('Pester', 'cDscResourceDesigner')
52 |
53 | # Assemblies that must be loaded prior to importing this module
54 | # RequiredAssemblies = @()
55 |
56 | # Script files (.ps1) that are run in the caller's environment prior to importing this module.
57 | # ScriptsToProcess = @()
58 |
59 | # Type files (.ps1xml) to be loaded when importing this module
60 | # TypesToProcess = @()
61 |
62 | # Format files (.ps1xml) to be loaded when importing this module
63 | # FormatsToProcess = @()
64 |
65 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
66 | # NestedModules = @()
67 |
68 | # Functions to export from this module
69 | FunctionsToExport = 'Invoke-DscBuild',
70 | 'Get-DscResourceWmiClass',
71 | 'Remove-DscResourceWmiClass',
72 | 'Resolve-DscConfigurationProperty'
73 |
74 | # Cmdlets to export from this module
75 | #CmdletsToExport = '*'
76 |
77 | # Variables to export from this module
78 | #VariablesToExport = '*'
79 |
80 | # Aliases to export from this module
81 | # AliasesToExport = 'Resolve-ConfigurationProperty'
82 |
83 | # List of all modules packaged with this module
84 | # ModuleList = @()
85 |
86 | # List of all files packaged with this module
87 | # FileList = @()
88 |
89 | # Private data to pass to the module specified in RootModule/ModuleToProcess
90 | # PrivateData = ''
91 |
92 | # HelpInfo URI of this module
93 | # HelpInfoURI = ''
94 |
95 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
96 | # DefaultCommandPrefix = ''
97 |
98 | }
99 |
100 |
101 |
--------------------------------------------------------------------------------
/Tooling/DscDevelopment/DscDevelopment.psd1:
--------------------------------------------------------------------------------
1 | #
2 | # Module manifest for module 'DscDevelopment'
3 | #
4 | # Generated by: Steven Murawski
5 | #
6 | # Generated on: 4/9/2014
7 | #
8 |
9 | @{
10 |
11 | # Script module or binary module file associated with this manifest.
12 | RootModule = 'DscDevelopment.psm1'
13 |
14 | # Version number of this module.
15 | ModuleVersion = '1.5.1.0'
16 |
17 | # ID used to uniquely identify this module
18 | GUID = 'e51973fd-3e2b-46ef-b307-66b02811db52'
19 |
20 | # Author of this module
21 | Author = 'Steven Murawski'
22 |
23 | # Company or vendor of this module
24 | CompanyName = 'Stack Exchange'
25 |
26 | # Copyright statement for this module
27 | Copyright = '(c) 2014 Steven Murawski. All rights reserved.'
28 |
29 | # Description of the functionality provided by this module
30 | # Description = ''
31 |
32 | # Minimum version of the Windows PowerShell engine required by this module
33 | # PowerShellVersion = ''
34 |
35 | # Name of the Windows PowerShell host required by this module
36 | # PowerShellHostName = ''
37 |
38 | # Minimum version of the Windows PowerShell host required by this module
39 | # PowerShellHostVersion = ''
40 |
41 | # Minimum version of Microsoft .NET Framework required by this module
42 | # DotNetFrameworkVersion = ''
43 |
44 | # Minimum version of the common language runtime (CLR) required by this module
45 | # CLRVersion = ''
46 |
47 | # Processor architecture (None, X86, Amd64) required by this module
48 | # ProcessorArchitecture = ''
49 |
50 | # Modules that must be imported into the global environment prior to importing this module
51 | RequiredModules = @('DscBuild', 'DscConfiguration')
52 |
53 | # Assemblies that must be loaded prior to importing this module
54 | # RequiredAssemblies = @()
55 |
56 | # Script files (.ps1) that are run in the caller's environment prior to importing this module.
57 | # ScriptsToProcess = @()
58 |
59 | # Type files (.ps1xml) to be loaded when importing this module
60 | # TypesToProcess = @()
61 |
62 | # Format files (.ps1xml) to be loaded when importing this module
63 | # FormatsToProcess = @()
64 |
65 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
66 | # NestedModules = @()
67 |
68 | # Functions to export from this module
69 | FunctionsToExport = 'Add-CachedCimClass',
70 | 'Get-CachedCimClass',
71 | 'Get-Hashtable',
72 | 'Import-CimInstance',
73 | 'New-cDscCompositeResource',
74 | 'New-DscResourceFromModule',
75 | 'New-DscResourceShell',
76 | 'New-MofFile',
77 | 'Reset-CimClassCache',
78 | 'Test-DscBuild',
79 | 'Test-MofFile'
80 |
81 | # Cmdlets to export from this module
82 | # CmdletsToExport = '*'
83 |
84 | # Variables to export from this module
85 | # VariablesToExport = '*'
86 |
87 | # Aliases to export from this module
88 | # AliasesToExport = '*'
89 |
90 | # List of all modules packaged with this module
91 | # ModuleList = @()
92 |
93 | # List of all files packaged with this module
94 | # FileList = @()
95 |
96 | # Private data to pass to the module specified in RootModule/ModuleToProcess
97 | # PrivateData = ''
98 |
99 | # HelpInfo URI of this module
100 | # HelpInfoURI = ''
101 |
102 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
103 | # DefaultCommandPrefix = ''
104 |
105 | }
106 |
107 |
108 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/Invoke-DscConfiguration.ps1:
--------------------------------------------------------------------------------
1 | function Invoke-DscConfiguration {
2 | [cmdletbinding(SupportsShouldProcess=$true)]
3 | param ()
4 |
5 | if ( Test-BuildConfiguration )
6 | {
7 | if ( $pscmdlet.shouldprocess("Configuration module $($script:DscBuildParameters.ConfigurationModuleName) and configuration $($script:DscBuildParameters.ConfigurationName)") ) {
8 | Write-Verbose ''
9 | Write-Verbose "Importing configuration module: $($script:DscBuildParameters.ConfigurationModuleName)"
10 | if (Get-Module -list -name "$($script:DscBuildParameters.ConfigurationModuleName)") {
11 |
12 | $ResetVerbosePreference = $false
13 | if ($PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters['Verbose'].IsPresent) {
14 | $ResetVerbosePreference = $true
15 | $VerbosePreference = 'SilentlyContinue'
16 | }
17 |
18 | try {
19 | import-module -name "$($script:DscBuildParameters.ConfigurationModuleName)" -force -Verbose:$false -ErrorAction Stop
20 | }
21 | catch {
22 |
23 | Write-Warning "Failed to load configuration module"
24 | $Exception = $_.Exception
25 | do {
26 | Write-Warning "`t$($_.Message)"
27 | $Exception = $_.InnerException
28 | } while ($Exception -ne $null)
29 | throw "Failed to load $($script:DscBuildParameters.ConfigurationModuleName)"
30 | }
31 |
32 | if ($ResetVerbosePreference) {
33 | $VerbosePreference = 'Continue'
34 | }
35 | Write-Verbose "Imported $($script:DscBuildParameters.ConfigurationModuleName)"
36 | Write-Verbose ''
37 | }
38 | else {
39 | Write-Warning "Unable to resolve the module '$($script:DscBuildParameters.ConfigurationModuleName)'"
40 | Write-Warning "Current modules on PSModulePath"
41 | $env:psmodulepath -split ';' |
42 | get-childitem -directory |
43 | foreach {
44 | Write-Warning "`tFound $($_.Name)"
45 | }
46 | throw "Failed to load configuration module"
47 | }
48 |
49 | try
50 | {
51 | Write-Verbose ""
52 | Write-Verbose 'Starting to generate configurations.'
53 | Write-Verbose "`tWriting configurations to $($script:DscBuildParameters.ConfigurationOutputPath)"
54 | $ErrorActionPreference = 'Stop'
55 |
56 | $output = . $script:DscBuildParameters.ConfigurationName -outputpath $script:DscBuildParameters.ConfigurationOutputPath -ConfigurationData $script:DscBuildParameters.ConfigurationData
57 |
58 | Write-Verbose "Done creating configurations. Get ready for some pullin' baby!"
59 | Write-Verbose ""
60 | }
61 | catch
62 | {
63 | Write-Warning 'Failed to generate configs.'
64 | throw 'Failed to generate configs.'
65 | }
66 |
67 |
68 | remove-module $script:DscBuildParameters.ConfigurationModuleName
69 | }
70 | }
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/DscConfiguration.psd1:
--------------------------------------------------------------------------------
1 | #
2 | # Module manifest for module 'DscConfiguration'
3 | #
4 | # Generated by: Steven
5 | #
6 | # Generated on: 4/14/2014
7 | #
8 |
9 | @{
10 |
11 | # Script module or binary module file associated with this manifest.
12 | RootModule = 'DscConfiguration.psm1'
13 |
14 | # Version number of this module.
15 | ModuleVersion = '1.5.1.0'
16 |
17 | # ID used to uniquely identify this module
18 | GUID = '0b48e2a7-dcd7-4888-95ec-f80d6fca03b6'
19 |
20 | # Author of this module
21 | Author = 'Steven Murawski'
22 |
23 | # Company or vendor of this module
24 | CompanyName = 'Stack Exchange'
25 |
26 | # Copyright statement for this module
27 | Copyright = '(c) 2014 Steven. All rights reserved.'
28 |
29 | # Description of the functionality provided by this module
30 | # Description = ''
31 |
32 | # Minimum version of the Windows PowerShell engine required by this module
33 | # PowerShellVersion = ''
34 |
35 | # Name of the Windows PowerShell host required by this module
36 | # PowerShellHostName = ''
37 |
38 | # Minimum version of the Windows PowerShell host required by this module
39 | # PowerShellHostVersion = ''
40 |
41 | # Minimum version of Microsoft .NET Framework required by this module
42 | # DotNetFrameworkVersion = ''
43 |
44 | # Minimum version of the common language runtime (CLR) required by this module
45 | # CLRVersion = ''
46 |
47 | # Processor architecture (None, X86, Amd64) required by this module
48 | # ProcessorArchitecture = ''
49 |
50 | # Modules that must be imported into the global environment prior to importing this module
51 | # RequiredModules = @()
52 |
53 | # Assemblies that must be loaded prior to importing this module
54 | # RequiredAssemblies = @()
55 |
56 | # Script files (.ps1) that are run in the caller's environment prior to importing this module.
57 | # ScriptsToProcess = @()
58 |
59 | # Type files (.ps1xml) to be loaded when importing this module
60 | # TypesToProcess = @()
61 |
62 | # Format files (.ps1xml) to be loaded when importing this module
63 | # FormatsToProcess = @()
64 |
65 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
66 | # NestedModules = @()
67 |
68 | # Functions to export from this module
69 | FunctionsToExport = 'Get-DscConfigurationData',
70 | 'New-DscNodeMetadata',
71 | 'New-DscServiceMetadata',
72 | 'New-DscSiteMetadata',
73 | 'Add-DscEncryptedPassword',
74 | 'Get-DscEncryptedPassword',
75 | 'Set-DscConfigurationDataPath',
76 | 'Get-DscConfigurationDataPath',
77 | 'New-DscConfigurationDataStore'
78 |
79 | # Cmdlets to export from this module
80 | #CmdletsToExport = '*'
81 |
82 | # Variables to export from this module
83 | #VariablesToExport = '*'
84 |
85 | # Aliases to export from this module
86 | AliasesToExport = 'Add-EncryptedPassword',
87 | 'Set-ConfigurationDataPath',
88 | 'Get-ConfigurationDataPath'
89 |
90 | # List of all modules packaged with this module
91 | # ModuleList = @()
92 |
93 | # List of all files packaged with this module
94 | # FileList = @()
95 |
96 | # Private data to pass to the module specified in RootModule/ModuleToProcess
97 | # PrivateData = ''
98 |
99 | # HelpInfo URI of this module
100 | # HelpInfoURI = ''
101 |
102 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
103 | # DefaultCommandPrefix = ''
104 |
105 | }
106 |
107 |
108 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/Where-DscResource.Tests.ps1:
--------------------------------------------------------------------------------
1 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path
2 | $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.ps1", ".ps1")
3 | $pathtosut = join-path $here $sut
4 |
5 | iex ( gc $pathtosut -Raw )
6 |
7 | <#
8 | Describe 'how Test-ZippedModuleChanged reponds' {
9 | $Setup = {
10 | mkdir testdrive:\Source -erroraction silentlycontinue | out-null
11 | $Source = get-item testdrive:\source | select -expand fullname
12 |
13 | mkdir testdrive:\Destination -erroraction silentlycontinue | out-null
14 | $Destination = get-item testdrive:\Destination | select -expand fullname
15 |
16 | $InputObjectProperties = @{
17 | Name = 'MyCustomResource'
18 | fullname = (join-path $Source 'MyCustomResource')
19 | }
20 |
21 | $InputObject = [pscustomobject]$InputObjectProperties
22 |
23 | mock -command Get-FileHash -parameterFilter {$path -like $InputObject.fullname} -mockwith {[pscustomobject]@{hash='123'}}
24 | }
25 |
26 |
27 | context "when the current version is the same as a previous version" {
28 | . $Setup
29 | mock -command test-path -mockwith {$true}
30 | mock -command Get-FileHash -parameterFilter {$path -like (Join-path $Destination 'MyCustomResource')} -mockwith {[pscustomobject]@{hash='123'}}
31 |
32 | $result = Test-ZippedModuleChanged
33 |
34 | it "should return false" {
35 | $result | should be $false
36 | }
37 | }
38 |
39 | context "when the current version is the different as a previous version" {
40 | . $Setup
41 | mock -command test-path -mockwith {$true}
42 | mock -command Get-FileHash -parameterFilter {$path -like (Join-path $Destination 'MyCustomResource')} -mockwith {[pscustomobject]@{hash='321'}}
43 |
44 | $result = Test-ZippedModuleChanged
45 |
46 | it "should return true" {
47 | $result | should be $true
48 | }
49 | }
50 |
51 | context "when no previous version exists" {
52 | . $Setup
53 | mock -command test-path -mockwith {$false}
54 |
55 | $result = Test-ZippedModuleChanged
56 |
57 | it "should return true" {
58 | $result | should be $true
59 | }
60 | }
61 | }
62 | #>
63 |
64 | Describe 'how Test-DscModuleResourceIsValid behaves' {
65 |
66 | context 'when there are failed DSC resources' {
67 | mock Get-DscResourceForModule -mockwith {}
68 | mock Get-FailedDscResource -mockwith {
69 | [pscustomobject]@{name='TestResource'},
70 | [pscustomobject]@{name='SecondResource'}
71 | }
72 |
73 | it 'should throw an exception' {
74 | { Test-DscModuleResourceIsValid} |
75 | should throw
76 | }
77 | }
78 |
79 | context 'when there are no DSC resources' {
80 | mock Get-DscResourceForModule -mockwith {}
81 | mock Get-FailedDscResource -mockwith {}
82 |
83 | $result = Test-DscModuleResourceIsValid
84 |
85 | it 'should return true' {
86 | $result | should be $true
87 | }
88 | }
89 |
90 | context 'when all DSC resources are valid' {
91 | mock Get-DscResourceForModule -mockwith {
92 | [pscustomobject]@{name='TestResource'},
93 | [pscustomobject]@{name='SecondResource'}
94 | }
95 | mock Get-FailedDscResource -mockwith {}
96 |
97 | $result = Test-DscModuleResourceIsValid
98 |
99 | it 'should return true' {
100 | $result | should be $true
101 | }
102 | }
103 | }
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/Tooling/DscDevelopment/New-DscCompositeResource.ps1:
--------------------------------------------------------------------------------
1 | Function New-DscCompositeResource
2 | {
3 | <#
4 | .Synopsis
5 | Short description of the command
6 | .Description
7 | Longer description of the command
8 | .EXAMPLE
9 | New-DscCompositeResource -Path "C:\TestModules" -ModuleName "Wakka" -ResourceName "Foo"
10 | .EXAMPLE
11 | New-DscCompositeResource -ModuleName "Wakka" -ResourceName "Foo"
12 | .EXAMPLE
13 | "Foo","Bar","Baz" | New-DscCompositeResource -ModuleName "Wakka"
14 | #>
15 | [CmdletBinding(SupportsShouldProcess=$true)]
16 | param(
17 | [Parameter()]
18 | [ValidateNotNullOrEmpty()]
19 | [string]
20 | $Path = "$($env:ProgramFiles)\WindowsPowerShell\Modules",
21 | [Parameter(Mandatory)]
22 | [ValidateNotNullOrEmpty()]
23 | [string]
24 | $ModuleName,
25 | [Parameter(
26 | Mandatory,
27 | ValueFromPipeline
28 | )]
29 | [ValidateNotNullOrEmpty()]
30 | [string]
31 | $ResourceName,
32 | [string]
33 | $Author = $env:USERNAME,
34 | [string]
35 | $Company = "Unknown",
36 | $Copyright = "(c) $([DateTime]::Now.Year) $env:USERNAME. All rights reserved.",
37 | [switch]
38 | $Force
39 | )
40 | begin
41 | {
42 | $admin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
43 | if(-not $admin -and $Path -eq "$env:ProgramFiles\WindowsPowerShell\Modules"){
44 | throw "Must be in Administrative context to write to $Path"
45 | }
46 |
47 | $rootModule = Join-Path $Path $ModuleName
48 | Write-Verbose "Root module path - $RootModule"
49 |
50 | $rootModulePSD = Join-Path $rootModule "$($moduleName).psd1"
51 | Write-Verbose "Root module metadata file - $rootModulePSD"
52 |
53 | $rootModulePath = Join-Path $rootModule "DSCResources"
54 | Write-Verbose "DSCResources folder path $rootModulePath"
55 |
56 | if (-not (test-path $rootModulePSD)) {
57 | if($PSCmdlet.ShouldProcess($rootModule, 'Creating a base module to host DSC Resources')) {
58 | New-Item -ItemType Directory -Path $rootModule -Force:$Force | Out-Null
59 | New-ModuleManifest -Path $rootModulePSD -ModuleVersion '1.0.0' -Author $Author -CompanyName $Company -Description "CompositeResource Main module" -Copyright $Copyright
60 | }
61 | }
62 | else {
63 | Write-Verbose "Module and manifest already exist at $rootModulePSD"
64 | }
65 |
66 | if (-not (test-path $rootModulePath) ) {
67 | if($PSCmdlet.ShouldProcess($rootModulePath, 'Creating the DSCResources directory')) {
68 | New-Item -ItemType Directory -Path $rootModulePath -Force:$Force | Out-Null
69 | }
70 | }
71 | else {
72 | Write-Verbose "DSCResources folder already exists at $rootModulePath"
73 | }
74 | }
75 | process
76 | {
77 | $resourceFolder = Join-Path $rootModulePath $ResourceName
78 | $resourcePSMName = "$($ResourceName).schema.psm1"
79 | $resourcePSM = Join-Path $resourceFolder $resourcePSMName
80 | $resourcePSD = Join-Path $resourceFolder "$($ResourceName).psd1"
81 |
82 | if($PSCmdlet.ShouldProcess($resourceFolder, "Creating new resource $ResourceName"))
83 | {
84 | New-Item -ItemType Directory -Path $resourceFolder -Force:$Force | Out-Null
85 |
86 | if ((-not (test-path $resourcePSM)) -or ($force)) {
87 | New-Item -ItemType File -Path $resourcePSM -Force:$Force | Out-Null
88 | Add-Content -Path $resourcePSM -Value "Configuration $ResourceName`r`n{`r`n}"
89 | }
90 | if ((-not (test-path $resourcePSD)) -or ($force)) {
91 | New-ModuleManifest -Path $resourcePSD -RootModule $resourcePSMName -ModuleVersion '1.0.0' -Author $Author -CompanyName $Company -Copyright $Copyright
92 | }
93 |
94 | }
95 |
96 | }
97 |
98 | }
99 |
100 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BIG CHANGES
2 |
3 | We have moved the DSC Resources into their own repositories under PowerShell.Org.
4 |
5 | ## Why?
6 | * To make it easier to track issues for individual resources
7 | * To allow more community members to participate in the maintence of these resources
8 | * To make it easier to automate publishing of individual resources to PowerShellGet and other sources
9 |
10 | ## How can I contribute?
11 |
12 | ### If you have a new resource you want to share,
13 | * File an issue here and let us know what the name of the project should be.
14 | * We'll create a repository and make you an contributor (if you want) to that repository.
15 | * You can then populate that repository via a pull request or we can clone an existing repository in the organization.
16 | or
17 | * You can create a gist containing the files for your DSC resource and we can move them into a repository.
18 |
19 | ### If you want to help maintain an existing resource
20 | * Start by contributing something.. a bugfix, documentation, file issues
21 | * After a contribution or two, file an issue requesting to become a maintainer for the project
22 | * We (along with the other maintainers - if any exist), can start a conversation about how you can help the project.
23 |
24 | or
25 |
26 | * Fork the resource repository
27 | * Create your feature branch (`git checkout -b my-new-feature`)
28 | * Commit your changes (`git commit -am 'Add some feature'`)
29 | * Push to the branch (`git push origin my-new-feature`)
30 | * Create new Pull Request
31 |
32 | ## What is still here?
33 | So far, the DSC Tooling. We'll probably break that out as well. The focus of the repository will be documentation, examples and filing of issues that are broader than any one resource or tool.
34 |
35 | Current Repositories
36 | * [SystemHosting](https://github.com/PowerShellOrg/SystemHosting)
37 | * SHT_AllowedServices
38 | * SHT_DNSClient
39 | * SHT_GroupResource
40 | * SHT_IscsiInitiatorTargetPortal
41 | * SHT_MPIOSetting
42 | * SHT_NetAdapter
43 | * SHT_NetAdapterAdvancedProperty
44 | * SHT_NetAdapterBinding
45 | * SHT_NetAdapterNetBios
46 | * [cWebAdministration](https://github.com/PowerShellOrg/cWebAdministration)
47 | * PSHOrg_cAppPool
48 | * PSHOrg_cWebsite
49 | * [cChoco](https://github.com/PowerShellOrg/cChoco)
50 | * cChocoInstaller
51 | * cChocoPackageInstall
52 | * [StackExchangeResources](https://github.com/PowerShellOrg/StackExchangeResources)
53 | * StackExchange_CertificateStore
54 | * StackExchange_FirewallRule
55 | * StackExchange_NetworkAdapter
56 | * StackExchange_Pagefile
57 | * StackExchange_PowerPlan
58 | * StackExchange_ScheduledTask
59 | * StackExchange_SetExecutionPolicy
60 | * StackExchange_Timezone
61 | * [rchaganti](https://github.com/PowerShellOrg/rchaganti)
62 | * HostsFile
63 | * [PowerShellAccessControl](https://github.com/PowerShellOrg/PowerShellAccessControl)
64 | * PowerShellAccessControl_cAccessControlEntry
65 | * PowerShellAccessControl_cSecurityDescriptor
66 | * PowerShellAccessControl_cSecurityDescriptorSddl
67 | * [cSystemCenterManagement](https://github.com/PowerShellOrg/cSystemCenterManagement)
68 | * ICG_SCOMAgentMgmtGroup
69 | * ICG_SCOMBulkMP
70 | * ICG_SCOMImportMP
71 | * [cSqlPs](https://github.com/PowerShellOrg/cSqlPs)
72 | * PSHOrg_cSqlHAEndPoint
73 | * PSHOrg_cSqlHAGroup
74 | * PSHOrg_cSqlHAService
75 | * PSHOrg_cSqlServerInstall
76 | * PSHOrg_cWaitForSqlHAGroup
77 | * cScriptResource
78 | * [cSmbShare](https://github.com/PowerShellOrg/cSmbShare)
79 | * PSHOrg_cSmbShare
80 | * [cRDPEnabled](https://github.com/PowerShellOrg/cRDPEnabled)
81 | * PSHOrg_cRDPEnabled
82 | * [Craig-Martin](https://github.com/PowerShellOrg/Craig-Martin)
83 | * GlobalAssemblyCache
84 | * [cPSGet](https://github.com/PowerShellOrg/cPSGet)
85 | * PSHOrg_cPSGet
86 | * [cPSDesiredStateConfiguration](https://github.com/PowerShellOrg/cPSDesiredStateConfiguration)
87 | * PSHOrg_cDSCWebService
88 | * [cNetworking](https://github.com/PowerShellOrg/cNetworking)
89 | * PSHOrg_cDNSServerAddress
90 | * PSHOrg_cFirewall
91 | * PSHOrg_cIPAddress
92 | * [cHyper-V](https://github.com/PowerShellOrg/cHyper-V)
93 | * PSHOrg_cVHD
94 | * PSHOrg_cVMHost
95 | * PSHOrg_cVMHyperV
96 | * PSHOrg_cVMSwitch
97 | * PSHOrg_cVhdFileDirectory
98 | * [cFailoverCluster](https://github.com/PowerShellOrg/cFailoverCluster)
99 | * PSHOrg_cCluster
100 | * PSHOrg_cWaitForCluster
101 | * [cComputerManagement](https://github.com/PowerShellOrg/cComputerManagement)
102 | * PSHOrg_cComputer
103 | * [cActiveDirectory](https://github.com/PowerShellOrg/cActiveDirectory)
104 | * PSHOrg_cADDomain
105 | * PSHOrg_cADDomainController
106 | * PSHOrg_cADUser
107 | * PSHOrg_cWaitForADDomain
108 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/DscBuild.psm1:
--------------------------------------------------------------------------------
1 | . $psscriptroot\Assert-DestinationDirectory.ps1
2 | . $psscriptroot\Clear-CachedDscResource.ps1
3 | . $psscriptroot\Clear-InstalledDscResource.ps1
4 | . $psscriptroot\Compress-DscResourceModule.ps1
5 | . $psscriptroot\Copy-CurrentDscResource.ps1
6 | . $psscriptroot\DscResourceWmiClass.ps1
7 | . $psscriptroot\Invoke-DscBuild.ps1
8 | . $psscriptroot\Invoke-DscConfiguration.ps1
9 | . $psscriptroot\Invoke-DscResourceUnitTest.ps1
10 | . $psscriptroot\New-DscChecksumFile.ps1
11 | . $psscriptroot\New-DscZipFile.ps1
12 | . $psscriptroot\Publish-DscConfiguration.ps1
13 | . $psscriptroot\Publish-DscResourceModule.ps1
14 | . $psscriptroot\Publish-DscToolModule.ps1
15 | . $psscriptroot\Test-DscResourceIsValid.ps1
16 | . $psscriptroot\Where-DscResource.ps1
17 | . $psscriptroot\Resolve-ConfigurationProperty.ps1
18 | . $psscriptroot\Get-DscResourceVersion.ps1
19 | . $psscriptroot\Update-ModuleMetadataVersion.ps1
20 |
21 |
22 | $DscBuildParameters = $null
23 |
24 | function Add-DscBuildParameter {
25 | <#
26 | .Synopsis
27 | Adds a parameter to the module scoped DscBuildParameters object.
28 | .Description
29 | Adds a parameter to the module scoped DscBuildParameters object. This object is available to all functions in a build and is built from the parameters to Invoke-DscBuild.
30 | .Example
31 | Add-DscBuildParameter -Name ProgramFilesModuleDirectory -value (join-path $env:ProgramFiles 'WindowsPowerShell\Modules')
32 | #>
33 | [cmdletbinding(SupportsShouldProcess=$true)]
34 | param (
35 | #Name of the property to add
36 | [string]
37 | $Name,
38 | #Value of the property to add
39 | [object]
40 | $Value
41 | )
42 |
43 | if ($psboundparameters.containskey('WhatIf')) {
44 | $psboundparameters.Remove('WhatIf') | out-null
45 | }
46 |
47 | Write-Verbose ''
48 | Write-Verbose "Adding DscBuildParameter: $Name"
49 | Write-Verbose "`tWith Value: $Value"
50 | $script:DscBuildParameters |
51 | add-member -membertype Noteproperty -force @psboundparameters
52 | Write-Verbose ''
53 | }
54 |
55 | function Test-BuildResource {
56 | <#
57 | .Synopsis
58 | Checks to see if a build started with Invoke-DscBuild will be processing new or existing resources.
59 | .Description
60 | Checks to see if a build started with Invoke-DscBuild will be processing new or existing resources. This is used by functions in the module to determine whether a particular block needs to execute.
61 | .Example
62 | if (Test-BuildResource) { do something...}
63 | #>
64 | [cmdletbinding()]
65 | param ()
66 | $IsBuild = ( $script:DscBuildParameters.Resource -or
67 | (-not ($script:DscBuildParameters.Tools -or $script:DscBuildParameters.Configuration) ) )
68 | Write-Verbose ''
69 | Write-Verbose "Is a Resource Build - $IsBuild"
70 | Write-Verbose ''
71 | return $IsBuild
72 | }
73 |
74 | function Test-BuildConfiguration {
75 | <#
76 | .Synopsis
77 | Checks to see if a build started with Invoke-DscBuild will be processing new or existing configurations.
78 | .Description
79 | Checks to see if a build started with Invoke-DscBuild will be processing new or existing configurations. This is used by functions in the module to determine whether a particular block needs to execute.
80 | .Example
81 | if (Test-BuildConfiguration) { do something...}
82 | #>
83 | [cmdletbinding()]
84 | param ()
85 | $IsBuild = ( $script:DscBuildParameters.Configuration -or
86 | (-not ($script:DscBuildParameters.Tools -or $script:DscBuildParameters.Resource) ) )
87 | Write-Verbose ''
88 | Write-Verbose "Is a Configuration Build - $IsBuild"
89 | Write-Verbose ''
90 | return $IsBuild
91 | }
92 |
93 | function Test-BuildTools {
94 | <#
95 | .Synopsis
96 | Checks to see if a build started with Invoke-DscBuild will be processing new or existing tools.
97 | .Description
98 | Checks to see if a build started with Invoke-DscBuild will be processing new or existing tools. This is used by functions in the module to determine whether a particular block needs to execute.
99 | .Example
100 | if (Test-BuildTools) { do something...}
101 | #>
102 | [cmdletbinding()]
103 | param ()
104 | $IsBuild = ( $script:DscBuildParameters.Tools -or
105 | (-not ($script:DscBuildParameters.Configuration -or $script:DscBuildParameters.Resource) ) )
106 | Write-Verbose ''
107 | Write-Verbose "Is a Tools Build - $IsBuild"
108 | Write-Verbose ''
109 | return $IsBuild
110 | }
111 |
--------------------------------------------------------------------------------
/Tooling/cDscDiagnostics/cDscDiagnosticsFormat.ps1xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Microsoft.PowerShell.cDscDiagnostics.GroupedEvents
7 |
8 | Microsoft.PowerShell.cDscDiagnostics.GroupedEvents
9 |
10 |
11 |
12 |
13 |
14 | 14
15 |
16 |
17 |
18 | 10
19 |
20 |
21 |
22 | 21
23 |
24 |
25 |
26 | 8
27 |
28 |
29 |
30 | 37
31 |
32 |
33 |
34 | 21
35 |
36 |
37 |
38 |
39 |
40 |
41 | ComputerName
42 |
43 |
44 | SequenceId
45 |
46 |
47 | TimeCreated
48 |
49 |
50 | Result
51 |
52 |
53 | JobID
54 |
55 |
56 | AllEvents
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | Microsoft.PowerShell.cDscDiagnostics.TraceOutput
65 |
66 | Microsoft.PowerShell.cDscDiagnostics.TraceOutput
67 |
68 |
69 |
70 |
71 |
72 | 14
73 |
74 |
75 |
76 | 12
77 |
78 |
79 |
80 | 21
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | ComputerName
91 |
92 |
93 | EventType
94 |
95 |
96 | TimeCreated
97 |
98 |
99 | Message
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/Tooling/DscDevelopment/Test-DscBuild.ps1:
--------------------------------------------------------------------------------
1 | function Test-DscBuild
2 | {
3 | <#
4 | .Synopsis
5 | Runs a build of the Stack Exchange DSC configurations.
6 | .DESCRIPTION
7 | Creates an isolated runspace and executes a build, returning input immediately to the console window.
8 | This is the same process used by the build server to publish the current DSC configurations.
9 |
10 | WARNING: This will replace the current DSC resource modules on the machine running the build.
11 | .EXAMPLE
12 | Test-DscBuild -Development
13 | Runs a build to the Dsc-Dev folder that should be located in the working directory.
14 | .EXAMPLE
15 | Test-DscBuild -DeploymentDirectory c:\temp
16 | Runs a build to the c:\temp folder and does not create compressed versions of the resource modules
17 | (saves a bit of time when repeatedly iterating on configuration changes)
18 | #>
19 | [cmdletbinding(supportsshouldprocess=$true)]
20 | param (
21 | #The path to local copy of the DSC-Prod repository
22 | #Defaults to two folders above the DscDevelopment module (..\..\DscDevelopment)
23 | [parameter(
24 | Position = 0
25 | )]
26 | [string]
27 | $WorkingDirectory = (Split-Path (Split-Path $psscriptroot)),
28 |
29 | #The path to the script build script to run.
30 | #By default, this points to the TeamCityBuild folder in the working directory.
31 | [parameter(
32 | Position = 1
33 | )]
34 | [ValidateNotNullOrEmpty()]
35 | [string]
36 | $BuildScript,
37 |
38 | #Destination for the generated configurations and packaged resources.
39 | #Two folders will be created underneath this location, one for Configurations and one for
40 | #resource modules.
41 | [parameter(
42 | Mandatory,
43 | Position = 2
44 | )]
45 | [ValidateNotNullOrEmpty()]
46 | [string]
47 | $DestinationRootDirectory,
48 |
49 | [parameter()]
50 | [ValidateNotNullOrEmpty()]
51 | [string]
52 | $DestinationToolDirectory,
53 |
54 | [parameter()]
55 | [string]
56 | $CurrentToolsDirectory,
57 |
58 | #
59 | [parameter()]
60 | [switch]
61 | $ConfigurationOnly,
62 |
63 | [parameter()]
64 | [switch]
65 | $ResourceOnly,
66 |
67 | [parameter()]
68 | [switch]
69 | $ToolsOnly,
70 |
71 | [switch]
72 | $CleanEnvironment,
73 |
74 | [switch]
75 | $ShowConfigurationDebugMessages
76 | )
77 |
78 | $PassedParameters = @{
79 | WorkingDirectory = $WorkingDirectory
80 | DestinationToolDirectory = $DestinationToolDirectory
81 | CurrentToolsDirectory = $CurrentToolsDirectory
82 | ConfigurationOnly = $ConfigurationOnly
83 | ResourceOnly = $ResourceOnly
84 | ToolsOnly = $ToolsOnly
85 | ShowConfigurationDebugMessages = $ShowConfigurationDebugMessages
86 | }
87 |
88 | foreach ($key in ('Whatif', 'Verbose', 'Debug'))
89 | {
90 | if ($PSBoundParameters.ContainsKey($key)) {
91 | $PassedParameters[$key] = $PSBoundParameters[$key]
92 | }
93 | }
94 |
95 | if (-not (Test-Path $WorkingDirectory))
96 | {
97 | throw 'Working directory not found. Please supply a path to the directory with DSC_Resources and DSC_Configuration repositories.'
98 | }
99 |
100 | if ($PSBoundParameters.ContainsKey('DestinationRootDirectory'))
101 | {
102 | $PassedParameters.DestinationRootDirectory = $DestinationRootDirectory
103 | }
104 | else
105 | {
106 | throw "Need to supply a DestinationRootDirectory."
107 | }
108 |
109 | Write-Verbose "Parameters to pass are: "
110 | foreach ($key in $PassedParameters.Keys)
111 | {
112 | Write-Verbose "`t`tKey: $Key Value: $($PassedParameters[$key])"
113 | }
114 |
115 | if (-not (Test-Path $BuildScript))
116 | {
117 | throw @"
118 | Failed to find a build script at $BuildScript.
119 | Either specify a path to the build script or clone the TeamCityBuild repository adjacent to the DSC-Prod repo.
120 | "@
121 | }
122 |
123 | if ($CleanEnvironment) {
124 | remove-item (join-path $PassedParameters.DestinationDirectory 'Configuration') -recurse -erroraction SilentlyContinue
125 | remove-item (join-path $PassedParameters.DestinationDirectory 'Modules') -recurse -erroraction SilentlyContinue
126 | remove-item (join-path $PassedParameters.WorkingDirectory 'BuildOutput') -recurse -erroraction SilentlyContinue
127 | }
128 |
129 | start-job -ArgumentList $BuildScript, $PassedParameters {
130 | param ([string]$BuildScript, [System.Collections.Hashtable]$PassedParameters)
131 | . $BuildScript @PassedParameters
132 | } | receive-job -wait
133 |
134 | }
135 |
136 |
137 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/Invoke-DscBuild.ps1:
--------------------------------------------------------------------------------
1 | function Invoke-DscBuild
2 | {
3 | <#
4 | .Synopsis
5 | Starts a build of DSC configurations, resources, and tools.
6 | .Description
7 | Starts a build of DSC configurations, resources, and tools. This command is the global entry point for DSC builds controls the flow of operations.
8 | .Example
9 | $BuildParameters = @{
10 | WorkingDirectory = 'd:\gitlab\'
11 | DestinationRootDirectory = 'd:\PullServerOutputTest\'
12 | DestinationToolDirectory = 'd:\ToolsOutputTest\'
13 | }
14 | Invoke-DscBuild @BuildParameters
15 | #>
16 | [cmdletbinding(SupportsShouldProcess=$true)]
17 | param (
18 | #Root of your source control check outs or the folder above your Dsc_Configuration, Dsc_Resources, and Dsc_Tools directory.
19 | [parameter(mandatory)]
20 | [string]
21 | $WorkingDirectory,
22 |
23 | #Directory containing all the resources to process. Defaults to a Dsc_Resources directory under the working directory.
24 | [parameter()]
25 | [ValidateNotNullOrEmpty()]
26 | [string]
27 | $SourceResourceDirectory,
28 |
29 | #Directory containing all the tools to process. Defaults to a Dsc_Tooling directory under the working directory.
30 | [parameter()]
31 | [ValidateNotNullOrEmpty()]
32 | [string]
33 | $SourceToolDirectory,
34 |
35 | #Root of the location where pull server artificates (configurations and zipped resources) are published.
36 | [parameter(mandatory)]
37 | [string]
38 | $DestinationRootDirectory,
39 |
40 | #Destination for any tools that are published.
41 | [parameter(mandatory)]
42 | [string]
43 | $DestinationToolDirectory,
44 |
45 | #Modules to exclude from the resource testing and deployment process.
46 | [parameter(mandatory)]
47 | [string[]]
48 | $ExcludedModules,
49 |
50 | #The configuration data hashtable for the configuration to apply against.
51 | [parameter(mandatory)]
52 | [System.Collections.Hashtable]
53 | $ConfigurationData,
54 |
55 | #The name of the module to load that contains the configuration to run.
56 | [parameter(mandatory)]
57 | [string]
58 | $ConfigurationModuleName,
59 |
60 | #The name of the configuration to run.
61 | [parameter(mandatory)]
62 | [string]
63 | $ConfigurationName,
64 |
65 | #Custom location for the location of the DSC Build Tools modules.
66 | [parameter()]
67 | [ValidateNotNullOrEmpty()]
68 | [string]
69 | $CurrentToolsDirectory,
70 |
71 | #This switch is used to indicate that configuration documents should be generated and deployed.
72 | [parameter()]
73 | [switch]
74 | $Configuration,
75 |
76 | #This switch is used to indicate that custom resources should be tested and deployed.
77 | [parameter()]
78 | [switch]
79 | $Resource,
80 |
81 | #This switch is used to indicate that the custom tools should be tested and deployed.
82 | [parameter()]
83 | [switch]
84 | $Tools
85 |
86 | )
87 |
88 | $script:DscBuildParameters = new-object PSObject -property $PSBoundParameters
89 | if (-not $PSBoundParameters.ContainsKey('SourceResourceDirectory')) {
90 | Add-DscBuildParameter -Name SourceResourceDirectory -value (Join-Path $WorkingDirectory 'Dsc_Resources')
91 | }
92 | if (-not $PSBoundParameters.ContainsKey('SourceToolDirectory')) {
93 | Add-DscBuildParameter -Name SourceToolDirectory -value (Join-Path $WorkingDirectory 'Dsc_Tooling')
94 | }
95 | if (-not $PSBoundParameters.ContainsKey('CurrentToolsDirectory')) {
96 | Add-DscBuildParameter -Name CurrentToolsDirectory -value (join-path $env:ProgramFiles 'WindowsPowerShell\Modules')
97 | }
98 | Add-DscBuildParameter -Name ProgramFilesModuleDirectory -value (join-path $env:ProgramFiles 'WindowsPowerShell\Modules')
99 |
100 | $ParametersToPass = @{}
101 | foreach ($key in ('Whatif', 'Verbose', 'Debug'))
102 | {
103 | if ($PSBoundParameters.ContainsKey($key)) {
104 | $ParametersToPass[$key] = $PSBoundParameters[$key]
105 | }
106 | }
107 |
108 | Clear-InstalledDscResource @ParametersToPass
109 | Clear-CachedDscResource @ParametersToPass
110 |
111 | Invoke-DscResourceUnitTest @ParametersToPass
112 |
113 | Copy-CurrentDscResource @ParametersToPass
114 | Copy-CurrentDscTools @ParametersToPass
115 |
116 | Test-DscResourceIsValid @ParametersToPass
117 |
118 | Assert-DestinationDirectory @ParametersToPass
119 |
120 | Invoke-DscConfiguration @ParametersToPass
121 |
122 | Compress-DscResourceModule @ParametersToPass
123 | Publish-DscToolModule @ParametersToPass
124 | Publish-DscResourceModule @ParametersToPass
125 | Publish-DscConfiguration @ParametersToPass
126 | }
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/New-DscNodeMetadata.ps1:
--------------------------------------------------------------------------------
1 | function New-DscNodeMetadata
2 | {
3 | <#
4 | .Synopsis
5 | Creates a new Dsc metadata file describing a node.
6 | .Description
7 | Create a new Dsc metadata file to populate AllNodes in a Dsc Configuration.
8 | .Example
9 | New-DscNodeMetadata -Name NY-TestSQL01 -Location NY -ServerType VM
10 | .Example
11 | New-DscNodeMetadata -Name NY-TestService01 -Location NY -ServerType Physical
12 | #>
13 | param
14 | (
15 | #Server name, same as ActiveDirectory server account name.
16 | [parameter(
17 | Mandatory,
18 | ValueFromPipelineByPropertyName,
19 | Position = 1
20 | )]
21 | [string]
22 | $Name,
23 |
24 | #Data center or site the server is in.
25 | [parameter(
26 | Mandatory,
27 | ValueFromPipelineByPropertyName,
28 | Position = 1
29 | )]
30 | [string]
31 | $Location,
32 |
33 | #Type of server (physical or virtual)
34 | [parameter(
35 | Mandatory,
36 | ValueFromPipelineByPropertyName
37 | )]
38 | [ValidateSet('Physical','VM')]
39 | [string]
40 | $ServerType,
41 |
42 |
43 | #Unique identifier for this node. Will automatically generate one if not supplied.
44 | [parameter(
45 | ValueFromPipelineByPropertyName
46 | )]
47 | [ValidateNotNullOrEmpty()]
48 | [guid]
49 | $NodeName,
50 |
51 | #Path to the AllNodes subfolder in the configuration data folder.
52 | #Defaults to ${repository root}/Configuration/AllNodes
53 | [parameter()]
54 | [ValidateNotNullOrEmpty()]
55 | [string]
56 | $Path
57 | )
58 | begin
59 | {
60 | if ($psboundparameters.containskey('path')) {
61 | $psboundparameters.Remove('path') | out-null
62 | }
63 | Resolve-ConfigurationDataPath -Path $Path
64 |
65 | $AllNodesConfigurationPath = (join-path $script:ConfigurationDataPath 'AllNodes')
66 | }
67 | process
68 | {
69 | if (-not $psboundparameters.containskey('NodeName')){
70 | $psboundparameters.Add('NodeName', [guid]::NewGuid().Guid)
71 | }
72 | Out-ConfigurationDataFile -Parameters $psboundparameters -ConfigurationDataPath $AllNodesConfigurationPath
73 | }
74 |
75 | }
76 |
77 | function New-DscServiceMetadata {
78 | [cmdletbinding()]
79 | param (
80 | [parameter(
81 | Mandatory,
82 | ValueFromPipelineByPropertyName
83 | )]
84 | [string]
85 | $Name,
86 | [string[]]
87 | $Nodes,
88 | [string[]]
89 | $Roles,
90 | [string]
91 | $Path
92 | )
93 |
94 | begin {
95 | if ($psboundparameters.containskey('path')) {
96 | $psboundparameters.Remove('path') | out-null
97 | }
98 | Resolve-ConfigurationDataPath -Path $Path
99 |
100 | $ServicesConfigurationPath = (join-path $script:ConfigurationDataPath 'Services')
101 | }
102 | process {
103 | $OutConfigurationDataFileParams = @{
104 | Parameters = $psboundparameters
105 | ConfigurationDataPath = $ServicesConfigurationPath
106 | DoNotIncludeName = $true
107 | }
108 | Out-ConfigurationDataFile @OutConfigurationDataFileParams
109 | }
110 | }
111 |
112 | function New-DscSiteMetadata {
113 | [cmdletbinding()]
114 | param (
115 | [string]
116 | $Name,
117 | [string]
118 | $Path
119 | )
120 |
121 | begin {
122 | if ($psboundparameters.containskey('path')) {
123 | $psboundparameters.Remove('path') | out-null
124 | }
125 | Resolve-ConfigurationDataPath -Path $Path
126 |
127 | $SiteDataConfigurationPath = (join-path $script:ConfigurationDataPath 'SiteData')
128 | }
129 | process {
130 | Out-ConfigurationDataFile -Parameters $psboundparameters -ConfigurationDataPath $SiteDataConfigurationPath
131 | }
132 | }
133 |
134 |
135 | function Out-ConfigurationDataFile {
136 | [cmdletbinding()]
137 | param($Parameters, $ConfigurationDataPath, [switch]$DoNotIncludeName)
138 |
139 | $StartingBlock = "@{`r`n"
140 | $EndingBlock = "`r`n}"
141 | $ExcludedParameters = [System.Management.Automation.Internal.CommonParameters].GetProperties().Name
142 | if ($DoNotIncludeName) {
143 | $ExcludedParameters += 'Name'
144 | }
145 | $ofs = "', '"
146 |
147 | $configuration = $StartingBlock
148 | foreach ($key in $Parameters.keys) {
149 | if ($ExcludedParameters -notcontains $key )
150 | {
151 | $Configuration += "`r`n`t$key = '$($Parameters[$key])'"
152 | }
153 | }
154 |
155 | $Configuration += $EndingBlock
156 |
157 | $configuration | out-file (join-path $ConfigurationDataPath "$($Parameters['Name'].toupper()).psd1") -Encoding Ascii
158 |
159 | }
160 |
161 |
--------------------------------------------------------------------------------
/Tooling/DscOperations/Set-DscClient.ps1:
--------------------------------------------------------------------------------
1 | function Set-DscClient
2 | {
3 | <#
4 | .Synopsis
5 | Configures the Local Configuration Manager (LCM) for a server
6 | .Description
7 | Configures the Local Configuration Manager (LCM) for a server. Parameters can either be specified manually or looked up from configuration data.
8 | .Example
9 | Set-DscClient -Name OR-WEB01 -ConfigurationData (Get-DscConfigurationData -path d:\gitlab\Dsc_Configuration)
10 | #>
11 | [cmdletbinding(DefaultParameterSetName='FromCommandLine')]
12 | param (
13 | #Name of the host to configure. If you are using -ConfigurationData, this will need to be the host name as specified in the Name property.
14 | [parameter(
15 | Position = 0,
16 | Mandatory,
17 | ValueFromPipelineByPropertyName
18 | )]
19 | [ValidateNotNullOrEmpty()]
20 | [alias('ComputerName', 'PSComputerName', '__Server')]
21 | [string]
22 | $Name,
23 |
24 | #NodeName is what will be set for the ConfigurationID on the LCM.
25 | [parameter(
26 | Position = 1,
27 | ValueFromPipelineByPropertyName,
28 | Mandatory,
29 | ParameterSetName = 'FromCommandLine'
30 | )]
31 | [string]
32 | $NodeName,
33 |
34 | #The url for the Pull Server to contact for configurations.
35 | [parameter(
36 | Position = 2,
37 | ValueFromPipelineByPropertyName,
38 | Mandatory,
39 | ParameterSetName = 'FromCommandLine'
40 | )]
41 | [string]
42 | $PullServerUrl,
43 |
44 | #The thumbprint for the certificate the LCM should use to decrypt passed credentials.
45 | [parameter(
46 | Position = 3,
47 | ValueFromPipelineByPropertyName,
48 | Mandatory,
49 | ParameterSetName = 'FromCommandLine'
50 | )]
51 | [string]
52 | $CertificateThumbprint,
53 |
54 | #Setting this flag will just clear the existing LCM settings.
55 | [parameter(
56 | ParameterSetName = 'ClearConfigOnly'
57 | )]
58 | [switch]
59 | $ClearConfigurationOnly,
60 |
61 | #The ConfigurationData hashtable (from Get-DscConfigurationData in the DscConfiguration module).
62 | [parameter(
63 | Position = 1,
64 | Mandatory,
65 | ParameterSetName = 'FromConfigurationData'
66 | )]
67 | [System.Collections.Hashtable]
68 | $ConfigurationData
69 | )
70 |
71 | process {
72 |
73 | if ($PSCmdlet.ParameterSetName -eq 'FromConfigurationData') {
74 | $Node = ($ConfigurationData.AllNodes |
75 | Where-Object { $_.Name -like $Name })
76 | $NodeName = $Node.NodeName
77 | $CertificateThumbprint = $ConfigurationData['AllNodes'].where({$_.NodeName -eq '*'}).CertificateID
78 | $PullServerUrl = Resolve-DscConfigurationProperty -ConfigurationData $ConfigurationData -Node $Node -PropertyName 'PullServer'
79 | }
80 |
81 | $ICMParams = @{
82 | Session = New-PSSession -ComputerName $Name
83 | }
84 |
85 | Write-Verbose 'Clearing out pending and current MOFs and existing LCM configuration.'
86 | icm @ICMParams -ScriptBlock {
87 | dir 'c:\windows\System32\configuration\*.mof*' | Remove-Item
88 | Get-Process -Name WmiPrvSE -ErrorAction SilentlyContinue |
89 | Stop-Process -Force
90 | }
91 |
92 | if (-not $ClearConfigurationOnly)
93 | {
94 | Write-Verbose ""
95 | Write-Verbose "$Name will be configured with: "
96 | Write-Verbose "`tNodeName = $NodeName"
97 | Write-Verbose "`tConfigurationID = $ConfigurationID"
98 | Write-Verbose "`tPullServerUrl = $PullServerUrl"
99 | Write-Verbose "`tCertificateID = $CertificateThumbprint"
100 |
101 |
102 | configuration PullClientConfig
103 | {
104 | param ($NodeName, $ConfigurationID, $PullServer, $LocalCertificateThumbprint)
105 |
106 | Node $NodeName
107 | {
108 | LocalConfigurationManager
109 | {
110 | AllowModuleOverwrite = 'True'
111 | CertificateID = $LocalCertificateThumbprint
112 | ConfigurationID = $ConfigurationID
113 | ConfigurationModeFrequencyMins = 60
114 | ConfigurationMode = 'ApplyAndAutoCorrect'
115 | RebootNodeIfNeeded = 'True'
116 | RefreshMode = 'PULL'
117 | DownloadManagerName = 'WebDownloadManager'
118 | DownloadManagerCustomData = (@{ServerUrl = "http://$PullServer/psdscpullserver.svc";AllowUnsecureConnection = 'True'})
119 | }
120 | }
121 | }
122 |
123 | if (-not [string]::IsNullOrEmpty($NodeName))
124 | {
125 | Write-Verbose "Generating Pull Client Configuration for $Name."
126 | PullClientConfig -NodeName $Name -ConfigurationID $NodeName -PullServer $PullServerUrl -LocalCertificateThumbprint $CertificateThumbprint
127 |
128 | Write-Verbose "Applying Pull Client Configuration for $Name"
129 | Set-DSCLocalConfigurationManager -Path .\PullClientConfig -ComputerName $Name -Verbose
130 | Remove-Item ./pullclientconfig -Recurse -Force
131 | }
132 | else
133 | {
134 | Write-Verbose "No matching NodeName for $Name."
135 | }
136 |
137 | }
138 | }
139 | }
140 |
141 |
142 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/ConvertTo-EncryptedFile.ps1:
--------------------------------------------------------------------------------
1 | Function ConvertTo-EncryptedFile
2 | {
3 | [cmdletbinding(DefaultParameterSetName='LocalCertStoreAndFilePath')]
4 | Param(
5 | #Path to the file to encrypt.
6 | [Parameter(
7 | Position=0,
8 | Mandatory=$true,
9 | ValueFromPipelineByPropertyName=$true,
10 | ParameterSetName = 'LocalCertStoreAndFilePath'
11 | )]
12 | [Parameter(
13 | Position=0,
14 | Mandatory=$true,
15 | ValueFromPipelineByPropertyName=$true,
16 | ParameterSetName = 'ArbitraryCertAndFilePath'
17 | )]
18 | [string]
19 | $Path,
20 |
21 | #FileInfo object to encrypt.
22 | [Parameter(
23 | Position=0,
24 | ValueFromPipeline=$true,
25 | ParameterSetName = 'LocalCertStoreAndInputObject'
26 | )]
27 | [Parameter(
28 | Position=0,
29 | ValueFromPipeline=$true,
30 | ParameterSetName = 'ArbitraryCertAndInputObject'
31 | )]
32 | [System.IO.FileInfo]
33 | $InputObject,
34 |
35 | #Can be a path to the local cert store like Cert:\CurrentUser\My\9554F368FEA619A655A1D49408FC13C3E0D60E11
36 | [Parameter(
37 | mandatory=$true,
38 | position = 1,
39 | ParameterSetName = 'LocalCertStoreAndFilePath'
40 | )]
41 | [Parameter(
42 | mandatory=$true,
43 | position = 1,
44 | ParameterSetName = 'LocalCertStoreAndInputObject'
45 | )]
46 | [string]
47 | $CertificatePath,
48 |
49 | #Must be a System.Security.Cryptography.X509Certificates.X509Certificate2 object
50 | [Parameter(
51 | mandatory=$true,
52 | position = 1,
53 | ParameterSetName = 'ArbitraryCertAndInputObject'
54 | )]
55 | [Parameter(
56 | mandatory=$true,
57 | position = 1,
58 | ParameterSetName = 'ArbitraryCertAndFilePath'
59 | )]
60 | [System.Security.Cryptography.X509Certificates.X509Certificate2]
61 | $Certificate,
62 | [parameter()]
63 | [string]
64 | $FileExtension = 'encrypted'
65 | )
66 |
67 | process
68 | {
69 | switch ($PSCmdlet.ParameterSetName)
70 | {
71 | 'LocalCertStoreAndFilePath' { Write-Verbose "Loading certificate from $CertificatePath"; $Certificate = Get-Item $CertificatePath }
72 | 'LocalCertStoreAndInputObject' { Write-Verbose "Loading certificate from $CertificatePath"; $Certificate = Get-Item $CertificatePath ; $Path = $InputObject.FullName }
73 | 'ArbitraryCertAndInputObject' { $Path = $InputObject.FullName }
74 | 'ArbitraryCertAndFilePath' { }
75 | }
76 | try
77 | {
78 | $Path = (Resolve-Path $Path -ErrorAction Stop).ProviderPath
79 |
80 | $AesProvider = New-Object System.Security.Cryptography.AesManaged
81 | $AesProvider.KeySize = 256
82 | $AesProvider.BlockSize = 128
83 | $AesProvider.Mode = [System.Security.Cryptography.CipherMode]::CBC
84 |
85 | $KeyFormatter = New-Object System.Security.Cryptography.RSAPKCS1KeyExchangeFormatter($Certificate.PublicKey.Key)
86 | [Byte[]]$KeyEncrypted = $KeyFormatter.CreateKeyExchange($AesProvider.Key, $AesProvider.GetType())
87 | [Byte[]]$LenKey = $Null
88 | [Byte[]]$LenIV = $Null
89 | [Int]$LKey = $KeyEncrypted.Length
90 | $LenKey = [System.BitConverter]::GetBytes($LKey)
91 | [Int]$LIV = $AesProvider.IV.Length
92 | $LenIV = [System.BitConverter]::GetBytes($LIV)
93 |
94 | $FileStreamWriter = $Null
95 | Try
96 | {
97 | $FileStreamWriter = New-Object System.IO.FileStream("$Path.$FileExtension", [System.IO.FileMode]::Create)
98 | }
99 | Catch
100 | {
101 | $message = "Unable to open output file ($Path.$FileExtension) for writing."
102 | throw $message
103 | }
104 |
105 | $FileStreamWriter.Write($LenKey, 0, 4)
106 | $FileStreamWriter.Write($LenIV, 0, 4)
107 | $FileStreamWriter.Write($KeyEncrypted, 0, $LKey)
108 | $FileStreamWriter.Write($AesProvider.IV, 0, $LIV)
109 |
110 | $Transform = $AesProvider.CreateEncryptor()
111 | $CryptoStream = New-Object System.Security.Cryptography.CryptoStream($FileStreamWriter, $Transform, [System.Security.Cryptography.CryptoStreamMode]::Write)
112 | [Int]$Count = 0
113 | [Int]$Offset = 0
114 | [Int]$BlockSizeBytes = $AesProvider.BlockSize / 8
115 |
116 | [Byte[]]$Data = New-Object Byte[] $BlockSizeBytes
117 | [Int]$BytesRead = 0
118 |
119 | Try
120 | {
121 | $FileStreamReader = New-Object System.IO.FileStream("$Path", [System.IO.FileMode]::Open)
122 | }
123 | Catch
124 | {
125 | throw "Unable to open input file ($Path) for reading."
126 | }
127 |
128 | Do
129 | {
130 | $Count = $FileStreamReader.Read($Data, 0, $BlockSizeBytes)
131 | $Offset += $Count
132 | $CryptoStream.Write($Data, 0, $Count)
133 | $BytesRead += $BlockSizeBytes
134 | } While ($Count -gt 0)
135 |
136 | $CryptoStream.FlushFinalBlock()
137 | }
138 | catch
139 | {
140 | throw $_
141 | }
142 | finally
143 | {
144 | $CryptoStream.Close()
145 | $FileStreamReader.Close()
146 | $FileStreamWriter.Close()
147 | }
148 | }
149 | }
150 |
151 |
152 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/Update-ModuleMetadataVersion.ps1:
--------------------------------------------------------------------------------
1 | function Update-ModuleMetadataVersion
2 | {
3 | [cmdletbinding(DefaultParameterSetName='ByDirectoryInfo')]
4 | param (
5 | [parameter(
6 | ParameterSetName = 'ByPath',
7 | Position = 0,
8 | Mandatory,
9 | ValueFromPipelineByPropertyName
10 | )]
11 | [string]
12 | $Path,
13 | [parameter(
14 | ParameterSetName = 'ByDirectoryInfo',
15 | Mandatory,
16 | ValueFromPipeline
17 | )]
18 | [System.IO.DirectoryInfo]
19 | $InputObject
20 | )
21 | process
22 | {
23 | $ModuleMetadataFile = ''
24 | switch ($PSCmdlet.ParameterSetName)
25 | {
26 | 'ByPath' { $ModuleMetadataFile = Resolve-ModuleMetadataFile -path $Path }
27 | 'ByDirectoryInfo' { $ModuleMetadataFile = Resolve-ModuleMetadataFile -InputObject $InputObject }
28 | }
29 |
30 | if (-not [string]::IsNullOrEmpty($ModuleMetadataFile))
31 | {
32 | Write-Verbose "Loading PSD1 properties."
33 | $ModuleMetadataHash = Get-Hashtable -Path $ModuleMetadataFile
34 | $Version = [Version]::Parse( $ModuleMetadataHash.ModuleVersion )
35 | Write-Verbose "Current build is $Version"
36 |
37 | $NewVersion = "$($Version.Major).$($Version.Minor).$($Version.Build + 1)"
38 | Write-Verbose "New build is $NewVersion"
39 | $ModuleMetadataHash.ModuleVersion = $NewVersion
40 |
41 | Write-Verbose "Writing new manifest file - $ModuleMetadataFile."
42 | New-ModuleManifest -Path $ModuleMetadataFile @ModuleMetadataHash
43 |
44 | Get-Item (Split-Path $ModuleMetadataFile)
45 | }
46 | else
47 | {
48 | Write-Warning "No module metadata file updated."
49 | }
50 | }
51 | }
52 |
53 | function Resolve-ModuleMetadataFile
54 | {
55 | [cmdletbinding(DefaultParameterSetName='ByDirectoryInfo')]
56 | param (
57 | [parameter(
58 | ParameterSetName = 'ByPath',
59 | Mandatory,
60 | ValueFromPipelineByPropertyName
61 | )]
62 | [string]
63 | $Path,
64 | [parameter(
65 | ParameterSetName = 'ByDirectoryInfo',
66 | Mandatory,
67 | ValueFromPipeline
68 | )]
69 | [System.IO.DirectoryInfo]
70 | $InputObject
71 |
72 | )
73 |
74 | process
75 | {
76 | $MetadataFileFound = $true
77 | $MetadataFilePath = ''
78 | Write-Verbose "Using Parameter set - $($PSCmdlet.ParameterSetName)"
79 | switch ($PSCmdlet.ParameterSetName)
80 | {
81 | 'ByPath' {
82 | Write-Verbose "Testing Path - $path"
83 | if (Test-Path $Path)
84 | {
85 | Write-Verbose "`tFound $path."
86 | $item = (Get-Item $Path)
87 | if ($item.psiscontainer)
88 | {
89 | Write-Verbose "`t`tIt is a folder."
90 | $ModuleName = Split-Path $Path -Leaf
91 | $MetadataFilePath = Join-Path $Path "$ModuleName.psd1"
92 | $MetadataFileFound = Test-Path $MetadataFilePath
93 | }
94 | else
95 | {
96 | if ($item.Extension -like '.psd1')
97 | {
98 | Write-Verbose "`t`tIt is a module metadata file."
99 | $MetadataFilePath = $item.FullName
100 | $MetadataFileFound = $true
101 | }
102 | else
103 | {
104 | $ModulePath = Split-Path $Path
105 | Write-Verbose "`t`tSearching for module metadata folder in $ModulePath"
106 | $ModuleName = Split-Path $ModulePath -Leaf
107 | Write-Verbose "`t`tModule name is $ModuleName."
108 | $MetadataFilePath = Join-Path $ModulePath "$ModuleName.psd1"
109 | Write-Verbose "`t`tChecking for $MetadataFilePath."
110 | $MetadataFileFound = Test-Path $MetadataFilePath
111 | }
112 | }
113 | }
114 | else
115 | {
116 | $MetadataFileFound = $false
117 | }
118 | }
119 | 'ByDirectoryInfo' {
120 | $ModuleName = $InputObject.Name
121 | $MetadataFilePath = Join-Path $InputObject.FullName "$ModuleName.psd1"
122 | $MetadataFileFound = Test-Path $MetadataFilePath
123 | }
124 |
125 | }
126 |
127 | if ($MetadataFileFound -and (-not [string]::IsNullOrEmpty($MetadataFilePath)))
128 | {
129 | Write-Verbose "Found a module metadata file at $MetadataFilePath."
130 | Convert-path $MetadataFilePath
131 | }
132 | else
133 | {
134 | Write-Error "Failed to find a module metadata file at $MetadataFilePath."
135 | }
136 | }
137 | }
138 |
139 |
140 |
--------------------------------------------------------------------------------
/Tooling/DscDevelopment/Deserializer.ps1:
--------------------------------------------------------------------------------
1 | #
2 | # Functions for examining DSC MOF documents. #
3 | # Because parsing and validation require that the classes
4 | # are available when loading an instance document, we maintain and in-memory cache of the classes
5 | # that are loaded. The typical use pattern looks something like:
6 | #
7 | # Reset-CimClassCache # Clears the cache, then loads the system default DSC classes
8 | #
9 | # Add-CachedCimClass -MofClassFile myClasses.mof # add classes defined in the file to the cache
10 | # Add-CachedCimClass -MofClassFile moreOfMyClasses.mof # add classes defined in a second file
11 | #
12 | # Get-CachedCimClass -ListLoadedFiles # List all of the loaded files
13 | # Get-CachedCimClass -FileName (rvpa myClasses.mof) # List all of the classes defined in specifed file.
14 | #
15 | # Import-CimInstances -MofInstanceFilePath myInstances.mof # import and emit all CIM instances defined in this doc.
16 | #
17 |
18 | <#
19 | .Synopsis
20 | Reset the CIM class cache
21 | .DESCRIPTION
22 | Before a MOF file defining CIM instances can be read, the classes
23 | must be loaded into the class cache. Calling this cmdlet resets the
24 | class cache to the default classes
25 | .EXAMPLE
26 | Reset-CimClassCache
27 | .EXAMPLE
28 | Another example of how to use this cmdlet
29 | #>
30 | function Reset-CimClassCache
31 | {
32 | [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ClearCache()
33 | [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::LoadDefaultCimKeywords()
34 | }
35 |
36 | <#
37 | .Synopsis
38 | Adds the classes defined in a MOF file to the current set of classes
39 | .DESCRIPTION
40 | Before a MOF file defining CIM instances can be read, the classes
41 | must be loaded into the class cache. Calling this function will import classes
42 | from a MOF file and add them to the set of cached classes. It must be used
43 | to import the CIM class definition of any custom classes that might be
44 | contained in an instance document.
45 | .EXAMPLE
46 | Add-CachedCimClass ./mtFileDefininingCustomCimClasses.schema.mof
47 | #>
48 | function Add-CachedCimClass
49 | {
50 | [CmdletBinding(DefaultParameterSetName="FromMofFile")]
51 | param (
52 | # The MOF file to import classes from
53 | [Parameter(ParameterSetName="FromMofFile", ValueFromPipelineByPropertyName, Position=0)]
54 | [alias('fullname', 'path')]
55 | [string]
56 | $MofClassFile,
57 | [Parameter(ParameterSetName="FromModule")]
58 | $Module
59 | )
60 |
61 | process {
62 | $errors = New-Object System.Collections.ObjectModel.Collection[Exception]
63 |
64 | try
65 | {
66 | if ($PSCmdlet.ParameterSetName -eq "FromMofFile")
67 | {
68 | $resolvedMofPath = Resolve-Path -ErrorAction stop $MofClassFile
69 | [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ImportClasses($resolvedMofPath, $null, $errors)
70 | $errors | Write-Error
71 | }
72 | else
73 | {
74 | foreach ($mi in Get-Module -ListAvailable -Name $Module)
75 | {
76 | Write-Verbose -Verbose:$Verbose "Processing module $($module.Name)"
77 | $schemaFile = ""
78 | [void] [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ImportCimKeywordsFromModule($mi, $null, [ref] $schemaFile)
79 | if ($schemaFile)
80 | {
81 | Write-Verbose -Verbose:$verbose " Schema loaded from file: '$schemaFile'"
82 | }
83 | else
84 | {
85 | Write-Verbose -Verbose:$verbose " No schema file was found."
86 | }
87 | }
88 | }
89 | }
90 | catch
91 | {
92 | throw $_
93 | }
94 | }
95 | }
96 |
97 | <#
98 | .Synopsis
99 | Returns Dump out all of the cached CIM classes
100 | .DESCRIPTION
101 | Before a MOF file defining CIM instances can be read, the classes
102 | must be loaded into the class cache or an error will occur, This function will
103 | .EXAMPLE
104 | Import-CimInstances ./myInstanceDoc.mof
105 | #>
106 |
107 | function Get-CachedCimClass
108 | {
109 | [CmdletBinding(DefaultParameterSetName="ByClassName")]
110 | param (
111 | [Parameter(ParameterSetName="ByClassName", Position=0)]
112 | $ClassName = "*",
113 | [Parameter(ParameterSetName="ByFileName")]
114 | $FileName,
115 | [Parameter(ParameterSetName="ByModuleName")]
116 | $ModuleName,
117 | [Parameter(ParameterSetName="ListLoadedFiles")]
118 | [switch]
119 | $ListLoadedFiles
120 | )
121 |
122 | switch ($PSCmdlet.ParameterSetName)
123 | {
124 | ByFileName {
125 | [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::GetCachedClassByFileName($FileName)
126 | break
127 | }
128 | ByModuleName {
129 | [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::GetCachedClassByModuleName($ModuleName)
130 | break
131 | }
132 | ListLoadedFiles {
133 | [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::GetLoadedFiles()
134 | break
135 | }
136 | default {
137 | [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::GetCachedClasses().
138 | Where{$_.CimClassName -like $ClassName}
139 | break
140 | }
141 | }
142 | }
143 |
144 | <#
145 | .Synopsis
146 | Returns CIM instances defined in a MOF file.
147 | .DESCRIPTION
148 | This file will parse a MOF instance document and return the resulting instances.
149 | But before a MOF file defining CIM instances can be read, the classes
150 | must be loaded into the class cache or an error will occur
151 | .EXAMPLE
152 | Import-CimInstance ./myInstanceDoc.mof
153 | #>
154 | function Import-CimInstance
155 | {
156 | param (
157 | [parameter(ValueFromPipelineByPropertyName)]
158 | [alias('fullname', 'path')]
159 | [string]
160 | $MofInstanceFilePath
161 | )
162 |
163 | process {
164 | try
165 | {
166 |
167 | $resolvedMofPath = Resolve-Path -ErrorAction stop $MofInstanceFilePath
168 | [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ImportInstances($resolvedMofPath)
169 | }
170 | catch
171 | {
172 | Write-Error -Exception $_.Exception -Message "Error with $MofInstanceFilePath"
173 | }
174 | }
175 | }
176 |
177 |
--------------------------------------------------------------------------------
/Tooling/DscConfiguration/ConvertFrom-EncryptedFile.ps1:
--------------------------------------------------------------------------------
1 | Function ConvertFrom-EncryptedFile
2 | {
3 | [cmdletbinding(DefaultParameterSetName='LocalCertStoreAndFilePath')]
4 | Param(
5 | [Parameter(
6 | Position=0,
7 | Mandatory = $true,
8 | ValueFromPipelineByPropertyName=$true,
9 | ParameterSetName = 'LocalCertStoreAndFilePath'
10 | )]
11 | [Parameter(
12 | Position=0,
13 | Mandatory = $true,
14 | ValueFromPipelineByPropertyName=$true,
15 | ParameterSetName = 'ArbitraryCertAndFilePath'
16 | )]
17 | [string]
18 | $Path,
19 |
20 | [Parameter(
21 | Position=0,
22 | Mandatory = $true,
23 | ValueFromPipeline=$true,
24 | ParameterSetName = 'LocalCertStoreAndInputObject'
25 | )]
26 | [Parameter(
27 | Position=0,
28 | Mandatory = $true,
29 | ValueFromPipeline=$true,
30 | ParameterSetName = 'ArbitraryCertAndInputObject'
31 | )]
32 | [System.IO.FileInfo]
33 | $InputObject,
34 |
35 | #Can be a path to the local cert store like Cert:\CurrentUser\My\9554F368FEA619A655A1D49408FC13C3E0D60E11
36 | [Parameter(
37 | mandatory=$true,
38 | position = 1,
39 | ParameterSetName = 'LocalCertStoreAndFilePath'
40 | )]
41 | [Parameter(
42 | mandatory=$true,
43 | position = 1,
44 | ParameterSetName = 'LocalCertStoreAndInputObject'
45 | )]
46 | [string]
47 | $CertificatePath,
48 |
49 | #Must be a System.Security.Cryptography.X509Certificates.X509Certificate2 object
50 | [Parameter(
51 | mandatory=$true,
52 | position = 1,
53 | ParameterSetName = 'ArbitraryCertAndInputObject'
54 | )]
55 | [Parameter(
56 | mandatory=$true,
57 | position = 1,
58 | ParameterSetName = 'ArbitraryCertAndFilePath'
59 | )]
60 | [System.Security.Cryptography.X509Certificates.X509Certificate2]
61 | $Certificate,
62 | [parameter()]
63 | [string]
64 | $FileExtension = 'encrypted'
65 | )
66 |
67 | process
68 | {
69 | switch ($PSCmdlet.ParameterSetName)
70 | {
71 | 'LocalCertStoreAndFilePath' { Write-Verbose "Loading certificate from $CertificatePath"; $Certificate = Get-Item $CertificatePath }
72 | 'LocalCertStoreAndInputObject' { Write-Verbose "Loading certificate from $CertificatePath"; $Certificate = Get-Item $CertificatePath ; $Path = $InputObject.FullName }
73 | 'ArbitraryCertAndInputObject' { $Path = $InputObject.FullName }
74 | 'ArbitraryCertAndFilePath' { }
75 | }
76 |
77 | try
78 | {
79 | $Path = (Resolve-Path $Path -ErrorAction Stop).ProviderPath
80 |
81 | $AesProvider = New-Object System.Security.Cryptography.AesManaged
82 | $AesProvider.KeySize = 256
83 | $AesProvider.BlockSize = 128
84 | $AesProvider.Mode = [System.Security.Cryptography.CipherMode]::CBC
85 | [Byte[]]$LenKey = New-Object Byte[] 4
86 | [Byte[]]$LenIV = New-Object Byte[] 4
87 |
88 | If($Path.Split(".")[-1] -ne $FileExtension)
89 | {
90 | Write-Error "The file to decrypt must be named *.encrypted."
91 | Return
92 | }
93 |
94 | If($Certificate.HasPrivateKey -eq $False -or $Certificate.HasPrivateKey -eq $null)
95 | {
96 | Write-Error "The supplied certificate does not contain a private key, or it could not be accessed."
97 | Return
98 | }
99 |
100 | Try
101 | {
102 | $FileStreamReader = New-Object System.IO.FileStream("$Path", [System.IO.FileMode]::Open)
103 | }
104 | Catch
105 | {
106 | $message = "Unable to open input file $path for reading."
107 | throw $message
108 | }
109 |
110 | $FileStreamReader.Seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null
111 | $FileStreamReader.Seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null
112 | $FileStreamReader.Read($LenKey, 0, 3) | Out-Null
113 | $FileStreamReader.Seek(4, [System.IO.SeekOrigin]::Begin) | Out-Null
114 | $FileStreamReader.Read($LenIV, 0, 3) | Out-Null
115 | [Int]$LKey = [System.BitConverter]::ToInt32($LenKey, 0)
116 | [Int]$LIV = [System.BitConverter]::ToInt32($LenIV, 0)
117 | [Int]$StartC = $LKey + $LIV + 8
118 | [Int]$LenC = [Int]$FileStreamReader.Length - $StartC
119 | [Byte[]]$KeyEncrypted = New-Object Byte[] $LKey
120 | [Byte[]]$IV = New-Object Byte[] $LIV
121 | $FileStreamReader.Seek(8, [System.IO.SeekOrigin]::Begin) | Out-Null
122 | $FileStreamReader.Read($KeyEncrypted, 0, $LKey) | Out-Null
123 | $FileStreamReader.Seek(8 + $LKey, [System.IO.SeekOrigin]::Begin) | Out-Null
124 | $FileStreamReader.Read($IV, 0, $LIV) | Out-Null
125 | [Byte[]]$KeyDecrypted = $Certificate.PrivateKey.Decrypt($KeyEncrypted, $false)
126 | $Transform = $AesProvider.CreateDecryptor($KeyDecrypted, $IV)
127 | Try
128 | {
129 | $FileStreamWriter = New-Object System.IO.FileStream("$($path -replace '\.encrypted')", [System.IO.FileMode]::Create)
130 | }
131 | Catch
132 | {
133 | Write-Error "Unable to open output file for writing.`r`n$($_.Message)"
134 | $FileStreamReader.Close()
135 | Return
136 | }
137 | [Int]$Count = 0
138 | [Int]$Offset = 0
139 | [Int]$BlockSizeBytes = $AesProvider.BlockSize / 8
140 | [Byte[]]$Data = New-Object Byte[] $BlockSizeBytes
141 | $CryptoStream = New-Object System.Security.Cryptography.CryptoStream($FileStreamWriter, $Transform, [System.Security.Cryptography.CryptoStreamMode]::Write)
142 | Do
143 | {
144 | $Count = $FileStreamReader.Read($Data, 0, $BlockSizeBytes)
145 | $Offset += $Count
146 | $CryptoStream.Write($Data, 0, $Count)
147 | }
148 | While ($Count -gt 0)
149 | $CryptoStream.FlushFinalBlock()
150 | }
151 | catch
152 | {
153 | throw $_
154 | }
155 | finally
156 | {
157 | $CryptoStream.Close()
158 | $FileStreamWriter.Close()
159 | $FileStreamReader.Close()
160 | }
161 | Get-Item "$($path -replace '\.encrypted')"
162 | }
163 | }
164 |
165 |
166 |
--------------------------------------------------------------------------------
/Tooling/DscDevelopment/New-MofFile.ps1:
--------------------------------------------------------------------------------
1 | function New-MofFile
2 | {
3 | <#
4 | .Synopsis
5 | Generates a MOF Class declaration for a DSC Resource
6 | .DESCRIPTION
7 | Uses the parameters of Set-TargetResource in a DSC Resource Module to generate a MOF schema file for use in DSC.
8 | .EXAMPLE
9 | New-MofFile -Name d:\source\dsc-prod\resources\baseresources\dscresources\Pagefile
10 | #>
11 | param (
12 | [parameter()]
13 | [string]
14 | $Path,
15 | $Version = '1.0' ,
16 |
17 | [Parameter()]
18 | [ValidateNotNullOrEmpty()]
19 | [string]
20 | $FriendlyName ,
21 |
22 | [Switch]
23 | $LoadTypes
24 | )
25 |
26 | $ResourceName = Split-Path $Path -Leaf
27 |
28 | if (!$FriendlyName) {
29 | $FriendlyName = $ResourceName
30 | }
31 |
32 | $ResourcePath = Join-Path $Path "$ResourceName.psm1"
33 |
34 | Write-Verbose "Attempting to parse $ResourcePath."
35 | try
36 | {
37 | $CommandAst = [System.Management.Automation.Language.Parser]::ParseFile($ResourcePath, [ref]$null, [ref]$null)
38 | $SetTargetResourceAst = $CommandAst.FindAll(
39 | {$args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst]},
40 | $false
41 | ) |
42 | Where-Object {$_.name -like 'Set-TargetResource'}
43 |
44 | $ParametersAst = $SetTargetResourceAst.Body.ParamBlock.Parameters
45 |
46 | # Look for type definitions and execute them in this context
47 | # This could be very buggy.
48 | if ($LoadTypes) {
49 | Write-Warning "Types added to session with -LoadType will remain in the session until it ends."
50 | $CommandAst.FindAll(
51 | {$args[0] -is [System.Management.Automation.Language.CommandAst]},
52 | $false
53 | ) |
54 | Where-Object { $_.CommandElements[0].Value -ieq 'Add-Type' } |
55 | ForEach-Object {
56 | Write-Verbose "Adding type:`n`n$($_.ToString())"
57 | Invoke-Expression $_.ToString() -ErrorAction Continue
58 | }
59 | }
60 |
61 | $Template = @"
62 | [ClassVersion("$Version"), FriendlyName("$FriendlyName")]
63 | class $ResourceName : OMI_BaseResource
64 | {
65 |
66 | "@
67 | foreach ($ParameterAst in $ParametersAst)
68 | {
69 | $PropertyString = '[write'
70 | $IsKey = $false
71 |
72 | $ParameterName = $ParameterAst.Name -replace '\$'
73 | Write-Verbose "Processing $ParameterName."
74 |
75 | $ParameterAttributesAst = $ParameterAst.Attributes |
76 | Where-Object {$_ -is [System.Management.Automation.Language.AttributeAst]}
77 | $ParameterTypeAttributeAst = $ParameterAst.Attributes |
78 | Where-Object {$_ -is [System.Management.Automation.Language.TypeConstraintAst]}
79 |
80 | switch ($ParameterAttributesAst)
81 | {
82 | {($_.typename -like 'parameter') -and (($_.NamedArguments.ArgumentName) -contains 'Mandatory')} {
83 | Write-Verbose "Parameter - $ParameterName is Mandatory."
84 | $PropertyString = '[Key'
85 | $IsKey = $true
86 | }
87 | }
88 |
89 | switch ($ParameterAttributesAst)
90 | {
91 | {$_.typename -like 'ValidateSet'} {
92 | Write-Verbose "Parameter - $ParameterName has a validate set."
93 | $oldOFS = $OFS
94 | $OFS = '", "'
95 | $SingleQuote = "'"
96 | $ValidValues = "$($_.PositionalArguments.Value -replace $SingleQuote)"
97 | $PropertyString += @"
98 | ,ValueMap{"$ValidValues"},Values{"$ValidValues"}
99 | "@
100 | $OFS = $oldOFS
101 | }
102 | }
103 |
104 | Write-Verbose "Parameter - $ParameterName is typed with $($ParameterTypeAttributeAst.TypeName)."
105 |
106 | $type = $ParameterTypeAttributeAst.TypeName.FullName -as [Type]
107 |
108 | $table = @{
109 | [string] = 'string'
110 | [string[]] = 'string'
111 | [switch] = 'boolean'
112 | [bool] = 'boolean'
113 | [boolean[]] = 'boolean'
114 | [long] = 'sint64'
115 | [long[]] = 'sint64'
116 | [int] = 'sint32'
117 | [int[]] = 'sint32'
118 | [byte] = 'uint8'
119 | [byte[]] = 'uint8'
120 | [uint32] = 'uint32'
121 | [uint32[]] = 'uint32'
122 | [uint64] = 'uint64'
123 | [uint64[]] = 'uint64'
124 | }
125 |
126 | if ($table.ContainsKey($type))
127 | {
128 | $PropertyString += "] $($table[$type]) "
129 | }
130 | elseif ($type -eq [pscredential])
131 | {
132 | $PropertyString += ',EmbeddedInstance("MSFT_Credential")] string '
133 | }
134 | else
135 | {
136 | $goodType = $false
137 |
138 | if ($null -ne $type -and $type.IsEnum)
139 | {
140 | Write-Verbose "'$type' is an Enum type. Let's convert it into a ValueMap."
141 |
142 | $eNames = ($type.GetEnumNames() | ForEach-Object { "`"$_`"" }) -join ','
143 | $eValues = ($type.GetEnumValues().value__ | ForEach-Object { "`"$_`"" }) -join ','
144 | $eType = $type.GetEnumUnderlyingType()
145 |
146 | if ($table.ContainsKey($eType))
147 | {
148 | $goodType = $true
149 | $PropertyString += ",ValueMap{$eValues},Values{$eNames}] $($table[$eType]) "
150 | }
151 | }
152 |
153 | if (-not $goodType)
154 | {
155 | Write-Warning "Don't know what to do with $($ParameterTypeAttributeAst.TypeName.FullName)"
156 | }
157 | }
158 |
159 | $arrayString = if ($type.IsArray) { '[]' } else { '' }
160 |
161 | $Template += $PropertyString + "$ParameterName$arrayString;`r`n"
162 | }
163 |
164 | $Template += @'
165 | };
166 | '@
167 |
168 | $TargetPath = join-path $Path "$ResourceName.schema.mof"
169 |
170 | if (Test-Path $TargetPath)
171 | {
172 | Write-Verbose "Removing previous file from $TargetPath."
173 | Remove-Item -Path $TargetPath -Force
174 | }
175 |
176 | Write-Verbose "Writing $ResourceName.schema.mof to $Path"
177 |
178 | $Template |
179 | Out-File -Encoding ascii -FilePath $TargetPath
180 | }
181 | catch
182 | {
183 | throw $_
184 | }
185 | }
186 |
187 |
188 |
--------------------------------------------------------------------------------
/README.old.md:
--------------------------------------------------------------------------------
1 | PowerShell Community DSC Modules
2 | ===========
3 |
4 | Desired State Configuration Modules to augment the initial offering in PowerShell V4
5 |
6 | Check out my blog series on DSC at PowerShell.org:
7 | - Overview ()
8 | - Configuring the Pull Server (REST version) ()
9 | - Creating Configurations (, )
10 | - Configuring Clients ()
11 | - Building Custom Resources ()
12 | - Packaging Custom Resources
13 | - Advanced Client Targeting
14 |
15 |
16 | ToDo
17 | =====
18 | - [x] Initial upload
19 | - [x] Make New-MofFile handle more data types and complex data types
20 | - [x] Improve the DSC Module creation documentation for General Availability
21 | - [ ] Add samples of complete configurations
22 | - [ ] Add samples of composite configurations
23 | - [ ] MORE Modules!
24 |
25 | ###Required Modules
26 | In order to run some of the code in this pack some outside resources are needed. Make sure the following are installed on the dev system.
27 |
28 | * Pester ()
29 |
30 |
31 | Getting Started With DSC Modules
32 | --------------------------------
33 |
34 |
35 | NOTE:
36 |
37 |
38 | There are two sets of MOF (Managed Object Framework) files generated in this process. Each provider module has a MOF file defining a class representing the parameters to the functions in provider module.
39 |
40 | The second type of MOF file is created by running the configuration, with one generated per target node. This MOF file is the serialization of the configuration defined in MOF format. It references the MOF classes defined in the provider modules schema files and contains the values supplied in the configuration associated to the appropriate parameters.
41 |
42 | The general flow of Resource Processing in DSC (from Configuration MOF to Desired State)
43 |
44 | When you run the configuration function, a MOF file for each node is generated. This describes the state the machine should be in after processing the MOF file. The generated configuration MOF will note the resources used and their host module (see below).
45 |
46 | For each resource defined, the DSC engine uses the classes defined in the MOF to marshal parameters to call the PowerShell DSC resource (which is basically a PowerShell module).
47 |
48 | The DSC engine calls Test-TargetResource with the parameters defined in the MOF file (as mapped in the schema MOF). If Test-TargetResource returns $false, then Set-TargetResource is called with the same parameter set.
49 |
50 | ###DSC Resources
51 |
52 | DSC Resources are nested in a module (which I'll call the host module). Resources are located in a subfolder in a host module called DscResources. One host module can contain zero or more DSC Resources.
53 |
54 | DSC Resources are modules as well, but by not being located directly on the PSModulePath, they won't clutter up your environment.
55 |
56 | ####Versioning
57 |
58 | DSC Resources are versioned by the module version of their host module.
59 |
60 |
61 | ####Naming
62 |
63 | The module name will be the resource name when configurations are defined, unless you specify an alias name for the module in the MOF schema (detailed below).
64 |
65 |
66 | ####Functions
67 |
68 | - Set-TargetResource
69 | - Set-TargetResource is called if Test-TargetResource (described in a bit) returns false. Test-TargetResource is called with the same parameters as Set-TargetResource.
70 | - This function implements the change requested. You'll need to support both the case of Ensure = 'Present' and Ensure = 'Absent'. If this resource represents a multi-step process and that process needs to support suspend/resume or requires reboots, it may be an indication that you want to break it into several resources, otherwise you'll have to implement the stage checking in this function.
71 | - Each parameter for this function will need to be modeled in the CIM schema in a CIM class named for the resource type, so if you expect structured objects, you'll need to define comprehensive schema documents.
72 | - Logging for this function occurs in the form of verbose output (which is written to the DSC event log).
73 | - While the DSC engine should only call this function if Test-TargetResource returns false, it would be prudent to write the system state changes in as idempotent a manner as possible.
74 |
75 | - Test-TargetResource
76 | - Test-TargetResource validates whether a resource configuration is in the desired state.
77 | - Test-TargetResource offers the same parameters as Set-TargetResource.
78 | - Test-TargetResource evaluate the final state of the resource (not intermediate steps) and returns a $true if the configuration matches or $false if the configuration does not.
79 | - This function needs to support both Ensure = 'Present' and Ensure = 'Absent' declarations.
80 |
81 | - Get-TargetResource
82 | - This function inventories the resource based on the key values (mandatory parameters) for the CIM schema.
83 | - Get-TargetResource returns a hashtable containing the values that match the current state of the resource configuration.
84 | - Get-TargetResource only needs to support parameters that are noted as key values in the schema.mof file (mandatory parameters in the Set-TargetResource and Get-TargetResource functions).
85 |
86 |
87 |
88 | ###The Schema
89 |
90 | The final bit of creating a provider module is the MOF schema file. The MOF schema file defines a CIM class used for serializing the parameter values from the configuration file and deserializing to apply as parameters to the call the above functions.
91 |
92 | Detailed documentation about MOF datatypes can be found here - [http://msdn.microsoft.com/en-us/library/cc250850.aspx](http://msdn.microsoft.com/en-us/library/cc250850.aspx)
93 |
94 |
95 |
96 | ####In creating the MOF schema file, there are a couple of rules.
97 |
98 | - All resource classes (those that represent the parameters for the Set-TargetResource) must inherit from OMI_BaseResource.
99 | - The file is named {resource}.schema.mof
100 | - Classes are attributed with a version number which is currently meaningless
101 |
102 | - Classes can be attributed with a FriendlyName, which would be the name that the resource would use in the configuration declaration. The full class name is used in the generated configuration MOF.
103 | - Mandatory parameters are annotated as [Key] values.
104 | - Other parameters are annotated as [Write] values.
105 | - If there is a ValidateSet or Enumeration, they are represented in a ValueMap and Values combination as part of the Write annotation.
106 |
107 | - The file encoding has to be Unicode or ASCII. UTF8 will fail validation.
108 |
109 |
110 | Validation of the MOF file should be done by running:
111 |
112 | mofcomp.exe -check {path to your mof file}.
113 |
114 |
115 |
116 | ####Example of a very basic schema.mof file:
117 |
118 | ````
119 | [version("1.0.0"), FriendlyName("PowerPlan")]
120 |
121 | class PowerPlan : OMI_BaseResource
122 |
123 | {
124 |
125 | [Key] string Name;
126 |
127 | [write,ValueMap{"Present", "Absent"},Values{"Present", "Absent"}] string Ensure;
128 |
129 | };
130 | ````
131 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/Resolve-ConfigurationProperty.Tests.ps1:
--------------------------------------------------------------------------------
1 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path
2 | $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.ps1", ".psm1")
3 | if (-not (Test-Path $sut))
4 | {
5 | $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.ps1", ".ps1")
6 | }
7 | $pathtosut = join-path $here $sut
8 | if (-not (Test-Path $pathtosut))
9 | {
10 | Write-Error "Failed to find script to test at $pathtosut"
11 | }
12 |
13 |
14 | iex ( gc $pathtosut -Raw )
15 |
16 | describe 'how Resolve-DscConfigurationProperty responds' {
17 |
18 | $ConfigurationData = @{
19 | AllNodes = @();
20 | SiteData = @{};
21 | Services = @{};
22 | Applications = @{};
23 | }
24 | $ConfigurationData.SiteData = @{ NY = @{ PullServerPath = 'ConfiguredBySite' } }
25 | context 'when a node has an override for a site property' {
26 | $Node = @{
27 | Name = 'TestBox'
28 | Location = 'NY'
29 | PullServerPath = 'ConfiguredByNode'
30 | }
31 |
32 | $result = Resolve-DscConfigurationProperty -Node $Node -PropertyName 'PullServerPath'
33 |
34 | it "should return the node's override" {
35 | $result | should be 'ConfiguredByNode'
36 | }
37 | }
38 |
39 | context 'when a node does not override the site property' {
40 | $Node = @{
41 | Name = 'TestBox'
42 | Location = 'NY'
43 | }
44 |
45 | $result = Resolve-DscConfigurationProperty -Node $Node -PropertyName 'PullServerPath'
46 | it "should return the site's default value" {
47 | $result | should be 'ConfiguredBySite'
48 | }
49 | }
50 |
51 | context 'when a specific site does not have the property but the base configuration data does' {
52 | $ConfigurationData.SiteData = @{
53 | All = @{ PullServerPath = 'ConfiguredByDefault' }
54 | NY = @{ PullServerPath = 'ConfiguredBySite' }
55 | }
56 |
57 | $Node = @{
58 | Name = 'TestBox'
59 | Location = 'OR'
60 | }
61 |
62 | $result = Resolve-DscConfigurationProperty -Node $Node -PropertyName 'PullServerPath'
63 | it "should return the site's default value" {
64 | $result | should be 'ConfiguredByDefault'
65 | }
66 | }
67 | }
68 |
69 | describe 'how Resolve-DscConfigurationProperty (services) responds' {
70 | $ConfigurationData = @{AllNodes = @(); SiteData = @{} ; Services = @{}; Applications = @{}}
71 |
72 | $ConfigurationData.Services = @{
73 | MyTestService = @{
74 | DataSource = 'MyDefaultValue'
75 | }
76 | }
77 |
78 | context 'when a default value is supplied for a service and node has a property override' {
79 |
80 | $Node = @{
81 | Name = 'TestBox'
82 | Location = 'NY'
83 | Services = @{
84 | MyTestService = @{
85 | DataSource = 'MyCustomValue'
86 | }
87 | }
88 | }
89 |
90 | $result = Resolve-DscConfigurationProperty -Node $Node -ServiceName MyTestService -PropertyName DataSource
91 |
92 | it 'should return the override from the node' {
93 | $result | should be 'MyCustomValue'
94 |
95 | }
96 | }
97 |
98 | context 'when a site level override is present' {
99 | $ConfigurationData.SiteData = @{
100 | NY = @{
101 | Services = @{
102 | MyTestService = @{
103 | DataSource = 'MySiteValue'
104 | }
105 | }
106 | }
107 | }
108 | $Node = @{
109 | Name = 'TestBox'
110 | Location = 'NY'
111 | }
112 |
113 | $result = Resolve-DscConfigurationProperty -Node $Node -ServiceName MyTestService -PropertyName DataSource
114 |
115 | it 'should return the override from the site' {
116 | $result | should be 'MySiteValue'
117 | }
118 | }
119 |
120 | context 'when a global site level override is present' {
121 | $ConfigurationData.SiteData = @{
122 | All = @{
123 | Services = @{
124 | MyTestService = @{
125 | DataSource = 'FromAllSite'
126 | }
127 | }
128 | }
129 | NY = @{
130 | Services = @{
131 | MyTestService = @{}
132 | }
133 | }
134 | }
135 | $ConfigurationData.Services = @{
136 | MyTestService = @{}
137 | }
138 | $Node = @{
139 | Name = 'TestBox'
140 | Location = 'NY'
141 | }
142 |
143 | $result = Resolve-DscConfigurationProperty -Node $Node -ServiceName MyTestService -PropertyName DataSource
144 |
145 | it 'should return the override from the site' {
146 | $result | should be 'FromAllSite'
147 |
148 | }
149 | }
150 |
151 | context 'when no node or site level override is present' {
152 | $ConfigurationData.Services = @{
153 | MyTestService = @{
154 | DataSource = 'MyDefaultValue'
155 | }
156 | }
157 | $ConfigurationData.SiteData = @{
158 | All = @{ DataSource = 'NotMyDefaultValue'}
159 | NY = @{
160 | Services = @{
161 | MyTestService = @{}
162 | }
163 | }
164 | }
165 | $Node = @{
166 | Name = 'TestBox'
167 | Location = 'NY'
168 | }
169 |
170 | $result = Resolve-DscConfigurationProperty -Node $Node -ServiceName MyTestService -PropertyName DataSource
171 |
172 | it 'should return the default value from the service' {
173 | $result | should be 'MyDefaultValue'
174 |
175 | }
176 | }
177 |
178 | context 'when no service default is specified' {
179 |
180 | $Node = @{
181 | Name = 'TestBox'
182 | Location = 'NY'
183 | MissingFromFirstServiceConfig = 'FromNodeWithoutService'
184 | }
185 |
186 | $result = Resolve-DscConfigurationProperty -Node $Node -ServiceName MyTestService -PropertyName MissingFromFirstServiceConfig
187 | it 'should fall back to checking for the parameter without the service name' {
188 | $result | should be 'FromNodeWithoutService'
189 | }
190 | }
191 |
192 | context 'when two services are specified default is specified' {
193 | $ConfigurationData.Services = @{
194 | MyTestService = @{}
195 | MySecondTestService = @{
196 | MissingFromFirstServiceConfig = 'FromSecondServiceConfig'
197 | }
198 | }
199 | $Node = @{
200 | Name = 'TestBox'
201 | Location = 'NY'
202 | }
203 |
204 | $result = Resolve-DscConfigurationProperty -Node $Node -ServiceName MyTestService, MySecondTestService -PropertyName MissingFromFirstServiceConfig
205 |
206 | it 'should retrieve the parameter from the second service before falling back to the node' {
207 | $result | should be 'FromSecondServiceConfig'
208 | }
209 | }
210 | }
211 |
212 | describe 'how Resolve-DscConfigurationProperty (applications) responds' {
213 | $ConfigurationData = @{AllNodes = @(); SiteData = @{} ; Services = @{}; Applications = @{}}
214 | $ConfigurationData.Applications = @{
215 | Git = @{
216 | LocalPath = 'c:\installs\Git\'
217 | InstallerName = 'setup.exe'
218 | SourcePath = 'c:\global\git\setup.exe'
219 | }
220 | Mercurial = @{
221 | LocalPath = 'c:\installs\Mercurial\'
222 | SourcePath = 'c:\global\Mercurial\setup.exe'
223 | InstallerName = 'Setup.exe'
224 | }
225 | WinMerge = @{
226 | LocalPath = 'c:\installs\winmerge\'
227 | InstallerName = 'setup.exe'
228 | SourcePath = 'c:\global\winmerge\setup.exe'
229 | }
230 | }
231 | $ConfigurationData.SiteData.NY = @{
232 | Applications = @{
233 | Mercurial = @{
234 | LocalPath = 'c:\installs\Mercurial\'
235 | SourcePath = 'c:\site\Mercurial\setup.exe'
236 | InstallerName = 'Setup.exe'
237 | }
238 | WinMerge = @{
239 | LocalPath = 'c:\installs\winmerge\'
240 | InstallerName = 'setup.exe'
241 | SourcePath = 'c:\site\winmerge\setup.exe'
242 | }
243 | }
244 | }
245 | $Node = @{
246 | Name = 'TestBox'
247 | Location = 'NY'
248 | Applications = @{
249 | WinMerge = @{
250 | LocalPath = 'c:\installs\winmerge\'
251 | InstallerName = 'setup.exe'
252 | SourcePath = 'c:\node\winmerge\setup.exe'
253 | }
254 | }
255 | }
256 | context 'When there is a base setting for an application' {
257 |
258 | $result = Resolve-DscConfigurationProperty -Node $Node -Application 'Git'
259 |
260 | it 'should return the application level configuration' {
261 | $result.SourcePath | should be 'c:\global\git\setup.exe'
262 | }
263 | }
264 |
265 | context 'When there is a site level override for the base setting for an application' {
266 |
267 | $result = Resolve-DscConfigurationProperty -Node $Node -Application 'Mercurial'
268 |
269 | it 'should return the site application level configuration' {
270 | $result.SourcePath | should be 'c:\site\Mercurial\setup.exe'
271 | }
272 | }
273 |
274 | context 'When there is a node level override for the base setting for an application' {
275 |
276 | $result = Resolve-DscConfigurationProperty -Node $Node -Application 'WinMerge'
277 |
278 | it 'should return the node application level configuration' {
279 | $result.SourcePath | should be 'c:\node\winmerge\setup.exe'
280 | }
281 | }
282 | }
283 | #<#
284 | describe 'how Resolve-DscConfigurationProperty (applications/services) responds' {
285 | $ConfigurationData = @{AllNodes = @(); SiteData = @{} ; Services = @{}; Applications = @{}}
286 | $ConfigurationData.Applications.Sublime = @{
287 | LocalPath = 'c:\installs\Sublime\'
288 | InstallerName = 'setup.exe'
289 | SourcePath = 'c:\default\Sublime\setup.exe'
290 | }
291 | $ConfigurationData.Services = @{
292 | BuildAgent = @{
293 | Applications = @{
294 | Git = @{
295 | LocalPath = 'c:\installs\Git\'
296 | InstallerName = 'setup.exe'
297 | SourcePath = 'c:\global\git\setup.exe'
298 | }
299 | Mercurial = @{
300 | LocalPath = 'c:\installs\Mercurial\'
301 | SourcePath = 'c:\global\Mercurial\setup.exe'
302 | InstallerName = 'Setup.exe'
303 | }
304 | WinMerge = @{
305 | LocalPath = 'c:\installs\winmerge\'
306 | InstallerName = 'setup.exe'
307 | SourcePath = 'c:\global\winmerge\setup.exe'
308 | }
309 | }
310 | }
311 | }
312 | $ConfigurationData.SiteData.NY = @{
313 | Services = @{
314 | BuildAgent = @{
315 | Applications = @{
316 | Mercurial = @{
317 | LocalPath = 'c:\installs\Mercurial\'
318 | SourcePath = 'c:\site\Mercurial\setup.exe'
319 | InstallerName = 'Setup.exe'
320 | }
321 | WinMerge = @{
322 | LocalPath = 'c:\installs\winmerge\'
323 | InstallerName = 'setup.exe'
324 | SourcePath = 'c:\site\winmerge\setup.exe'
325 | }
326 | }
327 | }
328 | }
329 | }
330 | $Node = @{
331 | Name = 'TestBox'
332 | Location = 'NY'
333 | Services = @{
334 | BuildAgent = @{
335 | Applications = @{
336 | WinMerge = @{
337 | LocalPath = 'c:\installs\winmerge\'
338 | InstallerName = 'setup.exe'
339 | SourcePath = 'c:\node\winmerge\setup.exe'
340 | }
341 | }
342 | }
343 | }
344 | }
345 | context 'When there is a base setting for an application' {
346 |
347 | $result = Resolve-DscConfigurationProperty -Node $Node -Application 'Git' -ServiceName 'BuildAgent'
348 |
349 | it 'should return the application level configuration' {
350 | $result.SourcePath | should be 'c:\global\git\setup.exe'
351 | }
352 | }
353 |
354 | context 'When there is a site level override for the base setting for an application' {
355 |
356 | $result = Resolve-DscConfigurationProperty -Node $Node -Application 'Mercurial' -ServiceName 'BuildAgent'
357 |
358 | it 'should return the site application level configuration' {
359 | $result.SourcePath | should be 'c:\site\Mercurial\setup.exe'
360 | }
361 | }
362 |
363 | context 'When there is a node level override for the base setting for an application' {
364 |
365 | $result = Resolve-DscConfigurationProperty -Node $Node -Application 'WinMerge' -ServiceName 'BuildAgent'
366 |
367 | it 'should return the node application level configuration' {
368 | $result.SourcePath | should be 'c:\node\winmerge\setup.exe'
369 | }
370 | }
371 |
372 | context 'When there is no service level setting for an application, but there is a default config' {
373 |
374 | $result = Resolve-DscConfigurationProperty -Node $Node -Application 'Sublime' -ServiceName 'BuildAgent'
375 |
376 | it 'should return the node application level configuration' {
377 | $result.SourcePath | should be 'c:\default\Sublime\setup.exe'
378 | }
379 | }
380 | }
381 | #>
382 |
--------------------------------------------------------------------------------
/Tooling/dscbuild/Resolve-ConfigurationProperty.ps1:
--------------------------------------------------------------------------------
1 | function Resolve-DscConfigurationProperty {
2 | <#
3 | .Synopsis
4 | Searches DSC metadata
5 | .Description
6 | Longer description of the command
7 | .Example
8 |
9 | #>
10 | [cmdletbinding()]
11 | param (
12 | #The current node being evaluated for the specified property or application.
13 | [parameter()]
14 | [System.Collections.Hashtable]
15 | $Node,
16 |
17 | #The service(s) that will be checked for the specified property or application.
18 | [parameter()]
19 | [ValidateNotNullOrEmpty()]
20 | [string[]]
21 | $ServiceName,
22 |
23 | #The application metadata that will be checked for.
24 | [parameter()]
25 | [ValidateNotNullOrEmpty()]
26 | [string]
27 | $Application,
28 |
29 | #The property that will be checked for.
30 | [parameter()]
31 | [ValidateNotNullOrEmpty()]
32 | [string]
33 | $PropertyName,
34 |
35 | #By default, all results must return just one entry. If multiple results are allowed, this flag must be enabled.
36 | [parameter()]
37 | [switch]
38 | $AllowMultipleResults,
39 |
40 | #If you want to override the default behavior of checking up-scope for configuration data, it can be supplied here.
41 | [parameter()]
42 | [System.Collections.Hashtable]
43 | $ConfigurationData
44 | )
45 |
46 | Write-Verbose ""
47 | if (-not $PSBoundParameters.ContainsKey('ConfigurationData')) {
48 | Write-Verbose ""
49 | Write-Verbose "Resolving ConfigurationData"
50 | $ScopeToCheck = 1
51 | do {
52 | try {
53 | $ConfigurationData = Get-Variable -scope $ScopeToCheck -Name 'ConfigurationData' -ValueOnly -ErrorAction Stop
54 | }
55 | catch {
56 | Write-Verbose "`t`tNothing in scope $ScopeToCheck for ConfigurationData"
57 | }
58 | $ScopeToCheck++
59 | } until ($ScopeToCheck -gt 5 -or ($ConfigurationData -is [hashtable] -and $ConfigurationData.Keys.Count -gt 0))
60 |
61 | if ($ConfigurationData -isnot [hashtable] -or $ConfigurationData.Keys.Count -eq 0) {
62 | throw 'Failed to resolve ConfigurationData. Please confirm that $ConfigurationData is property set in a scope above this Resolve-DscConfigurationProperty or passed to Resolve-DscConfigurationProperty via the ConfigurationData parameter.'
63 | }
64 | else {
65 | $PSBoundParameters.Add('ConfigurationData', $ConfigurationData)
66 | }
67 | }
68 | Write-Verbose "Starting to evaluate $($Node.Name) for PropertyName: $PropertyName Application: $Application From Services: $ServiceName"
69 |
70 | $Value = @()
71 | if (($Node -ne $null)) {
72 | $Value = Assert-NodeOverride @PSBoundParameters
73 | Write-Verbose "Value after checking the node is $Value"
74 | }
75 | if (-not $PSBoundParameters.ContainsKey('Application')) {
76 | $Value = ($Value | where-object {-not [string]::IsNullOrEmpty($_)})
77 | }
78 |
79 | if ($Value.count -eq 0) {
80 | $Value += Assert-SiteOverride @PSBoundParameters
81 | Write-Verbose "Value after checking the site is $Value"
82 | }
83 | if (-not $PSBoundParameters.ContainsKey('Application')) {
84 | $Value = ($Value | where-object {-not [string]::IsNullOrEmpty($_)})
85 | }
86 |
87 | if ($Value.count -eq 0) {
88 | $Value += Assert-GlobalSetting @PSBoundParameters
89 | Write-Verbose "Value after checking the global is $Value"
90 | }
91 | if (-not $PSBoundParameters.ContainsKey('Application')) {
92 | $Value = ($Value | where-object {-not [string]::IsNullOrEmpty($_)})
93 | }
94 |
95 | if (-not $PSBoundParameters.ContainsKey('Application')) {
96 | if (($Value.count -eq 0) -and ($ServiceName.Count -gt 0))
97 | {
98 | $PSBoundParameters.Remove('ServiceName') | out-null
99 | $Value = Resolve-DscConfigurationProperty @PSBoundParameters
100 | }
101 |
102 | if ($Value.count -eq 0)
103 | {
104 | throw "Failed to resolve $PropertyName for $($Node.Name). Please update your node, service, site, or all sites with a default value."
105 | }
106 |
107 | if ($AllowMultipleResults) {
108 | return $Value
109 | }
110 | elseif ((-not $AllowMultipleResults) -and ($Value.count -gt 1)) {
111 | throw "More than one result was returned for $PropertyName for $($Node.Name). Verify that your property configurations are correct. If multiples are to be allowed, use -AllowMultipleResults."
112 | }
113 | else {
114 | return $Value
115 | }
116 | }
117 | else {
118 | if ($value -eq $null) {
119 | $PSBoundParameters.Remove('ServiceName') | out-null
120 | $Value = Resolve-DscConfigurationProperty @PSBoundParameters
121 | }
122 | if ($value -is [System.Collections.Hashtable]) {
123 | return $value
124 | }
125 | else {
126 | throw "Failed to resolve $Application for $($Node.Name). Please update your node, service, site, or all sites with a default value."
127 | }
128 | }
129 | }
130 |
131 | Set-Alias -Name 'Resolve-ConfigurationProperty' -Value 'Resolve-DscConfigurationProperty'
132 |
133 | function Test-HashtableKey {
134 | [cmdletbinding()]
135 | param (
136 | [parameter(position=0)]
137 | [System.Collections.Hashtable]
138 | $Hashtable,
139 | [parameter(position=1)]
140 | [string]
141 | $key,
142 | $NumberOfTabs
143 | )
144 | if ($Hashtable -ne $null) {
145 | #Write-Verbose (("`t" * $NumberOfTabs) + "$((Get-PSCallStack)[1].Command)")
146 | #Write-Verbose (("`t" * $NumberOfTabs) + "$((Get-PSCallStack)[1].ScriptLineNumber)")
147 | $ofs = ', '
148 | Write-Verbose (("`t" * $NumberOfTabs) + "Testing for $key from ( $($Hashtable.keys) )")
149 | $Found = $Hashtable.ContainsKey($key)
150 | Write-Verbose (("`t" * $NumberOfTabs) + "$key was found: $Found")
151 | return $found
152 | }
153 | return $false
154 | }
155 |
156 | function Test-ApplicationKey {
157 | param (
158 | [System.Collections.Hashtable]
159 | $Hashtable,
160 | [string]
161 | $Application
162 | )
163 | $NumberOfTabs = 3
164 | if ($Hashtable -ne $null) {
165 | if (-not [string]::IsNullOrEmpty($Application)) {
166 | if (Test-HashtableKey $Hashtable 'Applications' -NumberOfTabs $NumberOfTabs) {
167 | $NumberOfTabs++
168 | if (Test-HashtableKey $Hashtable['Applications'] $Application -NumberOfTabs $NumberOfTabs) {
169 | Write-Verbose ("`t" * $NumberOfTabs + "Found $Application")
170 | return $true
171 | }
172 | }
173 | }
174 | }
175 | return $false
176 | }
177 |
178 | function Test-ServiceKey {
179 | param (
180 | [System.Collections.Hashtable]
181 | $Hashtable,
182 | [string]
183 | $Service,
184 | [string]
185 | $PropertyName
186 | )
187 | $NumberOfTabs = 3
188 | if ($Hashtable -ne $null) {
189 | if (-not [string]::IsNullOrEmpty($Service)) {
190 | if (Test-HashtableKey $Hashtable 'Services' -NumberOfTabs $NumberOfTabs) {
191 | $NumberOfTabs++
192 | if (Test-HashtableKey $Hashtable['Services'] $Service -NumberOfTabs $NumberOfTabs) {
193 | $NumberOfTabs++
194 | if (Test-HashtableKey $Hashtable['Services'][$Service] $PropertyName -NumberOfTabs $NumberOfTabs) {
195 | return $true
196 | }
197 | }
198 | }
199 | }
200 | }
201 | return $false
202 | }
203 |
204 |
205 | function Resolve-HashtableProperty {
206 | param (
207 | [System.Collections.Hashtable]
208 | $Hashtable,
209 | [string]
210 | $PropertyName
211 | )
212 |
213 | if ($Hashtable -ne $null) {
214 | $PropertyValue = $Hashtable[$PropertyName]
215 | if ($PropertyValue -ne $null) {
216 | Write-Verbose "`t`t`t$Found PropertyName $PropertyName with value $PropertyValue"
217 | return $PropertyValue
218 | }
219 | }
220 | }
221 |
222 | function Assert-NodeOverride {
223 | [cmdletbinding()]
224 | param (
225 | [System.Collections.Hashtable]
226 | $Node,
227 | [string[]]
228 | $ServiceName,
229 | [string]
230 | $Application,
231 | [string]
232 | $PropertyName,
233 | [switch]
234 | $AllowMultipleResults,
235 | [System.Collections.Hashtable]
236 | $ConfigurationData
237 | )
238 | $Value = @()
239 | Write-Verbose "`tChecking Node: $($Node.Name)"
240 | if (( $ServiceName.count -eq 0 ) -and
241 | ( -not [string]::IsNullOrEmpty($Application) ) -and
242 | ( Test-ApplicationKey -Hashtable $Node -Application $Application )) {
243 |
244 | $Value += Resolve-HashtableProperty $Node['Applications'] $Application
245 |
246 | }
247 | elseif (($ServiceName.count -eq 0) -and
248 | ( Test-HashtableKey $Node $PropertyName -NumberOfTabs 2)) {
249 | $Value += Resolve-HashtableProperty $Node $PropertyName
250 | }
251 | else {
252 | foreach ($Service in $ServiceName) {
253 | if ( Test-HashtableKey $Node 'Services' -NumberOfTabs 2) {
254 | if (Test-HashtableKey $Node['Services'] $Service -NumberOfTabs 3) {
255 |
256 | if ( Test-ServiceKey -Hashtable $Node -Service $Service -PropertyName $PropertyName ) {
257 | $Value += Resolve-HashtableProperty $Node['Services'][$Service] $PropertyName
258 | }
259 | elseif (Test-ApplicationKey -Hashtable $Node['Services'][$Service] -Application $Application) {
260 | $Value += Resolve-HashtableProperty $Node['Services'][$Service]['Applications'] $Application
261 | }
262 | }
263 | }
264 | }
265 | }
266 |
267 | if ($value.count -gt 0) {
268 | Write-Verbose "`t`tFound Node Value: $Value"
269 | }
270 | Write-Verbose "`tFinished checking Node $($Node.Name)"
271 | return $Value
272 | }
273 |
274 | function Assert-SiteOverride {
275 | [cmdletbinding()]
276 | param (
277 | [System.Collections.Hashtable]
278 | $Node,
279 | [string[]]
280 | $ServiceName,
281 | [string]
282 | $Application,
283 | [string]
284 | $PropertyName,
285 | [switch]
286 | $AllowMultipleResults,
287 | [System.Collections.Hashtable]
288 | $ConfigurationData
289 | )
290 | $Value = @()
291 | $Site = $Node.Location
292 | Write-Verbose "`tStarting to check Site $Site"
293 | if ((Test-HashtableKey $ConfigurationData 'SiteData' -NumberOfTabs 2) -and
294 | (Test-HashtableKey $ConfigurationData.SiteData $Site -NumberOfTabs 2)
295 | ) {
296 | if ( ($ServiceName.count -eq 0) -and
297 | (-not [string]::IsNullOrEmpty($Application)) -and
298 | (Test-ApplicationKey -Hashtable $ConfigurationData.SiteData[$Site] -Application $Application )) {
299 | $Value += Resolve-HashtableProperty $ConfigurationData.SiteData[$Site]['Applications'] $Application
300 |
301 | }
302 | elseif ( ($ServiceName.count -eq 0) -and
303 | (Test-HashtableKey $ConfigurationData.SiteData[$Site] $PropertyName ) ){
304 | $Value += Resolve-HashtableProperty $ConfigurationData.SiteData[$Site] $PropertyName
305 | }
306 | else {
307 | foreach ($Service in $ServiceName) {
308 | if (Test-HashtableKey $ConfigurationData.SiteData[$Site] 'Services' -NumberOfTabs 2) {
309 | if (Test-HashtableKey $ConfigurationData.SiteData[$Site]['Services'] $Service -NumberOfTabs 3) {
310 | if (Test-ApplicationKey -Hashtable $ConfigurationData.SiteData[$Site]['Services'][$Service] -Application $Application) {
311 | $Value += Resolve-HashtableProperty $ConfigurationData.SiteData[$Site]['Services'][$Service]['Applications'] $Application
312 | }
313 | elseif ( Test-ServiceKey -Hashtable $ConfigurationData.SiteData[$Site] -Service $Service -PropertyName $PropertyName ) {
314 | $Value += Resolve-HashtableProperty $ConfigurationData.SiteData[$Site]['Services'][$Service] $PropertyName
315 | }
316 | }
317 | }
318 | }
319 | }
320 | }
321 | Write-Verbose "`tFinished checking Site $Site"
322 | return $Value
323 | }
324 |
325 | function Assert-GlobalSetting {
326 | [cmdletbinding()]
327 | param (
328 | [System.Collections.Hashtable]
329 | $Node,
330 | [string[]]
331 | $ServiceName,
332 | [string]
333 | $Application,
334 | [string]
335 | $PropertyName,
336 | [switch]
337 | $AllowMultipleResults,
338 | [System.Collections.Hashtable]
339 | $ConfigurationData
340 | )
341 | $Value = @()
342 | Write-Verbose "Bound parameters include:"
343 | foreach ($key in $PSBoundParameters.keys) {
344 | Write-Verbose "`t$key is $($PSBoundParameters[$key])"
345 | }
346 | Write-Verbose "`tStarting to check global settings"
347 | if ($ServiceName.count -eq 0) {
348 | if (-not [string]::IsNullOrEmpty($Application)) {
349 | if (Test-ApplicationKey -Hashtable $ConfigurationData -Application $Application) {
350 | $Value += Resolve-HashtableProperty $ConfigurationData['Applications'] $Application
351 | }
352 | }
353 | else {
354 | Write-Verbose "`t`tStarting to check Site: All"
355 | if (Test-HashtableKey $ConfigurationData 'SiteData' -NumberOfTabs 3) {
356 | if (Test-HashtableKey $ConfigurationData.SiteData 'All' -NumberOfTabs 4) {
357 | if (Test-HashtableKey $ConfigurationData.SiteData.All $PropertyName -NumberOfTabs 5) {
358 | $Value += Resolve-HashtableProperty $ConfigurationData.SiteData.All $PropertyName
359 | }
360 | }
361 | }
362 | }
363 | Write-Verbose "`t`tFinished checking Site: All"
364 | }
365 | else {
366 | foreach ($Service in $ServiceName) {
367 | Write-Verbose "`t`tStarting to check Service:$Service"
368 | $Found = $false
369 | if (Test-HashtableKey $ConfigurationData 'SiteData' -NumberOfTabs 3) {
370 | if (Test-HashtableKey $ConfigurationData.SiteData 'All' -NumberOfTabs 4) {
371 | if (Test-HashtableKey $ConfigurationData.SiteData.All 'Services' -NumberOfTabs 5) {
372 | if (Test-HashtableKey $ConfigurationData.SiteData.All.Services $Service -NumberOfTabs 6) {
373 | if (Test-ServiceKey -Hashtable $ConfigurationData.SiteData.All -Service $Service -PropertyName $PropertyName) {
374 | $Found = $true
375 | $Value += Resolve-HashtableProperty $ConfigurationData.SiteData.All.Services[$Service] $PropertyName
376 | }
377 | elseif (Test-ApplicationKey -Hashtable $ConfigurationData.SiteData.All.Services[$Service] -Application $Application) {
378 | $Found = $true
379 | $Value += Resolve-HashtableProperty $ConfigurationData.SiteData.All.Services[$Service]['Applications'] $Application
380 | }
381 | }
382 | }
383 | }
384 | }
385 | if ((-not $found) -and (Test-HashtableKey $ConfigurationData 'Services' -NumberOfTabs 3)) {
386 | if (Test-HashtableKey $ConfigurationData.Services $Service -NumberOfTabs 4) {
387 | if (Test-ServiceKey -Hashtable $ConfigurationData -Service $Service -PropertyName $PropertyName) {
388 | $Value += Resolve-HashtableProperty $ConfigurationData.Services[$Service] $PropertyName
389 | }
390 | elseif (Test-ApplicationKey -Hashtable $ConfigurationData.Services[$Service] -Application $Application ) {
391 | $Value += Resolve-HashtableProperty $ConfigurationData.Services[$Service]['Applications'] $Application
392 | }
393 | }
394 | }
395 |
396 | Write-Verbose "`t`tFinished checking Service:$Service"
397 | }
398 | }
399 | Write-Verbose "`tFound Global Value: $Value"
400 | return $Value
401 | }
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
--------------------------------------------------------------------------------
/Tooling/cDscDiagnostics/cDscDiagnostics.psm1.Tests.ps1:
--------------------------------------------------------------------------------
1 | Remove-Module [c]DscDiagnostics
2 | Import-Module $PSScriptRoot\cDscDiagnostics.psm1
3 |
4 | Describe "Trace-cDscOperation" {
5 | Context "does it call its internal functions" {
6 | Mock -ModuleName cDscDiagnostics Add-ClassTypes {}
7 | Mock -ModuleName cDscDiagnostics Trace-DscOperationInternal {}
8 | Mock -ModuleName cDscDiagnostics Log {}
9 |
10 | $result = Trace-cDscOperation -ComputerName $env:ComputerName;
11 |
12 | It "should call Add-ClassType" {
13 | Assert-MockCalled Add-ClassTypes -ModuleName cDscDiagnostics -Times 1
14 | }
15 |
16 | It "should call Trace-DscOperationInternal" {
17 | Assert-MockCalled Trace-DscOperationInternal -ModuleName cDscDiagnostics -Times 1
18 | }
19 |
20 | It "should call Log" {
21 | Assert-MockCalled Log -ModuleName cDscDiagnostics -Times 1
22 | }
23 | }
24 | }
25 |
26 | Describe "Add-ClassTypes" {
27 | Context "when its called" {
28 | Mock -ModuleName cDscDiagnostics Update-FormatData {}
29 | Mock -ModuleName cDscDiagnostics Trace-DscOperationInternal {}
30 | Mock -ModuleName cDscDiagnostics Log {}
31 |
32 | $result = Trace-cDscOperation -ComputerName $env:ComputerName;
33 |
34 | It "should have loaded it's event types" {
35 | { [Microsoft.PowerShell.cDscDiagnostics.EventType]::ANALYTIC } | Should Not Throw
36 | }
37 |
38 | It "should have loaded it's group events" {
39 | { [Microsoft.PowerShell.cDscDiagnostics.GroupedEvents] } | Should Not Throw
40 | }
41 | }
42 | }
43 |
44 | InModuleScope cDscDiagnostics {
45 | Describe 'Log' {
46 | It 'Should write verbosely' {
47 | $text = "Verbose Text"
48 | $verboseLog = Log $text -Verbose 4>&1
49 | $verboseLog | Should Be $text
50 | }
51 |
52 | It 'should write errors' {
53 | $text = "Error Text"
54 | $errorLog = Log $text -Error 2>&1
55 | $errorLog | Should Be $text
56 | }
57 |
58 | BeforeEach {
59 | $vPreference = $VerbosePreference;
60 | $ePreference = $ErrorActionPreference;
61 |
62 | $VerbosePreference = "Continue";
63 | $ErrorActionPreference = "Continue";
64 | }
65 |
66 | AfterEach {
67 | $VerbosePreference = $vPreference;
68 | $ErrorActionPreference = $ePreference;
69 | }
70 | }
71 |
72 | Describe 'Trace-DscOperationInternal' {
73 | Context 'SequenceID is passed' {
74 | Mock Log { }
75 | $result = Trace-DscOperationInternal -SequenceID 0;
76 |
77 | It 'should return null if SequenceID is less then 1' {
78 | $result | Should Be $null;
79 | }
80 |
81 | $date = Get-Date;
82 | $message = "Some Message";
83 | # Choosing Application because we need /something/ here and we can't assume that the machine has run a DSC command.
84 | $event = Get-WinEvent -LogName Application -MaxEvents 1
85 |
86 | $traceOutput = New-Object PSObject -Property @{
87 | Type = "Operational";
88 | TimeCreated = $date;
89 | Message = $message;
90 | ComputerName = $env:ComputerName;
91 | SequenceID = 2;
92 | Event = $event;
93 | }
94 |
95 | Mock Get-SingleDscOperation { return @{
96 | AllEvents = $traceOutput;
97 | ErrorEvents = "SomeEvent";
98 | JobId = "54137c5a-f607-48a5-9311-d6e102c15f83";
99 | }
100 | }
101 |
102 | Mock Test-DscEventLogStatus { }
103 | Mock Get-DscErrorMessage { return "Error Text" }
104 |
105 | $result = Trace-DscOperationInternal -SequenceID 2;
106 |
107 | It 'should do call Test-DscEventLogStatus' {
108 | Assert-MockCalled Test-DscEventLogStatus -Times 2
109 | }
110 |
111 | It 'should call Get-SingleDscOperation' {
112 | Assert-MockCalled Get-SingleDscOperation -Times 1
113 | }
114 |
115 | It 'should call Get-DscErrorMessage' {
116 | Assert-MockCalled Get-DscErrorMessage -Times 1
117 | }
118 |
119 | It 'should return the correct event type' {
120 | $result.EventType | Should Be "Operational";
121 | }
122 |
123 | It 'should return the correct time' {
124 | $result.TimeCreated | Should Be $date;
125 | }
126 |
127 | It 'should return the correct message' {
128 | $result.Message | Should Be $message;
129 | }
130 |
131 | It 'should return the correct machine' {
132 | $result.ComputerName | Should Be $env:ComputerName;
133 | }
134 |
135 | It 'should return the correct SequenceID' {
136 | $result.SequenceID | Should Be 2;
137 | }
138 |
139 | It 'should return the correct Event' {
140 | $result.Event | Should Be $event;
141 | }
142 | }
143 |
144 | Context 'JobId Passed' {
145 | $date = Get-Date;
146 | $message = "Some Message";
147 | # Choosing Application because we need /something/ here and we can't assume that the machine has run a DSC command.
148 | $event = Get-WinEvent -LogName Application -MaxEvents 1
149 |
150 | $traceOutput = New-Object PSObject -Property @{
151 | Type = "Operational";
152 | TimeCreated = $date;
153 | Message = $message;
154 | ComputerName = $env:ComputerName;
155 | SequenceID = 1;
156 | Event = $event;
157 | }
158 |
159 | Mock Get-SingleDscOperation { return @{
160 | AllEvents = $traceOutput;
161 | ErrorEvents = "SomeEvent";
162 | JobId = "54137c5a-f607-48a5-9311-d6e102c15f83";
163 | }
164 | }
165 |
166 | Mock Test-DscEventLogStatus { }
167 | Mock Get-DscErrorMessage { return "Error Text" }
168 |
169 | $result = Trace-DscOperationInternal -SequenceID 1 -JobId "54137c5a-f607-48a5-9311-d6e102c15f83";
170 |
171 | It 'should return the Job Id' {
172 | $result.JobId | Should Be "54137c5a-f607-48a5-9311-d6e102c15f83"
173 | }
174 | }
175 |
176 | Context 'Get-SingleDscOperation returns null' {
177 | Mock Test-DscEventLogStatus { }
178 | Mock Get-SingleDscOperation { }
179 |
180 | $result = Trace-DscOperationInternal -SequenceID 1 -JobId "54137c5a-f607-48a5-9311-d6e102c15f83";
181 |
182 | It 'should return null' {
183 | $result | Should Be $null;
184 | }
185 | }
186 |
187 | # This way we don't cache anything while testing.
188 | AfterEach {
189 | Clear-DscDiagnosticsCache
190 | }
191 | }
192 |
193 | Describe 'Get-DscLatestJobId' {
194 | Context 'It has a Job to Return' {
195 | Mock Get-WinEvent {
196 | $value = @{"Value" = "{3BBB79B7-BD46-424C-9718-983C8C76D37E}"}
197 | $returnObject = @(@{Properties = @($value)}, [Environment]::NewLine)
198 | return @(@{Properties = @($value)}, [Environment]::NewLine)
199 | }
200 |
201 | $result = Get-DscLatestJobId;
202 |
203 | It 'should return the GUID' {
204 | $result | Should Be "{3BBB79B7-BD46-424C-9718-983C8C76D37E}"
205 | }
206 | }
207 |
208 | Context 'When it does not have a Job' {
209 | Mock Get-WinEvent {
210 | return $null
211 | }
212 |
213 | $result = Get-DscLatestJobId;
214 |
215 | It 'should return "NOJOBID"' {
216 | $result | Should Be "NOJOBID"
217 | }
218 | }
219 |
220 | AfterEach {
221 | Clear-DscDiagnosticsCache
222 | }
223 | }
224 |
225 | Describe 'Get-AllDscEvents' {
226 | Mock Get-WinEvent {
227 | Microsoft.PowerShell.Diagnostics\Get-WinEvent -LogName Application -MaxEvents 1
228 | }
229 |
230 | $result = Get-AllDscEvents;
231 |
232 | It 'should do call Get-WinEvent for each log passed' {
233 | Assert-MockCalled Get-WinEvent -Times 3
234 | $result.Count | Should Be 3
235 | }
236 |
237 | It 'should return something' {
238 | $result | Should Not Be $null;
239 | }
240 | }
241 |
242 | Describe 'Get-AllGroupedDscEvents' {
243 | Context 'Get-AllDscEvents returns null' {
244 | Mock Get-AllDscEvents {}
245 | Mock Get-DscLatestJobId {return "NOJOBID"}
246 | Mock Log {}
247 | $result = Get-AllGroupedDscEvents;
248 |
249 | It "should call Log" {
250 | Assert-MockCalled Log -Times 1
251 | }
252 |
253 | It 'should call Get-DscLatestJobId' {
254 | Assert-MockCalled Get-DscLatestJobId -Times 1
255 | }
256 |
257 | It 'should return null' {
258 | $result | Should Be $null
259 | }
260 | }
261 |
262 | Context 'Get-AllDscEvents returns events' {
263 | $value = @{"Value" = "{3BBB79B7-BD46-424C-9718-983C8C76D37E}"}
264 | Mock Get-AllDscEvents {return @{Properties = @($value)}}
265 | Mock Log {}
266 | Mock Get-DscLatestJobId { return 'MOCKEDJOBID' }
267 | $result = Get-AllGroupedDscEvents
268 |
269 | It 'should return a specific event' {
270 | $result.Name | Should Be "{3BBB79B7-BD46-424C-9718-983C8C76D37E}"
271 | }
272 | }
273 |
274 | Context 'Cached Events' {
275 | Mock Get-DscLatestJobId { return 'MOCKEDJOBID' }
276 | Mock Get-AllDscEvents {}
277 |
278 | $result = Get-AllGroupedDscEvents;
279 |
280 | It 'should return a specific event' {
281 | Assert-MockCalled Get-AllDscEvents -Scope It -Times 0
282 | $result.Name | Should Be "{3BBB79B7-BD46-424C-9718-983C8C76D37E}"
283 | }
284 | }
285 | }
286 |
287 | Describe 'Get-SingleDscOperation' {
288 | Context 'Get-AllGroupedDscEvents is empty' {
289 | Mock Get-AllGroupedDscEvents {}
290 |
291 | $result = Get-SingleDscOperation;
292 |
293 | It 'should return empty' {
294 | $result | Should BeNullorEmpty
295 | }
296 | }
297 |
298 | Context 'JobId is Passed but its not found' {
299 | Mock Log {}
300 | Mock Get-AllGroupedDscEvents {@{Name = "NOJOBID"}}
301 | $result = Get-SingleDscOperation -JobId 3BBB79B7-BD46-424C-9718-983C8C76D37E
302 |
303 | It 'should return empty' {
304 | $result | Should BeNullorEmpty
305 | }
306 | }
307 |
308 | Context 'JobId is passed and found' {
309 | Mock Log {}
310 | Mock Get-AllGroupedDscEvents {
311 | return New-Object PSObject -Property @{
312 | Name = New-Object PSObject -Property @{
313 | Guid = "3bbb79b7-bd46-424c-9718-983c8c76d37e"
314 | }
315 |
316 | Count = 1
317 | }
318 | }
319 |
320 | Mock Split-SingleDscGroupedRecord { $true }
321 |
322 | $result = Get-SingleDscOperation -JobId 3BBB79B7-BD46-424C-9718-983C8C76D37E
323 |
324 | It 'should have called Get-SingleDscOperation' {
325 | Assert-MockCalled Split-SingleDscGroupedRecord -Times 1
326 | $result | Should Be $true
327 | }
328 | }
329 | }
330 |
331 | Describe 'Split-SingleDscGroupedRecord' {
332 | Context 'Passed an bad DSC record' {
333 | $TimeCreated = Get-Date;
334 | $singleRecordInGroupedEvents = New-Object PSObject -Property @{
335 | Group = New-Object PSObject -Property @{
336 | Guid = "3bbb79b7-bd46-424c-9718-983c8c76d37e"
337 | TimeCreated = $TimeCreated;
338 | Level = 2;
339 | ContainerLog = "Microsoft-Windows-Dsc/Operational";
340 | }
341 | Name = "{54137C5A-F607-48A5-9311-D6E102C15F83}"
342 | Count = 1
343 | }
344 |
345 | Mock Get-MessageFromEvent {return "Mocked Message"}
346 |
347 | $result = Split-SingleDscGroupedRecord -singleRecordInGroupedEvents $singleRecordInGroupedEvents -Index 0;
348 |
349 | It 'should have SequenceID match the index' {
350 | $result.SequenceID | Should Be 0
351 | }
352 |
353 | It 'should have ComputerName match this computer' {
354 | $result.ComputerName | Should Be $env:ComputerName
355 | }
356 |
357 | It 'should have a failure as the result' {
358 | $result.Result | Should Be "Failure"
359 | }
360 |
361 | It 'should have error as the type' {
362 | $result.AllEvents[0].Type | Should Be "ERROR"
363 | }
364 |
365 | It 'should have the mocked message' {
366 | $result.AllEvents[0].Message | Should Be "Mocked Message"
367 | }
368 |
369 | It 'should have the right JobId' {
370 | $result.JobId | Should Be "54137C5A-F607-48A5-9311-D6E102C15F83"
371 | }
372 |
373 | It 'should have the right count' {
374 | $result.NumberOfEvents | Should Be 1
375 | }
376 | }
377 |
378 | Context 'Passing a Warning' {
379 | $TimeCreated = Get-Date;
380 | $singleRecordInGroupedEvents = New-Object PSObject -Property @{
381 | Group = New-Object PSObject -Property @{
382 | Guid = "3bbb79b7-bd46-424c-9718-983c8c76d37e"
383 | TimeCreated = $TimeCreated;
384 | Level = 1;
385 | ContainerLog = "Microsoft-Windows-Dsc/Operational";
386 | LevelDisplayName = "Warning";
387 | }
388 | Name = "{54137C5A-F607-48A5-9311-D6E102C15F83}"
389 | }
390 |
391 | Mock Get-MessageFromEvent {return "Mocked Message"}
392 |
393 | $result = Split-SingleDscGroupedRecord -singleRecordInGroupedEvents $singleRecordInGroupedEvents -Index 0;
394 |
395 | it 'should find a warning event' {
396 | $result.WarningEvents | Should Not Be $null
397 | }
398 | }
399 |
400 | Context 'Passing an Operational Log' {
401 | $TimeCreated = Get-Date;
402 | $singleRecordInGroupedEvents = New-Object PSObject -Property @{
403 | Group = New-Object PSObject -Property @{
404 | Guid = "3bbb79b7-bd46-424c-9718-983c8c76d37e"
405 | TimeCreated = $TimeCreated;
406 | Level = 1;
407 | ContainerLog = "Microsoft-Windows-Dsc/operational";
408 | LevelDisplayName = "Operational";
409 | }
410 | Name = "{54137C5A-F607-48A5-9311-D6E102C15F83}"
411 | }
412 |
413 | Mock Get-MessageFromEvent {return "Mocked Message"}
414 |
415 | $result = Split-SingleDscGroupedRecord -singleRecordInGroupedEvents $singleRecordInGroupedEvents -Index 0;
416 |
417 | It 'should find the right type' {
418 | $result.AllEvents[0].Type | Should Be "OPERATIONAL"
419 | }
420 |
421 | It 'should find some operational events' {
422 | $result.OperationalEvents | Should Not Be $null
423 | }
424 |
425 | It 'should find some non-verbose events' {
426 | $result.NonVerboseEvents | Should Not Be $null
427 | }
428 | }
429 |
430 | Context 'Passing a Debug Log' {
431 | $TimeCreated = Get-Date;
432 | $singleRecordInGroupedEvents = New-Object PSObject -Property @{
433 | Group = New-Object PSObject -Property @{
434 | Guid = "3bbb79b7-bd46-424c-9718-983c8c76d37e"
435 | TimeCreated = $TimeCreated;
436 | Level = 1;
437 | ContainerLog = "Microsoft-Windows-Dsc/debug";
438 | LevelDisplayName = "Debug";
439 | }
440 | Name = "{54137C5A-F607-48A5-9311-D6E102C15F83}"
441 | }
442 |
443 | Mock Get-MessageFromEvent {return "Mocked Message"}
444 |
445 | $result = Split-SingleDscGroupedRecord -singleRecordInGroupedEvents $singleRecordInGroupedEvents -Index 0;
446 |
447 | It 'should find the right type' {
448 | $result.AllEvents[0].Type | Should Be "DEBUG"
449 | }
450 |
451 | It 'should find some debug events' {
452 | $result.DebugEvents | Should Not Be $null
453 | }
454 | }
455 |
456 | Context 'Passing a Verbose Log' {
457 | $TimeCreated = Get-Date;
458 | $singleRecordInGroupedEvents = New-Object PSObject -Property @{
459 | Group = New-Object PSObject -Property @{
460 | Guid = "3bbb79b7-bd46-424c-9718-983c8c76d37e"
461 | TimeCreated = $TimeCreated;
462 | Level = 1;
463 | ContainerLog = "Microsoft-Windows-Dsc/analytic";
464 | LevelDisplayName = "analytic";
465 | Id = 4100;
466 | }
467 | Name = "{54137C5A-F607-48A5-9311-D6E102C15F83}"
468 | }
469 |
470 | Mock Get-MessageFromEvent {return "Mocked Message"}
471 |
472 | $result = Split-SingleDscGroupedRecord -singleRecordInGroupedEvents $singleRecordInGroupedEvents -Index 0;
473 |
474 | It 'should find the right type' {
475 | $result.AllEvents[0].Type | Should Be "Verbose"
476 | }
477 |
478 | It 'should find some debug events' {
479 | $result.VerboseEvents | Should Not Be $null
480 | }
481 | }
482 |
483 | Context 'Passing an Analytic Log' {
484 | $TimeCreated = Get-Date;
485 | $singleRecordInGroupedEvents = New-Object PSObject -Property @{
486 | Group = New-Object PSObject -Property @{
487 | Guid = "3bbb79b7-bd46-424c-9718-983c8c76d37e"
488 | TimeCreated = $TimeCreated;
489 | Level = 1;
490 | ContainerLog = "Microsoft-Windows-Dsc/analytic";
491 | LevelDisplayName = "analytic";
492 | }
493 | Name = "{54137C5A-F607-48A5-9311-D6E102C15F83}"
494 | }
495 |
496 | Mock Get-MessageFromEvent {return "Mocked Message"}
497 |
498 | $result = Split-SingleDscGroupedRecord -singleRecordInGroupedEvents $singleRecordInGroupedEvents -Index 0;
499 |
500 | It 'should find the right type' {
501 | $result.AllEvents[0].Type | Should Be "Analytic"
502 | }
503 |
504 | It 'should find some analytic events' {
505 | $result.NonVerboseEvents | Should Not Be $null
506 | }
507 | }
508 | }
509 |
510 | Describe 'Get-MessageFromEvent' {
511 | Context 'For a non-verbose event' {
512 | $eventRecord = New-Object PSObject -Property @{
513 | Message = [Environment]::NewLine + "Some Message";
514 | }
515 |
516 | $result = Get-MessageFromEvent -EventRecord $eventRecord
517 |
518 | It 'should return the value' {
519 | $result | Should Be "Some Message"
520 | }
521 | }
522 |
523 | Context 'For a verbose event' {
524 | $verboseMessage = New-Object PSObject -Property @{Value = "Verbose Message"}
525 | $value = @([Environment]::NewLine, [Environment]::NewLine, $verboseMessage)
526 | $eventRecord = New-Object PSObject -Property @{
527 | Message = [Environment]::NewLine + "Some Message";
528 | Id = 4117
529 | Properties = $value;
530 | }
531 |
532 | $result = Get-MessageFromEvent -EventRecord $eventRecord -verboseType
533 |
534 | It 'should return the value' {
535 | $result | Should Be "Verbose Message"
536 | }
537 | }
538 | }
539 |
540 | Describe 'Get-DscErrorMessage' {
541 | Mock Get-SingleRelevantErrorMessage {return "Output Error Message"}
542 | $errorRecords = @{Id = "4131";}
543 |
544 | $result = Get-DscErrorMessage -ErrorRecords $errorRecords
545 |
546 | It 'should be the message' {
547 | $result | should be "Output Error Message "
548 | }
549 | }
550 |
551 | Describe 'Get-SingleRelevantErrorMessage' {
552 | Context 'Property Index is -1' {
553 | $value = @{Value = "Property"}
554 | $hash = @([Environment]::NewLine; $value)
555 | $errorEvent = @{Id = 4131; Properties = $hash}
556 |
557 | $result = Get-SingleRelevantErrorMessage -ErrorEvent $errorEvent;
558 |
559 | It 'should reutrn the correct string' {
560 | $result | should be "Property"
561 | }
562 | }
563 |
564 | Context 'Property Index is not -1' {
565 | Mock Get-MessageFromEvent {return "Mocked Message"}
566 |
567 | $value = @{Value = "Property"}
568 | $hash = @([Environment]::NewLine; $value)
569 | $errorEvent = @{Id = 4183; Properties = $hash}
570 |
571 | $result = Get-SingleRelevantErrorMessage -ErrorEvent $errorEvent;
572 |
573 | It 'should call Get-MessageFromEvent' {
574 | Assert-MockCalled Get-MessageFromEvent
575 | }
576 |
577 | It 'should reutrn the correct string' {
578 | $result | should be "Mocked Message"
579 | }
580 | }
581 | }
582 |
583 | Describe 'Get-DscOperationInternal' {
584 | Mock Get-AllGroupedDscEvents { return "GroupedEvents" }
585 | Mock Split-SingleDscGroupedRecord { return "singleOutputRecord" }
586 |
587 | $result = Get-DscOperationInternal -Newest 1;
588 |
589 | It 'should call Get-AllGroupedDscEvents' {
590 | Assert-MockCalled Get-AllGroupedDscEvents
591 | }
592 |
593 | It 'should loop over the events the correct amount of times' {
594 | Assert-MockCalled Split-SingleDscGroupedRecord -Times 1
595 | }
596 |
597 | It 'should return the singleOutputRecord' {
598 | $result | Should Be "singleOutputRecord"
599 | }
600 | }
601 |
602 | Describe 'Test-DscEventLogStatus' {
603 | Context 'Log is Enabled' {
604 | Mock Get-WinEvent {return @{IsEnabled = $true}}
605 | $result = Test-DscEventLogStatus
606 |
607 | It 'should return true' {
608 | $result | Should Be $true
609 | }
610 | }
611 |
612 | Context 'Log is Disabled and not enabled' {
613 | Mock Get-WinEvent {return @{IsEnabled = $false}}
614 | Mock Log {}
615 | Mock Read-Host {return "n"};
616 |
617 | $result = Test-DscEventLogStatus
618 |
619 | It 'should return false' {
620 | $result | Should Be $false
621 | }
622 |
623 | It 'should call Log' {
624 | Assert-MockCalled Log
625 | }
626 |
627 | It 'should call Read-Host' {
628 | Assert-MockCalled Read-Host
629 | }
630 | }
631 |
632 | Context 'Log is Disabled and is enabled' {
633 | Mock Get-WinEvent {return @{IsEnabled = $false}}
634 | Mock Enable-DscEventLog {}
635 | Mock Read-Host {return "y"};
636 | Mock Write-Host {};
637 |
638 | $result = Test-DscEventLogStatus
639 |
640 | It 'should return false' {
641 | $result | Should Be $false
642 | }
643 |
644 | It 'should call Write-Host' {
645 | Assert-MockCalled Write-Host
646 | }
647 |
648 | It 'should call Read-Host' {
649 | Assert-MockCalled Read-Host
650 | }
651 |
652 | It 'should call Enable-DscEventLog' {
653 | Assert-MockCalled Enable-DscEventLog
654 | }
655 | }
656 | }
657 | }
658 |
659 | Describe "Get-cDscOperation" {
660 | Context "does it call its internal functions" {
661 | Mock -ModuleName cDscDiagnostics Add-ClassTypes {}
662 | Mock -ModuleName cDscDiagnostics Get-DscOperationInternal {}
663 | Mock -ModuleName cDscDiagnostics Log {}
664 |
665 | $result = Get-cDscOperation -ComputerName $env:ComputerName;
666 |
667 | It "should call Add-ClassType" {
668 | Assert-MockCalled Add-ClassTypes -ModuleName cDscDiagnostics -Times 1
669 | }
670 |
671 | It "should call Get-DscOperationInternal" {
672 | Assert-MockCalled Get-DscOperationInternal -ModuleName cDscDiagnostics -Times 1
673 | }
674 |
675 | It "should call Log" {
676 | Assert-MockCalled Log -ModuleName cDscDiagnostics -Times 1
677 | }
678 | }
679 | }
--------------------------------------------------------------------------------