In this application a program is using the .Net System.Diagnostics.Process class to invoke PowerShell with redirected I/O. The script sent to PowerShell via standard input creates a ScriptBlock variable, encodes it in base 64 and launches a PowerShell as an elevated process.
In both the “outer script” and the “inner” scriptblock, flow control statements don’t appear to execute, although all single line PowerShell statements do execute. While I am able to achieve my objective the inability to have flow control weakens the robustness of the code. Please see the code below.
Can anyone suggest why flow control statement don’t execute?
on line 15 you are actually comparing “MachineName\DomainServiceAccount” to “LocalSystem”. That’s always gonna be false.
on line 22 and 29 you check if the last command completed successfully. As both these command are variable assignments they’re likely always be successful. And btw: because $? returns a boolean you could write it this way: “if ( -not $?)”
… I did not pay close attention yet but when you work with scriptblocks and variables you have to be careful with the scopes and the variables. Outside variables are not available inside a scriptblock by default.
The variables are a copy & paste error into the forum post. I have a templated script that uses variable substitution at runtime to fill in variable values. I wasn’t thinking when I pasted $outputFileName into the outer script block logic.
with regards to the expression: $serviceAccount -eq “LocalSystem”
and the initialization: $serviceAccount = “MachineName\DomainServiceAccount”
you are correct! this is another case of a bad example as the $serviceAccount variable is provided by an outside source.
what is interesting (frustrating) is that, using the code provided in this post, the snippet:
The code shown reeks of desperation in an attempt to use the old method of writing messages to a “log” since I can’t easily perform real-time debugging within the execution context:
BizApp -> invoke hidden powershell process with redirected I/O -> invoke powershell elevated.
using redirected input avoids the need to set Execution policies for scripting (the intention is to use this in third party environments where there will be a mix of policies as we move from environment to environment (resulting from different IT practices)).
Thanks for your time & consideration.
Kind regards, April
I’m still confused. First you start to fill in a varaiable by calling Get-Credential. Of course it prompts for credentials … you told it to.
Then you check if this variable assignment completed successfully. As long as you enter something in the credentials prompt it always will. So the condition you use will likely never be true.
Sorry for the confusion. You are correct the code snippet I provided has logic “dead-ends”. My mistake.
Also important note - this is a PowerShell 4.0 system (on Win 2012 R2)
The snippet is overly contrived. As a templated script the account is inserted into the template just prior to execution. The value could be: LocalSystem or a Domain service account. The desire is to: 1) if it is “LocalSystem” generate credentials and no need to prompt the user (in the BizApp). If the account specified is not recognized as “LocalSystem” then prompt the user for the domain service account credentials. Unfortunately the if-the-else logic does execute at all (the core of the issue) based on diagnostic inspection.
Case 1 - Local System Account - there should be no prompt for credentials, but a prompt does occur when the New-Service line is reached. There is no evidence that $serviceCredentials was populated via the if statement (if ($serviceAccount -eq “LocalSystem”) )
$serviceAccount = "LocalSystem"
$serviceStartupType = "Manual"
$serviceName = "SomeWindowsService"
$serviceDisplay = "Some Windows Service"
$serviceDescription = "Not some other service"
$servicePath = "c:\bin\someservice.exe"
$machineName = "MachineName"
$sspw = ConvertTo-SecureString "ignoreThisPassword" -asPlainText -Force
if ($serviceAccount -eq "LocalSystem")
{
$serviceCredentials = new-object -typename System.Management.Automation.PSCredential -argumentlist "$machineName\LocalSystem",$sspw
}
else
{
$serviceCredentials = Get-Credential -UserName $serviceAccount -Message "Enter Service Credentials"
if ($? -eq $false )
{
$outLog += "Get Creds failed`n"
}
}
$catchOutput = New-Service -Name $serviceName -BinaryPathName $servicePath -StartupType $serviceStartupType -Credential $serviceCredentials -DisplayName $serviceDisplay -Description $serviceDescription
</pre>
Case 2 - Domain Service Account - there should be a prompt for credentials with the Prompt Message: Enter Domain Service Account Service Credentials, but this prompt does not occur and there is no evidence that $serviceCredentials was populated via the else clause of the if statement: if ($serviceAccount -eq "LocalSystem")
Instead we get a prompt for credentials at the New-Service line
<pre>
$serviceAccount = "MyDomainServiceAccount"
$serviceStartupType = "Manual"
$serviceName = "SomeWindowsService"
$serviceDisplay = "Some Windows Service"
$serviceDescription = "Not some other service"
$servicePath = "c:\bin\someservice.exe"
$machineName = "MachineName"
$sspw = ConvertTo-SecureString "ignoreThisPassword" -asPlainText -Force
if ($serviceAccount -eq "LocalSystem")
{
$serviceCredentials = new-object -typename System.Management.Automation.PSCredential -argumentlist "$machineName\LocalSystem",$sspw
}
else
{
$serviceCredentials = Get-Credential -UserName $serviceAccount -Message "Enter Domain Service Account Service Credentials"
if ($? -eq $false )
{
$outLog += "Get Creds failed`n"
}
}
$catchOutput = New-Service -Name $serviceName -BinaryPathName $servicePath -StartupType $serviceStartupType -Credential $serviceCredentials -DisplayName $serviceDisplay -Description $serviceDescription
Everything I have done indicates that if-then-else statements are not executing in this execution context. I can take the contents of the $innerScriptBlock and type them line by line at the Powershell command line or as a script in the IDE and they work as expected.
Sorry for the confusion. You are correct the code snippet I provided has logic “dead-ends”. My mistake.
Also important note - this is a PowerShell 4.0 system (on Win 2012 R2)
The snippet is overly contrived. As a templated script the account is inserted into the template just prior to execution. The value could be: LocalSystem or a Domain service account. The desire is to: 1) if it is “LocalSystem” generate credentials and no need to prompt the user (in the BizApp). If the account specified is not recognized as “LocalSystem” then prompt the user for the domain service account credentials. Unfortunately the if-the-else logic does execute at all (the core of the issue) based on diagnostic inspection.
Case 1 - Local System Account - there should be no prompt for credentials, but a prompt does occur when the New-Service line is reached. There is no evidence that $serviceCredentials was populated via the if statement
$serviceAccount = "LocalSystem"
$serviceStartupType = "Manual"
$serviceName = "SomeWindowsService"
$serviceDisplay = "Some Windows Service"
$serviceDescription = "Not some other service"
$servicePath = "c:\bin\someservice.exe"
$machineName = "MachineName"
$sspw = ConvertTo-SecureString "ignoreThisPassword" -asPlainText -Force
if ($serviceAccount -eq "LocalSystem")
{
$serviceCredentials = new-object -typename System.Management.Automation.PSCredential -argumentlist "$machineName\LocalSystem",$sspw
}
else
{
$serviceCredentials = Get-Credential -UserName $serviceAccount -Message "Enter Service Credentials"
if ($? -eq $false )
{
$outLog += "Get Creds failed`n"
}
}
$catchOutput = New-Service -Name $serviceName -BinaryPathName $servicePath -StartupType $serviceStartupType -Credential $serviceCredentials -DisplayName $serviceDisplay -Description $serviceDescription
</pre>
Case 2 - Domain Service Account - there should be a prompt for credentials with the Prompt Message: Enter Domain Service Account Service Credentials, but this prompt does not occur and there is no evidence that $serviceCredentials was populated via the else clause of the if statement.
<pre>
$serviceAccount = "MyDomainServiceAccount"
$serviceStartupType = "Manual"
$serviceName = "SomeWindowsService"
$serviceDisplay = "Some Windows Service"
$serviceDescription = "Not some other service"
$servicePath = "c:\bin\someservice.exe"
$machineName = "MachineName"
$sspw = ConvertTo-SecureString "ignoreThisPassword" -asPlainText -Force
if ($serviceAccount -eq "LocalSystem")
{
$serviceCredentials = new-object -typename System.Management.Automation.PSCredential -argumentlist "$machineName\LocalSystem",$sspw
}
else
{
$serviceCredentials = Get-Credential -UserName $serviceAccount -Message "Enter Domain Service Account Service Credentials"
if ($? -eq $false )
{
$outLog += "Get Creds failed`n"
}
}
$catchOutput = New-Service -Name $serviceName -BinaryPathName $servicePath -StartupType $serviceStartupType -Credential $serviceCredentials -DisplayName $serviceDisplay -Description $serviceDescription
Everything I have done indicates that if-then-else statements are not executing in this execution context. I can take the contents of the $innerScriptBlock and type them line by line at the Powershell command line or as a script in the IDE and they work as expected.
@Olaf - the $outLog messages are capturing execution of different steps. I have tried lots of variations and they all indicate that if-then-else wasn’t executed.
@js - glad that is worked - it at least demonstrates that it should work! I will try using the semi-colon delimiters and compress the flow-control to a single line.
There may be subtleties between use of command line parameters (?). For example when using redirected I/O does: “-Command -” differ from “-File -”? is there a difference? (IDK)
The original blog post only shows example using single line commands. My script blocks show that all single line commands within the scriptblock work, but multi-line flow-control lines doesn’t (hence maybe compress to a single line and use ‘;’ line delimiter as @js demonstrated). Observations also indicate that the outer script via standard input is also not executing the while loop.