Getting "Can't find the drive. The drive called 'IIS' does not exist." on first request - powershell-5.1

Working on Windows Server 2019 Datacenter, $PSVersionTable.PSVersion 5.1.17763.2931
In a powershell script started as admin I have to check if an IIS AppPool exists. I used
Get-Item -Path ("IIS:\AppPools\$apppoolname") -ErrorAction SilentlyContinue -ErrorVariable AppPoolError -OutVariable AppPoolOut
The first such Get-Item always fails with "Path IIS does not exist".
Now I set this statemens in front of the script.
$_ = Get-Item -Path ("IIS:\AppPools\DefaultAppPool") -ErrorAction SilentlyContinue -ErrorVariable AppPoolError -OutVariable AppPoolOut
$_ = Get-WebAppPoolState -Name DefaultAppPool -ErrorAction SilentlyContinue -ErrorVariable AppPoolStateError -OutVariable AppPoolStateOut
$_ = Get-Item -Path ("IIS:\AppPools\DefaultAppPool") -ErrorAction SilentlyContinue -ErrorVariable AppPoolError -OutVariable AppPoolOut
Whenever I let run this statement sequence in a newly opened powershell window (as admin) I get the error "Path IIS does not exist" after the first Get-Item. Get-WebAppPoolState returns a proper answer, the second Get-Item doesn't return any data.
Running the same sequence again in the same window, I get meaningful data returned after every request.
Screenshot of 2 successive passes
Can someone explain to me what is going on here and how I can prevent the first Get-Item from failing?

Related

powershell variable into where-object does not return data

I am writing a script to ultimately check a block of servers for a certificate by FriendlyName and then go back and delete them once confirmed. Right now I am just trying to get the initial check to work. Currently it is not returning any data. Can anyone help?
$ContentsPath = "C:\Servers.txt"
$Servers = Get-Content $ContentsPath
$CertDeletionFile = "C:\CertsDeleted.csv"
$Today = Get-Date
$Certificate = Read-Host -Prompt "What certificate would you like to
REMOVE?"
write-host $Certificate
function findCert {
param ([string]$Certificate)
Invoke-Command -ComputerName $Servers -ScriptBlock {Get-Childitem -Path
Cert:LocalMachine\My | where {$_.friendlyname -eq $Certificate } | Select-
Object -Property FriendlyName }
}
findCert
As Mathias R. Jessen comments, your findcert function needs a certificate name as a parameter, and you aren't passing anything when you call it, so it won't run properly.
You're also trying to use a local computer variable $Certificate, on a remote computer inside an invoke-command, and the remote computer can't get to that variable across the remoting.
I've rewritten it, with $using: which is a syntax that tells PS to send the value over the remoting session, and with renamed variables so it's more clear which part is accessing which variables:
$ContentsPath = 'C:\Servers.txt'
$Servers = Get-Content -LiteralPath $ContentsPath
$CertDeletionFile = 'C:\CertsDeleted.csv'
$Today = Get-Date
$typedCertificateName = Read-Host -Prompt "What certificate would you like to
REMOVE?"
write-host $typedCertificateName
function findCert {
param ([string]$Certificate)
Invoke-Command -ComputerName $Servers -ScriptBlock {
Get-Childitem -Path Cert:LocalMachine\My |
where-Object {$_.friendlyname -eq $using:Certificate } |
Select-Object -Property FriendlyName
}
}
findCert -Certificate $typedCertificateName

How can i uninstall Google Chrome using Power Shell

