PS C:\> 'server1','server2','Badserver' | Set-UserPassword -Username 'test' -Password 'pass123' -Verbose VERBOSE: Processing server 'server1'... VERBOSE: Connected to server 'server1'... VERBOSE: Retrieved user objects from server 'server1'... VERBOSE: Found user 'test' from server 'server1'... 10/12/2015 14:41:31 - Successfully changed password for user 'test' on server 'server1' VERBOSE: Processing server 'server2'... VERBOSE: Connected to server 'server2'... VERBOSE: Retrieved user objects from server 'server2'... WARNING: ERROR: No user 'test' on server 'server2' VERBOSE: Processing server 'Badserver'... WARNING: ERROR: Failed to connect to server 'Badserver' PS C:\>
About Me
My Links
Monday, October 12, 2015
Powershell Script - Set-UserPassword - Remotely sets local account passwords
Here's a great script to change passwords in bulk on many servers. I've added verbose and error output for logging purposes as well as time/date stamping for when actual password setting occurs.
Monday, October 5, 2015
Powershell Quick Script - Get emailed when a specific account is locked
Here's something quick and simple which sends you an email when an AD user account is locked out. The script just runs in a loop, polling ever 30 seconds and then finishes once the account is locked out. This requires a SMTP relay or receive connector on your exchange server to receive email.
$username = 'myuser' While(Get-AdUser $username).enabled){Start-Sleep -Seconds 30} Send-MailMessage -To 'admin@domain.com' -From 'YourPSscript@domain.com' -Subject "Account locked out - $username" ` -SmtpServer exchangeserver -Body "Your account $username"
Monday, August 10, 2015
Powershell Script - Push-Patch - remote installation for KB hotfix, MSI, MSU, or EXE files
Here's a wickedly useful function I threw together based on a strong need to push emergency patches to thousands of servers without the standard route of using a patching deployment system like SCCM, WSUS, Altiris, or HPSA. All of these tools have their strengths and are generally considered superior to just manually pushing patches, but the sheer versatility of ease of being able to quickly deploy patches is what I required to make tight patching deadlines.
I recommend using this function with Get-HotFix, or Get-WmiObject -class Win32_product for a list of installed patches/software to determine if installation is needed and/or was successful. You can also come in after the push and look at the log files on the servers in an automated fashion to see how things went.
This script does require PSRemoting to be enabled on target servers, which is true by default on all Windows Server 2012 R2 installs, but needs to be turned on either by GPO or by hand on previous versions. There's a built in workaround if you pull down PSExec which will work on older servers without PSRemoting.
I recommend using this function with Get-HotFix, or Get-WmiObject -class Win32_product for a list of installed patches/software to determine if installation is needed and/or was successful. You can also come in after the push and look at the log files on the servers in an automated fashion to see how things went.
This script does require PSRemoting to be enabled on target servers, which is true by default on all Windows Server 2012 R2 installs, but needs to be turned on either by GPO or by hand on previous versions. There's a built in workaround if you pull down PSExec which will work on older servers without PSRemoting.
Tuesday, August 4, 2015
Powershell Script - Get-LocalUsers - Remotely query all local users and details
Here's a lovely script that runs with parallel pipeline queries using the [ADSI] .NET class to remotely query SAM to build nice object outputs which are great for security audits. Here's some sample output:
And the code:
PS C:\> 'Server1','Server2' | Get-LocalUsers Server : Server1 UserName : Administrator Active : True PasswordExpired : False PasswordAgeDays : 16 LastLogin : 6/3/2015 6:34:27 PM Groups : Administrators Description : Built-in account for administering the computer/domain Server : Server1 UserName : Guest Active : False PasswordExpired : False PasswordAgeDays : 0 LastLogin : Groups : Guests Description : Built-in account for guest access to the computer/domain Server : Server2 UserName : Administrator Active : True PasswordExpired : False PasswordAgeDays : 1 LastLogin : 3/5/2015 7:28:14 PM Groups : Administrators Description : Built-in account for administering the computer/domain Server : Server2 UserName : Guest Active : False PasswordExpired : False PasswordAgeDays : 0 LastLogin : Groups : Guests Description : Built-in account for guest access to the computer/domain PS C:\>
And the code:
Wednesday, July 22, 2015
Powershell Script - Get-ServerHDDinfo - Remotely pull hdd size info quickly
I wrote two versions of this function for different scenarios. The first version is much faster but requires PS remoting enabled on all target servers. the second version will work with any server with remote WMI enabled which means it will support older 2003 servers much more easily.
The core of the functions is the same: pull the Win32_Volume WMI class and build a report from hdd information. The first function remotely executes a script block which builds it's own report and then sends it back to the calling computer, while the second version calls each computer's WMI directly and builds one report for all servers. Even with Get-WmiObject's ability to query multiple servers at once, it still can't match the speed of Invoke-Command, which leverages the faster CIM framework.
Version 1 - Much faster Invoke-Command Version
Version 2 - Slower WMI
The core of the functions is the same: pull the Win32_Volume WMI class and build a report from hdd information. The first function remotely executes a script block which builds it's own report and then sends it back to the calling computer, while the second version calls each computer's WMI directly and builds one report for all servers. Even with Get-WmiObject's ability to query multiple servers at once, it still can't match the speed of Invoke-Command, which leverages the faster CIM framework.
Version 1 - Much faster Invoke-Command Version
Version 2 - Slower WMI
Tuesday, July 21, 2015
Beginner's Guide - Working with lists (part 4)
In this final installment of my 4 part series on working with lists, we'll cover advanced Select-Object statements, utilizing the power of computed expressions with custom names for each column (parameter).
In case you missed the first three parts, you can check them out here:
Part 1 - simple string arrays
Part 2 - object arrays
Part 3 - building custom object arrays
Custom Parameter Names
From Part 2 - Selecting, we covered how to return only a subset of columns (parameters) from our object arrays and we can even order the parameters so they appear the way we want them in a table or CSV file, etc. Custom parameter names takes this one step further and instead of just selecting which parameters we want to output, we also choose new names for them.
To do this, we use hashtables, one for each parameter. Within the hashtable, we specify two name-value pairs. The first one is "name" (or "n" is also acceptable) with the value equal to whatever name we want to give the new parameter. The second pair is "expression" (or "exp", or even just "e") with the value of the old parameter's name in the form of a powershell expression.
Below you see an example where we replace the default "dir" output with our own names. Don't be afraid of the dollar-underscore part of the statement, we'll cover this later; for now, just understand that it's a special variable that represents the current object (record) in the table. The select statement transforms the incoming data object by object and the $_ variable just means the current one it's processing.
As you can see the expression part of the hashtable has it's own curly brackets. You can use this template to help you create your own custom parameter names:
Rounding and changing units
Another very common use for using custom selects is to change data units and rounding fractions. Using another "dir" example, say we want to convert the units to GB or TB, as working with bytes can be annoying.
Computing new parameters
Perhaps the most powerful use of custom selects is to compute data from other parameters. When possible, using custom selects is the fastest way to transform and compute data from lists. It's much better than writing a whole script to create a new object array and build new objects with different parameters.
In case you missed the first three parts, you can check them out here:
Part 1 - simple string arrays
Part 2 - object arrays
Part 3 - building custom object arrays
Custom Parameter Names
From Part 2 - Selecting, we covered how to return only a subset of columns (parameters) from our object arrays and we can even order the parameters so they appear the way we want them in a table or CSV file, etc. Custom parameter names takes this one step further and instead of just selecting which parameters we want to output, we also choose new names for them.
To do this, we use hashtables, one for each parameter. Within the hashtable, we specify two name-value pairs. The first one is "name" (or "n" is also acceptable) with the value equal to whatever name we want to give the new parameter. The second pair is "expression" (or "exp", or even just "e") with the value of the old parameter's name in the form of a powershell expression.
Below you see an example where we replace the default "dir" output with our own names. Don't be afraid of the dollar-underscore part of the statement, we'll cover this later; for now, just understand that it's a special variable that represents the current object (record) in the table. The select statement transforms the incoming data object by object and the $_ variable just means the current one it's processing.
PS C:\> dir Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- d-r--- 7/19/2015 3:39 AM Program Files d-r--- 7/21/2015 12:49 PM Program Files (x86) d-r--- 7/19/2015 3:39 AM Users d----- 7/19/2015 3:40 AM Windows -a---- 7/2/2015 7:39 PM 2544 myserverstatus.csv -a---- 7/2/2015 7:39 PM 2544 myserverstatus.xml PS C:\> dir |select Name Name ---- Program Files Program Files (x86) Users Windows myserverstatus.csv myserverstatus.xml PS C:\> dir | select @{n="Nombre"; e={$_.name}} Nombre ------ Program Files Program Files (x86) Users Windows myserverstatus.csv myserverstatus.xml PS C:\> dir | select @{n="Nombre"; e={$_.name}}, @{n="Type"; e={$_.mode}} Nombre Type ------ ---- Program Files d-r--- Program Files (x86) d-r--- Users d-r--- Windows d----- myserverstatus.csv -a---- myserverstatus.xml -a---- PS C:\> dir | select @{n="Nombre"; e={$_.name}}, @{n="Type"; e={$_.mode}}, @{n="File Write Time"; e={$_.LastWriteTime}} Nombre Type File Write Time ------ ---- --------------- AMD d----- 7/19/2015 3:38:16 AM EFI d----- 5/23/2015 4:52:28 AM Games d----- 6/20/2015 8:13:11 PM HyperV d----- 7/7/2015 3:58:26 PM Intel d----- 6/24/2015 6:59:47 PM MSI d----- 6/24/2015 6:50:19 PM PerfLogs d----- 7/10/2015 4:04:22 AM Program Files d-r--- 7/19/2015 3:39:38 AM Program Files (x86) d-r--- 7/21/2015 12:49:26 PM Users d-r--- 7/19/2015 3:39:41 AM Windows d----- 7/19/2015 3:40:35 AM Windows.old d----- 7/19/2015 4:36:26 AM myserverstatus.csv -a---- 7/2/2015 7:39:18 PM myserverstatus.xml -a---- 7/2/2015 7:39:27 PM PS C:\>
As you can see the expression part of the hashtable has it's own curly brackets. You can use this template to help you create your own custom parameter names:
$datatable | select @{n="New Parameter Name 1"; e={$_.OldName1}}, @{n="New Parameter Name 2"; e={$_.OldName2}}, @{n="New Parameter Name X"; e={$_.OldNameX}}
Rounding and changing units
Another very common use for using custom selects is to change data units and rounding fractions. Using another "dir" example, say we want to convert the units to GB or TB, as working with bytes can be annoying.
PS C:\> dir C:\HyperV\VHD\ | select Name, Length Name Length ---- ------ BVadm1.vhdx 11446255616 BVDC1.vhdx 10305404928 BVServer10.vhdx 9030336512 BVSMA1.vhdx 9936306176 BVSQL1.vhdx 10741612544 BVSQL1_DATA.vhdx 1077936128 DBadm2.vhdx 18291359744 Dev10.vhdx 18996002816 PS C:\> dir C:\HyperV\VHD\ | select Name, @{n="Size (GB)"; e={$_.length / 1GB}} Name Size (GB) ---- --------- BVadm1.vhdx 10.66015625 BVDC1.vhdx 9.59765625 BVServer10.vhdx 8.41015625 BVSMA1.vhdx 9.25390625 BVSQL1.vhdx 10.00390625 BVSQL1_DATA.vhdx 1.00390625 DBadm2.vhdx 17.03515625 Dev10.vhdx 17.69140625 PS C:\> dir C:\HyperV\VHD\ | select Name, @{n="Size (GB)"; e={[math]::round($_.length / 1GB,2)}} Name Size (GB) ---- --------- BVadm1.vhdx 10.66 BVDC1.vhdx 9.6 BVServer10.vhdx 8.41 BVSMA1.vhdx 9.25 BVSQL1.vhdx 10 BVSQL1_DATA.vhdx 1 DBadm2.vhdx 17.04 Dev10.vhdx 17.69 PS C:\>Notice how I can mix and match normal parameters with custom parameters, and that they appear in the order I list them in the select statement.
Computing new parameters
Perhaps the most powerful use of custom selects is to compute data from other parameters. When possible, using custom selects is the fastest way to transform and compute data from lists. It's much better than writing a whole script to create a new object array and build new objects with different parameters.
PS C:\> dir | select Name, >>> @{n="Directory"; e={$_.mode -like "d*"}}, >>> @{n="Name Length"; e={$_.name.length}}, >>> @{n="Days Old"; e={((get-date) - $_.creationTime).days}} Name Directory Name Length Days Old ---- --------- ----------- -------- AMD True 3 31 HyperV True 6 31 Intel True 5 27 Program Files True 13 11 Program Files (x86) True 19 11 Users True 5 11 Windows True 7 11 Windows.old True 11 2 myserverstatus.csv False 18 19 myserverstatus.xml False 18 19 PS C:\> gwmi win32_logicaldisk | select @{n="Letter"; e={$_.deviceid[0]}}, >>> @{n="Used Space (TB)"; e={[math]::round(($_.size - $_.freespace)/1TB,3)}}, >>> @{n="Percent Free"; e={[math]::round(100 * $_.freespace / $_.size,0)}} Letter Used Space (TB) Percent Free ------ --------------- ------------ C 0.022 82 D 0.017 86 I 2.202 27 M 3.917 22 PS C:\>As you can see, using custom selects with object arrays is a great way to extract exactly what you need from source data without doing too much work. Also note that you can put each parameter definition on a new line as long as you end the current line with a comma as I did here.
Sunday, July 12, 2015
My son was born!
Apologizes for not posting any new content, I've been busy with my second child. I'll post part 4 of my beginners series on lists within a few days.
Tuesday, July 7, 2015
Beginner's Guide - Working with lists (part 3)
This is the third installment of my beginner's guide to working with lists. Part 1 covered simple string arrays and Part 2 dived into object arrays and working with them. We'll cover building custom object arrays from scratch in this section.
Making Custom Objects
Since we already know a bit about working with arrays in general and we know that object arrays are just ordered collections of objects, we are going to spend most of this covering building custom objects. There are two ways to make a custom object.
The first way is to define an empty object and then add parameters, along with data types and data, one at a time. This way is more common because order of the parameters is preserved so when you output your object arrays, they columns (parameters) are in the in the order you'd like and you don't have to explicitly name an order of your preference.
The second way is quicker, as you add all of the parameters, data types, and data at once, but you lose the order in which your parameters are added. This isn't a problem if you explicitly name your output order with either a select or format-table statement, but you just have to remember to do that.
Lets define some objects using the first way and then add them together into an array.
The second way: Hashtable
As I briefly covered in part 1, hashtables are collections of unordered name & value pairs. The second way to define a custom object is to pass in a parameter list in the form of a hashtable.
In part 4, we'll go into more advanced ways of selecting parts of existing object arrays to make new ones using custom expressions.
Making Custom Objects
Since we already know a bit about working with arrays in general and we know that object arrays are just ordered collections of objects, we are going to spend most of this covering building custom objects. There are two ways to make a custom object.
The first way is to define an empty object and then add parameters, along with data types and data, one at a time. This way is more common because order of the parameters is preserved so when you output your object arrays, they columns (parameters) are in the in the order you'd like and you don't have to explicitly name an order of your preference.
The second way is quicker, as you add all of the parameters, data types, and data at once, but you lose the order in which your parameters are added. This isn't a problem if you explicitly name your output order with either a select or format-table statement, but you just have to remember to do that.
Lets define some objects using the first way and then add them together into an array.
PS C:\> $person1 = New-Object psobject PS C:\> Write-Output $person1 PS C:\> $person1 | Add-Member -Type NoteProperty -Name "First" -Value "Bob" PS C:\> $person1 | Add-Member -Type NoteProperty -Name "Last" -Value "Smith" PS C:\> $person1 | Add-Member -Type NoteProperty -Name "Age" -Value 40 PS C:\> $person1 | Add-Member -Type NoteProperty -Name "isAlive" -Value $true PS C:\> $person1 | Add-Member -Type NoteProperty -Name "Birthdate" -Value ([datetime]"4/2/75") PS C:\> Write-Output $person1 First : Bob Last : Smith Age : 40 isAlive : True Birthdate : 4/2/1975 12:00:00 AMLet's make another person object.
PS C:\> $person2 = New-Object psobject PS C:\> $person2 | Add-Member -Type NoteProperty -Name "First" -Value "Mary" PS C:\> $person2 | Add-Member -Type NoteProperty -Name "Last" -Value "Jones" PS C:\> $person2 | Add-Member -Type NoteProperty -Name "Age" -Value 45 PS C:\> $person2 | Add-Member -Type NoteProperty -Name "isAlive" -Value $true PS C:\> $person2 | Add-Member -Type NoteProperty -Name "Birthdate" -Value ([datetime]"2/21/70") PS C:\> Write-Output $person2 First : Mary Last : Jones Age : 45 isAlive : True Birthdate : 2/21/1970 12:00:00 AMLets now combine the two objects into a new object array. As we saw in part 1, the notation of using @() will define an empty array.
PS C:\> $people = @($person1,$person2) PS C:\> $people.GetType() #We created a new object array with two records IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Object[] System.Array PS C:\> $people.Count 2 PS C:\> $people | Format-Table -AutoSize First Last Age isAlive Birthdate ----- ---- --- ------- --------- Bob Smith 40 True 4/2/1975 12:00:00 AM Mary Jones 45 True 2/21/1970 12:00:00 AM
The second way: Hashtable
As I briefly covered in part 1, hashtables are collections of unordered name & value pairs. The second way to define a custom object is to pass in a parameter list in the form of a hashtable.
PS C:\> $person3 = New-Object psobject -Property @{"First"="Sandra"; "Last"="Johnson"; "Age"=85; "isAlive"=$false; "Birthdate"=([datetime]"7/1/30")} PS C:\> Write-Output $person3 Birthdate : 7/1/1930 12:00:00 AM Last : Johnson First : Sandra Age : 85 isAlive : FalseAs you can see, I've defined the hashtable as part of the parameter for new-object. Lets create another person.
PS C:\> $HT = @{"First"="James"; "Last"="Brown"; "Age"=95; "isAlive"=$false; "Birthdate"=([datetime]"3/19/20")} PS C:\> $person4 = New-Object psobject -Property $HT PS C:\> Write-Output $person4 isAlive : False First : James Age : 95 Birthdate : 3/19/2020 12:00:00 AM Last : BrownAgain, it's random as to how parameters are added. Now lets combine them along with our first list.
PS C:\> $morepeople = @() #Since the first object I'm combining is already an object array, I could have skipped this line. PS C:\> $morepeople += $people #adds the two records from the first object array PS C:\> $morepeople += $person3 #the plus-equal operator appends the object array with another record PS C:\> $morepeople += $person4 PS C:\> $morepeople | Format-Table First,Last,Age,isAlive,Birthdate -AutoSize #Here is where I explicitly define the order. First Last Age isAlive Birthdate ----- ---- --- ------- --------- Bob Smith 40 True 4/2/1975 12:00:00 AM Mary Jones 45 True 2/21/1970 12:00:00 AM Sandra Johnson 85 False 7/1/1930 12:00:00 AM James Brown 95 False 3/19/2020 12:00:00 AM
In part 4, we'll go into more advanced ways of selecting parts of existing object arrays to make new ones using custom expressions.
Thursday, July 2, 2015
Beginner's Guide - Working with lists (part 2)
In part 1 of this series, we covered string arrays and briefly touched on hash tables. In this tutorial we'll cover how to use and define custom lists (object arrays). All data flows in and out of cmdlets are objects. Even plain numbers or strings are technically objects. And most of the time, you'll be working with more than one of these objects in the form of an array of objects, which is why this topic is pretty crucial for understanding and working with Powershell effectively.
As we covered in part 1, if you think of an excel spreadsheet as a table of data or in Powershell as an array of objects. Each column has a header which is known as Properties and a data type, while rows or records are known as Objects (or PSCustomObject). Here are some examples of Object arrays.
With Get-Process, the process objects themselves have many properties by default. One way to make a custom object array is to select a subset from a larger object array. By far, this is likely what you'll be doing in most of your scripting, so I'm going to break it down for you.
Sorting
As with simple string arrays, you can sort any custom object array in ascending or descending order, however with object arrays, you must specify which property you wish to sort on.
Selecting
By far one of the most useful object array modifiers and you'll send up using it the most of these. Selecting is a way to remove certain properties from an array, which allows you to do away with the extra columns of data you don't need. Select is easy to use, just pipe in your object array and list which properties you want output. We briefly touched on special parameters like -First <#>, -Skip <#>, and -Last <#> which actually work on a record level, not the properties.
Where Clause
If you are familiar with SQL, you'll recognize the Where-Object (aka "where" or "?" aliases) as a way to exclude certain records given a conditional check to each record. If a True is produced, the record is included, if a False is calculated, the record is excluded. The parameter for Where-Object is actually a script block which is ran on each record.
Grouping
Grouping actually creates a nested object array (and object array inside of an object array) and is a bit more advanced and less used, so I'm only going to mention it for now. Grouping finds the matching records by the property specified and colapses the records into a new property called Group which is a nested array. Count is also generated to show how many objects are inside of the group property.
Converting
Once you have your array, you might need to transform it into a different format for reporting or saving it.
Converting examples
PS C:\> $serverstatus | ConvertTo-Json #The JSON format is widely used in web API and stores nested object arrays.
[
{
"ServerName": "server1",
"TimeStamp": "2015-06-26T09:35:51",
"RDPPort": "3389",
"Results": "Up"
},
{
"ServerName": "server2",
"TimeStamp": "2015-06-26T09:35:52",
"RDPPort": "3389",
"Results": "Up"
},
{
"ServerName": "server3",
"TimeStamp": "2015-06-26T09:35:53",
"RDPPort": "3389",
"Results": "Up"
},
{
"ServerName": "server4",
"TimeStamp": "2015-06-26T09:35:54",
"RDPPort": "3389",
"Results": "Up"
},
{
"ServerName": "server5",
"TimeStamp": "2015-06-26T09:35:55",
"RDPPort": "3389",
"Results": "Up"
}
]
Exporting
Exporting is basically the same thing as ConvertTo-____ + save it to a file. So Export-CSV .\filename.csv is the same thing as ConvertTo-CSV | Set-Content .\filename.csv
In Part 3, we'll cover building custom objects from scratch and touch more on hash tables.
As we covered in part 1, if you think of an excel spreadsheet as a table of data or in Powershell as an array of objects. Each column has a header which is known as Properties and a data type, while rows or records are known as Objects (or PSCustomObject). Here are some examples of Object arrays.
PS C:\> $serverstatus = import-csv C:\ServerStatus.csv PS C:\> $serverstatus | Format-Table -AutoSize ServerName TimeStamp RDPPort Results ---------- --------- ------- ------- server1 2015-06-26T09:35:51 3389 Up server2 2015-06-26T09:35:52 3389 Up server3 2015-06-26T09:35:53 3389 Up server4 2015-06-26T09:35:54 3389 Up server5 2015-06-26T09:35:55 3389 Up PS C:\> $serverstatus.GetType() #[] indicates array, so this is a data type of Object Array IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Object[] System.Array PS C:\> $serverstatus[0] #from part 1 - indexing the array, 0 = the first record ServerName TimeStamp RDPPort Results ---------- --------- ------- ------- server1 2015-06-26T09:35:51 3389 Up PS C:\> $serverstatus[0].ServerName.GetType() #The data type of the property server name is of type String IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True String System.Object PS C:\> $serverstatus[0].TimeStamp # referencing the 2015-06-26T09:35:51 PS C:\> $cdrive = Get-ChildItem #dir is the alias for Get-ChildItem. PS C:\> $cdrive | Format-Table -AutoSize Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 6/30/2015 5:01 PM HyperV d----- 5/23/2015 6:06 AM PerfLogs d-r--- 7/2/2015 5:23 PM Program Files d-r--- 7/1/2015 6:54 PM Program Files (x86) d-r--- 6/20/2015 7:05 PM Users d----- 6/27/2015 8:20 AM Windows PS C:\> $cdrive.GetType() #again, we can see it's an Object Array IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Object[] System.Array PS C:\> $cdrive | Get-Member #By default, get-member is only looking at the first object in the array TypeName: System.IO.DirectoryInfo Name MemberType Definition ---- ---------- ---------- Mode CodeProperty System.String Mode{get=Mode;} Create Method void Create(), void Create(System.Security.AccessControl.DirectorySecurity ... CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType) CreateSubdirectory Method System.IO.DirectoryInfo CreateSubdirectory(string path), System.IO.Director... Delete Method void Delete(), void Delete(bool recursive) EnumerateDirectories Method System.Collections.Generic.IEnumerable[System.IO.DirectoryInfo] EnumerateDi... EnumerateFiles Method System.Collections.Generic.IEnumerable[System.IO.FileInfo] EnumerateFiles()... EnumerateFileSystemInfos Method System.Collections.Generic.IEnumerable[System.IO.FileSystemInfo] EnumerateF... Equals Method bool Equals(System.Object obj) GetAccessControl Method System.Security.AccessControl.DirectorySecurity GetAccessControl(), System.... GetDirectories Method System.IO.DirectoryInfo[] GetDirectories(), System.IO.DirectoryInfo[] GetDi... GetFiles Method System.IO.FileInfo[] GetFiles(string searchPattern), System.IO.FileInfo[] G... GetFileSystemInfos Method System.IO.FileSystemInfo[] GetFileSystemInfos(string searchPattern), System... GetHashCode Method int GetHashCode() GetLifetimeService Method System.Object GetLifetimeService() GetObjectData Method void GetObjectData(System.Runtime.Serialization.SerializationInfo info, Sys... GetType Method type GetType() InitializeLifetimeService Method System.Object InitializeLifetimeService() MoveTo Method void MoveTo(string destDirName) Refresh Method void Refresh() SetAccessControl Method void SetAccessControl(System.Security.AccessControl.DirectorySecurity direc... ToString Method string ToString() PSChildName NoteProperty string PSChildName=AMD PSDrive NoteProperty PSDriveInfo PSDrive=C PSIsContainer NoteProperty bool PSIsContainer=True PSParentPath NoteProperty string PSParentPath=Microsoft.PowerShell.Core\FileSystem::C:\ PSPath NoteProperty string PSPath=Microsoft.PowerShell.Core\FileSystem::C:\AMD PSProvider NoteProperty ProviderInfo PSProvider=Microsoft.PowerShell.Core\FileSystem Attributes Property System.IO.FileAttributes Attributes {get;set;} CreationTime Property datetime CreationTime {get;set;} CreationTimeUtc Property datetime CreationTimeUtc {get;set;} Exists Property bool Exists {get;} Extension Property string Extension {get;} FullName Property string FullName {get;} LastAccessTime Property datetime LastAccessTime {get;set;} LastAccessTimeUtc Property datetime LastAccessTimeUtc {get;set;} LastWriteTime Property datetime LastWriteTime {get;set;} LastWriteTimeUtc Property datetime LastWriteTimeUtc {get;set;} Name Property string Name {get;} Parent Property System.IO.DirectoryInfo Parent {get;} Root Property System.IO.DirectoryInfo Root {get;} BaseName ScriptProperty System.Object BaseName {get=$this.Name;} LinkType ScriptProperty System.Object LinkType {get=[Microsoft.PowerShell.Commands.InternalSymbolic... Target ScriptProperty System.Object Target {get=[Microsoft.PowerShell.Commands.InternalSymbolicLi... PS C:\> $processes = Get-Process #Gets the current active processes PS C:\> $processes | Format-Table -AutoSize #there are many more properties per object, but only these 8 are being shown Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 480 29 69460 37712 2097402 2.41 6236 ApplicationFrameHost 275 34 90344 71268 396 275.47 3524 chrome 206 21 28128 13200 198 1.48 3600 chrome 206 20 27776 15156 196 1.61 3740 chrome 208 21 29668 12836 198 1.45 3744 chrome 4301 146 232976 253580 1291 16,956.36 5072 chrome 94 9 4424 3572 2097235 1.45 3108 conhost 114 11 10908 10148 2097277 7.80 6264 conhost 114 10 4844 11348 2097246 2.09 9760 conhost 354 14 1308 1512 2097204 2.98 540 csrss 705 49 13956 6252 2097341 122.59 692 csrss 238 12 2784 2460 2097253 0.39 5636 dllhost 1064 65 203812 45616 2097693 617.16 1204 dwm 2791 211 158364 125988 2098011 577.27 3948 explorer 37 7 924 1300 2097209 0.17 6824 fontdrvhost 0 0 0 4 0 0 Idle 234 15 2800 3032 69 0.16 8136 LMS 1759 30 8056 13904 2097205 65.52 824 lsass 1603 68 95844 43124 2098071 2,758.66 8128 mmc 353 35 26680 7628 592 2.41 6984 MOM 596 67 123360 80576 2097484 1,872.06 2420 MsMpEng 962 34 41648 22180 2097413 477.55 716 mstsc 269 14 9744 2324 2097206 200.13 3000 NisSrv 996 67 130260 116112 2097932 12.22 3628 powershell 1063 82 421540 449268 2098281 62.19 11572 powershell 1173 47 37088 52140 2097484 59.64 4048 RuntimeBroker 112 8 1172 5684 2097186 0.00 10644 SearchFilterHost 813 75 43600 44424 2097584 94.95 4148 SearchIndexer 735 25 6720 20576 2097495 0.11 12072 SearchProtocolHost 966 82 94528 103820 33439 7.97 4460 SearchUI 291 12 4316 4884 2097189 22.48 816 services 864 43 88108 55028 405 3,876.80 3572 ShellExperienceHost 408 18 5588 14368 2097295 7.41 3612 sihost 49 3 336 316 2097156 0.08 344 smss 176 13 3288 3124 2097252 1.23 7880 splwow64 466 31 8920 8808 2097258 22.00 1524 spoolsv 915 53 9596 13912 2098446 12.58 436 svchost 2388 75 28756 49576 2097395 2,164.47 812 svchost 644 22 6808 11160 2097222 12.31 932 svchost 703 23 6044 8404 2097238 60.58 988 svchost 937 31 13752 16112 2097246 2,608.06 1064 svchost 709 38 6976 10896 2097248 7.03 1076 svchost 593 31 7184 9836 2101998 231.47 1304 svchost 220 17 2256 3872 2097197 3.28 1316 svchost 516 44 18376 20044 2097281 37.47 1356 svchost 469 25 19024 25668 2097318 71.41 2284 svchost 260 17 6612 11388 2097862 26.20 2368 svchost 201 14 3328 2440 2097239 0.03 3620 svchost 1647 0 448 63616 129 2,523.84 4 System 343 30 6412 9044 2097515 2.13 3696 taskhostw 413 27 16992 25596 2097361 1,212.61 2476 Taskmgr 145 12 1904 3360 2097210 2.38 7740 unsecapp 568 26 16380 18020 2097282 574.36 2376 vmms 507 27 41316 9784 2097276 1,244.92 2244 vmwp 84 8 904 1032 2097194 0.05 684 wininit 213 10 1864 3444 2097213 0.47 772 winlogon 356 18 9144 12744 2097240 2,362.16 2732 WmiPrvSE 143 10 2480 5460 2097189 0.41 5716 WmiPrvSE 480 23 7232 18772 2097297 0.95 4156 WSHost PS C:\> $processes.GetType() #Again, you can see it's an Object Array IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Object[] System.Array PS C:\> $processes[0] | Format-List * #Now we get to see the full list of properties per object, similar to Get-Member __NounName : Process Name : ApplicationFrameHost Handles : 480 VM : 2199285780480 WS : 38617088 PM : 71127040 NPM : 29232 Path : C:\Windows\system32\ApplicationFrameHost.exe Company : Microsoft Corporation CPU : 2.40625 FileVersion : 10.0.10130.0 (fbl_impressive.150522-2224) ProductVersion : 10.0.10130.0 Description : Application Frame Host Product : Microsoft® Windows® Operating System Id : 6236 PriorityClass : Normal HandleCount : 480 WorkingSet : 38617088 PagedMemorySize : 71127040 PrivateMemorySize : 71127040 VirtualMemorySize : 262524928 TotalProcessorTime : 00:00:02.4062500 BasePriority : 8 ExitCode : HasExited : False ExitTime : Handle : 5628 SafeHandle : Microsoft.Win32.SafeHandles.SafeProcessHandle MachineName : . MainWindowHandle : 266852 MainWindowTitle : Calculator MainModule : System.Diagnostics.ProcessModule (ApplicationFrameHost.exe) MaxWorkingSet : 1413120 MinWorkingSet : 204800 Modules : {System.Diagnostics.ProcessModule (ApplicationFrameHost.exe), System.Diagnostics.ProcessModule (ntdll.dll), System.Diagnostics.ProcessModule (KERNEL32.DLL), System.Diagnostics.ProcessModule (KERNELBASE.dll)...} NonpagedSystemMemorySize : 29232 NonpagedSystemMemorySize64 : 29232 PagedMemorySize64 : 71127040 PagedSystemMemorySize : 456312 PagedSystemMemorySize64 : 456312 PeakPagedMemorySize : 72245248 PeakPagedMemorySize64 : 72245248 PeakWorkingSet : 66281472 PeakWorkingSet64 : 66281472 PeakVirtualMemorySize : 266883072 PeakVirtualMemorySize64 : 2199290138624 PriorityBoostEnabled : True PrivateMemorySize64 : 71127040 PrivilegedProcessorTime : 00:00:01.7500000 ProcessName : ApplicationFrameHost ProcessorAffinity : 15 Responding : True SessionId : 1 StartInfo : System.Diagnostics.ProcessStartInfo StartTime : 6/30/2015 3:54:14 PM SynchronizingObject : Threads : {7732, 6172, 3132, 7996...} UserProcessorTime : 00:00:00.6562500 VirtualMemorySize64 : 2199285780480 EnableRaisingEvents : False StandardInput : StandardOutput : StandardError : WorkingSet64 : 38617088 Site : Container : PS C:\> $processes[0].PrivilegedProcessorTime.GetType() #the property here is of data type TimeSpan. IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True TimeSpan System.ValueType
With Get-Process, the process objects themselves have many properties by default. One way to make a custom object array is to select a subset from a larger object array. By far, this is likely what you'll be doing in most of your scripting, so I'm going to break it down for you.
Sorting
As with simple string arrays, you can sort any custom object array in ascending or descending order, however with object arrays, you must specify which property you wish to sort on.
PS C:\> $cdrive | sort LastWriteTime #sort by the the last time the directory was written to Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 5/23/2015 6:06 AM PerfLogs d-r--- 6/20/2015 7:05 PM Users d----- 6/27/2015 8:20 AM Windows d----- 6/30/2015 5:01 PM HyperV d-r--- 7/1/2015 6:54 PM Program Files (x86) d-r--- 7/2/2015 5:23 PM Program Files PS C:\> $serverstatus | sort Servername -Descending #sort in reverse order the servername property ServerName TimeStamp RDPPort Results ---------- --------- ------- ------- server5 2015-06-26T09:35:51 3389 Up server4 2015-06-26T09:35:55 3389 Up server3 2015-06-26T09:35:54 3389 Up server2 2015-06-26T09:35:53 3389 Up server1 2015-06-26T09:35:52 3389 Up
Selecting
By far one of the most useful object array modifiers and you'll send up using it the most of these. Selecting is a way to remove certain properties from an array, which allows you to do away with the extra columns of data you don't need. Select is easy to use, just pipe in your object array and list which properties you want output. We briefly touched on special parameters like -First <#>, -Skip <#>, and -Last <#> which actually work on a record level, not the properties.
PS C:\> $cdrive | select FullName,Attributes,CreationTime,LastAccessTime -Last 3 #Only returns the last 3 entries of the array FullName Attributes CreationTime LastAccessTime -------- ---------- ------------ -------------- C:\Program Files (x86) ReadOnly, Directory, Compressed 5/23/2015 4:52:28 AM 7/1/2015 6:54:47 PM C:\Users ReadOnly, Directory, Compressed 5/23/2015 4:52:28 AM 6/20/2015 7:05:34 PM C:\Windows Directory, Compressed 5/23/2015 4:52:28 AM 6/27/2015 8:20:52 AM PS C:\> $processes | Select Name,VirtualMemorySize64,BasePriority,Company -First 4 -Skip 1 Name VirtualMemorySize64 BasePriority Company ---- ------------------- ------------ ------- chrome 1354072064 8 Google Inc. chrome 470994944 8 Google Inc. chrome 319963136 8 Google Inc. chrome 474648576 8 Google Inc. PS C:\> $processes | select * -First 1 #Select * is the wildcard for all properties. This is the same as $processes[0] | Select * __NounName : Process Name : ApplicationFrameHost Handles : 480 VM : 2199285780480 WS : 38617088 PM : 71127040 NPM : 29232 Path : C:\Windows\system32\ApplicationFrameHost.exe Company : Microsoft Corporation CPU : 2.40625 FileVersion : 10.0.10130.0 (fbl_impressive.150522-2224) ProductVersion : 10.0.10130.0 Description : Application Frame Host Product : Microsoft® Windows® Operating System Id : 6236 PriorityClass : Normal HandleCount : 480 WorkingSet : 38617088 PagedMemorySize : 71127040 PrivateMemorySize : 71127040 VirtualMemorySize : 262524928 TotalProcessorTime : 00:00:02.4062500 BasePriority : 8 ExitCode : HasExited : False ExitTime : Handle : 5628 SafeHandle : Microsoft.Win32.SafeHandles.SafeProcessHandle MachineName : . MainWindowHandle : 266852 MainWindowTitle : Calculator MainModule : System.Diagnostics.ProcessModule (ApplicationFrameHost.exe) MaxWorkingSet : 1413120 MinWorkingSet : 204800 Modules : {System.Diagnostics.ProcessModule (ApplicationFrameHost.exe), System.Diagnostics.ProcessModule (ntdll.dll), System.Diagnostics.ProcessModule (KERNEL32.DLL), System.Diagnostics.ProcessModule (KERNELBASE.dll)...} NonpagedSystemMemorySize : 29232 NonpagedSystemMemorySize64 : 29232 PagedMemorySize64 : 71127040 PagedSystemMemorySize : 456312 PagedSystemMemorySize64 : 456312 PeakPagedMemorySize : 72245248 PeakPagedMemorySize64 : 72245248 PeakWorkingSet : 66281472 PeakWorkingSet64 : 66281472 PeakVirtualMemorySize : 266883072 PeakVirtualMemorySize64 : 2199290138624 PriorityBoostEnabled : True PrivateMemorySize64 : 71127040 PrivilegedProcessorTime : 00:00:01.7500000 ProcessName : ApplicationFrameHost ProcessorAffinity : 15 Responding : True SessionId : 1 StartInfo : System.Diagnostics.ProcessStartInfo StartTime : 6/30/2015 3:54:14 PM SynchronizingObject : Threads : {7732, 6172, 3132, 7996...} UserProcessorTime : 00:00:00.6562500 VirtualMemorySize64 : 2199285780480 EnableRaisingEvents : False StandardInput : StandardOutput : StandardError : WorkingSet64 : 38617088 Site : Container :
Where Clause
If you are familiar with SQL, you'll recognize the Where-Object (aka "where" or "?" aliases) as a way to exclude certain records given a conditional check to each record. If a True is produced, the record is included, if a False is calculated, the record is excluded. The parameter for Where-Object is actually a script block which is ran on each record.
PS C:\> $cdrive | Where-Object{$_.mode -like "*r*"} #returns only records that have the read only attribute set on them (R from mode property). Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- d-r--- 7/2/2015 5:23 PM Program Files d-r--- 7/1/2015 6:54 PM Program Files (x86) d-r--- 6/20/2015 7:05 PM Users PS C:\> $processes | Where-Object {$_.company -notlike "*microsoft*"} #returns only records of processes that aren't labeled as Microsoft Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 4301 146 232976 253580 1291 ...35.02 5072 chrome 255 38 171652 146496 449 1,789.36 8516 chrome 253 29 60472 42340 305 239.45 8524 chrome 285 40 119432 114596 453 1,657.22 8872 chrome 404 72 284048 523196 1041 ...49.17 8976 chrome 322 40 91900 101556 349 1,574.55 9076 chrome 289 37 100332 112892 342 74.33 9628 chrome 248 30 91844 69756 277 305.22 10400 chrome 302 37 113524 85084 433 466.67 11176 chrome 292 40 132888 138396 334 55.25 11636 chrome 354 14 1308 1512 ...04 2.98 540 csrss 705 49 13956 6252 ...41 126.80 692 csrss 0 0 0 4 0 0 Idle 234 15 2800 3032 69 0.16 8136 LMS 353 35 26680 7628 592 2.44 6984 MOM 596 67 123360 80576 ...84 1,876.20 2420 MsMpEng 269 14 9744 2324 ...06 200.53 3000 NisSrv 112 8 1172 5684 ...86 10644 SearchFilterHost 735 25 6720 20576 ...95 12072 SearchProtocolHost 966 82 94528 103820 33439 7.97 4460 SearchUI 291 12 4316 4884 ...89 22.70 816 services 49 3 336 316 ...56 0.08 344 smss 1647 0 448 63616 129 2,550.19 4 System 84 8 904 1032 ...94 0.05 684 wininit
Grouping
Grouping actually creates a nested object array (and object array inside of an object array) and is a bit more advanced and less used, so I'm only going to mention it for now. Grouping finds the matching records by the property specified and colapses the records into a new property called Group which is a nested array. Count is also generated to show how many objects are inside of the group property.
PS C:\> $serverstatus | group results #since all of the results were up, there's only one entry in this group output. Count Name Group ----- ---- ----- 5 Up {@{ServerName=server1; TimeStamp=2015-06-26T09:35:51; RDPPort=3389; Results=Up}, @{Serve... PS C:\> $processes | group ProcessName Count Name Group ----- ---- ----- 1 ApplicationFrameHost {System.Diagnostics.Process (ApplicationFrameHost)} 9 chrome {System.Diagnostics.Process (chrome), System.Diagnostics.Process (chrome), System.Di... 3 conhost {System.Diagnostics.Process (conhost), System.Diagnostics.Process (conhost), System.... 2 csrss {System.Diagnostics.Process (csrss), System.Diagnostics.Process (csrss)} 1 dllhost {System.Diagnostics.Process (dllhost)} 1 dwm {System.Diagnostics.Process (dwm)} 1 explorer {System.Diagnostics.Process (explorer)} 1 fontdrvhost {System.Diagnostics.Process (fontdrvhost)} 1 Idle {System.Diagnostics.Process (Idle)} 1 LMS {System.Diagnostics.Process (LMS)} 1 lsass {System.Diagnostics.Process (lsass)} 1 mmc {System.Diagnostics.Process (mmc)} 1 MOM {System.Diagnostics.Process (MOM)} 1 MsMpEng {System.Diagnostics.Process (MsMpEng)} 1 mstsc {System.Diagnostics.Process (mstsc)} 1 NisSrv {System.Diagnostics.Process (NisSrv)} 2 powershell {System.Diagnostics.Process (powershell), System.Diagnostics.Process (powershell)} 1 RuntimeBroker {System.Diagnostics.Process (RuntimeBroker)} 1 SearchFilterHost {System.Diagnostics.Process (SearchFilterHost)} 1 SearchIndexer {System.Diagnostics.Process (SearchIndexer)} 1 SearchProtocolHost {System.Diagnostics.Process (SearchProtocolHost)} 1 SearchUI {System.Diagnostics.Process (SearchUI)} 1 services {System.Diagnostics.Process (services)} 1 ShellExperienceHost {System.Diagnostics.Process (ShellExperienceHost)} 1 sihost {System.Diagnostics.Process (sihost)} 1 smss {System.Diagnostics.Process (smss)} 1 splwow64 {System.Diagnostics.Process (splwow64)} 1 spoolsv {System.Diagnostics.Process (spoolsv)} 12 svchost {System.Diagnostics.Process (svchost), System.Diagnostics.Process (svchost), System.... 1 System {System.Diagnostics.Process (System)} 1 taskhostw {System.Diagnostics.Process (taskhostw)} 1 Taskmgr {System.Diagnostics.Process (Taskmgr)} 1 unsecapp {System.Diagnostics.Process (unsecapp)} 1 vmms {System.Diagnostics.Process (vmms)} 1 vmwp {System.Diagnostics.Process (vmwp)} 1 wininit {System.Diagnostics.Process (wininit)} 1 winlogon {System.Diagnostics.Process (winlogon)} 2 WmiPrvSE {System.Diagnostics.Process (WmiPrvSE), System.Diagnostics.Process (WmiPrvSE)} 1 WSHost {System.Diagnostics.Process (WSHost)}
Converting
Once you have your array, you might need to transform it into a different format for reporting or saving it.
Converting examples
PS C:\> $serverstatus | ConvertTo-Csv -NoTypeInformation #converts the object array into a Comma Separated Value (CSV) format "ServerName","TimeStamp","RDPPort","Results" "server1","2015-06-26T09:35:51","3389","Up" "server2","2015-06-26T09:35:52","3389","Up" "server3","2015-06-26T09:35:53","3389","Up" "server4","2015-06-26T09:35:54","3389","Up" "server5","2015-06-26T09:35:55","3389","Up" PS C:\> $serverstatus | ConvertTo-Html -Title "Server Status" #HTML - web pagesServer Status
ServerName | TimeStamp | RDPPort | Results |
---|---|---|---|
server1 | 2015-06-26T09:35:51 | 3389 | Up |
server2 | 2015-06-26T09:35:52 | 3389 | Up |
server3 | 2015-06-26T09:35:53 | 3389 | Up |
server4 | 2015-06-26T09:35:54 | 3389 | Up |
server5 | 2015-06-26T09:35:55 | 3389 | Up |
Exporting
Exporting is basically the same thing as ConvertTo-____ + save it to a file. So Export-CSV .\filename.csv is the same thing as ConvertTo-CSV | Set-Content .\filename.csv
PS C:\> $serverstatus | Export-Csv myserverstatus.csv -Verbose #You'll use CSV's a lot as they open up easily in excel VERBOSE: Performing the operation "Export-Csv" on target "myserverstatus.csv". PS C:\> $serverstatus | Export-Clixml myserverstatus.xml -Verbose #Exports to XML format, supports nested object arrays VERBOSE: Performing the operation "Export-Clixml" on target "myserverstatus.xml".
In Part 3, we'll cover building custom objects from scratch and touch more on hash tables.
Wednesday, July 1, 2015
Quick Script - Find which servers are connecting to your 2003 boxes
With Windows server 2008, we got the lovely Resmon.exe tool which lets you easily see TCP/IP connections, complete with reverse name lookup. This has proven invaluable in troubleshooting issues. But what do you do about your 2003 servers that you are still supportting? Here's a quick script which uses the legacy tool netstat.exe to create a properly formed Powershell table (object array). Remember, UDP is stateless, so it doesn't ever have data for the connection state column.
If you wish to get the processID, just change "netstat -a" to "netstat -ao" and add ,"PID" to the Header line like this. Note: the columns now don't parse correctly for UDP because of how things are being split up. It's possible to fix, but most of the time spent troubleshooting will probably be spent on TCP connections.
PS C:\> ((netstat -a | select -skip 4) -replace '^\s+','') -replace '\s+',',' | >>> ConvertFrom-Csv -Header ("Protocol","Local Address","Foreign Address","State") | >>> sort state -Descending| FT -AutoSize Protocol Local Address Foreign Address State -------- ------------- --------------- ----- TCP 127.0.0.1:61840 server10:8080 SYN_SENT TCP 127.0.0.1:31014 MyPCname:0 LISTENING TCP 127.0.0.1:63202 server20:443 ESTABLISHED TCP 192.168.0.2:61819 server30:1433 CLOSE_WAIT UDP 127.0.0.1:64204 *:*
If you wish to get the processID, just change "netstat -a" to "netstat -ao" and add ,"PID" to the Header line like this. Note: the columns now don't parse correctly for UDP because of how things are being split up. It's possible to fix, but most of the time spent troubleshooting will probably be spent on TCP connections.
PS C:\> ((netstat -ao | select -skip 4) -replace '^\s+','') -replace '\s+',',' | >>> ConvertFrom-Csv -Header ("Protocol","Local Address","Foreign Address","State","PID") | >>> sort state -Descending| FT -AutoSize Protocol Local Address Foreign Address State PID -------- ------------- --------------- ----- --- TCP 127.0.0.1:61840 server10:8080 SYN_SENT 1235 TCP 127.0.0.1:31014 MyPCname:0 LISTENING 8765 TCP 127.0.0.1:63202 server20:443 ESTABLISHED 2345 TCP 192.168.0.2:61819 server30:1433 CLOSE_WAIT 3454 UDP 127.0.0.1:64204 *:* 234
Beginner's Guide - Working with lists (part 1)
One of the great advantages to using Powershell as an administration method in Windows AD environments is its ability to work with many computers or user accounts or files at once. One of the key concepts is understanding lists. The basic assumption I make here is that you know how variables are assigned and created. Lets cover some of the many ways to be effective with lists.
If you'd like to skip ahead:
Part 2: working with object arrays
Part 3: building custom object arrays from scratch
Part 4: manipulating objection arrays and custom selects
Computer Science 101 - Arrays
The first thing you must understand is that there are two types of "lists" in Powershell: hash tables and arrays. Hash tables are lists of name + value pairs. Notice how they aren't ordered; this is true for all hash tables. Arrays are always ordered.
We won't spend much time on hash tables as they have very important purposes, but for the direction of this tutorial, we'll be focusing on arrays. The key takeaway with hash tables is that they have only two "columns" of data, Name and Value. An array is a collection of objects. An object in Powershell can be of any data type. Here's some information on data types in Powershell.
In addition to the basic data types, objects can be any combination of data types. Think about an excel spreadsheet with many columns of data, each different. In Powershell, that would be an array of objects, where the row is the object record or item. The whole spreadsheet has many records. The columns symbolize the different fields (called properties in Powershell) and each one can be a different type.
For simplicity sake, lets start with an array of strings. This is probably what you think of when you hear "list". Notice that they don't have any column headings when you do a Write-Output on them. Here are a few ways to make lists:
String Array indexing
So now we have some basic concepts down, lets look a bit more at string arrays specifically. You can use the built in parameter ".count" which lets you see how many items are in the array. Also, you can select any record in the array by addressing it's index number. Arrays are ordered with an index starting at 0 and going up to the .count - 1.
More String Array fun
Once you have your String Array, you can do fun things like selecting parts of it, and sorting it alphabetically (or reverse). You can also select records which contain (or don't contain) certain characters, are of a minimum (or maximum) length, and of course write it out to a file.
Well, that's all for now. In part 2, we'll look at other object arrays.
If you'd like to skip ahead:
Part 2: working with object arrays
Part 3: building custom object arrays from scratch
Part 4: manipulating objection arrays and custom selects
Computer Science 101 - Arrays
The first thing you must understand is that there are two types of "lists" in Powershell: hash tables and arrays. Hash tables are lists of name + value pairs. Notice how they aren't ordered; this is true for all hash tables. Arrays are always ordered.
PS C:\> $HT = @{} #Declares a new empty hash table PS C:\> $HT["head"]="hat" PS C:\> $HT.Add("chest","shirt") PS C:\> $HT["legs"]="pants" PS C:\> $HT.Add("feet","shoes") PS C:\> Write-Output $HT Name Value ---- ----- chest shirt head hat legs pants feet shoes PS C:\> $HT.GetType() #This handy method tells you what kind of data your variable is. IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Hashtable System.Object
We won't spend much time on hash tables as they have very important purposes, but for the direction of this tutorial, we'll be focusing on arrays. The key takeaway with hash tables is that they have only two "columns" of data, Name and Value. An array is a collection of objects. An object in Powershell can be of any data type. Here's some information on data types in Powershell.
In addition to the basic data types, objects can be any combination of data types. Think about an excel spreadsheet with many columns of data, each different. In Powershell, that would be an array of objects, where the row is the object record or item. The whole spreadsheet has many records. The columns symbolize the different fields (called properties in Powershell) and each one can be a different type.
For simplicity sake, lets start with an array of strings. This is probably what you think of when you hear "list". Notice that they don't have any column headings when you do a Write-Output on them. Here are a few ways to make lists:
PS C:\> notepad servers.txt #Enter one server per line PS C:\> $stringarray = Get-Content .\servers.txt #Reads in the file and creates a single array, string object per line. PS C:\> Write-Output $stringarray Server1 Server2 Server3 Server4 NotAServer PS C:\> $stringarray.GetType() #technically, all arrays are just object arrays IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Object[] System.Array PS C:\> PS C:\> #This is NOT an example of an array, it's simple a multi-line string denoted by the special end markers @" and "@ PS C:\> $notAstringArray = @" >>> lets >>> try >>> this >>> out >>> "@ PS C:\> $notAstringArray.GetType() #This is just a string, not an array IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True String System.Object PS C:\> PS C:\> $AlsoStringArray = @() #Definitng an empty array PS C:\> $AlsoStringArray += "First" #The += operator basically means append this to my array PS C:\> $AlsoStringArray += "Second" PS C:\> $AlsoStringArray += "Third" PS C:\> Write-Output $AlsoStringArray First Second Third
String Array indexing
So now we have some basic concepts down, lets look a bit more at string arrays specifically. You can use the built in parameter ".count" which lets you see how many items are in the array. Also, you can select any record in the array by addressing it's index number. Arrays are ordered with an index starting at 0 and going up to the .count - 1.
PS C:\> $AnotherStringArray = @("server10","server20","server30") #another way to make a string array PS C:\> $AnotherStringArray.count #Shows number of records in array 3 PS C:\> $AnotherStringArray[0] #gets the first item in the array server10 PS C:\> $AnotherStringArray[1] server20 PS C:\> $AnotherStringArray[2] server30 PS C:\> $AnotherStringArray[3] #gets the 4th item, which there is none, so $null is returned (nothing) PS C:\> $AnotherStringArray[-1] #special index for getting the last item server30 PS C:\> $AnotherStringArray[-2] #gets the second to last item server20 PS C:\> $AnotherStringArray[0].GetType() #If we do a .GetType() on the actual record, it's a data type of string, not array IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True String System.Object PS C:\>
More String Array fun
Once you have your String Array, you can do fun things like selecting parts of it, and sorting it alphabetically (or reverse). You can also select records which contain (or don't contain) certain characters, are of a minimum (or maximum) length, and of course write it out to a file.
PS C:\> $servers = Get-Content C:\users\bryan.VINES\servers.txt PS C:\> $servers | select -first 3 -skip 1 #returns the first 3 items in the list after skipping 1 Server2 Server3 Server4 PS C:\> $servers | select -Last 2 #returns the last 2 items Server4 NotAServer PS C:\> $servers | sort -Descending Server4 Server3 Server2 Server1 NotAServer PS C:\> $servers | Where-Object{$_ -like "server*"} #returns only items that start with server (non case sensitive) Server1 Server2 Server3 Server4 PS C:\> $servers | Where-Object{$_ -like "*S*2*"} #returns the items that have the letter S and then 2 in order in the them Server2 PS C:\> $servers | Set-Content C:\outservers.txt -Verbose #writes the string array to a text file. VERBOSE: Performing the operation "Set Content" on target "Path: C:\outservers.txt".
Well, that's all for now. In part 2, we'll look at other object arrays.
Monday, June 29, 2015
Powershell Scripts - Get-Parse & Publish-Parse - upload and download data from Parse.com Core DB
If you haven't checked out Parse as a hosted database platform for your website and/or app, you should. They are free to start out and when your site or app takes off, they scale up with your traffic. It's great for startups. I didn't see a connector in Powershell to upload/download data from the core DB, so I made a pair of functions to do just that. They require Invoke-RestMethod which requires Powershell version 3 or later.
Lets try to upload a very simple table of 10 items, 2 columns:
I can see the new table in my web data browser in Parse:
Lets try to get that data back. Notice how Parse adds extra fields; if you don't want this in your data, use Select -ExcludeProperty to remove them.
Source Code:
Lets try to upload a very simple table of 10 items, 2 columns:
PS C:\> $Data | FT -AutoSize code available ---- --------- 4993918 True 5449831 True 9565284 True 9251223 True 6062778 True 5107628 True 3599651 True 6477287 True 7101669 True 8115872 True PS C:\> Publish-Parse -Class 'Codes' -AppID $AppID -APIKey $APIKey -Data $Data createdAt objectId --------- -------- 2015-06-29T23:42:14.104Z pmgrk5M87c 2015-06-29T23:42:14.330Z ivWv0Va6vE 2015-06-29T23:42:14.552Z Srj9Rcsvek 2015-06-29T23:42:14.766Z 4Muyx5v3kc 2015-06-29T23:42:14.978Z 6o4UrWc12J 2015-06-29T23:42:15.209Z 4EKTi7ZkTl 2015-06-29T23:42:15.423Z 6LMOBaSlbw 2015-06-29T23:42:15.646Z VzEgWXYbCV 2015-06-29T23:42:15.858Z YRIyxUD3Ue 2015-06-29T23:42:16.080Z UJgd2vfP5Y
I can see the new table in my web data browser in Parse:
Lets try to get that data back. Notice how Parse adds extra fields; if you don't want this in your data, use Select -ExcludeProperty to remove them.
PS C:\> Get-Parse -Class 'Codes' -AppID $AppID -APIKey $APIKey | FT -AutoSize available code createdAt objectId updatedAt --------- ---- --------- -------- --------- True 4993918 2015-06-29T23:42:14.104Z pmgrk5M87c 2015-06-29T23:42:14.104Z True 5449831 2015-06-29T23:42:14.330Z ivWv0Va6vE 2015-06-29T23:42:14.330Z True 9565284 2015-06-29T23:42:14.552Z Srj9Rcsvek 2015-06-29T23:42:14.552Z True 9251223 2015-06-29T23:42:14.766Z 4Muyx5v3kc 2015-06-29T23:42:14.766Z True 6062778 2015-06-29T23:42:14.978Z 6o4UrWc12J 2015-06-29T23:42:14.978Z True 5107628 2015-06-29T23:42:15.209Z 4EKTi7ZkTl 2015-06-29T23:42:15.209Z True 3599651 2015-06-29T23:42:15.423Z 6LMOBaSlbw 2015-06-29T23:42:15.423Z True 6477287 2015-06-29T23:42:15.646Z VzEgWXYbCV 2015-06-29T23:42:15.646Z True 7101669 2015-06-29T23:42:15.858Z YRIyxUD3Ue 2015-06-29T23:42:15.858Z True 8115872 2015-06-29T23:42:16.080Z UJgd2vfP5Y 2015-06-29T23:42:16.080Z PS C:\> Get-Parse -Class 'Codes' -AppID $AppID -APIKey $APIKey -objectID "pmgrk5M87c","VzEgWXYbCV" | FT -AutoSize available code createdAt objectId updatedAt --------- ---- --------- -------- --------- True 4993918 2015-06-29T23:42:14.104Z pmgrk5M87c 2015-06-29T23:42:14.104Z True 6477287 2015-06-29T23:42:15.646Z VzEgWXYbCV 2015-06-29T23:42:15.646Z
Source Code:
Sunday, June 28, 2015
Powershell Script - Compare-SourceFiles - source code comparison between environments (prod, bcp, qa, dev)
I just built this little script this weekend while troubleshooting a release. We needed a way to compare the code between different environments as well as compare folder permission security within environments on different nodes in the farm. This function does exactly that, producing a nice report (use -ReportFile to output to a file as well) used to prevent change drift and environment variance. If the files are text files, a line by line comparison is also generated should the file hashes be different, which shows specific lines that are different. Binary files are still flagged as different should their MD5 hashes be different. When checking ACLs, effective file permissions are displayed for each source which can speed up troubleshooting permissions issues.
PS c:\> Compare-SourceFiles -Source1 \\server1\c$\prod -Source2 \\server2\c$\dev -ACLs WARNING: Files missing from '\\server1\c$\prod': 3 \FrameXML.log \gx.log \Sound.log WARNING: File Hash different: '\a.exe' WARNING: ACL Access Permissions different: '\a.exe' File Permissions: '\\server1\c$\prod\a.exe' FileSystemRights AccessControlType IdentityReference IsInherited InheritanceFlags PropagationFlags ---------------- ----------------- ----------------- ----------- ---------------- ---------------- FullControl Allow BUILTIN\Administrators False None None FullControl Allow NT AUTHORITY\SYSTEM False None None FullControl Allow DOMAIN\User1 False None None File Permissions: '\\server2\c$\dev\a.exe' FileSystemRights AccessControlType IdentityReference IsInherited InheritanceFlags PropagationFlags ---------------- ----------------- ----------------- ----------- ---------------- ---------------- FullControl Allow BUILTIN\Administrators False None None ReadAndExecute, Synchronize Allow Everyone False None None Modify, Synchronize Allow NT AUTHORITY\Authenticated Users False None None FullControl Allow NT AUTHORITY\SYSTEM False None None WARNING: File Hash different: '\connection.log' InputObject SideIndicator ----------- ------------- 2/16 16:02:39.744 GRUNT: state: LOGIN_STATE_CONNECTING result: LOGIN_OK => 2/16 16:02:39.946 GRUNT: state: LOGIN_STATE_AUTHENTICATING result: LOGIN_OK => 2/16 16:02:40.128 GRUNT: state: LOGIN_STATE_CHECKINGVERSIONS result: LOGIN_OK => 2/16 16:02:40.205 GRUNT: state: LOGIN_STATE_HANDSHAKING result: LOGIN_OK => 2/16 16:02:40.375 GRUNT: state: LOGIN_STATE_AUTHENTICATED result: LOGIN_OK => 2/16 16:02:40.375 ClientConnection Initiating: COP_CONNECT code=CSTATUS_CONNECTING => 2/16 16:02:40.708 ClientConnection Completed: COP_CONNECT code=RESPONSE_CONNECTED result=TRUE => 2/16 16:02:40.724 ClientConnection Initiating: COP_AUTHENTICATE code=CSTATUS_AUTHENTICATING => 2/16 16:02:41.311 ClientConnection Completed: COP_AUTHENTICATE code=AUTH_OK result=TRUE => 2/16 16:02:41.780 ClientConnection Initiating: COP_GET_CHARACTERS code=43 => 2/16 16:02:42.286 ClientConnection Completed: COP_GET_CHARACTERS code=44 result=TRUE => 2/16 16:02:59.661 GRUNT: state: LOGIN_STATE_DISCONNECTED result: LOGIN_OK => 2/16 16:03:03.816 ClientConnection Initiating: COP_LOGIN_CHARACTER code=77 => 2/16 16:03:04.237 ClientConnection Completed: COP_LOGIN_CHARACTER code=78 result=TRUE => 2/16 16:01:53.409 GRUNT: state: LOGIN_STATE_CONNECTING result: LOGIN_OK <= 2/16 16:02:14.449 GRUNT: state: LOGIN_STATE_FAILED result: LOGIN_CONVERSION_REQUIRED <= 2/16 16:02:14.453 GRUNT: state: LOGIN_STATE_FAILED result: LOGIN_CONVERSION_REQUIRED <= WARNING: File Hash different: '\cpu.log' InputObject SideIndicator ----------- ------------- 2/16 16:02:30.896 vendor: 1 => 2/16 16:02:30.896 features: 00000397 => 2/16 16:02:30.896 cores: 2 => 2/16 16:02:30.896 threads: 4 => 2/16 16:02:30.896 vendor id string= GenuineIntel => 2/16 16:02:30.896 standard (13): 1b=02100800 1c=7FDAFBBF 1d=bfebfbff 4a=1C004121 => 2/16 16:02:30.896 extended (8): 1c=00000021 1d=2c100000 8c=00000000 => 2/16 16:02:30.896 processor brand string= Intel(R) Core(TM) i5-4670K CPU @ 3.40GHz => 2/16 16:01:42.716 vendor: 1 <= 2/16 16:01:42.716 features: 00000397 <= 2/16 16:01:42.716 cores: 2 <= 2/16 16:01:42.716 threads: 4 <= 2/16 16:01:42.716 vendor id string= GenuineIntel <= 2/16 16:01:42.716 standard (13): 1b=00100800 1c=7FDAFBBF 1d=bfebfbff 4a=1C004121 <= 2/16 16:01:42.716 extended (8): 1c=00000021 1d=2c100000 8c=00000000 <= 2/16 16:01:42.716 processor brand string= Intel(R) Core(TM) i5-4670K CPU @ 3.40GHz <= ============================== File Source Comparison Summary ============================== Path FileCount MissingFiles SharedFiles DifferentHashes DifferentACLs ---- --------- ------------ ----------- --------------- ------------- \\server1\c$\prod 3 3 3 3 1 \\server2\c$\dev 6 0 3 3 1
Saturday, June 27, 2015
How-To - Name your functions correctly
When you name functions, there is a set standard which you must follow or you'll see this error if you try and put your function into a module and load it:
If you run Get-Verb by itself, you'll get a list that runs off the page, but also shows which group the verbs belong to. For the most part, try to use the verb which mostly relates to the action that you are trying to do. You can see what other functions do by simply looking at same verb function's help:
To see verbs from all commands that are currently loaded, try this little block. I've marked it up the code to help explain what each line does. Try running the first line (leaving off the pipe "|"), then the first and second lines, then the first 3 lines, etc. You can see how passing the output of one cmdlet into the next works this way.
WARNING: Some imported command names include unapproved verbs which might make them less discoverable. Use the Verbose parameter for more detail or type Get-Verb to see the list of approved verbs.
That format is the basic combination of a Verb and a Noun joined by a dash ("-"). The Noun can be an alphanumeric string, no spaces and a limited scope of special characters (underscore is ok). The Verb however, has to be on a specific list. Lets take a look at the valid list:
PS C:\> Get-Verb | Format-Wide -AutoSize Add Clear Close Copy Enter Exit Find Format Get Hide Join Lock Move New Open Optimize Pop Push Redo Remove Rename Reset Resize Search Select Set Show Skip Split Step Switch Undo Unlock Watch Backup Checkpoint Compare Compress Convert ConvertFrom ConvertTo Dismount Edit Expand Export Group Import Initialize Limit Merge Mount Out Publish Restore Save Sync Unpublish Update Approve Assert Complete Confirm Deny Disable Enable Install Invoke Register Request Restart Resume Start Stop Submit Suspend Uninstall Unregister Wait Debug Measure Ping Repair Resolve Test Trace Connect Disconnect Read Receive Send Write Block Grant Protect Revoke Unblock Unprotect Use
If you run Get-Verb by itself, you'll get a list that runs off the page, but also shows which group the verbs belong to. For the most part, try to use the verb which mostly relates to the action that you are trying to do. You can see what other functions do by simply looking at same verb function's help:
PS C:\> Get-Help wait* | Format-Table Name,Synopsis -AutoSize Name Synopsis ---- -------- Wait-Job Suppresses the command prompt until one or all of the Windows PowerShell background... Wait-Debugger Stops a script in the debugger before running the next statement in the script. Wait-Event Waits until a particular event is raised before continuing to run. Wait-Process Waits for the processes to be stopped before accepting more input.
Get-Command | #Gets all commands from all loaded modules select -ExpandProperty Name | #Expands the Name column Where-Object{$_.contains("-")} | #Only selects names containing the dash character ForEach-Object{$_.split("-")[0]} | #For each of the names, split it up into 2 objects on the dash, select only the 1st select -Unique | #Remove duplicates sort | #sort alphabetically ConvertFrom-Csv -Header "Name" | #Converts a string list into an object list Format-Wide -AutoSize #Displays the list in a multi-column list
Lastly, if you are planning on releasing a module with a bunch of functions, try naming them in a similar way to indicate they are all from your module. Most third party modules do this. The format should be Verb-PrefixNoun. For example, the NetApp DataONTAP Powershell module uses the prefix "Na", so there commands look like this:
- Get-NaLun
- Set-NaVol
- Restore-NaHostFile
Friday, June 26, 2015
Quick Script - Test-PingRDP - Check if servers are pingable and have RDP enabled
I answered a question on a forum with a similar version of this script. I made it a function instead of a lose script, which have some negatives comparatively. Functions also are much easier to wrap up into a module for easy distribution. PSGet/OneGet also requires everything in modules.
Example:
Code:
Example:
PS C:\> @" Server1 Server2 Server3 Server4 NotAServer "@ | Set-Content servers.txt PS C:\>Test-IPRDP -ServerList servers.txt PS C:\>Import-Csv .\ServerStatus.csv ServerName TimeStamp RDPPort Results ---------- --------- ------- ------- server1 2015-06-26T09:35:51 3389 Up server2 2015-06-26T09:35:52 3389 Up server3 2015-06-26T09:35:53 3389 Up server4 2015-06-26T09:35:54 3389 Up NotAServer 2015-06-26T09:35:55 3389 Down PS C:\>
Code:
Beginner's Guide - Powershell for the newbie
Whether you're new to the world of Windows administration or you've been living under a rock for the past 8 years, it's OK not to know about perhaps the single most effective tool for administrating Windows environments since Active Directory. I'm not going to reinvent the wheel in this blog entry as there are tons of great resources out there, but I am going to provide some of my favorites and recommendations.
Above all, in order to learn how to use Powershell, you must practice. Most commands are innocuous and safe to run on your workstation or laptop; but to be safe, you are better off having a dedicated PC or virtual machine to create a sandbox environment. I'll be creating a blog called "How-to: Building a Virtual Lab" soon and I'll add the link when it's up.
For now, lets get to the good stuff.
Getting Started
Coming to Windows?
Books
Above all, in order to learn how to use Powershell, you must practice. Most commands are innocuous and safe to run on your workstation or laptop; but to be safe, you are better off having a dedicated PC or virtual machine to create a sandbox environment. I'll be creating a blog called "How-to: Building a Virtual Lab" soon and I'll add the link when it's up.
For now, lets get to the good stuff.
Getting Started
- Technet Windows Powershell: Self Study Guide - I tell everyone to go here first. This probably the best collection of intro links I know of.
- Microsoft Virtual Academy: Powershell Jump Start - If you like videos, this is the place to start. Jason Helmick and Jeffery Snover (inventor of Powershell) get you right into the shell and using it in minutes.
- Don Jones Powershell Crash Course - Don't have time to watch the MVA video above? Check this out.
- Powershell Pro Tutorials - A good blog that I first used to get started (it's a bit dated)
- Windows IT Pro - A really great blog for enabling Powershell to run scripts. By default, running unsigned scripts is disabled, this shows you have to get around that.
Building the knowledge
- Powershell Tutorial Online - Another excellent article series which has valuable under the hood fundamentals for making your scripts really do amazing things.
- MVA: Advanced Tools and Scripting - The second part which is a must see.
Coming to Windows?
- Powershell for Unix people - Many unix commands work in a similar way to bash, though not case sensitive thank goodness.
- Powershell on Mac - well editing on a mac anyways. Powershell over SSH will come soon enough and then you'll be able to run commands on a remote server from your non-windows client.
Books
- Learn Windows Powershell in a Month of Lunches - Don Jones & Jeffrey Hicks classic - most people learn powershell with this book.
- Learn Powershell Toolmaking in a Month of Lunches - Another Jones & Hicks book which I owe a lot of my more advanced knowledge to.
- Windows Powershell Cookbook - Lee Holmes offers a different perspective (through a developer's eyes) and has some creative tips for the IT admin.
- Powershell TFM (The Freak'n Manual) - My first Powershell book.
Classroom Learning
- Interface Powershell Training - I took the Don Jones Master Class (boot camp) last year and he blew my mind in 5 days, despite my 6 previous years of Powershell experience. Worth the $3500 and they offer web based learning. If you can get work reimbursement, all the better.
- Powershell.org Events - Many free webinars and if you are lucky, you might have a Powershell meetup group near you.
That's all for now. Did I miss something that you think should be on this list? Let me know in a comment below or Tweet me.
Thursday, June 25, 2015
Powershell Script - Get-LastBootTime - remotely find out when your servers where last rebooted
Here's another handy and simple function which lets you query a bunch of servers at once to find out when they were last rebooted. It's taking advantage of Get-WmiObject and the root\civ2\Win32_OperatingSystem class. For maximum parallelization, I've forced pipeline usage to first colapse the process block into a single array to create only one Get-WmiObject call. This is significantly faster than the default pipeline behavior which is sequential, with 1000 servers completing under a minute (local LAN) vs sequential calls taking close to 10 minutes.
Here's it in action:
Source Code:
Here's it in action:
PS C:\> Get-Content .\serverlist.txt | Get-LastBootTime Name UpSince ---- ------- NOTSERVER Unknown SERVER001 6/20/2015 10:46:25 PM SERVER002 6/20/2015 10:26:24 PM SERVER003 6/20/2015 10:30:17 PM SERVER004 6/20/2015 10:27:52 PM SERVER005 6/20/2015 10:38:27 PM SERVER006 6/20/2015 10:30:12 PM SERVER007 6/20/2015 11:54:01 PM SERVER008 6/20/2015 10:28:39 PM SERVER009 6/20/2015 10:26:15 PM SERVER010 6/20/2015 10:27:01 PM
Source Code:
Wednesday, June 24, 2015
Powershell Script - ConvertTo-HtmlTable - making pretty html formatted tables for reports and emails.
Here's a nifty utility function I built long ago that really makes emailing reports or rendering web reports easy as pie (cake is a lie). You can even change the font color, background color, border color, border width, and cell padding around the text.
Here's a little example which displays the current directory contents and opens up your browser for you after the report's finished.
dir . | select Name,Length,Mode,LastWritetime | ConvertTo-HtmlTable | Set-Content .\test.html Invoke-Item .\test.html
And the output in your browser:
Tuesday, June 23, 2015
Powershell Script - Invoke-Multithread - Make any script multithreaded
Here's a very useful wrapper which helps you turn your single threaded sequential functions into multithreaded beasts which complete in a fraction of the time when addressing many servers (or whatever network devices).
The function works by taking your code as a [scriptblock] object and a list of computers (servers, IPs, whatever) and breaking up the list based on a throttle limit (default is 25) and starting powershell jobs. The script waits for each job to finish and captures the return from the job and parses it into an object array and associates each computer's output with the name.
This function is great for generating reports, pushing configurations, making changes, etc, where native multithreaded cmdlets won't work and sequential code must be used. Most admins new to powershell don't know how to leverage built in commands that automatically thread and this wrapper function does a nice job at speeding up slow scripts.
Here's an example where I'm just checking to see if the server is a windows server, but this could be any function which takes a string array "ComputerName" as a parameter.
And the output:
ComputerName Output ------------ ------ SERVER1 Is a Windows Server SERVER2 Is a Windows Server SERVER3 Is a Windows Server SERVER4 Is a Windows Server SERVER5 Is a Windows Server SERVER6 Is a Windows Server SERVER7 Is a Windows Server SERVER8 Is a Windows Server SERVER9 Is a Windows Server SERVER10 Is a Windows Server SERVER11 Is a Windows Server SERVER12 Is a Windows Server SERVER13 Is a Windows Server SERVER14 Is a Windows Server SERVER15 Is a Windows Server SERVER16 Is a Windows Server SERVER17 Is a Windows Server SERVER18 Is a Windows Server SERVER19 Is a Windows Server SERVER20 Is a Windows Server SERVER21 Is a Windows Server SERVER22 Is a Windows Server SERVER23 Is a Windows Server SERVER24 Is a Windows Server SERVER25 Is a Windows Server SERVER26 Is a Windows Server SERVER27 Is a Windows Server SERVER28 Is a Windows Server SERVER29 Is a Windows Server SERVER30 Is a Windows Server SERVER31 Is a Windows Server SERVER32 Is a Windows Server SERVER33 Is a Windows Server SERVER34 Is a Windows Server SERVER35 Is a Windows Server SERVER36 Is a Windows Server SERVER37 Is a Windows Server SERVER38 Is a Windows Server SERVER39 Is a Windows Server SERVER40 Is a Windows Server SERVER41 Is a Windows Server SERVER42 Is a Windows Server SERVER43 Is a Windows Server SERVER44 Is a Windows Server SERVER45 Is a Windows Server SERVER46 Is a Windows Server SERVER47 Is a Windows Server SERVER48 Is a Windows Server SERVER49 Is a Windows Server SERVER50 Is a Windows Server NAS1 Is NOT a Windows Server
Monday, June 22, 2015
Powershell script: Cleaning up C:\Windows\Installer directory - the correct way
This very simple script is built off Heath Stewart's VB Script which identifies the files linked for installed products that need to be kept. My powershell wrapper simply takes his output and automates the deleting of what's not needed. Usage is simple: drop the script in a temp directory, and run. It will create and run Heath's script which creates another file (output.txt) which is then read back in and parsed. You'll see what's being removed and what's being kept.
It can be pushed as a scriptblock to remote servers using PS Remoting or PSexec.exe.
Enjoy!
Hello World!
Hello there fellow techies, scripters, IT professionals, developers. I'm Bryan, Systems Architect and automation guru specializing in Powershell with experience with most of Microsoft technologies (see my Linkedin profile for more details).
The purpose of this blog is to pay back the 15+ years of professional experience I've acquired as much of my knowledge was built on blogs like these. With Microsoft's shift towards an open source model, I thought it'd be a great time to open up my library of over 2000 scripts. I'll be documenting how-to's as I build out new environments and labs as well as sharing some of my scripts and modules.
The usual disclaimer: I'm not responsible for anything you break by running my scripts or following my how-to's. Please use caution and take time to understand what you are doing before you run something (I'll link technet as much as possible). Always test in a non-production environment if possible. My code is free to use, but please give credit. Materials on this blog or any of my code repositories may not be republished without explicit written authorization from me.
The purpose of this blog is to pay back the 15+ years of professional experience I've acquired as much of my knowledge was built on blogs like these. With Microsoft's shift towards an open source model, I thought it'd be a great time to open up my library of over 2000 scripts. I'll be documenting how-to's as I build out new environments and labs as well as sharing some of my scripts and modules.
The usual disclaimer: I'm not responsible for anything you break by running my scripts or following my how-to's. Please use caution and take time to understand what you are doing before you run something (I'll link technet as much as possible). Always test in a non-production environment if possible. My code is free to use, but please give credit. Materials on this blog or any of my code repositories may not be republished without explicit written authorization from me.
Subscribe to:
Posts (Atom)