Selecting a Random Runbook Server – Part 2 – a Closer Look

It’s been quite a while since my last post.  I’ve had several people email me and ask for a closer look at selecting a random runbook server.

A bit of a review, refer to the previous post https://trwagner1.wordpress.com/2013/11/27/selecting-a-random-runbook-server/ for reference.  But, I was curious about how you could genuinely select a RANDOM server rather than rely on a “primary” and or “secondary” that were “static”.  (Please read the disclaimer at the bottom of this post)

Since this is a closer look, I’ll try to keep things simple and include screen shots.

I will show you how to subscribe to and use data within the data bus.

First, a bit of design explanation.  For simplicity, there are two runbook servers.  Their names are “SCORCH01” and “SCORCH02”.  Tying into Jon Mattivi’s post, we will create two variables.

Here’s an example of creating the first variable “RBServer1”.

ss1

You should now have two variables as in the example below:

ss2

 

I have an initiation runbook that starts another runbook.  For an explanation of what an initiation runbook folder is, download this free book “Designing System Center Runbooks“from Microsoft.

The other runbook is invoked from the initiation runbook that runs a PowerShell script, queries groups for members, creates a report and emails it to a distribution list.

For the purpose of this blog post, we don’t care about the contents of the runbook that generates a report so that won’t be discussed.

I want to point something out here.  My initiation run book is NOT using a random server!  What I want to do is edit the properties of the runbook so that I am overriding the default roles and I add any and all servers I want to be able to run this.  The top server listed is the primary.  If the primary isn’t available, then the secondary is used.

Here is a screen shot showing the properties of my initiation runbook.  click the “Runbook Servers” tab, then override and select your servers to use when this RB runs.

ss9

 

The initiation runbook looks like this:

ss5

The activity “Run .Net Script” titled “Generate Random RB Server” is our primary focus here.

Type in your PowerShell script:

<code>

$RBServer = ($Server = “”,””) | Get-Random.

</code>

Now, you could add the data to subscribe the data.  The easy way is to RIGHT click and subscribe to the data.  So, as you type in the script after the first double quotes, right click the mouse.  Then, select your first variable.  Add the closing double quotes, then the comma, then the double quotes again, right click, subscribe, then close double quotes then right parenthesis.

That will give you this:

ss3

Now, DO NOT click the finish button yet.

Click the Published Data tab, click the Add button and enter in a new variable, type string, name “RBServer”.  This is what puts the returned data into the data bus so you can use it in any subsequent activities in your runbook(s).

ss4

The last activity in my initiation runbook is “Invoke Runbook”.  I’ve linked my runbook that I want to invoke.  Right click click in the “Runbook Servers” text field, select “Subscribe > Published Data”

ss7

Since my last activity was the activity that runs the PowerShell script, I don’t need to hunt for it.  But, if you have a more complicated script that has many activities, you HAVE to pay attention to the flow of your runbook.  You can only select published data that would have been generated prior to the point at which you are subscribing to the data…. after all, you can’t use data that isn’t established.

In this example, I am looking for the published data “RBServer”.  Select it and click ok.

ss8

Now, your dialogue box for “Invoke Runbook” should look like the this:

ss6

 

Now, when this initiation runbook runs, it generates a random server name, invokes a runbook on the server name selected by the PowerShell script.

DISCLAIMER

A disclaimer?  What for?  Well, here’s why and what for.  There may be practical uses of this approach.  But, remember that I went after this with some curiosity following an Orchestrator class. But, this example is a very SIMPLE example.

So, we’ve used “HA” by adding multiple servers in the properties of the runbook.  We’ve generated a random runbook server… HOWEVER, if you pay close attention, my PowerShell script is nothing more than a simple method to select a random string from only two that I’ve provided.  That’s all it is!  I would not suggest using this example in production without a lot of improvements!

What if SCORCH01 is down?  The script doesn’t provide for any error handling or error checking either.

