Welcome to MSDN Blogs Sign in | Join | Help

PowerShell at the PDC

I’m giving the following session at the PDC in LA in a couple of weeks:

PowerShell: Creating Manageable Web Services

Learn how to create management web services using Windows PowerShell. Topics include: how to script against of a large set of machines, how to manage raw HW devices using WS-MAN and Web Services, how to create custom "constrained environments" for scripts to run in and how to make those available to thousands of customers by writing a scalable IIS application hosting PowerShell, and how to use proxy commands.

 

I’ll be showing some things that have never been seen before and making a little bit of news so I encourage you to attend if you’ll be at the PDC.

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

Domain Specific Languages (DSLs) - In General and How PowerShell Relates

While attending JAOO, I had the great pleasure to meet and talk with Martin Fowler and Neil Ford on the topic of Domain Specific Languages (DSLs).  Channel 9 videotaped that discussion and you can watch it HERE.

I'm quite passionate about DSLs - both FOR them and AGAINST them.  The details matter!

I have this concept called, "Beer Cardinality" and it applies to DSLs.  Here is the way it works:

Have no beers  => Yawn!
Have 1 beers    => What's the point?
Have 2-6 beers => Happy Happy!
Have 24 beers  => Vomiting, hangovers, regret regret regret

DSLs are awesome BUT if you have too many, it can lead to a nightmare.  Let me draw a distinction and be more precise - there are SYNTAXes and there are VOCABULARIES.  It is fine to have a zillion Domain Specific Vocabularies (DSVs) but you only want small set of Domain Specific Syntaxes. 

There is also a very interesting discussion around Internal vs External DSLs.  Internal DSLs are essentially DSVs for particular languages Javascript, C#, PowerShell etc.  External DSL are distinct, self-contained elements orthogonal to the engine and language used to implement the engine (which processes the DSL artifact).  The problem is that you want BOTH.  You want the syntax of an Internal DSL so you don't have to learn a set of skills which put your resume into the bit bucket.  You also want an external DSL so that you can create artifacts with a well define scope of effect to facilitate transferability of the artifact.  Consider these 2 cases: 

1) I give you a 20,000 line file written as an external DSL and that DSL can do exactly one thing - it can generate Strings. 
2) I give you a 20,000 line file that is Java, C#, JavaScript,etc and I tell you all that it does is to generate strings. 

Now I say, you have to run that on your production server.  Consider what level of QA you have to do in the 2 cases.  In case 1 - you KNOW with metaphysical certitude that the DSL can ONLY generate strings.   In case 2 - it can do anything but is optimized for generating strings.

See my point?

So what you want is both internal and external DSL.  Hmmmmmm, that would explain why we have the DATA segment as part of PowerShell V2.  This gives you the best of both internal and external DSLs.  Notice that in our DATA segments, you can specify -SUPPORTEDCOMMANDS - this gives you the ability to specify a DSV.   Yummmmmm! 

