Monday, January 30, 2012

PowerShell: Get All Nested Members of an AD Group

A quick PowerShell script to get all nested members of a group. Enjoy.

##############################################################################

# Script Name: AD_Get_All_Nested_Members_Of_Group.ps1

# Version: 1.0

# Author: Dean Bunn

# Last Edited: 01/30/2012

# Description: Get All Nested Members of an AD Group

##############################################################################

#Function for Group Members Check

function checkMember([string]$objCN)

{

#Var for LDAP Path

$ldapPath = "LDAP://" + $objCN

#Retrieve AD Object

$adObj = [ADSI]$ldapPath

#Null Check on Class

if($adObj.Class)

{

#Switch Statement for Object Type

Switch($adObj.Class.ToString().ToLower())

{

#Group Objects

"group"

{

#Loop Through Group Members and Perform Recursive Member Check

foreach($member in $adObj.member)

{

#Check to See If Member Has Been Checked Already

if(!$htUniqueMembers.ContainsKey($member))

{

#Add Member to Unique HashTable

$htUniqueMembers.add($member,"1")

#Run checkMember on This Group Member

checkMember $member.ToString()

}

}

}#End of Group

#User Objects

"user"

{

#Check to See If Member Has Been Added to Unique Member. If Not Then Add

if(!$htUniqueMembers.ContainsKey($objCN))

{

$htUniqueMembers.add($objCN,"1")

}

}#End of User

#Add Other Classes Here If Necessary

}#End of Class Switch Statement

}#End Class Null Check

}#End of checkMember Function


#HashTable for All Unique Members

$htUniqueMembers = @{}


#Run checkMember on Top Group

checkMember "CN=STATS-ALL-MEMBERS,OU=DEPARTMENTS,DC=MYCOLLEGE,DC=EDU"


#Loop Through Each Unique Group Member

foreach($uCN in $htUniqueMembers.Keys)

{

Write-Host $uCN

}

Saturday, January 28, 2012

PowerShell: Remote PSSession Files Filling Up Drive

Thanks to colleague's help, I was finally able to figure out why available disk size on one of my utility servers was shrinking so fast. Basically, remote PSSessions in PowerShell cause the system to download any required modules from the remote system. Since one of my scheduled scripts creates a remote PowerShell session to one of my unit's Lync servers, it was downloading 4 MB of Lync PowerShell files every time it runs (which is every 15 minutes). By the end of the day, the drive had lost another 400 MBs of space.

The PSSession files are created in a folder with a random name and have these three different file extensions.

.ps1xml

.psd1 (windows PowerShell Data)

.psm1 (windows PowerShell Module)

So I added to another maintenance script this PowerShell one liner to remove files and folders that are older than three hours in my admin account temp directory.

Get-ChildItem "C:\Users\adminaccount\AppData\Local\Temp" -recurse | Where {$_.CreationTime -lt (Get-Date).AddHours(-3) } | Remove-Item -Force –Recurse

PowerShell: Delete Child Domain DNS Records Using DNSCMD

One of the departments on campus recently upgraded their systems (located in child domain) and requested that my unit clean up their old DNS records and the IPv6 records for systems they forgot to disable IPv6 on. So I sent them an Excel spreadsheet of their records and then sent me back which ones to delete. Then I created the script below to remove the 400+ records. Due to the way the DNSCMD command works, I had to add some serious variable checking and a five second delay between records so that it would work.

##############################################################################

# Script Name: DNS_Delete_Records_Listed_in_CSV.ps1

# Version: 1.0

# Author: Dean Bunn

# Last Edited: 01/27/2012

# Description: Deletes Child Domain DNS Entries from CSV File

##############################################################################

#Var for DNS Server

$dnsServer = "dc1.mycollege.edu"

#Var for DNS Zone

$dnsZone = "mycollege.edu"

#Var for Child Domain Name

$childDomain = "adax"

#Load CSV of Records to Be Deleted (Headers: Name, Type, and Data)

$dnsDeleteRecords = Import-Csv "c:\Users\adminaccount\Desktop\DNS_Delete.csv"

foreach($ddr in $dnsDeleteRecords)