So, just take note.  To be much more robust, you’ll want to beef up that script quite a bit.  If your Orchestrator environment is built properly, you’ll have more than one runbook server for HA and load balancing.  To be honest, using the primary and secondary servers in the “Runbook Servers” tab on the properties of your runbook is probably sufficient.

Selecting a random runbook server

Here’s a link to a blog post that piqued my interest before taking a class on System Center Orchestrator.

http://jmattivi.blogspot.com/2012/06/high-availability-for-runbook-servers.html

I attended a remote class on orchestrator, company and instructor name withheld intentionally, shared Jon’s blog post and posed the following question, “can you randomly select a runbook server?”

The instructor said no. I thought he was a bit short-sighted.  The instructor was a proponent of saying (and implying) that you don’t need to know PowerShell.  Do you really need to know PowerShell to use System Center Orchestrator?  Technically, no.  Now, that’s “technically” a correct statement.

However, if you are going to put forth the time and effort to attend a class to learn a System Center product AND advance your skillset, AND, better leverage a product’s features, you are truly doing yourself a disservice beyond belief.  I know that won’t float well with several out there.  However, it is my opinion.  But, you have to face facts.  We are in an industry where to excel above and beyond and to be “good” you need to educate yourself and take advantage of making the fullest use of a product’s strengths as you can.  This includes spending time after hours with your own testing, learning your own, etc.  Learning PowerShell is NOT THAT HARD.  Using the command line is NOT THAT HARD.  At least familiarize yourself with PowerShell, learn the basics and see if you can use it to your advantage.  I bet you can find LOTS of ways to use PowerShell in any IT environment.  Enough said, I’ll jump off my soap box, for now.

Jon Mattivi’s blog post really made me think about this a lot. “When I design my company’s Orchestrator environment, I not only want it to be highly available, but I also want to implement the DRY principle.  I also want to extend the high availability by distributing the work load as evenly as I can.  How do I do that?”

Jon’s blog post seemed too static for my tastes.  Nothing against Jon’s post.  It’s a valid alternative.

After spending many, MANY, hours in my own home lab with Orchestrator and learning the ins and outs of building runbooks and becoming more advanced, a light clicked and I decided to try this.

I responded to Jon’s blog post with my solution.

Here’s the idea in a nutshell.  To distribute your runbook workload evenly, you can set up a global variable for each of your runbook servers.

In my example, we’ll say those two global variables are “SCORCH01” and “SCORCH02”

Next, create your initiation runbook (for more information on what an initiation runbook is, spend the best few bucks you can buy and purchase “Designing Orchestrator Runbooks” by David Ziembicki, Aaron Cushner, Andreas Rynes, and Mitch Tulloch.  It’s a very affordable book.  It is very easy to read, and in my humble opinion, the best money you can spend on a book for Orchestrator and a must have for your library).  The price is very low and frankly, it’s a must buy just because of the price!  (No, I’m not getting any commission on that, just a friendly recommendation I don’t think you should refuse.  It’s like passing up a gas station that has gasoline selling at $1.00 a gallon.  You’d be silly not to stop and top off your tank!)

In your initiation runbook, add a “Run .Net Script” activity.  Change the language to PowerShell.  Add this one liner (replace the server names in quotes and subscribe to your global variables).

<br />$RBServer = ($Server = "SCORCH01","SCORCH02") | Get-Random<br />

Set up the activity to publish the $RBServer variable to the Orchestrator bus.

Add an invoke runbook activity and link the two together. In the “Invoke Runbook” activity, in the runbook servers text box, subscribe to the published data for the data returned by the “Run .Net Script” activity.

Now, when the initiation runbook activity runs, you’ll be selecting from a random runbook server! If you have more than 2 runbook servers, this becomes a bit more useful in selecting a server at random.

So, the answer is “yes” you CAN select a random runbook server when your runbooks run.  Wasn’t that easy?

