How to find and delete duplicate Outlook emails in file explorer with PowerShell? - windows

Please forgive me, I have little experience with PowerShell, but I know in theory what i need to do.
I have been given a list of 21,000 outlook emails and told to delete duplicates. The server uploaded these incorrectly somehow.The subject of the emails is a randomly generate string so is unique. I need to delete duplicates based on email size and manually opening the email to check that the content is the same. Rather than eyeballing them and comparing them manually which will take me approximately 25 years lol, does anyone know how to do this in PowerShell?
E.g. traverse through Outlook files line by line. IF one file size matches the previous, open both emails. AND Compare first line of email with both emails. IF they both match in terms of file size and content,delete one email. Surely this wouldn't be too difficult to do? Please help me, I cannot fathom looking at 21k emails!!
I've got powershell open and I navigated to the directory which hosts the 21k outlook emails. Please can someone help me? I know i am going to need some sort of loop in there and for 21k files it isn't going to be quick, but eyeballing them and doing it manually will take MUCH MUCH longer, the thought of manually doing it is giving me shivers...
Thanks! Much appreciated!
I am in powershell and navigated to the directory which hosts the 21k emails. I now need to find out how to traverse through line by line, find matching sizes and compare content, IF both are true then delete one file. I am not a programmer and I don't want to mess up by randomly winging it.

I'm not sure I understood everything but this might help you:
$Outlook = New-Object -ComObject Outlook.Application
$Namespace = $Outlook.GetNamespace("MAPI")
$Folder = $Namespace.GetDefaultFolder(6) # 6 is the index for the Inbox folder
$Emails = $Folder.Items
$Emails.Sort("[ReceivedTime]", $true)
$PreviousEmail = $null
foreach ($Email in $Emails) {
if ($PreviousEmail -ne $null -and $Email.Subject -eq $PreviousEmail.Subject -and $Email.ReceivedTime -eq $PreviousEmail.ReceivedTime) {
Write-Host "Deleting duplicate email with Subject:" $Email.Subject "and Received Time:" $Email.ReceivedTime
$Email.Delete()
}
$PreviousEmail = $Email
}
To run this tho, you need to change the directory to the outlook file locations. You can use "cd C:\PATH" or "Set-Location C:\PATH".
Eg:
cd C:\Users\MyUserName\Documents\Outlook\
Give it a try and let me know if works or it errors out. You might need to adjust some lines, I don't have outlook on my PC to test.
Also please do a backup/copy of the folder before running the script, in case it does delete emails it shouldn't.

Related

Reading REG_QWORD with VBScript?

I think the question speaks for itself. I have trouble getting some values out of the registry, and I was hoping someone around here might help me.
I'm stuck at IE9, as it is the only one which has some reasonable CSS capabilities, and does support GetObject().
So right now, lets say I'm trying to retrieve the memory size of a GPU at "HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000\HardwareInformation.qwMemorySize" (as far as I know, this should be a universal path & key).
This is where the problem begins. Either I get no output, or some error saying something is different, or what (my system is running in a different language so I cant offer the right translation).
After some research, I seem to have found the issue - the value I'm trying to read is REG_QWORD, and unfortunately I was only able to find very little covering this topic, and most of the solutions did not work for me.
So right now, I am with this code, which, unsurprisingly, also does not work (the code I had since like the beginning):
for Each oItem in colGPUs
memory = oItem.AdapterRAM / 1048576
If memory < 0 Then
If InStr(oItem.Name, "NVIDIA") Then
Set wssx = CreateObject("WScript.Shell")
msgbox CStr(wssx.RegRead("HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\000" + GPUID + "\HardwareInformation.qwMemorySize"))
End If
End If
Unfortunatelly it seems like there is no direct way of retrieving the value - within HTA itself.
I was able to get the value, however I did it using Powershell, executed the command, set its output to a specific file and read it.
Anyways, here is the actual solution I came up with specifically for this issue
wshell.Run "powershell (Get-ItemPropertyValue 'HKLM:\SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000' 'HardwareInformation.qwMemorySize') | Out-File -FilePath C:\temp\gpu_mem.txt", 0, true
Set f = fso.OpenTextFile("C:\temp\gpu_mem.txt", 1, False, -1)
gpu_mem = CStr(f.ReadAll)
With this method Im directly obtaining the integer and passing it to the VBS

what's cleanest way in adding local windows firewall rules with powershell

