Disk cleanup using PowerShell - powershell

I am trying to write a script for cleaning disks. All works except running cleanmgr. Can someone advise?
$objShell = New-Object -ComObject Shell.Application
$objFolder = $objShell.Namespace(0xA)
$computername = Read-Host -Prompt 'Enter machine name'
foreach ($hosts in $computername) {
#$temp = get-ChildItem "env:\TEMP"
#$temp2 = $emp.Value
#$swtools = "c:\SWTOOLS\*"
$ccmcache = "\\$hosts\c$\Windows\ccmcache\*"
$temp = "\\$hosts\C$\Users\*****\AppData\Local\Temp\*"
#$WinTemp = "c:\Windows\Temp\*"
$FreespaceBefore = (Get-WmiObject -Class win32_logicaldisk -ComputerName $hosts -Filter "DeviceID='C:'" | select Freespace).FreeSpace/1GB
#Write-Output "Disk Space Before"
Write-Output "$FreespaceBefore"
Write-Host "Removing Junk files in $ccmcache on $hosts." -ForegroundColor Green
Remove-Item -Recurse $ccmcache -Force -Verbose
Write-Host "Removing Junk files in $temp on $hosts." -ForegroundColor DarkYellow
Remove-Item -Recurse $temp -Force -Verbose
Write-Host "Finally now , Running Windows disk Clean up Tool" -ForegroundColor Cyan
Invoke-Command -ComputerName pc -ScriptBlock {Start-Process cleanmgr.exe}
#Write-Output "Disk Space after"
#Write-Output "$diskspaceafter"
$([char]7)
Sleep 1
$([char]7)
Sleep 1
Write-Host "I finished the cleanup task,Succefully " -ForegroundColor Yellow
}

2# Running Disk Clean up Tool
write-Host "Finally now , Running Windows disk Clean up Tool" -ForegroundColor Cyan
cleanmgr /sagerun:1 /VERYLOWDISK | out-Null
$([char]7)
Sleep 1
$([char]7)
Sleep 1

I know this question is old but for anyone still looking...
This link could provide a couple options, like the one below
foreach ($computer in $computers){
if(!(Test-Connection -Cn $computer -BufferSize 16 -Count 1 -ea 0 -quiet))
{write-host "cannot reach $computer" -f red}
else {& \\$computer\C$\Windows\System32\cleanmgr.exe /sagerun:1}}

Related

PowerShell - How do you get the latest windows updates from a list of servers? Array/For Each problem

I think the issue is in the $computer and foreach sections. When I run this nothing returns. What am I missing please?
$computer = #('server1', 'server2')
ForEach ($COMPUTER in (Get-ADComputer -Filter ('Name -like "{0}"' -f $computer)))
{if(!(Test-Connection -Cn $computer -BufferSize 16 -Count 1 -ea 0 -quiet))
{write-host "cannot reach $computer" -f red}
else {
$key = "SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\Results\Install"
$keytype = [Microsoft.Win32.RegistryHive]::LocalMachine
$RemoteBase = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($keytype,$Server)
$regKey = $RemoteBase.OpenSubKey($key)
$KeyValue = $regkey.GetValue("LastSuccessTime"”)
$System = (Get-Date -Format "yyyy-MM-dd hh:mm:ss")
if ($KeyValue -lt $System)
{
Write-Host " "
Write-Host $computer "Last time updates were installed was: " $KeyValue
}
}
}
Thanks for your help.
i think this should work. But at the moment i have no possibility to test it.
Some explanations:
name var ADComputer instead of COMPUTER
$COMPUTER and $coMpUTeR and $COMputer and $computer are all the same because of powershell's case-insensitivity
create var outside the loop, if you don't change them in it
if ($KeyValue -lt $System) makes no sense it will always be true, because the last successfull installation will always be in the past
Keep it simple, you can easily get the remote's registry value with the Invoke-Command, which allows you to "invoke" a script block on a remote host and returns its output. Also it is simpler to call the registry with the Get-ItemPropertyValue cmdlet.
# create array computer
$computer = #('server1', 'server2')
# store Registry key
$key = “HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\Results\Install”
# iterate through computers
$computer.Foreach({
# Get Computer from AD
$ADComputer = Get-ADComputer $_
# Test if server is not reachable and report it
if(!(Test-Connection -Cn $ADComputer.DNSHostName -BufferSize 16 -Count 1 -ea 0 -quiet)){
Write-Host ("cannot reach {0} " -f $_) -ForegroundColor Red
}else{
# Get remote computers registry entry
$KeyValue = Invoke-Command { (Get-ItemProperty -Path $args[0] -Name "LastSuccessTime") } -ComputerName $ADComputer.DNSHostName -ArgumentList $key
Write-Host " "
# write LastSuccessTime to host
Write-Host ("{0}: Last time updates were installed was: {1}" -f $ADComputer.Name, $KeyValue)
}
})

