Hello Readers/Viewers!

It has been a little while since I posted raw PowerShell, so I figured it was time! What do I have for you this time?

Use Case Components

Let’s say you have the following things, and want them in a simple working scenario:

  1. Virtual Machine Manager 2012 R2
  2. Multi-Tenant Private Clouds ready for Tenant Resources
  3. Logical Network (or Networks) ready for Network Virtualization (associated with those Clouds)
  4. VMM Service Templates (with parameters)
  5. Different Tenant Administrator Users needing what is offered by those VMM Service Templates
  6. Windows PowerShell

Oh, you say you also have Windows Azure Pack (WAP), Service Management Automation (SMA) and/or System Center Orchestrator? Well, that is excellent! Just take the PowerShell examples you see here, and leverage them as you see fit. Of course, I will give some context around usage scenarios for the PowerShell scripts provided here (see “Next Steps” below), but I wanted to be as generic as possible.


Use Case Scenario

Here is the high level breakdown of the scenario in this blog post (all steps are performed with PowerShell against Virtual Machine Manager):

  1. Create a Tenant Administrator User Role
  2. Create a VM Network “OnBehalfOf” that User Role
  3. Deploy a Service Template with Parameters “OnBehalfOf” that User Role

Before we dive in, let’s take a quick look at some of these concepts…

Multi-Tenancy

Multi-tenancy within Virtual Machine Manager provides capabilities for VM Network Isolation, individualized Service Deployment and Tenant Administration. Tenant Administrators are responsible for cloud resources and self-service user administration.

What is a Tenant Administrator?

Here is how Virtual Machine Manager defines the “Tenant Administrator” User Role Profile:

image

Essentially, Tenant Administrators can be allocated a specific set of resources in a specified cloud. How each Tenant Administrator uses these resources is up to them, and they are seen as “owners” of the resources within Virtual Machine Manager.

image

Note     If you would like a little bit more information about the Tenant Administrator User Role in VMM, check out this TechNet Library entry: How to Create a Tenant Administrator User Role in VMM (manual steps)

Why is the concept of “OnBehalfOf” important?

It allows for Virtual Machine Manager commands to be executed “On Behalf Of” specified Users. In our scenario, these commands are executed “On Behalf Of” created and assigned Tenant Administrators.

While a bit dated, it is still relevant - here is a bit more information about “Proxying Identity” with “OnBehalfOf” in Virtual Machine Manager:

image

Resource: Enabling Hosted IaaS Clouds for Service Providers

So, let’s not waste any more time on explanation, and get right to the scripts!


Create a Tenant Administrator User Role

The following PowerShell workflow script (Create-TenantAdminUserRole) will create a VMM Tenant Administrator User Role with the following settings:

  • User Role Name: <User Role Name generated by User defined with parameter>
  • Profile: <defined in script: “TenantAdmin”>
  • Members: <none – not part of this example>
  • Scope: <Cloud Name defined with parameter>
  • Quotas for the…: <default “Use Maximum” values from in scope Cloud>
  • Networking: <none – defined later in the process>
  • Resources: <none – defined later in the process>
  • Permissions: <defined in script: @("Author", "AuthorVMNetwork", "CreateFromVHDOrTemplate", "Create", "AllowLocalAdmin", "PauseAndResume", "Remove", "Shutdown", "Start", "Stop")>
  • Run As accounts: <none – not part of this example>
  • Quotas for VM networks: <defined in script: -RemoveVMNetworkMaximumPerUser -RemoveVMNetworkMaximum>

Note     You may keep these example settings, or modify to fit your deployment specifications.

