First time in PowerShell 5 and I'm having trouble calling a function that writes messages to a file from another function. The following is a simplified version of what I'm doing.
workflow test {
function logMessage {
param([string] $Msg)
Write-Output $Msg
}
function RemoveMachineFromCollection{
param([string]$Collection, [string]$Machine)
# If there's an error
LogMessage "Error Removing Machine"
# If all is good
LogMessage "successfully remove machine"
}
$Collections = DatabaseQuery1
foreach -parallel($coll in $Collections) {
logMessage "operating on $coll collection"
$Machines = DatabaseQuery2
foreach($Mach in $Machines) {
logMessage "Removing $Mach from $coll"
RemoveMachineFromCollection -Collection $coll -Machine $Mach
}
}
}
test
Here's the error it generates:
The term 'logMessage' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. + CategoryInfo : ObjectNotFound: (logMessage:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException + PSComputerName : [localhost]
I've tried moving the logMessage function around in the file and even tried Global scope.
In any other language I would be able to call logMessage from any other function. As that's the purpose of a function.
What's the "Workflow way" of reusing a block of code?
Do I need to create some logging module that gets loaded into the Workflow?
You could move the functions and function call to an InlineScript
(PowerShell ScriptBlock) inside the workflow like below.
workflow test {
InlineScript
{
function func1{
Write-Output "Func 1"
logMessage
}
function logMessage{
Write-Output "logMessage"
}
func1
}
}
Would Output:
Func 1
logMessage
As @JeffZeitlin mentioned in his answer, workflows are not PowerShell and are much more restrictive. The InlineScript block allows for normal PowerShell code to be interpreted however the scope will be tied to the InlineScript block. For instance, if you define the functions in the script block then attempt to call the func1
function outside of the InlineScript block (but still within the workflow) it will fail because it is out of scope.
The same would happen if you define the two functions either outside of the workflow or inside of the workflow but not in an InlineScript block.
Now for an example of how you can apply this to running a foreach -parallel
loop.
workflow test {
## workflow parameter
param($MyList)
## parallel foreach loop on workflow parameter
foreach -parallel ($Item in $MyList)
{
## inlinescript
inlinescript
{
## function func1 declaration
function func1{
param($MyItem)
Write-Output ('Func 1, MyItem {0}' -f $MyItem)
logMessage $MyItem
}
## function logMessage declaration
function logMessage{
param($MyItem)
Write-Output ('logMessage, MyItem: {0}' -f $MyItem)
}
## func1 call with $Using:Item statement
## $Using: prefix allows us to call items that are in the workflow scope but not in the inlinescript scope.
func1 $Using:Item
}
}
}
Example call to this workflow would look like this
PS> $MyList = 1,2,3
PS> test $MyList
Func 1, MyItem 3
Func 1, MyItem 1
Func 1, MyItem 2
logMessage, MyItem: 3
logMessage, MyItem: 2
logMessage, MyItem: 1
You will notice (and as expected) the output order is random since it was run in parallel.