Monthly Archives: February 2022

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!