Tag Archives: PowerCLI

vSAN Storage Policy Summary using PowerCLI

I recently came across a need to review the Storage Policies in use within a vCenter environment and how many objects or virtual machines where using each policy.

I saw this as an excuse to refresh my PowerShell skills and wrote a quick function.

Source code can be found on my GitHub, here. Check there for any updates but below is the code at the time of writing.

function Get-vSANSPSummary {
    <#
    .SYNOPSIS
        Export vSAN Storage Policy Information.
    .DESCRIPTION
        Export vSAN Storage Policies from vCenter showing FTT & Stripe information and amount of amount of VM's using each.
    .PARAMETER ExportPath
        Path the export the output HTML file.
    .NOTES
        Tags: VMware, vCenter, SPBM, PowerCLI, API
        Author: Stephan McTighe
        Website: stephanmctighe.com
    .EXAMPLE
        PS C:\> Get-vSANSPSummary -ExportPath "C:\report\vSAN-Storage-Policy-Summary.html"
        Outputs a HTML file containing the Storage Policy Information for vSAN Storage Policies to a specified location.

#>
#Requires -Modules VMware.VimAutomation.Storage
    [CmdletBinding()]
    param (        
        [Parameter(Mandatory)]
        [string] $ExportFilePath)

    Begin {}

    Process {
        try {
            $Output = @()
            $vSANstoragepolicies = Get-SpbmStoragePolicy -Namespace "VSAN"
            $SPBM = $vSANstoragepolicies | Select-Object Name, AnyOfRuleSets
            ForEach ($SP in $SPBM) {
                $Attributes = @( $SP | ForEach-Object { $_.AnyOfRuleSets } | Select-Object -ExpandProperty AllofRules)
                $object = [PSCustomObject]@{
                    SPName         = $SP.Name
                    ObjectCount    = $ObjectCount = (Get-SpbmEntityConfiguration -StoragePolicy "$($SP.name)").count
                    VMCount        = $VMCount = (Get-SpbmEntityConfiguration -StoragePolicy "$($SP.Name)" | Where-Object {$_.Entity -notlike "hard*"}).count
                    RAID           = $attributes | Where-Object { $_.Capability -like "*VSAN.replicaPreference*" } | Select-Object -ExpandProperty Value
                    FTT            = $attributes | Where-Object { $_.Capability -like "*VSAN.hostFailuresToTolerate*" } | Select-Object -ExpandProperty Value
                    SubFTT         = $attributes | Where-Object { $_.Capability -like "*VSAN.subFailuresToTolerate*" } | Select-Object -ExpandProperty Value
                    Stripes        = $attributes | Where-Object { $_.Capability -like "*VSAN.stripeWidth*" } | Select-Object -ExpandProperty Value
                    ForceProvision = $attributes | Where-Object { $_.Capability -like "*VSAN.forceProvisioning*" } | Select-Object -ExpandProperty Value
                    StorageType    = $attributes | Where-Object { $_.Capability -like "*VSAN.storageType*" } | Select-Object -ExpandProperty Value
                    IOPSLimit      = $attributes | Where-Object { $_.Capability -like "*VSAN.iopsLimit*" } | Select-Object -ExpandProperty Value
        
                }
                $Output += $object

            }
            $Output | ConvertTo-Html -Property SPName, VMCount, ObjectCount, RAID, FTT, SubFTT, Stripes, ForceProvision, StorageType, IOPSLimit | Out-File $ExportFilePath
        }
        catch {
            Write-Host "An error occurred!" -ForegroundColor Red
            Write-Host $_ -ForegroundColor Red
        }

    }  
}         

Output currently as a basic HTML table but you could change this to add some ‘HTMLness’ or output to CSV.

As always, thanks for reading and I hope this has been useful to someone.

If you like my content, consider following me on Twitter so you don’t miss out!

Different Tools; Same Result – vSphere Tags

Following the last blog post on create vSphere Port Groups, let’s take a look at creating Tags and Tag Categories.

Let’s first look at the process via the GUI, in this case, the vSphere Client. (Based on vSphere 7.0.3c)

vSphere Client

I wont go into to much detail here as this information is readily available, but here is a brief run through.

After logging into the vSphere Client, select the menu followed by Tags & Custom Attributes.

You the have the option to select either Tags or Categories, followed by the ‘New’ option.

For Categories you need to provide the Category name, optional description, the cardinality (single or multiple) and select the objects that can have this tag associated with it.

Then with Tags, you need to provide the name, optional description and the category the tag will be part of.

Now this may be ok for one or two, but if you need to create in bulk, this will take a while! Lets look as some alternatives.

PowerShell

Firstly, PowerShell, specifically the VMware PowerCLI PowerShell module. Here are examples of the using the cmdlets New-TagCategory and New-Tag to create the same thing we did in the vSphere Client.

#Tag Categories
New-TagCategory -Name "costcentre" -Description "Created with PowerCLI" -Cardinality "MULTIPLE" -EntityType "VirtualMachine", "Datastore"
#Tags
New-Tag -Name "0001" -Category "costcentre" -Description "Created with PowerCLI"

Below is the output from PowerShell after running the script above:

Name                                     Cardinality Description
----                                     ----------- -----------
costcentre                               Multiple    Created with PowerCLI

Name                           Category                       Description
----                           --------                       -----------
0001                           costcentre                     Created with PowerCLI

Now this isn’t much quicker than doing it in the vSphere Client so here is one way to create in bulk.

Here is a custom array with multiple categories and the additional values needed to create a Category.

$TagCategories = @(
    [pscustomobject]@{Name = "costcentre"; Cardinality = "MULTIPLE"; EntityType = "VirtualMachine", "Datastore" }
    [pscustomobject]@{Name = "environment"; Cardinality = "SINGLE"; EntityType = "VirtualMachine", "Datastore" }
    [pscustomobject]@{Name = "nsx-tier"; Cardinality = "MULTIPLE"; EntityType = "VirtualMachine" }
)
foreach ($Category in $TagCategories) {
    New-TagCategory -Name $Category.Name -Cardinality $Category.Cardinality -EntityType $Category.EntityType -Description "Created with PowerCLI"
}

Here is the output:

Name                                     Cardinality Description
----                                     ----------- -----------
costcentre                               Multiple    Created with PowerCLI
environment                              Single      Created with PowerCLI
nsx-tier                                 Multiple    Created with PowerCLI

And now the same principal but with Tags.

$Tags = @(
    [pscustomobject]@{Name = "0001"; Category = "costcentre" }
    [pscustomobject]@{Name = "0002"; Category = "costcentre" }
    [pscustomobject]@{Name = "0003"; Category = "costcentre" }
    [pscustomobject]@{Name = "0004"; Category = "costcentre" }
    [pscustomobject]@{Name = "environment"; Category = "environment" }
    [pscustomobject]@{Name = "production"; Category = "environment" }
    [pscustomobject]@{Name = "pre-production"; Category = "environment" }
    [pscustomobject]@{Name = "test"; Category = "environment" }
    [pscustomobject]@{Name = "development"; Category = "environment" }
    [pscustomobject]@{Name = "web"; Category = "nsx-tier" }
    [pscustomobject]@{Name = "app"; Category = "nsx-tier" }
    [pscustomobject]@{Name = "data"; Category = "nsx-tier" }
)
foreach ($Tag in $Tags) {
    New-Tag -Name $Tag.Name -Category $Tag.Category -Description "Created with PowerCLI"
}

Output:

Name                           Category                       Description
----                           --------                       -----------
0001                           costcentre                     Created with PowerCLI
0002                           costcentre                     Created with PowerCLI
0003                           costcentre                     Created with PowerCLI
0004                           costcentre                     Created with PowerCLI
environment                    environment                    Created with PowerCLI
production                     environment                    Created with PowerCLI
pre-production                 environment                    Created with PowerCLI
test                           environment                    Created with PowerCLI
development                    environment                    Created with PowerCLI
web                            nsx-tier                       Created with PowerCLI
app                            nsx-tier                       Created with PowerCLI
data                           nsx-tier                       Created with PowerCLI

That is just one way to create multiple Categories and Tags. You could take this information from a CSV file using the ‘Get-Content’ cmdlet as an alternative to creating the array manually.

Terraform

Now let’s take a look at using Terraform to achieve the same result. Terraform is an infrastructure and code tool used to manage infrastructure in the form of configuration files and state:

#Providers
provider "vsphere" {
  vsphere_server       = "vcsa-fqdn"
  user                 = "domain\\user"
  password             = "password"
  allow_unverified_ssl = false
}
#Tag categories
resource "vsphere_tag_category" "costcentre" {
  name        = "costcentre"
  description = "Managed by Terraform"
  cardinality = "MULTIPLE"
  associable_types = [
    "VirtualMachine",
    "Datastore",
  ]
}
resource "vsphere_tag_category" "environment" {
  name        = "environment"
  description = "Managed by Terraform"
  cardinality = "SINGLE"
  associable_types = [
    "VirtualMachine",
    "Datastore",
  ]
}
resource "vsphere_tag_category" "nsx-tier" {
  name        = "nsx-tier"
  description = "Managed by Terraform"
  cardinality = "MULTIPLE"
  associable_types = [
    "VirtualMachine"
  ]
}
#Tags
#Local values
locals {
  costcentre_tags  = ["0001", "0002", "0003", "0004"]
  environment_tags = ["production", "pre-production", "test", "development"]
  nsx_tier_tags    = ["web", "app", "data"]
}
#Resources
resource "vsphere_tag" "costcentre-tags" {
  for_each    = toset(local.costcentre_tags)
  name        = each.key
  category_id = vsphere_tag_category.costcentre.id
  description = "Managed by Terraform"
}
resource "vsphere_tag" "environment-tags" {
  for_each    = toset(local.environment_tags)
  name        = each.key
  category_id = vsphere_tag_category.environment.id
  description = "Managed by Terraform"
}
resource "vsphere_tag" "nsx-tier-tags" {
  for_each    = toset(local.nsx_tier_tags)
  name        = each.key
  category_id = vsphere_tag_category.nsx-tier.id
  description = "Managed by Terraform"
}

Lets break this down.

First we are specifying which terraform provider we want to use, this will be the vSphere provider in this case. We are then providing some parameters for the provider to connect to your vCenter instance; VCSA FQDN and credentials. You would want make use of variables for this data, but for this blog I am keeping it simple.

provider "vsphere" {
  vsphere_server       = "vcsa-fqdn"
  user                 = "domain\\user"
  password             = "password"
  allow_unverified_ssl = false
}

We then have three vsphere_tag_category resource blocks, one for each of the categories we want to create. This again provides values for cardinality and associable types like we did in PowerShell.

resource "vsphere_tag_category" "costcentre" {
  name        = "costcentre"
  description = "Managed by Terraform"
  cardinality = "MULTIPLE"
  associable_types = [
    "VirtualMachine",
    "Datastore",
  ]
}
resource "vsphere_tag_category" "environment" {
  name        = "environment"
  description = "Managed by Terraform"
  cardinality = "SINGLE"
  associable_types = [
    "VirtualMachine",
    "Datastore",
  ]
}
resource "vsphere_tag_category" "nsx-tier" {
  name        = "nsx-tier"
  description = "Managed by Terraform"
  cardinality = "MULTIPLE"
  associable_types = [
    "VirtualMachine"
  ]
}

