Powershell Workflow DynamicActivity Compiler Error '.' Expected Using $Date Variable - powershell

Usually powershell errors give me something to go on, but this one has thrown me through a loop. To test, execute the following code:
workflow test-date{
$Date = Get-Date -format yyyyMMddHHmm -Verbose
Write-Output $Date
}
The error I get is:
The workflow 'test-date' could not be started: The following errors were encountered while processing the workflow tree:
'DynamicActivity': The private implementation of activity '1: DynamicActivity' has the following validation error: Compiler error(s) encountered processing expression "Date".
'.' expected.
At line:383 char:21
+ throw (New-Object System.Management.Automation.ErrorRecord $ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (System.Manageme...etersDictionary:PSBoundParametersDictionary) [], RuntimeException
+ FullyQualifiedErrorId : StartWorkflow.InvalidArgument
The part that really get's me is that it says it's expecting a "." somewhere. I don't know where it's expecting to find a '.'. I've googled the error which didn't produce anything relevant to my situation. I'd like to know more about what this error means, and why I'm getting it. It'd be nice if someone knows the solution too.

I don't know the actual cause, but if you rename the variable $date to $something_else, the workflow works.
Such as:
Workflow Test-Date {
$aDate = Get-Date Get-Date -format yyyyMMddHHmm
$New_Date = Get-Date -format yyyyMMddHHmm
Write-Output $aDate
Write-Output $New_Date
}
I assume it's triggering a keyword in .NET during the conversion.

From : http://blogs.msdn.com/b/powershell/archive/2012/07/21/new-workflow-makeiteasy-authoring-workflows-using-powershell-extended-syntax.aspx
By default, each command in a workflow is executed with no PowerShell state sharing
Don't know how the error pertains to this but this would certainly be an issue since
Variables created by one command are not visible to the next command.
So what you should have to do is use an inlinescript block
workflow test-date{
inlinescript{
$Date = Get-Date -format "yyyyMMddHHmm" -Verbose
Write-Output $Date
}
}
Output:
PS C:\Users\mcameron> test-date
201501231144

Related

Why is Export-PnPFlows Skipping Flows?

This is my first post, so please pardon formatting errors!
I've been trying to export my tenant's Power Automate flows via Export-PnPFlow. I have a few hundred flows, so doing it by hand isn't really feasible.
The script works well enough for some flows, but is throwing an error for others, but I can't see why.
It does not seem to be caused by if it's enabled/disabled, owned by a certain user, in a certain environment, or in/out of a solution.
The ones that work, work perfectly; the others give the following error:
Export-PnPFlow : {"error":{"code":"ConnectionAuthorizationFailed","message":"The caller object id is '08#####-#####-####-###'. Connection '2#####-#####-####-####' to 'shared_logicflows' cannot be used to activate this flow, either because
this is not a valid connection or because it is not a connection you have access permission for. Either replace the connection with a valid connection you can access or have the connection owner activate the flow, so the connection is shared with you in the context of
this flow."}}
At C:\Users\jutrust\script.ps1:13 char:21
+ ... Export-PnPFlow -Environment $environment -Identity $flow. ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Export-PnPFlow], HttpRequestException
+ FullyQualifiedErrorId : System.Net.Http.HttpRequestException,PnP.PowerShell.Commands.PowerPlatform.PowerAutomate.ExportFlow
My question is, is it possible that these flows are deleted and that's why I get this error? If so, how can I check?
Code below.
Connect-PnPOnline -url https://########.sharepoint.com
$environments = get-pnppowerplatformenvironment
foreach($environment in $environments)
{
$flows = Get-PnPFlow -Environment $environment -AsAdmin
foreach ($flow in $flows)
{
$filename = $flow.Properties.DisplayName.Replace(" ", "")
$timestamp = Get-Date -Format "yyyymmddhhmmss"
$exportPath = "$($filename)_$($timestamp)"
$exportPath = $exportPath.Split([IO.Path]::GetInvalidFileNameChars()) -join '_'
Export-PnPFlow -Environment $environment -Identity $flow.Name | Out-File "C:\Users\jutrust\documents\$exportPath.json"
}
}
Help!
Edit: Updated error code

using powershell invoke-expression to run code output

I have been doing a lot of reading on invoke-expression (also known as iex) and I'm having trouble getting it to work for me.
My understanding is, it will run any powershell code you give to it. However, when I run my tests on it, it does not run the code.
Example:
## testcode.ps1
$myvar = "i am here"
if ($myvar -ne $null) {
"($myvar) variable is Full"
} else {
"($myvar) variable is Empty"
}
Now, if I cat(gc) this file and I pass it to iex, it outputs a bunch of errors. Same thing happens when I save the code into a variable and then feed the variable to iex. Neither works.
Despite the fact that I've tried numerous examples, I feel there's something minor I'm doing wrong that I'm hoping someone can point out for me.
I'm new to Windows scripting, so please bear with me. These are the results of the tests I performed:
First Test:
PS C:\Users\J> gc C:\Users\J\testcode.ps1 | iex
Invoke-Expression : Cannot bind argument to parameter 'Command' because it is an empty string.
At line:1 char:31
+ cat C:\Users\J\testcode.ps1 | iex
+ ~~~
+ CategoryInfo : InvalidData: (:PSObject) [Invoke-Expression], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.Commands.InvokeExpressionCommand
iex : At line:1 char:23
+ if ($myvar -ne $null) {
+ ~
Missing closing '}' in statement block or type definition.
At line:1 char:31
+ cat C:\Users\J\testcode.ps1 | iex
+ ~~~
+ CategoryInfo : ParserError: (:) [Invoke-Expression], ParseException
+ FullyQualifiedErrorId : MissingEndCurlyBrace,Microsoft.PowerShell.Commands.InvokeExpressionCommand
Second Test:
PS C:\Users\J> $scriptBlock = gc C:\Users\J\testcode.ps1
PS C:\Users\J>
PS C:\Users\J> iex -Command "$scriptBlock"
iex : At line:1 char:23
+ $myvar = "i am here" if ($myvar -ne $null) { "($myvar) variable ...
+ ~~
Unexpected token 'if' in expression or statement.
At line:1 char:1
+ iex -Command "$scriptBlock"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ParserError: (:) [Invoke-Expression], ParseException
+ FullyQualifiedErrorId : UnexpectedToken,Microsoft.PowerShell.Commands.InvokeExpressionCommand
PS C:\Users\J>
I'm aware that I can just run the file containing the code. However, I need help figuring out how iex works and what it is I'm doing wrong.
Please kindly advise.
First things first:
Invoke-Expression should generally be avoided and used only as a last resort, due to its security risks. In short: avoid it, if possible, given that superior alternatives are usually available. If there truly is no alternative, only ever use it on input you either provided yourself or fully trust - see this answer.
For the record: in the case at hand, the superior alternative is to directly invoke the script file:
# Prepend `& `, if the script file path is quoted or references a variable.
C:\Users\J\testcode.ps1
Invoke-Expression (iex) accepts multiple strings via the pipeline, and evaluates each individually, as a self-contained script.
Therefore, you must provide the contents of your script as a whole, as a single string, which is what Get-Content's (gc's) -Raw switch does[1]:
Get-Content -Raw C:\Users\J\testcode.ps1 | Invoke-Expression
Alternatively, pass the script-file contents as an argument:
Invoke-Expression (Get-Content -Raw C:\Users\J\testcode.ps1)
Note that passing the string to evaluate as an argument truly only accepts a single string, so the command would fail without -Raw.
[1] By default, the Get-Content cmdlet reads a file line by line, passing each line through the pipeline as it is being read.
$myvar = "I'm Here"
#Using Invoke-Expression - Accepts a STRING as Input
$SBCode = 'if ($Null -ne $myvar) {"($myvar) variable is Full"}' +
'else {"`$myvar variable is Empty"}'
Clear-Host
"Before Invoke-Expression `$myvar = $myvar"
$Result = Invoke-Expression $SBCode
"Invoke-Expression Returns: $Result"
#Using Invoke-Command - Accepts Script Block as Input
$SBCode = {
if ($myvar -ne $null) {
"($myvar) variable is Full"
}
else {
"`$myvar variable is Empty"
}
} #End $SBCode Script Block
"Before Invoke-Command `$myvar = $myvar"
$Result = Invoke-Command -ScriptBlock $SBCode
"Invoke-Command Returns: $Result"
Results:
Before Invoke-Expression $myvar = I'm Here
Invoke-Expression Returns: (I'm Here) variable is Full
Before Invoke-Command $myvar = I'm Here
Invoke-Command Returns: (I'm Here) variable is Full
# After changing $MyVar = $Null
Before Invoke-Expression $myvar =
Invoke-Expression Returns: $myvar variable is Empty
Before Invoke-Command $myvar =
Invoke-Command Returns: $myvar variable is Empty
HTH
You can use out-string to convert output into string.
cat C:\Users\J\testcode.ps1 | out-string | Invoke-Expression

PowerShell try-catch loses repeated access errors

Try-catch appears to not reliably trap all errors. Try-catch on get-ChildItem does not report all access errors that are reported outside of try-catch.
Edit: It is not true that try-catch is being unreliable, and there is a sound reason why it reported only one error. See my comment to the accepted answer to see my misunderstanding behind this question.
In Windows 10 Pro 64, running PowerShell 5.1, this script:
$sScriptName = "ErrorTest.ps1"
write-host ("I will get the file structure of ""C:\Windows\System32"", but this yields two access-denied errors:")
$aoFileSystem = #(get-ChildItem "C:\Windows\System32" -recurse -force)
write-host ("I will now do it again and trap for those errors. But I only get one of them:")
try {$aoFileSystem = #(get-ChildItem $sPath -recurse -force -ErrorAction Stop)}
catch [System.UnauthorizedAccessException]
{$sErrorMessage = $_.ToString()
write-host ("System.UnauthorizedAccessException: " + $sErrorMessage.substring(20, $sErrorMessage.length - 32))}
running in the ISE as administrator, gets this output:
PS C:\WINDOWS\system32> D:\<path>\ErrorTest.ps1
I will get the file structure of "C:\Windows\System32", but this yields two access-denied errors:
get-ChildItem : Access to the path 'C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\INetCache\Content.IE5' is denied.
At D:\<path>\ErrorTest.ps1:5 char:19
+ ... aoFileSystem = #(get-ChildItem "C:\Windows\System32" -recurse -force)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (C:\Windows\Syst...che\Content.IE5:String) [Get-ChildItem], UnauthorizedAccessException
+ FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
get-ChildItem : Access to the path 'C:\Windows\System32\LogFiles\WMI\RtBackup' is denied.
At D:\<path>\ErrorTest.ps1:5 char:19
+ ... aoFileSystem = #(get-ChildItem "C:\Windows\System32" -recurse -force)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (C:\Windows\Syst...es\WMI\RtBackup:String) [Get-ChildItem], UnauthorizedAccessException
+ FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
I will now do it again and trap for those errors. But I only get one of them:
System.UnauthorizedAccessException: C:\WINDOWS\system32\config\systemprofile\AppData\Local\Microsoft\Windows\INetCache\Content.IE5
PS C:\WINDOWS\system32>
I want to log the access errors, but when I try to trap for them, I only get the first one. What do I need to do to get the second one to show up when I trap for them?
Edit: I got a message telling me that I need to edit my question to explain how it's different from Can PowerShell trap errors in GetChildItem and continue looping?. So I'll repeat here what I said in response to the comment below from Scepticalist. That question is for get-ChildItem in a ForEach loop. My problem does not involve such a loop. Still, I tried something from the accepted answer there, using -ErrorAction SilentlyContinue, but that hides the errors without trapping them. However, in the solution I found and am about to post as an answer, I do use -ErrorAction SilentlyContinue in combination with -ErrorVariable.
The errors emitted by Get-ChildItem "C:\Windows\System32" -recurse -force are non-terminating errors, and, given that such errors by definition don't terminate a command, a single command may emit multiple errors of this type.
All non-terminating errors emitted by a cmdlet can be captured in a designated variable whose name you pass to the -ErrorVariable common parameter.
Additionally, all errors (both non-terminating and terminating ones) are by default recorded in the session-global automatic $Error collection variable.
try / catch only acts on terminating errors, so by default it has no effect on commands that emit non-terminating errors only, such as Get-ChildItem "C:\Windows\System32" -recurse -force
You can instruct the command to turn the first non-terminating error encountered into a terminating one by adding -ErrorAction Stop (using the -ErrorAction common parameter), in which case an enclosing try / catch does catch the error, but note that execution then stops after the first error encountered either way.
Caveat: There are two types of terminating errors (and the fact that there are may be a historical accident); while both types can be caught with try / catch, their default behavior differs:
Statement-terminating errors, which only terminate the statement at hand and, after emitting an error message, by default continue script execution; typically, these errors are emitted by (compiled) cmdlets if they encounter errors that aren't limited to the input at hand and doesn't allow them to continue processing further input.
Script-terminating errors (runspace-terminating errors), which abort processing altogether by default. PowerShell code that uses the Throw statement generates such errors, as does passing -ErrorAction Stop to a command that emits non-terminating errors.
Caveat: While -ErrorAction Stop has no effect on statement-terminating errors, the seemingly equivalent preference-variable setting, $ErrorActionPreference = 'Stop', unexpectedly promotes statement-terminating errors to script-terminating ones.
Further reading:
For a comprehensive overview of PowerShell's - bewilderingly complex - error handling, see this GitHub docs issue.
As for when to report a terminating vs. a non-terminating error when authoring commands, see this answer.
I did some research on the ErrorAction parameter, and doing that, discovered the ErrorVariable parameter. (Both are common parameters, so they don't show up in the documentation of get-ChildItem.) And from there, I was able to figure out how to correctly trap for the errors.
The new script below shows four ways of doing this. Methods 2 and 3 work okay. Method 4 doesn't work because it incorrectly attempts to use the parameter InputObject in ForEach-object. Method 1, the original try-catch method, doesn't work and I still don't know why. This is troubling because it's important that error trapping works as expected. If I did not know to expect two errors, I would not have known that try-catch was not giving me the right output.
I am not accepting this (my own) answer because, although the script below shows two methods to correctly trap for these errors, it would still be better if someone can explain why try-catch does not work for this.
New script:
$sScriptName = "ErrorTest.ps1"
$sPath = "C:\Windows\System32"
write-host ("I will get the file structure of """ + $sPath + """, but this yields two access-denied errors:")
$aoFileSystem = #(get-ChildItem $sPath -recurse -force)
write-host ("I will now do it again and trap for those errors.")
write-host ("`r`nMethod 1: Original method using try-catch (incorrect results; only finds one of the two errors; why?):")
try {$aoFileSystem = #(get-ChildItem $sPath -recurse -force -ErrorAction Stop)}
catch [System.UnauthorizedAccessException]
{$sErrorMessage = $_.ToString()
write-host ("System.UnauthorizedAccessException: " + $sErrorMessage.substring(20, $sErrorMessage.length - 32))}
write-host ("`r`nGet array for Methods 2 to 4.")
$aoFileSystem = #(get-ChildItem $sPath -recurse -force -ErrorAction SilentlyContinue -ErrorVariable aoChildItemError)
write-host ("`r`nMethod 2: Output by piping to ForEach-object (correct results):")
$aoChildItemError |
ForEach-object `
{$oErrorRecord = $_
write-host ($oErrorRecord.CategoryInfo.reason + ": """ + $oErrorRecord.TargetObject + """")}
write-host ("`r`nMethod 3: Output by for loop (correct results):")
for ($nCount = 0; $nCount -lt $aoChildItemError.count; $nCount++)
{$oErrorRecord = $aoChildItemError[$nCount]
write-host ($oErrorRecord.CategoryInfo.reason + ": """ + $oErrorRecord.TargetObject + """")}
write-host ("`r`nMethod 4: Output by ForEach-object loop without pipeline (incorrect results because it incorrectly attempts to use the parameter ""InputObject"" in ""ForEach-object""):")
ForEach-object -InputObject $aoChildItemError `
{$oErrorRecord = $_
write-host ($oErrorRecord.CategoryInfo.reason + ": """ + $oErrorRecord.TargetObject + """")}
New output:
PS C:\WINDOWS\system32> D:\_\z-temp\ErrorTest.ps1
I will get the file structure of "C:\Windows\System32", but this yields two access-denied errors:
get-ChildItem : Access to the path 'C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\INetCache\Content.IE5' is denied.
At D:\_\z-temp\ErrorTest.ps1:6 char:19
+ $aoFileSystem = #(get-ChildItem $sPath -recurse -force)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (C:\Windows\Syst...che\Content.IE5:String) [Get-ChildItem], UnauthorizedAccessException
+ FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
get-ChildItem : Access to the path 'C:\Windows\System32\LogFiles\WMI\RtBackup' is denied.
At D:\_\z-temp\ErrorTest.ps1:6 char:19
+ $aoFileSystem = #(get-ChildItem $sPath -recurse -force)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (C:\Windows\Syst...es\WMI\RtBackup:String) [Get-ChildItem], UnauthorizedAccessException
+ FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
I will now do it again and trap for those errors.
Method 1: Original method using try-catch (incorrect results; only finds one of the two errors; why?):
System.UnauthorizedAccessException: C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\INetCache\Content.IE5
Get array for Methods 2 to 4.
Method 2: Output by piping to ForEach-object (correct results):
UnauthorizedAccessException: "C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\INetCache\Content.IE5"
UnauthorizedAccessException: "C:\Windows\System32\LogFiles\WMI\RtBackup"
Method 3: Output by for loop (correct results):
UnauthorizedAccessException: "C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\INetCache\Content.IE5"
UnauthorizedAccessException: "C:\Windows\System32\LogFiles\WMI\RtBackup"
Method 4: Output by ForEach-object loop without pipeline (incorrect results because it incorrectly attempts to use the parameter "InputObject" in "ForEach-object"):
UnauthorizedAccessException UnauthorizedAccessException : " C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\INetCache\Content.IE5 C:\Window
s\System32\LogFiles\WMI\RtBackup "
PS C:\WINDOWS\system32>
Quite simple! You specified the parameter -ErrorAction Stop in your second directory access statement. Switch it to -ErrorAction Continue and you get both error messages.

Weird PowerShell problem: [ref] cannot be applied to a variable that does not exist

My Powershell script exited with "[ref] cannot be applied to a variable that does not exist" after running a while (it actually worked for a while)
The code snippet is something like
function outputData(...) {
$data = $null
if ($outputQueue.TryTake([ref] $data, 1000) -eq $false) {
continue
}
Write-Host $data
}
The detail errors thrown at the end are as below:
[ref] cannot be applied to a variable that does not exist.
At C:\Program Files\mfile.ps1:1213 char:13
+ if ($outputQueue.TryTake([ref] $data, 1000) -eq $ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (data:VariablePath) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : NonExistingVariableReference
May i ask if any thoughts about the cause ?
Thanks !
While error messages aren't always helpful, this one is:
It tells you that the $data variable you're trying to use with [ref] must already exist, i.e., must have been created explicitly, which in PowerShell means:
creating it by assigning a value to it - even if that value is $null,
or using New-Variable to create it.
A simplified example:
$data = $null # create variable $data
# OK to use $data with [ref], now that it exists.
# $data receives [int] value 10 in the process.
[int]::TryParse('10', [ref] $data)

Function prompts value for Add-Content and treats .txt file as directory

I am writing a PowerShell script for creating error status files. The info I need is defined in a global variable ($info) and is returning the correct string I need it to. The type is System.String.
function Add-Fail
{
$date = Get-Date -f "yyyy-MM-dd"
Add-Content \\Path\To\FailMonitor\${date}.txt $info
}
I have also tried passing the variable like this:
function Add-Fail ($info)
{
$date = Get-Date -f "yyyy-MM-dd"
Add-Content \\Path\To\FailMonitor\${date}.txt $info
}
and calling it like this where info is a string:
Add-Fail($info)
and this
Add-Fail -info $info
For both of these cases, I get the same output which is asking for a value. If I input values, it keeps prompting me for the next one in an infinite array. If I don't input one and just hit enter, it gives me an error that has no information, and even refers to my Add-Content line on the complete wrong line number.
Output (Edited out username):
cmdlet Add-Content at command pipeline position 1
Supply values for the following parameters:
Value[0]: 1
Value[1]: 2
Value[2]:
Add-Content : The filename, directory name, or volume label syntax is incorrect.
At C:\Users\********\Documents\Workspace\Error_Monitor.ps1:40 char:5
+ Add-Content \\nserver\scsi\HPQ\FailMonitor\$date\
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (\\nserver\scsi\...tor\2016-10-18\:String) [Add-Content], IOException
+ FullyQualifiedErrorId : GetContentWriterIOError,Microsoft.PowerShell.Commands.AddContentCommand
As you can see, it refers to my date as a directory. For reference, my Add-Content expression is on line 45, char 5. It sees my .txt file as a directory and treats it as such. The text file is not a directory at all. It isn't a variable problem either as I have cleared the variables. I have also tried no brackets around the date, but that didn't work either.
Why is PowerShell behaving like this? What am I missing?