Click an Ad

If you find this blog helpful, please support me by clicking an ad!

Monday, October 12, 2015

My Neverending Ping Script

It happened that I needed to remote into every one of our computers to do some work. Of course, some systems were offline, go figure. After working through my computer list and removing the ones I was able to access and fix, I wrote a script to ping the rest of them and email me when they were found to be online.

The way the script works, is that you feed it a list of computers, and it goes through trying to ping them. If the ping is successful, it send me an email, then removes that system from the list. The script keeps running until the amount of systems in the list reaches 0.

########################### BEGIN SCRIPT ###########################

#Variables
$ComputerListPath = "C:\Temp\NeverendingPingList.txt"
$To = "me@contoso.com"
$From = "help@contoso.com"
$Body = "Responding to pings!"
$SMTPServer = "mailserver.contoso.com"

#Get Computer List
$Computers = Get-Content $ComputerListPath

#Count the number of computers
$Count = ($Computers | Measure-Object).count

If ($Count -le 0)
{
Write-Host "No computers to ping, exiting"
exit
}

#Create a "matchlist" - if a computer responds, it is added to this list, then when the script iterates through the foreach loop, it skips the computers that exist in this list
[System.Collections.ArrayList]$Matchlist = @()

#Clear the screen
Clear-host

#For each computer in the list, ping it, while the count is greater than 0
Do
{
foreach ($Computer in $Computers)
{
#If the computer matches a member of the matchlist, skip to the next iteration of the foreach loop
If ($Matchlist -contains $Computer) { Continue }

#Test the connection to the computer, using 1 ping packet
$Result = (Test-Connection -ComputerName $Computer -Count 1 -Quiet)

#If no ping response, write to host
If ($Result -eq $false) { Write-Host "No ping received from $Computer - will pass again" }

#If it responds, email me AND remove it from the list of computers, then recount computers
If ($Result -eq $true)
{
$Subject = "$Computer is on the network!!!"
Send-MailMessage -To $To -From $From -SmtpServer $SMTPServer -Body $Body -Subject $Subject
$Matchlist.Add($Computer)
} #End If
} #End Foreach

Write-Host "`r`n========================================================`r`n========================================================`r`n"

#Sleep for 30 seconds
Start-Sleep -Seconds 30

} while ($Count -gt 0)

########################### END SCRIPT ###########################

Thursday, October 8, 2015

Getting the Windows Install Date from ALL of Your Computers

This script is inspired by a post I ran across on Windows Networking showing how to extract and format the Windows installation date via Powershell. This data would be useful to me for planning hardware refreshes.

On to the script!

################ BEGIN SCRIPT ################

#Purpose: To scan computers for Windows Install Date

#Choose whether this is an initial scan or a rescan
$InitialChoice = Read-Host "Enter 'I' for initial scan, or 'R' for rescan"

#Output file variables
$MissedComputersFile = "C:\Temp\Script - InstallDatesMissed.txt"
$OutputFile = "C:\Temp\Script - InstallDates.csv"

#Get the computer list, depending on initial choice
If ($InitialChoice -like "I"){
    $Computers = get-adcomputer -filter * | select name | sort name
} #End If

If ($InitialChoice -like "R"){
    $Computers = Get-Content $MissedComputersFile
    Remove-Item $MissedComputersFile -Force
} #End If

#Build an empty array for the data
$Results = @()

