Top Banner
PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from Carnival Cruise Lines email: [email protected] Other qualifications: See next page for email signature
35

PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

Dec 26, 2015

Download

Documents

Octavia Kelley
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

PowerShell from the Trenches

Three Examples of solutions implemented with PowerShell

Presented by:Jorge Besada - SQL Server Database Administrator from Carnival Cruise Lines email: [email protected] qualifications:See next page for email signature

Page 2: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

Jorge Besada - CPE, MPD(Carnival PowerShell Evangelist - Master PowerShell Developer)------------------------------------------------------------------------------From Jeff Snover, PowerShell creator"It's like programming with hand grenades.”

Page 3: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

First application

Here at work we make extensive use of SAN storage and many of the server drives are not local drives. They work well, we forget about them until one of them fails. 99% of the time is the problem is not an actual failure, but the connection/mapping of the drive/SAN is lost.

And when the drive that goes down happens to contain the SQL server installation of an instance you get a lot of calls from angry customers and it is difficult to diagnose initially (especially if it has been a long time since the last incident of missing drives) what actually happened. When you miss a drive holding user database files is not that bad, you see the database as offline and eventually you find the cause.The fix is easy: the network person just does a rescan

Page 4: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

From command line:echo rescan | diskpart

Page 5: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

So to save ourselves DBAs some aggravation a powershell program was created that does the following:It lists all drives from a list of serversIt compares the list of today’s run with the saved list from yesterday’s run, the differences will be the servers with missing drivesIf there is a difference it runs the rescan command on the affected serversAn email is sent reporting the issue

Page 6: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

• #GetMissingDrives.ps1• #sends email with servers and missing drives• #rescans drives

• #how to use it:• #Edit the file servers_list.dat to include the list of servers to check

(a server per line)• #(comments can be added starting with #)• #Sample file:

• ##Server list to monitor hard disk space• ##To add comments start the line with #• ##---------SQLSERVERS-----------------• #CCLTSTINFSQL1• #CCLDEVINFSQL1

• $SERVERLIST = 'C:\Users\jorgebe\Documents\powershell\servers_list.dat'• $EMAILTOLIST = '[email protected]'• $FROM = "[email protected]"• $SUBJECT = "Servers Missing Drives Report"• $LOGFILE = 'C:\Users\jorgebe\Documents\powershell\today.lst'• $REFERENCE = 'C:\Users\jorgebe\Documents\powershell\reference.lst'

Page 7: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

