Adding an Exception into try catch - powershell

I have a question regarding the Script below the script checks multiple Mailservers against RBL Lists, the Problem is that some of the Lists (like hostkarma.junkemailfilter.com) have certain answers that do not mean that the IP is actually blacklisted.
So I want to add an Exception into the try..catch function that if hostkarma.junkemailfilter.com answers with 127.0.2.1 the Mailserver is not getting into $blacklistedOn.
The Script:
Param([string]$IP)
$statusAlive = "ScriptRes:Host is alive:"
$statusDead = "ScriptRes:No answer:"
$statusUnknown = "ScriptRes:Unknown:"
$statusNotResolved = "ScriptRes:Unknown host:"
$statusOk = "ScriptRes:Ok:"
$statusBad = "ScriptRes:Bad:"
$statusBadContents = "ScriptRes:Bad contents:"
$reversedIP = ($IP -split '\.')[3..0] -join '.'
$blacklistServers = #(
"dnsbl-3.uceprotect.net";
"dnsbl-2.uceprotect.net";
"dnsbl-1.uceprotect.net";
"ix.dnsbl.manitu.net";
"bl.spamcop.net";
"bl.spamcannibal.org";
"b.barracudacentral.org";
"ips.backscatterer.org";
"dnsbl.sorbs.net";
"cbl.abuseat.org";
"all.spamrats.com";
"black.uribl.com";
"multi.surbl.org";
"multi.uribl.com";
"truncate.gbudb.net";
"bl.blocklist.de";
"dnsbl.inps.de";
"bl.mailspike.net";
"bl.spameatingmonkey.net";
"db.wpbl.info";
"dnsbl.justspam.org";
"hostkarma.junkemailfilter.com";
"mail-abuse.blacklist.jippg.org";
"psbl.surriel.com";
"spam.dnsbl.anonmails.de";
"ubl.unsubscore.com";
"cblless.anti-spam.org.cn";
"cblplus.anti-spam.org.cn";
"spam.pedantic.org";
"dev.null.dk";
"blackholes.five-ten-sg.com";
"spamsources.fabel.dk";
"zen.spamhaus.org";
"spamguard.leadmon.net";
"dialups.visi.com";
"dnsbl.kempt.net";
"dnsbl.dronebl.org";
"no-more-funn.moensted.dk";
"relays.bl.kundenserver.de";
)
$blacklistedOn = #()
foreach ($server in $blacklistServers) {
$IPServer = "$reversedIP.$server"
try {
$null = [System.Net.Dns]::GetHostEntry($IPServer)
$blacklistedOn += $server
} catch { }
}
if ($blacklistedOn.Count -gt 0) {
# The IP was blacklisted on one or more servers; send your email here.
# $blacklistedOn is an array of the servers that returned positive results.
Write-Host "$statusAlive"$blacklistedOn
} else {
Write-Host "$statusDead"$blacklistedOn
}

Currently your script discards whatever is returned by your host lookup. To skip over an address if a particular response is returned you need to assign it to a variable and check that before actually adding the server to $blacklistedOn:
try {
$addr = [Net.Dns]::GetHostEntry($IPServer)
if (-not ($server -eq 'hostkarma.junkemailfilter.com' -and $addr.AddressList.IPAddressToString -contains '127.0.2.1')) {
$blacklistedOn += $server
}
} catch { }

Related

How to check if Certain programs are installed and if they are display message

