In Powershell, how can a non-admin user run a process as different user and then read the process exit code? - powershell-5.0

I need to run an executable as a particular user during a build task in Azure Pipelines. To determine whether to fail the build, I need to read that process's exit code.
I can't run the script from an explicitly administrative session.
When I view the process handle using the code below, the exit code is always empty. I'm positive that the executable is returning exit codes (it was written in-house).
In addition to the code below, I also tried using $LASTEXITCODE, but it won't be set unless I run the executable directly (as opposed to using Start-Process).
Is there a way to view the exit code of that process?
$process = Start-Process -FilePath $pathToExe -ArgumentList $argsString -Credential $credential -PassThru
# I tried waiting like this as well
#while ($process.HasExited -ne $true) {
# Start-Sleep -Seconds 5
#}
$process.WaitForExit()
Write-Host "Process exit code: $($process.ExitCode)"
An approach similar to the one below can be used to access the exit code of a process started by one user in the context of another, but it requires an Admin session.
$scriptBlock = {
param($exePath, $exeArgs)
$process = Start-Process -FilePath $exePath -ArgumentList $exeArgs -PassThru
$process.WaitForExit()
return $process.ExitCode
}
$runAsUserSession = New-PSSession -Credential $credential
$exitCode = Invoke-Command -ScriptBlock $scriptBlock -ArgumentList #($pathToExe, $argsString) -Session $runAsUserSession
Write-Host "Process exit code: $exitCode"

Try invoke method:
$process = Start-Process -FilePath $pathToExe -ArgumentList $argsString -Credential $credential -PassThru
$process.WaitForExit()
#while ($process.HasExited -ne $true) {
# Start-Sleep -Seconds 5
#}
Write-Host "Process exit code:" $process.ExitCode

Related

Azure automation runbook Completed before running the code

I have a situation where in the Azure automation runbook results in 'Completed' state and does not run the 'actual' code. I have pasted the code below. It creates a Event Hub inside a Namespace. The code works perfectly executing in local machine but it does not execute in Runbook.
I have written a 'write-output "Declaring local variables for use in script"' --> to check if the printing is working. However, the code is not going beyond that. I am sure, I am missing some thing. Kindly help me.
Param(
[Parameter(Mandatory=$true)]
[string] $NameSpaceNameName,
[Parameter(Mandatory=$true)]
[string[]] $EventhubNames,
[Parameter(Mandatory=$true)]
[string] $ProjectId,
[Parameter(Mandatory=$true)]
[int] $PartitionCount,
[Parameter(Mandatory=$true)]
[string]$Requested_for,
[Parameter(Mandatory=$true)]
[string]$Comments
)
## Login to Azure using RunAsAccount
$servicePrincipalConnection = Get-AutomationConnection -Name 'AzureRunAsConnection'
Write-Output ("Logging in to Az Account...")
Login-AzAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
write-output "Declaring local variables for use in script"
## Declaring local variables for use in script
$Creation_date = [System.Collections.ArrayList]#()
$ResourceGroups = Get-AzResourceGroup
$provided_name_space_exists = $false
## Change context to Platform subscription
select-azsubscription -subscription "GC302_Sub-platform_Dev"
## Create Event Hub
foreach($Resourcegroup in $ResourceGroups){
Write-Host("Processing the Resource Group: {0} " -f $Resourcegroup.ResourceGroupName)
$EventhubNameSpaces = Get-AzEventHubNamespace -ResourceGroupName $ResourceGroup.ResourceGroupName
# Iterate over each Namespace. Fetch the Resource Group that contains the provided Namespace
foreach($EHNameSpace in $EventhubNameSpaces){
if($EHNameSpace.Name -eq $NameSpaceName){
$provided_name_space_exists = $true
Write-Host ("Found the provided Namespace in resource group: {0}" -f $Resourcegroup.ResourceGroupName)
Write-Output ("Found the provided Namespace in resource group: {0}" -f $Resourcegroup.ResourceGroupName)
$nameSpace_resource_group = $ResourceGroup.ResourceGroupName
# Fetch the existing Event Hubs in the Namespace
$existing_event_hubs_list = Get-AzEventHub -Namespace $EHNameSpace.Name -ResourceGroupName $nameSpace_resource_group
## Check provided EH for uniqueness
if($existing_event_hubs_list.Count -le 1000){
for($i = 0;$i -lt $EventhubNames.Count;$i++){
if($existing_event_hubs_list.name -notcontains $EventhubNames[$i]){
$EventHub = New-AzEventHub -ResourceGroupName $nameSpace_resource_group -Namespace $EHNameSpace.Name -Name $EventhubNames[$i] -PartitionCount $PartitionCount
$date = $EventHub.CreatedAt
$Creation_date+= $date.GetDateTimeFormats()[46]
}else{
Write-Host ("Event hub: '{0}' already exists in the NameSpace: {1}. skipping this Event hub creation" -f $EventhubNames[$i], $EHNameSpace.Name)
}
}
}else{
Write-Host ("The Namespace - {0} has Event Hubs count greater or equal to 1000." -f $EHNameSpace.Name)
Write-Host ("Please refer the Link for Eevent Hubs quota/limit: 'https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-quotas#event-hubs-dedicated---quotas-and-limits'")
exit
}
}
}
}
# Print a message that Namespace does not exist
if($provided_name_space_exists -eq $false){
Write-Host ("Provided NameSpace: {0} does not exist." -f $NameSpaceName)
exit
}
Screenshot:
You have $NameSpaceNameName in the parameters section of the runbook but later in the runbook at 50th line you have $NameSpaceName which is not the same as mentioned in parameters section. Correct it and then it should work as expected. One suggestion is to always have an else block wherever you have if block to overcome such issues in future.