how to automate Wusa with remoting and overcoming the lack of wait cmd

Scenario: Taking a list of kb files and executing them remotely with WUSA install of target machines across network.
The flow is like this:
enter a powershell session W/ target computer
for each loop $KB in $List
wusa.exe $kb
wait 'til installed
back to the wusa.exe for next device in $list
Code:
# SET UP ERROR-HANDLING FOR THE PS SESSION
$ErrorActionPreference = "continue"
# DECLARE VARIABLE THAT CONTAINS LIST OF PATCHES AVAILABLE FOR INSTALL
$PatchList = (Get-ChildItem -Path C:\WinPatch -recurse | Where-Object {$_.Extension -eq '.msu'})
# ESTABLISH LOOP TO ITERATE THRU PATCHES
foreach ($Patch in $PatchList)
{
Try
{
Write-Host ("`n Preparing to install: " + $Patch) -ForegroundColor Yellow
Write-Host ("`n Installing...") -ForegroundColor Magenta
$SB = {
$arglist = "$Patch", "/quiet", "/norestart"
Start-Process -FilePath "C:\windows\system32\wusa.exe" -ArgumentList $arglist -Wait}
Invoke-Command -ScriptBlock $SB
Write-Host "`n Installation complete`n" -ForegroundColor Green
}
Catch
{
[System.Exception]
Write-Host "Installation failed with Error -- $Error()" -ForegroundColor Red
$Error.Clear()
}
}
# RESTART OPTIONS
$Ans1 = Read-Host "`n Would you like to restart this computer now (Type Y for yes or N for no)"
if ($Ans1 -eq 'Y' -or $Ans1 -eq 'y')
{
Remove-Item -Path C:\WinPatch\*.msu -Force
Write-Host "`n This computer will restart in 5 seconds..." -ForegroundColor Yellow
Start-Sleep -Seconds 5
Restart-Computer -Force
}
else
{
# TEST COMPUTER FOR PS VERSION
$Tester = test-wsman -computername localhost | Select-Object -Property ProductVersion
if ($Tester.ProductVersion.EndsWith("1.0"))
{
Write-Host "`n This computer has PS v1.0 installed and you will have to open Task Scheduler to schedule restart" -ForegroundColor Red
Read-Host "`n Press ENTER to continue..."
}
elseif ($Tester.ProductVersion.EndsWith("2.0"))
{
Write-Host "`n This computer has PS v2.0 installed and you will have to open Task Scheduler to schedule restart" -ForegroundColor Red
Read-Host "`n Press ENTER to continue..."
}
else
{
# SCHEDULE RESTART
Import-Module PSScheduledJob
$RST = Read-Host -Prompt "`n Enter date/time to restart computer...format is --> mm/dd/yyyy hh:mmAM/PM"
$Ans2 = Read-Host -Prompt "`n You entered: $RST... if this is correct, enter Y for yes"
if ($Ans2 -eq 'Y' -or $Ans2 -eq 'y')
{
$Nomen = Read-Host -Prompt "`n Enter name for scheduled restart "
$Trig = New-JobTrigger -Once -At $RST
Register-ScheduledJob -Name $Nomen -Trigger $Trig -ScriptBlock { Restart-Computer -force } -RunAs32
}
else
{
Write-Host "`n Please restart the script and try again" -ForegroundColor Red
}
}
}
Break
PsExec.exe -u $domain\$username -p $password -h -s -accepteula \\$computer wusa.exe /quiet 'C:\Program Files\WindowsPowershell\Modules\windows10.0-kb5005112-x64.msu' /wait /forcerestart
This works for me. If you wrap this in an Invoke-Command block you can run this remotely. :-)
Thanks,

Is there any Faster method to do WMI query from Powershell ..?