I'm trying to create a script that will check if Program A,B,C and D is installed. If so display message to say they are all installed else say they are not installed.
From research i have created the following script.
$ProgramList = #("A","B","C","D")
ForEach ($Program in $ProgramList){
Function Get-InstalledApps
{
$Regpath = #(
'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
Get-ItemProperty $Regpath | .{Process{If($_.DisplayName) { $_ } }}
}
$Result = Get-InstalledApps | Where {$_.DisplayName -like "*$Program*"}
If ($Result) {
[Windows.Forms.Messagebox]::Show("INSTALLED")
} Else {
[Windows.Forms.Messagebox]::Show("NOT INSTALLED")
}
}
My issue is when i run this i get 4 message boxes popup to say the program is installed. i'm trying to make this so it will just give a single message box. if all are installed and if one or more is not installed another message box to say the programs are not installed.
Any help is greatly Appreciated.
You're getting four pop-ups because your calling the msgbox four times (as it's within your loop). Just moving it out doesn't completely solve your problem since it's going to only look at the last one, but if you need to do it the way you are, then something like this would work:
$ProgramList = #("A","B","C","D")
$allInstalled = $true # Assume they're all installed
ForEach ($Program in $ProgramList){
Function Get-InstalledApps {
$Regpath = #(
'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
Get-ItemProperty $Regpath | .{Process{If($_.DisplayName) { $_ } }}
}
If(-not(Get-InstalledApps | Where {$_.DisplayName -like "*$Program*"})) {
# We know at least one isn't installed
$allInstalled = $false
}
}
If($allInstalled) {
[Windows.Forms.Messagebox]::Show("INSTALLED")
} Else {
[Windows.Forms.Messagebox]::Show("NOT INSTALLED")
}
If you're able to tweak the function a bit, you can speed it up by only pulling in the registry information once. The BEGIN section here runs just once when you call the function with multiple applications.
Function Test-InstalledApps {
Param(
[Parameter(ValueFromPipeline)]
[string[]]$appName
)
Begin {
$Regpath = #('HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*','HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*')
$allApps = Get-ItemProperty $Regpath | Select DisplayName
$allAppsInstalled = $true
}
Process {
ForEach($app in $appName) {
If(-Not($allApps | Where-Object { $_.DisplayName -like "*$app*" })) {
# We know at least one isn't installed
$allAppsInstalled = $false
}
}
}
End {
Return $allAppsInstalled
}
}
If(Test-InstalledApps #("A","B","C")) {
[Windows.Forms.Messagebox]::Show("INSTALLED")
} Else {
[Windows.Forms.Messagebox]::Show("NOT INSTALLED")
}
I think this might be a bit better approach, using Out-GridView. In my opinion this would look cleaner, I know this is not answering your question but it might suit you better:
$programList = #(
'SomeRandomProgram1'
'Microsoft Visual'
'7-Zip'
'SomeRandomProgram2'
)
$Regpath = #(
'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
$installedPrograms = (Get-ItemProperty $Regpath).where({$_.DisplayName})
$result = foreach($program in $programList)
{
$check = $installedPrograms.DisplayName -match $program
if($check)
{
foreach($match in $check)
{
[pscustomobject]#{
Program = $program
Status = 'Found'
Match = $match
}
}
continue
}
[pscustomobject]#{
Program = $program
Status = 'Not Found'
Match = $null
}
}
$result | Out-GridView
In my computer the OGV looks like this:
I know I'm a bit late to the game here and my answer is you might say completely different buy I did it on a quest to learn a few things and thought others might find it interesting. It produces output like this.
The interesting part is that the menu is self adjusting, within screen size limits, so you can search for more or fewer programs just by passing them in an array. The program searched directories vs the Registry so you can locate programs which are not installed (portable). By default it searches the two Windows locations but you can also pass it an array with additional search locations.
I'm sure it wouldn't take to much to modify to have it search the registry keys instead.
If you're interested you can download a zip file (needed to include the graphics files for the check mark and red x from my OneDrive here.

Powershell scripting for url custom monitoring