• function ListDrives($Servername)• {• ForEach-Object `• {• get-wmiobject -computername $Servername win32_logicaldisk | select-object systemname, deviceID, Size, Freespace,

DriveType • }• }

• #Returns array of servers and drives• <# Sample output• SERVER1|C• SERVER1|D• SERVER2|C• SERVER2|D• SERVER2|E• ...• #>• function ProcessServers($Serverlist)• {

• $outarray = @()• $Computers = Get-Content -Path $Serverlist;• foreach ($z in $Computers) • {• if ($z -gt "" -and $z[0] -ne "#")• {• $erroractionpreference = "SilentlyContinue"• $a = ListDrives($z)• foreach ($k in $a)• {• if ($k.DriveType -eq 3)• {• $p = $k.deviceid -replace(":", "")

$outarray += $z + '|' + $p

• }• }• • }• }

return $outarray

• • }

Page 8: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

Send mail goodie• #Usage: SendMail message • # emailist (as array)• # c:\attach1.txt;c:\attach2.txt • # [email protected] • # "This is the subject"• function SendMail ($report,$emailarray,$attacharray,$from,$subject)• {• $smtpServer = "smtphost.carnival.com"• $msg = new-object Net.Mail.MailMessage• $smtp = new-object Net.Mail.SmtpClient($smtpServer)• $msg.From = $from • foreach ($c in $emailarray)• { • $msg.To.Add($c)• } • $attlist = $attacharray.Split(";")• foreach ($c in $attlist)• { • $att = new-object Net.Mail.Attachment($c)• $msg.Attachments.Add($att)• } • $msg.Subject = $subject• $msg.Body = $report• $smtp.Send($msg)• }

Page 9: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

The program

• Write-Host "Processing server list " $SERVERLIST• #remember: server list has the simple list of servers, no drives• #SERVER1• #SERVER2

• #this call expands the original server list to include the drives• #SERVER1|C SERVER1|D SERVER2|C SERVER2|D SERVER2|E

• $r = ProcessServers $SERVERLIST

• #And we save the actual list to a file today.lst• Set-Content $r -Path $LOGFILE -Force

• #We read the reference list (no missing drives, file reference.lst into a variable• $z = Get-Content $REFERENCE

• #And this is one of the best things of PowerShell: the comparison comandlet• #Compare-Object -ReferenceObject $z -DifferenceObject $r• $g = Compare-Object $z $r

• #if there is a difference that means we lost a drive• if ($g)• {

$msg = [char]13 + 'Re-scanning disks...' + [char]13Write-Host $msg

• foreach ($x in $g)• {• $c = $x.InputObject.Split('|')[0]• Write-Host 'Computer: ' $c• $msg = $msg + $c + [char]13• #And this is another extremely cool powershell thing: The Invoke-Command• #to use invoke-command you need to be at PowerShell version 2.0• $h = invoke-command -computername $c {"rescan" | diskpart}• foreach ($f in $h)• {• $msg = $msg + $f + [char]13• }• }• SendMail $msg $EMAILTOLIST $LOGFILE $FROM $SUBJECT

• #The previous code fixed the missing drives calling rescan for the involved servers• #now we reset the reference list, to leave it ready for the next run• $r = ProcessServers $SERVERLIST• Set-Content $r -Path $REFERENCE -Force• }

Page 10: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

Quiz Now

The program as listed in the previous slide has a bug. It works as expected, but the bug is there … lurking. I discovered it while preparing this presentation and I have not yet fixed it

Can you find it?Going back to the code screen …

Page 11: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

The little bug: suppose you missed more than one drive in a server. You only need to rescan once for all the missing drives to be reattached. The original code would have done the rescan once for each missing drive.This is fixed in the following code:

• if ($g)• {• $msg = [char]13 + 'Re-scanning disks...' + [char]13• Write-Host $msg• $c_previous = "" • foreach ($x in $g)• {• $c = $x.InputObject.Split('|')[0]#here we get the computer name• #Here is the fix:• if ($c -ne $c_previous)• {• Write-Host 'Computer: ' $c• $msg = $msg + $c + [char]13• $h = invoke-command -computername $c {"rescan" | diskpart}• foreach ($f in $h)• {• $msg = $msg + $f + [char]13• }• $c_previous = $c• }• }

Page 12: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

End of First ApplicationApp number two: The Data Quest

High level manager request data to be pulled from all ships. After a lot of work two queries were created. The task was to run these two queries in every ship and collect the results in csv files- The queries may have to be re-run after

updates- Satellite connections to the ships are slow, and

sometimes not available

Page 13: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

First try

A cmd file was created calling sqlcmd for each ship, executing the two queries on each ship sql server and saving the output to a file:

One_ship.cmd:Sqlcmd –S %1 –s, -W –d ssadserver –I”cpg_query1.sql” –o%1”_1.csv”Sqlcmd –S %1 –s, -W –d ssadserver –I”cpg_query2.sql” –o%1”_2.csv”

All_ships.cmd:One_ship.cmd SHIP1One_ship.cmd SHIP2… many ships

This did not work: the results coming across the network (some files were 60Mb and more), the process hung for hours.We needed a program to do this:- Copy the queries to the ship- Execute them in the ship, keep results in ship

Page 14: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

- Compress the output file- Copy results back to office

- All these steps could have been done with cmd files, but I have noticed that I save a lot of time using powershell combined with cmd files. I have a simple rule for this:

- can the program be done with a single bat file- If yes, just do it - If not, most of the time you save time using

powershell as a driver of other scripts

Page 15: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

• #CPG_Transfers2.ps1 FILE SHARE VERSION, Jorge's workstation version• #Runs sql queries in ships, collects resulting csv files

• #How to use it:• #Edit the file servers_list.dat to include the list of servers to check (a server per line)• #(comments can be added starting with #)• #Sample file:

• ##To add comments start the line with #• ##---------SQLSERVERS-----------------• #SHIPSERVER1• #SHIPSERVER2

• $SERVERLIST = 'C:\Users\jorgebe\Documents\powershell\ships_list.dat'• $EMAILTOLIST = '[email protected]'• $FROM = "[email protected]"• $SUBJECT = "CPG Transfer Report - Fileshare version"• $SAVE_FOLDER = '\\CCLDEVINFSQL1\TEMP\backup\'

• #Returns ping number or empty string if no response• function CheckPing($server)• {

$v = (ping $server -n 1)

• foreach ($k in $v)• {• if ($k.StartsWith("Reply"))• {• break• }• }• $l = $k.Replace('<', '=')• $lst = $l.split('=')[2]• if ($lst)• {• $r = $lst.Replace('ms TTL', '')• }• else• {• $r = ""• }• return $r• }

Page 16: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

• function ProcessServers($Serverlist)• {

• $outdict = @{}• $Computers = Get-Content -Path $Serverlist;• foreach ($z in $Computers) • {• #Sort by ping process• if ($z -gt "" -and $z[0] -ne "#")• {• $ping = CheckPing($z)• $outdict[$z] = [int]$ping• }

• }

• $sorted = $outdict.GetEnumerator() | Sort-Object Value

Page 17: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

• foreach ($j in $sorted)• {• $t1 = Get-Date• $dt1 = $t1.tostring()• Write-Host "Started" $dt1

• $ping = $j.value• $z = $j.name• $ping_current = CheckPing($z)• Write-Host "Ping response original run: " $ping" Current: " $ping_current• if ($ping_current -gt "")• {• Write-Host "Copying queries to ship" $z• $dest = '\\' + $z + '\DBAScripts'• copy "cpg_query1.sql" $dest -Force• copy "cpg_query2.sql" $dest -Force

• Write-Host "Executing CPG queries on ship"• (C:\Users\jorgebe\Documents\powershell\cpg_transfers.cmd $z)

• Write-Host "Compressing results"• (C:\Users\jorgebe\Documents\powershell\cpg_transfers_compress.cmd $z)

• Write-Host "Collecting results"• $src1 = $dest + '\cpg_query1.csv'• $src2 = $dest + '\cpg_query2.csv'• copy $src1 ($SAVE_FOLDER + $z + '_1.csv') -Force• copy $src2 ($SAVE_FOLDER + $z + '_2.csv') -Force

• $SUBJECT = $z + " cpg queries process completed"• $msg = "Started at " + $dt1 + [char]13 + "Completed at " + $dt2 + [char]13• $tt = $t2 - $t1• $msg = $msg + "Elapsed time: " + $tt.ToString()+ [char]13• $msg = $msg + "Ping response: " + $ping + [char]13• SendMail $msg $EMAILTOLIST "" $FROM $SUBJECT• }• else• {• Write-Host "-------------------------------------"• Write-Host "Ship " $z " not responding"• Write-Host "-------------------------------------"• }

}

Page 18: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

Vodoo cmd file (cpg_transfers.cmd)The double combination of the 2 sqlcmd calls and xp_cmdshell enables the placing of the output file in the desired remote server location

Line 1:

sqlcmd -S %1,9999 -l 10000 -Q“xp_cmdshell ‘sqlcmd -h -1 -l 10000 -S . -d ssadserver -s, -W -I -id:\DBAScripts\cpg_query1.sql -o d:\DBAScripts\cpg_query1.csv‘“

Line 2:

sqlcmd -S %1,9999 -l 10000 -Q“xp_cmdshell ‘sqlcmd -h -1 -l 10000 -S . -d ssadserver -s, -W -I -id:\DBAScripts\cpg_query2.sql -o d:\DBAScripts\cpg_query2.csv‘“

Page 19: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

Remote compress cmd file cpg_transfers_compress.cmd:“compact” is a little known compress utility, it is built in latest server versions

sqlcmd -S %1,9999-l 10000-Q"xp_cmdshell 'compact /c d:\DBAScripts\cpg_query1.csv'“

sqlcmd -S %1,9999-l 10000-Q"xp_cmdshell 'compact /c d:\DBAScripts\cpg_query2.csv'"

Page 20: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

SAMPLE OUTPUT FOR FOUR SHIPS

Thursday, March 14, 2013 3:04:22 PMProcessing ships list ships_list.datPing response: 695 Ship: LESQL1.LEGEND.CARNIVAL.COMCopying queries to ship3/14/2013 3:04:23 PM3/14/2013 3:05:04 PMExecuting CPG queries on ship3/14/2013 3:07:00 PMCompressing results3/14/2013 3:07:19 PMCollecting results3/14/2013 3:08:13 PMcompleted 3/14/2013 3:08:13 PMPing response: 840 Ship: SPSQL1.SPIRIT.CARNIVAL.COMCopying queries to ship3/14/2013 3:08:15 PM3/14/2013 3:08:38 PMExecuting CPG queries on ship3/14/2013 3:09:42 PMCompressing results3/14/2013 3:09:58 PMCollecting results3/14/2013 3:10:39 PMcompleted 3/14/2013 3:10:39 PMProcess completed Thursday, March 14, 2013 3:10:40 PM

Thursday, March 14, 2013 4:18:01 PMProcessing ships list ships_list.datPing response: 1517 Ship: IMSQL1.IMAGINATION.CARNIVAL.COMCopying queries to ship3/14/2013 4:18:03 PM3/14/2013 4:18:55 PMExecuting CPG queries on ship3/14/2013 4:25:47 PMCompressing results3/14/2013 4:26:03 PMCollecting results3/14/2013 4:43:25 PMcompleted 3/14/2013 4:43:25 PMPing response: 799 Ship: SESQL1.SENSATION.CARNIVAL.COMCopying queries to ship3/14/2013 4:43:27 PM3/14/2013 4:43:55 PMExecuting CPG queries on ship3/14/2013 4:49:34 PMCompressing results3/14/2013 4:49:50 PMCollecting results3/14/2013 5:55:56 PMcompleted 3/14/2013 5:55:56 PMProcess completed Thursday, March 14, 2013 5:55:56 PM

Page 21: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

App Number Three:Original Request

• There was an old Vb.net application used to send files from corporate office to the remote locations and collect other files from them. It used MSMQ. At some time the application started to have intermittent failures and after some research it was detected that the files had grown overtime to sizes that exceeded the message queue limits

Page 22: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

Original Plan “A”Solution

• Add code to the old VB.net application to incorporate file splitting before the transmission and file joining after reception

• A couple of PowerShell functions were created to do the split/join

Page 23: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

SplitDeveloped using advanced techniques (next slide)

function split($inFile, $outfolder, $outPrefix){

$bufSize = (Get-ItemProperty $infile).Length / $CHUNKSWrite-Host 'bufsize = ' $bufSize

$stream = [System.IO.File]::OpenRead($inFile) $chunkNum = 1 [Int64]$curOffset = 0 $barr = New-Object byte[] $bufSize

while( $bytesRead = $stream.Read($barr,0,$bufsize)){ $outFile = $outfolder + '\' + "$outPrefix$chunkNum" $ostream = [System.IO.File]::OpenWrite($outFile) $ostream.Write($barr,0,$bytesRead); $ostream.close(); echo "wrote $outFile" $chunkNum += 1 $curOffset += $bytesRead $stream.seek($curOffset,0); }}

rm ($WORKFOLDER + '\*') -Force split $SOURCEFILE $WORKFOLDER $FILEPREFIX

Page 24: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

Advanced Technique

• I learned this technique in TechEd 2012 from the PowerShell gurus

Steal from The BestAndCreate the Rest

Page 25: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

Join function join($outfile, $folder, $prefix){$cnt = (Get-ChildItem $folder).Count$ostream = [System.IO.File]::OpenWrite($outFile)$i = 1while ($i -lt $cnt + 1){Write-Host $i$file = $folder + '\' + $prefix + $i$bufSize = (Get-ItemProperty $file).LengthWrite-Host $file $bufSize$stream = [System.IO.File]::OpenRead($file) $barr = New-Object byte[] $bufSize

while( $bytesRead = $stream.Read($barr,0,$bufsize)){ $ostream.Write($barr,0,$bytesRead);}$i++}$ostream.close();}

Page 26: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

Intermezzo

• All this is fine and dandy, but …• This thing of taking an old vb.net app, adding a

powershell hack to it looks like a … hack

• Can we have a more elegant approach using PowerShell for everything?

• Let’s see the “Plan B”

Page 27: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

FileExchanger.ps1- What it does:

The contents of the source main folder \\MAINSERVER\outbound_miamiare sent to the shared folders in the remote servers, each server receives the corresponding filesbased on the file id in the file name to be sentThe contents of the source main folder \\MAINSERVER\inbound_miamireceives the files from all the remote servers

Remote servers:The contents of shared outbound folder (sample folder \\REMOTESERVER\outbound )are sent to the mainserver collecting folder \\MAINSERVER\inbound_miami

How to test:

First:Place files with the correct file name format in the remote server folderSample values:For remote server xxdevsql1 file id used was 300Sample file name is 000000_300_MOP_15269.zip in \\xxdevsql1\app\outboundFor remote server ccltstsql1 file id is 350

Second:Place files with the correct file name format in the mainserver outbound folderSample values:300_000000_MOF_12010.zip350_000000_MOF_12012.zip375_000000_MOF_11256.zip400_000000_CORP_9897.zip

Third: Execute the powershell program FileExchanger.ps1 from MAINSERVER

Results:The files from remote servers will end up in the mainserver inbound folderThe files from the mainserver outbound folder will end up in the corresponding inbound folder in the remote serversThe extra files in the mainserver outbound folder (for remote servers not in the list) are not processed.

Page 28: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

FileExchanger.ps1 – the code - 1

#MAINSERVER SHARES$FiletoSendLoc1 = '\\ccldevsql1\outbound_miami'$PlacetoSave1 = '\\ccldevsql1\inbound_miami‘

$REMOTESERVERS = @{300="Miami|xxdevsql1"325="New York|ccltstsql1"350="Rio de Janeiro|ccltstcorpsql1"400="London|ccldevinfsql1"}

function CheckPing($server){$v = (ping $server -n 1)$l = $v[2].Replace('<', '=')$lst = $l.split('=')[2]$r = $lst.Replace('ms TTL', '')return $r}

function ExtractItems($file_id){$server_code, $server_name = $REMOTESERVERS[$file_id].Split('|')return $server_code, $server_name}

Page 29: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

FileExchanger.ps1 – the code - 2

function SendFiles($file_id){ Write-Host 'Sending files to ' $REMOTESERVERS[$file_id] $server_code, $server_name = ExtractItems($file_id) Write-Host 'Server name: ' $server_name ', Server code: ' $server_code $dest = '\\' + $server_name + '\app\inbound' $p = $FiletoSendLoc1 + '\' + $file_id + '_*.zip' Write-Host 'destination folder: ' $dest copy $p $dest -Force -Verbose}

function ReceiveFiles($file_id){Write-Host 'Receive files from ' $REMOTESERVERS[$file_id]$server_code, $server_name = ExtractItems($file_id)Write-Host 'Server name: ' $server_name ',Server code: ' $server_code $src = '\\' + $server_name + '\app\outbound\000000_' + $file_id + '_' + '*.zip'Write-Host 'destination folder: ' $PlacetoSave1copy $src $PlacetoSave1 -Force -Verbose}

function MainLoop{foreach ($file_id in $REMOTESERVERS.get_keys()){Write-Host 'Processing' $file_id$server_code, $server_name = ExtractItems($file_id)$t = CheckPing($server_name)Write-Host 'Checking ping response for the record:' $server_name $t 'ms'if ($t -lt 1000){SendFiles($file_id)ReceiveFiles($file_id)}else{Write-Host 'Ping response too high, skipping ' $server_name ' in this run' }}

}

MainLoop

Page 30: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

FileExchanger.ps1 – the output

Page 31: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

Final Notes

I was fortunate to attend TechEd 2012 this year in Orlando.

It was a highly technical event, I attended most of the PowerShell conferences:

Page 32: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

Final Notes

Ooops … wrong slide here

Page 33: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

Jeff Snover – PowerShell Creator

... Snover, (the architect behind the product now responsible for the entire windows server platform):

"It's like programming with hand grenades.”

Page 34: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

I know people!

Page 35: PowerShell from the Trenches Three Examples of solutions implemented with PowerShell Presented by: Jorge Besada - SQL Server Database Administrator from.

Conclusion

If you want to be a good PowerSheller …

You Better Wear Vibram Shoes