Automating MS Updates with Powershell - powershell

I am trying to Automate my Microsoft update process based on the KB/MSU packages I download. I have a WMIC csv file that I am able to install based on simple If else statement. I would like to use the list extract just the "KBXXXXX" from the downloaded files and compare it to the list of installed KBs and discover what is missing.
KB install List (WMIC output):
KB123456
KB234567
KB345678
Downloaded KB file list format $KBUpdateList:
Name
WINDOWS6.1-KB2533552-X64.MSU
WINDOWS6.1-KB2533552-X86.MSU
WINDOWS6.1-KB2539635-X64.MSU
Windows6.1-KB958488-v6001-x64.MSU
Here is what I have that is not working to pull KB number:
PowerShell script to list the MSU files under the selected folder:
$Dir = get-childitem $folder -recurse
$KBUpdateList = $Dir | where {$_.extension -eq ".msu"}
$KBUpdatenames = $KBUpdateList | format-table name
$KBNumberonly = $KBUpdateList.split("-")[1]
The split fails and I can't find a fix. I just want to return the KBXXXX number so I can run my foreach statement that follows. Thank You

This should suffice for what you are looking for.
$KBUpdatenames = get-childitem $folder -recurse -Filter "*.msu" | Select-Object -Expand Name
$KBNumberonly = $KBUpdatenames | ForEach-Object{$_.split("-")[1]}
Use Get-ChildItem to get the files of type ".msu". Using -Filter is more efficient than Where-Object in most case where you are just looking for extensions. Then we expand just the names of the files with Select-Object
As for the Format-Table in your code I will refer you to an answer about Format-Taco that I enjoy.

Related

Powershell script to list files without folder names

I open a PS in a folder then use
dir -name > asd.xls -recurse.
How can I modify this so it doesn't incude folders in the filenames?
Instead of using -name, try using
(Get-ChildItem -Recurse).Name > asd.xls
and be aware that you won’t get a valid Excel workbook that way. You can get a valid CSV that can be loaded into Excel with
(Get-ChildItem -Recurse) | Select-Object -Property Name | Export-CSV -Path asd.csv -NoTypeInformation

Removing old folders and keeping the most recent

I have been working on a script of late and have come across a snag. I am in the process of removing folders which are automatically created. I want to delete the older versions of those files whilst keeping the new folders untouched, for example:
18.212.1021.0008 //Created on the 19/11/2018 12:12
18.212.1021.0008_1 //Created on the 19/11/2018 12:23
18.212.1021.0008_2 //Created on the 19/11/2018 12:27
18.212.1021.0008_3 //Created on the 19/11/2018 12:32
I would want to keep 18.212.1021.008_3 so I guess I would need to keep the folder with the most recent creation date.
Please see the code below:
$Versionarray = 13..20
Get-ChildItem "$env:LOCALAPPDATA\Microsoft\OneDrive" -Recurse | Where-Object {
# Recusivly deletes OneDrive version folders within
# Appdata\local which build up everytime OneDrive
# is installed/script is run
$item = $_
$item -is [System.IO.DirectoryInfo] -and (
$Versionarray | Where-Object { $item.Name.Contains($_) }
)
} | Remove-Item -Recurse -Confirm:$false -ErrorAction SilentlyContinue
If the newest folder you want to keep is also the one with the newest creation time, you can use this simple one-liner:
Get-ChildItem "$env:LOCALAPPDATA\Microsoft\OneDrive" -Directory | sort CreationTime | select -SkipLast 1 | Remove-Item -Recurse -Force
If you want to filter out only a specific type of folders by name, you could use a simple regex match. I cannot help you with the exact regex (since I would have to know your folder naming pattern) but it would look something like this:
Get-ChildItem "$env:LOCALAPPDATA\Microsoft\OneDrive" -Directory | where Name -match '\d\d+' | sort CreationTime | select -SkipLast 1 | Remove-Item -Recurse -Force
(Note that this is syntax might not work if you use an old Powershell version. If that's the case, let me know and I will provide a compatible fallback solution.)
UPDATE
In response to your comment: Your requirements are still a bit unclear, but here is something to get you started:
If you want to make sure to only delete folders that "look like" version folders, you can adjust the regex in the where-filter. _\d+$ will match anything with an underscore and numbers at the end:
where $_.Name -match '_\d+$'
If you also want to make sure, that this is actually a versioned copy of another existing folder, you could check that too:
where { $_.FullName -match '^(?<OriginalPath>.+)_\d+$' -and (Test-Path $Matches.OriginalPath) }

Powershell: Recursively search a drive or directory for a file type in a specific time frame of creation

I am trying to incorporate Powershell into my everyday workflow so I can move up from a Desktop Support guy to a Systems Admin. One question that I encountered when helping a coworker was how to search for a lost or forgotten file saved in an unknown directory. The pipeline I came up with was:
dir C:\ -Recurse -Filter *.pdf -ErrorAction SilentlyContinue -Force | Out-File pdfs.txt
This code performed exactly how I wanted but now I want to extend this command and make it more efficient. Especially since my company has clients with very messy file management.
What I want to do with this pipeline:
Recursively search for a specific file-type that was created in a specified time-frame. Lets say the oldest file allowed in this search is a file from two days ago.
Save the file to a text file with the columns containing the Filename, FullName(Path), and sorted by the created time in descending order.
What I have so far:
dir C:\ -Recurse -Filter *.pdf -ErrorAction SilentlyContinue -Force | Select-Object Name, FullName | Out-File *pdfs.txt
I really need help on how to create a filter for the time that the file was created. I think I need to use the Where-Object cmdlet right after the dir pipe and before the Select Object pipe but I don't know how to set that up. This is what I wrote: Where-Object {$_.CreationTime <
You're on the right track, to get the files from a specific file creation date range, you can pipe the dir command results to:
Where-Object {$_.CreationTime -ge "06/20/2017" -and $_.CreationTime -le "06/22/2017"}
If you want something more repeatable where you don't have to hard-code the dates everytime and just want to search for files from up to 2 days ago you can set variables:
$today = (Get-Date)
$daysago = (Get-Date).AddDays(-2)
then plugin the variables:
Where-Object {$_.CreationTime -ge $daysago -and $_.CreationTime -le $today}
I'm not near my Windows PC to test this but I think it should work!
See if this helps
dir c:\ -Recurse -Filter *.ps1 -ErrorAction SilentlyContinue -Force | select LastWriteTime,Name | Where-Object {$_.LastWriteTime -ge [DateTime]::Now.AddDays(-2) } | Out-File Temp.txt

Creating Zip files using PowerShell

I have these below files at a location C:\Desktop\Mobile.
Apple_iphone6.dat
Apple_iphone7.dat
Samsung_edge7.dat
Samsung_galaxy.dat
Sony_experia.dat
Sony_M2.dat
I need to create a script that writes the similar files into a single zip. So files Apple_iphone6.dat and Apple_iphone7.dat must be into single zip.
So the final zip files created would be:
Apple_Files_Timestamp.zip
Samsung_Files_Timestamp.zip
Sony_Files_Timestamp.zip
I tried this
Get-ChildItem C:\Desktop\Mobile -Recurse -File -Include *.dat | Where-Object { $_.LastWriteTime -lt $date } | Compress-Archive -DestinationPath C:\Desktop\Mobile
But it gives me error 'Compress-Archive' is not recognized as the name of a cmdlet.
How can I get this code work?
You have two problems, I will try to summarize both of them.
1. Compress files
In order to use Compress-Archive command you need to have PowerShell 5 as already commented by #LotPings. You can:
run your script on Windows 10 machine, or Server 2016 which are coming with v5
download and install PoSh 5, see details on MSDN
If you cannot do either of those, you can
install some module from PowerShell gallery that provides similar functionality via 7-zip tool. Search resultes are here. Download and check those modules before use!
use .NET 4.5 class, check answer here on Stack Overflow
2. Group files
Once you group files, you can easily pipe them to compressing command, similar as you already tried. Proper grouping would be achieved with something like this:
$Files = Get-ChildItem 'C:\Desktop\Mobile'
$Groups = $Files | ForEach-Object {($_.Name).split('_')[0]} | Select-Object -Unique
foreach ($Group in $Groups) {
$Files | where Name -Match "^$Group" | Compress-Archive "C:\Desktop\Mobile\$Group.7z"
}
Pre Powershell v5 you can use this. No additional downloads needed.
$FullName = "Path\FileName"
$Name = CompressedFileName
$ZipFile = "Path\ZipFileName"
$Zip = [System.IO.Compression.ZipFile]::Open($ZipFile,'Update')
[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($Zip,$FullName,$Name,"optimal")
$Zip.Dispose()
With Powershell 2.0 you can't use Compress-Archive, you need download the original terminal executables to zip and unzip files from here.
You can use:
zip <path> <zip_name> -i <pattern_files>
In your example:
zip "C:\Desktop\Mobile" Apple_Files_Timestamp.zip -i Apple*.dat
zip "C:\Desktop\Mobile" Samsung_Files_Timestamp.zip -i Samsung*.dat
zip "C:\Desktop\Mobile" Sony_Files_Timestamp.zip -i Sony*.dat
If you need use adittional zip options, visit zip manual.
The following script does the grouping,
the zipping command depends on your chosen zipper.
$TimeStamp = Get-Date -Format "yyyyMMddhhmmss"
Get-ChildItem *.dat|
Group-Object {($_.Name).split('_')[0]}|
ForEach-Object {
$Make = $_.Name
Foreach($File in $_.Group){
"{0,20} --> {1}_Files_{2}.zip" -f $File.Name,$Make,$TimeStamp
}
}
Sample output:
> .\SO_44030884.ps1
Samsung_edge7.dat --> Samsung_Files_20170517081753.zip
Samsung_galaxy.dat --> Samsung_Files_20170517081753.zip
Apple_iphone6.dat --> Apple_Files_20170517081753.zip
Apple_iphone7.dat --> Apple_Files_20170517081753.zip
Sony_M2.dat --> Sony_Files_20170517081753.zip
Sony_experia.dat --> Sony_Files_20170517081753.zip
This link might help Module to Synchronously Zip and Unzip using PowerShell 2.0

Get Directories Names Only

I have a directoy X that has say 500 subdirectories. What I need is a quick way to just get only my directory and the names of these 500 subdirectories in my X directory (so, no Mode, no LastWriteTime or anything else, just the name) and pipe it to a file.
So, for example, I have this:
-X
|+Dir1
|+Dir2
|+Dir3
|
...
|+Dir500
What I want to get piped to a txt file is this
X/Dir1
X/Dir2
X/Dir3
...
X/Dir500
How can I do this using PowerShell or CommandLine?
I am using Windows 7 and PowerShell 4.0
Thanks,
Get-ChildItem will do the same thing as dir in command-line: it gets whatever is in your directory. You're looking only for directories. PS v3 and up has this built-in by using the flag of -directory. In older PowerShell versions, you can pipe your Get-ChildItem to a Where{$_.PSIsContainer to get directories and then pipe that to select Name to just get the names of the directories, not their full paths (e.g. "Dir1" vs. "X:\Dir1"). If you want the full path, use select FullName. Assuming you want that as a CSV, pipe that to Export-Csv followed by the name of the CSV you're creating, such as DirectoriesInX.csv and if you don't want the type information, add the flag of -NoTypeInformation.
PowerShell v3 and up:
Get-ChildItem "X:\" -directory | Select FullName | Export-Csv "DirectoriesInX.csv" -NoTypeInformation
PowerShell v2 and below:
Get-ChildItem "X:\" | Where{$_.PSIsContainer} | Select FullName | Export-Csv "DirectoriesInX.csv" -NoTypeInformation
I would have not used -Recurse based on requirement.
Moreover, OP wants to pipe output to a file :
(Get-ChildItem "X" -Directory).FullName | Out-File c:\myList.txt
The -Directory switch is only available from PS3.
The -Recurse switch would go as deep as possible in the tree and list all folders