I am trying to build a custom script for URL monitoring. I am able to run the URL's from the file and enter the same in a logfile(named with time stamp).
Till here I have completed
Issue is when I compare the values from present(present timestamp) and previous logfile(previous timestamp).
This portion is not working fine. Please help me correct it.
Here is my code trying to compare value line by line from present logfile and previous logfile and run commands to generate output:
# New log is new logfile data
$Newlog = Get-Content $URLlogfile
$old_file = Dir C:\Scripts\logs | Sort CreationTime -Descending | Select Name -last 1
# Old log is Old logfile data
$oldlog = Get-Content $old_file -ErrorAction SilentlyContinue
Foreach($logdata in $Newlog) {
$url = ($logdata.Split(" "))[0]
$nodename = ($logdata.Split(" "))[1]
$statuscheck = ($logdata.Split(" "))[2]
$description = ($logdata.Split(" "))[3]
$statuscode = ($logdata.Split(" "))[4]
Foreach($log1data in $oldlog) {
$url1 = ($log1data.Split(" "))[0]
$nodename1 = ($log1data.Split(" "))[1]
$statuscheck1 = ($log1data.Split(" "))[2]
$description1 = ($log1data.Split(" "))[3]
$statuscode1 = ($log1data.Split(" "))[4]
While ($url = $url1) {
if ($statuscheck = $statuscheck1 ) {
write-output "output is same"
} elseif ($statuscheck = Fail) {
While ($statuscheck1 = Pass) {
write-output "$url is down at $nodename1- testing event sent"
}
} elseif ($statuscheck = Pass) {
While ($statuscheck1 = Fail) {
write-output "$url is up at $nodename1- testing event sent"
}
}
}
Break
}
}
#At end am clearing the old logs except present one
dir C:\Scripts\logs -recurse | where { ((get-date)-$_.creationTime).minutes -gt 3 } | remove-item -force
Per the comment from BenH, the following part of your code needs correcting as follows:
If ($url -eq $url1) {
if ($statuscheck -eq $statuscheck1 ) {
write-output "output is same"
} elseif ($statuscheck -eq 'Fail' -and $statuscheck1 -eq 'Pass') {
write-output "$url is down at $nodename1- testing event sent"
} elseif ($statuscheck -eq 'Pass' -and $statuscheck1 -eq 'Fail') {
write-output "$url is up at $nodename1- testing event sent"
}
}
Corrections:
In your comparison statements the = needs to be -eq. In PowerShell = always assigns a value.
In your comparison statements Pass and Fail need to be surrounded by single quotes so they are treated as strings (otherwise they are treated like function statements, for functions which don't exist).
I've replaced the While statements with If statements. I'm not sure what the intent of those was but I think they'd just get stuck in an infinite loop as the variable they test is never changed from within the loop.

How do I create an exclusion into query results?

Forgive me for I am relatively new to PowerShell. I have an issue were we need to log off all disconnected users with the exception of one ID. I found this script which does a great job logging off the disconnected users. My question is how would I modify this section to ignore one or more specified users from the query results?
function Get-Sessions
{
$queryResults = query session
$starters = New-Object psobject -Property #{"SessionName" = 0; "UserName" = 0; "ID" = 0; "State" = 0; "Type" = 0; "Device" = 0;}
foreach ($result in $queryResults)
{
try
{
if($result.trim().substring(0, $result.trim().indexof(" ")) -eq "SESSIONNAME")
{
$starters.UserName = $result.indexof("USERNAME");
$starters.ID = $result.indexof("ID");
$starters.State = $result.indexof("STATE");
$starters.Type = $result.indexof("TYPE");
$starters.Device = $result.indexof("DEVICE");
continue;
}
New-Object psobject -Property #{
"SessionName" = $result.trim().substring(0, $result.trim().indexof(" ")).trim(">");
"Username" = $result.Substring($starters.Username, $result.IndexOf(" ", $starters.Username) - $starters.Username);
"ID" = $result.Substring($result.IndexOf(" ", $starters.Username), $starters.ID - $result.IndexOf(" ", $starters.Username) + 2).trim();
"State" = $result.Substring($starters.State, $result.IndexOf(" ", $starters.State)-$starters.State).trim();
"Type" = $result.Substring($starters.Type, $starters.Device - $starters.Type).trim();
"Device" = $result.Substring($starters.Device).trim()
}
}
catch
{
$e = $_;
Write-Log "ERROR: " + $e.PSMessageDetails
}
}
}
Thank you for any input you may have.
So you want to omit certain results? You are using PowerShell objects so this should be easy with Where-Object
$omissions = "user1","user2"
foreach ($result in $queryResults)
{
# Stuff inside the loop
} | Where-Object{$omissions -notcontains $_.Username}
Tack on the where-object and we use -notcontains to see if a given user is not in the array of $omissions. Which is of course a list of users.
Caveat
You might have issues with the output of that foreach construct. I have seen this in PowerShell versions earlier than 3.0. If that does happen you could just capture the New-Objects into an array to later filter or use ForEach-Object.

