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:

	Get Share Infornmation on Network Computers
	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.
	File Name	: get-NetworkComputerShares.ps1
	Version		: 1.1
	Date		: 02/18/2012
	Author		: Ted Wagner
	Requires	: PowerShell V2  
	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
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){
	$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){
			$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) {
					$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
				Write-warning "Error on computer number $counter - $Computer"
				$FailSummary = "" | Select ComputerName, Error
				$FailSummary.Computername = $Computer
				$FailSummary.Error = $WMIError[0].Exception
				$FailList += $FailSummary
			$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


Linux – Your child will surprise you!

Like many IT folks with children, I grew tired of re-building a computer I had for my daughter every time Windows XP or Windows 7 threw its back out.  About a year ago, I put Linux Mint 11 on my daughter’s computer.  Actually, dual boot with Windows 7.  She started to like it quite a bit.  She has done all of her homework on it and has adapted quite well.  As the months went by, she became more and more independent with her “assistance needs” using Linux.

My daughter is 12.  Since she has been using Linux, she’s never had to reboot due to memory issues, she’s never had any issues with any apps, she’s been able to use LibreOffice for all of her homework, and she’s adapted well to using Linux as opposed to Windows.

Yesterday, I burned a live DVD yesterday of Linux Mint 12 and Solus OS RC3.  After my daughter finished her homework, she revved up both and was quite delighted.  I told her to spend a week evaluating each and let me know which one she wanted installed.  Now, I normally don’t like to install/use an RC of anything.  But, Solus OS is looking quite attractive and it’s a true Debian distro.

So, we’ll see how it goes and what she chooses.

A couple of posts worth noting

There have been three recent posts of interest which I believe are worth mentioning. Well, one is relatively recent and the other is within the last 6 months or so.

On March 19th, there was an excellent post about scheduling background jobs in on the Windows PowerShell blog. It is well worth the read. This is something I will start playing with at home.


And, finally, here is another great place to do some learning. Fire up that Virtual Box and take a small Exchange 2010 boot camp. There is no cost to this and no excuse not to expose yourself to Exchange 2010 if you are unfamiliar with it.


Microsoft CA – Templates Not Showing up in IIS Web Enrollment

I’ve seen a good number of questions posted about someone’s templates not showing up under the IIS web enrollment page. But, they always seem to miss a critical piece of information when someone has created an Enterprise CA that is Windows 2008 R2.

TYPICALLY the problem is one or a combination of the following three things below:

1) In certificate template Subject tab wasn’t switched to Supply in request.

2) Certificate template was configured for validity greater than one year (this is actually not 100% true by the way)

3) the enrollment permissions on the certificate are incorrect

ALWAYS make SURE you know what functional level of AD you have BEFORE you create a new template. If you are 2003, make sure you create the template as 2003 Enterprise. Do not pass go, do not collect $100, do not create a 2008 Enterprise template when AD is at a 2003 functinoal level IF you want that template showing up under web enrollment.

You can create the template. There is nothing that prevents you from doing so. However, you would have to manually create a certificate request using the following procedure:

Again, if you want that template to show up under web enrollment, your CA is 2008 R2 and your functional level of AD is at 2003, make sure you create that template as 2003 enterprise and follow the other best practices.

Now, the bit about a 1 year limitation causing problems with the template showing up under web enrollment, I haven’t seen that as a valid problem. I’ve gone as high as 10 years in a lab environment and it works just fine.

For the sake of endorsing Microsoft documentation, make sure you read and follow the following information:


vSphere 4.x Web Console – Missing VM’s

I’ve had difficulty finding this in the VMware forums and thought I’d post something on it here.

You may find that the vSphere web console is missing some VM’s. But, in the regular vSphere client, they are all there and accessible.

Chances are there’s a folder that was renamed or deleted.

In the vSphere client, select the VMs and Templates view. Expand your datacenter icon. You should have 1 folder. I believe the name should beDiscovered virtual machine“. If not, create one. Don’t do anything else. Don’t place any VM’s in that folder. Just leave it as is.

Wait several minutes and log into your vSphere web console. You should give vCenter some time to rebuild that list. If you launch the vSphere web console and the VM’s still aren’t there. Log off vSphere web console and give it some more time. Log back in and refresh if necessary.

You should be good to go after that.

If you don’t use the vSphere web console, then you don’t need that folder under the VMs and Templates view.

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


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

# Kill any dumpcap.exe processes
Start-Sleep -seconds 2
$ProcMonTestProcess = Get-Process | where {$_.ProcessName -eq "dumpcap"}
if ($ProcMonTestProcess){
Stop-Process $ProcMonTestProcess.Id}
$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
Write-Host "Exception: " + $_.Exception.Message
Writing & Editing
Code » Posting Source Code

Code, Embed, Languages, php, programming, Shortcode

    Configuration Parameters


    404 Not Found error
    Archives Shortcode
    Contact Form
    Copy a Post
    Deleted Posts and Pages
    Distraction Free Writing
    Editing With Multiple Authors
    Featured Images
    Memolane Embed
    Microsoft Word
    Missing Visual Editor
    Offline Editing
    Plinky: A Tool for Writer’s Block
    Post by Email
    Post by Voice
    Post vs. Page
    Press This
    Request Feedback
    Splitting Content
    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