PowerShell – Refresh SCCM Updates Compliance

Here’s a quick post since I haven’t posted in a while.

Some of you SCCM users will run into circumstances where a system should be compliant with your current update deployment.  However, it doesn’t report properly in the report server within SCCM.

The “old” SMS way to do that was a common VBscript:  http://msdn.microsoft.com/en-us/library/cc146437.aspx

Ah, VBscript.  Well now, let’s do that in PowerShell!  This is so simple and only takes two lines.

You can easily do this using System Center Orchestrator as well.  Just combine the Execute PowerShell Script IP with the Configuration Manager IP for 2012 and you’re all set.  Just get collection members and then run this against each member of the collection.

Here’s the PowerShell way:

$SCCMUpdatesStore = New-Object -ComObject Microsoft.CCM.UpdatesStore
$SCCMUpdatesStore.RefreshServerComplianceState()

Voila!

(just remember to run as “Administrator”)

PowerShell and Tombstoned DNS Records

I recently ran into a situation where DNS A and PTR records were mysteriously disappearing in a Windows 2008 R2 Forest.  I searched and searched for scripts to assist me in trying to “track” this problem by using event logs and even created filters with a logging product my company owns, with little luck.

The following two links are quite helpful when trying to troubleshoot, understand, setup, DNS and DHCP, etc.

http://blogs.technet.com/b/networking/archive/2011/08/17/tracking-dns-record-deletion.aspx

http://msmvps.com/blogs/acefekay/archive/2010/12/09/dns-records-disappearing-and-dns-auditing.aspx

However, a bit of a hint.  There’s some information left out!  First, you have to read Ace’s post very carefully and understand the PROCESS of how a DNS record is deleted.  In fact, don’t use the word delete.  A record isn’t deleted, it’s tombstoned, not visible and you are unable to query it through any DNS methods.  When the record goes “away”, the AD object is “cleaned/cleared” from AD.  To be simplistic, it’s a two step process.

The other information is, don’t just look for event ID 4662!  If you read Ace’s article and begin to understand the process, what’s happening is an attribute is changing on the record itself!  You also need to pay attention to event ID 5136 and 5137.

Now, you say, if the Windows security event logs don’t necessarily give you information on the actual record deletion, but only a change in the dnstombstone attribute, then is there a better way?

Certainly!  PowerShell to the rescue!  In fact, forget about the DNS console and the event logs altogether!

This short little script queries AD itself, looks at the attribute dNSTombstoned and the attribute whenChanged, creates a csv file and then emails it.  You then can see a report of any record (object DN) and when it was last changed.  You can limit the scope by altering the DN. Notice I’m using the root DN for all my zones so I’m including any reverse zones as well as additional forward zones.

Resist the urge to use the “-IncludeAllProperties” switch when using anything that queries. You can do that to dig down while developing your scripts (hopefully in a test environment), but to speed up your scripts, just include only the specific properties that you are going to use.

It’s simple enough to set up a scheduled task in windows to allow this script to run as often as you like.  Please ignore the lack of error handling here, I’m trying to keep it plain and simple.

Note the requirement of PowerShell V2 and PsSnapIn quest.activeroles.admanagement.

Also note that it’s only necessary to use the script during troubleshooting and not on a continual basis.  Probably won’t hurt anything, but if it’s not necessary to run it, why waste resources?

Also, as always, please test scripts you find on the internet in a test lab and fully understand them before bringing them into your production environments.

<#
.SYNOPSIS
   Script to generate report on DNS records marked tombstoned
.DESCRIPTION
   This script queries AD objects to find DNS records marked as tombstoned, creates a CSV file with the name of the record and when the record was changed to tombstoned.
   It then emails it to a list of individuals
.REQUIRES
	VERSION 2
	PsSnapIn quest.activeroles.admanagement 
.EXAMPLE
   PS c:\foo> .\Start-DNSTombstoneReport.ps1
#>