Powershell - match with Containskey & set value of hashtable don't work

I am working on a script by Richard L. Mueller to disable inactive account in our AD.
Trap {"Error: $_"; Break;}
$D = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$Domain = [ADSI]"LDAP://$D"
$Searcher = New-Object System.DirectoryServices.DirectorySearcher
$Searcher.PageSize = 200
$Searcher.SearchScope = "subtree"
$Searcher.Filter = "(&(objectCategory=person)(objectClass=user))"
$Searcher.PropertiesToLoad.Add("samAccountName") > $Null
$Searcher.PropertiesToLoad.Add("lastLogon") > $Null
$Searcher.PropertiesToLoad.Add("accountExpires") > $Null
# Create hash table of users and their last logon dates.
$arrUsers = #{}
# Enumerate all Domain Controllers.
ForEach ($DC In $D.DomainControllers)
{
$Server = $DC.Name
$Searcher.SearchRoot = "LDAP://$Server/" + $Domain.distinguishedName
$Results = $Searcher.FindAll()
#$Results[100].Properties.item("samAccountName")
#$Results[100].Properties.item("lastlogon")
ForEach ($Result In $Results)
{
$DN = $Result.Properties.Item("samAccountName")
$LL = $Result.Properties.Item("lastLogon")
If ($LL.Count -eq 0)
{
$Last = [DateTime]0
}
Else
{
$Last = [DateTime]$LL.Item(0)
}
If ($Last -eq 0)
{
$LastLogon = $Last.AddYears(1600)
}
Else
{
$LastLogon = $Last.AddYears(1600).ToLocalTime()
}
If ($arrUsers.ContainsKey("$DN"))
{
If ($LastLogon -gt $arrUsers["$DN"])
{
$arrUsers["$DN"] = $LastLogon
}
}
Else
{
$arrUsers.Add("$DN", $LastLogon)
}
}
}
Now I have the most updated LastLogon date of my AD users.
Then I do:
Foreach ($ou in $searchRoot) {
$inactiveUsers += #(Get-QADUser -SearchRoot $ou -Enabled -PasswordNeverExpires:$false -CreatedBefore $creationCutoff -SizeLimit $sizeLimit | Select-Object Name,SamAccountName,LastLogonTimeStamp,Description,passwordneverexpires,canonicalName | Sort-Object Name)
}
I do not use this to disable the ID because LastLogonTimeStamp has a delay being updated from 9-14 days. And with the real last logon date in $arrUsers, I would like to replace LastLogonTimeStamp with it. So I want to match them using the user ID:
Foreach ($inuser in $inactiveUsers) {
If ($arrUsers.ContainsKey("$inuser.samAccountName"))
{
write-host "True"
$inuser.LastLogonTimeStamp = $arrUsers["$inuser.samAccountName"]
$inuser.LastLogonTimeStamp = $inuser.LastLogonTimeStamp.adddays(30)
If ((Get-Date) -gt $inuser.LastLogonTimeStamp)
{
write-host $inuser.samAccountName "should be disabled"
}
Else
{
write-host $inuser.samAccountName "is still active"
}
}
}
Else
{
write-host "False"
}
I have 2 problems here.
First the "If ($arrUsers.ContainsKey("$inuser.samAccountName"))" doesn't seems working. I always get a false result.
Second, to replace the LastLogonTimeStamp using "$inuser.LastLogonTimeStamp = $arrUsers["$inuser.samAccountName"]", my LastLogonTimeStamp become blank.
Could someone able to provide some assistants?
You're not using variable expansion correctly. Object properties aren't expanded, so this
"$inuser.samaccountname"
is actually:
$inuser.ToString() + ".samaccountname"
To expand an expression in a string, you must surround it with $(), e.g.
"$($inuser.samaccountname)"
In your case, however, you don't even need to do that. Leave the quotes out entirely:
$arrusers[$DN]
$arrusers.ContainsKey($inuser.samaccountname)
See the about_Quoting_Rules help topic for details.
I have solved this by assign the samAccountName value to another variable:
$tmpAccountName = $inuser.samAccountName
then
If ($arrUsers.ContainsKey("$tmpAccountName"))
instead of throw the $inuser.samAccountName directly to the checking. Not so sure why it cannot be read directly however at least it is solved now =). Same goes to the problem #2.