Next we are going to create the tags, but I am going to use a set of local variables to then pass into the three vsphere_tag resource blocks to reduce the amount of repeating code.

Here are the local variables. This is similar to creating the array we did in PowerShell.

locals {
  costcentre_tags  = ["0001", "0002", "0003", "0004"]
  environment_tags = ["production", "pre-production", "test", "development"]
  nsx_tier_tags    = ["web", "app", "data"]
}

And then the resource blocks, notice the for_each parameter. For each Tag Category, it will cycle through each value in the locals array for each category. This is just like we did in PowerShell foreach function earlier.

resource "vsphere_tag" "costcentre-tags" {
  for_each    = toset(local.costcentre_tags)
  name        = each.key
  category_id = vsphere_tag_category.costcentre.id
  description = "Managed by Terraform"
}
resource "vsphere_tag" "environment-tags" {
  for_each    = toset(local.environment_tags)
  name        = each.key
  category_id = vsphere_tag_category.environment.id
  description = "Managed by Terraform"
}
resource "vsphere_tag" "nsx-tier-tags" {
  for_each    = toset(local.nsx_tier_tags)
  name        = each.key
  category_id = vsphere_tag_category.nsx-tier.id
  description = "Managed by Terraform"
}

Now when we run ‘terraform apply’ from the command line to apply for code, this is the output:

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
    
Terraform will perform the following actions:

  # vsphere_tag.costcentre-tags["0001"] will be created
  + resource "vsphere_tag" "costcentre-tags" {
      + category_id = (known after apply)
      + description = "Managed by Terraform"
      + id          = (known after apply)
      + name        = "0001"
    }

  # vsphere_tag.costcentre-tags["0002"] will be created
  + resource "vsphere_tag" "costcentre-tags" {
      + category_id = (known after apply)
      + description = "Managed by Terraform"
      + id          = (known after apply)
      + name        = "0002"
    }

  # vsphere_tag.costcentre-tags["0003"] will be created
  + resource "vsphere_tag" "costcentre-tags" {
      + category_id = (known after apply)
      + description = "Managed by Terraform"
      + id          = (known after apply)
      + name        = "0003"
    }

  # vsphere_tag.costcentre-tags["0004"] will be created
  + resource "vsphere_tag" "costcentre-tags" {
      + category_id = (known after apply)
      + description = "Managed by Terraform"
      + id          = (known after apply)
      + name        = "0004"
    }

  # vsphere_tag.environment-tags["development"] will be created
  + resource "vsphere_tag" "environment-tags" {
      + category_id = (known after apply)
      + description = "Managed by Terraform"
      + id          = (known after apply)
      + name        = "development"
    }

  # vsphere_tag.environment-tags["pre-production"] will be created
  + resource "vsphere_tag" "environment-tags" {
      + category_id = (known after apply)
      + description = "Managed by Terraform"
      + id          = (known after apply)
      + name        = "pre-production"
    }

  # vsphere_tag.environment-tags["production"] will be created
  + resource "vsphere_tag" "environment-tags" {
      + category_id = (known after apply)
      + description = "Managed by Terraform"
      + id          = (known after apply)
      + name        = "production"
    }

  # vsphere_tag.environment-tags["test"] will be created
  + resource "vsphere_tag" "environment-tags" {
      + category_id = (known after apply)
      + description = "Managed by Terraform"
      + id          = (known after apply)
      + name        = "test"
    }

  # vsphere_tag.nsx-tier-tags["app"] will be created
  + resource "vsphere_tag" "nsx-tier-tags" {
      + category_id = (known after apply)
      + description = "Managed by Terraform"
      + id          = (known after apply)
      + name        = "app"
    }

  # vsphere_tag.nsx-tier-tags["data"] will be created
  + resource "vsphere_tag" "nsx-tier-tags" {
      + category_id = (known after apply)
      + description = "Managed by Terraform"
      + id          = (known after apply)
      + name        = "data"
    }

  # vsphere_tag.nsx-tier-tags["web"] will be created
  + resource "vsphere_tag" "nsx-tier-tags" {
      + category_id = (known after apply)
      + description = "Managed by Terraform"
      + id          = (known after apply)
      + name        = "web"
    }

  # vsphere_tag_category.costcentre will be created
  + resource "vsphere_tag_category" "costcentre" {
      + associable_types = [
          + "Datastore",
          + "VirtualMachine",
        ]
      + cardinality      = "MULTIPLE"
      + description      = "Managed by Terraform"
      + id               = (known after apply)
      + name             = "costcentre"
    }

  # vsphere_tag_category.environment will be created
  + resource "vsphere_tag_category" "environment" {
      + associable_types = [
          + "Datastore",
          + "VirtualMachine",
        ]
vsphere_tag.environment-tags["production"]: Creating...
vsphere_tag.environment-tags["pre-production"]: Creating...
vsphere_tag_category.nsx-tier: Creation complete after 0s [id=urn:vmomi:InventoryServiceCategory:20a2167a-b0f8-4a60-9d29-6c7ca57711ef:GLOBAL]
vsphere_tag.nsx-tier-tags["data"]: Creating...
vsphere_tag.nsx-tier-tags["app"]: Creating...
vsphere_tag.nsx-tier-tags["web"]: Creating...
vsphere_tag_category.costcentre: Creation complete after 0s [id=urn:vmomi:InventoryServiceCategory:28a909f5-ee41-4d94-b228-b5e96e09284e:GLOBAL]
vsphere_tag.costcentre-tags["0004"]: Creating...
vsphere_tag.costcentre-tags["0002"]: Creating...
vsphere_tag.costcentre-tags["0003"]: Creating...
vsphere_tag.environment-tags["development"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:5b63e350-ef6e-4bbc-a633-09c9047b327b:GLOBAL]
vsphere_tag.costcentre-tags["0001"]: Creating...
vsphere_tag.environment-tags["pre-production"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:e2a8737c-e42a-4c6f-b9a8-716a1681d0c0:GLOBAL]
vsphere_tag.nsx-tier-tags["data"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:b9d3394d-388c-4018-b7b2-9e4d3da8287b:GLOBAL]
vsphere_tag.costcentre-tags["0002"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:8a482528-5d67-40e9-86cb-4dbf566f85ac:GLOBAL]
vsphere_tag.nsx-tier-tags["web"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:5a325904-4dfd-46ac-b0db-37fd6fda1533:GLOBAL]
vsphere_tag.environment-tags["production"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:89c609b9-7f90-457d-9f71-0bd0b7cc667d:GLOBAL]
vsphere_tag.nsx-tier-tags["app"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:45c2dd0e-533a-4917-82be-987d3245137a:GLOBAL]
vsphere_tag.costcentre-tags["0004"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:230db56e-7352-4e14-ba63-0ad4b4c0ba18:GLOBAL]
vsphere_tag.environment-tags["test"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:ebcf1809-8cae-4cb2-a5fa-82a492e54227:GLOBAL]
vsphere_tag.costcentre-tags["0001"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:e4649ad2-08d2-4dcd-aabf-4e2d74f93a36:GLOBAL]
vsphere_tag.costcentre-tags["0003"]: Creation complete after 0s [id=urn:vmomi:InventoryServiceTag:18de9eca-456c-4539-ad6c-19d625ac5be7:GLOBAL]

Apply complete! Resources: 14 added, 0 changed, 0 destroyed.

For more information on the vSphere provider from Terraform, check out this link.

I hope this has given you some idea’s on how you can perhaps leverage other options beside the GUI, especially when looking to build or configure in bulk. All the code in this blog can be found on my GitHub here.

Thanks for reading!

vCenter Server Profiles

Whilst beginning preparations to take the VMware Certificated Advanced Professional Deploy exam, I have started to look into the features and topics that I’m not overly familiar with. To help with the learning process, I am going to be deploying and configuring these features, and writing blogs on many of the topics to help cement the information. Hopefully these will be useful for anyone else learning or researching these topics!

The first one; vCenter Server Profiles!

Background

vCenter Server Profiles (Section 7.2.4 Configure VMware vCenter Server® profiles in the Exam Blueprint) were first introduced in vSphere 7.0. These addressed the challenges around configuration consistency in large multi vCenter environments by allowing a ‘base’ or ‘source’ vCenter configuration to be exported, modified and imported into multiple other vCenter’s.

Not only does this help ensure a consistent configuration for things like NTP, Syslog, SSH and security settings on the appliance, but also Roles and Users can be copied to all vCenter servers quickly and easily. The Roles and Users can be a great help if you have a role on all vCenter servers for things like backup solutions or templating.

The profile or configuration is exported as a JSON file, therefore this easily allows you to store this as a source control tool such as GitHub to enable you to track and review changes to the configuration. This can be really useful when it comes to larger teams with many updates happening.

The export and import process is all done via API’s. More on that later, first lets take a look at this in the form of a high level diagram!

API’s

As mentioned earlier, vCenter Server Profiles are currently managed via API’s, not the GUI. They can be found in the Developer Center within vCenter itself as you can see below:

There are 5 API’s in total I am going to be working with as part of this blog:

  • Acquiring a SessionID token – Required to authenticate.
  • Getting a list of profiles.
  • Exporting a profile.
  • Validating a profile.
  • Importing a profile.

I will be using PowerShell to make the API calls, lets take a look at the code to do each step as well as the output.

Acquiring a SessionID Token

There are a few variable values you will need to fill out here; Username, Password and the FQDN of the source vCenter Server.

#Authentication
$User = "administrator@vsphere.local"
$Pass = "SecurePassword!"
$Auth = $User + ":" + $Pass
$Encoded = [System.Text.Encoding]::UTF8.GetBytes($Auth)
$EncodedAuth = [System.Convert]::ToBase64String($Encoded)
$Headers = @{"Authorization" = "Basic $($EncodedAuth)" }
$sourcevcenter = "vm-vcsa-01.smt.com"
#Get Session ID
$Session = Invoke-RestMethod -Method POST -Uri "https://$($sourcevcenter)/rest/com/vmware/cis/session" -Headers $Headers
$SessionID = $Session.Value
$SessionID

Now when you run this, a value is outputted. This is what we will use to authenticate the other API calls.

d84b95e370f1ed68b997f4affbe6feba

Listing Profiles

Now lets look at listing the available profiles. You will notice the output may remind you of the diagram above.

$SessionHeaders = @{'vmware-api-session-id' = "$($SessionID)"
}
Invoke-RestMethod -Method GET -Uri "https://$($sourcevcenter)/api/appliance/infraprofile/configs" -Headers $SessionHeaders

Note the SessionID variable being used here. This is the value from the previous step.

name                info
----                ----
ApplianceManagement Appliance Mangment Service
ApplianceNetwork    Appliance Network Configuration
AuthManagement      Authentication & Authorization Management

Exporting a Profile

Now to export the profile. As mentioned earlier, the configuration is outputted to a JSON file to a path of your choosing.

$SessionHeaders = @{
    "vmware-api-session-id" = "$($SessionID)"
    "Content-type"          = "application/json"
}
$Export = Invoke-RestMethod -Method POST -Uri "https://$($sourcevcenter)/api/appliance/infraprofile/configs?action=export" -Headers $SessionHeaders
$Export | Out-File "C:\temp\vcenter-profile-export.json"

Here is a trimmed look at the content of the file:

{"action":"RESTART_SERVICE","productName":"VMware vCenter Server","creationTime":"2021-12-30T18:12:42+0000","version":"7.0.3.00100","profiles":{"ApplianceNetwork":{"description":"Appliance Network Configuration","action":"RESTART_SERVICE","actionOn":{"VC_SERVICES":["applmgmt"],"SYSTEMD":["systemd-networkd","systemd-resolved"]},"version":"7.0","config":{"/etc/sysconfig/proxy":{"HTTPS PROXY":"\"\"","SOCKS PROXY":"\"\"","FTP PROXY":"\"\"","GOPHER PROXY":"\"\"","PROXY ENABLED":"\"no\"","SOCKS5 SERVER":"\"\"","HTTP PROXY":"\"\"","NO PROXY":["\"localhost","127.0.0.1\""]},"/etc/systemd/resolved.conf":{"Fallback DNS":null,"LLMNR is enabled":"false","DNS":"127.0.0.1 10.200.15.1"}},"name":"ApplianceNetwork"},"AuthManagement":{"description":"Authentication & Authorization Management","action":"NO_ACTION","version":"1.0","config":{"Lockout Policy":{"Maximum number of failed login attempts":5,"Time interval between failures":180,"Unlock time":300},"Password Policy":{"Minimum special characters":1,"Minimum alphabetic characters":2,"Minimum uppercase characters":1,"Minimum lowercase characters":1,"Minimum numeric characters":1,"Minimum adjacent identical characters":3,"Previous password reuse restriction":5,"Maximum lifetime":90,"Maximum length":20,"Minimum length":8},"Token Policy":{"Clock tolerance ms":600000,"Maximum token renewal count":10,"Maximum token delegation count":10,"Maximum Bearer RefreshToken lifetime":21600000,"Maximum HoK RefreshToken lifetime":2592000000}
...
Trimmed to save the scrolling...
...
{"principal":{"name":"VSPHERE.LOCAL\\NsxAuditors","group":true},"roles":[741131114],"propagate":true},{"principal":{"name":"VSPHERE.LOCAL\\NsxViAdministrators","group":true},"roles":[-2094871953],"propagate":true},{"principal":{"name":"VSPHERE.LOCAL\\NsxAdministrators","group":true},"roles":[-1723127349],"propagate":true},{"principal":{"name":"VSPHERE.LOCAL\\RegistryAdministrators","group":true},"roles":[1006],"propagate":true},{"principal":{"name":"SMT.COM\\stephan","group":false},"roles":[-1],"propagate":true},{"principal":{"name":"VSPHERE.LOCAL\\vStatsGroup","group":true},"roles":[-292639496],"propagate":true}]},"name":"AuthManagement"},"ApplianceManagement":{"description":"Appliance Mangment Service","action":"RESTART_SERVICE","actionOn":{"VC_SERVICES":["applmgmt"],"SYSTEMD":["sendmail","rsyslog"]},"version":"7.0","config":{"/etc/applmgmt/appliance/appliance.conf":{"Is shell Enabled":false,"Shell Expiration Time":null,"TimeSync Mode (Host/NTP)":"NTP"},"/etc/sysconfig/clock":{"Time zone":"\"Europe/London\"","UTC":"1"},"/usr/bin/systemctl/sshd.service":{"Enable SSH":"true"},"/etc/ntp.conf":{"Time servers":["uk.pool.ntp.org"]},"/etc/mail/sendmail.cf":{"SMTP Port":null,"Mail server":null},"/etc/vmware-syslog/syslog.conf":{"Port [2]":null,"Port [1]":null,"Port [0]":null,"Protocol [2]":null,"Remote Syslog Host [1]":null,"Protocol [1]":null,"Remote Syslog Host [0]":null,"Protocol [0]":null,"Remote Syslog Host [2]":null},"/etc/pam.d/system-auth":{"Deny Login after these many Unsuccessful Attempts.":"3","Unlock root after (seconds)":"300","On Error Login will be.":"fail","Include Root user for SSH lockout.":true,"Unlock user after (seconds)":"900"},"/etc/shadow":{"root":{"maximumDays":"","warningDays":"7"},"bin":{"maximumDays":"90","warningDays":"7"},"daemon":{"maximumDays":"90","warningDays":"7"},"messagebus":{"maximumDays":"90","warningDays":"7"},"systemd-bus-proxy":{"maximumDays":"90","warningDays":"7"},"systemd-journal-gateway":{"maximumDays":"90","warningDays":"7"},"systemd-journal-remote":{"maximumDays":"90","warningDays":"7"},"systemd-journal-upload":{"maximumDays":"90","warningDays":"7"},"systemd-network":{"maximumDays":"90","warningDays":"7"},"systemd-resolve":{"maximumDays":"90","warningDays":"7"},"systemd-timesync":{"maximumDays":"90","warningDays":"7"},"nobody":{"maximumDays":"90","warningDays":"7"},"rpc":{"maximumDays":"90","warningDays":"7"},"ntp":{"maximumDays":"90","warningDays":"7"},"sshd":{"maximumDays":"90","warningDays":"7"},"smmsp":{"maximumDays":"90","warningDays":"7"},"apache":{"maximumDays":"90","warningDays":"7"},"sso-user":{"maximumDays":"90","warningDays":"7"},"vpostgres":{"maximumDays":"","warningDays":"7"},"vapiEndpoint":{"maximumDays":"90","warningDays":"7"},"eam":{"maximumDays":"90","warningDays":"7"},"vlcm":{"maximumDays":"90","warningDays":"7"},"vsan-health":{"maximumDays":"90","warningDays":"7"},"vsm":{"maximumDays":"90","warningDays":"7"},"vsphere-ui":{"maximumDays":"90","warningDays":"7"},"wcp":{"maximumDays":"","warningDays":"7"},"content-library":{"maximumDays":"90","warningDays":"7"},"imagebuilder":{"maximumDays":"90","warningDays":"7"},"perfcharts":{"maximumDays":"90","warningDays":"7"},"vpgmonusr":{"maximumDays":"","warningDays":"7"},"vtsdbmonusr":{"maximumDays":"","warningDays":"7"},"Send Waring before this No of Days.":null,"Password validity (days)":null}},"name":"ApplianceManagement"}}}