$TestQADSnapin = get-pssnapin | where { $_.Name -eq "Quest.ActiveRoles.ADManagement"} 
if($TestQADSnapin -eq $null){
	add-pssnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction SilentlyContinue
}
$CSVFile = "C:\temp\dnsTombstoneReport.csv"

$Result = Test-Path $CSVFile
If ($Result){
	Remove-Item $CSVFile -Force
}

$TombstonedRecords = Get-QADObject -searchroot 'CN=MicrosoftDNS,DC=ForestDnsZones,DC=MyDomain,DC=Local' -IncludedProperties dNSTombstoned,name,distinguishedName,whenchanged  -SizeLimit 0 -ShowProgress:$False | Where {($_.dNSTombstoned -eq $true) -and ($_.name -NotLike "_*")} | select-object distinguishedName,@{name="DateChanged";expression={$_.whenChanged.ToShortDateString()}}
If ($TombstonedRecords){
	$TombstonedRecords | Sort-Object -Descending DateChanged | Export-Csv $CSVFile -NoTypeInformation
	Send-MailMessage -From "SomeGuy@MyDomain.Local" -To "SomeGuy@MyDomain.Local"  -subject "DNS Tombstone Records Report" -smtpServer "smtp.MyDomain.Local" -attachments $CSVFile
	Remove-Item $CSVFile
}

PowerShell and Listing Shares on Your Network

From time to time, you may hear a question from staff about what shares have been set up on machines in your network.  One reason to ask this question is in implementing DFS.  To know all the shares that have been created over an extended period, and to make sure you document and understand what’s available on your network to all of your users, you need to somehow query this information.  Once you have file shares documented, you can more effectively implement DFS.  You will also know which printer shares have been set up.  And, ultimately, it’s always good to be aware of what is out there on your network or what people are doing with their machines.

There are some simple PowerShell scripts out there that touch on this in a very simple way.  But, a simple way to find a share on a machine is through this one-liner:


get-wmiobject -class "Win32_Share" -namespace "root\CIMV2" -computername "computername"

Simple enough.  However, one challenge is understanding RPC errors.  The primary reasons for returning an RPC error are:  You have a DNS resolution issue, a firewall issue on the target computer, or RPC isn’t running.

My primary goal in writing this script was to create some documentation and have a place to start for A) making sure we knew about all shares on all machines, B) having a report in csv format to see which shares didn’t have descriptions entered on the share setup, and finally C) being aware of any DNS issues so we could clean up DNS entries if necessary.

While this script isn’t 100% polished, it gets the job done.  We are doing several things here:

  • Get the entire list of computer objects in AD
  • Provide a simple progress indication to the user
  • Ping the computer and only try a WMI query against it if it responds
  • If the responding computer has an RPC error, handle that error and save the information to a csv file for resolution
  • Save all share information for each computer to a CSV file for documentation purposes
  • Only save the failed RPC errors if they occur

As always, please read through the script before attempting to run this.  I’ve commented each area to help you understand each section.  One section you might want to improve upon is only gathering share information if it is of a specific type(s).  In my case, I wanted to document ALL shares.

There’s a great post here:  http://blogs.technet.com/b/josebda/archive/2010/04/08/using-powershell-v2-to-gather-info-on-free-space-on-the-volumes-of-your-remote-file-server.aspx from Jose Barreto.  You could easily alter this script to show disk space and other information.

04/18/2012 – ADDENDUM: While running this script in a lab, I found a few problems with the script that I didn’t originally encounter on the first version. I will be adding some more conditional logic and see if I can’t correct the issues I’ve found.

I found a couple issues here. One after looking through comments from get-wmiobject queries by other people and several responses, including Jeffery Hicks, I decided only to query shares that would normally be accessed by users. This reduced the number of errors returned and also made the amount of data in the array smaller. One of the biggest challenges I ran into were the handful of different RPC errors. I found this quite curious. I went through all of the possible issues… are the services started, are there permissions issues, is it OS specific, etc. etc. I found no valid reason for these error. While enumerating machines from get-adcomputer or from a text file, the error would occur on random PC’s. If I used a one-liner to query the information using the computer names which failed on a previous test run of the script, the query was always successful.

To better understand what was happening, I set the following exception variables to true: ReportErrorShowExceptionClass, ReportErrorShowInnerException, and ReportErrorShowStackTrace. The continue statement in my trap wasn’t working when this error occurred, so I commented out my trap, set the above variables, and removed the default error action in order to find more information about what was going on.

Something curious happened. I ran the script a couple of times, no errors which terminated the script. Odd. Then, I had a hunch. It had to be the way I was handling the errors. I threw in a pause function and then to test the error handling, added a write-host with the error information and a pause statement to create my own breakpoint. I saw the big glaring problem, the way I was handling the errors from the get-wmiobject wasn’t working. Time to employ Try Catch.

But changing my previous error handling with a try/catch/finally block along with setting the default error action preference to silently continue and “logging” the errors seemed to do the trick.

I’m still curious why the wmi queries were so random. Why did they work in a one-liner and failed in a script? Some may know the answer, I’ve not ran into a reason for the cause.

Here is the current working version:

<#  
.SYNOPSIS
	Get Share Infornmation on Network Computers
.DESCRIPTION
	This script uses WMI and Active Directory to query all computers on a network within
	the domain to document shares.  It converts the share type information to readable share type.
.NOTES  
	File Name	: get-NetworkComputerShares.ps1
	Version		: 1.1
	Date		: 02/18/2012
	Author		: Ted Wagner
	Requires	: PowerShell V2  
.EXAMPLE
	PS c:\foo> .\get-NetworkComputerShares.ps1
#>
#Requires -Version 2.0

# Convert Share Type to Type Description
function get-ShareType($Share){
$ShareType = $(switch($Share){
		"0" {"Disk Drive"}
		"1" {"Print Queue"}
		"2" {"Device"}
		"3" {"IPC"}
		"2147483648" {"Disk Drive Admin"}
		"2147483649" {"Print Queue Admin"}
		"2147483650" {"Device Admin"}
		"2147483651" {"IPC Admin"}
	}
)
return $ShareType
}
Clear-Host
Import-Module ActiveDirectory

# Set Variables
$ErrorActionPreference = "silentlycontinue"
$counter = 0
$TotalProcessedCount = 0
$TotalSharesCount = 0
$ShareList = @()
$FailList = @()
# Let's set the error action preference to continue.  Trap using a trap statement below, display the error, and always continue.

# Get all AD Computer objects, select only the property "Name"
$Computers = Get-ADComputer -Filter 'Name -like "*"' | Select -Expand Name

foreach ($Computer in $Computers){
	$Counter++
	$TotalShares = $Computers.count
	$progress = [int]($Counter / $TotalShares * 100)
	Write-Progress -Activity "Getting inventory of Computers" -status "Processing shares on computer: $Computer - $counter out of $TotalShares" -perc $progress
	if (Test-Connection -Quiet -Count 1 -ComputerName $Computer){
		$TotalProcessedCount++
		try{
			$Shares = get-wmiobject -class "Win32_Share" -computername $Computer -ErrorVariable WMIError -ErrorAction SilentlyContinue | Select Name, Description, Path, Type | Where-Object { $_.type -eq "0"}
			# Make sure get-wmiobject returns data then populate array
			if ($Shares){
				foreach ($Share in $Shares) {
					$TotalSharesCount++			
					$ShareSummary = "" | Select ComputerName, Description, Name, Path, ShareType 
					$ShareSummary.Computername = $Computer
					$ShareSummary.Name = $Share.Name
					$ShareSummary.Description = $Share.Description
					$ShareSummary.Path = $Share.Path
					$ShareSummary.ShareType = get-ShareType $Share.Type
					$ShareList += $ShareSummary
				}
			}
		}
		catch{
				Write-warning "Error on computer number $counter - $Computer"
				$FailSummary = "" | Select ComputerName, Error
				$FailSummary.Computername = $Computer
				$FailSummary.Error = $WMIError[0].Exception
				$FailList += $FailSummary
				$WMIError.clear()
				continue
			}
		finally
		{
			$script:Exception = $_; continue
		}
	}
}

