# Thursday, August 16, 2007

Then get ready for PS+ !

http://www.powershell.com/plus/

 

posted on Thursday, August 16, 2007 4:32:09 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Tuesday, August 14, 2007

I got tired of typing add-pssnapin blah, especially with Quest's AD snap-in's longer than usual name so I came up with a little script that enumerates registered snap-ins, and lists a simple numbered menu showing those that are not already loaded into memory. You just type the number and hit enter to load it. Enter on an empty line exits the script. Yeah, it's brain-dead, but hey, I think I was brain-dead to continually type long commands in every single session to load stuff ;-) 

UPDATE 2007-08-16: I was even more braindead than I realised - I copied up a broken version without the $notloaded array. Ooops. Fixed.

  1. write-progress "Enumerating Snapins" "Registered..."
  2. $snaps = get-pssnapin -r
  3.  
  4. write-progress "Enumerating Snapins" "Loaded..."
  5. $loaded = get-pssnapin | % {$_.name}
  6. $notloaded  = @()
  7.  
  8. write-progress "Enumerating Snapins" "Complete." -Completed # glitchy
  9.  
  10. $i = 0; "";
  11.  
  12. foreach ($snap in $snaps) {
  13.         if (-not($loaded -contains $snap.name)) {
  14.                 Write-Host -fore cyan -nonewline "${i}) "
  15.                 write-host -fore green $snap.name
  16.                     $notloaded  += $snap
  17.                 $i++
  18.         }
  19. }
  20.  
  21. if ($i -eq 0) {
  22.         Write-Warning "Any eligible snapins are already loaded."
  23.         exit;
  24. }
  25.  
  26. $i--;
  27. Write-Host -fore yellow "<enter> to quit"
  28.  
  29. do {
  30.         write-host -nonewline "Load 0 - ${i}: ";
  31.         $choice = [console]::readline()
  32.         if ($choice -and ($choice -lt 0) -or ($choice -gt $i)) {
  33.                 Write-Warning "'${choice}' is out of range!"
  34.                 continue;
  35.         }
  36.         if ($choice) {
  37.                 "loading $($notloaded[$choice].name) ..."
  38.                 add-pssnapin $notloaded[$choice]
  39.         }
  40. } while ($choice)


Just pop it into a function or a ps1 script.

You'll notice how when this is run from a clear screen, the progress bar disappears, then suddenly reappears and the first few menu items are hidden. This is a bug in the powershell.exe host. Vote on it here:

https://connect.microsoft.com/feedback/ViewFeedback.aspx?FeedbackID=291380&SiteID=99

 

posted on Tuesday, August 14, 2007 4:05:06 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [2] Trackback
# Monday, August 13, 2007

Someone on the PowerShell NG asked recently if there was some kind of equivalent to c#'s "using namespace;" or VB.NET's "Imports namespace" statements. Well, the short answer to that is no, not really. However, you can simulate it by creating functions for each static method on the class:

  1. param([type]$type = $(throw "need a type!"))
  2.  
  3. $type | gm -static | ? {$_.membertype -eq "method" } | % {
  4.         $func = "function:$($_.name)"
  5.         if (test-path $func) { remove-item $func }
  6.         $flags = 'Public,Static,InvokeMethod,DeclaredOnly'
  7.         new-item $func -value "[$($type.fullname)].InvokeMember('$($_.name)', ${flags}, `$null, `$null, `$args[0])"
  8. }

Save this as "import.ps1" for example and use like so:

PS > . .\import.ps1 ([Math])
...

PS > Sin 1
0.841470984807897

Methods that take multiple args must have them passed as a single array:

PS > Max @(1,2)
2

Have fun!

Update: there are some interesting extensions appearing based around my initial post on microsoft.public.windows.powershell.

 



posted on Monday, August 13, 2007 4:27:05 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [2] Trackback
# Tuesday, August 07, 2007

I noticed that my last script could not handle more complex webservices like the MSDN ContentService (unlike WSDL.EXE) for example, so I rewrote it from the ground up. This time, it supports Basic Profile 1.1 and can generate proxy instances for simple ASMX services and more complicated multi-schema services like the MSDN/TechNet Publishing System (MTPS) Content Service.

What's New:

  • No need for the .NET 2.0 SDK, just PowerShell
  • Auto discovery of all referenced schema
  • No need to point it at page.asmx?wsdl any more, just the asmx
  • Validation against WS-I Basic Profile 1.1