#Foreach computer in the list
Foreach ($Computer in $Computers){
    
    #Here I need to pick a naming method but it's dependent on where the data came from (the initial choice and subsequent computer name import)
    #For the initial scan, where data comes from AD:
    If ($InitialChoice -like "I"){$TestSystem = ($Computer.Name)}

    #For the rescan, where data comes from the text file:
    If ($InitialChoice -like "R"){$TestSystem = $Computer}
    
    #Test the connection
    If (Test-Connection -ComputerName $TestSystem -count 1 -quiet){
        
        #Create a new object to populate
        $ResultsEntry = New-Object System.Object
        
        #Get the computer's install date from WMI
        $InstallDate = (Get-WmiObject -ComputerName $TestSystem win32_operatingsystem | select @{Name="InstallDate"; Expression={$_.ConvertToDateTime($_.InstallDate)}}).InstallDate
        
        #Reformat the install date
        $InstallDateRefined = ($InstallDate.Year).ToString() + "-" + ($InstallDate.Month).ToString() + "-" + ($InstallDate.Day).ToString()
        
        #Add the name of the computer to the object
        $ResultsEntry | Add-Member -type NoteProperty -name Name -value $TestSystem
        
        #Add the install date to the object
        $ResultsEntry | Add-Member -type NoteProperty -name OSInstallDate -value $InstallDateRefined
        
        #Add the object to the array
        $Results += $ResultsEntry

        #Print a status message to the screen
        Write-Host -ForegroundColor Green "$TestSystem - Windows Install Date is $InstallDateRefined"
    } #End If
    
    #This runs if the test-connection is no good
    Else {
        
        #Adds the name of the system to the "Missed" file
        $TestSystem | Add-Content $MissedComputersFile

        #Print a status message to the screen
        Write-Host -ForegroundColor Red "$TestSystem - Not Online"
    } #End If
} #End Foreach

#Export the array to CSV
$Results | sort name | export-csv $OutputFile -NoTypeInformation


################ END SCRIPT ################

So after the initial script, you just feed it the "Missed" file over and over until you get everything.

Wednesday, October 7, 2015

Exchange Issues with Delegation due to AdminSDHolder and Protected Groups

We had some really weird issues going on delegating permissions to certain users within Active Directory. Basically, when we would assign them some rights, the rights would just disappear for no rhyme nor reason. What we discovered was Protected Groups. I'm not going to try to explain it, because I don't completely understand it (here's someone much more intelligent doing so on Technet), but I'm going to tell you how to fix it!

The jist of it is that you can't delegate stuff to a user account that is a member of a protected group. Complicating matters is that when you remove someone from a protected group, the setting does not change! You have to go into ADSIEdit and change the AdminCount property on the user's AD object from 1 to 0 manually. Well, you could script that too, but I only had this happen with a few users so I didn't bother with that.

WHY we ran across this is that we had removed some user accounts from the Domain Admins group and had issues with delegating in Exchange. I hear it's a problem with Lync, too, but we don't run that. Again, simply removing someone from the protected group (Domain Admins in this example) does not change the setting. It also bears mentioning that nesting counts. So if User A is a member of a group that's a member of Domain Admins, their AdminCount value will change to 1.

The following commands must be performed inside an Active Directory Powershell Session.

To find which groups are protected, use this command:
Get-ADGroup -LDAPFilter "(objectcategory=group)(admincount=1)" | select name | sort name

To find out which users are protected, use this:
Get-ADUser -LDAPFilter "(objectcategory=person)(samaccountname=*)(admincount=1)" | select name

So what you have to do is get the list of protected users, then cross out any users that are direct or indirect members of the protected groups. The users that remain are unjustly protected. To resolve, simply change the AdminCount value on the remaining users.

Here's a handy function I ran across to get nested group memberships (shout out to Piotr Lewandowski for that).

The protected groups by default include:
Account Operators
Administrators
Backup Operators
Cert Publishers
Domain Admins
Domain Controllers
Enterprise Admins
Print Operators
Read-only Domain Controllers
Replicator
Schema Admins
Server Operators


Thursday, October 1, 2015

All OUs in this Domain Should be Protected from Accidental Deletion

I run the best practices analyzer on my domain controllers on the first of the month, every month.

Today I got this result: All OUs in this Domain Should be Protected from Accidental Deletion


So this begets the question: How do I find out which OUs are not protected?

Answer: Of course, make sure you are running this command after doing an import-module activedirectory, from a computer that has the Active Directory Powershell module installed.

The command is:
Get-ADOrganizationalUnit -filter * -Properties * | where {$_.ProtectedFromAccidentalDeletion -eq $False} | select DistinguishedName

There may be a good reason for them not to be protected, but if you want to go ahead and set protection on each OU, you can run this command:
Get-ADOrganizationalUnit -filter * | Set-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $true