Bruce Payette, a member of the PowerShell team is Writing a book about PowerShell :
PowerShell in Action, You can also find an Unedited Draft of Chapter 1 (Welcome to PowerShell).
I realy liked the background information about PowerShell in this chapter, it does explain the "Why" behind PowerShell and where it came from very well.
so a recommended read if you are looking for information about PowerShell
As with the switch from Monad to PowerShell there where some breaking changes, I have started a walk trough my blog history, and will re-do some of the scripts. I will start with the first script on my blog : MSH Object Viewer
I posted this script also on the NG some time before, and got a great suprise when I did watch the second Channel 9 video with Jeffrey Snover : MSHObjectViewer mentioned in More talking about Monad (links to all the channel 9 videos you can find in the $links section in the bottom of the blog)
The script will take a Object from the commandline (If you use the PowerShell Analyzer or VS it will look familiar ;-))
B.T.W. Karl did also update his MSH analyzer to PowerShell Analyzer, if you don't know this great PowerTool check out his blog Karl Prosser - Klumsy Geek
I did add the -noBase switch (but it seems not needed that often anymore)
and I did add a -array switch as I made the function also take pipeline input, I did rename it and fixed the Focus issue (for more info search for GUI in the blog), now you can use it like this
out-propertyGrid (get-date)
opg (LS)
opg (ls)[0] # shows first file / dir
ls | opg # shows last file (check the date editor ;-)
ls | opg -array # shows list
gwmi win32_operatingsystem | opg # note that you can browse in the Grid !
gwmi win32_share | opg -a
if you have a WMI Object in the PropertyGrid, try to open the properties, you get a nice list. Also try to change a date of a file you get a nice calendar.
the MSHObject (PsObject) was needed in the old beta as otherwise I did get the wrapperObject, so maybe its better to make the shitch called -PsObject and change it to $psObject.PsObject)
here is the Updated script :
# out-PropertyGrid.ps1 # # will show an Object in a PropertyGrid (propertywindow in Visual Studio) # this is an easy way to look at MSH objects, you can read / write the object properties. # # /\/\o\/\/ 2006 # http://mow001.blogspot.com
In this post an example function/script (get-Site) that will find the AD site from a given IPAddress or Hostname.
In this script two nice new features in PowerShell are shown :
byRef Parameters and Switch Parameters.
also you could see I started using new aliases a couple of weeks ago. I waited a long time using the common % and ? aliases as I keep forgetting them (see new on my blog) but now the fist one its still mine but the others are standard ! so these line are not necessary anymore :
# check needed aliases if (!(get-command -ea SilentlyContinue new)) {set-alias new New-Object} if (!(get-command -ea SilentlyContinue %)) {set-alias % ForEach-Object} if (!(get-command -ea SilentlyContinue ?)) {set-alias ? Where-Object} if (!(get-command -ea SilentlyContinue gwmi)) {set-alias gwmi get-WMIObject}
and you will see them a lot more ;-)
an other thing to note is that I did split up the function in 2 parts
the function Find-site and the function Get-site. I do this becouse I like to paste code into the PowerShell Console.
b.t.w. this is also why I always put the { at the end of the line (oposed to on the next line) and why I never use tabs in the examples this is to keep it parsable from the interactive prompt , so that I can paste it. in a script this is not needed, but this keeps the samples easy to try.
if you want to build it in a script, this would be a more common form :
# get-Site.ps1 param ($Computer,[switch]$Verbose)
# Main function Function Main {}
# helper functions
function FindSiteByIp {}
# Start Main Function
. Main
but to test by pasting them into the commandline when needed, I wil use 2 functions
The first function is the WorkHorse :
it will take a net.ipAddress Object as input, calculate possible Subnets of the IP number and try to find the subnet in AD till it does find one, then it does list the subnet and Site info from AD.
*Note* again for the example I did declare the AD stuff in one place, in a production script you would declare it only once and just set the filter in the loop. but now with no AD handy its easy to remark the AD part and more readable I think for an example.
the first function does look like this :
# find the AD site from a given IP Number # Helper Function for Get-Site Function / Script # /\/\o\/\/ 2006 # http:/mow001.blogspot.com
if ($verbose.IsPresent) {write-host -fore 'yellow'"Trying : $network"} $de = new directoryservices.directoryentry('LDAP://rootDSE') $Root = new directoryservices.directoryentry("LDAP://$($de.configurationNamingContext)") $ds = new directoryservices.directorySearcher($root) $ds.filter = "(CN=$NetWork)" $r = $ds.findone()
# If subnet found, write info and exit script.
if ($r) { write-host -fore 'yellow'"AD Site found for $IP :" $r.properties.name $r.properties.description $r.GetDirectoryEntry().siteObject break } } }
You will see some math to calculate the different network ID's possible.
then it will connect to the RootDSE to get the configurationNamingContext, connects to that with a Directory Searcher to find the subnet.
note also the :
$r.GetDirectoryEntry().siteObject
to get from the searchresult to the directoryentry and then to the Site Object.
I will not go into that in this post but search for AD on my blog (top or botom of page) to find more info about PowerShell and AD and for Byte for more info about the Math .
this Math was from an Old VB.NET "script" I made, working from former byte examples, this would be a way also : (I made this to refresh my IP math, from other byte examples) ;-)
this script also shows the usage of the [ref] variable in tryParse() But on to the Second (not counting parse-IP sample), Script.
this will do 2 things, first it checks if the $computer parameter is an IP Address by doing a tryParse() , (this method is much nicer to work with as the Parse function, as you canuse it in an IF statement as in the Example. Before in Monad/MSH this was not yet possible as the [ref] type was not supported yet.) if its not an IP number it will assume it is a hostname and will try to ping it to get the IP adress. (if ICMP is disabled, but you are admin on the remote, you can also consider adding a WMI try also to get the IP address (for more WMI info search for WMI on my blog)
the Second thing you will see is the use of the [switch] parameter, as the boolean behavour is changed (see also release notes)
so the second script is a wrapper to make it more easy to use the find-ip function.
this script looks like this :
# find the AD site from a given IP Number # Will Find an AD site from an IP Number or a Hostname # /\/\o\/\/ 2006 # http://mow001.blogspot.com
Function Get-site { param ($Computer,[switch]$Verbose) $ip = 0 if ([net.ipaddress]::tryparse($computer,[ref]$ip)) { find-site $ip }ELSE{ if ($verbose.IsPresent) {write-host -fore 'yellow'"$Computer is No IP Address, trying Ping"} $ping = new Net.NetworkInformation.Ping &{TRAP{$ip = $null;continue} if ($verbose.IsPresent) {write-host -fore 'yellow'"Pinging : $computer"} $ip = $ping.send("$Computer").address if ($ip) { write-host -fore 'yellow'"$computer resolved to $ip";find-site $ip }ELSE { write-host -fore 'yellow'"$computer Not found" } } } }
You see in the examples below Used the [switch] to togle verbose mode to output more info (shortened to -V), and in the scripts I use :
if ($verbose.IsPresent) {write-host -fore 'yellow' "text"}
constructions to output the extra info (only to console not to pipeline !).
Usage Examples :
# Get-Site using IP number
MowPS>Get-site 10.10.215.249
AD Site found for10.10.215.249 : 10.10.208.0/20 MOW The Netherlands CN=Mow-01,CN=Sites,CN=Configuration,DC=Mow,DC=com
# get site using HostName
MowPS>Get-site MowPC44
MowPC44 resolved to 10.10.215.249 AD Site found for10.10.215.249 : 10.10.208.0/20 MOW The Netherlands CN=Mow-01,CN=Sites,CN=Configuration,DC=Mow,DC=com
# Using Verbose Mode :
MowPS>Get-site mowPC44 -v
MowPC44 is No IP Address, trying Ping Pinging : MowPC44 MowPC44 resolved to 10.10.215.249 Trying : 10.10.215.248/30 Trying : 10.10.215.248/29 Trying : 10.10.215.240/28 Trying : 10.10.215.224/27 Trying : 10.10.215.192/26 Trying : 10.10.215.128/25 Trying : 10.10.215.0/24 Trying : 10.10.214.0/23 Trying : 10.10.212.0/22 Trying : 10.10.208.0/21 Trying : 10.10.208.0/20 AD Site found for10.10.215.249 : 10.10.208.0/20 MOW The Netherlands CN=Mow-01,CN=Sites,CN=Configuration,DC=Mow,DC=com
You see that the Wrapper makes a a lot easier, by checking the IP number and the option to use a hostname using ping, I use the Ping version to locate SMS roaming clients, to check DPs.
Yesterday I did read about this MMS session from Jim Bradbury: LC09 – Automating SMS Tasks with Monad Scripting, here :Day 1 - MMS 2006 - Part 2
Nice to hear he did mention my blog, I did see this session on the agenda of the MMS and did already think it did look interesting, as I just did an SMS 2003 implementation and still I'm busy with it, and I also Used Monad (and still do use PowerShell) a lot with SMS.
So I hope there will be a transcript of this session.
but then I did realize that I only have one SMS example yet on my blog and it's hidden in the CSV series. (it's in part 1), I made this into a 36 lines select statement (one-liner ;-) to do a Crosstab software report building from the CSV sample using a lot of SMS queries AD and CMDB info that way in PowerShell. the rest I mostly did prototyping in MSH, but to make it up a bit I post some basic examples here :
basicly I used 3 methods to use SMS with PowerShell,
1) WMI queries like the one in the CSV example.
2) Using the Client COM Objects :
getting available actions (as in controlPanel)
MowPS>$mgr = new -com CPApplet.cpappletmgr MowPS>$mgr.GetClientActions()
The triggering of the computer policy is very handy after sending software to the client, as this has to be done remote on the client, in the end I used a Vbscript and PSExec, as I don't have PowerShell deployed yet ;-) But I did a lot of prototyping in MSH (PS).
3) you can Find a .NET DLL in the SMS SDK you can use to manage SMS :
# Make Managed Connection
[System.Reflection.Assembly]::LoadFile('c:\sms\Microsoft.SystemsManagementServer.Automation.dll') $cred = get-credential $sms = new Microsoft.SystemsManagementServer.Automation.smsprovider("Mowsms001",$cred.username,($cred.GetNetworkCredential().password),"Site_M03")
You can see, that I can just load the DLL into PowerShell and start using it. This where just some basic examples, but I hope that this, together with the CSV power of PowerShell (see the CSV series) and working with SMS I'm sure Jim Bradbury did a better job at this, but I wanted to do a post for the ones that,as me, could not be there and have to hope for a transcript
also you have to watch for big WMI queries in SMS that will take a long time and a lot of memory. (but most SMS admins I think will be used to that) but in MSH it could stall the pipeline and also stop Ctrl-C from working (ctrl-Break will work but quits the Shell)
See this example :
$oq = new system.management.objectquery $oq.QueryString = "select * from SMS_CM_RES_COLL_MOW000B6" $mos = new-object system.management.ManagementObjectSearcher($oq) $mos.scope.path = "\\SMSServer\root\sms\Site_MOW"
# Streaming (items one-by-one)
$mos.get()
# gathering / blocking (Items all at the same time)
$mos.get() | select name,ResourceID $mos.get() | ft
b.t.w. this is only a collection, hence is still workable, the real problems begin when I do this with big software queries.
I could only do a quick post a few examples, as I also was planning an item about some new features in PowerShell As Ref variables and switch-parameters to functions. but still hope this is useful.
Dont't forget to use Get-Member on the SMS Objects to See whet they can do this helps a lot.
I will do this in an example to get the AD subnet and sitename of an IP adress or a Hostname, I hope to post later this evening .
While we are waiting for the RC Download to appear (could be any minute now) ,
*EDIT* the RC1 Bits are there ! (See links last post)
The first post is in on the new Windows PowerShell Blog about the MMS Keynote, With this first post the Monad Team blog has moved to this new location.
And if you download and install the new Bits, do not start regedit to soon ...,
Set-ExecutionPolicy RemoteSigned
... might do the trick ;-)
but on the other hand I think we have some renaming to do the get the old profile and scripts functioning again, as there are a lot of naming changes in the extensions, profile locations and CMDlets.
Hence, if you have a lot of scripts or it will take a bit more time as with last upgrades to get the custom Power back to the Shell again ;-).
and if you run the New bits, try to hit tab after a command and a minus-sign
Eg :
Get-Command -[tab]
Get-Command -Verb
this is a great way to explore the possible arguments of a CMDlet, without the need to use get-help.
The new name of Monad is announced, it is Windows PowerShell.
And more good news from the MMS, RC1 of Windows PowerShell will be released later today, so watch the download center ;-).
You can use the links below that will point to the latest versions (at the moment 3.1 but as said this will change in a couple of hours (Tip : also check the Docs) :
Also there will be launced a PowerShell portal on microsoft.com later this week and we will move to our own PowerShell newsgroup ( this will take a month or so ).
In these new RC bits there are a lot of cool improvements as Tab completion on Parameters and ByRef variables.
and also some new names for commandlets, and the extension for an PowerShell script will be .PS1
I will soon post some examples of the new fuctionality on my blog.
working from the C# example id William Stacey in the Newsgroup, (Monitoring Ports on Remote System ), I made this very simple MSH example.
I did remove the Threading (so if you have a lot of ports, Yes it's slow) but I did add some basic Banner Grabbing.
The script looks like this :
# Scan-Ports.msh # # Scans computer for a list of ports and tries to get Banners on open ports # # /\/\o\/\/ 2006 # http://mow001.blogspot.com
# check needed aliases
if (!(get-command -ea SilentlyContinue new)) {set-alias new New-Object}
# Load Function
Function Scan-Ports { param ($server = "localhost",[int[]]$ports) &{ trap{"Server $Server not Found";continue} $ping = new Net.NetworkInformation.Ping $script:result = $null $script:result = $ping.send($server) } if ($result) { foreach ($port in $ports) { Trap {"$port Not Open";continue} "Checking $server $port :" $client = new net.sockets.tcpclient $client.connect($server,$port) if ($client.connected) { "$port is Open, Trying to get banner" $stream = $client.GetStream() $chars = @() sleep -m 1000# Give server some time to react while ($stream.DataAvailable) {$chars += [char]$stream.readByte()} #([System.Text.ASCIIEncoding]::utf8).GetChars([byte[]]$chars) [string]::concat($chars) } } } }
You can Use the script like this :
# Usage :
MSH>Scan-Ports mail 110
Checking mail 110 : 110 is Open, Trying to get banner +OK InterMail POP3 server ready.
Scan-Ports $server $ports <snap> ... <snap> Checking mail 80 : 80 Not Open Checking mail 110 : 110 is Open, Trying to get banner +OK InterMail POP3 server ready.
Checking mail 8080 : 8080 Not Open Checking mail 8080 : 8080 Not Open Checking mail 4444 : 4444 Not Open Checking mail 130 : 130 Not Open Checking mail 131 : 131 Not Open
Note, that I first ping the Computer and only if I get a reply I do a portscan, to speed it up a bit, if ICMP is disabled you should remove this. Also you can get some speed using Karl Prosser's (very Cool ) start backgroundpipeline snapin : http://www.karlprosser.com/coder/?p=39
and as you can see in last example the MS telnet banner is unreadable.
Motivated by the following Newsgroup thread about listing a AD query result of more then 1000 Objects.
For gettings objects from AD this is easy fixed by setting just setting a PageSize. see also Retrieving Large Results Sets [ADSI] on MSDN for more information.
You can see this is a bit more work as we have to do the paging ourselves. Also the first time I did run into this it has cost me some time to figure this out (while I already was aware of the "normal" paging).
So I hope this will help if you come to this situation yourself.
and here the link to the Monad feed on his sharepoint blog.
To bad this is another provider I cannot test at the time, but it's great to see another provider being born, and for the Sharepoint guys, be sure to check this one out ;-)
In my first post about accessing the WinNt provider from MSH, MSH access NT provider I used an ugly workaround using inline VB.NET code.
I found a better solution (inspired by James truher's get-lastlogon example on MonadSource)
This one first loads the microsoft.visualbasic namespace, then uses the GetObject method of the microsoft.visualbasic.interaction class to get the Com-Object.
after that I will use the InvokeMember Method on the type, using Reflection binding flags to call the method or Property.
# Get-Admins.msh # # gets the users in the admininstrators group # using Visualbasic namespace, reflection and the WinNT provider # /\/\o\/\/ 2006 # http://mow001.blogspot.com
It's still a bit "Hacked" way to do this, but I think it's much better as the VB.NET one ;-)
This makes it realy possible to make much better use of the WinNT provider from MSH We are still "working in the Dark", as we can not use GM, but You can use the ADSI WinNT Provider doc's in the SDK on MSDN to get help on the properties and methods.
I'm not a programmer so not using it, but if you are be sure to check it out, I'm told it's Cool ;-)
also while searching for a link to the Channel 9 videos, I could not find them all, so here they are all, if you have not seen them be sure to check them out !!
more and more sample scripts for Monad are posted,
On scripts.readify.net under scripts 5 scripts are added this week, you can get 12 examples there now, and the number seems to grow fast, they also have a RRS feed so you can get notified when new scripts are added , I'm subscribed !.
In the last script in the script session , "Read Records from a Database" you can see how te read the data from the connection without putting it in a dataset first and work with it ofline, as I mostly do see last 2 post (" working with CSV files in MSH (part two) "," working with CSV files in MSH (part one) and the links to connecting other datasources in part 1, you can use this method with all conections mentioned.
if you only want to looponce trough the records this is not so resource intensive (E.g. do something for each record in a CSV file), then using just a reader, as I wil show in the examples below. if you only need the first field of the first record found a executeScalar is even faster.
Also on www.monadsource.com in Downloads new Scripts are added, some of them are from my blog, e.g. Remote Desktop Management, Cluster Watch, WMI Data Grid , and some more (not all links are working yet at the moment, but I'm sure this get fixed) , but scripts from all kind of sources are added here, making it more handy to get to the script.
MSH>$AdComputers = new-DbCommand "Select * from ADComputers#csv" MSH>$computers = $adcomputers.ExecuteReader()
# get the names of the tables
MSH>$computers.GetName(1) logon MSH>$computers.GetName(0) name
# now we first need to read
MSH>$computers[0] Unable to index into an object of type System.Data.OleDb.OleDbDataReader. At line:1char:12 + $computers[0 <<<< ]
MSH>$computers.read() True
# now this works
MSH>$computers[0] PC42 MSH>$computers[1] 10/17/200511:02:32 AM MSH>$computers[2] Foo
#read next record
MSH>$computers.read() True MSH>$computers[0] PC81
# for a real quick way (just to check a computer is there this reurns only the first field of the first record):
MSH>(new-DbCommand "Select name from [ADComputers#csv] where name = 'PC41'").executescalar() MSH>(new-DbCommand "Select name from [ADComputers#csv] where name = 'PC42'").executescalar() PC42
*edit* commented this also, but think this deserves an edit, don't forget the MSH "shortcuts" to arrays(enums) can be used here also
MSH>$computers[0,2] PC42 Foo
MSH>$computers[0..2] PC42 10/17/200511:02:32 AM Foo
MSH>$computers[0..($computers.fieldcount - 1)] PC42 10/17/200511:02:32 AM Foo
MSH>$computers['name'] PC42 MSH>$computers['logon'] 10/17/2005 11:02:32 AM MSH>$computers['description'] Foo
You can see from the interactive examples above, and the SQL script from scripts.readify.net that does a loop (as you did see in the examples above this works also for a CSV, Excel of Access ADO connection), that this is a great way to use those connections to make action upon .(I'm sure I will show it a later example on my blog)