{

#Var for Host Name

$hostName = $null

#Var for Record Type

$rType = $null

#Var for IP Address

$ipAddr = $null

#Check Length of Host Name Before Assigning Var

if($ddr.Name.ToString().Trim().Length -gt 1)

{

#Compose Hostname As SystemName.ChildDomain

$hostName = $ddr.Name.ToString().Trim() + "." + $childDomain.ToString()

}

#Switch Statement for Record Type

switch($ddr.Type.ToString().Trim())

{

"Host (A)"

{

$rType = "A"

}

"IPv6 Host (AAAA)"

{

$rType = "AAAA"

}

#Add Additional Record Types When Necessary

}

#Check the Length of the IP Address Information

if($ddr.Data.ToString().Trim().Length -gt 8)

{

#Assign IP Addr Var

$ipAddr = $ddr.Data.ToString().Trim()

}

if($rType -ne $null -and $hostName -ne $null -and $ipAddr -ne $null)

{

#Compose DNSCMD with Variables

#Should Look Like "dnscmd dc1.mycollege.edu /RecordDelete mycollege.edu dept-webstudent.adax A 192.168.60.49 `/f"

[string]$dnsDltCmd = "dnscmd $dnsServer /RecordDelete $dnsZone $hostName $rType $ipAddr `/f"

#Run the DNSCMD and Wait Five Seconds Before Moving to Next Entry

Invoke-Expression $dnsDltCmd

Start-Sleep -Seconds 5

}

else

{

Write-Host "Not Enough Info for " $ddr.Name

}

}#End of Foreach on $dnsDeleteRecords



Wednesday, January 4, 2012

PowerShell: Protect All AD Objects in Specific OUs

A strong need came up to set all AD objects in specific OUs with "Protect Object from Accidental Deletion"; however, some of these OUs were in different domains and this would require that would require a specific server setting for the Get-ADObject command. I solved this issue by using a hashtable to store the OU and DC info.

Enjoy.

##############################################################################

# Script Name: AD_Prevent_Accidental_Deletion.ps1

# Version: 1.5

# Author: Dean Bunn

# Last Edited: 01/01/2011

# Description: Protect All AD Objects in Select OUs from Accidental Deletion

##############################################################################

#Add Active Directory Module

Import-Module ActiveDirectory

#Create a HashTable to Hold OUs

$hOU = @{}

#Add OU and DC Info into HashTable

$hOU["ou=Payroll,dc=campus,dc=edu"] = "dccmp1.campus.edu"

$hOU["ou=Payroll,dc=campus,dc=edu"] = "dccmp1.campus.edu"

$hOU["ou=Marketing,dc=phys,dc=campus,dc=edu"] = "dcphys1.phys.campus.edu"

$hOU["ou=Marketing,dc=phys,dc=campus,dc=edu"] = "dcphys1.phys.campus.edu"

#Loop Through the OU HashTable

foreach($key in $hOU.keys)

