IE Automation with Powershell - windows

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

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.

Exception calling fill with 1 argument ora-00933 sql command not properly ended

I'm having the above error message when I use powershell 5.1 in vscode to execute an oracle sql query. I'm testing with a simple sql statement, but eventually it will be more complicated and the more complicated sql has at least 3 semicolons.
I'm looking at remove semicolons which said to remove semicolons, but I know this won't ultimately fix it because of my query. I'm unclear what their other numbered solutions are saying to do.
I know sqlPlus isn't working for me for this because of a possible oracle driver issue. Right now I'm trying the following:
Add-Type -AssemblyName System.Data.OracleClient #at the top
...
$dataSource = "RMMPRDBLAH.company.net:1521/RMMPRDB"
$connectionString = "Data Source=$dataSource;User Id=$username;Password=$password;"
$con = New-Object System.Data.OracleClient.OracleConnection($connectionString)
$query = "select * from SEVERITY_TBL;" #Get-Content $sqlPath
Write-Host "query: $query"
$con.open()
$dtSet = New-Object System.Data.DataSet
$OracleAdapter = New-Object System.Data.OracleClient.OracleDataAdapter($query,$con)
[void]$OracleAdapter.Fill($dtSet)
Which works fine until .Fill, where I get the error message. When I remove the semicolon from my simple query, it returns results, but like I said, ultimately I have a 200 line query to read from a file which has multiple semicolons, so I need to be able to have the semicolons. I was having trouble trying to use ManagedDataAccess.Client.OracleDataAdapter (maybe I need to do something other than Add-Type -AssemblyName System.Data.OracleClient) for this, so I'm using OracleClient.OracleDataAdapter, which was fine until I found the semicolon issue. How can I get this to work with the semicolon without sqlplus? I have the Oracle Developer Tools for VS Code.
When I tried this method (which I'm not sure if this would help my semicolon issue)
$cmd = New-Object Oracle.ManagedDataAccess.Client.OracleCommand
It had this error, which I'm not sure how to fix:
New-Object : Cannot find type
[Oracle.ManagedDataAccess.Client.OracleCommand]: verify that the
assembly containing this type is loaded.
Thanks!
Update:
I was able to simplify my long query so that it doesn't have more than one semicolon. I see the issue with having multiples. After I made this change, it Got past the fill.
In .net if we are trying to try parsing wrong type it can be issue.
ex:-if we trying to do int.TryParse for long/Guid type values

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.

AppActivate does not allow to search by word

I have an application on my Windows that the window name is something like this: a random number followed by space and after that comes the name of it RECFED. For example "3894 RECFED".
I would like to send a key to that window but I can do that with AppActivate cause I can't know the exactly title of the window. I am using something like this
$wshell = New-Object -ComObject WScript.Shell
$wshell.AppActivate("RECFED")
$wshell.SendKeys('a')
The code above works if I place the exact name of the window at AppActivate but I don't have the exact name in hand all the time.
Use Get-Process to identify a process by its window title, then use that process's PID for bringing the window to the foreground:
$id = Get-Process |
Where-Object { $_.MainWindowTitle -like '*RECFED*' } |
Select-Object -First 1 -Expand Id
$wshell.AppActivate($id)
With that said, be warned that SendKeys() is a terrible (flaky, unreliable) automation approach, that should only be used as an absolute last resort when everything else has failed.

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.

Resources