# Output data to CSV files - only output for RPC errors if errors received
$CSVFile = Read-Host "Enter filename/path for the Share Summary Report";$ShareList | Export-Csv $CSVFile -NoTypeInformation
if ($FailList){
	$CSVFile = Read-Host "Enter filename/path for the Share Failed Summary Report";$FailList | Export-Csv $CSVFile -NoTypeInformation
}
Write-Host "Processed $TotalSharesCount shares on $TotalProcessedCount computers"

November 2016 Update:

I’ve since been using the approach mentioned in this link: http://serverfault.com/questions/623710/can-you-use-a-powershell-script-to-find-all-shares-on-servers

Password Expiration Notifications – PowerShell to the Rescue

I grappled with posting this. Google PowerShell with Password Expiration Notification and you’ll find a bunch of posts and questions. I typically don’t like to make a PowerShell blog post when there is a lot of info out there.

So a little background. Microsoft changed the behavior of how users receive system based messages. That’s all fine and dandy I guess. In this case, I think what they did was a mistake when Windows 7 is in an Active Directory domain. If a GPO is in place to notify users of expiring passwords, often times they never see the message in the notification center.

Instead of receiving a dialogue box which makes it obvious to the users that their password is expiring, the message shows up in the notification area. Most users don’t pay attention to the notification area. And, it’s not customizable enough to force password notifications to be more prevalent.

There are a few possibilities here. You can write a PowerShell script that runs on demand, or is a scheduled task, which can email notifications to your users. There are a few examples of this out there using a variety of methods to email users. However, I don’t like this approach.

What if someone on your staff, in the future, decides they want to modify the email content or the script in any way? Like it or not, there are still a lot of people who have NOT jumped on board with PowerShell.

My alternate solution is still PowerShell based.

Wait, didn’t I just say… ? Yes, I did. The script I put together is more of a static script that (potentially) wouldn’t necessarily need changes. The script is run as a scheduled task and runs on a weekly basis. The script itself will look for expiring passwords in the next 14 days and send an HTML report to a distribution list. In my case, the distribution list members are help desk technicians. They use the report to be little proactive in speaking with managers or other important internal customers who might show up on this list allowing them to be aware of who they may be receiving calls from.

Instead of including the style sheet as an external document, the information is small enough and was included it in the script itself.

The script is relatively simple. Please realize that this was a quick script using one that Richard Siddaway posted in 2007. There are more dynamic and efficient ways to do what’s in this script. But, to get the job completed quickly to meet our needs, this is what I came up with.

I convert the csv to html so I can send the converted csv as html in the body of the email.

Giving credit where credit is due, many thanks to Richard Siddaway and Richard L. Mueller for some ideas from their blogs. The base of the script is pretty much Richard Siddaway’s script modified to meet my needs.

Also a note, since we’re dealing with objects here this line is important:

import-csv $Filename | ConvertTo-Html -Title "Password Expiration Report" -head $style | Out-File $FilenameHTML
$Body = Get-Content $FilenameHTML | Out-String

I used this simple trick be2cause with send-mailmessage you can’t send an object as an attachment.

# The "base" for this script is from Richard Siddaway
# http://richardspowershellblog.wordpress.com/2007/10/07/password-expiry/</code>

# Need requires 2.0 since it's using the send-mailmessage cmdlet
#requires -version 2
write-host "Requires PowerShell v2 because of the use of the send-mailmessage cmdlet."

#Test for QAD Snapin
$TestQADSnapin = get-pssnapin | where { $_.Name -eq "Quest.ActiveRoles.ADManagement"}
if($TestQADSnapin -eq $null){
Write-Host "Requires ActiveRoles Management Shell for Active Directory http://www.quest.com/powershell/activeroles-server.aspx"
add-pssnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction SilentlyContinue
}

# Create date for filename
$date = get-date -Format MM-dd-yyyy
$Filename = "c:\temp\PasswordsExpireReport-$date.csv"
$FilenameHTML = "c:\temp\report.html"

#create base CSV
Add-Content $Filename "Last Name, First Name, Username,Password Expires in (days)"

$date = Get-Date
## get the Domain Policy for the maximum password age
$dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$root = $dom.GetDirectoryEntry()

## get the account policy
$search = [System.DirectoryServices.DirectorySearcher]$root
$search.Filter = "(objectclass=domainDNS)"
$result = $search.FindOne()

## maximum password age
$t = New-Object System.TimeSpan([System.Math]::ABS($result.properties["maxpwdage"][0]))

## want all accounts where password will expire in next 7 days
## ie password was set (max password age) – 7 days ago
$d = ($t.Days)* -1 ## max password age days ago
$d1 = $d + 14 ## 7 days on from max password age

$ExpiringPasswords = Get-QADUser -SizeLimit 0 -IncludedProperties FirstName, LastName, SamAccountName, pwdLastSet, PasswordExpires, PrimarySMTPAddress | where {($_.PwdLastSet -gt (Get-Date).AddDays($d)) -and ($_.PwdLastSet -lt (Get-Date).AddDays($d1)) -and ($_.PasswordExpires) -and ($_.memberof -ne "CN=Service Account,CN=Users,DC=contoso,DC=com") -and -not ($_.AccountIsDisabled) } | select FirstName, LastName, SamAccountName, pwdLastSet, PasswordExpires, PrimarySMTPAddress

foreach ($user in $ExpiringPasswords | sort PasswordExpires){
$DaysLeft = (($user.PasswordExpires - $date).Days)
$Username = $User.SamAccountName
$FirstName = $User.FirstName
$LastName = $User.Lastname
Add-Content $Filename "$LastName, $FirstName, $Username, $DaysLeft"
}

