Posts Tagged‘powershell’

TechEd Day 3: Less Hectic, More Interesting.

Today started out pretty much like yesterday. I did my typical thing of staying up just a tad too late thanks to DOTA 2 and my terrible addiction to watching the Discovery Channel if its on the hotel TV (you should’ve seen the gold dredging showdown I watched, it was incredible¬†television) meant I wasn’t at 100% when I got up but the smorgasbord¬†of breakfast stuffs and coffee are a powerful motivator. Also it seems the combination of some good old fashioned delayed onset muscle soreness coupled with what I think is a mild cold has left me in less than stellar shape. Still I made it to all the sessions I planned to today and some of them really impressed me, not least of which was PowerShell V3.0

I won’t go into terrible detail about it here as my post tomorrow on LifeHacker will give a better rundown of the features but suffice to say I’m excited to use it. It might be a long time before I get to see any of it in production (my current project is only just getting onto Windows 7) but I’ll probably be playing around with it at home as there’s an awful lot of good stuff in there that I could make use of. I’m probably going to have to sweet talk my way into a TechNet/MSDN subscription though as I don’t have access to one at the moment (nudge nudge wink wink Microsoft).

I was also very impressed by the number of value add services available from Microsoft for any kind of application. Long time readers will know of the pains I had back when I thought that I was only 2 steps away from being the next Internet success story and it seems I’m not alone if Microsoft has put this much effort into giving us plebs some amazing things for free. I’ve actually got an application in the pipeline that I’ve been working on casually for the past couple weeks and I think it’s going to be a good candidate to try some of these services out and hopefully actually launch it instead of procrastinating endlessly.

There was one particular session I was rather disappointed in (Building Cross Device Mobile Applications Powered By SQL Azure Federations if you were wondering) as the name lead me to believe there’d be a heavy focus on the challenges of cross platform development. It wasn’t unfortunately as the majority of the session was dedicated to the back end infrastructure with the cross platform part of it amounting to little more than “We used MonoTouch”. That’s cool and all but it’s nothing I didn’t learn a year ago after an hour or so of Googling the different options. I can understand that they can’t really spend the majority of their time here spruiking another company’s product but that doesn’t stop me from feeling somewhat disappointed.

Tomorrow’s my last day here and thankfully it’ll be a relatively tame affair as my current condition coupled with the potential¬†shenanigans that I might get up to at the Hype party that’s currently raging near me could leave me as an incoherent mess. I’ll power on though because I’m crazy like that and it’d be a right shame to let an opportunity like this go to waste because I wasn’t feeling perfect on the day.

Powershell: Why Did I Resist?

A good deal of any system administrators job is automation. Even when you’re working in small environments doing the same thing on every user’s machine individually is needlessly tiresome and always error prone. My current environment has well over 400 servers and at least 1000 desktops so anything that needs to touch all of them has to be automated, there’s just no other option. In the past VBScript was the be all and end all of Windows based scripting and is still used as the de facto automation language for many IT shops today. However with the coming of Vista and Server 2008 we saw the introduction of a plucky new tool called Powershell (first seen in the wild in 2006) which looked to be the next greatest thing for automating your IT environment. Due to Vista’s poor reception and by association Server 2008 Powershell didn’t really take off that well. In fact I’d actively ignored it up until about 6 months ago when I started looking at it more closely as a tool to automate some VMware tasks. Little did I know then that this new world of Powershell would soon make up the majority of my day to day work.

Now the developers out there will know that Visual Basic (VB) is somewhat of a beginner’s programming language. Sure it’s feature complete when compared to its bigger brother C# however it’s rather lax with its standards and this makes any code done in VB rather inelegant. This was probably why I shied away from Powershell initially as I thought it would just be an evolutionary step from VBScript, but I couldn’t have been more wrong. The syntax is decidedly closer to C# than VB although the legacy of behind the scenes tricks to hide some complexities from its users is still there, although with the added benefit of those small tricks being available should you know where to look. Additionally the ease of integration with other Microsoft coding platforms (like loading .NET dlls) is absolutely amazing, giving you the power of doing almost anything you can with their other languages right there in your script.