{

#Assign OU and Server Names to Local Variables (Easier to Read Command)

$srchBase = $key.ToString()

$srvr = $hOU[$key].ToString()

#Set "Protect Object from Accidental Deletion" on All OU AD Objects

Get-ADObject -Filter * -SearchBase $srchBase -ResultPageSize 500 `

-SearchScope Subtree -Properties ProtectedFromAccidentalDeletion -Server $srvr `

| Where {$_.ProtectedFromAccidentalDeletion -eq $false} `

| Set-ADObject -ProtectedFromAccidentalDeletion $true -Server $srvr

}


Monday, November 28, 2011

PowerShell: Change Local Admin Password

A colleague needed a way to change the local admin password on several desktops without storing the password in a script or group policy. I came up with the script listed below.


#############################################################################
# Script Name: Change_Local_Admin_Password.ps1
# Version: 1.0
# Last Edited: 11/28/2011
# Description: Changes Local Administrator Account Password on Remote Systems
#############################################################################

#Write Out Blank Line (Easier on the Eyes)
Write-Host ""

#Prompt User for New Password (Stored as Secure String)
$sPwd = Read-Host -assecurestring "Please Enter The New Local Admin Password"

#Create PSCredential with Secure String Password (Needed for Secure String Conversion)
$tempCred = New-Object System.Management.Automation.PSCredential("nada",$sPwd)

#Create Network Credential Using The PSCredential Object
$nc = $tempCred.GetNetworkCredential()

#Pull Plain Text Password from Network Credential Object
$uPwd = $nc.Password.ToString()

Write-Host "" #Same Eyes

#Array of Computer Names that Need Local Admin Password Changed
$computers = @("dept-wrks1.my.domain.net","dept-wrks2.my.domain.net","dept-wrks3..my.domain.net")

foreach($computer in $computers)
{

#Ping Computer Before Attempting Password Change
if (test-connection -computername $computer -quiet)
{

try
{

#Var for WinNT Path to Local Administrator Account on Remote System
$WinNTPath = "WinNT://" + $computer + "/Administrator,User"

#Attach to Local Admin Account
$lAdmin = [ADSI]$WinNTPath

#Set Local Admin Password
$lAdmin.setpassword($uPwd)

Write-Host "Successfully Set Password on " $computer

}
catch
{
#Notify Admin of Failure
Write-Host "Error When Setting Password on " $computer
}

}
else
{
#Notify Admin of Failed Ping
Write-Host "Ping Failed to" $computer
}

}

Thursday, July 28, 2011

C#: FEP Client Alert Notice

As an alternative to running SCCM for Forefront Endpoint Protection alert notices (since you can easily control the client with GPOs and WSUS for status) I wrote up a C# console application that parses the system log to look for virus\malware notifications from the FEP client and then emails the alert message(s) to the system admins. This is just a proof of concept. Ideally you would want to create a Windows service that run this code to parse the log every hour or so.


using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Net.Mail;

namespace FEP_Status_Check
{
class Program
{
static void Main(string[] args)
{
//Counter for FEP Alerts
int x = 0;

//String Building for FEP Log Entry Data
StringBuilder stbFEPNotice = new StringBuilder();

//Create Collection of System Log Entries
EventLog elSys = new EventLog("System", ".");
EventLogEntryCollection elecSystem = elSys.Entries;

//Loop Through System Events Collection
foreach (EventLogEntry sysLogEntry in elecSystem)
{
//Check to See if From FEP
if (sysLogEntry.Source.ToString() == "Microsoft Antimalware")
{
//Check to See if Event Took Place Within 2 Hours
if (DateTime.Compare(DateTime.Now.AddMinutes(-60), sysLogEntry.TimeGenerated) < 0)
{
//Check for Virus\Malware Alert or Remediation Action Alert
if (sysLogEntry.InstanceId.ToString() == "1116" || sysLogEntry.InstanceId.ToString() == "1117")
{
//Add Log Entry Text to FEP Notice
stbFEPNotice.Append(sysLogEntry.Message.ToString());
stbFEPNotice.Append("\n\n\n");
//Increment Counter
x++;
}
}


}

}

//Notify Network Admins of Any FEP Alerts
if (x > 0)
{

//Pull Computer Name
string strHostName = System.Environment.MachineName.ToString();
//Create and Send Email Notice
MailMessage mmFEPNotice = new MailMessage("DCAdmins@my.company.com", "admins@my.company.com");
mmFEPNotice.Subject = "FEP Alert Notice from " + strHostName + " on " + DateTime.Now.ToShortDateString();
mmFEPNotice.IsBodyHtml = false;
mmFEPNotice.Body = stbFEPNotice.ToString();
SmtpClient scMail = new SmtpClient("smtp.my.company.com");
scMail.Send(mmFEPNotice);

}


}
}
}

PowerShell: AD DC Failed Logins Report

I wanted to get a summary view of failed login attempts on a network DCs. Came up with this script that parses Windows 2008 R2 DCs security log files for all failed login attempts in the last 24 hours. Then it compiles a totals counts for each IP and host name combo listed in the logs of all the DCs. Sends a quick summary email to the admins.

#############################################################
# Script Name: AD_DCs_Failed_Login_Report.ps1
# Version: 1.0
# Author: Dean Bunn
# Last Edited: 07/26/2011
# Description: Failed Logins Report for DCs
#############################################################

#Array for All Failed Login Entries
$arrFailures = @()
#Array for Reporting
$Summary = @()

#Domain Controller Array
$DCs = @("dc1","dc2","dc3")

foreach($DC in $DCs)
{
#Retrieve Failed Logins on Each DC for the Last 24 Hours
$failedLogins = get-eventlog -computername $DC -logname security -after (get-date).adddays(-1) | where-object {$_.instanceID -eq 4625 }

#Loop Through Each Failed Login
foreach($failedLogin in $failedLogins)
{

#Var for Workstation Name
$workstation = ""
#Var for IP Address
$networkAddress = ""

#Array of Failed Login Log Entry Message (Split by Line Break)
$flM = $failedLogin.message.Split("`n")

#Loop Through Each Line in the Log Entry Message
foreach($fl in $flM)
{
#Check to See if Line has Source Network Address Info
if($fl.Contains("Source Network Address:"))
{
#Remove Unneeded Data from Line
$fl = $fl.Replace("Source Network Address:","")
#Clean UP Network Address Info
$networkAddress = $fl.ToString().Trim()
}

#Check to See if Line has Workstation Info
if($fl.Contains("Workstation Name:"))
{
#Remove Unneeded Data from Line
$fl = $fl.Replace("Workstation Name:","")
#Clean Up Workstation Info
$workstation = $fl.ToString().ToUpper().Trim()
}

}

#Format Failed Login Entry Data Before Adding to Array
$flEntry = $networkAddress + "," + $workstation

#Quick Check to See if IP And Host Name Weren't Empty
if($flEntry.length -gt 1)
{
#Added Failed Entry to Array
$arrFailures += $flEntry
}


}

}

#Create Hashtable for Unique Check
$htReport = @{}

#Loop Through Failed Log Entries Array and Count How Many Failed Logins
foreach($flEntry in $arrFailures)
{
#Int for Counting Failed Login Attempts
$intEC = 0

if(!$htReport.ContainsKey($flEntry))
{
#Loop Again Through Array Looking for IP + Host Name Match
foreach($item in $arrFailures)
{
if($flEntry -eq $item)
{
$intEC = $intEC + 1
}
}

#After Determining Matches, See if Entry Count Added To Report Already
#And Only Report on 10 or Greater Failed Logins for IP + Host Name Pair
if($intEC -gt 10) #
{

#Split Apart IP Host Name Entry to Add It to Report Summary
$arrFlEntry = $flEntry.Split(",")

#Create New PowerShell Object and Assign Data to It
$uEntry = new-Object PSObject
$uEntry | add-Member -memberType noteProperty -name "IP" -Value $arrFlEntry[0].ToString()
$uEntry | add-Member -memberType noteProperty -name "Host Name" -Value $arrFlEntry[1].ToString()
$uEntry | add-Member -memberType noteProperty -name "Failed Logins" -Value $intEC.ToString()
#Add Entry to Summary Array
$Summary += $uEntry

}

#Add Entry Info to Reporting Hashtable
$htReport.add($flEntry,"1")

}

}

#Get Current Short Date
$rptDate = Get-Date -Format d

#Style for HTML Table in ConvertTo-HTML
$a = "<style>"
$a = $a + "TABLE{border-width: 1px;border-style: solid;border-color: black;}"
$a = $a + "TH{border-width: 1px;padding: 5px;border-style: solid;border-color: black;text-align: center;}"
$a = $a + "TD{border-width: 1px;padding: 5px;border-style: solid;border-color: black;text-align: left;}"
$a = $a + "</style>"

#Message Body (Sorted by Failed Login Attempts)
$emsg = $Summary | Sort-Object {[int]$_."Failed Logins"} -descending | ConvertTo-Html -head $a | Out-String

#Settings for Email Message
$messageParameters = @{
Subject = "DCs Failed Logins Report for " + $rptDate
Body = $emsg
From = "DCAdmins@my.company.com"
To = "DCAdmins@my.company.com"
SmtpServer = "smtp.my.company.com"
}
#Send Report Email Message
Send-MailMessage @messageParameters –BodyAsHtml