LocalDev.psm1
$Script:LocalAutomationVariable = @{} <# .SYNOPSIS Returns a scriptblock that, when dot-sourced, will import a workflow and all its dependencies. .DESCRIPTION Import-Workflow is a helper function to resolve dependencies of a given workflow. It must be invoked with a very specific syntax in order to import workflows into your current session. See the example. .PARAMETER WorkflowName The name of the workflow to import. .PARAMETER Path The path containing all ps1 files to search for workflow dependencies. .EXAMPLE PS > . (Import-Workflow Test-Workflow) #> function Import-Workflow { param( [Parameter(Mandatory=$True)] [String] $WorkflowName, [Parameter(Mandatory=$False)] [String] $Path = $env:AutomationWorkflowPath ) # TODO: Make $CompileStack a more appropriate data structure. # We're not really using the stack functionality at all. In fact, # it was buggy. :-( $CompileStack = New-Object -TypeName 'System.Collections.Stack' $TokenizeStack = New-Object -TypeName 'System.Collections.Stack' $DeclaredCommands = Find-DeclaredCommand -Path $Path $BaseWorkflowPath = $DeclaredCommands[$WorkflowName].Path $Stacked = @{$BaseWorkflowPath = $True} $TokenizeStack.Push($BaseWorkflowPath) $CompileStack.Push($BaseWorkflowPath) while ($TokenizeStack.Count -gt 0) { $ScriptPath = $TokenizeStack.Pop() $Tokens = [System.Management.Automation.PSParser]::Tokenize((Get-Content -Path $ScriptPath), [ref] $null) $NeededCommands = ($Tokens | Where-Object -FilterScript { $_.Type -eq 'Command' }).Content foreach ($Command in $NeededCommands) { $NeededScriptPath = $DeclaredCommands[$Command].Path # If $NeededScriptPath is $null, we didn't find it when we searched declared # commands in the runbook path. We'll assume that is okay and that the command # is provided by some other means (e.g. a module import) if (($NeededScriptPath -ne $null) -and (-not $Stacked[$NeededScriptPath])) { $TokenizeStack.Push($NeededScriptPath) $CompileStack.Push($NeededScriptPath) $Stacked[$NeededScriptPath] = $True } } } $WorkflowDefinitions = ($CompileStack | foreach { (Get-Content -Path $_) }) -join "`n" $ScriptBlock = [ScriptBlock]::Create($WorkflowDefinitions) return $ScriptBlock } <# .SYNOPSIS Returns the named automation variable, referencing local JSON files to find values. .PARAMETER Name The name of the variable to retrieve. #> Function Get-AutomationVariable { Param( [Parameter(Mandatory=$True)] [String] $Name ) If(-not $Script:LocalAutomationVariable.ContainsKey($Name)) { Update-LocalAutomationVariable If(-not $Script:LocalAutomationVariable.ContainsKey($Name)) { Write-Warning -Message "Couldn't find variable $Name" -WarningAction 'Continue' Throw-Exception -Type 'VariableDoesNotExist' ` -Message "Couldn't find variable $Name" ` -Property @{ 'Variable' = $Name; } } } Return ($Script:LocalAutomationVariable[$Name]).Value } function Update-LocalAutomationVariable { param() Write-Verbose -Message 'Updating variables in memory' $FilesToProcess = (Get-ChildItem -Path $env:AutomationGlobalsPath -Include '*.json' -Recurse).FullName Read-SmaJSONVariables -Path $FilesToProcess } <# .SYNOPSIS Processes an JSON file, caching any variables it finds. .PARAMETER Path The JSON files that should be processed. #> Function Read-SmaJSONVariables { Param( [Parameter(Mandatory=$True)] [AllowNull()] [String[]] $Path ) $Script:LocalAutomationVariable = @{} ForEach($_Path in $Path) { Try { $JSON = ConvertFrom-JSON -InputObject ((Get-Content -Path $_Path) -as [String]) } Catch { Write-Warning -Message "Could not process [$_Path] - variables from that file will not be available" -WarningAction 'Continue' Write-Warning -Message "Does [$_Path] contain malformed JSON?" -WarningAction 'Continue' Write-Exception -Exception $_ -Stream 'Warning' } if(-not (Test-IsNullOrEmpty $JSON.Variables)) { ForEach($VariableName in ($JSON.Variables | Get-Member -MemberType NoteProperty).Name) { $Var = $JSON.Variables."$VariableName" $retVar = New-Object -TypeName 'PSObject' -Property @{ 'Name' = $VariableName; 'Value' = $var.Value } $Script:LocalAutomationVariable[$VariableName] = $retVar } } } } <# .Synopsis Creates a variable in a json file. When this is commited these variables will be created in SMA through continuous integration .Parameter VariableFilePath The path to the file to store this variable in. If not passed it is assumed you want to store it in the same file you did last time .Parameter Name The name of the variable to create. Variables will be named in the format Prefix-Name .Parameter Prefix The prefix of the variable to create. If not passed it will default to the name of the variable file you are storing it in Variables will be named in the format Prefix-Name .Parameter Value The value to store in the object. If a non primative is passed it will be converted into a string using convertto-json .Parameter Description The description of the variable to store in SMA .isEncrypted A boolean flag representing if this value should be encrypted in SMA #> Function Set-AutomationVariable { Param( [Parameter(Mandatory=$True)] $Name, [Parameter(Mandatory=$True)] $Value, [Parameter(Mandatory=$False)] $Prefix = [System.String]::Empty, [Parameter(Mandatory=$False)] $Description = [System.String]::Empty, [Parameter(Mandatory=$False)] $isEncrypted = $False ) if(-not $Prefix) { $Prefix = $Name.Split('-')[0] } else { $Name = "$Prefix-$Name" } $SettingsFilePath = "$($Env:AutomationGlobalsPath)\$($Prefix).json" if(-not (Test-Path -Path $SettingsFilePath)) { New-Item -ItemType File ` -Path $settingsFilePath } $SettingsVars = ConvertFrom-JSON -InputObject ((Get-Content -Path $SettingsFilePath) -as [String]) if(-not $SettingsVars) { $SettingsVars = @{} } else { $SettingsVars = ConvertFrom-PSCustomObject $SettingsVars } if(-not $SettingsVars.ContainsKey('Variables')) { $SettingsVars.Add('Variables',@{}) | out-null } if($Value.GetType().Name -notin @('Int32','String','DateTime')) { $Value = ConvertTo-JSON $Value -Compress } if($SettingsVars.Variables.GetType().name -eq 'PSCustomObject') { $SettingsVars.Variables = ConvertFrom-PSCustomObject $SettingsVars.Variables } if($SettingsVars.Variables.ContainsKey($Name)) { $SettingsVars.Variables."$Name".Value = $Value if($Description) { $SettingsVars.Variables."$Name".Description = $Description } if($Encrypted) { $SettingsVars.Variables."$Name".Encrypted = $Encrypted } } else { $SettingsVars.Variables.Add( $Name, @{ 'Value' = $Value ; 'Description' = $Description ; 'isEncrypted' = $isEncrypted } ) } Set-Content -Path $SettingsFilePath -Value (ConvertTo-JSON $SettingsVars) -Encoding UTF8 Read-SmaJSONVariables $SettingsFilePath } Function Remove-AutomationVariable { Param( [Parameter(Mandatory=$True)] [string] $Name, [Parameter(Mandatory=$False)] [string] $Prefix ) if(-not $Prefix) { $Prefix = $Name.Split('-')[0] } else { $Name = "$Prefix-$Name" } $SettingsFilePath = "$($Env:AutomationGlobalsPath)\$($Prefix).json" if(-not (Test-Path $SettingsFilePath)) { Throw-Exception -Type 'SettingsFileNotFound' ` -Message 'Could not find the settings file for the target variable.' ` -Property @{ 'SettingsFilePath' = $SettingsFilePath ; 'VariableName' = $Name ; 'VariablePrefix' = $Prefix ; } } $SettingsVars = ConvertFrom-JSON -InputObject ((Get-Content -Path $SettingsFilePath) -as [String]) if(-not $SettingsVars) { $SettingsVars = @{} } else { $SettingsVars = ConvertFrom-PSCustomObject $SettingsVars } if(-not $SettingsVars.ContainsKey('Variables')) { $SettingsVars.Add('Variables',@{}) | out-null } if($SettingsVars.Variables.GetType().name -eq 'PSCustomObject') { $SettingsVars.Variables = ConvertFrom-PSCustomObject $SettingsVars.Variables } if($SettingsVars.Variables.ContainsKey($Name)) { $SettingsVars.Variables.Remove($Name) Set-Content -Path $SettingsFilePath -Value (ConvertTo-JSON $SettingsVars) -Encoding UTF8 Read-SmaJSONVariables $SettingsFilePath } else { Write-Warning (New-Exception -Type 'Variable Not found' ` -Message 'The variable was not found in the current variable file.' ` -Property @{ 'VariableName' = $Name ; 'CurrentFilePath' = $SettingsFilePath ; 'VariableJSON' = $SettingsVars }) ` -WarningAction 'Continue' } } Workflow Get-AutomationPSCredential { [OutputType([System.Management.Automation.PSCredential])] param( [Parameter(Mandatory=$true)] [string] $Name ) Try { $Val = (Get-PasswordVaultCredential -UserName $Name -WithPassword) Write-Verbose -Message "Credential [$Name] found in PasswordVault" } Catch { Throw-Exception -Type 'CredentialNotFound' ` -Message 'Could not find credential. Please set it up in the local password vault using Set-PasswordVaultCredential' ` -Property @{ 'Name' = $Name } } $SecurePassword = $Val.Password | ConvertTo-SecureString -asPlainText -Force $Credential = New-Object -TypeName System.Management.Automation.PSCredential($Val.Username, $SecurePassword) Return $Credential -as [System.Management.Automation.PSCredential] } Function Set-AutomationSchedule { Param( [Parameter(Mandatory=$True)] [String] $Name, [Parameter(Mandatory=$False)] [String] $Prefix = [System.String]::Emptpy, [Parameter(Mandatory=$False)] [String] $Description = [System.String]::Emptpy, [Parameter(Mandatory=$False)] [DateTime] $NextRun = $null, [Parameter(Mandatory=$False)] [DateTime] $ExpirationTime = $null, [Parameter(Mandatory=$False)] [Int] $DayInterval = $null, [Parameter(Mandatory=$False)] [String] $RunbookName = [System.String]::Emptpy, [Parameter(Mandatory=$False)] [HashTable] $Parameter = @{} ) if(-not $Prefix) { $Prefix = $Name.Split('-')[0] } else { $Name = "$Prefix-$Name" } $SettingsFilePath = "$($Env:AutomationGlobalsPath)\$($Prefix).json" if(-not (Test-Path -Path $SettingsFilePath)) { New-Item -ItemType File ` -Path $settingsFilePath } $SettingsVars = ConvertFrom-JSON -InputObject ((Get-Content -Path $SettingsFilePath) -as [String]) if(-not $SettingsVars) { $SettingsVars = @{} } else { $SettingsVars = ConvertFrom-PSCustomObject $SettingsVars } if(-not $SettingsVars.ContainsKey('Schedules')) { $SettingsVars.Add('Schedules',@{}) | out-null } if($SettingsVars.Schedules.GetType().name -eq 'PSCustomObject') { $SettingsVars.Schedules = ConvertFrom-PSCustomObject $SettingsVars.Schedules } if($SettingsVars.Schedules.ContainsKey($Name)) { if($Description) { $SettingsVars.Schedules."$Name".Description = $Description } if($NextRun) { $SettingsVars.Schedules."$Name".NextRun = $NextRun } if($ExpirationTime) { $SettingsVars.Schedules."$Name".ExpirationTime = $ExpirationTime } if($DayInterval) { $SettingsVars.Schedules."$Name".DayInterval = $DayInterval } if($RunbookName) { $SettingsVars.Schedules."$Name".RunbookName = $RunbookName } if($Parameter) { $SettingsVars.Schedules."$Name".Parameter = $(ConvertTo-Json $Parameter) } } else { if(-not $ExpirationTime -or -not $NextRun -or -not $DayInterval -or -not $RunbookName ) { Throw-Exception -Type 'MinimumNewParametersNotFound' ` -Message 'The minimum set of input parameters for creating a new schedule was not supplied. Look at nulls' ` -Property @{ 'Name' = $Name ; 'ExpirationTime' = $ExpirationTime ; 'NextRun' = $NextRun ; 'DayInterval' = $DayInterval ; 'RunbookName' = $RunbookName ; } } $SettingsVars.Schedules.Add( $Name, @{ 'Description' = $Description ; 'ExpirationTime' = $ExpirationTime -as [DateTime] ; 'NextRun' = $NextRun -as [DateTime] ; 'DayInterval' = $DayInterval ; 'RunbookName' = $RunbookName ; 'Parameter' = $(ConvertTo-JSON $Parameter) } ) } Set-Content -Path $SettingsFilePath -Value (ConvertTo-JSON $SettingsVars) -Encoding UTF8 } Function Remove-AutomationSchedule { Param( [Parameter(Mandatory=$False)] $SettingsFilePath, [Parameter(Mandatory=$True)] $Name, [Parameter(Mandatory=$False)] $Prefix ) if(-not $Prefix) { $Prefix = $Name.Split('-')[0] } $SettingsFilePath = "$($Env:AutomationGlobalsPath)\$($Prefix).json" if(-not (Test-Path $SettingsFilePath)) { Throw-Exception -Type 'SettingsFileNotFound' ` -Message 'Could not find the settings file for the target variable.' ` -Property @{ 'SettingsFilePath' = $SettingsFilePath ; 'ScheduleName' = $Name ; 'SchedulePrefix' = $Prefix ; } } $SettingsVars = ConvertFrom-JSON -InputObject ((Get-Content -Path $SettingsFilePath) -as [String]) if(Test-IsNullOrEmpty $SettingsVars.Schedules) { Add-Member -InputObject $SettingsVars -MemberType NoteProperty -Value @() -Name Schedules } if(($SettingsVars.Schedules | Get-Member -MemberType NoteProperty).Name -Contains $Name) { $SettingsVars.Schedules = $SettingsVars.Schedules | Select-Object -Property * -ExcludeProperty $Name Set-Content -Path $SettingsFilePath -Value (ConvertTo-JSON $SettingsVars) -Encoding UTF8 } else { Write-Warning (New-Exception -Type 'Schedule Not found' ` -Message 'The schedule was not found in the current variable file. Try specifiying the file' ` -Property @{ 'VariableName' = $Name ; 'CurrentFilePath' = $SettingsFilePath ; 'SettingsJSON' = $SettingsVars }) ` -WarningAction 'Continue' } } <# .Synopsis Fake for Set-AutomationActivityMetadata for local dev. #> Function Set-AutomationActivityMetadata { Param([Parameter(Mandatory=$True)] $ModuleName, [Parameter(Mandatory=$True)] $ModuleVersion, [Parameter(Mandatory=$True)] $ListOfCommands) $Inputs = ConvertTo-JSON @{ 'ModuleName' = $ModuleName; 'ModuleVersion' = $ModuleVersion; 'ListOfCommands' = $ListOfCommands } Write-Verbose -Message "$Inputs" } Export-ModuleMember -Function * -Verbose:$false |