I am working towards writing a powershell script for uninstalling the current version 54.0.2840.99 m of Google Chrome from my machine but could not be able to do so. I am using the following piece of code in my script:
$app = Get-WmiObject -Class Win32_Product | Where-Object {
$_.Name -match “Google Chrome”}
$app.Uninstall()
The chrome is installed in my machine but the above code is not showing Google Chrome in the list. It is returning null value and it could not be able to uninstall.
Could you please tell me where i have been went wrong or any other alternative solution for uninstalling the Google Chrome via PowerShell?
Google Chrome doesn't use WMI when installing chrome. You can use the command below to find the version, and uninstall chrome using its setup package.
(Get-ItemProperty -path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome').version | ForEach-Object {& ${env:ProgramFiles(x86)}\Google\Chrome\Application\$_\Installer\setup.exe --uninstall --multi-install --chrome --system-level --force-uninstall}
I use this:
$GoogleChrome = [CimInstance](Get-CimInstance -ClassName Win32_Product | Where-Object {$_.Name -eq 'Google Chrome'})
If ($GoogleChrome -ne $null) {
Write-Host 'Uninstalling Google Chrome'$GoogleChrome.Version
Invoke-CimMethod -InputObject $GoogleChrome -MethodName 'Uninstall' | Out-Null
}
The search for the uninstaller takes longer than I would like, but it works for the 64-bit version of Google Chrome we use.
There might have been some changes with Chrome that gave me some trouble with the solutions above. This worked for me just recently, and it will work with some other software packages too. It also gives a good basis for confirming the package is no longer installed:
$target_computers='computer1','computer2','computer3'
$software_to_remove='chrome'
Get-PSSession | Remove-PSSession
$target_sessions=New-PSSession -ComputerName $target_computers
if ($software_to_remove -Like 'chrome') {
Invoke-Command -Session (Get-PSSession) -ScriptBlock{
$computer_name=$env:COMPUTERNAME
$chrome_installed_object=(Get-ItemProperty -path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome')
if ($chrome_installed_object -ne $null) {
$chrome_version=($chrome_installed_object | select-object -ExpandProperty Version)
$chrome_uninstaller_string=($chrome_installed_object | Select-Object -ExpandProperty UninstallString)
$chrome_uninstaller_full_string=($chrome_uninstaller_string+" --force-uninstall")
Write-Host "For $computer_name, we have found the following version of Chrome: $chrome_version"
Write-Host "Removing version $chrome_version"
Write-Host "This is the Chrome uninstall string we are using: $chrome_uninstaller_full_string"
cmd.exe /c $chrome_uninstaller_full_string
}
}
}
Invoke-Command -Session (Get-PSSession) -ScriptBlock{
param($software_to_remove)
$computer_name=$env:Computername
#Look at installed programs
Write-Host ("Looking for a package matching this pattern: "+$software_to_remove+" on server "+$computer_name)
$program_to_remove = $(Get-WmiObject -Class Win32_Product | Where-Object -Property Name -Like "*$software_to_remove*")
if (![string]::IsNullOrWhiteSpace($program_to_remove)) {
Write-Host "These programs have been located: $program_to_remove"
$oktoremove=$(Read-Host "OK to remove these programs (y or n)?")
If (!($oktoremove -eq 'y')) {
Write-Host 'Exiting the entire script on server $computer_name since you do not wish to take any further action now'
exit
}
$program_to_remove.Uninstall()
}
#Look at installed packages
$PackageToRemove=$(Get-Package -Provider Programs -IncludeWindowsInstaller | Where-Object -Property Name -Like "*$software_to_remove*")
$PackageToRemove_name=$(Get-Package -Provider Programs -IncludeWindowsInstaller | Where-Object -Property Name -Like "*$software_to_remove*" | Select-Object -ExpandProperty Name)
if (![string]::IsNullOrWhiteSpace($PackageToRemove)) {
Write-Host "$PackageToRemove_name"
$oktoremove=$(Read-Host "OK to remove software (y or n)?")
If (!($oktoremove -eq 'y')) {
Write-Host 'Exiting the entire script on server $computer_name since you do not wish to take any further action now'
exit
}
Uninstall-Package -Name $PackageToRemove -Verbose -Force
}
#Just in case the previous approach did not remove the package successfully, use the GUID this time
Get-Package -Provider Programs -IncludeWindowsInstaller | Where-Object -Property Name -Like "*$software_to_remove*" | Uninstall-Package
#Check again, just to make sure
$x86Path = "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
$installedItemsX86 = Get-ItemProperty -Path $x86Path | Select-Object -Property PSChildName, DisplayName, DisplayVersion
$x64Path = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*"
$installedItemsX64 = Get-ItemProperty -Path $x64Path | Select-Object -Property PSChildName, DisplayName, DisplayVersion
$installedItems = $installedItemsX86 + $installedItemsX64
$installedItems | Where-Object -FilterScript { $null -ne $_.DisplayName } | Sort-Object -Property DisplayName | ft
} -ArgumentList($software_to_remove)

Powershell script works in ISE but not in console

I am writing a powershell script that will start a process, monitor a folder for file changes, and then backup that folder when files are added/changed.
If I run my script from within the Powershell ISE, it works fine, I can monitor the folder and it will properly save a backup as expected.
The problem is that I want to run a batch file that will run the powershell script. But whenever I run the script from a powershell console, or whenver I run my batch file which runs the script, it doesn't work any longer. The script runs and the events are registered. When I copy a file over to the watched folder though, I ONLY get the changed event and not the created event, and the doStuff function is no longer called. I'm not sure how to go about debugging this :/
Following is my script. I have removed parts that don't pertain to what is actualyl wrong currently, so some variables I'm using here you won't see declared but they are there. I get the write-host's to the console when the changed event happens but not the created event (though as stated eariler in the ISE, I get both events and everything works just fine)
#unregister events, in case they weren't unregistered properly before. Just error siliently if they don't exist
Unregister-Event ConsoleStopped -ErrorAction SilentlyContinue
Unregister-Event FileCreated -ErrorAction SilentlyContinue
Unregister-Event FileChanged -ErrorAction SilentlyContinue
Unregister-Event TimerTick -ErrorAction SilentlyContinue
#start the console process
Write-Host Starting console process...
$consoleProcess = Start-Process "$consoleExe" -PassThru
#register to listen for when the console stops
Register-ObjectEvent $consoleProcess Exited -SourceIdentifier ConsoleStopped -Action {
Write-Host Console stopped
#unregister events
Unregister-Event ConsoleStopped -ErrorAction SilentlyContinue
Unregister-Event FileCreated -ErrorAction SilentlyContinue
Unregister-Event FileChanged -ErrorAction SilentlyContinue
if(!$timer.Enabled) {
Unregister-Event TimerElapsed -ErrorAction SilentlyContinue
Remove-Item $timer
}
Remove-Item $fsw
Remove-Item $consoleProcess
}
#watch all files/folders
$filter = '*.*' # You can enter a wildcard filter here.
# In the following line, you can change 'IncludeSubdirectories to $true if required.
$fsw = New-Object IO.FileSystemWatcher $folder, $filter -Property #{IncludeSubdirectories = $true;NotifyFilter = [IO.NotifyFilters]'DirectoryName, FileName, LastWrite'}
#register for FileCreated event
Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action {
write-host Created event has occurred
doStuff($Event)
}
#register for FileChanged event
Register-ObjectEvent $fsw Changed -SourceIdentifier FileChanged -Action {
Write-Host Change event has occurred
doStuff($Event)
}
function doStuff($event)
{
write-host doStuff has been called
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
Write-Host "The file '$name' in '$folder' was $changeType at $timeStamp" -fore green
if(!$timer.Enabled) {
Write-Host Starting save timer
Register-ObjectEvent $timer Elapsed -SourceIdentifier TimerElapsed -Action $TimerAction
$timer.Start()
Out-File "$backupDir\backup.log" -Append -Force -InputObject "A request for a backup created at $timeStamp"
}
else {
Write-Host A backup has already been request
}
}
function backupSave ()
{
Write-Host Starting backup...
$timestamp = Get-Date -Format o | foreach { $_ -replace ":", "." }
Copy-Item $folder "$backupDir\backup_$timestamp" -Recurse -Force
}
Try moving your functions to the beginning of your script.
i.e. move function doStuff($event) and function backupSave () to the beginning of the script.
The problem may be because when you are calling the functions from the script they haven't been defined yet. It works in Powershell ISE because you ran the script several times and the functions were defined at one time or another.

Printing the error in try catch block in powershell

Here is my script which returns a boolean
param($fileName, $path, $contextMenuItem, $automationDLLPath)
function CloseWindowsExplorer()
{
(New-Object -comObject Shell.Application).Windows() | foreach-object {$_.quit()}
}
Try
{
Import-Module $automationDLLPath
# Open the explorer window in a maximized form
Start-Process explorer $path -WindowStyle Maximized
Start-Sleep 1
Get-UIAActiveWindow
# Get the "Items View" in Explorer to go through all the lements
$list = Get-UIAList -Name 'Items View' -TimeOut 30000;
# Get the file specified in the feature file from the Items View
# Added a sleep because the VM takes time to perform the functions
Start-Sleep 1
$file = $list | Get-UIAListItem -Name $fileName;
# Perform a single click on the file to invoke a right click on it
Invoke-UIAListItemSelectItem -InputObject $file -ItemName $fileName;
# Added a sleep because the VM takes time to perform the functions
Start-Sleep 1
# Invoke the right click on the selected file
$menu = Invoke-UIAControlContextMenu -InputObject $file;
Start-Sleep 1
# select our context menu item
$menuItem = Get-UIAMenuItem -InputObject $menu $contextMenuItem -TimeOut 30000;
# Display error if the required item in the context menu is not found
if( $null -eq $menuItem){
%{ Write-Host 'cannot find menuItem' }
}
# Invoke the item if found in the context menu
else{
Invoke-UIAMenuItemClick -InputObject $menuItem
}
# close the windows explorer and return true
CloseWindowsExplorer
Write-Output "true"
}
Catch
{
# close the explorer window as a part of teardown and return false to reflect test failure
Write-Output "false"
CloseWindowsExplorer
}
I want the script to print the exact exception that was caught as well as return a boolean but in this case it is just returning false when the script fails. Any help is appreciated
Basically I need to print the exception as if the try catch block does not exist.
You need to use the special variable $_
This small example shows how it works:
try {
testmenow
} catch {
Write-Host $_
}
$_ is an object so you can do
$_|gm
in the catch block in order to see the methods you can call.

PowerShell: Copy-Item throws DriveNotFoundException

My script keeps bugging me with the following exception
copy-item : Cannot find drive. A drive with the name 'F' does not exist.
At C:\Program Files (x86)\CA\ARCserve Backup\Templates\RB_Pre_Process.ps1:58 char:1
+ copy-item -Path $drive -Destination $DST_DRIVE -Recurse -ErrorAction Stop
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (F:String) [Copy-Item], DriveNotFoundException
+ FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.CopyItemCommand
This is what my script looks like. I am mounting an ISO image on drive F: and I have added a "start-slepp -s 5" command so i can verify the image get's mounted, which it does!
$BACKUP_PATH = "E:\00_BACKUP_DATA"
$DR_PATH = "E:\01_DR_DATA"
$ISO_IMAGE = "C:\Program Files (x86)\CA\ARCserve Backup\Templates\Winpe_x64.iso"
$DST_DRIVE = "E:\"
try {
New-EventLog -LogName Application -Source "RB_Pre_Process.ps1" -ErrorAction Stop
} catch [System.InvalidOperationException] {
Write-host $_
}
try {
Write-Host "Preparing RDX cartridge..."
# Query for disk object
$disk_number = (Get-Disk | Where-Object -Property FriendlyName -like "TANDBERG RDX*").Number
# Remove partitions
Get-Disk $disk_number | Clear-Disk -RemoveData -Confirm:$false | Out-Null
# Create new partition
New-Partition -DiskNumber $disk_number -UseMaximumSize | Out-Null
# Format partition
Format-Volume -DriveLetter E -FileSystem NTFS -NewFileSystemLabel "RDX_TAPE" -Confirm:$false | Out-Null
# Set partition as active
Set-Partition -DriveLetter E -IsActive:$true | Out-Null
} catch {
Write-Host $_
Write-EventLog -LogName Application -Source $MyInvocation.MyCommand.Name -EventID 2 -Message $_
}
try {
Write-Host "Creating folder structure..."
new-item -itemtype directory -Path $BACKUP_PATH -ErrorAction stop | Out-Null
new-item -itemtype directory -path $DR_PATH -ErrorAction stop | Out-Null
} catch {
Write-Host $_
Write-EventLog -LogName Application -Source $MyInvocation.MyCommand.Name -EventID 2 -Message $_
}
try {
Write-Host "Mounting ISO image..."
$image = Mount-DiskImage -ImagePath $ISO_IMAGE -PassThru -ErrorAction Stop
} catch [ParameterBindingException] {
Write-Host $_
Write-EventLog -LogName Application -Source $MyInvocation.MyCommand.Name -EventId 2 -Message $_
}
$drive = ($image | Get-Volume).DriveLetter
$drive += ":\*"
Start-Sleep -s 5
try {
Write-Host "Copying ISO content..."
copy-item -Path $drive -Destination $DST_DRIVE -Recurse -ErrorAction Stop
} catch {
Write-Host $_
Write-EventLog -LogName Application -Source $MyInvocation.MyCommand.Name -EventId 2 -Message $_
}
try {
Write-Host "Unmounting ISO image..."
Dismount-DiskImage -ImagePath $ISO_IMAGE -ErrorAction Stop
} catch [System.Exception] {
Write-Host $_
Write-EventLog -LogName Application -Source $MyInvocation.MyCommand.Name -EventId 2 -Message $_
}
So, what's going wrong here? Sometimes it works sometimes not...
I "solved" the issue... my script is working perfectly fine when it's getting started directly from the PowerShell prompt instead of the PowerShell ISE... So the IDE is the culprit.
it seems the mounted image can't be reached in powershell. I think it's a limitation of the provider. A possible workaround is issuing CMD command. You could replace
copy-item -Path $drive -Destination $DST_DRIVE -Recurse -ErrorAction Stop
with
& cmd /C "copy F:\* G:\dest\"
Here I just give an example, you may need to do further work to copy recursively..you could use the xcopy or robocopy which could handle recursive copy.