DscBuildHelpers.psm1
#Region '.\Private\Assert-DscModuleResourceIsValid.ps1' -1 function Assert-DscModuleResourceIsValid { [CmdletBinding()] param ( [Parameter(ValueFromPipeline = $true)] [Microsoft.PowerShell.DesiredStateConfiguration.DscResourceInfo] $DscResources ) begin { Write-Verbose 'Testing for valid resources.' $failedDscResources = @() } process { foreach ($DscResource in $DscResources) { $failedDscResources += Get-FailedDscResource -DscResource $DscResource } } end { if ($failedDscResources.Count -gt 0) { Write-Verbose 'Found failed resources.' foreach ($resource in $failedDscResources) { Write-Warning "`t`tFailed Resource - $($resource.Name) ($($resource.Version))" } throw 'One or more resources is invalid.' } } } #EndRegion '.\Private\Assert-DscModuleResourceIsValid.ps1' 38 #Region '.\Private\Get-RequiredModulesFromMOF.ps1' -1 #author Iain Brighton, from here: https://gist.github.com/iainbrighton/9d3dd03630225ee44126769c5d9c50a9 # Not sure that takes all possibilities into account: # i.e. when using Import-DscResource -Name ResourceName #even if it's bad practice # Also need to return PSModuleInfo, instead of @{ModuleName='<version>'} # Then probably worth promoting to public function Get-RequiredModulesFromMOF { <# .SYNOPSIS Scans a Desired State Configuration .mof file and returns the declared/ required modules. #> [CmdletBinding()] [OutputType([hashtable])] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [System.String] $Path ) process { $modules = @{} $moduleName = $null $moduleVersion = $null Get-Content -Path $Path -Encoding Unicode | ForEach-Object { $line = $_ if ($line -match '^\s?Instance of') { ## We have a new instance so write the existing one if (($null -ne $moduleName) -and ($null -ne $moduleVersion)) { $modules[$moduleName] = $moduleVersion $moduleName = $null $moduleVersion = $null Write-Verbose "Module Instance found: '$moduleName $moduleVersion'." } } elseif ($line -match '(?<=^\s?ModuleName\s?=\s?")\S+(?=";)') { ## Ignore the default PSDesiredStateConfiguration module if ($Matches[0] -notmatch 'PSDesiredStateConfiguration') { $moduleName = $Matches[0] Write-Verbose "Found Module Name '$modulename'." } else { Write-Verbose 'Excluding PSDesiredStateConfiguration module' } } elseif ($line -match '(?<=^\s?ModuleVersion\s?=\s?")\S+(?=";)') { $moduleVersion = $Matches[0] -as [System.Version] Write-Verbose "Module version = '$moduleVersion'." } } $modules } } #EndRegion '.\Private\Get-RequiredModulesFromMOF.ps1' 64 #Region '.\Private\Get-StandardCimType.ps1' -1 function Get-StandardCimType { $types = @{ Boolean = 'System.Boolean' UInt8 = 'System.Byte' SInt8 = 'System.SByte' UInt16 = 'System.UInt16' SInt16 = 'System.Int16' UInt32 = 'System.UInt32' SInt32 = 'System.Int32' UInt64 = 'System.UInt64' SInt64 = 'System.Int64' Real32 = 'System.Single' Real64 = 'System.Double' Char16 = 'System.Char' DateTime = 'System.DateTime' String = 'System.String' Reference = 'Microsoft.Management.Infrastructure.CimInstance' Instance = 'Microsoft.Management.Infrastructure.CimInstance' BooleanArray = 'System.Boolean[]' UInt8Array = 'System.Byte[]' SInt8Array = 'System.SByte[]' UInt16Array = 'System.UInt16[]' SInt16Array = 'System.Int16[]' UInt32Array = 'System.UInt32[]' SInt32Array = 'System.Int32[]' UInt64Array = 'System.UInt64[]' SInt64Array = 'System.Int64[]' Real32Array = 'System.Single[]' Real64Array = 'System.Double[]' Char16Array = 'System.Char[]' DateTimeArray = 'System.DateTime[]' StringArray = 'System.String[]' MSFT_Credential = 'System.Management.Automation.PSCredential' 'MSFT_KeyValuePair[]' = 'System.Collections.Hashtable' MSFT_KeyValuePair = 'System.Collections.Hashtable' } try { $types.GetEnumerator() | ForEach-Object { $null = Invoke-Command -ScriptBlock ([scriptblock]::Create("[$($_.Value)]")) -ErrorAction Stop [PSCustomObject]@{ CimType = $_.Key DotNetType = $_.Value } } } catch { Write-Error -Message "Failed to load CIM Types. The error was: $($_.Exception.Message)" } } #EndRegion '.\Private\Get-StandardCimType.ps1' 55 #Region '.\Private\Resolve-ModuleMetadataFile.ps1' -1 function Resolve-ModuleMetadataFile { [CmdletBinding(DefaultParameterSetName = 'ByDirectoryInfo')] param ( [Parameter(ParameterSetName = 'ByPath', Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $Path, [Parameter(ParameterSetName = 'ByDirectoryInfo', Mandatory = $true, ValueFromPipeline = $true)] [System.IO.DirectoryInfo] $InputObject ) process { $metadataFileFound = $true $metadataFilePath = '' Write-Verbose "Using Parameter set - $($PSCmdlet.ParameterSetName)." switch ($PSCmdlet.ParameterSetName) { 'ByPath' { Write-Verbose "Testing Path - $path." if (Test-Path -Path $Path) { Write-Verbose "`tFound $path." $item = (Get-Item -Path $Path) if ($item.PSIsContainer) { Write-Verbose "`t`tIt is a folder." $moduleName = Split-Path $Path -Leaf $metadataFilePath = Join-Path -Path $Path -ChildPath "$moduleName.psd1" $metadataFileFound = Test-Path -Path $metadataFilePath } else { if ($item.Extension -like '.psd1') { Write-Verbose "`t`tIt is a module metadata file." $metadataFilePath = $item.FullName $metadataFileFound = $true } else { $modulePath = Split-Path -Path $Path Write-Verbose "`t`tSearching for module metadata folder in '$ModulePath'." $moduleName = Split-Path $modulePath -Leaf Write-Verbose "`t`tModule name is '$moduleName'." $metadataFilePath = Join-Path -Path $ModulePath -ChildPath "$ModuleName.psd1" Write-Verbose "`t`tChecking for '$metadataFilePath'." $metadataFileFound = Test-Path -Path $metadataFilePath } } } else { $metadataFileFound = $false } } 'ByDirectoryInfo' { $moduleName = $InputObject.Name $metadataFilePath = Join-Path -Path $InputObject.FullName -ChildPath "$moduleName.psd1" $metadataFileFound = Test-Path -Path $metadataFilePath } } if ($metadataFileFound -and (-not [string]::IsNullOrEmpty($metadataFilePath))) { Write-Verbose "Found a module metadata file at '$metadataFilePath'." Convert-Path -Path $metadataFilePath } else { Write-Error "Failed to find a module metadata file at '$metadataFilePath'." } } } #EndRegion '.\Private\Resolve-ModuleMetadataFile.ps1' 80 #Region '.\Public\Clear-CachedDscResource.ps1' -1 function Clear-CachedDscResource { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWMICmdlet', '', Justification = 'Not possible via CIM')] [CmdletBinding(SupportsShouldProcess = $true)] param () if ($pscmdlet.ShouldProcess($env:computername)) { Write-Verbose 'Stopping any existing WMI processes to clear cached resources.' ### find the process that is hosting the DSC engine $dscProcessID = Get-WmiObject msft_providers | Where-Object { $_.provider -like 'dsccore' } | Select-Object -ExpandProperty HostProcessIdentifier ### Stop the process if ($dscProcessID -and $PSCmdlet.ShouldProcess('DSC Process')) { Get-Process -Id $dscProcessID | Stop-Process } else { Write-Verbose 'Skipping killing the DSC Process' } Write-Verbose 'Clearing out any tmp WMI classes from tested resources.' Get-DscResourceWmiClass -Class tmp* | Remove-DscResourceWmiClass } } #EndRegion '.\Public\Clear-CachedDscResource.ps1' 30 #Region '.\Public\Compress-DscResourceModule.ps1' -1 function Compress-DscResourceModule { [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $DscBuildOutputModules, [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [AllowNull()] [PSModuleInfo[]] $Modules ) begin { if (-not (Test-Path -Path $DscBuildOutputModules)) { mkdir -Path $DscBuildOutputModules -Force } } process { foreach ($module in $Modules) { if ($PSCmdlet.ShouldProcess("Compress $Module $($Module.Version) from $(Split-Path -Parent $Module.Path) to $DscBuildOutputModules")) { Write-Verbose "Publishing Module $(Split-Path -Parent $Module.Path) to $DscBuildOutputModules" $destinationPath = Join-Path -Path $DscBuildOutputModules -ChildPath "$($module.Name)_$($module.Version).zip" Compress-Archive -Path "$($module.ModuleBase)\*" -DestinationPath $destinationPath (Get-FileHash -Path $destinationPath).Hash | Set-Content -Path "$destinationPath.checksum" -NoNewline } } } } #EndRegion '.\Public\Compress-DscResourceModule.ps1' 39 #Region '.\Public\Find-ModuleToPublish.ps1' -1 function Find-ModuleToPublish { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String[]] $DscBuildSourceResources, [Parameter()] [ValidateNotNullOrEmpty()] [Microsoft.PowerShell.Commands.ModuleSpecification[]] $ExcludedModules = $null, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] $DscBuildOutputModules ) $modulesAvailable = Get-ModuleFromFolder -ModuleFolder $DscBuildSourceResources -ExcludedModules $ExcludedModules foreach ($module in $modulesAvailable) { $publishTargetZip = [System.IO.Path]::Combine( $DscBuildOutputModules, "$($module.Name)_$($module.version).zip" ) $publishTargetZipCheckSum = [System.IO.Path]::Combine( $DscBuildOutputModules, "$($module.Name)_$($module.version).zip.checksum" ) $zipExists = Test-Path -Path $publishTargetZip $checksumExists = Test-Path -Path $publishTargetZipCheckSum if (-not ($zipExists -and $checksumExists)) { Write-Debug "ZipExists = $zipExists; CheckSum exists = $checksumExists" Write-Verbose -Message "Adding $($Module.Name)_$($Module.Version) to the Modules To Publish" Write-Output -InputObject $Module } else { Write-Verbose -Message "$($Module.Name) does not need to be published" } } } #EndRegion '.\Public\Find-ModuleToPublish.ps1' 48 #Region '.\Public\Get-DscCimInstanceReference.ps1' -1 function Get-DscCimInstanceReference { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '', Justification='For debugging purposes')] param ( [Parameter(Mandatory = $true)] [string] $ResourceName, [Parameter(Mandatory = $true)] [string] $ParameterName, [Parameter()] [object] $Data ) if ($Script:allDscResourcePropertiesTable) { if ($allDscResourcePropertiesTable.ContainsKey("$($ResourceName)-$($ParameterName)")) { $p = $allDscResourcePropertiesTable."$($ResourceName)-$($ParameterName)" $typeConstraint = $p.TypeConstraint -replace '\[\]', '' Get-DscSplattedResource -ResourceName $typeConstraint -Properties $Data -NoInvoke } } else { Write-Host "No DSC Resource Properties metadata was found, cannot translate CimInstance parameters. Call 'Initialize-DscResourceMetaInfo' first is this is needed." } } #EndRegion '.\Public\Get-DscCimInstanceReference.ps1' 32 #Region '.\Public\Get-DscFailedResource.ps1' -1 function Get-DscFailedResource { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [Microsoft.PowerShell.DesiredStateConfiguration.DscResourceInfo[]] $DscResource ) process { foreach ($resource in $DscResource) { if ($resource.Path) { $resourceNameOrPath = Split-Path $resource.Path -Parent } else { $resourceNameOrPath = $resource.Name } if (-not (Test-xDscResource -Name $resourceNameOrPath)) { Write-Warning "`tResources $($_.name) is invalid." $resource } else { Write-Verbose ('DSC Resource Name {0} {1} is Valid' -f $resource.Name, $resource.Version) } } } } #EndRegion '.\Public\Get-DscFailedResource.ps1' 35 #Region '.\Public\Get-DscResourceFromModuleInFolder.ps1' -1 function Get-DscResourceFromModuleInFolder { [CmdletBinding()] [OutputType([object[]])] param ( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [string] $ModuleFolder, [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSModuleInfo[]] $Modules ) begin { $oldPSModulePath = $env:PSModulePath $env:PSModulePath = $ModuleFolder Write-Verbose "Retrieving all resources for '$ModuleFolder'." $dscResources = Get-DscResource $env:PSModulePath = $oldPSModulePath $result = @() } process { Write-Verbose "Filtering the $($dscResources.Count) resources." Write-Debug ($dscResources | Format-Table -AutoSize | Out-String) foreach ($dscResource in $dscResources) { if ($null -eq $dscResource.Module) { Write-Debug "Excluding resource '$($dscResource.Name) - $($dscResource.Version)', it is not part of a module." continue } foreach ($module in $Modules) { if (-not (Compare-Object -ReferenceObject $dscResource.Module -DifferenceObject $Module -Property ModuleType, Version, Name)) { Write-Debug "Resource $($dscResource.Name) matches one of the supplied Modules." Write-Debug "`tIncluding $($dscResource.Name) $($dscResource.Version)" $result += $dscResource } } } } end { $result } } #EndRegion '.\Public\Get-DscResourceFromModuleInFolder.ps1' 60 #Region '.\Public\Get-DscResourceProperty.ps1' -1 function Get-DscResourceProperty { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ParameterSetName = 'ModuleInfo')] [System.Management.Automation.PSModuleInfo] $ModuleInfo, [Parameter(Mandatory = $true, ParameterSetName = 'ModuleName')] [string] $ModuleName, [Parameter(Mandatory = $true)] [string] $ResourceName ) $ModuleInfo = if ($ModuleName) { Import-Module -Name $ModuleName -PassThru -Force } else { Import-Module -Name $ModuleInfo.Name -PassThru -Force } [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ClearCache() $functionsToDefine = New-Object -TypeName 'System.Collections.Generic.Dictionary[string,ScriptBlock]'([System.StringComparer]::OrdinalIgnoreCase) [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::LoadDefaultCimKeywords($functionsToDefine) $schemaFilePath = $null $keywordErrors = New-Object -TypeName 'System.Collections.ObjectModel.Collection[System.Exception]' $foundCimSchema = [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ImportCimKeywordsFromModule($ModuleInfo, $ResourceName, [ref] $SchemaFilePath, $functionsToDefine, $keywordErrors) if ($foundCimSchema) { [void][Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ImportScriptKeywordsFromModule($ModuleInfo, $ResourceName, [ref] $SchemaFilePath, $functionsToDefine) } else { [System.Collections.Generic.List[string]]$resourceNameAsList = $ResourceName [void][Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ImportClassResourcesFromModule($ModuleInfo, $resourceNameAsList, $functionsToDefine) } $resourceProperties = ([System.Management.Automation.Language.DynamicKeyword]::GetKeyword($ResourceName)).Properties foreach ($key in $resourceProperties.Keys) { $resourceProperty = $resourceProperties.$key $dscClassParameterInfo = & $ModuleInfo { param ( [Parameter(Mandatory = $true)] [string]$TypeName ) $result = @{ ElementType = $null Type = $null } try { $result.Type = Invoke-Command -ScriptBlock ([scriptblock]::Create("[$($TypeName)]")) if ($result.Type.IsArray) { $result.ElementType = $result.Type.GetElementType().FullName } } catch { Write-Verbose "The type '$TypeName' could not be resolved." } return $result } $resourceProperty.TypeConstraint [PSCustomObject]@{ Name = $resourceProperty.Name ModuleName = $ModuleInfo.Name ResourceName = $ResourceName TypeConstraint = $resourceProperty.TypeConstraint Attributes = $resourceProperty.Attributes Values = $resourceProperty.Values ValueMap = $resourceProperty.ValueMap Mandatory = $resourceProperty.Mandatory IsKey = $resourceProperty.IsKey Range = $resourceProperty.Range ElementType = $dscClassParameterInfo.ElementType Type = $dscClassParameterInfo.Type } } } #EndRegion '.\Public\Get-DscResourceProperty.ps1' 97 #Region '.\Public\Get-DscResourceWmiClass.ps1' -1 function Get-DscResourceWmiClass { <# .Synopsis Retrieves WMI classes from the DSC namespace. .Description Retrieves WMI classes from the DSC namespace. .Example Get-DscResourceWmiClass -Class tmp* .Example Get-DscResourceWmiClass -Class 'MSFT_UserResource' #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWMICmdlet', '', Justification = 'Not possible via CIM')] param ( #The WMI Class name search for. Supports wildcards. [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('Name')] [string] $Class ) begin { $dscNamespace = 'root/Microsoft/Windows/DesiredStateConfiguration' } process { Get-WmiObject -Namespace $dscNamespace -List @PSBoundParameters } } #EndRegion '.\Public\Get-DscResourceWmiClass.ps1' 33 #Region '.\Public\Get-DscSplattedResource.ps1' -1 function Get-DscSplattedResource { [CmdletBinding()] [OutputType([scriptblock])] param ( [Parameter(Mandatory = $true)] [String] $ResourceName, [Parameter()] [String] $ExecutionName, [Parameter()] [hashtable] $Properties, [Parameter()] [switch] $NoInvoke ) if (-not $script:allDscResourcePropertiesTable -and -not $script:allDscResourcePropertiesTableWarningShown) { Write-Warning -Message "The 'allDscResourcePropertiesTable' is not defined. This will be an expensive operation. Resources with MOF sub-types are only supported when calling 'Initialize-DscResourceMetaInfo' once before starting the compilation process." $script:allDscResourcePropertiesTableWarningShown = $true } $standardCimTypes = Get-StandardCimType # Remove Case Sensitivity of ordered Dictionary or Hashtables $Properties = @{} + $Properties $stringBuilder = [System.Text.StringBuilder]::new() $null = $stringBuilder.AppendLine("Param([hashtable]`$Parameters)") $null = $stringBuilder.AppendLine() if ($ExecutionName) { $null = $stringBuilder.AppendLine("$ResourceName '$ExecutionName' {") } else { $null = $stringBuilder.AppendLine("$ResourceName {") } foreach ($PropertyName in $Properties.Keys) { $cimType = $allDscResourcePropertiesTable."$ResourceName-$PropertyName" if ($cimType) { $isCimArray = $cimType.TypeConstraint.EndsWith('[]') $cimProperties = $Properties.$PropertyName $null = $stringBuilder.AppendLine("$PropertyName = {0}" -f $(if ($isCimArray) { '@(' } else { "$($cimType.TypeConstraint.Replace('[]', '')) {" })) if ($isCimArray) { if ($Properties.$PropertyName -isnot [array]) { Write-Warning -Message "The property '$PropertyName' is an array and the BindingInfo data is not an array" -ErrorAction Stop } $i = 0 foreach ($cimPropertyValue in $cimProperties) { $null = $stringBuilder.AppendLine($cimType.TypeConstraint.Replace('[]', '')) $null = $stringBuilder.AppendLine('{') foreach ($cimSubProperty in $cimPropertyValue.GetEnumerator()) { if ($cimType.Type.GetElementType().GetProperty($cimSubProperty.Name).PropertyType.IsArray) { $null = $stringBuilder.AppendLine("$($cimSubProperty.Name) = @(") $arrayItemTypeName = $cimType.Type.GetElementType().GetProperty($cimSubProperty.Name).PropertyType.GetElementType().Name $j = 0 $isCimSubArray = $cimType.Type.GetElementType().GetProperty($cimSubProperty.Name).PropertyType.GetElementType().FullName -notin $standardCimTypes.DotNetType foreach ($arrayItem in $cimSubProperty.Value) { if ($isCimSubArray) { $null = $stringBuilder.AppendLine("$arrayItemTypeName {") foreach ($arrayItemKey in $arrayItem.Keys) { $null = $stringBuilder.AppendLine("$arrayItemKey = `$Parameters['$PropertyName'][$($i)]['$($cimSubProperty.Name)'][$($j)]['$($arrayItemKey)']") } $null = $stringBuilder.AppendLine('}') } else { $null = $stringBuilder.AppendLine("@(`$Parameters['$PropertyName'][$($i)]['$($cimSubProperty.Name)'])[$($j)]") } $j++ } $null = $stringBuilder.AppendLine(')') } else { $null = $stringBuilder.AppendLine("$($cimSubProperty.Name) = `$Parameters['$PropertyName'][$($i)]['$($cimSubProperty.Name)']") } } $null = $stringBuilder.AppendLine('}') $i++ } $null = $stringBuilder.AppendLine('{0}' -f $(if ($isCimArray) { ')' })) } else { foreach ($cimProperty in $cimProperties.GetEnumerator()) { $null = $stringBuilder.AppendLine("$($cimProperty.Name) = `$Parameters['$PropertyName']['$($($cimProperty.Name))']") } $null = $stringBuilder.AppendLine('}') } } else { $null = $stringBuilder.AppendLine("$PropertyName = `$Parameters['$PropertyName']") } } $null = $stringBuilder.AppendLine('}') Write-Debug -Message ('Generated Resource Block = {0}' -f $stringBuilder.ToString()) if ($NoInvoke) { [scriptblock]::Create($stringBuilder.ToString()) } else { if ($Properties) { [scriptblock]::Create($stringBuilder.ToString()).Invoke($Properties) } else { [scriptblock]::Create($stringBuilder.ToString()).Invoke() } } } Set-Alias -Name x -Value Get-DscSplattedResource -Scope Global #EndRegion '.\Public\Get-DscSplattedResource.ps1' 159 #Region '.\Public\Get-ModuleFromFolder.ps1' -1 function Get-ModuleFromFolder { [CmdletBinding()] [OutputType([System.Management.Automation.PSModuleInfo[]])] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [System.IO.DirectoryInfo[]] $ModuleFolder, [Parameter()] [AllowNull()] [Microsoft.PowerShell.Commands.ModuleSpecification[]] $ExcludedModules ) begin { $allModulesInFolder = @() } process { foreach ($folder in $ModuleFolder) { Write-Debug -Message "Replacing Module path with $folder" $oldPSModulePath = $env:PSModulePath $env:PSModulePath = $folder Write-Debug -Message 'Discovering modules from folder' $allModulesInFolder += Get-Module -Refresh -ListAvailable Write-Debug -Message 'Reverting PSModulePath' $env:PSModulePath = $oldPSModulePath } } end { $allModulesInFolder | Where-Object { $source = $_ Write-Debug -Message "Checking if module '$source' is sxcluded." $isExcluded = foreach ($excludedModule in $ExcludedModules) { Write-Debug "`t Excluded module '$ExcludedModule'" if (($excludedModule.Name -and $excludedModule.Name -eq $source.Name) -and ( (-not $excludedModule.Version -and -not $excludedModule.Guid -and -not $excludedModule.MaximumVersion -and -not $excludedModule.RequiredVersion ) -or ($excludedModule.Version -and $excludedModule.Version -eq $source.Version) -or ($excludedModule.Guid -and $excludedModule.Guid -ne $source.Guid) -or ($excludedModule.MaximumVersion -and $excludedModule.MaximumVersion -ge $source.Version) -or ($excludedModule.RequiredVersion -and $excludedModule.RequiredVersion -eq $source.Version) ) ) { Write-Debug ('Skipping {0} {1} {2}' -f $source.Name, $source.Version, $source.Guid) return $false } } if (-not $isExcluded) { return $true } } } } #EndRegion '.\Public\Get-ModuleFromFolder.ps1' 70 #Region '.\Public\Initialize-DscResourceMetaInfo.ps1' -1 function Initialize-DscResourceMetaInfo { param ( [Parameter(Mandatory = $true)] [string] $ModulePath, [Parameter()] [switch] $ReturnAllProperties, [Parameter()] [switch] $Force, [Parameter()] [switch] $PassThru ) if ($script:allDscResourcePropertiesTable.Count -ne 0 -and -not $Force) { if ($PassThru) { return $script:allDscResourcePropertiesTable } else { return } } $allModules = Get-ModuleFromFolder -ModuleFolder $ModulePath $allDscResources = Get-DscResourceFromModuleInFolder -ModuleFolder $ModulePath -Modules $allModules $modulesWithDscResources = $allDscResources | Select-Object -ExpandProperty ModuleName -Unique $modulesWithDscResources = $allModules | Where-Object Name -In $modulesWithDscResources $standardCimTypes = Get-StandardCimType $script:allDscResourcePropertiesTable = @{} $script:allDscResourceProperties = foreach ($dscResource in $allDscResources) { $moduleInfo = $modulesWithDscResources | Where-Object { $_.Name -EQ $dscResource.ModuleName -and $_.Version -eq $dscResource.Version } $cimProperties = if ($ReturnAllProperties) { Get-DscResourceProperty -ModuleInfo $moduleInfo -ResourceName $dscResource.Name } else { Get-DscResourceProperty -ModuleInfo $moduleInfo -ResourceName $dscResource.Name | Where-Object TypeConstraint -NotIn $standardCimTypes.CimType } foreach ($cimProperty in $cimProperties) { [PSCustomObject]@{ Name = $cimProperty.Name TypeConstraint = $cimProperty.TypeConstraint IsKey = $cimProperty.IsKey Mandatory = $cimProperty.Mandatory Values = $cimProperty.Values Range = $cimProperty.Range ModuleName = $dscResource.ModuleName ResourceName = $dscResource.Name } $script:allDscResourcePropertiesTable."$($dscResource.Name)-$($cimProperty.Name)" = $cimProperty } } if ($PassThru) { $script:allDscResourcePropertiesTable } } #EndRegion '.\Public\Initialize-DscResourceMetaInfo.ps1' 79 #Region '.\Public\Publish-DscConfiguration.ps1' -1 function Publish-DscConfiguration { [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $true)] [string] $DscBuildOutputConfigurations, [Parameter()] [string] $PullServerWebConfig = "$env:SystemDrive\inetpub\wwwroot\PSDSCPullServer\web.config" ) process { Write-Verbose "Publishing Configuration MOFs from '$DscBuildOutputConfigurations'." Get-ChildItem -Path (Join-Path -Path $DscBuildOutputConfigurations -ChildPath '*.mof') | ForEach-Object { if (-not (Test-Path -Path $PullServerWebConfig)) { Write-Warning "The Pull Server configg '$PullServerWebConfig' cannot be found." Write-Warning "`t Skipping Publishing Configuration MOFs" } elseif ($PSCmdlet.shouldprocess($_.BaseName)) { Write-Verbose -Message "Publishing $($_.Name)" Publish-MofToPullServer -FullName $_.FullName -PullServerWebConfig $PullServerWebConfig } } } } #EndRegion '.\Public\Publish-DscConfiguration.ps1' 33 #Region '.\Public\Publish-DscResourceModule.ps1' -1 function Publish-DscResourceModule { [CmdletBinding(SupportsShouldProcess = $true)] param ( [Parameter(Mandatory = $true)] [string] $DscBuildOutputModules, [Parameter()] [System.IO.FileInfo] $PullServerWebConfig = "$env:SystemDrive\inetpub\wwwroot\PSDSCPullServer\web.config" ) begin { if (-not (Test-Path $PullServerWebConfig)) { if ($PSBoundParameters['ErrorAction'] -eq 'SilentlyContinue') { Write-Warning -Message "Could not find the Web.config of the pull Server at '$PullServerWebConfig'." } else { throw "Could not find the Web.config of the pull Server at '$PullServerWebConfig'." } return } else { $webConfigXml = [xml](Get-Content -Raw -Path $PullServerWebConfig) $configXElement = $webConfigXml.SelectNodes("//appSettings/add[@key = 'ConfigurationPath']") $OutputFolderPath = $configXElement.Value } } process { if ($OutputFolderPath) { Write-Verbose 'Moving Processed Resource Modules from' Write-Verbose "`t$DscBuildOutputModules to" Write-Verbose "`t$OutputFolderPath" if ($PSCmdlet.ShouldProcess("copy '$DscBuildOutputModules' to '$OutputFolderPath'")) { Get-ChildItem -Path $DscBuildOutputModules -Include @('*.zip', '*.checksum') | Copy-Item -Destination $OutputFolderPath -Force } } } } #EndRegion '.\Public\Publish-DscResourceModule.ps1' 53 #Region '.\Public\Push-DscConfiguration.ps1' -1 function Push-DscConfiguration { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] [OutputType([void])] param ( [Parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.Management.Automation.Runspaces.PSSession] $Session, [Parameter()] [Alias('MOF', 'Path')] [System.IO.FileInfo] $ConfigurationDocument, [Parameter()] [System.Management.Automation.PSModuleInfo[]] $WithModule, [Parameter(ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $true, Position = 1)] [Alias('DscBuildOutputModules')] $StagingFolderPath = "$Env:TMP\DSC\BuildOutput\modules\", [Parameter(ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $true, Position = 3)] $RemoteStagingPath = '$Env:TMP\DSC\modules\', [Parameter(ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $true, Position = 4)] [switch] $Force ) process { if ($PSCmdlet.ShouldProcess($Session.ComputerName, "Applying MOF '$ConfigurationDocument'")) { if ($WithModule) { Push-DscModuleToNode -Module $WithModule -StagingFolderPath $StagingFolderPath -RemoteStagingPath $RemoteStagingPath -Session $Session } Write-Verbose 'Removing previously pushed configuration documents' $resolvedRemoteStagingPath = Invoke-Command -Session $Session -ScriptBlock { $resolvedStagingPath = $ExecutionContext.InvokeCommand.ExpandString($Using:RemoteStagingPath) $null = Get-Item "$resolvedStagingPath\*.mof" | Remove-Item -Force -ErrorAction SilentlyContinue if (-not (Test-Path $resolvedStagingPath)) { mkdir -Force $resolvedStagingPath -ErrorAction Stop } $resolvedStagingPath } -ErrorAction Stop $remoteConfigDocumentPath = [System.IO.Path]::Combine($ResolvedRemoteStagingPath, 'localhost.mof') Copy-Item -ToSession $Session -Path $ConfigurationDocument -Destination $remoteConfigDocumentPath -Force -ErrorAction Stop Write-Verbose "Attempting to apply '$remoteConfigDocumentPath' on '$($session.ComputerName)'" Invoke-Command -Session $Session -ScriptBlock { Start-DscConfiguration -Wait -Force -Path $Using:resolvedRemoteStagingPath -Verbose -ErrorAction Stop } } } } #EndRegion '.\Public\Push-DscConfiguration.ps1' 63 #Region '.\Public\Push-DscModuleToNode.ps1' -1 <# .SYNOPSIS Injects Modules via PS Session. .DESCRIPTION Injects the missing modules on a remote node via a PSSession. The module list is checked again the available modules from the remote computer, Any missing version is then zipped up and sent over the PS session, before being extracted in the root PSModulePath folder of the remote node. .PARAMETER Module A list of Modules required on the remote node. Those missing will be packaged based on their Path. .PARAMETER StagingFolderPath Staging folder where the modules are being zipped up locally before being sent accross. .PARAMETER Session Session to use to gather the missing modules and to copy the modules to. .PARAMETER RemoteStagingPath Path on the remote Node where the modules will be copied before extraction. .PARAMETER Force Force all modules to be re-zipped, re-sent, and re-extracted to the target node. .EXAMPLE Push-DscModuleToNode -Module (Get-ModuleFromFolder C:\src\SampleKitchen\modules) -Session $RemoteSession -StagingFolderPath "C:\BuildOutput" #> function Push-DscModuleToNode { [CmdletBinding()] [OutputType([void])] param ( # Param1 help description [Parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $true)] [Alias('ModuleInfo')] [System.Management.Automation.PSModuleInfo[]] $Module, [Parameter(Position = 1, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $true)] [Alias('DscBuildOutputModules')] $StagingFolderPath = "$Env:TMP\DSC\BuildOutput\modules\", [Parameter(Mandatory = $true, Position = 2, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $true)] [System.Management.Automation.Runspaces.PSSession] $Session, [Parameter(Position = 3, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $true)] $RemoteStagingPath = '$Env:TMP\DSC\modules\', [Parameter(Position = 4, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $true)] [switch] $Force ) process { # Find the modules already available remotely if (-not $Force) { $remoteModuleAvailable = Invoke-Command -Session $Session -ScriptBlock { Get-Module -ListAvailable } } $resolvedRemoteStagingPath = Invoke-Command -Session $Session -ScriptBlock { $ResolvedStagingPath = $ExecutionContext.InvokeCommand.ExpandString($using:RemoteStagingPath) if (-not (Test-Path $ResolvedStagingPath)) { mkdir -Force $ResolvedStagingPath } $resolvedStagingPath } # Find the modules missing on remote node $missingModules = $Module.Where{ $matchingModule = foreach ($remoteModule in $RemoteModuleAvailable) { if ( $remoteModule.Name -eq $_.Name -and $remoteModule.Version -eq $_.Version -and $remoteModule.Guid -eq $_.Guid ) { Write-Verbose "Module match: '$($remoteModule.Name)'." $remoteModule } } if (-not $matchingModule) { Write-Verbose "Module not found: '$($_.Name)'." $_ } } Write-Verbose "The Missing modules are '$($MissingModules.Name -join ', ')'." # Find the missing modules from the staging folder # and publish it there Write-Verbose "Looking for missing zip modules in '$($StagingFolderPath)'." $missingModules.Where{ -not (Test-Path -Path "$StagingFolderPath\$($_.Name)_$($_.version).zip") } | Compress-DscResourceModule -DscBuildOutputModules $StagingFolderPath # Copy missing modules to remote node if not present already foreach ($module in $missingModules) { $fileName = "$($StagingFolderPath)/$($module.Name)_$($module.Version).zip" $testPathResult = Invoke-Command -Session $Session -ScriptBlock { param ( [Parameter(Mandatory = $true)] [string] $FileName ) Test-Path -Path $FileName } -ArgumentList $fileName if ($Force -or -not ($testPathResult)) { Write-Verbose "Copying '$fileName*' to '$ResolvedRemoteStagingPath'." Invoke-Command -Session $Session -ScriptBlock { param ( [Parameter(Mandatory = $true)] [string] $PathToZips ) if (-not (Test-Path -Path $PathToZips)) { mkdir -Path $PathToZips -Force } } -ArgumentList $resolvedRemoteStagingPath $param = @{ ToSession = $Session Path = "$($StagingFolderPath)/$($module.Name)_$($module.Version)*" Destination = $ResolvedRemoteStagingPath Force = $true } Copy-Item @param | Out-Null } else { Write-Verbose 'The File is already present remotely.' } } # Extract missing modules on remote node to PSModulePath Write-Verbose "Expanding '$resolvedRemoteStagingPath/*.zip' to '$env:CommonProgramW6432\WindowsPowerShell\Modules\$($Module.Name)\$($module.version)'." Invoke-Command -Session $Session -ScriptBlock { param ( [Parameter()] $MissingModules, [Parameter()] $PathToZips ) foreach ($module in $MissingModules) { $fileName = "$($module.Name)_$($module.version).zip" Write-Verbose "Expanding '$PathToZips/$fileName' to '$Env:CommonProgramW6432\WindowsPowerShell\Modules\$($Module.Name)\$($module.version)'." Expand-Archive -Path "$PathToZips/$fileName" -DestinationPath "$Env:ProgramW6432\WindowsPowerShell\Modules\$($Module.Name)\$($module.version)" -Force } } -ArgumentList $missingModules, $resolvedRemoteStagingPath } } #EndRegion '.\Public\Push-DscModuleToNode.ps1' 165 #Region '.\Public\Remove-DscResourceWmiClass.ps1' -1 <# .Synopsis Removes a WMI class from the DSC namespace. .Description Removes a WMI class from the DSC namespace. .Example Get-DscResourceWmiClass -Class tmp* | Remove-DscResourceWmiClass .Example Remove-DscResourceWmiClass -Class 'tmpD460' #> function Remove-DscResourceWmiClass { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWMICmdlet', '', Justification = 'Not possible via CIM')] [CmdletBinding()] param ( #The WMI Class name to remove. Supports wildcards. [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('Name')] [string] $ResourceType ) begin { $dscNamespace = 'root/Microsoft/Windows/DesiredStateConfiguration' } process { #Have to use WMI here because I can't find how to delete a WMI instance via the CIM cmdlets. (Get-WmiObject -Namespace $dscNamespace -List -Class $ResourceType).psbase.Delete() } } #EndRegion '.\Public\Remove-DscResourceWmiClass.ps1' 34 #Region '.\Public\Test-DscResourceFromModuleInFolderIsValid.ps1' -1 function Test-DscResourceFromModuleInFolderIsValid { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.IO.DirectoryInfo] $ModuleFolder, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)] [System.Management.Automation.PSModuleInfo[]] [AllowNull()] $Modules ) process { foreach ($module in $Modules) { $Resources = Get-DscResourceFromModuleInFolder -ModuleFolder $ModuleFolder -Modules $module $Resources.Where{ $_.ImplementedAs -eq 'PowerShell' } | Assert-DscModuleResourceIsValid } } } #EndRegion '.\Public\Test-DscResourceFromModuleInFolderIsValid.ps1' 26 |