Wrote a small script to find the number Multipaths from the windows servers using WMI query. It works well for the servers which can connect directly without any issue. But if one server is pingable but not able to reach through WMI script, it takes long time to return the error ( for example if a linux server hostname is present in the servers.txt list).. Can somebody help me to do the same in a faster way..?
$Servers = Get-Content .\Servers.txt
$ErrorActionPreference = ‘SilentlyContinue’
FOREACH ($Server in $Servers) {
Write-Host $Server -nonewline
if (test-connection -computername $Server -Count 1 -quiet) {
$Name = $null
$NoPath =$null
$MPIODisks =$null
$MPIODisks = Get-WmiObject -Namespace root\wmi -Class mpio_disk_info -ComputerName "$Server" |Select-Object "DriveInfo"
if ($MPIODisks -eq $Null) {
write-host "`t - Unable to connect" -fore "RED"
} else {
write-host ""
write-host "Drive Name `tNo.Path" -fore "yellow"
Foreach ($Disk in $MPIODisks) {
$mpiodrives = $disk.DriveInfo
foreach ($Drive in $mpiodrives) {
$Name = $Drive.Name
$NoPath = $Drive.Numberpaths
If ($NoPath -lt 4) {
Write-Host $Name `t -nonewline
write-host $NoPath -fore "Red"
} else {
Write-Host $Name `t -nonewline
write-host $NoPath -fore "Green"
}
}
}
}
write-host ""
} else {
write-host "`t- Unknown Host" -fore "Red"
write-host ""
}
}
There is a connect item for Get-WmiObject to add a timeout parameter. A workaround noted in that item is to just pipe your WMI command to Wait-Job and specify a timeout period in seconds.
As long as your on PS version 3.0 or higher, this should work for you:
Get-WmiObject win32_computersystem -ComputerName <hostname> -AsJob | Wait-Job -Timeout 10 | Receive-Job
As an alternative, you could ask all servers for the result at once by passing them all into the query and avoiding the slow loop querying one server at a time. I don't have any MPIO drives to test with, but it could look something like this (using Get-Ciminstance which takes a timeout parameter):
$servers = Get-Content .\Servers.txt
# Get data from all servers with timeout
$servers_ok = Get-CimInstance -computername $servers -Namespace root\wmi -Class mpio_disk_info -ErrorAction SilentlyContinue -OperationTimeoutSec 1 | group pscomputername
# Output which servers gave no result back
foreach($no_result in $($servers | where { $_ -NotIn $servers_ok.Name })) {
write-host "No result for $no_result" -ForegroundColor Red
}
# Loop over the results and output
foreach($server in $servers_ok) {
Write-Host $server.Name
foreach($mpiodisk in $server.group)  {
$mpiodrives = $mpiodisk.DriveInfo
foreach ($mpiodrive in $mpiodrives) {
$name = $mpiodrive.Name
$noPath = $mpiodrive.NumberPaths
If ($NoPath -lt 4) {
write-host $name `t -nonewline
write-host $noPath -fore "Red"
} else {
write-host $name `t -nonewline
write-host $noPath -fore "Green"
}
}
}
}

Speed up Test-Connection before Foreach