At this point you can modify this file as needed. For example, you may need to modify the DNS configuration for a group of vCenter Servers to use a different one than that of the source vCenter, or you may want to remove all but the Appliance configuration.

Validating a Profile

Next we are looking at validating the profile against the target/remote vCenter that you want to apply it to. Be sure to get a session ID for the target vCenter Server to pass into this command!

$destinationvcenter = "vm-vcsa-02.smt.com"
$SessionHeaders = @{
    "vmware-api-session-id" = "$($SessionID)"
    "Content-type"          = "application/json"
}
$body = Convertto-json @{
    'config_spec' = Get-Content "C:\temp\vcenter-profile-export.json"
}
$validate = Invoke-RestMethod -Method POST -Uri "https://$($destinationvcenter)/api/appliance/infraprofile/configs?action=validate&vmw-task=true" -Headers $SessionHeaders -Body $body
$validate

The below output confirms the file is good to go.

912f7205-2e8f-429f-8b86-9610e5eac8f4:com.vmware.appliance.infraprofile.configs

Importing a Profile

Now to the good bit, importing the config! Like before, make sure to get a session ID for the target vCenter Server to pass into this command!

$destinationvcenter = "vm-vcsa-02.smt.com"
$SessionHeaders = @{
    "vmware-api-session-id" = "$($SessionID)"
    "Content-type"          = "application/json"
}
$body = @{
    'config_spec' = Get-Content "C:\temp\vcenter-profile-export.json"
}
$Import = Invoke-RestMethod -Method POST -Uri "https://$($destinationvcenter)/api/appliance/infraprofile/configs?action=import&vmw-task=true" -Headers $SessionHeaders -Body (Convertto-json $body)
$Import

Before:

Output from running the import commands:

d843c731-c631-4b9b-87fe-4894134f433c:com.vmware.appliance.infraprofile.configs

After:

PowerShell Functions

Now to make this a bit easier (and to practice my PowerShell Function skills), I have made 5 PowerShell Functions that can be used. The code for each can be found here.

Get-vCenterAPISessionID

Get-vCenterAPISessionID -vCenterFQDN vm-vcsa-02.smt.com -UserName administrator@vsphere.local -Password SecurePassword!
9ee52fe13c7ae8d42f777cadccf6b70d

Get-vCenterProfiles

Get-vCenterProfiles -vCenterFQDN vm-vcsa-01.smt.com -SessionID 9ee52fe13c7ae8d42f777cadccf6b70d
name                info
----                ----
ApplianceManagement ApplianceManagement
ApplianceNetwork    ApplianceNetwork
AuthManagement      Authentication & Authorization Management

Export-vCenterProfiles

Export-vCenterProfiles -vCenterFQDN vm-vcsa-02.smt.com -SessionID 9ee52fe13c7ae8d42f777cadccf6b70d -ExportPath C:\temp

Validate-vCenterProfiles

Validate-vCenterProfiles -vCenterFQDN vm-vcsa-03.smt.com -SessionID 2b3fdd91604f67d124af041a23b46a1a -jsonPath C:\temp
21bfd471-95e3-48d3-841d-8452f2a09527:com.vmware.appliance.infraprofile.configs

Import-vCenterProfiles

Import-vCenterProfiles -vCenterFQDN vm-vcsa-03.smt.com -SessionID 2b3fdd91604f67d124af041a23b46a1a -jsonPath C:\temp
4a742b45-c52e-4aa9-a67b-fe588084f02c:com.vmware.appliance.infraprofile.configs

Log File location on the vCenter Server: /var/log/vmware/infraprofile/infraprofile-svcs.log. This is were you need to be looking when troubleshooting!

Here’s a snippet:

2022-01-01T14:31:30.911Z [Thread-45 [] INFO  com.vmware.appliance.infraprofilev1.core.ProfileOperations  opId=] Complete importProfile ApplianceManagement
2022-01-01T14:31:31.072Z [Thread-45 [] INFO  com.vmware.appliance.infraprofilev1.plugins.ApplianceManagementPlugin  opId=] Start importing non generic format file /usr/bin/systemctl/sshd.service
2022-01-01T14:31:31.074Z [Thread-45 [] INFO  com.vmware.appliance.infraprofilev1.util.MiscUtils  opId=] Performing unmask operation on following System services: [[sshd.service]]
2022-01-01T14:31:31.439Z [Thread-45 [] INFO  com.vmware.appliance.infraprofilev1.util.MiscUtils  opId=] Performing enable operation on following System services: [[sshd.service]]
2022-01-01T14:31:31.864Z [Thread-45 [] INFO  com.vmware.appliance.infraprofilev1.util.MiscUtils  opId=] Performing start operation on following System services: [[sshd.service]]
2022-01-01T14:31:31.877Z [Thread-45 [] INFO  com.vmware.appliance.infraprofilev1.plugins.ApplianceManagementPlugin  opId=] Complete importing non generic format file /usr/bin/systemctl/sshd.service
2022-01-01T14:31:31.877Z [Thread-45 [] INFO  com.vmware.appliance.infraprofilev1.plugins.ApplianceManagementPlugin  opId=] Start importing non generic format file /etc/ntp.conf
2022-01-01T14:31:31.878Z [Thread-45 [] INFO  com.vmware.appliance.infraprofilev1.plugins.ApplianceManagementPlugin  opId=] Complete importing non generic format file /etc/ntp.conf
2022-01-01T14:31:31.878Z [Thread-45 [] INFO  com.vmware.appliance.infraprofilev1.plugins.ApplianceManagementPlugin  opId=] Start importing non generic format file /etc/mail/sendmail.cf
2022-01-01T14:31:31.882Z [Thread-45 [] INFO  com.vmware.appliance.infraprofilev1.plugins.ApplianceManagementPlugin  opId=] Complete importing non generic format file /etc/mail/sendmail.cf
2022-01-01T14:31:31.882Z [Thread-45 [] INFO  com.vmware.appliance.infraprofilev1.plugins.ApplianceManagementPlugin  opId=] Start importing non generic format file /etc/vmware-syslog/syslog.conf
2022-01-01T14:31:32.963Z [Thread-45 [] INFO  com.vmware.appliance.infraprofilev1.plugins.ApplianceManagementPlugin  opId=] Complete importing non generic format file /etc/vmware-syslog/syslog.conf

Thanks for reading!

Different Tools; Same Result – vSphere Distributed Port Groups

As technology moves forward, more and more ways to achieve your goal become available. Many people still rely on the good old trusty GUI to achieve their goal, I know I do at times. Is this because it’s quicker, more comfortable or familiar? Or perhaps because they don’t realise there are other options out there!?

This blog post will be one of many, where I highlight some of the options available for completing various technical tasks or configurations, in the hope it can provide additional options or tools for consideration.

To kick off, let’s take a look at a common example for a vSphere Administrator, creating Port Groups on a Distributed Switch.

vSphere Client

So let’s first look at the process via the GUI, in this case, the vSphere Client. I wont go into too much detail on the steps involved, as it is a well documented process, but the screenshots are below:

Repeat for the remaining Port Groups and you will be left with the finished article.

And there we have it, three Port Groups on a distributed Switch. Now, imagine doing this for 10’s or 100’s of Port Groups? It’s going to be slow and painful, so let’s look at some other options.

PowerShell

Firstly, PowerShell, specifically the VMware PowerCLI PowerShell module. Here is an example script that will create the same three Port Groups that we did using the GUI:

$vDSName = "vDS-Workload-Networks"
$Ports = "8"
$LoadBalancing = "LoadBalanceLoadBased" 
$ActiveUP = "Uplink 1", "Uplink 2"

$vdpgs = @(
    [pscustomobject]@{PG = 'dvPG-Guest-VM-1'; VLANID = '20'}
    [pscustomobject]@{PG = 'dvPG-Guest-VM-2'; VLANID = '21'}
    [pscustomobject]@{PG = 'dvPG-Secure-VM-1'; VLANID = '25'}
)

#Create Distributed Virtual Port Group.
ForEach ($vdpg in $vdpgs) {
    Get-VDSwitch -Name $vDSName | New-VDPortGroup -Name $VDPG.PG -VLanId $VDPG.VLANID -NumPorts $Ports
    #Set Load Balancing option
    Get-VDswitch -Name $vDSName | Get-VDPortgroup $VDPG.PG | Get-VDUplinkTeamingPolicy | Set-VDUplinkTeamingPolicy -LoadBalancingPolicy $LoadBalancing -ActiveUplinkPort $ActiveUP
}

So lets break down this code. Firstly we are defining some variables;

  • $vDSName – This is the name of an existing virtual distributed switch in which you will be creating your Port Groups.
  • $Ports – This defines the number of ports the Port Group will be initially configured with. (By default 128 ports are created, there is nothing wrong with using the default, see the note further down as to why I have specified 8.)
  • $LoadBalancing – This is the load balancing policy I wish to set for the Port Group. Available options are:LoadBalanceLoadBased, LoadBalanceIP, LoadBalanceSrcMac, LoadBalanceSrcId, ExplicitFailover. This can be adjusted as required.
  • $ActiveUP – This variable defines the uplinks you wish to set as active for the Port Group. (If you want to add standby uplinks, you could add this parameter in too)
  • $VDPGS – Finally, this is an array containing both the name and VLAN ID for each Port Group.

Now we have our input information in variables, we move onto the next two lines of code. These are within a ‘ForEach Loop’. This will take each entry within an array and run a block of code against it. In this case, each Port Group we wish to create.

So for each entry in the array, ‘Get-VDswitch -Name $vDSName‘ gets the existing Virtual Distributed Switch based on the variable and then pipes (‘|’) this into the command (New-VDPortGroup -Name $VDPG.PG -VLanId $VDPG.VLANID -NumPorts $Ports) to create the Port Group on the Distributed Switch, using the properties set for each line of the array.

Secondly, we get the Port Group we just created (Get-VDswitch -Name $vDSName | Get-VDPortgroup $VDPG.PG) and then ‘Get & Set’ the Teaming and Loadbalancing options (Get-VDUplinkTeamingPolicy | Set-VDUplinkTeamingPolicy -LoadBalancingPolicy $LoadBalancing -ActiveUplinkPort $ActiveUP), again ‘piping’ the results into the next command.

Below is the output from PowerShell after running the script above:

Name                           NumPorts PortBinding
----                           -------- -----------
dvPG-Guest-VM-1                8        Static

VDPortgroup                      : dvPG-Guest-VM-1
NotifySwitches                   : True
NotifySwitchesInherited          : True
LoadBalancingPolicy              : LoadBalanceLoadBased
LoadBalancingPolicyInherited     : False
FailoverDetectionPolicy          : LinkStatus
ActiveUplinkPort                 : {Uplink 1, Uplink 2}
StandbyUplinkPort                : {}
UplinkPortOrderInherited         : False
Failback                         : False
EnableFailback                   : True
FailbackInherited                : True
UnusedUplinkPort                 : {}
FailoverDetectionPolicyInherited : True
Uid                              : /VIServer=vsphere.local\administrator@vm-vcsa-01.smt-lab.local:443/VDPortgroupUplinkTeamingPolicy=cec49f0b7f124d0c9f37814392494a31/

dvPG-Guest-VM-2                8        Static

VDPortgroup                      : dvPG-Guest-VM-2
NotifySwitches                   : True
NotifySwitchesInherited          : True
LoadBalancingPolicy              : LoadBalanceLoadBased
LoadBalancingPolicyInherited     : False
FailoverDetectionPolicy          : LinkStatus
ActiveUplinkPort                 : {Uplink 1, Uplink 2}
StandbyUplinkPort                : {}
UplinkPortOrderInherited         : False
Failback                         : False
EnableFailback                   : True
FailbackInherited                : True
UnusedUplinkPort                 : {}
FailoverDetectionPolicyInherited : True
Uid                              : /VIServer=vsphere.local\administrator@vm-vcsa-01.smt-lab.local:443/VDPortgroupUplinkTeamingPolicy=e126093ed67f45a3b7c42874c5affc20/

dvPG-Secure-VM-1               8        Static

VDPortgroup                      : dvPG-Secure-VM-1
NotifySwitches                   : True
NotifySwitchesInherited          : True
LoadBalancingPolicy              : LoadBalanceLoadBased
LoadBalancingPolicyInherited     : False
FailoverDetectionPolicy          : LinkStatus
ActiveUplinkPort                 : {Uplink 1, Uplink 2}
StandbyUplinkPort                : {}
UplinkPortOrderInherited         : False
Failback                         : False
EnableFailback                   : True
FailbackInherited                : True
UnusedUplinkPort                 : {}
FailoverDetectionPolicyInherited : True
Uid                              : /VIServer=vsphere.local\administrator@vm-vcsa-01.smt-lab.local:443/VDPortgroupUplinkTeamingPolicy=b5f1889461584b1daf314379cd935f50/

Terraform

Now let’s take a look at using Terraform to achieve the same result. Terraform is an infrastructure and code tool used to manage infrastructure in the form of configuration files and state:

provider "vsphere" {
  vsphere_server = "vCenter Server FQDN"
  user           = "Domain\\Username"
  password       = "Password"
}
data "vsphere_datacenter" "datacenter" {
  name = "dc-smt-01"
}
data "vsphere_distributed_virtual_switch" "vds" {
  name          = "vDS-Workload-Networks"
  datacenter_id = data.vsphere_datacenter.datacenter.id
}
resource "vsphere_distributed_port_group" "pg20" {
  name                            = "dvPG-Guest-VM-1"
  distributed_virtual_switch_uuid = data.vsphere_distributed_virtual_switch.vds.id
  number_of_ports                 = 8
  vlan_id                         = 20
}
resource "vsphere_distributed_port_group" "pg21" {
  name                            = "dvPG-Guest-VM-2"
  distributed_virtual_switch_uuid = data.vsphere_distributed_virtual_switch.vds.id
  number_of_ports                 = 8
  vlan_id                         = 21
}
resource "vsphere_distributed_port_group" "pg25" {
  name                            = "dvPG-Secure-VM-1"
  distributed_virtual_switch_uuid = data.vsphere_distributed_virtual_switch.vds.id
  number_of_ports                 = 8
  vlan_id                         = 25
}

Lets break this down.

First we are specifying which terraform provider we want to use, this will be the vSphere provider in this case. We are then providing some parameters for Terraform to connect to your vCenter instance; VCSA FQDN and credentials.

We then have two ‘data’ blocks. These are used to get information about an existing resource, such as the Distributed Switch and the Datacenter it resides in. You could loosely consider this similar to populating variables in the PowerShell example.

Next we have three ‘resource’ blocks. Each block represents one of the three Port Groups we want to configure. It provides parameters for Name, number of ports and vlan ID for each, along with a reference to the Distributed Switch from the ‘data’ block.

Now when you run ‘terraform apply’ to apply for code, here is the output:

terraform apply  


Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # vsphere_distributed_port_group.pg20 will be created
  + resource "vsphere_distributed_port_group" "pg20" {
      + active_uplinks                    = (known after apply)
      + allow_forged_transmits            = (known after apply)
      + allow_mac_changes                 = (known after apply)
      + allow_promiscuous                 = (known after apply)
      + auto_expand                       = true
      + block_all_ports                   = (known after apply)
      + check_beacon                      = (known after apply)
      + config_version                    = (known after apply)
      + directpath_gen2_allowed           = (known after apply)
      + distributed_virtual_switch_uuid   = "50 33 5e 01 05 1e 32 66-ea f7 7c 42 ce fa f1 96"
      + egress_shaping_average_bandwidth  = (known after apply)
      + egress_shaping_burst_size         = (known after apply)
      + egress_shaping_enabled            = (known after apply)
      + egress_shaping_peak_bandwidth     = (known after apply)
      + failback                          = (known after apply)
      + id                                = (known after apply)
      + ingress_shaping_average_bandwidth = (known after apply)
      + ingress_shaping_burst_size        = (known after apply)
      + ingress_shaping_enabled           = (known after apply)
      + ingress_shaping_peak_bandwidth    = (known after apply)
      + key                               = (known after apply)
      + lacp_enabled                      = (known after apply)
      + lacp_mode                         = (known after apply)
      + name                              = "dvPG-Guest-VM-1"
      + netflow_enabled                   = (known after apply)
      + network_resource_pool_key         = "-1"
      + notify_switches                   = (known after apply)
      + number_of_ports                   = 8
      + port_private_secondary_vlan_id    = (known after apply)
      + standby_uplinks                   = (known after apply)
      + teaming_policy                    = (known after apply)
      + tx_uplink                         = (known after apply)
      + type                              = "earlyBinding"
      + vlan_id                           = 20

      + vlan_range {
          + max_vlan = (known after apply)
          + min_vlan = (known after apply)
        }
    }

  # vsphere_distributed_port_group.pg21 will be created
  + resource "vsphere_distributed_port_group" "pg21" {
      + active_uplinks                    = (known after apply)
      + allow_forged_transmits            = (known after apply)
      + allow_mac_changes                 = (known after apply)
      + allow_promiscuous                 = (known after apply)
      + auto_expand                       = true
      + block_all_ports                   = (known after apply)
      + check_beacon                      = (known after apply)
      + config_version                    = (known after apply)
      + directpath_gen2_allowed           = (known after apply)
      + distributed_virtual_switch_uuid   = "50 33 5e 01 05 1e 32 66-ea f7 7c 42 ce fa f1 96"
      + egress_shaping_average_bandwidth  = (known after apply)
      + egress_shaping_burst_size         = (known after apply)
      + egress_shaping_enabled            = (known after apply)
      + egress_shaping_peak_bandwidth     = (known after apply)
      + failback                          = (known after apply)
      + id                                = (known after apply)
      + ingress_shaping_average_bandwidth = (known after apply)
      + ingress_shaping_burst_size        = (known after apply)
      + ingress_shaping_enabled           = (known after apply)
      + ingress_shaping_peak_bandwidth    = (known after apply)
      + key                               = (known after apply)
      + lacp_enabled                      = (known after apply)
      + lacp_mode                         = (known after apply)
      + name                              = "dvPG-Guest-VM-2"
      + netflow_enabled                   = (known after apply)
      + network_resource_pool_key         = "-1"
      + notify_switches                   = (known after apply)
      + number_of_ports                   = 8
      + port_private_secondary_vlan_id    = (known after apply)
      + standby_uplinks                   = (known after apply)
      + teaming_policy                    = (known after apply)
      + tx_uplink                         = (known after apply)
      + type                              = "earlyBinding"
      + vlan_id                           = 21

      + vlan_range {
          + max_vlan = (known after apply)
          + min_vlan = (known after apply)
        }
    }

  # vsphere_distributed_port_group.pg25 will be created
  + resource "vsphere_distributed_port_group" "pg25" {
      + active_uplinks                    = (known after apply)
      + allow_forged_transmits            = (known after apply)
      + allow_mac_changes                 = (known after apply)
      + allow_promiscuous                 = (known after apply)
      + auto_expand                       = true
      + block_all_ports                   = (known after apply)
      + check_beacon                      = (known after apply)
      + config_version                    = (known after apply)
      + directpath_gen2_allowed           = (known after apply)
      + distributed_virtual_switch_uuid   = "50 33 5e 01 05 1e 32 66-ea f7 7c 42 ce fa f1 96"
      + egress_shaping_average_bandwidth  = (known after apply)
      + egress_shaping_burst_size         = (known after apply)
      + egress_shaping_enabled            = (known after apply)
      + egress_shaping_peak_bandwidth     = (known after apply)
      + failback                          = (known after apply)
      + id                                = (known after apply)
      + ingress_shaping_average_bandwidth = (known after apply)
      + ingress_shaping_burst_size        = (known after apply)
      + ingress_shaping_enabled           = (known after apply)
      + ingress_shaping_peak_bandwidth    = (known after apply)
      + key                               = (known after apply)
      + lacp_enabled                      = (known after apply)
      + lacp_mode                         = (known after apply)
      + name                              = "dvPG-Secure-VM-1"
      + netflow_enabled                   = (known after apply)
      + network_resource_pool_key         = "-1"
      + notify_switches                   = (known after apply)
      + number_of_ports                   = 8
      + port_private_secondary_vlan_id    = (known after apply)
      + standby_uplinks                   = (known after apply)
      + teaming_policy                    = (known after apply)
      + tx_uplink                         = (known after apply)
      + type                              = "earlyBinding"
      + vlan_id                           = 25

      + vlan_range {
          + max_vlan = (known after apply)
          + min_vlan = (known after apply)
        }
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

vsphere_distributed_port_group.pg20: Creating...
vsphere_distributed_port_group.pg21: Creating...
vsphere_distributed_port_group.pg25: Creating...
vsphere_distributed_port_group.pg25: Creation complete after 0s [id=dvportgroup-2669728]
vsphere_distributed_port_group.pg21: Creation complete after 0s [id=dvportgroup-2669730]
vsphere_distributed_port_group.pg20: Creation complete after 0s [id=dvportgroup-2669729]

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

For more information on the vSphere provider from Terraform, check out this link.

You will have noticed that I have explicitly defined the number of ports in both the PowerShell and Terraform examples. This is purely to match up with the default value that is set when using the vSphere Client; 8. By default the port allocation automatically expands as required, so this is for consistency rather than anything else.

If you are someone who relies heavily on a GUI as part of your work, I hope this have given you some idea’s on how you can perhaps leverage other options, especially when looking to build or configure in bulk.

Thanks for reading!

Get, Set and Remove Perennial Reservations Using PowerShell Functions

Having recently had to do some work with RDM perennial reservations I looked into ways to make this less of a manual headache. There are plenty of examples out there for doing this, which I took as a basis to make a PowerShell function. If anything it was a great way to refresh my PowerShell skills and an opportunity to learn some new skills.

Note: Although this has been tested in my environment, please make sure you test it appropriately before running against a production environment!

Lets take a look…

Get-PerennialReservation

This function targets a vSphere cluster, gets all RDM disks that are connected to VM’s and then queries each host in the cluster to check if the disk/storage device is perennially reserved or not.

You can find the code here.

There are multiple ways to use it, whether that is by specifying the target cluster using the -Cluster parameter or by piping it from Get-Cluster. You can also specify a specific canonical name or a comma separated string of them, if you just want the status of a single/select disk(s) using the -CanonicalName parameter. There is also an Export flag to export the results to CSV, if you wish to make use of the data outside of PowerShell. You can get the full usage information by running the following command once you have loaded the function into your PowerShell session:

Get-Help Get-PerennialReservation -full

Here it is in action:

Get-PerennialReservation -Cluster smt-lab-cl-mn-01
Get-Cluster | Get-PerennialReservation
Get-PerennialReservation -Cluster smt-lab-cl-mn-01 -CanonicalName naa.60003ff44dc75adcacba077cf38ccc60
Get-PerennialReservation -Cluster smt-lab-cl-mn-01 -ExportPath C:\temp

Set-PerennialReservation

This function again targets a vSphere cluster, gets all RDM disks that are connected to VM’s and sets the IsPerenniallyReserved flag too ‘True’ on all hosts.

You can find the code here.

There are multiple ways to use it like the Get function; specifying the target cluster using the -Cluster paramater or by piping it from Get-Cluster. You can still specify a specific canonical name or a comma separated string of them, if you just want to set the flag of a single/select disk(s) using the -CanonicalName parameter. There is still an Export function that will provide you an output to CSV. You can get the full usage information by running the following command once you have loaded the function into your PowerShell session:

Get-Help Set-PerennialReservation -full

Here it is in action:

Set-PerennialReservation -Cluster smt-lab-cl-mn-01
Get-Cluster | Set-PerennialReservation
Set-PerennialReservation -Cluster smt-lab-cl-mn-01 -CanonicalName naa.60003ff44dc75adc87371e49e5b78222
Set-PerennialReservation -Cluster smt-lab-cl-mn-01 -ExportPath C:\Temp\

Remove-PerennialReservation

To complete the set there is a Remove function. This function again targets a vSphere cluster, but this time you need to pass in the canonical name you wish to set the IsPerenniallyReserved flag too ‘False’ for.

You can find the code here.

To use this one, you need to specify the target cluster using the -Cluster paramater and specify a specific canonical name or a comma separated string of them, using the -CanonicalName parameter. There is still an Export function that will provide you an output to CSV. You can get the full usage information by running the following command once you have loaded the function into your PowerShell session:

Get-Help Remove-PerennialReservation -full

Here it is in action:

Remove-PerennialReservation -Cluster smt-lab-cl-mn-01 -CanonicalName naa.60003ff44dc75adc87371e49e5b78222
Remove-PerennialReservation -Cluster smt-lab-cl-mn-01 -CanonicalName naa.60003ff44dc75adcacba077cf38ccc60, naa.60003ff44dc75adcadc3f2be374bf90a
Remove-PerennialReservation -Cluster smt-lab-cl-mn-01 -CanonicalName naa.60003ff44dc75adc87371e49e5b78222, naa.60003ff44dc75adcacba077cf38ccc60, naa.60003ff44dc75adcadc3f2be374bf90a -Exportpath C:\Temp\

I hope this is of use to folks out there. There may be some updates/improvements added in the future so keep an eye on my GitHub for any updates!

I aim to bundle these, and other functions into a module in the near future!

As always, thanks for reading!

Administering ESXi Hosts With ESXCLI using PowerCLI

There are times as a vSphere admin, you are going to want to run ESXCLI commands against multiple ESXi Hosts from a central location. This could be for configuration / administration, reporting, patching or a number of other things.

Recently I have been testing different values in the /DataMover/MaxHWTransferSize advanced setting. To make life easier, I wanted a way to change multiple hosts quickly and easily. To do this, I customised a script that Luc Dekens posted as a solution to a problem someone was having that can be used to send ESXCLI commands to multiple hosts using PowerCLI and plink.exe. This slightly modified version uses a CSV file as a source containing my hosts FQDN and the username and password I will be connecting with.

Plink, which is part of the PuTTy suite, can can be found here.

When using this script, you need to either run the script from a directory containing the plink executable, copy it to where you want to run the script, or adjust the script to include the path to the plink executable… whichever takes your fancy.

Disclaimer: Always complete your own testing in an appropriate environment and refer to the vendors official documentation!

$Hosts = Import-Csv C:\ESXiHosts.csv
$Commad = 'esxcfg-advcfg -s 16384 /DataMover/MaxHWTransferSize'

Foreach ($H in $Hosts) {
    #Starting the SSH Service if not already started
    $SSHService = Get-VMHostService -VMHost $H.HostName | where {$_.Key -eq 'TSM-SSH'}
    if ($SSHService.Running -eq 'True') {
        Write-Host "****************************" -ForegroundColor Blue
        Write-Host "WARNING: SSH already enabled, this will be stopped on completion of this script" -ForegroundColor Yellow
    }
        Else {

            Write-Host "Starting SSH Service on Host $($H.HostName)" -ForegroundColor Green
            Start-VMHostService -HostService $SSHService -Confirm:$false > $null
        }
    #Running the defined ESXCLI Command(s)
    Write-host "Running remote SSH commands on $($H.HostName)." -ForegroundColor Green
    Echo Y | ./plink.exe $H.HostName -pw $H.Password -l $H.UserName $Commad
    
    #Stopping the SSH Service
    $SSHService = Get-VMHostService -VMHost $H.HostName | where {$_.Key -eq 'TSM-SSH'}
    if ($SSHService.Running) {
        Write-Host "Stopping SSH Service on Host $($H.HostName)" -ForegroundColor Green
        Stop-VMHostService -HostService $SSHService -Confirm:$false > $null
        Write-Host "****************************" -ForegroundColor Blue
    }
}
Write-Host "Complete $(Get-Date)" -ForegroundColor Green

You can run as many commands as you need by declaring another ‘Command’ variable at the beginning of the script and adding another line to the ‘Running the defined ESXCLI Command(s)’ section.

When run, it will then cycle through each of the ESXi hosts from your CSV file, enable SSH (if its not already enabled), accept the host key, run the commands you have specified and finally turn the SSH service off.

Here you can see it has set the MaxHWTransferSize to 16384 on each host.

You will see the Recent Task pane show the SSH Service starts and stops.

The commands passed in can be anything you need. All you need to do is change the commands that are defined in the variables section. For example, restarting the management agents –

$commad = 'etc/init.d/hostd restart'
$commad2 = 'etc/init.d/vpxa restart'

I hope this has been of use for anyone needing a centralised, quick way to administer multiple hosts via ESXCLI.

Thanks for reading!

Using Tags To Automate The Assigning Of vCenter Object Permissions

Tags are a really useful component in VMware. They can be used for all manor of things, whether it’s for storage policies, backups, identifying a group of objects or in the case of this post, managing permissions.

Having a method of easily assigning permissions to singular or multiple objects in vCenter can be a great benefit to a vSphere Admin as it’s gives them greater control over the environment they manage.

Lets take a look at what is needed to get this setup:

  • Script
  • Tag Category & Tags for each support role.
  • AD Security Groups
  • AD Service Account
  • vCenter Roles (one for the service account, then one for each of the support roles)
  • PowerCLI VICredentials
  • Scheduled Task

In this example I will use 4 common support teams that could be used, DBA, EUC, Operations and Storage. These can be anything you have a requirement for.

Script

Here is the script that applies the permissions based on the assigned tags. It can also be found here on GitHub. Save this on your management server of choice, or wherever you intend to run the scheduled task as a .PS1 file. In this example it’s saved on a management server in C:\Scripts\VI_Permissions.ps1.

#Load PowerCLI Modules
Import-module VMware.PowerCLI

#Get the Credentials
$creds = Get-VICredentialStoreItem -file  C:\Scripts\VM_Tagging_Perms.creds
 
#Connect to vCenter
Connect-VIServer -Server $creds.host -User $creds.User -Password $creds.Password -Force

#Tags
$dbaT = "Support Team/DBA_Team"
$storT = "Support Team/Storage_Team"
$eucT = "Support Team/EUC_Team"
$operT = "Support Team/Operations_Team"

#Active Directory Groups
$dbaG = "smt-lab\dba_admins"
$storG ="smt-lab\storage_admins"
$eucG = "smt-lab\euc_admins"
$OperG = "smt-lab\operations_users"

#Roles
$dbaR = "DBA VM Administrator"
$storR = "Storage VM Administrator"
$eucR = "End User VM Administrator"
$OperR = "Operations Users"


$VMs = Get-VM

ForEach ($VM in $VMs) {

        $TAGS = Get-TagAssignment -Entity $VM | Select @{l='SupportTeam';e={('{0}/{1}' -f $_.tag.category, $_.tag.name)}}, Entity

                                If ($TAGS.SupportTeam -contains $dbaT)  {New-VIPermission -Principal $dbaG -Role $dbaR -Entity $vm.name} Else {Get-VIPermission -Entity $vm.Name -Principal $dbaG | Remove-VIPermission -Confirm:$false}
                                If ($TAGS.SupportTeam -contains $storT) {New-VIPermission -Principal $storG -Role $storR -Entity $vm.Name} Else {Get-VIPermission -Entity $vm.Name -Principal $storG | Remove-VIPermission -Confirm:$false}
                                If ($TAGS.SupportTeam -contains $eucT) {New-VIPermission -Principal $eucG -Role $eucR -Entity $vm.Name}  Else {Get-VIPermission -Entity $vm.Name -Principal $eucG | Remove-VIPermission -Confirm:$false}
                                If ($TAGS.SupportTeam -contains $operT) {New-VIPermission -Principal $OperG -Role $OperR -Entity $vm.Name}  Else {Get-VIPermission -Entity $vm.Name -Principal $OperG | Remove-VIPermission -Confirm:$false}
                        }

Tag Category & Tags

Now onto Tag Categories and Tags in vCenter. Create a Tag category called ‘Support _Teams’ (Or something of your choosing, just make sure you are consistent throughout):

Or using PowerShell – New-TagCategory -Name Support_Teams -Cardinality Multiple -EntityType All

You can select as many object types as you wish and you will also want to allow multiple tags per object.

Now create a tag for each of the support teams in the tag category you just created:

New-Tag -Name Storage_Team -Category "Support_Teams"
New-Tag -Name DBA_Team -Category "Support_Teams"
New-Tag -Name EUC_Team -Category "Support_Teams"
New-Tag -Name Operations_Team -Category "Support_Teams"
Storage_Team 
DBA—Team 
EUC_Team 
Operations _ Team 
Support Team 
Support Team 
Support Team 
Support Team

Create AD groups

Now for some corresponding AD Security Group for each role you wish to have:

Name 
dba admins 
operation 
storage_ad, 
Type 
Security Group 
Security Group... 
Security Group, 
Security Group

Service Account (AD User)

Now to create an AD user account that will be used to apply the permissions within vCenter. This will be the account that will be used to run the scheduled task, connect to vCenter and will have the appropriate permissions to assign permissions for the support roles.

Name 
Type 
tag_permissions User

Support Team Roles

Now we need to create a suitable role for each team.  In this example I have copied the Virtual Machine Power User role, but these roles can contain which ever privilege’s you require.

Under ‘Administration > Roles’ you will see the options to either create a new Role or copy an existing.  From here you will be able to assign it a name and specify the privilege’s you require.

Roles 
Roles provider: 
Admlnlstrator 
Read-only 
No access 
AppdAppllenceUser 
AutoUpdateUser 
VSPHERE.LOCAL v 
DESCRIPTION 
Datastore 
USAGE 
PRIVILEGES 
Content llbrery edmlnlstretor (sample) 
Content Llbrery Reglsry admlnlstrator (sample) 
Datastore consumer (sample) 
DBA VM Admlnlstretor 
End User VM Admlnlstretor 
Network admlnlstretor (sample) 
No cryptography admlnlstrator 
No Trusted Infrastructure admlnlstretor 
NSX Admlnlstretor 
NSX Auditor 
NSX VI Admlnlstretor 
Operatlons Users 
Resource pool admlnlstretor (sample) 
SRM Admlnlstrator 
42 items 
Browse datastore 
Global 
Cancel task 
Scheduled task 
Create tasks 
• Modify task 
• Remove task 
• Run task 
Virtual machine 
Change Configuration 
Acquire disk lease 
Add existing disk 
Add new disk 
Add or remove device 
Advanced configuration 
Change CPU count 
Change Memory 
Change Settings 
Change resource 
o Modify device settings 
Remove disk

You will be referencing these Role names in the script so make sure you continue to match the names thought the process.

Permissioning Role

As mentioned in the service account section, the account (tag_permissions) running the scheduled task will need permissions in vCenter through a role.  The privileges this role will hold, needs to include all the privilege’s that are referenced in all of your Support Team Roles in order for it to have the right to assign the permissions. For example, if all your support roles are a copy of the ‘Virtual Machine power user’ role, your tagging permissions role will need to contain the same privileges.

Depending on how broad the scope of your support team roles, you may want to use the ‘Administrator’ or the ‘No cryptography administrator’ role.  This is entirely up to you and how you manage your estate.

For this example in my lab, I will use the predefined ‘Administrator’ role to grant the ‘tag_permissions’ AD account permissions at the Global Root, ensuring you have selected the ‘Propagate to children’ option.

Change Role 
Domain 
User/Group 
Role 
Global Permission Root 
SMT-LABLOCAL 
tag_permissions 
Administrator 
Propagate to children 
CANCEL

You could create a copy of the ‘Administrator’ role and name it something like ‘VI Permissions Service’ for instance, to give you flexibility to modify it in the future as well as making it easy to identify. With any high privileged account, ensure you secure it appropriately.

Create VI Credential Item

Now to create an encrypted credentials file that the service account running the scheduled task can import and then use to connect to vCenter without any intervention.

The AD account that is used to run the scheduled task, must be the account that also creates the credentials file as this is the only user that can use it.  It will require permissions to run PowerShell and have access to a folder location to store the credentials file on your chosen management server.

To begin, start a PowerShell session in the context of the service account:

Windows Security 
Run as different user 
Please enter credentials to use for 
Domain: smt-lab 
OK 
Cancel

Note: Ensure the server that you are running this scheduled task from has PowerCLI installed.  Installing PowerCLI.

Then run the following, entering your vCenter FQDN and the user and password that you created:

New-VICredentialStoreItem -host "smt-lab-vcsa-01.smt-lab.local" -user "smt-lab\tag_permissions" -password "VMware123!" -file C:\Scripts\VM_Tagging_Perms.creds
PS C: 
_Perms . creds 
New—VIC redenti al Storeltem 
C: \ Scri pts ng 
-host 
smt—lab. local" -user 
"smt—l ssion 
s" -password 
"VMware123! " 
Hos t 
User 
smt—1ab—vcsa—01 s 
smt—l . 
C: \ Scri ng_Perms . creds

Ensure you are storing the file somewhere with appropriate access to allow this but, also to restrict any unnecessary access.  The credentials file can have the password read if the user account that created it is compromised and gains access to the file using those windows credentials.

Scheduled Task

Now for the last component, the scheduled task. On a management server or a server of your choosing, create a scheduled task:

Vl_permissions Properties (Local Computer) 
General Triggers Actions Conditions Settings 
History 
Location: 
Author: 
VI Permissions 
SMT- LA8\Administrator 
Description: 
Security options 
When running the task, use the following user account: 
SMT- LA rmissio ns 
C) Run only when user is logged on 
@ Run whether user is logged on or not 
Change User or Group... 
[3 Do not store password. The task will only have access to local computer resources. 
Run with highest privileges 
Hidden 
Configure for: 
Windows Vista" , Windows Server" 2008 
Cancel

Assign an appropriate schedule that suits the level of change and size of your environment:

Vl_permissions Properties (Local Computer) 
General Triggers Actions Conditions Settings 
History 
Location: 
Author: 
VI Permissions 
SMT- LA8\Administrator 
Description: 
Security options 
When running the task, use the following user account: 
SMT- LA rmissio ns 
C) Run only when user is logged on 
@ Run whether user is logged on or not 
Change User or Group... 
[3 Do not store password. The task will only have access to local computer resources. 
Run with highest privileges 
Hidden 
Configure for: 
Windows Vista" , Windows Server" 2008 
Cancel

Now configure the trigger to execute the script:

(9 
Create Task 
General Triggers Actions Conditions Settings 
When you create a task, you must specify the action that will occur when your task starts. 
Action 
Start a program 
New... 
Details 
powershell -File 
Edit... 
Delete

Now thats everything you need to set this up, so lets give it a run though!

Assigning Tags and Permissions

Lets take a look at my demo VM permissions before we begin assigning permissions:

Lets check the VM permissions before having any tags assigned:

Get-TagAssignment -Entity $VM
$VM | ForEach-Object {Get-VIPermission -Entity $_ | Where {$_.Principal -like "*smt-lab*"} | Select Principal, Role}

Note that the tag_permissions account has been propagated from the root permissions you set earlier.

—TagAssignment 
-Entity 
ps Get 
ForEach—Object {Get—VIPermission 
principal 
Role 
SMT-LAB\tag_ 
permissions Admin 
—Entity 
Where 
principal 
—like 
Select principal, 
Role} 
SMT-LAB\Stephan 
Admin

Now assign a tag or two from the ones you created earlier using ‘New-TagAssignment’:

New-TagAssignment -Tag DBA_Team -Entity $VM
New-TagAssignment -Tag Operations_Team -Entity $VM
DBA_Team 
Tag 
Support 
Tag 
Support 
New—TagAssignment —Tag 
Team/DBA_Team 
New—TagAssignment —Tag 
Team/Operations_Team 
-Entity $VM 
Entity 
tfdemol 
Operations_Team —Entity $VM 
Entity 
tfdemol

Now you can either manually run the scheduled task or wait until its next scheduled run time.  Once the job has run, you can now check the tags match the permissions assigned by running the following:

Get-TagAssignment -Entity $VM
$VM | ForEach-Object {Get-VIPermission -Entity $_ | Where {$_.Principal -like "smt-lab"} | Select Principal, Role}
—Entity 
Tag 
Support 
Support 
Get—TagAssignment 
Team/DBA_Team 
Team/Operations_Team 
ForEach—Object {Get—VIPermission 
Entity 
tfdemol 
tfdemol 
—Entity 
Where 
principal 
—like 
Select principal, 
Role} 
principal 
SMT 
SMT 
SMT 
SMT 
—LAB\dba_admins 
—LAB\operations_users 
—LAB\tag_permissions 
—LAB\Stephan 
Role 
DBA VM Administrator 
Operations Users 
Admin 
Admin

You will see that the two tags assigned align with the the two AD groups being granted the corresponding role.

Now let do this for multiple VM’s:

Here I have multiple VM’s in the variable ‘$VM’ and I am assigning two tags to each of them.

$VM | ForEach-Object {New-TagAssignment -Tag DBA_Team -Entity $_}
$VM | ForEach-Object {New-TagAssignment -Tag Storage_Team -Entity $_}
DBA_Team 
Tag 
Support 
Support 
Support 
Support 
Support 
Tag 
Support 
Support 
Support 
Support 
Support 
—Object 
ForEach 
Team/DBA_Team 
Team/DBA_Team 
Team/DBA_Team 
Team/DBA_Team 
Team/DBA_Team 
—Object 
ForEach 
Team/Storage_Team 
Team/Storage_Team 
Team/Storage_Team 
Team/Storage_Team 
Team/Storage_Team 
{New—TagAssignment —Tag 
Entity 
Demopho€€6 
Demophoe€5 
Demopho€€2 
Demopho€€u 
Demophoe€3 
—Entity $_} 
{New—TagAssignment —Tag 
Storage_Team 
Entity 
Demopho€€6 
Demopho€€5 
Demopho€€2 
Demopho€€u 
Demopho€€3 
—Entity $_}

You can now see the tags assigned:

$VM | ForEach-Object {Get-TagAssignment -Entity $_}
{Get—TagAssignment 
Entity 
Tag 
Support 
Support 
Support 
Support 
Support 
Support 
Support 
Support 
Support 
Support 
ForEach—Object 
Team/Storage_Team 
Team/DBA_Team 
Team/Storage_Team 
Team/DBA_Team 
Team/Storage_Team 
Team/DBA_Team 
Team/Storage_Team 
Team/DBA_Team 
Team/Storage_Team 
Team/DBA_Team 
—Entity $_} 
Demophoe€6 
Demopho€€6 
Demopho€€5 
Demophoe€5 
Demopho€€2 
Demopho€€2 
Demophoeeu 
Demopho€€u 
Demophoe€3 
Demophoe€3

Following the script / job being run:

 $VM | ForEach-Object {Get-VIPermission -Entity $_ | Where {$_.Principal -like "*smt-lab*"} | Select Principal, Role} 
ForEach 
principal 
—Object {Get—VIPermission 
Role 
DBA VM Administrator 
—Entity 
Where 
principal 
—like 
Select principal, 
Role} 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
SMT 
—LAB\dba_admins 
—LAB\storage_admins 
—LAB\tag_permissions 
—LAB\Stephan 
—LAB\dba_admins 
—LAB\storage_admins 
—LAB\tag_permissions 
—LAB\Stephan 
—LAB\dba_admins 
—LAB\storage_admins 
—LAB\tag_permissions 
—LAB\Stephan 
—LAB\dba_admins 
—LAB\storage_admins 
—LAB\tag_permissions 
—LAB\Stephan 
—LAB\dba_admins 
—LAB\storage_admins 
—LAB\tag_permissions 
—LAB\Stephan 
Storage VM Administrator 
Admin 
Admin 
DBA VM Administrator 
Storage VM Administrator 
Admin 
Admin 
DBA VM Administrator 
Storage VM Administrator 
Admin 
Admin 
DBA VM Administrator 
Storage VM Administrator 
Admin 
Admin 
DBA VM Administrator 
Storage VM Administrator 
Admin 
Admin

As in the singular example, you will see that the two tags assigned, align with the the two AD groups being granted the corresponding role.

Removing Tags and Permissions

Now lets look at removing permissions, in this case, the Operations Team permissions from a VM:

Get-TagAssignment -Entity $VM | Where {$_.Tag -like "*Operations*"} | Remove-TagAssignment -Confirm:$false

Leaving it with just the ‘DBA_Team’ Tag assigned:

Once the script has run:

$VM | ForEach-Object {Get-VIPermission -Entity $_ | Where {$_.Principal -like "*smt-lab*"} | Select Principal, Role}

Reviewing Permissions

Finally, if you want to know which objects are supported by a specific team and have access you can check this by running:

Get-TagAssignment | Where {$_.Tag -like "Support_Teams/DBA_Team"}

You now have a way of assigning and removing permissions from vCenter objects using Tags. In this example I have used virtual machine object, but depending on your requirements, and the scope you set on the tag category, you could use this for other vCenter objects.

Thanks for reading!

Configuring ESXi for iSCSI Storage Using PowerCLI

Configuring host VMKernel adapters for iSCSI can be a time consuming process. PowerCLI can take away a lot if not all of the effort.

Below is an example of using PowerCLI to create a Standard Virtual Switch (vSS), configure a VMKernel adapter, set the VLAN, enable the software iSCSI adapter (if that’s what you are using), bind it to the required network adapter and finally, add a dynamic Discovery target and rescanning the HBA’s.

This is based on targeting a single host at a time and re-running it with the paramaters for each host.

#Load PowerCLI Modules
Import-module VMware.PowerCLI

#Variables
#vCenter or Host to Connect to 
$vCenter = "smt-lab-vcsa-01.smt-lab.local" 
#ESX Host to target
$ESXHost = Get-VMHost "smt-lab-esx-01.smt-lab.local"
#Name of the iSCSI Switch
$iSCSISwitchName = "vSS_Storage_iSCSI"
#vmnic to be used for iSCSI Switch
$iSCSISwitchNIC = "vmnic2"
#MTU size
$MTU = "9000"
#Name of the Portgroup for the VMKernel Adapter
$iSCSIVMKPortGroupName = "vSS_VMK_iSCSI_A"
#iSCSI VMK IP
$iSCSIIP = "10.200.33.50"
#iSCSI VMK SubnetMask
$iSCSISubnetMask = "255.255.255.0"
#iSCSI VMK VLAN ID
$VLANID = "300"
#iSCSI Portal Target
$Target = "10.200.33.1:3260"

#Connect to vCenter
Connect-VIServer $vCenter -Credential (Get-Credential) -Force

#New Standard Switch for iSCSI
$NewSwitch = New-VirtualSwitch -VMHost $ESXHost -Name $iSCSISwitchName -Nic $iSCSISwitchNIC -Mtu $MTU
$NewPortGroup = New-VMHostNetworkAdapter -VMhost $ESXHost -PortGroup $iSCSIVMKPortGroupName -VirtualSwitch $NewSwitch -IP $iSCSIIP -SubnetMask $iSCSISubnetMask -Mtu $MTU
Set-VirtualPortGroup -VirtualPortGroup (Get-virtualPortGroup -VMhost $ESXHost | Where {$_.Name -eq $iSCSIVMKPortGroupName}) -VLanId $VLANID

#Enable Software iSCSI Adapter
Get-VMHostStorage -VMHost $ESXHost | Set-VMHostStorage -SoftwareIScsiEnabled $True

#Bind the iSCSI VMKernel Adapter to Software iSCSI Adapter (credit to Luc Dekens for this)
$esxcli = Get-EsxCli -V2 -VMHost $ESXHost
$bind = @{
    adapter = ($iscsiHBA = $ESXHost | Get-VMHostHba -Type iScsi | Where {$_.Model -eq "iSCSI Software Adapter"}).Device
    force = $true
    nic = $NewPortGroup.Name
}
$esxcli.iscsi.networkportal.add.Invoke($bind)

#Add Dynamic Discovery Target
$ESXHost | Get-VMHostHba $iscsiHBA | New-IScsiHbaTarget -Address $Target

#Rescan Hba
Get-VMHostStorage -VMHost $ESXHost -RescanAllHba

The results –

v Physical Adapters 
vmnic210000 Full 
VLAN ID: 300 
v VMkernel Ports (1) 
vmk3 :
Ad apter 
Model: 'SCSI Software Adepter 
e vmhbe65 
Type 
'SCSI 
Sta tus 
Onllne
Properties 
Devices 
Paths 
Dynamic Discovery 
Static Discovery 
VMkemeI Adapter 
vmk3 
Network Port Binding 
Addm X Remove Vlew Details 
Port Group 
Advanced Options 
Port Group Policy 
Compllant 
Path Status 
Actlve 
Physical Network Adapter 
vmn.c2 (10 Gblt's, Full)
Properties 
Devices 
Paths 
Dynamic Discovery 
Advanced___ 
Static Discovery 
Network Port Binding 
Advanced Options 
Addm X Remove 
iSCSI server

Something you may also want to do is set the Path Selection Policy (PSP) to the commonly used; ‘Round Robin’. The first command will provide a list of attached storage, showing you the CanonicalName (which is what you need for the second command), the current Multipathing Policy and the size of the storage device.

Identify the device you wish to set the pathing policy on and substitute the Canonical Name (naa.xxxx) into the second command.

#Get storage
$esxhost | Get-ScsiLun -LunType disk | Select CanonicalName,MultipathPolicy, CapacityGB

#Set Path Selection Policy (PSP)
$esxhost | Get-ScsiLun -LunType disk -CanonicalName naa.6589cfc0000004ac4d8f1fb0d7d02184 | Set-ScsiLun -MultipathPolicy "RoundRobin"

You could of course take this further by importing all the data required for multiple hosts using an array, whether as a a manually created array in PowerShell, or by importing a csv or txt file to enable you to configure numerous hosts at once by making use of a ForEach loop.

Now, if you are using Virtual Distributed Switches (vDS), here is an alternative (I have assumed you already have an operational vDS in place).

#Load PowerCLI Modules
Import-module VMware.PowerCLI

#Variables
#vCenter or Host to Connect to
$vCenter = "smt-lab-vcsa-01.smt-lab.local"
#ESX Host to target
$ESXHost = Get-VMHost "smt-lab-esx-02.smt-lab.local"
#Name of the vDS
$iSCSISwitchName = "smt-lab-dc01_vDS_01"
#Name of the Portgroup for the VMKernel Adapter
$iSCSIVMKPortGroupName = "smt-lab-dc01_vDS_VMK_iSCSI_B"
#MTU size
$MTU = "9000"
#iSCSI VMK IP
$iSCSIIP = "10.200.34.51"
#iSCSI VMK SubnetMask
$iSCSISubnetMask = "255.255.255.0"
#iSCSI VMK VLAN ID
$VLANID = "301"
#iSCSI Portal Target
$Target = "10.200.34.1:3260"

Connect-VIServer $vCenter -Credential (Get-Credential) -Force

#New VMKernel Adapter on vDS
$NewPortGroup = New-VMHostNetworkAdapter -VMhost $ESXHost -PortGroup $iSCSIVMKPortGroupName -VirtualSwitch $iSCSISwitchName -IP $iSCSIIP -SubnetMask $iSCSISubnetMask -Mtu $MTU
Set-VDPortGroup -VDPortgroup (Get-VDPortGroup | Where {$_.Name -eq $iSCSIVMKPortGroupName}) -VLanId $VLANID

#Bind iSCSI VMKernel Adapter to Software iSCSI Adapter (credit to Luc Dekens for this)
$esxcli = Get-EsxCli -V2 -VMHost $ESXHost
$bind = @{
    adapter = ($iscsiHBA = $ESXHost | Get-VMHostHba -Type iScsi | Where {$_.Model -eq "iSCSI Software Adapter"}).Device
    force = $true
    nic = $NewPortGroup.Name
}
$esxcli.iscsi.networkportal.add.Invoke($bind)

#Add Dynamic Discovery Target
$ESXHost | Get-VMHostHba $iscsiHBA | New-IScsiHbaTarget -Address $Target

#Rescan Hba
Get-VMHostStorage -VMHost $ESXHost -RescanAllHba

A slight change to the cmdlts used; PortGroup > VDPortGroup.

Here are the results –

v smt-lab-dc01_vDS_01_uplinks 
> Uplink 1 (1 NIC Adapters) 
VLAN ID: 301 
v VMkernel Ports (1) 
vmk4 : 10.200.34_SO 
Virtual Machines (O)
Properties 
Devices 
Paths 
Dynamic Discovery 
Static Discovery 
VMkemeI Adapter 
vmk3 
Network Port Binding 
Addm X Remove Vlew Details 
Port Group 
(smt-lab-dc01 
Advanced Options 
Port Group Policy 
Compllant 
Compllant 
Path Status 
Actlve 
Acuve 
Physical Network Adapter 
vmn.c2 (10 Gblt/s, Full) 
vmn.c3 (10 GblVs, Full)

There are now two paths to my iSCSI device, one via a Standard Switch (vSS) and one via a Distributed Switch (vDS) across two subnets.

Storage Devices 
Refresh Attach 
Detach 
Name T 
Rename___ 
Turn on LED 
Turn Off LED 
Target 
Erase Pertltlons___ 
disk 
disk 
cdrom 
disk 
disk 
disk 
Mark as HDD Disk 
Mark as Local Mark as Perennlally Reserved 
Capacity 
FreeNAS 'SCSI Disk (nee.658gcfc0000004ac4d8fifbOd7d02184) 
FreeNAS [SCSI Disk (nee.6589cfcOOOOOOcaf3bc8066g7077d193) 
Local NECVMWar CD-ROM 
Local VMware Dlsk 
Local VMware Dlsk 
Local VMware Dlsk 
25000 GB 
5000 GB 
1600 GB 
2500 GB 
17500 GB 
Data store 
smvlab-ds-vmf._ 
Not Consumed 
Not Consumed 
Not Consumed 
smt-leb-ds-vsa___ 
smt-leb-ds-vsa___ 
Operational 
Attached 
Attached 
Attached 
Attached 
Attached 
Attached 
Name 
Hardware Acceleration 
Supported 
Supported 
Not supported 
Not supported 
Not supported 
Not supported 
Properties 
Enable Dlseble 
Runtime Name 
Paths 
Partition Details 
Acuve (I/O) 
Active (1/0)

Hope this has been helpful. It has saved me plenty of time throughout the countless builds and tear downs of my VMware home lab.

Thanks

Configuring Encrypted vMotion With PowerCLI

Encrypted vMotion is a feature available in vSphere 6.5 onwards. It is something that is always used to secure vMotions of encrypted virtual machines, its a required option, but is optional for non encrypted virtual machines.

By default, non encrypted virtual machines will be set to ‘opportunistic’. If both the source and destination hosts support it (so ESXi 6.5 onwards), vMotions will be encrypted. If the source or destination does not support it, then the vMotion traffic will not be encrypted.

The ‘required’ option is exactly what it says, encrypted vMotion is required. If either the source or destination host does not support it, the vMotion will fail. As I’ve said, encrypted virtual machines have no choice, they have to use encrypted vMotion.

The final option is to set it to ‘disabled’, for when you don’t want it to even attempt encrypting vMotion traffic for a virtual machine.

To set this option on either a singular virtual machine or all virtual machines, you can use the options below. Firstly to view the current settings you can run this. If you want to target a single VM enter the VM name after Get-VM.

Get-VM | Select-Object Name, @{Name="vMotionEncrpytion";Expression={$_.extensiondata.config.MigrateEncryption}}
Name vMotionEncrpytion
---- -----------------
CentOS opportunistic
vRepDR opportunistic
vcsa01 opportunistic
pho01 opportunistic

Now to change these all to ‘required’ you can run the following:

$VMView = Get-VM | Get-View
                    $Config = New-Object VMware.Vim.VirtualMachineConfigSpec
                    $Config.MigrateEncryption = New-Object VMware.Vim.VirtualMachineConfigSpecEncryptedVMotionModes
                    $Config.MigrateEncryption = "required"
            $VMView.ReconfigVM($Config)

You can confirm this by re-running the get command:

Name vMotionEncrpytion
---- -----------------
CentOS required
vRepDR required
vcsa01 required
pho01 required

To set them back to opportunistic, use the following:

$VMView = Get-VM | Get-View
                    $Config = New-Object VMware.Vim.VirtualMachineConfigSpec
                    $Config.MigrateEncryption = New-Object VMware.Vim.VirtualMachineConfigSpecEncryptedVMotionModes
                    $Config.MigrateEncryption = "opportunistic"
            $VMView.ReconfigVM($Config)

As you can see, they are then back to the default setting.

Name vMotionEncrpytion
---- -----------------
CentOS opportunistic
vRepDR opportunistic
vcsa01 opportunistic
pho01 opportunistic

And finally, setting it to ‘disabled’:

$VMView = Get-VM | Get-View
                    $Config = New-Object VMware.Vim.VirtualMachineConfigSpec
                    $Config.MigrateEncryption = New-Object VMware.Vim.VirtualMachineConfigSpecEncryptedVMotionModes
                    $Config.MigrateEncryption = "disabled"
            $VMView.ReconfigVM($Config)
Name vMotionEncrpytion
---- -----------------
CentOS disabled
vRepDR disabled
vcsa01 disabled
pho01 disabled

Here is a link to the official documentation on Encrypted vMotion for further information – here.

Thanks for reading!

Creating Virtual Distributed Port Groups Using PowerCLI

I recently needed to create a new Distributed Port Group and set a specific load balancing policy on an existing Distributed Switch. Nothing to exciting, but a task many have to complete. As this is a common repeatable task, i put together this short .ps1 to allow a repeatable way of completing this.

You can find the file here on GitHub

Just populate the Variables section with required information like so…

Save the file, then run the the .ps1 file from PowerShell prompt. (Ensure you have the PowerCLI Module installed; see my previous post)

Note you must add .\ to the beginning of the file name if you are executing the file from the directory you’re already in

Enter credentials with sufficient privilege in vCenter.

You will then see an output similar to this:

If you now take a look in the Web Client you will see the freshly created Distributed Port Group.

Creating just a single portgroup could potentially be quicker in the Web Client. What isn’t quicker, is multiple.

If you have a requirement to create multiple Distributed Port Groups on a vDS, you can use this script to do so in one go.

Just populate the Variables section with required information like above, then run the the .ps1 file from a PowerShell prompt. (Ensure you have the PowerCLI Module installed; see this post.

This uses and Array Table to build your source data, in this example, the PortGroup Name and VLAN ID for each. You can add further rows (more Port Groups) to the array by repeating the line in the red box, or add additional attributes by repeating the text from the green box on each line.

There are many ways you could modify this script to change the source of data, including ‘Get-Content’ from a .txt file for instance.

You can get the script here.

This is just one way to create Port Groups using PowerCLI, have a play around and make it work for you!

Thanks for reading.

« Older Entries