PowerShell GUI Output

I'm writing a small PowerShell script, with GUI, that processes the Ping command. Currently I have the script reading input from the user to determine the IP Address / URL to ping, and then displaying the output to a rich-text-box.
However, currently, the command COMPLETES then writes the entire output at once. I want it to display each line of output in real time - so that it appears the same way that it would running the command in the shell.
When the Ping button is pushed, the following function is called (and I think this is where the issue is):
Function FNPing {
$OutputBox.Text = "Please Wait..."
$ping = ping ($InputBox.text)
$OutputBox.Text = ""
foreach ($line in $ping) {
$OutputBox.Appendtext($line+[char]13+[char]10)
}
}
I imagine that the issue can probably be solved in the ForEach statement, I'm just not aware of how to do it! All help is appreciated!
I would try using the test-connection cmdlet. The problem is that ping an external exe so all you are getting out of it is a blob of text when it completes. The only way to get the output of ping while it is running is going to be by using start-process and redirecting the output (this is quite messy).
With test-connection you won't get a pretty summary but the info is all there. For the summary info, measure-object can help. Here's an imitation of ping's output that should get you started:
function fnping {
$outputBox.Text = "Please Wait..."
$count = 4
$results = test-connection $inputbox.Text -count $count | foreach { $outputBox.AppendText("Reply from $($_.ProtocolAddress): bytes=$($_.ReplySize) time=$($_.ResponseTime)ms TTL=$($_.TimeToLive)`r`n"); $_ }
$summary = $results | measure-object -Property ResponseTime -Average -Minimum -Maximum
$lost = $count - $summary.Count
$percentLost = $lost * 100.0 / $count
$outputBox.AppendText("Packets: Sent = $count, Received = $($summary.Count), Lost = $lost ($($percentLost)% loss)`r`n")
$outputBox.AppendText("Minimum = $($summary.Minimum)ms, Maximum = $($summary.Maximum)ms, Average = $($summary.Average)ms`r`n")
}
Edit
Actually I stand corrected. Assigning the result to a variable ($ping) is causing powershell to wait for the output stream to be closed. You can easily do what you want, with foreach-object. Here I use a small helper function to clear the output box before writing the to the box:
function WriteEach-Object() {
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[object[]]$inputs
)
begin { $outputBox.Text = "" }
process { $inputs | foreach { $outputBox.AppendText($_) } }
end { $outputBox.AppendText("`r`n") }
}
function fnping() {
$outputBox.Text = "Please Wait..."
ping $inputBox.Text | writeeach-object
}
You need to add BEGIN {} PROCESS {} and END {} statements to control how your function works. If no such are given, Powershell assumes that everything is in the END {} statement, processing everything in one go.
Changing into this
Function FNPing {
BEGIN {
}
PROCESS {
$OutputBox.Text = "Please Wait..."
$ping = ping ($InputBox.text)
$OutputBox.Text = ""
foreach ($line in $ping) {
$OutputBox.Appendtext($line+[char]13+[char]10)
}
}
END {
}
}
should do the trick. Alternatively, you can replace Function with Filter, which assumes the code resides in the PROCESS {} block.
For more info: http://technet.microsoft.com/en-us/magazine/hh413265.aspx