Migrating from NetApp to Windows File Servers with PowerShell – part 4

In our final entry in the Windows File Server management with PowerShell series, we have a quick look at managing shares using powershell.

The one feature of the NetApp that I will miss most was the automatic home share. Similar to Samba, you can tell the filer a set of directories under which user home directories are located. The filer will create these shares on-demand for a connecting user. Even under Server 2008 R2, Windows has no such capability. So what is the solution? Well, we have to create a share for every user. Augh! Fortunaltely, a beefy box can handle all these shares fairly gracefully, and with PowerShell, creating the shares is fast.

Some logic we added to this script includes probing for existing shares using “net share” before we attempt to create the share. If output of “net share” shows that the physical directory path is not correct, we delete the existing share and re-create it. If the share exists, and has the correct path, we don’t touch it. If the share does not exist, we create it.

I learned a fair bit about invoking external commands with PowerShell while writing this script. To summarize, PowerShell is not much like cmd.exe. When invoking a command with multiple parameters, you very often need to separate the parameters into array components, otherwise PowerShell does not know where to delimit the parameters. For example, let’s say we want to run the command:

net share jsmith=h:\homes1\j\jsmith /GRANT:DOMAIN\jsmith,FULL

The username and path are to be populated using variables in the script, so we try:

& net share ($uname)=($path) /GRANT:DOMAIN\($uname),FULL

Looks good, right? Unfortunately, not. The PS invocation command (“&”) parses the command parameters incorrectly, and thus passes a bunch of trash to the “net.exe” external command, and thus puts nasty red errors all over your console.

What we need to do is put the command parameters into an array, and feed that to the PS invocation command:

[string] $cmd = 'net.exe'
[string[]] $params = 'share', ($uname + '=' + $path),`
     ('/GRANT:DOMAIN\' + $uname + ',FULL')
& $cmd $params

Or perhaps more readably:

[string] $cmd = 'net.exe'
[array] $params = @()
[string] $params += 'share'
[string] $params += $uname + '=' + $path
[string] $params += '/GRANT:DOMAIN\' + $uname + ',FULL'
& $cmd $params

I do something similar below, showing once again that there is more than one way to code a cat (and that some cats are better looking than others):

# Shares all directories at the third directory level of the 
#	volumes specified in $homeVols.
# Assumes that all subdirectories will have a name matching an 
#	existing CAMPUS user ID.

Set-PSDebug -Strict

[string[]] $homeVols = @()
$homeVols = "h:\","i:\","j:\","k:\","l:\","m:\","n:\"

foreach ($vol in $homeVols) {
    $subDirs1 = gci $vol | ? {$_.Attributes.tostring() -match "Directory"}
    foreach ($dir1 in $subDirs1) {
        $subDirs2 = gci $dir1.FullName | ? {$_.Attributes.tostring() `
			-match "Directory"}
        foreach ($dir2 in $subDirs2) {
            $uhomes = gci $dir2.FullName | ? {$_.Attributes.tostring() `
				-match "Directory"}
            if ($uhomes.count -ge 1) { foreach ($uhome in $uhomes) {
				[bool]$doShare = $false
                [string] $cmd = "net.exe"
                [string] $arg1 = "share"
				[string] $arg2 = ($uhome.name)
				[string] $out = & net $arg1, $arg2 2>&1
				[string] $homePath = $vol + $dir1.name + "\" + $dir2.name + "\" `
					+ $uhome.name				
				[string] $homePathRX = $vol + "\" + $dir1.name + "\\" `
					+ $dir2.name + "\\" + $uhome.name
				if ($out -match "does not exist") {
					write-output "Share does not exist for $homePath"
					[bool] $doShare = $true
				} elseif ($out -notmatch $homePathRX) {
					write-output "Share not defined correctly for path $homePath"
					[bool] $doShare = $true
					#Delete existing share
					[string] $arg3 = "/DELETE"
					[string] $out = & net $arg1, $arg2, $arg3 2>&1
				if ($doShare) {
	                [string] $arg2 = ($uhome.name + "=" + $homePath)
	                [string] $arg3 = "/Grant:CAMPUS\" + $uhome.name + ",FULL"
	                #write-host "about to share: " $uhome.name
	                # The next line will call the "net" command to share the 
					#	current home directory.
	                # If no mapping can be made between the directory name and 
					#	an AD user, "No Mapping" will be in the error output.
	                [string] $out = & net $arg1, $arg2, $arg3 2>&1   
	                if ($out -match "No Mapping") {
						Write-Output `
							"User was not defined for share on path $homePath"
	                   $uhome.name | out-file -FilePath h:\noMapUsers.txt -append 
	                } else {
						Write-Output "Successfully shared $homePath"
            }} #End if ($uhomes.count -ge 1)
        } #End $dir2 in $subDirs2
    } #End $dir1 in $subDirs1
} #End $vol in $homeVols