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

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.

Related

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

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.

Sort a property in HCL Block

I would like to move name property to the top of the block like this.
There are many resouce block in a file.
Before
resource "datadog_monitor" "A" {
enable_logs_sample = "true"
name = "name"
tags = ["env:dev"]
}
resource "datadog_monitor" "B" {
enable_logs_sample = "true"
name = "name"
tags = ["env:dev"]
}
After
resource "datadog_monitor" "A" {
name = "name"
enable_logs_sample = "true"
tags = ["env:dev"]
}
resource "datadog_monitor" "B" {
name = "name"
enable_logs_sample = "true"
tags = ["env:dev"]
}
OK, I think :help :global and the range/address mechanism is one of Vim's best and most underrated feature so it might deserve a detailed run down.
The core editing pattern is the same as in the commands I suggested in my previous answer to a similar question of yours:
on each line matching a specific regular expression pattern,
do something.
Note that it is a "pattern", not a one-off trick. You are not supposed to "learn" this answer by heart, or commit it to "muscle memory", or bookmark it for the next time you have the exact same problem. Instead, you are supposed to grok the logic behind it in a way that allows you to:
recognize a situation where it might come handy,
apply it without too much thinking.
So, in order to implement the editing pattern described above, we use the :global command, which works like this:
:[range]global/<pattern>/[range]<command>
where:
in range (optional, default is %),
mark each line matching <pattern>,
then execute <command> on range (optional, default is .).
Like lots of things in Vim, :global is conceptually cool but not necessarily useful on its own. The more familiar you are with ranges/addresses and the more Ex commands you know, the more useful it is.
In this specific case, ranges don't matter much but addresses and Ex commands do… and their sum makes problems like these solvable in, IMO, a pretty intuitive way.
Now, let's go back to our problem:
move every "name" line to the top of the block
and express it in terms that match our editing pattern:
mark every line matching name,
then move it below the closest line above matching resource.
Which is a simple:
:g/name/m?resource?
Of course, the exact regular expression patterns to use are context-dependent.
The trick is to internalize the patterns so that you already know how to use any new building block you might come upon.
There is really nothing even remotely god-like, here.

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

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.

IE Automation with Powershell

I am attempting to automate the login to website on our intranet using Powershell and IE. So far, I have the following code that works:
$ie = new-object -com "InternetExplorer.Application"
$ie.navigate("https://somepage/jsp/module/Login.jsp/")
while($ie.ReadyState -ne 4) {start-sleep 1}
$ie.visible = $true
$doc = $ie.Document
If ($doc.nameProp -eq "Certificate Error: Navigation Blocked") {
$doc.getElementByID("overridelink").Click()
}
$loginid = $doc.getElementById("loginid")
$loginid.value= "username"
$password = $doc.getElementById("password")
$password.value = 'somepass'
$ie.navigate("javascript:doSubmit('login')",$null,$true)
So, the problem area I have now is that the site closes the original window used to login and opens a new IE window. How do I go about submitting inputs to that new window? I know I can use something like tasklist.exe /v to get the PID of the new window... but I'm not sure how I would go about taking control of it.
Also, after viewing my code, please know I do not intend to use embedded user name and passwords. That's only in place so that I don't have to continually type a un/pw combo every time I want to test the script.
After trying a bit more...
$applist = new-object -com shell.application
$newie = $applist.windows() | where {$_.Type -eq "HTML Document" -and $_.LocationURL -match "MainFrame.jsp"}
I found an old article that describes how to do what you're looking for, by looping through the windows of a Shell.Application object. I have to say that while it looks possible and does seem to answer your direct question, the approach seems pretty unpleasant and fragile to me.
If you're not averse to trying a different approach, I would suggest giving Selenium Webdriver a shot. You can use the Internet Explorer driver, and the C# examples in the documentation generally translate nicely into PowerShell. You get some other nice benefits too, like drivers for other web browsers or the ability to wait for a condition instead of relying on sleep/check loops. You will also have a driver.switchTo() function that allows you to bounce between windows or frames.
Try this.
$shell = (New-Object -ComObject Shell.Application).Windows()
$shell.Count
7
you have items 0-6 you can check this way.
$shell.Item(0).LocationName
or
$shell.Item(0).LocationURL
$shell.Item(1).LocationName
$shell.Item(1).LocationURL

Resources