Example PowerShell workflow script for Create-TenantAdminUserRole

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
workflow Create-TenantAdminUserRole
{
    param
    (
    [string]$VmmServerName,
    [string]$User,
    [string]$CloudName
    )


    inlinescript {

        Get-SCVMMServer -ComputerName $Using:VmmServerName | Out-Null

        $UserRole = Get-SCUserRole | where {$_.Name -match $Using:User}

        if (!$UserRole) {
            $Cloud = Get-SCCloud -Name $Using:CloudName
            $UserRoleProfile = "TenantAdmin"
            $UserRolePermissions = @("Author", "AuthorVMNetwork", "CreateFromVHDOrTemplate", "Create", `
                                     "AllowLocalAdmin", "PauseAndResume", "Remove", "Shutdown", "Start", "Stop")

            $NewUserRole = New-SCUserRole -Name $Using:User -UserRoleProfile $UserRoleProfile | `
                    Set-SCUserRole -AddScope $Cloud -Permission $UserRolePermissions -RemoveVMNetworkMaximumPerUser -RemoveVMNetworkMaximum
           
            $NewUserRoleName = "{0}_{1}" -f $NewUserRole.Name, $NewUserRole.ID

            $UserRole = Set-SCUserRole -UserRole $NewUserRole -Name $NewUserRoleName
        }
        Return $UserRole
    }
}

Calling the Create-TenantAdminUserRole PowerShell workflow

You can call the Create-TenantAdminUserRole workflow once per Tenant Admin, like this:

001
002
003
004
005
$VMMServer = "MY_VMM_SERVER"
$User = "tenant01@contoso.com"
$CloudName = "My Tenant Cloud"

Create-TenantAdminUserRole -VmmServerName $VMMServer -User $User -CloudName $CloudName

Or, you can create/leverage an array of Tenant Admins, and call Create-TenantAdminUserRole within a foreach statement, like this:

001
002
003
004
005
006
007
$VMMServer = "MY_VMM_SERVER"
$Users = @("tenant01@contoso.com","tenant02@contoso.com","tenant03@contoso.com")
$CloudName = "My Tenant Cloud"

foreach ($User in $Users) {
    Create-TenantAdminUserRole -VmmServerName $VMMServer -User $User -CloudName $CloudName
}

And, I don’t want to get too crazy, but you could even create a hash table to pass in a list of both UserRoleName and CloudName, like this:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
$VMMServer = "MY_VMM_SERVER"
$UsersAndClouds = @{
                "tenant01@contoso.com" = "Tenant Cloud"
                "tenant02@contoso.com" = "Tenant Cloud"
                "tenant03@contoso.com" = "Gold Cloud"
                "tenant04@contoso.com" = "Silver Cloud"
                "tenant05@contoso.com" = "Bronze Cloud"
}

foreach ($UserAndCloud in $UsersAndClouds.GetEnumerator()) {

    $User = $UserAndCloud.Name
    $CloudName = $UserAndCloud.Value

    Create-TenantAdminUserRole -VmmServerName $VMMServer -User $User -CloudName $CloudName
}

Finally, and one step beyond (I am not even going to show it), you could place the workflow call in another workflow, so you could leverage ForEach -Parallel.

There are lots of options here, choose the one that makes sense for your deployment.


Create a VM Network “OnBehalfOf” that User Role

The following PowerShell workflow script (Create-VMNetwork) will create a VMM VM Network with the following settings:

  • VM Network Name: <VM Network Name generated by Owner User Role Name defined with parameter>
  • Subnet Name: <defined in script: “TenantSubnet”>
  • Subnet Value: <defined in script: “192.168.0.0/24”>
  • IP Address Pool Name: <defined in script: “TenantIPPool”>
  • IP Address Range Start: <defined in script: “192.168.0.100”>
  • IP Address Range End: <defined in script: “192.168.0.199” – providing for 100 available addresses>
  • DNS IP: <defined in script: “192.168.0.100” – first IP in Pool>
  • OnBehalfOfUser: <User Name parsed from Owner User Role Name defined with parameter>
  • OnBehalfOfUserRole: <User Role parsed from Owner User Role Name defined with parameter>

Note     You may keep these example settings, or modify to fit your deployment specifications.

Example PowerShell workflow script for Create-VMNetwork

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
workflow Create-VMNetwork
{
    param
    (
    [string]$OwnerUserRoleName,
    [string]$VmmServerName,
    [string]$CloudName,
    [string]$LogicalNetworkName
    )
 
    inlinescript
    {
        $subnetValue = "192.168.0.0/24"
        $subnetName = "TenantSubnet"
        $dnsIP = "192.168.0.100"
        $ipAdressPoolName = "TenantIPPool"
        $ipAddressRangeStart = "192.168.0.100"
        $ipAddressRangeEnd = "192.168.0.199"
        $UserRole = $Using:OwnerUserRoleName
        $User = $UserRole.Split("_")[0]
        $vmNetworkName = "Tenant Network ($User)"
       
        Get-SCVMMServer -ComputerName $Using:VmmServerName -ForOnBehalfOf | Out-Null
        $OwnerUserRoleObj = Get-SCUserRole | where {$_.Name -match $Using:OwnerUserRoleName}

        $VMNetwork = Get-SCVMNetwork -OnBehalfOfUser $User -OnBehalfOfUserRole $OwnerUserRoleObj
        if(!$VMNetwork) {
            $CloudObj = Get-SCCloud -Name $Using:CloudName
            $logicalNetwork = Get-SCLogicalNetwork -Cloud $CloudObj -Name $Using:LogicalNetworkName
            $vmNetwork = New-SCVMNetwork -Name $vmNetworkName -LogicalNetwork $logicalNetwork `
                -OnBehalfOfUser $User -OnBehalfOfUserRole $OwnerUserRoleObj
       
            $subnet = New-SCSubnetVLan -Subnet $subnetValue 
            $vmSubnet = New-SCVMSubnet -Name $subnetName -VMNetwork $vmNetwork -SubnetVLan $subnet `
                -OnBehalfOfUser $User -OnBehalfOfUserRole $OwnerUserRoleObj

            $allDnsServer = @($dnsIP)

            $staticIPAddressPool = New-SCStaticIPAddressPool -Name $ipAdressPoolName `
                -VMSubnet $vmSubnet -Subnet $subnetValue -IPAddressRangeStart $ipAddressRangeStart `
                -IPAddressRangeEnd $ipAddressRangeEnd -DNSServer $allDnsServer `
                -RunAsynchronously -OnBehalfOfUser $User -OnBehalfOfUserRole $OwnerUserRoleObj
        }
    }
}

Calling the Create-VMNetwork PowerShell workflow

You can call the Create-VMNetwork workflow once per Tenant Admin, like this (leverages the Create-TenantAdminUserRole above):

001
002
003
004
005
006
007
$VMMServer = "MY_VMM_SERVER"
$User = "tenant01@contoso.com"
$CloudName = "My Tenant Cloud"
$LogicalNetworkName = "Contoso Logical Network"

$UserRole = Create-TenantAdminUserRole -VmmServerName $VMMServer -User $User -CloudName $CloudName
Create-VMNetwork -VmmServerName $VMMServer -OwnerUserRoleName $UserRole.Name -CloudName $CloudName -LogicalNetworkName $LogicalNetworkName

I am not going to dive into the details for this example, but obviously you can leverage all the array and hash table stuff mentioned above with this call (as it is a 1:1 Tenant Admin User Role:VMNetwork in this example).

Again, there are lots of options here, choose the one that makes sense for your deployment.


Deploy a Service Template with Parameters “OnBehalfOf” that User Role

The following PowerShell workflow script (Deploy-VMMService) will deploy a VMM Service Template with the following settings:

  • Service Name: <Service Configuration Name generated by Owner User Role Name defined with parameter>
  • Service Template Name: <Service Template Name defined with parameter>
  • Service Template Release: <Service Template Release generated by Owner User Role Name defined with parameter>
  • Service Template Global Settings: <Defined as Hash Table within a sub-workflow (Build-ServiceSettings) - example provided below>
  • Cloud: <Cloud Name defined with parameter>
  • OnBehalfOfUser: <User Name parsed from Owner User Role Name defined with parameter>
  • OnBehalfOfUserRole: <User Role parsed from Owner User Role Name defined with parameter>

Note     The Deploy-VMMService PowerShell workflow script depends on return values from the Build-ServiceSettings PowerShell workflow script.

Example PowerShell workflow script for Build-ServiceSettings

Obviously there are many ways to get/set Global Settings for VMM Service Template Deployment. The following example is just one of them:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
workflow Build-ServiceSettings
{
    param
    (
        [string]$OwnerUserRoleName,
        [string]$VmmServerName,
        [string]$ServiceTemplate
    )
   
    $User = $OwnerUserRoleName.Split("_")[0]
    $SCVMMServer = Get-SCVMMServer -ComputerName $VmmServerName
    $Domain = $User.Split("@")[1]
    $Network = (Get-SCVMNetwork | Where {$_.UserRole.Name -eq $OwnerUserRoleName}).ID.Guid
    $lowerOrg = $Domain.Split(".")[0]
    $Organization = $lowerOrg.Substring(0,1).ToUpper() + $lowerOrg.Substring(1)
   
    if($ServiceTemplate -eq "Active Directory" -or $ServiceTemplate -eq "SharePoint"){
        $globalSettings = @{
            Domain = $Domain
            Network = $Network
         }
    }
   
    if($ServiceTemplate -eq "Exchange Server 2013 Single Server"){
        $globalSettings = @{
            Domain = $Domain
            Organization = $Organization
            Network = $Network
         }
    }

    if($ServiceTemplate -eq "Lync Service Template"){
        $globalSettings = @{
            Network = $Network
         }
    }
    
     Return $globalSettings
}

Note   The above example is the one used in the “Tenant Provisioning POC” my team is running through. In the POC (as you can see from the script), we have four primary types of Service Templates (Active Directory, SharePoint, Exchange, and Lync). The parameters and naming for each Service Template vary to ensure a varied set of test deployments. To keep things simple, we are deriving all the Service Template Global Settings from pre-existing data (like $OwnerUserRoleName and the associated Tenant Network).

Calling the Build-ServiceSettings PowerShell workflow

As mentioned above, the Build-ServiceSettings PowerShell workflow script is called from the Deploy-VMMService PowerShell workflow script. The following is a simple example, but you will see it in action within the script of the Deploy-VMMService PowerShell workflow below:

001
002
003
004
005
$OwnerUserRoleName = "tenant01@contoso.com_d2b3536a-4755-4a1e-a146-f20046581ab5"
$VMMServer = "MY_VMM_SERVER"
$ServiceTemplateName = "Active Directory"

Build-ServiceSettings -OwnerUserRoleName $OwnerUserRoleName -VmmServerName $VMMServer -ServiceTemplate $ServiceTemplateName

Once again, this example is just so you can see what kind of data the Build-ServiceSettings PowerShell workflow script is “expecting”. Leverage this example as desired The way it is leveraged for our POC can be seen in the very next example.

Example PowerShell workflow script for Deploy-VMMService

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
workflow Deploy-VMMService
{
    param
    (
    [string]$VmmServerName,
    [string]$OwnerUserRoleName,
    [string]$CloudName,
    [string]$ServiceTemplateName,
    [string]$ServiceTemplateRelease
    )
   
    $globalSettingsHash = Build-ServiceSettings -OwnerUserRoleName $OwnerUserRoleName -VmmServerName $VmmServerName -ServiceTemplate $ServiceTemplateName

    inlinescript {
        $UserRole = $Using:OwnerUserRoleName
        $User = $UserRole.Split("_")[0]
        $ServiceConfigName = "$Using:ServiceTemplateName ($User)"
        $globalSettings = $Using:globalSettingsHash
       
        Get-SCVMMServer -ComputerName $Using:VmmServerName -ForOnBehalfOf | Out-Null
 
        $OwnerUserRoleObj = Get-SCUserRole | Where {$_.Name -eq $UserRole}
       
        $OriginalServiceTemplateName = $Using:ServiceTemplateName
        $OriginalServiceTemplateRelease = $Using:ServiceTemplateRelease
        $ServiceTemplateToCopy = Get-SCServiceTemplate -Name $OriginalServiceTemplateName | where {$_.Release -eq $OriginalServiceTemplateRelease}
        $ServiceTemplate = New-SCServiceTemplate -Name $OriginalServiceTemplateName -Release $UserRole -ServiceTemplate $ServiceTemplateToCopy
        Grant-SCResource -Resource $ServiceTemplate -UserRoleID $OwnerUserRoleObj.ID
               
        $Cloud = Get-SCCloud -Name $Using:CloudName

        $InitialServiceConfig = New-SCServiceConfiguration -ServiceTemplate $ServiceTemplate -Name $ServiceConfigName -Cloud $Cloud `
            -OnBehalfOfUser $User -OnBehalfOfUserRole $OwnerUserRoleObj
       
        foreach ($key in $($globalSettings.Keys)){
       
            $globalSetting = Get-SCServiceSetting -ServiceConfiguration $InitialServiceConfig -Name $key
            Set-SCServiceSetting -ServiceSetting $globalSetting -Value $globalSettings[$key] `
                -OnBehalfOfUser $User -OnBehalfOfUserRole $OwnerUserRoleObj
        }
       
        New-SCService -ServiceConfiguration $InitialServiceConfig -RunAsynchronously `
            -OnBehalfOfUser $User -OnBehalfOfUserRole $OwnerUserRoleObj
    }
}

Note     Lines 024 – 027 above deal with the dynamic generation of new Service Templates based on an original (known good) Service Template. The original Service Template is queried for by name and release, and the new Service Template (generated specifically for the $OwnerUserRoleName) is created using the same Service Template Name, but a Service Template Release matching the User Role Name (for unique identification and creation). Line 028 above is used to Grant the User Role permissions to this new Service Template. It should be known that the New-SCServiceTemplate command does not take “OnBehalfOf” switches.

Calling the Deploy-VMMService PowerShell workflow

You can call the Deploy-VMMService workflow once per Tenant Admin, like this (leverages both the Create-TenantAdminUserRole and Create-VMNetwork above):

001
002
003
004
005
006
007
008
009
010
$VMMServer = "MY_VMM_SERVER"
$User = "tenant01@contoso.com"
$CloudName = "My Tenant Cloud"
$LogicalNetworkName = "Contoso Logical Network"
$ServiceTemplateName = "Active Directory"
$ServiceTemplateRelease = "1.0.0.0"

$UserRole = Create-TenantAdminUserRole -VmmServerName $VMMServer -User $User -CloudName $CloudName
Create-VMNetwork -VmmServerName $VMMServer -OwnerUserRoleName $UserRole.Name -CloudName $CloudName -LogicalNetworkName $LogicalNetworkName
Deploy-VMMService -VmmServerName $VMMServer -OwnerUserRoleName $UserRole.Name -CloudName $CloudName -ServiceTemplateName $ServiceTemplateName -ServiceTemplateRelease $ServiceTemplateRelease

Note     The $ServiceTemplateName and $ServiceTemplateRelease values would vary based on your Service Template details.

See the following image for some of the Service Templates in our POC environment:

image

And this image is the results of a test of the above PowerShell workflow scripts:

image

And one final image illustrates what the dynamic Service Template creation per User Role looks like:

image


Example Breakdown: The "OnBehalfOf" Concept

There is a ton of information here, I can appreciate that. I hope it was laid out in a way that makes sense, and eases you into the world of "OnBehalfOf".

As a recap, here are the commands where the "OnBehalfOf" concept were covered in the examples provided:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
Get-SCVMMServer -ComputerName $Using:VmmServerName -ForOnBehalfOf | Out-Null

Get-SCVMNetwork
 -OnBehalfOfUser $User -OnBehalfOfUserRole $OwnerUserRoleObj

New-SCVMNetwork -Name $vmNetworkName -LogicalNetwork $logicalNetwork `
                -OnBehalfOfUser $User -OnBehalfOfUserRole $OwnerUserRoleObj

New-SCVMSubnet -Name $subnetName -VMNetwork $vmNetwork -SubnetVLan $subnet `
                -OnBehalfOfUser $User -OnBehalfOfUserRole $OwnerUserRoleObj

New-SCStaticIPAddressPool -Name $ipAdressPoolName `
                -VMSubnet $vmSubnet -Subnet $subnetValue -IPAddressRangeStart $ipAddressRangeStart `
                -IPAddressRangeEnd $ipAddressRangeEnd -DNSServer $allDnsServer `
                -RunAsynchronously -OnBehalfOfUser $User -OnBehalfOfUserRole $OwnerUserRoleObj

New-SCServiceConfiguration -ServiceTemplate $ServiceTemplate -Name $ServiceConfigName -Cloud $Cloud `
            -OnBehalfOfUser $User -OnBehalfOfUserRole $OwnerUserRoleObj

Set-SCServiceSetting -ServiceSetting $globalSetting -Value $globalSettings[$key] `
                -OnBehalfOfUser $User -OnBehalfOfUserRole $OwnerUserRoleObj

New-SCService -ServiceConfiguration $InitialServiceConfig -RunAsynchronously `
            -OnBehalfOfUser $User -OnBehalfOfUserRole $OwnerUserRoleObj

Note     Taking these commands out of context may not make sense - variable usage was based on the examples.


Next Steps

Do you want to take the raw PowerShell that you see here and leverage it in your environment? Would you like to leverage it within an existing Automation tool? Well if your environment includes Windows Azure Pack, Service Management Automation, and/or System Center Orchestrator (or anything else that can orchestrate PowerShell), you are in luck – simply take portions of the above PowerShell workflow script examples you want and start automating!

Well, I guess it is not exactly that easy. But it is close. Here is a brief explanation of how to leverage the PowerShell shown here in each of the aforementioned tools:

  • Windows Azure Pack – Register a Service Management Automation instance and plug the Create-VMNetwork and Deploy-VMMService examples right in!
    • Create-TenantAdminUserRole is not needed within a WAP environment, as User and User Role creation are handled automatically through the relationship between WAP, SPF, and VMM.
  • Service Management Automation– Because SMA leverages PowerShell workflow, these examples are immediately ready to use.
    • Again, while each of the examples are PowerShell workflow, it is up to you which ones make sense for your deployment.
  • System Center Orchestrator – PowerShell workflow is a bit more difficult to leverage in Orchestrator, but it is certainly possible. Either call these as PS1 files with the Run .Net Script or Run Program activities. Alternatively, you could take the script snippets you want to leverage out of PowerShell workflow  and directly into a Run .Net Script activity (likely leveraging Invoke-Command functionality).

Oh, and some more good news, I have it on pretty good authority that there will be another post related to this content coming out very soon, which will illustrate even more use case scenarios…


That’s it - thanks for checking it out!

And for more information, tips/tricks, and example solutions for PowerShell + System Center 2012 R2, be sure to watch for future blog posts in the Automation Track!

enJOY!