I made a script to check if users desktop folder are under the cuota limitation, if they're under the cuota limitation the backup to the server will be done correctly.
each user have his computer, so source CSV looks like:
pc1,user1
pc2,user2
pc800,user800
Some computers are Windows Xp and some W7, and the paths can be different 'cause of that I'm using Test-Path
W7 = C:\users\$user\desktop
XP = C:\document and settings\$user\desktop
But Test-Path is SUPER SLOW and I started to use a Test-Connection -count 1 before each Test-path
Anyway, the script still SLOW, in each "bad ping test" I lose lot of time.
CODE:
$csvLocation = '~\desktop\soourceReport.csv'
$csv = import-csv $csvLocation -Header PCName, User
$OuputReport = '~\desktop\newReport.csv'
# info:
# "209715200" Bytes = 200 MB
$cuota = "209715200"
$cuotaTranslate = "$($cuota / 1MB) MB"
Write-Host "Cuota is set to $cuotaTranslate"
$count=1
foreach($item in $csv)
{
write-host "$count# Revisando" $item.User "en" $item.PCName "..." #For debug
if (Test-Connection -Quiet -count 1 -computer $($item.PCname)){
$w7path = "\\$($item.PCname)\c$\users\$($item.User)\desktop"
#echo $w7path #debug
$xpPath = "\\$($item.PCname)\c$\Documents and Settings\$($item.User)\Escritorio"
#echo $xp #debug
if(Test-Path $W7path){
$desktopSize = (Get-ChildItem -Recurse -force $w7path | Measure-Object -ErrorAction "SilentlyContinue" -property length -sum)
write-host -ForegroundColor Green "access succeed"
if($($desktopSize.sum) -gt $cuota){
$newLine = "{0},{1},{2}" -f $($item.PCname),$($item.User),"$("{0:N0}" -f $($desktopSize.sum / 1MB)) MB"
$newLine | add-content $outputReport
Write-Host -ForegroundColor Yellow "cuota exceeded! -- added"
}
else{
Write-Host -ForegroundColor DarkYellow "cuota OK"
}
}
elseif(Test-Path $xpPath){
$desktopSize = (Get-ChildItem -Recurse -force $xpPath | Measure-Object -ErrorAction "SilentlyContinue" -property length -sum)
write-host -ForegroundColor Green "access succeed"
if($($desktopSize.sum) -gt $cuota){
$newLine = "{0},{1},{2}" -f $($item.PCname),$($item.User),"$("{0:N0}" -f $($desktopSize.sum / 1MB)) MB"
$newLine | add-content $outputReport
Write-Host -ForegroundColor Yellow "cuota exceeded! -- added"
}
else{
Write-Host -ForegroundColor DarkYellow "cuota OK"
}
else{
write-host -ForegroundColor Red "Error! - bad path"
}
}
else{
write-host -ForegroundColor Red "Error! - no ping"
}
$count++
}
Write-Host -ForegroundColor green -BackgroundColor DarkGray "All done! new report stored in $report"
To improve it I stored all computers in a $list using another Foreach, before the firstly mentioned SLOW-Foreach loop.
foreach($pcs in $csv){
$alivelist += #( $pcs.PCName )
}
Test-Connection -quiet -count 2 -computer $alivelist
Now, I don't now how to UPDATE or remove the rows ("dead" pc,user) from the SOURCE CSV before to enter into the second Foreach.
I need some of your "magic", or at least some ideas!
thanks
To speed up your script you need to run the checks in parallel (as others have already mentioned). Put your checks and the worker code in a scriptblock:
$sb = {
Param($computer, $username)
if (Test-Connection -Quiet -Count 2 $computer) { return }
$w7path = "\\$computer\c$\users\$username\desktop"
$xpPath = "\\$computer\c$\Documents and Settings\$username.TUITRA..."
if (Test-Path $W7path) {
#...
} elseif (Test-Path $xpPath) {
#...
} else {
#...
}
}
Then run the scriptblock as parallel jobs:
$csv | ForEach-Object {
Start-Job -ScriptBlock $sb -ArgumentList $_.PCName, $_.User
}
# wait for completion
do {
Start-Sleep -Milliseconds 100
} while (Get-Job -State 'Running')
# cleanup
Get-Job | ForEach-Object {
Receive-Job -Id $_.Id
Remove-Job -Id $_.Id
} | Out-File $outputReport
Use a queue if you need to limit the number of parallel jobs.
test-connection is weirdly fast with the -asjob parameter, pinging about 200 computers in 4 seconds:
$list = cat hp.txt
test-connection $list -AsJob ; job | receive-job -wait -AutoRemoveJob

Get-Service in a Powershell Script

A basic question - but why in a Powershell Script does Get-Service not print the list to the output?
Write-Host "Enumerating installed Services..." -ForegroundColor Green
Get-Service | Sort-Object status,displayname
Now in the script it gives no output at all.
But if I opened a powershell prompt and typed it manually it works fine.
Full Script:
param([string] $server)
$serverSystem = Get-WmiObject -computername $env:computername Win32_ComputerSystem
$serverCPUDetail = Get-WmiObject -ComputerName $env:computername Win32_Processor
switch ($ComputerSystem.DomainRole){
0 {$ComputerRole = "Standalone Workstation"}
1 {$ComputerRole = "Member Workstation"}
2 {$ComputerRole = "Standalone Server"}
3 {$ComputerRole = "Member Server"}
4 {$ComputerRole = "Domain Controller"}
5 {$ComputerRole = "Domain Controller"}
default {$ComputerRole = "No role available" }
}
$serverOS = Get-WmiObject -class Win32_OperatingSystem -ComputerName $env:computername
Write-Host "Enumerating System information..." -ForegroundColor Green
" "
Start-Sleep -s 1
Write-Host "Hostname:" ($env:computername)
Write-Host "Domain:" ($serverSystem.Domain)
Write-Host "Role:" ($ComputerRole)
Write-Host "Operating System:" ($serverOS.Caption)
Write-Host "Service Pack:" ($serverOS.CSDVersion)
$lastBootTime = $serverOS.ConvertToDateTime($serverOS.Lastbootuptime)
Write-Host "Last Boot Time:" $lastBootTime
" "
Write-Host "Enumerating Hardware information..." -ForegroundColor Green
" "
Start-Sleep -s 1
Write-Host "Model:" ($serverSystem.Model)
Write-Host "Manufacturer:" ($serverSystem.Manufacturer)
Write-Host "CPU - No. of Processors:" ($serverSystem.NumberOfProcessors)
Write-Host "CPU - No. of Cores:" ($serverCPUDetail.NumberofCores)
Write-Host "CPU - No. of Processors, Logical:" ($serverCPUDetail.NumberOfLogicalProcessors)
Write-Host "Memory in GB:" ([math]::round($serverSystem.TotalPhysicalMemory/1024/1024/1024,0))
" "
Write-Host "Enumerating Disk Information..." -ForegroundColor Green
Start-Sleep -s 1
Foreach ($s in $env:computername) {
Get-WmiObject -Class win32_volume -cn $s |
Select-Object #{LABEL='Comptuer';EXPRESSION={$s}},DriveLetter, Label,#{LABEL='Free Space (GB)';EXPRESSION={"{0:N2}" -f ($_.freespace/1GB)}}
}
" "
Write-Host "Enumerating Network Information..." -ForegroundColor Green
" "
Start-Sleep -s 1
ForEach($NIC in $env:computername) {
$intIndex = 1
$NICInfo = Get-WmiObject -ComputerName $env:computername Win32_NetworkAdapterConfiguration | Where-Object {$_.IPAddress -ne $null}
$caption = $NICInfo.Description
$ipAddress = $NICInfo.IPAddress
$ipSubnet = $NICInfo.IpSubnet
$ipGateWay = $NICInfo.DefaultIPGateway
$macAddress = $NICInfo.MACAddress
Write-Host "Network Card": $intIndex
Write-Host "Interface Name: $caption"
Write-Host "IP Addresses: $ipAddress"
Write-Host "Subnet Mask: $ipSubnet"
Write-Host "Default Gateway: $ipGateway"
Write-Host "MAC: $macAddress"
$intIndex += 1
}
" "
# Write-Host "Enumerating Software Installations..." -ForegroundColor Green
# " "
# Get-WmiObject -Class Win32_Product | Select-Object -property Name
Write-Host "Enumerating installed Services..." -ForegroundColor Green
Get-Service | Sort-Object status,displayname
The code that you posted is fine. It's something else that you are not showing that does not work.
Try this:
$script = #"
Write-Host "Enumerating installed Services..." -ForegroundColor Green
Get-Service | Sort-Object status,displayname
"#
$script | Set-Content ./test.ps1
./test.ps1
The above writes your two lines in a script file and then executes the script. As you would expect it produces desired output.
Update 1
Having seen the input, I can now reproduce the problem. Basically if you run this:
$a= Get-WmiObject -Class win32_volume -cn $s |
Select-Object #{LABEL='Comptuer';EXPRESSION={$s}},DriveLetter, Label,#{LABEL='Free Space (GB)';EXPRESSION={"{0:N2}" -f ($_.freespace/1GB)}}
$b = Get-Service | Sort-Object status,displayname
$a
$b
you will see that it does not quite work. What happening here is that by default Write-Output is used to process the results. Write-Output writes stuff in the success pipeline, and powershell does unwrapping of the arrays. Then, I'm guessing the output system can't cope with the fact that output from get-service and select-object are of different types (and because of unwrapping they end up in the same array).
If you change the last line of your script to
Get-Service | Sort-Object status,displayname | ft
it will work. However note, that if you are going to pass around the results of your script, you better format it (ft) at the very end right before displaying. Basically, once you piped it to ft there is rarely a good reason to pass it anywhere else but for output (display or file).
Update 2
This article explains, that powershell uses the first object in the stream to determine how to format all of them. This explains the behaviour that we are observing.