PowerShell and the Nimble Storage REST API
If you read my previous blog article on PowerShell Function to Determine the Installed VSS Providers then you're already aware that I recently migrated one of my customers to a Nimble Storage Area Network.
While Nimble does have a PowerShell module and it's decent, I wanted to see how difficult it is to work with their REST API directly with PowerShell. Their REST API documentation also seems to be decent.
Note: All of the functions shown in this blog article are a proof of concept and should be thoroughly tested before attempting to use them in a production environment.
First of all, you need to connect to the Nimble SAN group so I wrote a function for that:
1#Requires -Version 4.0 -Module TunableSSLValidator
2function Connect-MrNSGroup {
3
4<#
5.SYNOPSIS
6 Connects to a Nimble SAN.
7
8.DESCRIPTION
9 Connect-MrNSGroup is an advanced function that provides the initial connection to a Nimble SAN
10 so that other subsequent commands can be run without having to each authenticate individually.
11 The TunableSSLValidator module is required since Nimble uses an untrusted SSL certificate. It
12 can be found on GitHub: https://github.com/Jaykul/Tunable-SSL-Validator
13
14.PARAMETER Group
15 The DNS name or IP address of the Nimble group.
16
17.PARAMETER Port
18 The port number for the Nimble REST API. The default value is 5392. This parameter is hidden and
19 provided only as a means to easily change the port number if the API ever changes.
20
21.PARAMETER Credential
22 Specifies a user account that has permission to perform this action. Type a user name, such as User01
23 or enter a PSCredential object, such as one generated by the Get-Credential cmdlet. If you type a
24 user name, this function prompts you for a password.
25
26.EXAMPLE
27 Connect-MrNSGroup -Group nimblegroup.yourdns.local -Credential (Get-Credential)
28
29.EXAMPLE
30 Connect-MrNSGroup -Group 192.168.1.50 -Credential (Get-Credential)
31
32.INPUTS
33 None
34
35.OUTPUTS
36 None
37
38.NOTES
39 Author: Mike F Robbins
40 Website: http://mikefrobbins.com
41 Twitter: @mikefrobbins
42#>
43
44 [CmdletBinding()]
45 param (
46 [Parameter(Mandatory)]
47 [string]$Group,
48
49 [Parameter(DontShow)]
50 [ValidateNotNullOrEmpty()]
51 [int]$Port = 5392,
52
53 [Parameter(Mandatory)]
54 [System.Management.Automation.Credential()]$Credential
55 )
56
57 $Uri = "https://$($Group):$($Port)"
58
59 $Script:tokenData = TunableSSLValidator\Invoke-RestMethod -Uri "$Uri/v1/tokens" -InSecure -Method Post -Body ((@{data = @{username = $Credential.UserName;password = $Credential.GetNetworkCredential().password}}) | ConvertTo-Json)
60 $Script:RestVersion = (TunableSSLValidator\Invoke-RestMethod -Uri "$Uri/versions" -Insecure).data.name
61 $Script:session_token = $tokenData.data.session_token
62 $Script:array = $Group
63}
I'm not a big fan of globally scoping variables unless absolutely necessary, but in this scenario
unless you want to authenticate with every command, it does seem to be necessary. Their SAN uses a
self-signed SSL certificate and I found that fellow Microsoft MVP
Joel Bennett had written a PowerShell module named
TunableSSLValidator that adds an Insecure
parameter to Invoke-RestMethod
which kept me from having to worry about writing the code to handle
it myself.
Update: Based on feedback from Joel Bennett, I changed the variable scoping from Global to Script to limit their access to only the module.
While many examples you might find store things like the value for Body
parameter in a variable,
the password is decrypted and then transmitted using the SSL encryption so I didn't want the plain
text password hanging around in a variable for however long I leave my current PowerShell session
open so I decided to perform the decryption inline when that line of code is executed.
This next function gets the session tokens for users who are connected to the Nimble SAN group:
1#Requires -Version 4.0 -Module TunableSSLValidator
2function Get-MrNSToken {
3
4<#
5.SYNOPSIS
6 List user session tokens.
7
8.DESCRIPTION
9 Get-MrNSToken is an advanced function that lists user session tokens for the specified Nimble SAN.
10 The TunableSSLValidator module is required since Nimble uses an untrusted SSL certificate. It can
11 be found on GitHub: https://github.com/Jaykul/Tunable-SSL-Validator
12
13.PARAMETER Group
14 The DNS name or IP address of the Nimble group. The default is the group that you've already connected
15 to using the Connect-MrNSGroup function.
16
17.PARAMETER Port
18 The port number for the Nimble REST API. The default value is 5392. This parameter is hidden and
19 provided only as a means to easily change the port number if the API ever changes.
20
21.EXAMPLE
22 Get-NStoken
23
24.INPUTS
25 None
26
27.OUTPUTS
28 MrNS.Token
29
30.NOTES
31 Author: Mike F Robbins
32 Website: http://mikefrobbins.com
33 Twitter: @mikefrobbins
34#>
35
36 [CmdletBinding()]
37 param (
38 [Parameter(DontShow)]
39 [ValidateNotNullOrEmpty()]
40 [string]$Group = $array,
41
42 [Parameter(DontShow)]
43 [ValidateNotNullOrEmpty()]
44 [int]$Port = 5392
45 )
46
47 $Uri = "https://$($Group):$($Port)"
48
49 $Header = @{'X-Auth-Token' = $session_token}
50
51 $Tokens = TunableSSLValidator\Invoke-RestMethod -Uri "$Uri/v1/tokens" -Method Get -Header $Header -Insecure
52
53 foreach ($Token in $Tokens.data.id){
54
55 $TokenUri = "$Uri/v1/tokens/$Token"
56
57 $Result = (TunableSSLValidator\Invoke-RestMethod -Uri $TokenUri -Method Get -Header $header -Insecure).data
58 $Result.PSTypeNames.Insert(0,'MrNS.Token')
59
60 Write-Output $Result
61 }
62
63}
I decided to go ahead and turn these functions into a module so it would be easier to add both
custom formatting and custom types. One thing I've found is most SAN vendors simply give you
everything back from the API. That ends up being an overwhelming amount of information so I decided
to only return a few key properties in a table by default without having to hard code anything or
pipe to format table. That's where the custom formatting comes into play. This .format.ps1xml
file
also includes custom formatting for the next function we'll look at.
1<?xml version="1.0" encoding="utf-8" ?>
2<Configuration>
3 <ViewDefinitions>
4 <View>
5 <Name>MrNS.Token</Name>
6 <ViewSelectedBy>
7 <TypeName>MrNS.Token</TypeName>
8 </ViewSelectedBy>
9 <TableControl>
10 <TableHeaders>
11 <TableColumnHeader>
12 <Width>12</Width>
13 </TableColumnHeader>
14 <TableColumnHeader>
15 <Width>15</Width>
16 </TableColumnHeader>
17 <TableColumnHeader>
18 <Width>35</Width>
19 </TableColumnHeader>
20 <TableColumnHeader>
21 <Width>24</Width>
22 </TableColumnHeader>
23 <TableColumnHeader>
24 <Width>24</Width>
25 </TableColumnHeader>
26 </TableHeaders>
27 <TableRowEntries>
28 <TableRowEntry>
29 <TableColumnItems>
30 <TableColumnItem>
31 <PropertyName>UserName</PropertyName>
32 </TableColumnItem>
33 <TableColumnItem>
34 <PropertyName>Source_IP</PropertyName>
35 </TableColumnItem>
36 <TableColumnItem>
37 <PropertyName>Session_Token</PropertyName>
38 </TableColumnItem>
39 <TableColumnItem>
40 <PropertyName>CreateTime</PropertyName>
41 </TableColumnItem>
42 <TableColumnItem>
43 <PropertyName>ModifiedTime</PropertyName>
44 </TableColumnItem>
45 </TableColumnItems>
46 </TableRowEntry>
47 </TableRowEntries>
48 </TableControl>
49 </View>
50 <View>
51 <Name>MrNS.Volume</Name>
52 <ViewSelectedBy>
53 <TypeName>MrNS.Volume</TypeName>
54 </ViewSelectedBy>
55 <TableControl>
56 <TableHeaders>
57 <TableColumnHeader>
58 <Width>25</Width>
59 </TableColumnHeader>
60 <TableColumnHeader>
61 <Width>10</Width>
62 </TableColumnHeader>
63 <TableColumnHeader>
64 <Width>10</Width>
65 </TableColumnHeader>
66 <TableColumnHeader>
67 <Width>26</Width>
68 </TableColumnHeader>
69 <TableColumnHeader>
70 <Width>18</Width>
71 </TableColumnHeader>
72 <TableColumnHeader>
73 <Width>10</Width>
74 </TableColumnHeader>
75 </TableHeaders>
76 <TableRowEntries>
77 <TableRowEntry>
78 <TableColumnItems>
79 <TableColumnItem>
80 <PropertyName>Name</PropertyName>
81 </TableColumnItem>
82 <TableColumnItem>
83 <PropertyName>Size</PropertyName>
84 </TableColumnItem>
85 <TableColumnItem>
86 <PropertyName>vol_state</PropertyName>
87 </TableColumnItem>
88 <TableColumnItem>
89 <PropertyName>perfpolicy_name</PropertyName>
90 </TableColumnItem>
91 <TableColumnItem>
92 <PropertyName>thinly_provisioned</PropertyName>
93 </TableColumnItem>
94 <TableColumnItem>
95 <PropertyName>block_size</PropertyName>
96 </TableColumnItem>
97 </TableColumnItems>
98 </TableRowEntry>
99 </TableRowEntries>
100 </TableControl>
101 </View>
102 </ViewDefinitions>
103</Configuration>
You could also add custom formatting so piping to Format-List
would only return specific
properties unless all properties were explicitly requested either with Format-Table -Property *
or
Select-Object -Property *
. An example of how to write custom formatting for list views can be
found in my blog article about
My Solution: August 2015 PowerShell Scripting Games Puzzle.
I also added a custom types file which adds a human readable date for the session token creation and
modified time stamps. The value that the API returns is the number of seconds since January 1, 1970.
The .types.ps1xml
file simply adds a couple of script properties without having to manually add them
to the function itself which seemed to be a cleaner solution.
1<Types>
2 <Type>
3 <Name>MrNS.Token</Name>
4 <Members>
5 <ScriptProperty>
6 <Name>CreateTime</Name>
7 <GetScriptBlock>
8 (Get-Date -Date '1/1/1970').AddSeconds($this.creation_time)
9 </GetScriptBlock>
10 </ScriptProperty>
11 </Members>
12 </Type>
13 <Type>
14 <Name>MrNS.Token</Name>
15 <Members>
16 <ScriptProperty>
17 <Name>ModifiedTime</Name>
18 <GetScriptBlock>
19 (Get-Date -Date '1/1/1970').AddSeconds($this.last_modified)
20 </GetScriptBlock>
21 </ScriptProperty>
22 </Members>
23 </Type>
24</Types>
To me, the really cool function is the one I wrote to retrieve a list of volumes:
1#Requires -Version 4.0 -Module TunableSSLValidator
2function Get-MrNSVolume {
3
4<#
5.SYNOPSIS
6 Retrieves information for volumes on a Nimble SAN.
7
8.DESCRIPTION
9 Get-MrNSVolume is an advanced function that retrieves information for volumes on a Nimble SAN.
10 The TunableSSLValidator module is required since Nimble uses an untrusted SSL certificate. It can
11 be found on GitHub: https://github.com/Jaykul/Tunable-SSL-Validator
12
13.PARAMETER Group
14 The DNS name or IP address of the Nimble group. The default is the group that you've already connected
15 to using the Connect-MrNSGroup function.
16
17.PARAMETER Port
18 The port number for the Nimble REST API. The default value is 5392. This parameter is hidden and
19 provided only as a means to easily change the port number if the API ever changes.
20
21.PARAMETER Name
22 Name of the volume.
23
24.PARAMETER Id
25 Identifier for the volume.
26
27.EXAMPLE
28 Get-MrNSVolume -Name Volume001
29
30.EXAMPLE
31 Get-MrNSVolume -Id '07204756105a0139c1000000000000000000000009'
32
33.INPUTS
34 None
35
36.OUTPUTS
37 MrNS.Volume
38
39.NOTES
40 Author: Mike F Robbins
41 Website: http://mikefrobbins.com
42 Twitter: @mikefrobbins
43#>
44
45 [CmdletBinding(DefaultParameterSetName='Name')]
46 param (
47 [Parameter(DontShow)]
48 [ValidateNotNullOrEmpty()]
49 [string]$Group = $array,
50
51 [Parameter(DontShow)]
52 [ValidateNotNullOrEmpty()]
53 [int]$Port = 5392,
54
55 [Parameter(ParameterSetName='Name')]
56 [string]$Name,
57
58 [Parameter(ParameterSetName='Id')]
59 [string]$Id
60 )
61
62 $Uri = "https://$($Group):$($Port)"
63
64 $Params = @{
65 Header = @{'X-Auth-Token' = $session_token}
66 Method = 'Get'
67 Insecure = $true
68 }
69
70 if ($PSBoundParameters.Name){
71
72 $Params.Uri = "$Uri/v1/volumes?name=$Name"
73
74 }
75 elseif ($PSBoundParameters.Id) {
76
77 $Params.Uri = "$Uri/v1/volumes?id=$Id"
78 }
79 else {
80
81 $Params.Uri = "$Uri/v1/volumes"
82
83 }
84
85 $Volumes = (TunableSSLValidator\Invoke-RestMethod @Params).data
86
87 foreach ($Volume in $Volumes.id){
88
89 $Params.Uri = "$Uri/v1/volumes/$Volume"
90
91 $Result = (TunableSSLValidator\Invoke-RestMethod @Params).data
92 $Result.PSTypeNames.Insert(0,'MrNS.Volume')
93
94 Write-Output $Result
95
96 }
97
98}
Do you see how splatting is used to dynamically build the commands and how it's used to reduce the amount of redundancy and minimize the amount of code you have to write?
The functions shown in this blog article are part of my MrNS PowerShell module which can be downloaded from my Nimble repository on GitHub.
µ