Automating the world one-liner at a time…
Andy Schneider (from Get-PowerShell.com) recently asked me how he could make sure that everyone at Avanade could get a consistent set of modules. I run into a somewhat similar problem here at Microsoft, where I want to take scripts I've built to work with internal applications and make them easy for people to use, even if they're not already using PowerShell. I don't want the scripts to be broadly available (after all, what is someone outside of Microsoft going to do with a script that works with Product Studio?) but I want the scripts (and PowerShell) to be installed or updated with a single click.
Doing this is actually really easy. All I need is a windows file share. In that file share, I'll put this little batch file (InstallModule.cmd) and I'll make two subfolders, x86 (where I'll put the x86 CTP3 msi) and amd64 (where I'll put the amd64 CTP msi). Writing a .CMD file on the PowerShell Team Blog makes me want to take a shower, but its a necessary evil in this case.
if exist %windir%\system32\WindowsPowerShell\v1.0\powershell.exe goto :AlreadyInstalled
xcopy "%~dp0\%1" "%userprofile%\Documents\WindowsPowerShell\Modules\%1" /y /s /i /d
Excuse my while I take a shower... Still there? I feel better now. I feel fairly dirty for posting this .CMD script on a PowerShell blog, but after about 5 minutes in the fetal position I think I feel comfortable enough to explain it.
The first line goes to the Windows Directory (%windir%) and checks for powershell.exe. If it's there, it goes to the label :AlreadyInstalled, thus skipping the install. If it's not there, then it goes to the directory with the appropriate .MSI and installs PowerShell. echo %~dp0 will output the location that the .CMD file InstallModule.cmd is in. It's an artifact of when I wrote it, and a sanity check for the next person running the script. The final line uses some batch voodoo and the xcopy command to install the module. %~dp0\%1 translates to the subdirectory of the directory InstallModule.cmd is in + the first argument, so if I say \\MyServer\MyShare\InstallModule FooBar, It will copy from \\MyServer\MyShare\FooBar. The destination is a little clearer: it's %userprofile% (in my case C:\Users\JamesBru) + \Documents\WindowsPowerShell\Modules\ModuleName. The first flag (/y) prevents you from seeing prompts. The second flag (/s) copies subdirectories. The third flag (/i) indicates that the destination is a directory. The final flag (/d) indicates that I should only copy files newer than ones already in the directory, if the directory already existed. This means I can update the modules simply by putting newer files on my share and re-running InstallModule.cmd on it. XCopy is useful, but I need another shower.
Still there? Good.
Now you can simply create a lot of folders beneath \\MyServer\MyShare that contain modules. It doesn't matter to this script if the modules were compiled or scripts. If you want to make it easier to install a single module, you can put in a small file, like InstallMyModule.cmd. InstallMyModule.cmd is always very simple. It's just: \\myServer\MyShare\InstallModule.cmd MyModule. Since each module is being installed to the users' directory, you don't even need to bother to run InstallMyModule.cmd elevated.
My sincere apologies for blogging command scripts on the PowerShell team blog, but this useful tidbit of knowledge allows you to set up as many small module repositories as you'll ever need and makes it easier for everyday users to start using PowerShell.
Hope this Helps,
James Brundage [MSFT]
Why not use a version control system for this?
No more messing about with command scripts and with all the added benefits of a version control system.
For example: what if you wanted to remove an obsolete script on all the computers it was installed on? Easy with a VCS, but a bit of a challenge without it :-)
I think I'd be looking to call the PowerShell installer MSI with switches to perform a silent install and then I'd smarten up the copying of the module to support pre-Vista My Documents folder locations too.
Still, very cool.
But I think that PowerShell 2 (or maybe 3) needs a more elegant solution to this problem.
> xcopy "%~dp0\%1" "%userprofile%\Documents\WindowsPowerShell\Modules\%1" /y /s /i /d
Could you please provide PowerShell variant of this command?
I totally agree with James that a version control system is much much better than xcopy deployment even more because in ps2 each module resides in its own folder which makes organizing them in a version control so much easier!
In general, I tend to shy away from solutions that introduce extra dependencies. Using a version control system may be ideal for developers, but a systems administrator will probably not want to install TFS / SVN / CVS etc just to get the module on a box
In the version I have here, I actually call a batch script I wrote that uninstalls old versions of PowerShell and bumps you onto the latest version. This post is designed to show you the basis, not to updgrade
You can use the xcopy command within powershell (but the script location and the parameter will be different). Rewriting the whole thing as PowerShell would take a decent amount of additional code, and would not handle the scenario where the user does not have PowerShell installed.
I agree that a more elegant solution would be ideal
This is a great start. Thanks for the post. I definitely agree with you wrt the source control question.
Although very cool and elegant, not many sys admins want to be bothered with it. I think we will only have 2 or 3 people updating the share, and everything that is copied up there by these 2 or maybe 3 people will be checked into TFS before being placed on the share for the rest of the dept to use.
Thank you for submitting this cool story - Trackback from DotNetShoutout
I think that this is a bit ungraceful as a solution. In our environment, Powershell is being deployed via SCCM, a shared scripts folder (all scripts in this folder must be approved and signed) is being deployed via GPO and module installation will be automated via startup (powershell) script. this startup script can easily check the versions of installed modules and replace older ones when necessary. This solution is ideal because
1. It gives us assurance that every workstation will have what we think it should.
2. It gives the user what they need without intervention.
3. It's easy to manage.
4. We move further from the horrible cmd era.