The real kicker though is the shift in focus that Microsoft has taken when it implemented Powershell all those years ago. Typically their infrastructure products like Exchange or System Center were either built by separate teams or came from another company that Microsoft had purchased. This meant that there was no standard way of interfacing with these products making automation a real pain, usually ending up with you having to use a third party tool or write reams of VBScript. For most future releases however Microsoft has built their management tools on top of Powershell, meaning that any action performed in the management consoles can be replicated via a script. This was most obvious when they released Exchange 2007 and any command you performed on the GUI would show you the Powershell command that it ran.

To show you how much you can do with Powershell I’m going to include 2 of my own scripts which I invested quite a bit of time in. The first shown below is a script that will scan your domain and alert you when someone adds themselves to the Domain Administrators group:

$domainAdmins = dsget group “CN=Domain Admins,CN=Users,DC=your,DC=domain,DC=com” -members -expand
$list = Get-Content C:\Directory\Where\Script\Runs\DomainAdminsList.txt

$mail = new-object System.Net.Mail.MailMessage
$mail.From = new-object System.Net.Mail.MailAddress(“[email protected]“)
$mail.To.Add(“[email protected]“)
$smtpserver = “YourSMTPServer
$mail.Subject = “Unauthorized Domain Administrator Priveleges Detected.”
$smtp = new-object System.Net.Mail.SmtpClient($smtpserver)

foreach ($domainAdmin in $domainAdmins)
{
$found = $false
foreach ($line in $list)
{
if ($domainAdmin -eq $line){$found = $true}
}

if ($domainAdmin -eq “”){$found = $true}

if($found){}
else
{
$date = Get-Date
$hostname = hostname
Write-Host $domainAdmin “not found in control file.”
$mail.Body = $domainAdmin + ” not found in control file. Script run on ” + $hostname +” at ” + $date + ” using control file C:\Directory\Where\Script\Runs\DomainAdminsList.txt
$smtp.Send($mail)
}
}

You’ll want to first run “dsget group “CN=Domain Admins,CN=Users,DC=your,DC=domain,DC=com” -members -expand | DomainAdminsList.txt” to generate the text file of domain admins. Once you’ve done that you can schedule this to run say every hour or so and you’ll get an email when someone gives an account domain administrator. You can modify this for any group to, just update the first line with the CN of the group you want to scan.

The second is one that I’m quite proud of, it will tell you when someone changes a group policy in your domain. Pretty handy for when you’ve got a bunch of developers who have access to do that and routinely break other people’s systems when they do. You’ll need to grab the ListAllGPOs.ps1 script from here first (although I called it GPOList.ps1):

$GPOs = .\GPOList.ps1 -query -verbose -domain your.domain.com

$DCs = “DC01″,”DC02”

$baseline = Import-Csv GPOBaseline.csv

$outFile = “D:\Apps\Scripts\GPOScanner\Output.txt”
$outBody = “D:\Apps\Scripts\GPOScanner\OutBody.txt”
$null | Out-File $outFile
$null | Out-File $outBody
$emailRequired = $false

Write-Host “Scanning your.domain.com
your.domain.com” | Out-File $outFile -append
foreach ($cGPO in $devGPOs)
{
$found = $false
foreach ($bGPO in $baseline)
{
if ($cGPO.ID -match $bGPO.ID)
{
$found = $true

if ($bGPO.ModificationTime.Equals($cGPO.ModificationTime.ToString()))
{}
else
{
$output = “WARNING: GPO ” + $cGPO.Displayname + ” has been modified since baseline.”
Write-Host $output
$output | Out-File $outBody -append
$output = “Modification time: ” + $cGPO.ModificationTime + “”
Write-Host $output
$output | Out-File $outBody -append
$emailRequired = $true

$cGPO.ModificationTime.AddSeconds(1).ToString()
foreach ($dc in $DCs)
{
$dc
$logs = [System.Diagnostics.EventLog]::GetEventLogs($dc)
foreach($log in $logs)
{
if($log.LogDisplayName -eq “Security”)
{
$entries = $log.Entries
foreach($entry in $entries)
{
if ($entry.EventID.Equals(4663) -or $entry.EventID.Equals(4656) -or $entry.EventID.Equals(560))
{
if ($entry.Message.Contains($cGPO.ID))
{
$entry | fl
$entry | fl | Out-File $outFile -append
}
}
}
}
}
}
}
}
}

if ($found -eq $false)
{
$emailRequired = $true
$output = “New GPO ” + $cGPO.DisplayName + ” not found in baseline.”
Write-Host $output
$output | Out-File $outBody -append
}
}