Powershell expand variable in scriptblock

I am trying to follow this article to expand a variable in a scriptblock
My code tries this:
$exe = "setup.exe"
invoke-command -ComputerName $j -Credential $credentials -ScriptBlock {cmd /c 'C:\share\[scriptblock]::Create($exe)'}
How to fix the error:
The filename, directory name, or volume label syntax is incorrect.
+ CategoryInfo : NotSpecified: (The filename, d...x is incorrect.:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
+ PSComputerName : remote_computer
You definitely don't need to create a new script block for this scenario, see Bruce's comment at the bottom of the linked article for some good reasons why you shouldn't.
Bruce mentions passing parameters to a script block and that works well in this scenario:
$exe = 'setup.exe'
invoke-command -ComputerName $j -Credential $credentials -ScriptBlock { param($exe) & "C:\share\$exe" } -ArgumentList $exe
In PowerShell V3, there is an even easier way to pass parameters via Invoke-Command:
$exe = 'setup.exe'
invoke-command -ComputerName $j -Credential $credentials -ScriptBlock { & "C:\share\$using:exe" }
Note that PowerShell runs exe files just fine, there's usually no reason to run cmd first.
To follow the article, you want to make sure to leverage PowerShell's ability to expand variables in a string and then use [ScriptBlock]::Create() which takes a string to create a new ScriptBlock. What you are currently attempting is to generate a ScriptBlock within a ScriptBlock, which isn't going to work. It should look a little more like this:
$exe = 'setup.exe'
# The below line should expand the variable as needed
[String]$cmd = "cmd /c 'C:\share\$exe'"
# The below line creates the script block to pass in Invoke-Command
[ScriptBlock]$sb = [ScriptBlock]::Create($cmd)
Invoke-Command -ComputerName $j -Credential $credentials -ScriptBlock $sb

kvm.ps1 cannot be loaded because running scripts is disable on this system

I am trying to run the Asp.net vNext sample application.
But when i try to execute the command
kvm list
It gives me the error message
kvm.ps1 cannot be loaded because running scripts is disable on this system
I tried to change to execution policy also. But still i am getting the same error.
Try this
execute this command on console
**"powershell Set-ExecutionPolicy RemoteSigned"**,
after that run the commands bellow
$tempPath = Join-Path $env:TEMP "kvminstall"
$kvmPs1Path = Join-Path $tempPath "kvm.ps1"
$kvmCmdPath = Join-Path $tempPath "kvm.cmd"
Write-Host "Using temporary directory: $tempPath"
if (!(Test-Path $tempPath)) { md $tempPath | Out-Null }
$webClient = New-Object System.Net.WebClient
Write-Host "Downloading KVM.ps1 to $kvmPs1Path"
$webClient.DownloadFile('https://raw.githubusercontent.com/aspnet/Home/master/kvm.ps1', $kvmPs1Path)
Write-Host "Downloading KVM.cmd to $kvmCmdPath"
$webClient.DownloadFile('https://raw.githubusercontent.com/aspnet/Home/master/kvm.cmd', $kvmCmdPath)
Write-Host "Installing KVM"
& $kvmCmdPath setup
You could try the powershell command Unblock-File?

Powershell BitsTransfer https basic authentication syntax

I'm new to PowerShell scripting. I'm struggling with the MS documentation and finding few examples to work with.
I'm trying to automate the weekly download of a large txt file from ntis.gov with a BitsTransfer script. I'm using .ps1 script because apparently SSIS can't do this without writing .NET code.
Access to this text file is via https: with an NTIS issued username and password. How can I specify (hard code) the password into the authentication string? I know this is bad practice. Is there a better way to do this?
My script looks like this-
$date= Get-Date -format yyMMdd
Import-Module BitsTransfer
$Job = Start-BitsTransfer `
-DisplayName DMFweeklytrans `
-ProxyUsage AutoDetect `
-Source https://dmf.ntis.gov/dmldata/weekly/WA$date `
-Destination D:\Test.txt `
-Authentication Basic `
-Credential "myIssuedUsername" `
-Asynchronous
While (($Job.JobState -eq "Transferring") -or ($Job.JobState -eq "Connecting")) {sleep 5}
Switch($Job.JobState)
{
"Transfer Completed" {Complete-BitsTransfer -BitsJobs $Jobs}
default {$Job | Format-List}
}
When you have to provide credentials in non-interactive mode, you can create a PSCredential object in the following way.
$secpasswd = ConvertTo-SecureString "PlainTextPassword" -AsPlainText -Force
$yourcreds = New-Object System.Management.Automation.PSCredential ("username", $secpasswd)
$Job = Start-BitsTransfer `
-DisplayName DMFweeklytrans `
-ProxyUsage AutoDetect `
-Source https://dmf.ntis.gov/dmldata/weekly/WA$date `
-Destination D:\Test.txt `
-Authentication Basic `
-Credential $yourcreds `
-Asynchronous

Powershell in VB.NET with Admin rights

I'm writing a webservice with PowerShell commands where I want to start and stop services on the local computer and also on remote computer.
It's not a problem to start and stop the services on remote computers. I do this with an WmiObject as you can see below.
If I want to start a local service it says that I don't have the permissions.
I can't use an WmiObject with Credentials if I want to start an local service.
What can I do to start the service with admin rights?
My Script (strScriptText):
$username = "domain\administrator"
$pw = convertto-securestring "password" -asplaintext -force
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $pw
$computername = "serverAB"
if ( $computername.Contains("serverAB")){(Get-WmiObject -class Win32_Service -filter "name='AppIDSvc'").startservice().returnvalue}
else {(Get-WmiObject -class Win32_Service -ComputerName $computername -Credential $cred -filter "name='AppIDSvc'").startservice().returnvalue}
vb:
runspace = RunspaceFactory.CreateRunspace()
runspace.Open()
pipeline = runspace.CreatePipeline()
pipeline.Commands.AddScript(strScriptText)
pipeline.Commands.Add("Out-String")
Can't you try to use the old .NET method through PowerShell.
# Create an authentication object
$ConOptions = New-Object System.Management.ConnectionOptions
$ConOptions.Username = "dom\jpb"
$ConOptions.Password = "pwd"
$ConOptions.EnablePrivileges = $true
$ConOptions.Impersonation = "Impersonate"
$ConOptions.Authentication = "Default"
# Creation of a rmote or local process
$scope = New-Object System.Management.ManagementScope("\\dom.fr\root\cimV2", $ConOptions)
$ObjectGetOptions = New-Object System.Management.ObjectGetOptions($null,
[System.TimeSpan]::MaxValue, $true)
$proc = New-Object System.Management.ManagementClass($scope,
"\\dom.fr\ROOT\CIMV2:Win32_Process", $ObjectGetOptions)
# Equivalent to :
# $proc = [wmiclass]"\\.\ROOT\CIMV2:Win32_Process"
# $res = $proc.Create("cmd.exe")