How I learned to stop worrying about writing an SMA PowerShell provider and love the drive
In addition to handling storage, distribution, queuing, and execution of runbooks, Service Management Automation (aka SMA) also performs the storage of resources (referred to in the Windows Azure Pack as ‘assets’). SMA resources consist of connections, credentials, variables (though purists will rightly tell you that they’re constants), schedules, runbooks, and jobs. Access to these elements can be carried out via the cmdlets provided with SMA, from its web services, or, if you’re using it, the Windows Azure Pack admin portal itself.
I thought I’d set myself the challenge of seeing if I could write a PowerShell Provider for SMA so that I could review all my resources from a single drive. This prove to be too much of a challenge for me. Fortunately I found out that MVP Jim Christopher (@beefarino on Twitter), has written a PowerShell module called Simplex.
Simplex
Written for implementation with PowerShell in a module, Simplex deals with the parts that you would normally need to write a provider for. All that’s really required is to define the structure and PowerShell code in a domain specific language (DSL) style template for the provider you wish to create.
NOTE : Simplex creates a navigation provider. This means that whilst you are able to list and access content, writing new and updating content is not possible.
As mentioned, the contents of this file use a DSL type syntax, of which there are only three keywords.
Root is at the top of your provider code and defines what you will see if you are at the root of the providers drive. e.g. SMA:\. Nested under there, using braces to indicate the blocks can be the two other keyboards.
Folder indicates a container within our tree structure. The text immediately after this is the name given to the folder itself. The main thing to keep in mind with folder keywords in a configuration script is that they represent a static item whose contents will not change.
Script is similar to a folder, but with the exception that when a PS Drive compatible command, such as Get-ChildItem, is used to list contents, the code within braces is dynamically run.
Configuring the SMA PowerShell Provider
Ensuring that you’re running on a system with the SMA cmdlets already installed, there’s four things we need to do in order to setup our SMA drive (or indeed any provider) :
- Download the latest Simplex module
- Install the module
- Create our provider template
- Register the PS Drive
Download and Install the Simplex Module
- In your browser navigate to Jim’s repository
- Click Download Module Zip
- Download Simplex Module.zip
- Create a folder called Simplex in one of your module paths
- Extract the contents of the ZIP file to this folder
Provider Configuration Document
Copy the content below into the clipboard, and save the content to a location on your hard disk.
(Note that there is no code for Connections, simply because it is not a feature I use at all within my Runbooks)
root { script Connections { } script Credentials{ $defaultDisplaySet = 'Name', 'LastModifiedTime' $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', [string[]]$defaultDisplaySet) $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet) $credentials = Get-SmaCredential @smaParameters $credentials.ForEach{ $hash = @{ CredentialID = $psitem.CredentialID TenantID = $psitem.TenantID Name = $psitem.Name CreationTime = $psitem.CreationTime LastModifiedTime = $psitem.LastModifiedTime Password = $psitem.Password UserName = $psitem.UserName Description = $psitem.Description } $psObject = New-Object -TypeName PSObject -Property $hash $psObject | Add-Member MemberSet PSStandardMembers $PSStandardMembers $psObject } } script Schedules{ $defaultDisplaySet = 'Name', 'LastModifiedTime' $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', [string[]]$defaultDisplaySet) $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet) $schedules = Get-SmaSchedule @smaParameters $schedules.ForEach{ $hash = @{ DayInterval = $psitem.DayInterval ScheduleID = $psitem.ScheduleID TenantID = $psitem.TenantID Name = $psitem.Name Description = $psitem.Description StartTime = $psitem.StartTime ExpiryTime = $psitem.ExpiryTime CreationTime = $psitem.CreationTime LastModifiedTime = $psitem.LastModifiedTime IsEnabled = $psitem.IsEnabled NextRun = $psitem.NextRun JobContexts = $psitem.JobContexts Runbooks = $psitem.Runbooks } $psObject = New-Object -TypeName PSObject -Property $hash $psObject | Add-Member MemberSet PSStandardMembers $PSStandardMembers $psObject } } script Variables{ $defaultDisplaySet = 'Name', 'Value', 'LastModifiedTime' $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', [string[]]$defaultDisplaySet) $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet) $variables = Get-SmaVariable @smaParameters $variables.foreach{ $hash = @{ VariableId = $psitem.VariableId TenantID = $psitem.TenantID Name = $psitem.Name IsEncrypted = $psitem.IsEncrypted CreationTime = $psitem.CreationTime LastModifiedTime = $psitem.LastModifiedTime Value = $psitem.Value Description = $psitem.Description } $psObject = New-Object -TypeName PSObject -Property $hash $psObject | Add-Member MemberSet PSStandardMembers $PSStandardMembers $psObject } } script Jobs { } script Runbook { $defaultDisplaySet = 'RunbookName', 'LastModifiedTime' $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', [string[]]$defaultDisplaySet) $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet) $runbooks = Get-SmaRunbook @smaParameters ForEach ($Runbook in $runbooks) { <# Try { $DraftContent = Get-SmaRunbookDefinition -Name $Runbook.RunbookName -Type Draft @smaParameters $PublishedContent = Get-SmaRunbookDefinition -Name $Runbook.RunbookName -Type Published @smaParameters } Catch { }#> $hash = @{ TenantID = $Runbook.TenantID RunbookID = $Runbook.RunbookID RunbookName = $Runbook.RunbookName CreationTime = $Runbook.CreationTime LastModifiedTime = $Runbook.LastModifiedTime LastModifiedBy = $Runbook.LastModifiedBy Description = $Runbook.Description IsApiOnly = $Runbook.IsApiOnly IsGlobal = $Runbook.IsGlobal PublishedRunbookVersionID = $Runbook.PublishedRunbookVersionID DraftRunbookVersionID = $Runbook.DraftRunbookVersionID Tags = $Runbook.Tags LogDebug = $Runbook.LogDebug LogVerbose = $Runbook.LogVerbose LogProgress = $Runbook.LogProgress Statistics = $Runbook.Statistics Schedules = $Runbook.Schedules } $psObject = New-Object -TypeName PSObject -Property $hash $psObject | Add-Member MemberSet PSStandardMembers $PSStandardMembers $psObject } } }
Configuration Document Structure
The document content for our SMA PS Drive above will generate a structure of the following type.
- ROOTDRIVE: (We’ll be calling the drive SMA in this blog, so it will be SMA:)
- Connections
- Result of Connections scriptblock
- Credentials
- Result of Credentials scriptblock
- Schedules
- Result of Schedules scriptblock
- Variables
- Result of Variables scriptblock
- Jobs
- Result of Jobs scriptblock
- Runbooks
- Result of Runbooks scriptblock
A Quick Look at a Script section
Let’s take a look at the code within the Credentials scriptblock above.
The first thing to notice is that is now pure PowerShell code. The tasks it performs is to define the default parameters that will be returned, execute the command Get-SMACredential, an SMA cmdlet, and then output this object. You’ve probably noticed that we’re using a splat for it’s parameters. This is defined in our intialisation script, which is covered later in this article.
The rest of the script sections use a similar methodology to obtain the associated information from SMA. However, in the Runbook section, you’ll see that the runbook definition section is commented out. The reason for this is becaue it can result in substantially long waits before the information is returned. Because i am using about 50 runbooks, this is an option i’m currently not . It may be possible to speed up this operation by a direct SQL query or using multiple jobs. I’ve yet to test this though.
Initialising the PS Drive
Now that we’ve installed Simplex and have written our initial configuration document, all this is left to do is initialise the drive. After importing the module in a PowerShell session we use the New-PSDrive command with custom PSProvider and Root parameter settings. The value for Root is set to the path of the configuration document we earlier created.
In this same script we create a credential object, which is included along with the WebServiceEndpoint in our hashtable, used for paramter splatting in the configuration document.
Import-Module Simplex New-PSDrive -Name SMA -PSProvider Simplex -Root "d:\temp\config.ps1" $username = "contoso\administrator" $secpasswd = ConvertTo-SecureString "mypassword" -AsPlainText -Force $credential = New-Object System.Management.Automation.PSCredential ($username, $secpasswd) $smaParameters = @{ Credential = $Credential WebserviceEndpoint = 'https://sma01' }
SMA PSDrive in Action
Run the code above. All being well, you will shortly see confirmation that the drive has been initialised.
From this point, you can navigate and use navigational and filtering cmdlets, such as Get-ChildItem, and Where to customise your output.
Examples
- Set-Location SMA:
- Get-ChildItem
- Set-Location -Path .\Runbook
- Get-ChildItem | Sort RunbookName
- [datetime] $before = ‘1/5/2016’
- Get-ChildItem | Where StartTime -LT $before
Conclusion
Combining the use of SMA cmdlet’s with Jim’s module to make a PS Drive has made the retrieval of information from its database a relatively simple process, and makes it easy for extension at a later date. The data abstraction achieved by implementing a PS-Provider is also something for me personally that is desirable for writing other scripts that can use this for data retrieval.
Thanks for reading, and you can find the files for SmaPlex at my GitHub repo.
Links:
- SMA CmdLets reference
- A Windows PowerShell Provider Overview on MSDN
- Jim’s blog – Introducing Simplex
- Simplex documentation and files repository