Creating a Cisco UCS Profile using PowerTool

Creating a Cisco UCS Profile using PowerTool

This post is going to walk through creating a powershell function to create a Service Profile on a Cisco UCS domain. Later posts in this series will look at how to assign it to a server, configure shared storage on a NetApp filer, and configure the zoning on Fiber Channel switches – essentially all the work we’d need to do to automate the creation of a physical server.

There are a few ways to create Service Profiles on a Cisco UCS domain, but if you’re looking for consistency, best practice and the possibility to automate then you’ll need to use templates – not just a Service Profile template, but templates within the Service Profile template – for vNICs, vHBAs and for any other policies. I have assumed that templates have been set up and are being used.

We are going to be using Cisco UCS PowerTool for this, so ensure you’ve got this installed on your system. You can get it from , but you’ll need a Cisco.com login.

The File

Open up your favourite IDE or text editor (personally I use Notepad++) and create a new file. Save it with a filename and a location of your choice. My file is going to be called “automate.ps1” and I keep everything in a “scripts” folder in the root of a hard drive. Powershell has a verb system, so the cmdlets are prepended (for example) with “New”, “Get” and “Set”. I’m going to use the verb “Automate” to make our functions stand out from anything else that might exist on your system, but you might wish to use your company name or something else.

Below is a sample function definition for “Automate-NewUCSProfile”.

Function Automate-NewUcsProfile {
	Param()
	Process {
		# Our code will go here
	}
}