People have no idea of the power and flexibility that they are going to get with PowerShell V2.  It is going to be awesome!

 BTW - I went to Martin and Neil's talks and they are super smart guys and I highly recommend going to any talk they give.  Great stuff.  Truly enjoyable.  I didn't agree with it all (as you'll see from the interview) but it was all worth listening to.

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

PowerShell’s Security Guiding Principles

One of most common issues we face with PowerShell comes from users or ISVs misunderstanding PowerShell’s security guiding principles. At a high-level, it seems to all make sense – execution policies help ensure that you only run scripts that you trust on your system. These protections are driven by PowerShell’s three main security features. For people that will never use PowerShell, that trust decision is made for you by way of PowerShell’s default “Restricted” execution policy. Others may want to use a verifiable identity chain as their important guiding factor, in which case the Authenticode signing certificates (and associated identity) enforced by the “AllSigned” mode is ideal. Advanced scripters are typically very selective in the source of their scripts (and either deeply trust the source, or have reviewed the script itself) and select a RemoteSigned or Unrestricted execution policy.

These features are the core defenses in PowerShell’s security threat model.

The threat model of an application identifies:

  • What you are trying to protect. In PowerShell’s case, this is almost entirely "code execution.”
  • Sources of data, and how that data flows. In PowerShell’s case, these are scripts sent to you through email, scripts downloaded from the internet, your profile, user input, and other similar sources. From there, this data flows through many PowerShell features – the parser, cmdlet invocation, formatting and output, etc.
  • Boundaries between untrusted data and trusted data.
    • PowerShell doesn’t trust scripts that you download from the internet.
    • PowerShell doesn’t trust a random script or executable lying in the current location of your hard drive.
    • PowerShell does trust user input.
    • PowerShell does trust the administrator of the machine.
    • PowerShell does trust a running script.

Security features come into play any time information crosses a trust boundary. PowerShell doesn’t trust scripts that you’ve downloaded from the internet, so the Execution Policy gives you a way to usher the script across that trust boundary. Once you trust it, PowerShell trusts it, and runs it faithfully.

Example: A malicious script uses the .NET Reflection APIs to modify internal engine data structures. It creates the ability to send all of your output to a secret server somewhere in the bowels of the internet. Is this a security bug?

Answer: No. PowerShell didn’t trust the script when you got it, but your choice of execution policy declared it as safe. That means it is now trusted and should operate with full functionality. After all, what kind of programming language would PowerShell be without support for TCP scripting?

PowerShell trusts user input, and runs it as-is. PowerShell’s formatting and output system trusts the objects that arrive to it, and format them however is requested.

Now, this is where things tend to get confused. People easily understand the power of an execution policy to prevent scripts from running, but often forget to consider from whom. They might think of enforcing an “AllSigned” policy as a way to prevent the user from running non-approved applications, when it is designed as a way to prevent the attacker from running scripts that the user doesn’t approve. This misconception is often wrongly reinforced by the location of V1’s ExecutionPolicy configuration key – in a registry location that only machine administrators have access to.

System-wide PowerShell Execution Policies have never been a way to prevent the user from doing something they want to do. That job is left to the Windows Account Model, which is a security boundary. It controls what a user can do: what files they can access, what registry keys they can access, etc. PowerShell is a user-mode application, and is therefore (by the Windows security model) completely under the user’s control.

Execution Policies are user feature. Like seatbelts. It’s best to keep them on, but you always have the option to take them off. PowerShell’s installer sets the execution policy to “Restricted” as a safe default for the vast majority of users that will never run a PowerShell script in their life. A system administrator might set the execution policy to AllSigned because they want to define it as a best practice, or let non-technical users run a subset of safe scripts. At any time, the user can decide otherwise:

  • Type the commands by hand
  • Paste the script into their PowerShell prompt
  • Call Invoke-Expression (Get-Content <script>)
  • Call PowerShell –Command (Get-Content <script>)
  • Use our PowerShell hosting APIs to host PowerShell with a different Authorization Manager
  • Write their own minishell that has a “Say Yes To Everything” Authorization Manager
  • Launch PowerShell in a debugger, and skip the statements that verify the Execution Policy
  • Type PowerShell commands that use private reflection to switch the Authorization Manager
  • Decompile System.Management.Automation, hack out the Authorization Manager code, and recompile it
  • Use a resource editing tool to modify the LUA manifest, and let Vista do registry virtualization into HKCU
  • Launch PowerShell in a 3rd-party registry or application virtualization program such as SoftGrid or  ThInstall
  • Use the Application Compatibility toolkit to write your own shim
  • Inject API hooks into PowerShell.exe (Overview, Tool, SDK)
  • (etc)

These are all direct results of Windows’ core security tenet: you have complete control over any process you are running.

PowerShell V2 makes this reality much more transparent through a concept called “Execution Policy Scopes.” In V1, the scopes are as follows. Items on top, if defined, override items below them:

  1. Machine-Wide Group Policy
  2. Current-User Group Policy
  3. Machine-Wide ExecutionPolicy (stored in HKLM)

In V2, the scopes are as follows, with “Process“, ”CurrentUser”, and “LocalMachine” now surfaced as the –Scope parameter to Set-ExecutionPolicy

  1. Machine-Wide Group Policy
  2. Current-User Group Policy
  3. ExecutionPolicy parameter to PowerShell.exe
  4. PSExecutionContext environment variable
  5. Current-User ExecutionPolicy (stored in HKCU)
  6. Machine-Wide ExecutionPolicy (stored in HKLM)

At its core, this refinement lets administrators and users tailor their safety harness. Jane might be fluent and technical (and opt for a RemoteSigned execution policy,) while Bob (another user of the same machine with different security preferences) can still get the benefits of an AllSigned default execution policy. In addition, agents or automation tools can invoke PowerShell commands without having to modify the permanent state of the system.

A similar misunderstanding is that PowerShell’s authorization policies somehow protect you from malware already running on your machine. Another core Windows security tenet is that any program you run has the same capabilities as you do.  This includes Vista’s UAC. It is not a security boundary.

Now, why is

PowerShell.exe –ExecutionPolicy Bypass –File c:\temp\bad-script.ps1

not a security bug? Ultimately, if bad code has the ability to run this code, it already has control of the machine.

--
Lee Holmes [MSFT]
Windows PowerShell Development
Microsoft Corporation
This posting is provided “AS IS” with no warranties, and confers no rights.

PowerShell and JAOO

On Saturday I arrived in Aarhus Denmark for the JAOO conference.  JAOO stands for Java And Object Oriented but it is definitely NOT a Java conference - it is a language conference with lots of .NET, Ruby, Ajax content and then just a lot of really smart people talking about software in general. 

I'm honored to be invited to talk about Windows PowerShell at a language conference because it allows me to start the process of setting the record straight regarding PowerShell as a language.

People parse the world according to their past experience.  What that means is that a lot of people have put PowerShell into the Bash bucket - it is an interactive shell and a tiny bit of a glue language.  That is most certainly true - you can successfully use PowerShell for years and years and never go any deeper than that.   

What is ALSO true is that PowerShell is a rich SCALABLE language.  By "scalable" I mean that it seamlessly scales from a simple ad hoc Bash-style language to a sophisticated (near) Ruby-style programming language.  I say "near" because in V2, we still don't provide a easy "native" way to create your own classes and to subclass existing classes (you can do it easily with embedded C# or VB but there is no native mechanism). 

Did you realize that people are writing WinForms, WPF, and Web applications using PowerShell?  Check out: the PowerShell Player (A podcast player written in PowerShell using the awesome Admin Script Editor which provides a form-builder for PowerShell.

The motivations for allowing the language to scale from simple to sophisticated include:

  • Our eyes are totally on the prize - cmdlets - high level, task-oriented abstractions.  It is going to take us a long to dig ourselves out of the 30 year hole we've created and the customers need these for EVERYTHING they use not just the Microsoft stuff.  As such, the community is going to be the primary providers of Cmdlets.  PowerShell needs to be capable of generating cmdlets (it is in V2) and it needs to be able to do whatever it takes to do get the job done. 
    • The model here is that advanced users will take advantage of the sophisticated parts of PowerShell to produce simple abstractions for the rest of the community to share. 
  • Problems tend to grow as you solve them, we didn't want to you be 75% into solving a problem and then have the tool run out of steam on you and you have to restart with a new language.  PowerShell should be simple for ad hoc things, and then allow you to ask it to do more and more as your solution demands it.  It is important to note that PowerShell is not binary - simple and sophisticated.  It provides a glide path from simple to sophisticated.  You pick what you need and use it when you need it. This also means that you can learn it as you need it.
  • Career growth.  PowerShell allows you to start off simple and then invest to grow your career.  You can grow your career in 2 directions with PowerShell - you can producing increasingly more productive solutions to your environment and become a rock-star admin and you can easily grow your programming skills for fun or to get into programming.  We designed the syntax of PowerShell to provide a nice glide path to C# for people that want to go down this path. 
    • At Teched I ran into someone that thanked me for PowerShell because it gave him a path to write his first C# program.  He said he always wanted to do this but was intimidated.  He was writing some PowerShell scripts and asked a programmer buddy a question and the guy said something to the effect of, "you're already programming in C#".  He bought a book and found it easy to start programming in C#.
  • We want a large community of admins and programmers to efficiently communicate and cooperate with one another.  In the previous bullet I talked about how an admin got a programmer buddy to help him with his script and how smoothly that went.  Image how pear shaped that would have gone if the script was in Perl.  The essence of automation is communities sharing artifacts to help one another.  We want the largest community of people producing common artifacts and speaking the same language.

I want to restate that we are TOTALLY COMMITTED to the simple PowerShell experience. People will always be able to run interactive PowerShell and just type cmdlets and be blissfully ignorant of they power 2 inches away.  (It's like swimming in the ocean - if you're staying on the surface - it doesn't really matter whether the water is 20 feet deep or 2,000 feet deep.)

You walk away for the day is this:  IF and WHEN I want to, I can use PowerShell as a rich general purpose language.

Cheers!

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

PowerShellPlus Professional Edition Ships

I’ve talked about PowerShellPlus a few times before and it was the hit of last years IT Forum talks.  Now The official version is now available from Idera at http://www.idera.com/products/PowerShell/ .  It is going to cost $145/user but they are offering introductory pricing of $79/user until Oct 1st.

Cheers!

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

Free Windows PowerShell ebook (Now In Italian)

Efran Cobisi has published an Italian version of Frank Koch's very popular PowerShell ebook .  You can download the Italian version HERE or go to the Windows PowerShell Italian Community website and pick it up in the Tutorial section. 

It looks like there is some great stuff there - I wish I had learned Italian. :-)

 Enjoy

Ciao!

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

Decision Input Requested: -EQ with Characters

We are struggling with whether or not to make a change in our comparison operators and would like feedback from the community.

Here is the documentation about comparision operators: 

 All comparison operators are case-insensitive by default. To make a comparison operator
 case-sensitive, precede the operator name with a "c". For example, the case-sensitive
 version of "-eq" is "-ceq". To make the case-insensitivity explicit, precede the operator
 with an "i", for example, the explicitly case-insensitive version of "-eq" is "ieq".

Here are examples of the current -EQ semantics:

PS> "a" -eq "A"
True
PS> [char]'a' -eq 'a'
True
PS> 2 -eq 2
True

Notice that -EQ works with Strings, Chars, and INTs (and lots of other things). 

Now for the headache: 

PS> [char]'a' -eq 'A'
False
PS> [char]'a' -ieq 'A'
False

<Update:  Notice that you get FALSE whether you use -eq (EQUALS) or -ieq (CASE INSENSITIVE EQUALS) >

What is happening here is that CHARs are being treated like INTs and not like STRINGs.  In our bug triage, we the feeling was that this was clearly a bug and should be fixed but someone pointed out that this if a script made use of these semantics, fixing the bug would break that script.  There are 2 things to say about that:

  1. Whenever you fix a bug, you run the risk of breaking a script.
  2. We are committed to making V2 useful and compatible with V1.

So you see the rub.  We'd like your help and input on this decision.  I'm fine getting feedback on the "general principles" of the issue - understanding how you think about these issues is very helpful to us.  That said, I'm also keenly interested in comments about the specifics of this problem. If we make THIS change, do you think it will break scripts?  If so, should we fix this anyway or not?

We struggled with this decision and had lots of back and forth on this and could go either way.  In the end, we decided that a key stackholder wasn't in the room:  YOU. 

Please opine.

<UPDATE:  BTW 10,000 apologizes for this situation in the first place.  I should have started with that.>

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

Adam Weigert Puts NetApp On a String

Adam Weigert has started a new Codeplex project PowerShell OnTap.   (That brings the total number of PowerShell-related CodePlex projects to 48!)  OnTap provides a functions to manipulate NetApp servers using the OnTAP web service APIs.

I’m not a NetApp user but I was interested in what he was doing and was delighted to read his code.  It is an excellent example of PowerShell coding practices and has a number of excellent and clever techniques in it.   I LOVE Adam’s code – it is logical, clean and visually coherent.  You should view it in an editor that does syntax coloring (like PowerShell V2 CTP2).

My only complaint would be that he should use HERE Strings more.  A number of the utility functions he provides in his library are useful to everyone – I know because I came the same conclusion and we are adding them the next CTP of PowerShell! 

Go read his code and enjoy.  Oh yea, and if you use NetApps, you’ll want to go check it out as well.  :-)

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

A Thousand Things 1% Better

One of my all time favorite books is The IBM Way by Buck Rodgers.  (Actually make that "all time BUSINESS books", it's not in the same league as Discourse on Method or Thus Spake Zarathustra.)  One of my favorite statements Buck made was something to the effect of, "products of true enduring quality are not those that do 1 thing 1000% better but rather those products that do 1000 things 1 % better".  In other words, details matter. 

We tried to cover as many details as we could with V1 but frankly we covered so much ground that some things were not a crisp and clean as we would have liked.  Those things tend to be small issues and being small issues, they are often hard to get prioritized because there are so many big opportunities around.  At the end of the day, you only have so many calories to spend and the game is to figure out how to get the biggest customer value for every calorie you spend.  There are a bunch of things that just make me cringe when I use them in V1 but the great news is that we are addressing a bunch of them in V2.  We recently had some checkins that had a number of fixes that had me dancing in the hallways (sad but true [I can't tell you how much I LOVE this stuff!]). 

Here are a couple:

The output of the ENV, ALIAS, FUNCTION and VARIABLE drives are now sorted! 

PS> dir env:

Name                           Value
----                           -----
ALLUSERSPROFILE                C:\ProgramData
APPDATA                        C:\Users\jsnover\AppData\Roaming
CommonProgramFiles             C:\Program Files\Common Files
ComSpec                        C:\Windows\system32\cmd.exe

USERNAME                       jsnover
USERPROFILE                    C:\Users\jsnover
windir                         C:\Windows 

When you ask for HELP on something and we can't find it, we'll search for it.
PS> help remote

Name                              Category  Synopsis
----                              --------  --------
about_remote                      HelpFile  How to run remote commands in Windows PowerShell.
about_remote_FAQ                  HelpFile  Questions and answers about running remote commands
about_remote_jobs                 HelpFile  How to run background jobs on remote computers
about_remote_output               HelpFile  Interpreting and formatting the output of remote commands
about_remote_requirements         HelpFile  System requirements and configuration requirements for running
about_remote_troubleshooting      HelpFile  Troubleshooting remote operations in Windows PowerShell

DIR no longer shows the PROVIDER PATH at the top.

PS> dir c:\

    Directory: C:\

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----          9/3/2008  12:28 PM            doctemp
d----          9/3/2008  12:28 PM            Drivers
d----         9/17/2008   8:34 PM            Kits
d----         1/20/2008   6:32 PM            PerfLogs
d-r--         9/18/2008  10:00 PM            Program Files
d-r--         9/18/2008   9:44 PM            ps
d-r--         9/10/2008   1:49 PM            Users
d----         9/10/2008   7:24 PM            Windows
-a---         9/18/2006   2:43 PM         24 autoexec.bat
-a---         9/18/2006   2:43 PM         10 config.sys

So that’s 3 down and 997 to go.  :-) 

Enjoy!

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

RDP File Generation/Use of HERE-Strings

Over on the Everyday Nerd blog, I found a great blog entry you should read:  Create multiple RDP files with PowerShell.  He describes his environment where he has to manage a large set of servers using Remote Desktop (RDP).  Having to configure the RDP settings each time he connects to the server, he decided to automate the process by writing a script which generates the RDP files for him. 

The script itself is cool but the think I wanted to highlight was that the script is a great illustration of a very powerful scripting design pattern.  It uses:

  1. A Template file which defines the structure of the output
  2. A Script which takes input, applies it to the template and generates output
  3. A CSV file which drives the script

This pattern has a number of advantages including:

  • You can change the template file without touching the code. 
    • If the script is digitally signed, you can change the template without affecting the digital signature. 
    • The person updating the template might not have any programming skills
  • Using a CSV file allows you to have this script driven using the IT Pro's favorite input editor:  EXCEL.

I have a couple of suggestions for how to improve the script.

Use HERE-strings

I continue to see scripts which use lots of string processing which look difficult to create and maintain and are fragile because they are complex.  Here is the example from the script:

    # Begin building RDP file
    $temp = "`nfull address:s:" + $Entry.IP
 
    switch ($resolution) {
     
      "1024x768" {
        $temp = $temp + "`nscreen mode id:i:1"
        $temp = $temp + "`ndesktopwidth:i:1024"
        $temp = $temp + "`ndesktopheight:i:768"
  $temp = $temp + "`nusername:s:" + $Entry.USER
      }

      "1152x864" {
        $temp = $temp + "`nscreen mode id:i:1"
        $temp = $temp + "`ndesktopwidth:i:1152"
        $temp = $temp + "`ndesktopheight:i:864"
  $temp = $temp + "`nusername:s:" + $Entry.USER
      }
    }

This is exactly the sort of code we hoped to eliminate by providing HERE-strings.  HERE-Strings allow you to easily create multi-line strings (with or without variable expansion and command substitution).  HERE-Strings are NOT an advanced topic - every PowerShell scripter should take the time to learn how to use HERE-Strings.  Advanced scripters should use them because they are incredibly powerful.  Beginner scripters should use them because they are incredibly easy and reduce the complexity and fragility of your scripts.

Here is the same logic refactored and written with HERE-Strings:

    # Begin building RDP file
    $temp = "`nfull address:s:" + $Entry.IP
 
    switch ($resolution) {
     
      "1024x768" {
        $width = 1024
        $height = 768
      }

      "1152x864" {
        $width = 1152
        $height = 864
      }
    }
    $temp = $temp + @"
screen mode id:i:1
desktopwidth:i:$width
desktopheight:i:$height
username:s: $($Entry.USER)
"@

In this example I used double quotes which allow variable expansion (Notice the use of $width and $Height) and command substitution (notice the use of $($Entry.USER) [you could put any command or command sequence within $() and we'll run it and substitute it's output into the string]).

Use Verb-Noun naming and Proper Verbs

CreateRDP should be New-RDP or New-RDPFile.  What we are trying to do is to achieve a world where people will be able to guess and type and have it be right.  At the beginning, those patterns are not yet established so it might seem like "create" is the same as "new".  The problem is that if we aren't hardcore on this then 50 verbs will become 500 which will become 5,000 and we'll have an incoherent world where your guesses are useless and have ourselves to blame. I know what is going on here because it happens with me all the time - you are so focused on solving the problem, you don't want to spend the energy on getting the naming right.  At some point, that pattern will be well established and you won't have to think about it - you'll need a new name and you'll naturally pick the right ones.  Until then, you need to adopt a practice of reviewing and fixing the naming any script that you share with others.  When you share a script, you'll either be enforcing or destroying the pattern for people.  Be a force of good.

The community is the ultimate controller of whether this pattern will emerge or we'll have chaos.  I'll do whatever I can do to ensure that the artifacts that we produce enforce the pattern but the bulk of scripts are going to come from the community.  The community needs to politely self police itself.  When you see someone share a script that doesn't follow the correct naming, you need to leave a comment pointing it out and ask that the post be corrected. 

Let's be clear, that includes US.  If (when) you see use posting scripts that don't follow the pattern, call us on it and we'll get it fixed.

 

Use Pipelining Features (when available in PS V2)

One of the problems with the script is that it is hardwired to a CSV file.  It would be more flexible to declare your data requirements and then be flexible with how those data requirements are satisfied.  We are redoing the interfaces to this for the next version of the CTP so I want go into detail here but I didn't want to let the point pass either.  What you'll be able to do is to specify the parameters and then specify that their values can be retrieved from the properties of a pipelined object.  What that means is that users can create any object they want as long as it has the right property names and then pipeline that object to your command.  This is a TON more powerful/flexible then hardwiring to CSV files.  What this means is you'd be able to do things like:

PS> Import-CSV RDPLIST.CSV | New-RDPFile
PS> Invoke-Database -Query <something> | New-RDPFile
PS> Invoke-WebServer -Parameter <something> | New-RDPFile
etc

 

Enjoy!

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

Hate Add-Member? (PowerShell's Adaptive Type System to the Rescue)

Do you hate Add-Member as much as I do?

Wait - maybe you aren't familiar with Add-Member or the glory of PowerShell's Adaptive Type System.  (ATS).  When I looked at the .NET type system, my reaction was "....almost".  I'm not trying to throw a rock at .NET - anyone that knows me knows how much I love .NET but the reality is that it's type system "almost" meets the needs of a management system ... but doesn't.  That is why we invented the Adaptive Type System. 

There are 4 main components of ATS:

Type Adapters. 

A lot of technologies implement their own type systems within .NET.  Most of these technologies are critical to Management scenarios (WMI, ADSI, XML, ADO, etc)  so we needed to do something about this.  What I mean by "implement their own type system" is that they have a small number of .NET types which then map to zillions of their own types.  There are a zillion WMI objects but they are all represented by a 2 or 3 .NET classes.  Type adapters allow us to adapt those technologies so they look like what users what out of a type - namely it's properties and methods.  A normal WMIObject would look like this:

Scope            : System.Management.ManagementScope
Path             :
\\JPSLAP14\root\cimv2:Win32_Service.Name="AeLookupSvc"
Options          : System.Management.ObjectGetOptions
ClassPath        :
\\JPSLAP14\root\cimv2:Win32_Service
Properties       : {AcceptPause, AcceptStop, Caption, CheckPoint...}
SystemProperties : {__GENUS, __CLASS, __SUPERCLASS, __DYNASTY...}
Qualifiers       : {dynamic, Locale, provider, UUID}
Site             :
Container        :

We adapt that object so it looks like this:

ExitCode  : 0
Name      : AeLookupSvc
ProcessId : 1068
StartMode : Auto
State     : Running
Status    : OK

Extended Metadata System

.NET gives you a meat-and-potatoes type system giving you properties, fields, methods, interfaces and events.  That is awesome but in management scenarios, we need more.  PowerShell extends the typesystem with PropertySets, Aliases, MemberSets and then provides richer set of Properties and methods (e.g. Script Properties, CodeProperties, ParameterizedProperties, etc).

Type MashUps

One of the most powerful and least used aspects of PowerShell is it's type mashup system. What this does is allow anyone to extend the type system with their own properties, methods, metadata, etc.  You can extend the types or the instances of the types.  This is CRAZY useful when you have one command which outputs objects with a property called SERVER and you want to pipeline it to a command which accepts any object in the pipeline that has a property called COMPUTERNAME.  All you have to do is to modify that definition of that type to ALIAS the property COMPUTERNAME to SERVER and now the pipelining happens with no additional code. This is freakishly powerful stuff.

Dynamic Types

In addition to extending a TYPE, you can dynamically extend any instance of a type.  Take ANY object.  You can add ANY additional property, alias, method, etc to THAT instance of the object and it won't affect any other instance.  I find that I want to do this all the time.  The mechanism for doing this is Add-Member.  Here is what Add-Member looks like:

Adds a user-defined custom member to an instance of a Windows PowerShell object.
Add-Member
        (0)-MemberType | -Type <AliasProperty | All | CodeMethod | CodeProperty | Event | MemberSet | Method |
                                             Methods | NoteProperty | Parameterty | Properties | Property | PropertySet | 
                                             ScriptMethod | ScriptProperty>
        (1)-Name <String>
           -InputObject <PSObject> (ByValue)
        (2)[-Value <Object>]
        (3)[-SecondValue <Object>]
           [-Force ]
           [-PassThru ]

Here is an example of it in use:

PS>$l = gps lsass
PS>Add-Member -InputObject $l -MemberType NoteProperty -Name test -value "PowerShell ROCKS!"
PS>$l.test
PowerShell ROCKS!
PS>$l |fl t*


test               : PowerShell ROCKS!
Threads            : {684, 688, 692, 700...}
TotalProcessorTime : 00:01:48.9354983

SOOOO - why do I hate Add-Member?

I love that Add-Member lets me do a zillion things but in reality, 95% of the time, I just want to add a bunch of NOTEPROPERTIES to an object and Add-Member feels way to heavy to accomplish that.  I've complained about this a number of times and tried to shame Bruce into fixing it but "to ship is to choose" and it has not popped up as the next best use of available calories.  After trying and failing once again, I realized that I was being stupid - that ATS allowed me to control my own destiny. 

What I decided to do was to add a new script method PSAddMember() to EVERY object in the system by attaching it to System.Object.  This method would have 3 signatures. It would take a name/value pair, it would take a name/value/membertype tuple and it would take a hashtable which is a set of name/value pairs.  Here is the file that does that and a demonstration of it in action:

<?xml version="1.0" encoding="utf-8" ?>
<Types>
    <Type>
        <Name>System.Object</Name>
        <Members>
            <ScriptMethod>
                <Name>PSAddMember</Name>
                <Script>
switch ($args.count)
{
1   {
    $hash = $args[0] -as [HashTable]
    foreach ($key in $hash.keys)
    {
        Add-Member -InputObject $this -Name $key -value $hash.$key -MemberType Noteproperty -Force
    }
    }

2   {
    $name,$value = $args
    Add-Member -InputObject $this -Name $name -value $value -MemberType Noteproperty -Force
    }

3   {
    $name,$value,$MemberType = $args
    Add-Member -InputObject $this -Name $name -value $value -MemberType $MemberType -Force
    }
}
                </Script>
            </ScriptMethod>
        </Members>
    </Type>
</Types>

----------------------------------------------------------------

PS> $p = gps lsass
PS> $p.PSAddMember("q1","Value1")
PS> $p.PSAddMember("q3",{"*"* $this.Threads.Count},"ScriptProperty")
PS> $p.PSAddMember("q2",{"*"* $this.Threads.Count},"ScriptProperty")
PS> $hash = @{q3=4; q4="Q4"; q5=(gsv alg)}
PS> $p.PSAddMember($hash)
PS> $p

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
   1721      19    12688      19756    70   110.45    652 lsass

PS> $p.q1
Value1
PS> $p.q2
*****************************
PS> $p |fl q*


q1 : Value1
q2 : *****************************
q4 : Q4
q5 : System.ServiceProcess.ServiceController
q3 : 4

 

PS> $p

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
   1721      19    12688      19756    70   110.45    652 lsass

Lessons learned/relearned:

  1. PowerShell's Adaptive Type System (ATS) is awesome.
  2. With ATS you are in control of your own destiny.  (If you don't like the world - change it yourself. [then share to help others])

Enjoy!

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx


OMG - Check This Out (Data Visualization with PowerShell)

Doug Finke has been having fun working with PowerShell and .NetMap from MSR.  Now he has written this AMAZINGLY tiny script to call the World Bank's WebService and visualize the world by income level.  This has to be seen to be believed.

If you were ever unclear on the power of the compositional model (toolkit approach) or the productivity you can achieve with PowerShell - just go visit this blog. 

Check it out HERE.

Doug - awesome stuff.  Truely wonderful.  You've made my day.

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

Join Me Tonight on the PowerScripting Podcast

Tonight at 9pm EST (6pm PST) (UTC-4), I'll be one of Hal Rottenberg's guiests on his live Podcast.,  You can find the live show details at the top right corner of http://powerscripting.net.  Apparently you'll be able to ask questions but not throw beer bottles (the internet is awesome!).  I have no idea what we'll talk about so who knows what will come out.  Let's just hope I don't say anything that get's me fired.  ha ha.

 It should be fun - join us.

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

Text Output Is Not a Contract

In PS V1, this is what you get for GET-COMMAND -SYNTAX

PS> Get-Command Get-Process -Syntax
Get-Process [[-Name] <String[]>] [-ComputerName <String[]>] [-Module] [-FileVersionInfo] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Get-Process -Id <Int32[]> [-ComputerName <String[]>] [-Module] [-FileVersionInfo] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Get-Process [-ComputerName <String[]>] [-Module] [-FileVersionInfo] -InputObject <Process[]> [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]

I look at that and a little bit of vomit comes up into my mouth.  I've been experimenting with my own radically different version but that probably won't ship in V2 and we'd like V2 to be better.  I mentioned that if we just put a newline between the parametersets and got rid of the ubiquitous parameters - we would be TONS ahead.  This is what it would look like:

PS> Get-Command Get-Process -Syntax
Get-Process [[-Name] <String[]>] [-ComputerName <String[]>] [-Module] [-FileVersionInfo]

Get-Process -Id <Int32[]> [-ComputerName <String[]>] [-Module] [-FileVersionInfo]

Get-Process [-ComputerName <String[]>] [-Module] [-FileVersionInfo] -InputObject <Process[]>

MUCH better right?  As we discussed this, someone pointed out that this change would break any scripts that parsed the text output for the V1 version.  At first I looked at the person as though they had a rat's tail hanging out of their mouth (thinking - Get-Command gives you a structured object - why would you render it to text and then parse it?!!!).  Then I realized that we hadn't actually stated what I think is obvious so now I'll state it:

In PowerShell - TEXT OUTPUT IS NOT A CONTRACT.

In UNIX, text output is effectively a programming contract because that is what the whole system is built upon.  One command outputs text and other programs know what to expect so they parse the text to get the appropriate data elements so that they can code against it.  In this model, if you change the text output of a command - you run the risk of breaking a bunch of scripts.  In reality, this happens.  It doesn't happen often because the commands don't change that much.  The other reality is that admins cope.  That's what admins do - they deal with the world as it is not as it should be.  "Unix commands shouldn't change their outout because they break my scripts, they do, my scripts are broken, grrrrrrr - cope."  It's a dirty business but someone has to do it.  :-)

That is the world of TEXT parsing.  In PowerShell - we provide an object-based world where you don't have to parse the text to get the properties that you want - you just ask for property and you get it - no fuss, no muss.  Given that - in Powershell TEXT OUTPUT IS NOT A CONTRACT.  Said aother way -
We reserve the right to radically change our text rendering to improve our customer experience. 
(God knows we have some improvements to make. :-) )

Clever people will point out that we can change our text output and not break scripts as long as they are PowerShell scripts and not say a PERL script that invokes PowerShell and parses its output.  That is exactly correct and exactly why I'm blogging this now so that everyone is put on notice and can code accordingly. 

I imagine that some people will howl about us doing our Perl buddies wrong but the reality is that this is not an issue as long as we are clear about the rules and people can adjust.  In reality, we probably don't even need to do that.  

1) If you go the National Bureau of Standards in Denver and open up the closet marked "COPER", you'll find a System Admin clutching a PERL manual.  (I'm serious, these guys do an awesome job given the world we've created for them.)
2) These guys are going to invoke PowerShell in a way that it outputs text that is easy to parse.  They are going to render it to CSVs or XML or some other useful format which is SUPER easy to do in PowerShell and avoids a bunch of horrible parsing.

As I say, it is pretty obvious when you think about it for a couple minutes but I felt it was important for us to be explicit about it so there is no confusion downstream (well ... less confusion).

Cheers.

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

 

Speeding Up PowerShell Startup - Updating Update-Gac.ps1

Have I ever mentioned how much I love the community?  Today Kirill Osenkov posted a comment on the blog pointing out that there was an API to get the .NET Framework install path:  RuntimeEnvironment.GetRuntimeDirectory() .  I didn' t know that and now I do.  :-)

I took the opportunity to rewrite Update-Gac.ps1 and in the process I decided to make it better.  This illustrates the point I've made many times.  With GUIs - you click buttons and that the end you what do you have?  Maybe the system works, maybe it doesn't and that is it.  With scripting, you end up with a system that may or may not work (same as before) BUT you also produce an artifact (a script).  When you share that script - the world changes.  If your script is crap (or just doesn't work) - people will look at it and tell you so and in the process they often tell you how to make it work or make it better.  If it works - often people will show you better ways to accomplish the same task (as Kirill did today). 

Sometimes someone's script will spark an inspiration - "I don't need THAT but if it did THIS, that would be awesome!" and an incentive to create new scripts.

One way or the other, the act of sharing a script improves your world.  You get better.  Others get better. The world gets better.

 So that is one thought.  The other thought is that PowerShell is all about producing scripts OF THE RIGHT QUALITY

Does that mean PowerShell is all about producing high quality scripts?  ABSOLUTELY NOT!

High quality scripts are often very difficult and expensive to write.  We absolutely want to support people that want to do this (we think about the guy who wants to run scripts that run the Federal Reserve Bank's servers or the DataCenter at the CIA [I sure hope both of those environments write high quality scripts]).  BUT we also want to support people that want quick and dirty scripts. 

The task at hand should determine the quality of the script.  For my Update-Gac script, it was a quick and dirty script that I rarely use.  Now that I've decided to share it, I get a little embarrassed by how raw it is so I invested a few minutes this morning to improve the script itself (thanks again to Kirill) and the user experience of the script.  At that is the point, scripts are living things.  That start out and then over time we craft them, buff them, take suggestions from other people, sometimes other people take our scripts and bring them to new levels. 

The efficient and effective creation and sharing of IT artifacts (scripts) is probably the biggest contribution PowerShell can make to the industry.  Said another way, PowerShell doesn't solve your IT problems.  PowerShell helps the IT community to work together to solve it's problems.  This is precisely why we've added Graphical PowerShell (now called PowerShell Integrated Scripting Environment) to V2 - to make it easy to create and debug scripts.  It is also why we added Modules - to make it easier and safer to share scripts.

OK - enough with all that.  Here is the new and improved Update-gac.ps1

Set-Alias ngen (Join-Path ([System.Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()) ngen.exe)
[AppDomain]::CurrentDomain.GetAssemblies() |
    sort {Split-path $_.location -leaf} |
    %{
        $Name = (Split-Path $_.location -leaf)
        if ([System.Runtime.InteropServices.RuntimeEnvironment]::FromGlobalAccessCache($_))
        {
            Write-Host "Already GACed: $Name"
        }else
        {
            Write-Host -ForegroundColor Yellow "NGENing      : $Name"
            ngen $_.location | %{"`t$_"}
         }
      }

Cheers!

Jeffrey Snover [MSFT]
Windows Management Partner Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

Posted by PowerShellTeam | 3 Comments

Attachment(s): update-Gac.ps1
More Posts Next page »
 
Page view tracker