if ($emailRequired)
{
$hostname = hostname
$date = Get-Date
$output = “Script was run on ” + $hostname + ” at ” + $date + ” using control files located in D:\Apps\Scripts\GPOScanner. Please see the attachment for related event log information.”
$output | Out-File $outBody -append
$mail = new-object System.Net.Mail.MailMessage
$mail.From = new-object System.Net.Mail.MailAddress(“[email protected]“)
$mail.To.Add(“[email protected]”)
$smtpserver = “YourSMTPServer
$mail.Subject = “Group Policy Changes Detected.”
$smtp = new-object System.Net.Mail.SmtpClient($smtpserver)
$mail.Body = Get-Content $outBody
$att = new-object Net.Mail.Attachment($outFile)
$mail.Attachments.Add($att)
$smtp.Send($mail)
$att.Dispose()
}

Again you’ll want to run “.\GPOList.ps1 -query -verbose -domain your.domain.com | Export-Csv GPOBaseline.csv” to generate the baseline. This script will first look for any changes then scour the security logs of your domain controllers to find who did it, sending you the logs of who changed it and when. Pretty neat eh?

$GPOs = .\GPOList.ps1 -query -verbose -domain your.domain.com

$DCs = “DC01″,”DC02”

$baseline = Import-Csv CENTRALGPOBaseline.csv

$outFile = “D:\Apps\Scripts\GPOScanner\Output.txt”
$outBody = “D:\Apps\Scripts\GPOScanner\OutBody.txt”
$null | Out-File $outFile
$null | Out-File $outBody
$emailRequired = $false

Write-Host “Scanning your.domain.com”
“your.domain.com” | Out-File $outFile -append
foreach ($cGPO in $devGPOs)
{
$found = $false
foreach ($bGPO in $baseline)
{
if ($cGPO.ID -match $bGPO.ID)
{
$found = $true

if ($bGPO.ModificationTime.Equals($cGPO.ModificationTime.ToString()))
{}
else
{
$output = “WARNING: GPO ” + $cGPO.Displayname + ” has been modified since baseline.”
Write-Host $output
$output | Out-File $outBody -append
$output = “Modification time: ” + $cGPO.ModificationTime + “”
Write-Host $output
$output | Out-File $outBody -append
$emailRequired = $true

$cGPO.ModificationTime.AddSeconds(1).ToString()
foreach ($dc in $DCs)
{
$dc
$logs = [System.Diagnostics.EventLog]::GetEventLogs($dc)
foreach($log in $logs)
{
if($log.LogDisplayName -eq “Security”)
{
$entries = $log.Entries
foreach($entry in $entries)
{
if ($entry.EventID.Equals(4663) -or $entry.EventID.Equals(4656) -or $entry.EventID.Equals(560))
{
if ($entry.Message.Contains($cGPO.ID))
{
$entry | fl
$entry | fl | Out-File $outFile -append
}
}
}
}
}
}
}
}
}

if ($found -eq $false)
{
$emailRequired = $true
$output = “New GPO ” + $cGPO.DisplayName + ” not found in baseline.”
Write-Host $output
$output | Out-File $outBody -append
}
}

if ($emailRequired)
{
$hostname = hostname
$date = Get-Date
$output = “Script was run on ” + $hostname + ” at ” + $date + ” using control files located in D:\Apps\Scripts\GPOScanner. Please see the attachment for related event log information.”
$output | Out-File $outBody -append
$mail = new-object System.Net.Mail.MailMessage
$mail.From = new-object System.Net.Mail.MailAddress(“[email protected]”)
$mail.To.Add(“[email protected]”)
$smtpserver = “YourSMTPServer”
$mail.Subject = “Group Policy Changes Detected.”
$smtp = new-object System.Net.Mail.SmtpClient($smtpserver)
$mail.Body = Get-Content $outBody
$att = new-object Net.Mail.Attachment($outFile)
$mail.Attachments.Add($att)
$smtp.Send($mail)
$att.Dispose()
}