As promised yesterday, here there’s one of the deliverables I mentioned. About one month ago we published the first release of a set of PowerShell cmdlets for ACS: wrappers for the ACS management APIs, which allow you to easily script tasks such as wiping a namespace, adding an OpenID provider, automating often-used provisioning flows and much more.
The cmdlets were sample quality, but that didn’t prevent you from jumping on it with enthusiasm and give us a lot of great feedback: I credit especially the hosts and the audience of the Powerscripting podcast, who were so kind to have me on one episode and provide passionate commentary on what we had to improve.
Well, I am happy to announce that many of the requested improvements are here! If you head to http://wappowershell.codeplex.com/, you’ll find a new drop of the ACS cmdlets waiting for you. It’s the file ACSCmdlets20110711.exe, for good measure I took down the old one.
Make no mistake: this is still sample quality, but we added some features which will make those even more useful. Here there’s a list:
Snap-ins are out of fashion? Try our Modules
Hal and Jonathan had no doubt: our choice of delivering the cmdlets via snap-in was anachronistic, and we absolutely had to move things in a module.
We decided to offer that as an option at install time: now when you unpack the sample you will be prompted to choose if you want to use a module or a snap-in, perhaps if you are running an older version of PowerShell.
No more “plurals”
Another thing the Powerscripting crew was adamant about was the presence of one “singular” and one “plural” commands for every entity (ie Get-Rule and Get-Rules). As it turns out, the common practice in PowerShell is to have just the “singular” version and cleverly use the parameters (or lack thereof) to let PowerShell figure out the multiplicity of the result. That’s exactly what we’ve done! In our case, it’s the presence of the –Name parameter which determines if we are interested in one specific entity or a collection. The snippet below, helpfully provided by Lito from our friends at Southworks, hopefully gets the point across:
# retrieve the full list of Identity Providers
> Get-IdentityProvider –Namespace $yourNamespace –ManagementKey $yourManagementKey
# retrieve a single Identity Provider
> Get-IdentityProvider –Namespace $yourNamespace –ManagementKey $yourManagementKey –Name “Windows Live ID”
Your list exceeds the 100 entries? Try our new Get API
The feedback for this feature came from a colleague, who was very happy of the cmdlets until he discovered that he never managed to get result sets with more than 100 elements (he had the need to get MANY MORE). The ACS management API indeed cap their result to 100 elements, but as good OData citizens they also support continuation tokens. In the first release we didn’t handle those hence you were limited to what you get in the first shot to the API. This release does handle continuation tokens. It does so transparently, without surfacing the continuation token itself and forcing you to make multiple calls: we retrieve all the results for you, and if you want to break things down you can use the tools that PowerShell offers (like the classic | more).
# retrieve the full list of rules from a RuleGroup
> Get-Rule -GroupName $ruleGroup -Namespace $namespace -ManagementKey $managementKey | more
Add-, Get- and Remove- cmdlets for SeviceIdentities and ServiceIdentityKeys
You asked to be able to handle ServiceIdentity and associated keys: we obliged. Just make sure that you don’t fall in the fallacy of misusing them to use ACS as an identity provider, instead of unleashing its true federation provider potential.
> Add-ServiceIdentity -ServiceIdentity <ServiceIdentity>
> Add-ServiceIdentity –Name <String> –Description <string>
> Get-ServiceIdentity [-Name <string>]
> Remove-ServiceIdentity -Name <string>
> Add-ServiceIdentityKey -ServiceIdentityKey <ServiceIdentityKey> -ServiceIdentityName <String>
> Add-ServiceIdentityKey -Key <String> [-EffectiveDate <DateTime>] [-ExpirationDate <DateTime>] -ServiceIdentityName <String> [-Name <String>]
> Add-ServiceIdentityKey -Password <String> [-EffectiveDate <DateTime>] [-ExpirationDate <DateTime>] -ServiceIdentityName <String> [-Name <String>]
> Add-ServiceIdentityKey -Certificate <X509Certificate2> -ServiceIdentityName <String> [-Name <String>]
> Get-ServiceIdentityKey [-Id <Int64>] [-ServiceIdentityName <String>]
> Remove-ServiceIdentityKey -Id <Int64>
Add- cmdlets now can take an entire object as a parameter
We are getting closer to the entrée of this release, and the update discussed here is what makes it at all possible.
In the former release every Add- cmdlets took the attributes constituting the entity to be created as individual parameters. That worked, especially thanks to the fact that we picked meaningful defaults should the cmdlet be called with some omitted parameter. However it made especially hard to concatenate Add- with other commands, like a Get-, without adding complicated parsing logic that would break down the object coming from Get- in the individual parameters that Add- required. Well, get this: now all the Add- cmdlets accept also entire objects as parameters, making possible some interesting tricks like the one below:
# retrieve an Identity Provider from one namespace and add it to another one
> $RP = Get-RelyingParty -Name “Name Here” -MgmtToken $sourceNamespaceToken
> Add-RelyingParty -RelyingParty $RP -MgmtToken $targetNamespaceToken
You see where I am getting at here, right?
Backup and restore an ACS namespace
Enabling backup and restore was one of the main reasons for which we thought of creating a PowerShell cmdlets sample in the first place: with this release we are finally able to demonstrate that in a reasonably short and easy to read script.
In the cmdlets installation folder, sub-folder sampleScript/, you’ll find a series of sample scripts which can be used for save, restore or even transfer an entire namespace at once. Let’s play! Open a PowerShell prompt and navigate to the sampleScript folder. Pick an ACS namespace you like, retrieve the management key and enter something to the effect of
.\ExportNamespace.ps1 "myNamespace" "8m+1[.key.]mUE=" "c:\temp\myNamespace.acsns"
You’ll get the following output:
Importing AcsManagement Module…
Getting all the Identity Providers from myNamespace namespace…
Getting all the Relying Parties from myNamespace namespace…
Getting all the Rule Groups from myNamespace namespace…
Getting all the Service Keys from myNamespace namespace…
Getting all the Service Identities from myNamespace namespace…
Serializing all the information in myNamespace namespace to the c:\temp\myNamespace.acsns file…
Looks pretty simple! Let’s see what have we got in myNamespace.acsns. The namespace I used is pretty rich, resulting in a 32K file, hence dumping it here would not make a lot of sense. However take a look of the screenshot of the file as shown by XML Notepad:
Yessirs, that is an XML representation of your namespace! The script that generated this file is surprisingly simple and readable:
Param($sourceNamespace = "[your namespace]",
$sourceManagementKey = "[your namespace management key]",
[string]$fileToExport = "[path to output file]")
$Invocation = (Get-Variable MyInvocation -Scope 1).Value
$scriptDirectory = Get-ScriptDirectory
$sourceToken = Get-AcsManagementToken -Namespace $sourceNamespace -ManagementKey $sourceManagementKey
$acsNamespaceInfo = New-Object Microsoft.Samples.DPE.ACS.ServiceManagementTools.PowerShell.Model.ServiceNamespace
"Getting all the Identity Providers from $sourceNamespace namespace…"
$acsNamespaceInfo.IdentityProviders = Get-IdentityProvider -MgmtToken $sourceToken
"Getting all the Relying Parties from $sourceNamespace namespace…"
$acsNamespaceInfo.RelyingParties = @()
foreach ($s in Get-RelyingParty -MgmtToken $sourceToken)
$acsNamespaceInfo.RelyingParties += @(Get-RelyingParty -MgmtToken $sourceToken -Name $s.Name)
"Getting all the Rule Groups from $sourceNamespace namespace…"
$acsNamespaceInfo.RuleGroups = @()
foreach ($s in Get-RuleGroup -MgmtToken $sourceToken)
$acsNamespaceInfo.RuleGroups += @(Get-RuleGroup -MgmtToken $sourceToken -Name $s.Name)
"Getting all the Service Keys from $sourceNamespace namespace…"
$acsNamespaceInfo.ServiceKeys = Get-ServiceKey -MgmtToken $sourceToken
"Getting all the Service Identities from $sourceNamespace namespace…"
$acsNamespaceInfo.ServiceIdentities = Get-ServiceIdentity -MgmtToken $sourceToken
"Serializing all the information in $sourceNamespace namespace to the $fileToExport file…"
if (! [System.IO.Path]::IsPathRooted("$fileToExport"))
$fileToExport = Join-Path "$scriptDirectory" "$fileToExport"
In fact, there is nothing difficult about the above script: it’s more or less the same foreach applied in turn to IPs, RPs, rule groups & rules, service identities and keys.
Now that you have all your namespace in file you can restore it in its entirety via ImportNamespace.ps1. In fact, nothing prevents you from applying those settings even to a different ACS namespace! The CloneNamespace.ps1 demonstrates exactly that scenario.
Well, that’s it! Play with the cmdlets and let us know what you like, what you dislike and what you’d like to see: I’ll make sure to pass the feedback appropriately. As you know by now, I moved to the product team I won’t be driving the next release of the ACS cmdlets; in fact, without the kind help of Wade who took care of some last minute logistic details even this release would not have been in your hands now: thanks Wade!
And since we are on the thanks section, I wanted to take this chance to express my gratitude to the good folks at Southworks, with whom I worked very closely for the last few years, and to whom I owe my current caramel addiction (dulche de leche, to be precise). From the first identity training kits to the monumental work in FabrikamShipping SaaS, going through the identity labs in the windows azure platform training kit, keynote demos and occasional projects, the partnership with Tim, Matias, Lito, Johnny, PC, Iaco (signor Iacomuzzi!), Ariel, Nahuel, Diego, Fernando, Mariano, Nicholas, the “other Matias” and many others (sorry guys for not remembering all the names!) has been invaluable. You are probably not going to miss my OCS-grade nitpicking and inflexible quasi-religious ideas about how claims based identity should be messaged, but I will miss your professionalism, flexibility, exceptional work ethic, will to burn the midnight oil (remember that night in which the fire alarm rang in B18 (or was B24?) and when we came out we were practically the only ones in the place?) and especially all the common ground we built over the years. Best of luck you guys!