I have written the following simple script that reads a csv file then iterates through each and adds in some firewall rules to a local machine.
I wanted to know if this the 'cleanest' way of achieving this. (i know there is gpo etc) but I am trying more of my powershell. I'll also try and add a check if the rule names already exist or not.
$rulenames = Import-Csv -Path "C:\temp\newrulenames.csv"
ForEach ($name in $rulenames) {
New-NetFirewallRule -DisplayName $name.DisplayName -Name $name.Name -Enabled $name.Enabled -Profile $name.Profile -Direction $name.Direction -Action $name.Action -Protocol $name.Protocol -Program $name.Program -EdgeTraversalPolicy $name.EdgeTraversalPolicy
}
GPO seems like the way to go really but since you already are aware of that and are trying out PowerShell...
In cases like this I personally like to use something called splatting (see about_Splatting) since it improves script readability quite a bit in my opinion. For your particular code it could look something like this;
$rulenames = Import-Csv -Path "C:\temp\newrulenames.csv"
foreach($name in $rulenames)
{
$ruleProps=#{
DisplayName = $name.DisplayName
Name = $name.Name
Enabled = $name.Enabled
Profile = $name.Profile
Direction = $name.Direction
Action = $name.Action
Protocol = $name.Protocol
Program = $name.Program
EdgeTraversalPolicy = $name.EdgeTraversalPolicy
}
New-NetFirewallRule #ruleProps
}
And, in order to run some checks (rule name as you mentioned for instance) this could be done right away within the hashtable. For instance you could do something like this;
$ruleProps=#{
DisplayName = $name.DisplayName
Name = if(Get-NetFireWallRule -Name $name.Name -ErrorAction SilentlyContinue) { "$($name.Name)-2" } else { $name.name }
Enabled = $name.Enabled
Profile = $name.Profile
Direction = $name.Direction
Action = $name.Action
Protocol = $name.Protocol
Program = $name.Program
EdgeTraversalPolicy = $name.EdgeTraversalPolicy
}
In this hashtable example we run a check right away if we get a hit on that particular rule name. If it's a hit, we add -2 to the end of the name, else we name it as originally intended.
Alrighty, that was my lunch break - I will check back if you have any follow up questions and double check if I had any typos, sloppy mistakes or whatever, just figured I'd give you an idea of what you can do.
Edit: Well I came back sooner than I had imagined. In the second hashtable example, quite a bit of the improved readability I mentioned in example 1 actually gets lost. Maybe not with only the one name check but certainly so if you run multiple checks. It will most likely still work but - as I said - the readability might suffer.

Powershell Auditing effective windows share permissions and outputting to CSV

Powershell noob here. I'm trying to create a script that will take a list of users, a list of network shares, enumerate effective permissions for the user/share and output to CSV for audit purposes.
I've had success using GetPACEffectiveAccess from the PowerShellAccessControl Module on Technet gallery to do the enumeration, but am getting completely stuck on how to output this data to CSV how I want it.
The code I have is pretty simple:
$users=gc C:\scripts\users.txt
$shares=gc C:\scripts\shares.txt
foreach ($user in $users) {
foreach ($share in $shares) {
Get-PACEffectiveAccess -Path $share -Principal $user | select-object principal, accessmask, path | Export-Csv C:\scripts\EffectivePermissions\Audit.csv -append -NoTypeInformation}}
Since I have no idea how to do tables on StackOverflow (wow I am bad at this) I have attached a screenshot of the output I am getting from my simple script, and then the output I would like to get.
Any help would be much appreciated!
Thanks
After you gather the data, instead of outputting straight to the csv, you could add it to a two dimensional array, format it the way you'd like, and outfile it to a csv.

Change Chrome Settings via Powershell

I want to make a script to change the default page zoom in Chrome, however I do not know where these options are stored.
I guess that I have to find an appropriate options text file, parse it, and then make a textual replacement there, using powershell in order to apply the changes.
I need to do it every time I plugin my laptop to an external monitor, and it is kinda annowying to do it by hand
Any ideas?
The default zoom level is stored in a huge JSON config file called Preferences, that can be found in the local appdata folder:
$LocalAppData = [Environment]::GetFolderPath( [Environment+SpecialFolder]::LocalApplicationData )
$ChromeDefaults = Join-Path $LocalAppData "Google\Chrome\User Data\default"
$ChromePrefFile = Join-Path $ChromeDefaults "Preferences"
$Settings = Get-Content $ChromePrefFile | ConvertFrom-Json
The default Page Zoom level is stored under the partition object, although it seems to store it as a unique identifier with some sort of ratio value, this is what it looks like with 100% zoom:
PS C:\> $Settings.partition.default_zoom_level | Format-List
2166136261 : 0.0
Other than that, I have no idea. I don't expect this to be a good idea, Chrome seems to update a number of binary values everytime the default files are updated, you might end up with a corrupt Preferences file
$Env:
Is a special PSdrive that contains many of the SpecialFolder Paths
$Env:LOCALAPPDATA
and
[Environment]::GetFolderPath( [Environment+SpecialFolder]::LocalApplicationData )
Yield the same result in addition to the rest of Mathias answer.

Mass Scanning and Renaming of File Names

I have searched all over StackOverflow and other parts of the internet and so far I have found nothing that can help solve my problem.
My problem is that in iTunes I have about 220 songs that are listed like "Artist Name - Song Name" (ex. "Modest Mouse - Dramamine") and I am wondering if there is any way that I can write a Ruby script that would change those to listing properly in iTunes with the word(s) after the hyphen ("Dramamine") becoming the song title and the word(s) before the hyphen ("Modest Mouse") becoming the artist.
Also, if there are any programs that could accomplish the same goal it would be appreciated if someone could share a link, however, if anyone could point me to somewhere where I can learn how to mass scan and make changes to filenames in Ruby then put those song changes into songs that iTunes can read and display then that would also be greatly appreciated. Thanks!
Check out the Regex and File Ruby Libraries, as well as Dir. They give you the tools for the job.
For example,
Dir["path/to/your/music/*.m4a"].each do |filename|
m = File.basename(filename).match(/(.+)-(.+)/)
artist = m[1].strip # remove leading and trailing blanks
song = m[2].strip
new_filename = song + ' - ' + artist
File.rename(filename, "path/to/your/music/#{new_filename}.m4a"
|end|
There are other tricks, for instance you can use back-ticks (`, not ') to do file renaming by replacing the last line in the block with:
`mv #{filename} path/to/your/music/#{new_filename}.m4a`
This will run the mv command in the Unix shell.
Good luck tinkering around! Nothing beats learning by trial and error!

Resources