$style = "
BODY{background-color:white;} TABLE{border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse;} TH{border-width: 1px; padding: 2px; border-style: solid; border-color: black; background-color:#C0C0C0} TD{border-width: 1px; padding: 2px; border-style: solid; border-color: black; background-color:white} "

import-csv $Filename | ConvertTo-Html -Title "Password Expiration Report" -head $style | Out-File $FilenameHTML
$Body = Get-Content $FilenameHTML | Out-String
$From = "IT Automated Messages "
$To = "helpdesk@contoso.com"
send-mailmessage -SmtpServer "smtprelay.contoso.com" -to $To -from $From -subject "Expired Password Report for $date" -body $Body -BodyAsHtml

if (test-path $Filename){
Remove-Item $Filename -Force
}

if (test-path $FilenameHTML){
Remove-Item $FilenameHTML -Force
}

Powershell and Wireshark

This links in to another post I had: http://trwagner1.blogspot.com/2010/03/using-powershell-with-process-monitor.html

We recently had a need to provide Wireshark logs for a support incident on an issue that was quite random. One of the problems with Wireshark when you leave it run for any period of time is the pcap files can be quite large in size. Also, after running Wireshark for a long period of time, it will eat up a lot of resources and several instances of the dumpcap.exe process are left running.

The script below isn’t 100% polished or perfected as I didn’t have a whole lot of time to dedicate to it. I borrowed some quick script functions from others here as well.

This ran as a scheduled task on a Windows 2008 R2 server. In order to do so, a profile.ps1 had to be set up for the account used to run the scheduled task and an execution policy had to be set up. The script runs every 2 hours and creates a new pcap file each new run.

Command line: C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe
Arguments: -nologo \\scriblib\start-WireShark.ps1

# start-WireShark.ps1
# Ted Wagner
# 12/22/2011

# Install WireShark. Run wireshark.exe -D to find device names and place in script

# Requires the use of Windows Sysinternals handle.exe to check for open files
# http://technet.microsoft.com/en-us/sysinternals/bb896655.aspx

# Logstamp function by Jeffery Hicks
# http://jdhitsolutions.com/blog/2006/05/create-timestamp-log-in-powershell/
function logstamp {
$now = get-Date
$yr = $now.Year.ToString()
$mo = $now.Month.ToString()
$dy = $now.Day.ToString()
$hr = $now.Hour.ToString()
$mi = $now.Minute.ToString()
if ($mo.length -lt 2) {
$mo = "0"+$mo #pad single digit months with leading zero
}
if ($dy.length -lt 2) {
$dy = "0"+$dy #pad single digit day with leading zero
}
if ($hr.length -lt 2) {
$hr = "0"+$hr #pad single digit hour with leading zero
}
if ($mi.length -lt 2) {
$mi = "0"+$mi #pad single digit minute with leading zero
}
write-output $yr$mo$dy$hr$mi
}

clear-host

# Kill WireShark Process first
do{
Start-Sleep -seconds 2
$ProcMonTestProcess = Get-Process | where {$_.ProcessName -eq "wireshark"}
if ($ProcMonTestProcess){
Stop-Process $ProcMonTestProcess.Id}
}while(
$ProcMonTestProcess.Id -eq $true
)

# Kill any dumpcap.exe processes
do{
Start-Sleep -seconds 2
$ProcMonTestProcess = Get-Process | where {$_.ProcessName -eq "dumpcap"}
if ($ProcMonTestProcess){
Stop-Process $ProcMonTestProcess.Id}
}while(
$ProcMonTestProcess.Id -eq $true
)

$WireShark = "E:\Program Files\Wireshark\wireshark.exe"
$HandleExe = "\\server\tools\Utilities\Sysinternals\handle.exe"

# Start WireSharp, capture immediately and provide file name
# make sure to have the correct device name below. I suppose a script could be written to gather and provide this automatically but I didn't
# have a lot of time to spend on this script and grabbed the value using "wireshark -D"
&amp; $WireShark -i '\Device\NPF_{17B9CF31-0C9F-4C57-92AF-09294FB634F5}' -k -w "\\server\share\wireshark\Wireshark-Output-$(logstamp).pcap"

# Error Handling
trap{
Write-Host "Exception: " + $_.Exception.Message
break
}
Support
Writing & Editing
Code » Posting Source Code

Code, Embed, Languages, php, programming, Shortcode
Contents

    Configuration Parameters
    Credits

Related

    404 Not Found error
    Archives Shortcode
    Autosave
    Code
    Contact Form
    Copy a Post
    Deleted Posts and Pages
    Distraction Free Writing
    Editing With Multiple Authors
    Editors
    Featured Images
    Memolane Embed
    Microsoft Word
    Missing Visual Editor
    Offline Editing
    Pages
    Plinky: A Tool for Writer’s Block
    Post by Email
    Post by Voice
    Post vs. Page
    Posts
    Press This
    Proofreading
    Request Feedback
    ScribeFire
    Smilies
    Splitting Content
    Trash
    Twitter Follow Button
    Visual Editor
    Write a Book
    Writing and Formatting Poetry
    Writing Helper

While WordPress.com doesn’t allow you to use potentially dangerous code on your blog, there is a way to post source code for viewing. We have created a shortcode you can wrap around source code that preserves its formatting and even provides syntax highlighting for certain languages, like so:
1	#button {
2	    font-weight: bold;
3	    border: 2px solid #fff;
4	}

To accomplish the above, just wrap your code in these tags:


your code here