get-webservice2.ps1.txt (4.28 KB)  (updated 2007/12/19: errant comma in write-progress fixed)

  1. #
  2. # Get-WebService.ps1 (v2.0 Aug 6, 2007)
  3. #
  4. # Oisin Grehan <oising@gmail.com> (x0n)
  5. #
  6. # Usage:
  7. #   $proxy = .\get-webservice2.ps1 [-Url] http://site/service.asmx [-Anonymous] [[-SoapProtocol] <Soap | Soap12>]
  8. #
  9. # to see available webmethods:
  10. # $proxy | gm
  11. #
  12.  
  13. # $url = "http://services.msdn.microsoft.com/contentservices/contentservice.asmx?wsdl"
  14.  
  15. param($url = $(throw "need `$url"), [switch]$Anonymous, [string]$protocol = "Soap")
  16.  
  17. [void][system.Reflection.Assembly]::LoadWithPartialName("system.web.services")
  18.  
  19. trap {
  20.         "Error:`n`n $error";
  21.         break;
  22. }
  23.  
  24. #$request = [System.Net.WebRequest]::Create($url);
  25. $dcp = new-object system.web.services.discovery.discoveryclientprotocol
  26.  
  27. if (! $Anonymous) {
  28.     Write-Progress "Network Credentials" "Awaiting input..."
  29.     $dcp.Credentials = (Get-Credential).GetNetworkCredential()
  30. }
  31.  
  32. Write-Progress "Discovery" "Searching..."
  33. $dcp.AllowAutoRedirect = $true
  34. [void]$dcp.DiscoverAny($url)
  35. $dcp.ResolveAll()
  36.  
  37. # get service name
  38. foreach ($entry in $dcp.Documents.GetEnumerator()) { # needed for Dictionary
  39.     if ($entry.Value -is [System.Web.Services.Description.ServiceDescription]) {
  40.         $script:serviceName = $entry.Value.Services[0].Name
  41.         Write-Verbose "Service: $serviceName"
  42.     }
  43. }
  44.  
  45. Write-Progress "WS-I Basic Profile 1.1" "Validating..."
  46. $ns = new-Object System.CodeDom.CodeNamespace # "WebServices"
  47.  
  48. $wref = new-object System.Web.Services.Description.WebReference $dcp.Documents, $ns
  49. $wrefs = new-object system.web.services.description.webreferencecollection
  50. [void]$wrefs.Add($wref)
  51.  
  52. $ccUnit = new-object System.CodeDom.CodeCompileUnit
  53. [void]$ccUnit.Namespaces.Add($ns)
  54.  
  55. $violations = new-object system.web.Services.Description.BasicProfileViolationCollection
  56. $wsi11 = [system.web.services.WsiProfiles]::BasicProfile1_1
  57.  
  58. if ([system.web.Services.Description.WebServicesInteroperability]::CheckConformance($wsi11, $wref, $violations)) {
  59.     Write-Progress "Proxy Generation" "Compiling..."
  60.    
  61.     $webRefOpts = new-object System.Web.Services.Description.WebReferenceOptions
  62.         $webRefOpts.CodeGenerationOptions = "GenerateNewAsync","GenerateProperties" #,"GenerateOldAsync"
  63.  
  64.         #StringCollection strings = ServiceDescriptionImporter.GenerateWebReferences(
  65.         #       webReferences, codeProvider, codeCompileUnit, parameters.GetWebReferenceOptions());
  66.  
  67.     $csprovider = new-object Microsoft.CSharp.CSharpCodeProvider
  68.         $warnings = [System.Web.Services.Description.ServiceDescriptionImporter]::GenerateWebReferences(
  69.                 $wrefs, $csprovider, $ccunit, $webRefOpts)
  70.        
  71.     if ($warnings.Count -eq 0) {
  72.         $param = new-object system.CodeDom.Compiler.CompilerParameters
  73.         [void]$param.ReferencedAssemblies.Add("System.Xml.dll")
  74.         [void]$param.ReferencedAssemblies.Add("System.Web.Services.dll")       
  75.         $param.GenerateInMemory = $true;
  76.         #$param.TempFiles = (new-object System.CodeDom.Compiler.TempFileCollection "c:\temp", $true)
  77.         $param.GenerateExecutable = $false;
  78.         #$param.OutputAssembly = "$($ns.Name)_$($sdname).dll"
  79.         $param.TreatWarningsAsErrors = $false;
  80.         $param.WarningLevel = 4;
  81.        
  82.         # do it
  83.         $compileResults = $csprovider.CompileAssemblyFromDom($param, $ccUnit);
  84.  
  85.         if ($compileResults.Errors.Count -gt 0) {
  86.             Write-Progress "Proxy Generation" "Failed."
  87.             foreach ($output in $compileResults.Output) { write-host $output }
  88.             foreach ($err in $compileResults.Errors) { write-warning $err }           
  89.         } else {           
  90.             $assembly = $compileResults.CompiledAssembly
  91.  
  92.             if ($assembly) {
  93.                 $serviceType = $assembly.GetType($serviceName)               
  94.                 $assembly.GetTypes() | % { Write-Verbose $_.FullName }
  95.             } else {
  96.                 Write-Warning "Failed: `$assembly is null"
  97.                                 return
  98.             }
  99.            
  100.             # return proxy instance
  101.             $proxy = new-object $serviceType.FullName
  102.             if (! $Anonymous) {
  103.                 $proxy.Credentials = $dcp.Credentials
  104.             }
  105.             $proxy # dump instance to pipeline
  106.         }
  107.     } else {
  108.         Write-Progress "Proxy Generation" "Failed."       
  109.         Write-Warning $warnings
  110.     }
  111.     #Write-Progress -Completed
  112. }
posted on Tuesday, August 07, 2007 12:44:03 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Thursday, August 02, 2007

For me, this opens up a whole new world of PowerShell interaction with remote servers. No more constructing soap packets - just point it at a WSDL url, and you get back a first-class object with methods that you can call corresponding to remote webmethods. PowerShell's [xml] accelerator automatically wraps the responses so you can easily manipulate the results.

Have fun!

UPDATE: newer more robust version 2.0 available!

posted on Thursday, August 02, 2007 1:56:44 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback