System.EnterpriseServices and Powershell Locking dll - com

I am trying to use System.EnterpriseServices to uninstall a c# com+ component, replace the dll and reinstall the new version.
The problem is that when I get to the line copy-item the script always fails because System.EnterpriseSerivces is locking the destination file. If I break the script up into two sections one that calls UnistallAssembly and a second that does the copy and calls InstallAssembly everything works.
Any ideas for forcing system.enterpriseservices to release dll?
$comRoot = "C:\Comroot\"
[void][System.Reflection.Assembly]::LoadWithPartialName("System.EnterpriseServices")
[System.String]$applicationName = "My App Name";
[System.String]$typeLibraryName = $null;
$objAdmin = new-object -com COMAdmin.COMAdminCatalog
$objAdmin.ShutdownApplication("$applicationName");
$objAdmin = $null
$helper = New-Object System.EnterpriseServices.RegistrationHelper
$helper.UninstallAssembly("$comRoot\$StepName.dll", $applicationName)
$helper = $null
$applicationName = $null
[gc]::collect()
[gc]::WaitForPendingFinalizers()
"SOURCE : $BranchPath\Steps\$StepName\bin\Debug\"
"DESTINATION : $comRoot"
copy-item "$BranchPath\Steps\$StepName\bin\Debug\*$StepName*" -destination "$comRoot" - force
$helper = New-Object System.EnterpriseServices.RegistrationHelper
$helper.InstallAssembly("$comRoot\$StepName.dll", [ref] $applicationName, [ref] $typeLibraryName, [System.EnterpriseServices.InstallationFlags]::ConfigureComponentsOnly);
"Install Complete <$typeLibraryName>"
Read-Host "Press any key to exit"

I wonder if it is possible the uninstall isn't finished by the time you attempt the copy? That is, is the call to UninstallAssembly sync or async? One way to work around "file in use" issues is to attempt to rename the file using a tool like the SysInternals MoveFile.exe and then install the new version.

Related

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

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

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

Authentication error with webclient in powershell

I'm relatively new to Powershell so really not sure where to go with this issue now. I am trying to download a file from a subversion repository and am getting the (401) Unauthorized" error. I am able to log into the site and download the file using IE using the exact Same credentials on the same machine.
$source = "http://repository/folder/File.exe"
$destination = "E:\Temp\File.exe"
$wc = New-Object System.Net.WebClient
$user="user"
$pwd=convertto-securestring -string "password" -AsPlainText -force
$creds=New-Object System.Management.Automation.PSCredential -ArgumentList $user, $pwd
$wc.Credentials = New-Object System.Net.NetworkCredential ($user, $Creds.GetNetworkCredential().Password,"DOMAIN")
$download=$wc.DownloadFile($source, "$destination")
Exception calling "DownloadFile" with "2" argument(s): "The remote server returned an error: (401) Unauthorized."
Any ideas if this is cross platform issue? And how to get around this?
Thanks
Are you using basic auth on your iis/apache? If so try this:
$source = "http://repository/folder/File.exe"
$destination = "E:\Temp\File.exe"
$wc = new-object System.Net.WebClient
$credCache = new-object System.Net.CredentialCache
$creds = new-object System.Net.NetworkCredential($user,$pwd)
$credCache.Add($source, "Basic", $creds)
$wc.Credentials = $credCache
$wc.DownloadFile($source, $destination)

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?

Why is $DebugPreference getting lost in a module?

I have a powershell script and I have set the $DebugPreference to "Continue". However when I call Write-Debug from a module that is called from my script, the $DebugPreference changed to "SilentlyContinue". Why is that? How can I keep $DebugPreference the same as the calling script? Example below
CallingScript.ps1
$DebugPreference = "Continue"
Write-Host "Debug preference: $DebugPreference"
Write-Debug "Checking that debugging works"
Import-Module Logging;
Write-Log "Debug" "Checking that debugging still works!"
Logging.psm1
Function Write-Log
{
param (
[ValidateSet("Error","Warning","Debug","Info")][String]$type,
[String]$logMessage
)
Write-Host "Debug preference: $DebugPreference"
switch($type)
{
"Error" {Write-Error $logMessage;}
"Warning" {Write-Warning $logMessage;}
"Debug" {Write-Debug $logMessage;}
"Info" {Write-Output $logMessage;}
}
}
If I run the script, this is the output:
PS > .\CallingScript.ps1
Debug preference: Continue
DEBUG: Checking that debugging works
Debug preference: SilentlyContinue
PS >
As JPBlanc's link in his comment explains: It is a variable scope issue. The module's scope chain goes directly to the global scope and not through any script scopes. Even if it is imported from a script.
Your code will work if you set $DebugPreference from your script in the global scope, but of course this has influence on more than just your script.
$global:DebugPreference = "Continue"
Another solution in this specific $DebugPreference case is to use the -Debug parameter to pass it along. The downside is that you will have to do this with every command you call.
Write-Log "Debug" "Checking that debugging still works!" -debug:$DebugPreference
A third solution would be to set $DebugPreference at the module level.
$m = Import-Module Logging -PassThru
& $m {$script:DebugPreference = 'Continue'}