The first line declares the function and the curly brackets open its contents. The “Param()” line will be expanded so we can pass parameters into the function, and the main body of code will reside in the curly brackets are the “Process {” section. We then close off every open curly bracket “{” with the opposite curly bracket “}”.

If you’d like to learn more about Powershell functions, your search engine of choice will direct you.

Connections

When we connect to a UCS domain we can be given a handle to it. PowerTool will support connections to multiple UCS domains, though I believe the default is one and it needs to be reconfigured through the Set-UcsPowerToolConfiguration cmdlet (see it and the Connect-UCS cmdlet help for more information). If we are connecting to one UCS domain then we won’t need to use the handle when using other cmdlets, however I believe it is good practice to do so, so I will.

To connect we use:

Connect-UCS -Name ucspe01 -Credential (Get-Credential)

When running the above connect cmdlet (and after entering correct credentials for the target UCS domain) we are given something similar to below (which is a platform emulator output running on VMware Workstation 9):

Proxy                       :
Cookie                      : 1427096220/c399b102-8234-4bad-a94d-4202ed62593a
Domains                     : org-root
LastUpdateTime              : 23/03/2015 07:36:59
Name                        : 192.168.117.135
NoSsl                       : False
NumPendingConfigs           : 0
NumWatchers                 : 0
Port                        : 443
Priv                        : {aaa, admin, ext-lan-config, ext-lan-policy...}
RefreshPeriod               : 600
SessionId                   :
TransactionInProgress       : False
PromptOnCompleteTransaction : False
Ucs                         : UCSPE-192-168-117-135
Uri                         : https://192.168.117.135
UserName                    : config
VirtualIpv4Address          : 192.168.117.135
Version                     : 2.2(2c)
WatchThreadStatus           : None

So, if we want to connect without this information being printed to the screen then we’ll need to accept the handle being returned from the cmdlet, as below:

$ucs = Connect-UCS -Name ucspe01 -Credential (Get-Credential)

Credential can be prompted for by using the “(Get-Credential)” input above, or it can be provided ahead of time, by putting the output of “Get-Credential” into a variable.

$cred = Get-Credential
$ucs = Connect-Ucs -Name ucspe1 -Credential $cred

To disconnect, we use the cmdlet

Disconnect-UCS -Ucs $ucs

So building our function it will start to look as below. We are passing in the name of the UCS domain and the credentials to connect to it.

Function Automate-NewUcsProfile {
	Param(
		[Parameter(Mandatory=$true)][String] $ucs_domain,
		[Parameter(Mandatory=$true)][System.Management.Automation.PSCredential] $credential = (Get-Credential)
	)
	Process {
		# Our code will go here
		$ucs = Connect-Ucs -Name $ucs_domain -Credential $credential
		if ($ucs -ne $null) {
		
		
		
			Disconnect-Ucs -Ucs $ucs;
		}
	}
}

One thing to note here is that if a Credential is not provided, then the person running the script is prompted to provide one.

Creating a Service Profile from a template

One line of code is required to create a Service Profile based upon a template, and it takes the following parameters:

– ServiceProfile is the name of the template upon which to base the new profile
– NewName is the name of the new profile.

It returns a object to represent the service profiles that it creates. An example:

$service_profile = Add-UcsServiceProfileFromTemplate -ServiceProfile "template-name" -NewName "new-profile" -Ucs $ucs;

Note this this leaves the new profile bound to the template. We can change this later.

We should do some sanity checking though – I’m sure you are aware of the two types of templates – “initial” and “updating” (if not, Cisco have a useful page on Service Profiles). These are represented in PowerShell by being a Service Profile object, but with a type of either “initial-template” or “updating-template”. A Service Profile object that is actually a Service Profile has a type of “instance”. So let’s make sure that a profile with this name exists:

$template = Get-UCSServiceProfile -Name "template-name" -Ucs $ucs | ? { $_.type -ne "instance" }

This code is retrieving all service profiles of the name “template-name”. It then runs a check on them to see if they have type set to “instance”, and if so, the object is returned.

If $template is equal to $null (i.e. $template -eq $null) then no template could be found. Putting our code together, outside of our function, it would look like:

$ucs = Connect-UCS -Name ucspe01 -Credential (Get-Credential)
if ($ucs) {

	$template = Get-UCSServiceProfile -Name "template-name" -Ucs $ucs | ? { $_.type -ne "instance" };
	if ($template -ne $null) {
		$service_profile = Add-UcsServiceProfileFromTemplate -ServiceProfile $template -NewName "new-profile" -Ucs $ucs;
	}

	Disconnect-UCS -UCS $ucs;
}

Optional: Unbinding from the Service Profile template

If you wish to unbind from the template, the line is simple:

Set-UcsServiceProfile -ServiceProfile $service_profile -SrcTemplName '' -Force | out-null;

Though we could pipe the $service_profile variable to it, as per:

$service_profile | Set-UcsServiceProfile -SrcTemplName '' -Force | out-null

The “| out-null” just ensures that nothing is printed on the screen as it is being run.

Assembling our Function

From the above we need to know some things:

– The name of the UCS domain to connect to.
– The credentials to connect with.
– The name of the Service Profile we want to create.
– The name of the Template we are going to bind to.

So when we put the code from above into the function, and configure our parameters, this is what it should look like:

Function Automate-NewUCSProfileFromTemplate {
	Param(
		[Parameter(Mandatory=$true)][String] $Domain,
		[Parameter(Mandatory=$true)][System.Management.Automation.PSCredential] $Credential,
		[Parameter(Mandatory=$true)][String] $new_profile_name,
		[Parameter(Mandatory=$true)][String] $template_name
	)
	Process {
		$ucs = Connect-Ucs -Name $Domain -Credential $Credential
		if ($ucs -ne $null) {			
		
			$template = Get-UCSServiceProfile -Name $template_name -Ucs $ucs | ? { $_.type -ne "instance" };
			if ($template -ne $null) {
				Add-UcsServiceProfileFromTemplate -ServiceProfile $template -NewName $new_profile_name -Ucs $ucs;
			}
		}

		Disconnect-UCS $ucs;	
	}
}

Organisations and checking Profile existence

We’ve got one last check that we should make – that the service profile doesn’t already exist. This is slightly more complicated due to Organisations – service profile names are unique within each organisation. This is represented in service profile objects by distinguished names which are formed of two parts, the location and the service profile name. Running the code:

Get-UcsServiceProfile | ft Name, Rn, Dn

will show us the format:

PS C:\> get-ucsserviceprofile | ft Name, Rn, Dn

Name                                    Rn                                      Dn
----                                    --                                      --
test                                    ls-test                                 org-root/ls-test
test                                    ls-test                                 org-root/org-Finance/ls-test

As we can see, we have two service profiles with the name “test”, one in the “root” organisation, and one in the “Finance” organisation. Let’s get the details about the organisations:

PS C:\> Get-UcsOrg | ft Name, Dn

Name                                                        Dn
----                                                        --
root                                                        org-root
Acme                                                        org-root/org-Acme
Finance                                                     org-root/org-Finance

So when we create our profile we must make a choice about where we create it – by default it will be in the root organisation. Our code will have to take a string that allows callers to provide an organisation (but have a default of “root”). We will then search for the service profile within that organisation:

$existing_profile = Get-UcsServiceProfile -Name $sp_name -Org $org

It looks simple, but there is a catch – it will still match against any service profiles in sub-organisations. We need to add the “-LimitScope” parameter in order to only match within our desired target organisation. If $existing_profile is equal to $null, then we can continue.

So our function will now look like:

Function Automate-NewUCSProfileFromTemplate {
	Param(
		[Parameter(Mandatory=$true)][String] $Domain,
		[Parameter(Mandatory=$true)][System.Management.Automation.PSCredential] $Credential,
		[Parameter(Mandatory=$true)][String] $NewName,
		[String] $Organisation = "root",
		[Parameter(Mandatory=$true)][String] $Template
	)
	Process {
		$ucs = Connect-Ucs -Name $Domain -Credential $Credential
		if ($ucs -ne $null) {			
			$org = Get-UcsOrg $Organisation;
			if ($org) {
				# Check to see if profile already exists.
				$existing_sp = Get-UcsServiceProfile -Name $NewName -Org $org -LimitScope;
				if ($existing_sp -eq $null) {
		
					# Check to see if template exists.
					$sp_template = Get-UCSServiceProfile -Name $Template -Ucs $ucs | ? { $_.type -ne "instance" };
					if ($sp_template -ne $null) {
						$sp = Add-UcsServiceProfileFromTemplate -ServiceProfile $Template -NewName $NewName -Ucs $ucs;
					}
				}
			}
		}

		Disconnect-UCS $ucs;	
	}
}

Note: Organisation names are not unique throughout the Domain and it is the Dn that will determine uniqueness. This is something that we’ll deal with next.

One Final Step

To make everything simple, we should do all our checks to see if any stage of this will fail at the start of code. For the sake of separation we’ll create a new function for this:

Function Automate-CheckUcsServiceProfileConflict {
	Param(
		[Parameter(Mandatory=$true)][Cisco.Ucs.UcsHandle] $Ucs,
		[Parameter(Mandatory=$true)][String] $NewName,
		[String] $Organisation = "root",
		[Parameter(Mandatory=$true)][String] $Template
	)
	Process {
		$result = $True;
	
		# Check the Organisation exists.
		$org = Get-UcsOrg $Organisation;
		if ($org -eq $null) {
			Write-Host "The Organisation does not exist.";
			$result = $False;
		
		} elseif ($org.Count -gt 1) {
			# We found more than one Organisation
			Write-Host "There is more than one Organisation with the name '$Organisation'. Please use the Org Dn to create Service Profiles.";
			$result = $False;
		
		} else {		
			# Check that a profile with the existing name doesn't already exist.
			$existing_sp = Get-UcsServiceProfile -Name $NewName -Org $org -LimitScope;
			if ($existing_sp -ne $null) {
				Write-Host "A Service Profile with the name '$NewName' already exists in the organisation '$Organisation'.";
				$result = $False;
				
			}			
		}
		
		# Check that the template exists.
		$sp_template = Get-UCSServiceProfile -Name $Template -Ucs $ucs | ? { $_.type -ne "instance" };
		if ($sp_template -eq $null) {
			Write-Host "The template $Template does not exist.";
			$result = $False;
		}
		
		return $result;
	}
}

There are a few differences to our original function – we are passing in the connection handle to the UCS Domain, so we don’t have to deal with any Connect-Ucs/Disconnect-Ucs and credentials. We are then going to run some checks and return whether we can go ahead with creating the service profile.

Let’s go through the code:

– We first get all the Organisations that have the name we supply.
– If there are no Organisations then we output this to the user and ensure we will return failure.
– We see if there is more than one Organisation with the name.
– If there is one Organisation then we check to see if there is a service profile with the same name in it.

– We check to see if the service profile template exists.
– We return the result.

This is where we are handling that we could have multiple Organisations could have the same name. If more than one is found, then we will fail and tell the person running our function to supply the Dn if they wish to continue.

When we alter our original function to use this one to do checks, and put everything together, it looks as per the below:

Function Automate-CheckUcsServiceProfileConflict {
	Param(
		[Parameter(Mandatory=$true)][Cisco.Ucs.UcsHandle] $Ucs,
		[Parameter(Mandatory=$true)][String] $NewName,
		[String] $Organisation = "root",
		[Parameter(Mandatory=$true)][String] $Template
	)
	Process {
		$result = $True;
	
		# Check the Organisation exists.
		$org = Get-UcsOrg $Organisation;
		if ($org -eq $null) {
			Write-Host "The Organisation does not exist.";
			$result = $False;
		
		} elseif ($org.Count -gt 1) {
			# We found more than one Organisation
			Write-Host "There is more than one Organisation with the name '$Organisation'. Please use the Org Dn to create Service Profiles.";
			$result = $False;
		
		} else {		
			# Check that a profile with the existing name doesn't already exist.
			$existing_sp = Get-UcsServiceProfile -Name $NewName -Org $org -LimitScope;
			if ($existing_sp -ne $null) {
				Write-Host "A Service Profile with the name '$NewName' already exists in the organisation '$Organisation'.";
				$result = $False;
				
			}			
		}
		
		# Check that the template exists.
		$sp_template = Get-UCSServiceProfile -Name $Template -Ucs $ucs | ? { $_.type -ne "instance" };
		if ($sp_template -eq $null) {
			Write-Host "The template $Template does not exist.";
			$result = $False;
		}
		
		return $result;
	}
}

Function Automate-NewUCSProfileFromTemplate {
	Param(
		[Parameter(Mandatory=$true)][String] $Domain,
		[Parameter(Mandatory=$true)][System.Management.Automation.PSCredential] $Credential = (Get-Credential),
		[Parameter(Mandatory=$true)][String] $NewName,
		[String] $Organisation = "root",
		[Parameter(Mandatory=$true)][String] $Template
	)
	Process {
		$ucs = Connect-Ucs -Name $Domain -Credential $Credential
		if ($ucs -ne $null) {			

			$checks_passed = Automate-CheckUcsServiceProfileConflict -Ucs $ucs -NewName $NewName -Organisation $Organisation -Template $Template;
			if ($checks_passed -eq $True) {
				$sp = Add-UcsServiceProfileFromTemplate -ServiceProfile $Template -NewName $NewName -Ucs $ucs;
				if ($sp) {
					Write-Host "Service Profile Created";
				
					# If you want to unbind from the template then put the code here.
					Set-UcsServiceProfile -ServiceProfile $sp -SrcTemplName '' -Force | out-null;
				}
			}			
		}

		Disconnect-UCS -Ucs $ucs;	
	}
}

And that’s it :). We now have some code that will create a UCS Service Profile from a template in a given organisation and will pass the obvious sanity checks.

We can now create (or at least attempt to) a service profile by doing the following:

C:\Scripts\>. .\automate.ps1
C:\Scripts\>Automate-NewUCSProfileFromTemplate -Domain ucspe01 -NewName server01 -Template init-windows

Next time, we’re going to get the HBA WWPNs and associate the service profile with a server.