DDO-PSTemplates.psm1
# PUBLIC function Get-PSTemplateVariables { [CmdletBinding()] Param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, Position=0 )] [Alias('FullName')] [String] $Path, [Parameter( Position=1 )] [Switch] $OutHashString, [Parameter( DontShow )] [switch] $OutInvokeTemplate ) BEGIN { $baseMessage = "[ $($MyInvocation.InvocationName) ]" Write-Verbose "$baseMessage Executing" } PROCESS { $global:uniqueHash = [ordered]@{} $templateItemPaths = if (($Path | Get-PSTemplateType) -eq 'TemplateStack') { Get-ChildItem -Path $Path -Recurse } elseif (($Path | Get-PSTemplateType) -eq 'TemplateFile') { $Path } else { throw("$baseMessage Unexpected PSTemplateType: $($Path | Get-PSTemplateType)") } #-------------------------------------------------------------------------- $variablesResult = $templateItemPaths | Foreach-Object { $_ | Get-TemplateVariables | Foreach-Object { $v = $_ if ( -not ($uniqueHash.Contains($v.Name))) { $uniqueHash.Add($v.Name,[ordered]@{ Replace = $v.Match With = $null RegExEscape = $v.RegExEscape }) } $v } } } END { if (-not ($PSBoundParameters.ContainsKey('OutInvokeTemplate'))) { if (-not ($PSBoundParameters.ContainsKey('OutHashString'))) { $variablesResult | Sort-Object -Property Name } else { $maxDepth = $uniqueHash.Keys | Foreach-Object {$_.Length} | Sort-Object -Descending | Select-Object -First 1 $sb = [System.Text.StringBuilder]::new() [void]$sb.AppendLine('') [void]$sb.AppendLine('$VariablesHashTable = @{') $uniqueHash.Keys | Foreach-Object { $varName = $_.ToString() $depthString = " " * ($maxDepth - $varName.Length) $string = "`t$varName$($depthString) = `"`"" [void]$sb.AppendLine($string) } [void]$sb.AppendLine('}') $sb.ToString() } } else { $uniqueHash } } } # PUBLIC function Invoke-PSTemplate { [CmdletBinding()] Param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, Position=0 )] [Alias( 'FullName' )] [String] $Path, [Parameter( Mandatory, Position=1 )] [HashTable] $VariablesHashtable, [Parameter( Mandatory, Position=2 )] [System.IO.DirectoryInfo] $OutputDirectory ) BEGIN { $baseMessage = "[ $($MyInvocation.InvocationName) ]" Write-Verbose "$baseMessage Executing" $regExTemplateName = [regex]::new('^\.pstpl\.') $regExVariableName = [regex]::new('\{{2}\s(?<Variable>\w*)\s\}{2}') } PROCESS { Write-Verbose " - Finding all Template Variables" $templateVariables = $Path | Get-PSTemplateVariables -OutInvokeTemplate -Verbose:$false Write-Verbose " - Resolving Template Variables" $params = @{ TemplateVariables = $templateVariables InputVariables = $VariablesHashtable Verbose = $false } $process = Resolve-TemplateVariables @params switch ($Path | Get-PSTemplateType -Verbose:$false) { 'TemplateStack' { $stackDirectory = [System.IO.DirectoryInfo](Resolve-Path $Path).Path $stackName = $stackDirectory.Name -replace $regExTemplateName,'' Write-Verbose " - Creating TemplateStack $($stackName)" try { Push-Location $stackDirectory $params = @{ Path = $stackDirectory TemplateVariables = $process OutputDirectory = $outputDirectory Verbose = $false } Invoke-ProcessTemplateDirectory @params $stackDirectory | Get-ChildItem -Directory -Recurse | Foreach-Object { $directory = $_ #--------------------------------------------------------- $resolvedDirectoryPath = (Resolve-Path $directory -Relative) | ConvertFrom-TemplateFileName -TemplateVariables $process -Verbose:$false $outputDirectoryPath = Join-Path $outputDirectory ($resolvedDirectoryPath -replace '^\.\\','') #--------------------------------------------------------- mkdir $outputDirectoryPath -Force | Out-Null #--------------------------------------------------------- $params = @{ Path = $directory TemplateVariables = $process OutputDirectory = $outputDirectoryPath Verbose = $false } Invoke-ProcessTemplateDirectory @params } } finally { Pop-Location } } 'TemplateFile' { $params = @{ Path = $Path TemplateVariables = $process OutputDirectory = $OutputDirectory Verbose = $false } ConvertFrom-TemplateFile @params } } } END { Write-Verbose "$baseMessage Execution Complete" } } # PRIVATE function ConvertFrom-TemplateFile { [CmdletBinding()] Param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, Position=0 )] [Alias( 'FullName' )] [String] $Path, [Parameter( Mandatory, Position=1 )] $TemplateVariables, [Parameter( Mandatory, Position=2 )] [System.IO.DirectoryInfo] $OutputDirectory ) BEGIN { $baseMessage = "[ $($MyInvocation.InvocationName) ]" Write-Verbose "$baseMessage Executing" $regExTemplateName = [regex]::new('^\.pstpl\.') $regExVariableName = [regex]::new('\{{2}\s(?<Variable>\w*)\s\}{2}') } PROCESS { $templateFile = [System.IO.FileInfo](Resolve-Path $Path).Path Write-Verbose " - Creating TemplateFile $($templateFile.Name)" $fromName = $templateFile.Name $toName = ConvertFrom-TemplateFileName -Name $fromName -TemplateVariables $TemplateVariables -Verbose:$false $content = ConvertFrom-TemplateFileContent -Path $templateFile -TemplateVariables $TemplateVariables -Verbose:$false Write-Verbose " - Converted TemplateFile name $fromName to $toName" $outputPath = Join-Path $OutputDirectory $toName Write-Verbose " - Exporting TemplateFile $toName to $outputPath" $content | Out-File $outputPath -Force } } # PRIVATE function ConvertFrom-TemplateFileContent { [CmdletBinding()] Param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, Position=0 )] [Alias( 'FullName' )] [String] $Path, [Parameter( Mandatory, Position=1 )] $TemplateVariables ) BEGIN { $baseMessage = "[ $($MyInvocation.InvocationName) ]" Write-Verbose "$baseMessage Executing" $regExTemplateName = [regex]::new('^\.pstpl\.') $regExVariableName = [regex]::new('\{{2}\s(?<Variable>\w*)\s\}{2}') } PROCESS { $content = Get-Content -Path $Path -Raw $TemplateVariables.Keys | Foreach-Object { $variableName = $_ $replace = $TemplateVariables[$variableName] $content = $content -replace $replace.REgExEscape,$replace.With } $content } } # PRIVATE function ConvertFrom-TemplateFileName { [CmdletBinding()] Param( [Parameter( Mandatory, ValueFromPipeline, Position=0 )] [String] $Name, [Parameter( Mandatory, Position=1 )] $TemplateVariables ) BEGIN { $baseMessage = "[ $($MyInvocation.InvocationName) ]" Write-Verbose "$baseMessage Executing" $regExTemplateName = [regex]::new('^\.pstpl\.') $regExVariableName = [regex]::new('\{{2}\s(?<Variable>\w*)\s\}{2}') } PROCESS { $Name = $Name -replace $regExTemplateName,'' $TemplateVariables.Keys | Foreach-Object { $variableName = $_ $replace = $TemplateVariables[$variableName] $Name = $Name -replace $replace.REgExEscape,$replace.With } $Name } } # PRIVATE function Get-PathType { [CmdletBinding()] Param( [Parameter( Position=0, ValueFromPipeline, ValueFromPipelineByPropertyName )] [Alias('FullName')] [string] $Path = ".\" ) Begin { $baseMessage = "[ $($MyInvocation.InvocationName) ]" Write-Verbose "$baseMessage Executing" } Process { if (Test-Path -Path $Path) { if (Test-Path -Path $Path -PathType Container -ErrorAction SilentlyContinue) { 'Directory' } elseif (Test-Path -Path $Path -PathType Leaf -ErrorAction SilentlyContinue) { 'File' } else { throw "$baseMessage Path exists, but unknown path type..." } } else { throw "$baseMessage Path does not exist. Path: $Path" } } } # PRIVATE function Get-PSTemplateType { [CmdletBinding()] Param( [Parameter( Position=0, ValueFromPipeline, ValueFromPipelineByPropertyName )] [Alias('FullName')] [string] $Path = ".\" ) Begin { $baseMessage = "[ $($MyInvocation.InvocationName) ]" Write-Verbose "$baseMessage Executing" $regExTemplateName = [regex]::new('^\.pstpl\.') $regExVariableName = [regex]::new('\{{2}\s(?<Variable>\w*)\s\}{2}') } Process { switch ($Path | Get-PathType) { 'Directory' { $directory = [System.IO.DirectoryInfo]$Path if ($directory.Name -match $regExTemplateName) { 'TemplateStack' } else { throw("$baseMessage $($directory.FullName) is not a valid TemplateStack directory. The directory name should be prefixed with `".pstpl.`"") } } 'File' { $file = [System.IO.FileInfo]$Path if ($file.Name -match $regExTemplateName) { 'TemplateFile' } else { throw("$baseMessage $($file.FullName) is not a valid TemplateStack directory. The directory name should be prefixed with `".pstpl.`"") } } } } } # PRIVATE function Get-TemplateVariables { [CmdletBinding()] Param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] [Alias('FullName')] [String] $Path ) BEGIN { $baseMessage = "[ $($MyInvocation.InvocationName) ]" Write-Verbose "$baseMessage Executing" $regExTemplateName = [regex]::new('^\.pstpl\.') $regExVariableName = [regex]::new('\{{2}\s(?<Variable>\w*)\s\}{2}') } PROCESS { switch ($Path | Get-PathType) { 'Directory' { $directory = [System.IO.DirectoryInfo](Resolve-Path $Path).Path $regExVariableName.Matches($directory.Name) | Foreach-Object { $m = $_ [PSCustomObject]@{ Name = $m.Groups['Variable'].Value LineNumber = 'InName' Match = $m.Groups[0].Value RegExEscape = [System.Text.RegularExpressions.Regex]::Escape($m.Groups[0].Value) Path = $directory.FullName } } } 'File' { $file = [System.IO.FileInfo](Resolve-Path $Path).Path $regExVariableName.Matches($file.Name) | Foreach-Object { $m = $_ [PSCustomObject]@{ Name = $m.Groups['Variable'].Value LineNumber = 'InName' Match = $m.Groups[0].Value RegExEscape = [System.Text.RegularExpressions.Regex]::Escape($m.Groups[0].Value) Path = $file.FullName } } $content = Get-Content -Path $file -Raw if ($content) { $regExVariableName.Matches($content) | Foreach-Object { $m = $_ $lineNumber = ($content.Substring(0, $m.Index + 1) | Measure-Object -Line).Lines [PSCustomObject]@{ Name = $m.Groups['Variable'].Value LineNumber = $lineNumber Match = $m.Groups[0].Value RegExEscape = [System.Text.RegularExpressions.Regex]::Escape($m.Groups[0].Value) Path = $file.FullName } } } } } } } # PRIVATE function Invoke-ProcessTemplateDirectory { [CmdletBinding()] Param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, Position=0 )] [Alias( 'FullName' )] [String] $Path, [Parameter( Mandatory, Position=1 )] $TemplateVariables, [Parameter( Mandatory, Position=2 )] [System.IO.DirectoryInfo] $OutputDirectory ) BEGIN { $baseMessage = "[ $($MyInvocation.InvocationName) ]" Write-Verbose "$baseMessage Executing" } PROCESS { $Path | Get-ChildItem -File | Foreach-Object { $file = $_ if ($file.Name -match $regExTemplateName) { Write-Verbose "- Processing TemplateFile $($file.Name)" $params = @{ Path = $file TemplateVariables = $TemplateVariables OutputDirectory = $outputDirectory } ConvertFrom-TemplateFile @params } else { $updatedFileName = $file.Name | ConvertFrom-TemplateFileName -TemplateVariables $TemplateVariables $updatedFilePath = Join-Path $outputDirectory $updatedFileName Copy-Item -Path $file.FullName -Destination $updatedFilePath -Force } } } } # PRIVATE function Resolve-TemplateVariables { [CmdletBinding()] Param( [Parameter( Mandatory, ValueFromPipeline, Position=0 )] [System.Collections.Specialized.OrderedDictionary] $TemplateVariables, [Parameter( Mandatory, Position=1 )] [HashTable] $InputVariables ) BEGIN { $baseMessage = "[ $($MyInvocation.InvocationName) ]" Write-Verbose "$baseMessage Executing" } PROCESS { $toProcessHash = [ordered]@{} $warnings = [System.Collections.Generic.List[string]]::new() $TemplateVariables.Keys | Foreach-Object { $variableName = $_ if (-not ($InputVariables.ContainsKey($variableName))) { <# The Below is to resolve any conflicts for missing variables or warning of possible case conslicts #> $TemplateVariables[$variableName].With = Read-Host -Prompt "Enter in the value for the Template Variable: $($variableName)" # TODO: Need to add some case sensitivity warnings... } else { <# This is if the user has supplied a variable name and value correctly #> $TemplateVariables[$variableName].With = $InputVariables[$variableName] } } $TemplateVariables } } |