dewyser.net

solutions, scripting and more

Removing old agents in VMware Horizon VDI golden image — 5th Jan 2024

Removing old agents in VMware Horizon VDI golden image

The following script finds the uninstall strings in the registry and removes old agents from the golden image.

$execpolicy = Get-ExecutionPolicy

Set-ExecutionPolicy Unrestricted

$installed = Get-ItemProperty 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*' | Where-Object {$_.UninstallString -match "MsiExec.exe"} | Select-Object DisplayName, DisplayVersion, UninstallString
$installed += Get-ItemProperty 'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' | Where-Object {$_.UninstallString -match "MsiExec.exe"} | Select-Object DisplayName, DisplayVersion, UninstallString

$apps = @("VMware Tools","VMware Horizon Agent","VMware Horizon Agent Direct-Connection Plugin","VMware Dynamic Environment Manager Enterprise","App Volumes Agent","Microsoft FSLogix Apps")

$uninstall = $installed | Where-Object {($_.DisplayName -in $apps)}

$uninstall

foreach ($app in $uninstall) {
    $uninstcmd = $app.UninstallString

    $uninstcmd = (($uninstcmd -split " ")[1] -replace "/I","/X") + " /qn REBOOT=ReallySuppress"
    $uninstprc = Start-Process msiexec.exe -ArgumentList $uninstcmd -NoNewWindow -PassThru -Wait
}

$check = Get-ItemProperty 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*' | Where-Object {$_.UninstallString -match "MsiExec.exe"} | Select-Object DisplayName, DisplayVersion, UninstallString
$check += Get-ItemProperty 'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' | Where-Object {$_.UninstallString -match "MsiExec.exe"} | Select-Object DisplayName, DisplayVersion, UninstallString

$check | Where-Object {($_.DisplayName -in $apps)} | Format-Table

Set-ExecutionPolicy $execpolicy
Installing new Microsoft Teams in VMware Horizon VDI — 4th Jan 2024

Installing new Microsoft Teams in VMware Horizon VDI

The following script installs the new teams in the vdi golden image. If an old version is installed it removes it first. Afterwards a registry key is set to disable automatic updates.

The correct files can be downloaded here.

# get the current execution policy
$execpolicy = Get-ExecutionPolicy

Set-ExecutionPolicy Unrestricted

# location to the teams bootstrapper file
$bootstrapperpath = $PSScriptRoot + '\teamsbootstrapper.exe'

# allow execution of the teams bootstrapper file
Unblock-File -Path $bootstrapperpath

# check if teams is currently installed
$teamsversion = Get-AppxPackage -Name *MSTEAMS*

if ($teamsversion) {
    # if installed remove teams
    Write-Host 'Uninstalling Microsoft Teams version ' $teamsversion.version -ForegroundColor Red

    Start-Process -FilePath $bootstrapperpath -ArgumentList '-x' -Wait
    $teamsversion = ""
}

# location to the teams msix file
$msixpath = $PSScriptRoot + '\MSTeams-x64.msix'

# configure installation parameters
$args = '-p -o "' + $msixpath + '"'

# install teams
Start-Process -FilePath $bootstrapperpath -ArgumentList $args -Wait

# registry path
$registrypath = 'HKLM:\SOFTWARE\Microsoft\Teams'

# disable teams autoupdate 
if (!(Test-Path $registryPath)) {
    New-Item -Path $registrypath -Force | Out-Null
    New-ItemProperty $registrypath -Name 'disableAutoUpdate' -Value 1 -PropertyType DWord -Force | Out-Null
} else {
    if (!(Get-ItemProperty $registrypath -Name 'disableAutoUpdate')) {
        New-ItemProperty $registrypath -Name 'disableAutoUpdate' -Value 1 -PropertyType DWord -Force | Out-Null
    } else {
        Set-ItemProperty $registrypath -Name 'disableAutoUpdate' -Value 1 -PropertyType DWord -Force | Out-Null
    }
}

# check if teams installed correctly
$teamsversion = Get-AppxPackage -Name *MSTEAMS*

if ($teamsversion) {
    Write-Host 'Installed Microsoft Teams version ' $teamsversion.version -ForegroundColor Green
}

# set execution policy back to original value
Set-ExecutionPolicy $execpolicy
Certificates (in the home lab) made easy – renewing esxi certificates — 25th Aug 2023

Certificates (in the home lab) made easy – renewing esxi certificates

You could do this manually using vCenter GUI:

Select a Host and go to Configure > Certificate. There you have the option to first refresh and then renew the certificate. Now that your vCenter is an issuing certificate authority with our custom certificates it would issue certificate with the template we configured 2 steps back.

Another option, and certainly the preferred one if you have several hosts is to do this with PowerCLI:

$CertificateManager = Get-View -id (Get-View ServiceInstance).Content.CertificateManager

Get-VMHost | ForEach-Object -Process {$CertificateManager.CertMgrRefreshCACertificatesAndCRLs($_.id)}

Get-VMHost | ForEach-Object -Process {$CertificateManager.CertMgrRefreshCertificates($_.id)}

Now you’ll see that if you visit the esxi servers, they also have a valid certificate.

Certificates (in the home lab) made easy – vCenter preparation — 24th Aug 2023

Certificates (in the home lab) made easy – vCenter preparation

Next we will set some advance parameters in our vCenter appliance using PowerCli. Below are the parameters that need to be changed:

  • vpxd.certmgmt.certs.cn.country
  • vpxd.certmgmt.certs.cn.email
  • vpxd.certmgmt.certs.cn.localityName
  • vpxd.certmgmt.certs.cn.organizationalUnitName
  • vpxd.certmgmt.certs.cn.organizationName
  • vpxd.certmgmt.certs.cn.state
  • vpxd.certmgmt.certs.daysValid
  • vpxd.certmgmt.certs.minutesBefore

If the vCenter appliance currently has a untrusted certificate we need to change the certificate action so that we can connect to the vCenter.

Set-PowerCLIConfiguration -InvalidCertificateAction:Ignore -Confirm:$false

Next we can connection to the vCenter using the following PowerCLI command:

connect-VIServer vc-01.dewyser.lab

You will receive output name, port and username if the connection is successful.

With the following command you can retrieve the current values for the parameters specified above:

Get-AdvancedSetting -Entity vc-01.dewyser.lab -Name vpxd.certmgmt.certs.*

Next set the parameters to the correct values:

Get-AdvancedSetting -Entity vc-01.dewyser.lab -Name vpxd.certmgmt.certs.cn.country | Set-AdvancedSetting -Value 'BE' -Confirm:$false
Get-AdvancedSetting -Entity vc-01.dewyser.lab -Name vpxd.certmgmt.certs.cn.email | Set-AdvancedSetting -Value 'info@dewyser.lab' -Confirm:$false
Get-AdvancedSetting -Entity vc-01.dewyser.lab -Name vpxd.certmgmt.certs.cn.localityName | Set-AdvancedSetting -Value 'Diksmuide' -Confirm:$false
Get-AdvancedSetting -Entity vc-01.dewyser.lab -Name vpxd.certmgmt.certs.cn.organizationalUnitName | Set-AdvancedSetting -Value 'IT Infrastructure' -Confirm:$false
Get-AdvancedSetting -Entity vc-01.dewyser.lab -Name vpxd.certmgmt.certs.cn.organizationName | Set-AdvancedSetting -Value 'dewyser.lab' -Confirm:$false
Get-AdvancedSetting -Entity vc-01.dewyser.lab -Name vpxd.certmgmt.certs.cn.state | Set-AdvancedSetting -Value 'West-Vlaanderen' -Confirm:$false
Get-AdvancedSetting -Entity vc-01.dewyser.lab -Name vpxd.certmgmt.certs.daysValid | Set-AdvancedSetting -Value '397' -Confirm:$false
Get-AdvancedSetting -Entity vc-01.dewyser.lab -Name vpxd.certmgmt.certs.minutesBefore | Set-AdvancedSetting -Value '5' -Confirm:$false

Check the values again using the asterisk command to check if the changes are applied. The output should look like this:

If the values are set we are ready for the next part.

Automate the vlan creation — 13th Feb 2023

Automate the vlan creation

Ever had a distributed switch failing and need to roll back to a standard switch, realising that you need to manually create all the vlan on that standard switch?

The following script reads all the vms on a host and creates all the necessary vlan currently on the dvs on a new temporary standard switch.

# Create a hashed secure password file from the client input
# Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File D:\secure-password.txt

$svc_account_username = "<username>"
$svc_account_password = get-content D:\secure-password.txt | ConvertTo-SecureString

# Parameter input variables
$vcenter = "<FQDN vCenter>"
$vcenter_host = "<FQDN Host>"

$portgroup_check = "dvv" #parameter to look for / name of the distributed switch vlan
$portgroup_replace = "stv" #parameter to replace with / name vlan on the new standard switch

$standard_switch = "<name new standard switch>"

$vms= @()
$dpgs = @()

Function OpenConnection {
    param(
     [Parameter()]
     [string]$vcenter,
     [Parameter()]
     [string]$username,
     [Parameter()]
     [securestring]$password
    )

    # Create a credential
    $cred = New-Object System.Management.Automation.PSCredential -ArgumentList ($username, $password) -ErrorAction Stop

    # Connect to vCenter
    if ($cred) {
        $connection = Connect-VIServer -Server $vcenter -Credential $cred -Force -ErrorAction Stop
    } else {
        $connection = $null
    }
    
    return $connection
}

Function GetHostVM {
    param(
     [Parameter()]
     [string]$vcenter_host
    )
    $vms = Get-VMHost -Name $vcenter_host -ErrorAction SilentlyContinue | Get-VM -ErrorAction SilentlyContinue | Select-Object -Property Name, @{N='PortGroupName';E={(Get-NetworkAdapter -VM $_).NetworkName -join '|'}}

    return $vms
}

Function GetVM {
    param(
     [Parameter()]
     [string]$vcenter_host
    )
    $vms = Get-VM -ErrorAction SilentlyContinue | Select-Object -Property Name, @{N='HostName';E={$_.VMHost.Name}}, @{N='PortGroupName';E={(Get-NetworkAdapter -VM $_).NetworkName -join '|'}} | Where-Object  {$_.HostName -like @($vcenter_host.Substring(0,7)+"*")}

    return $vms
}

Function CloseConnection {
    param(
     [Parameter()]
     [string]$vcenter
    )
    # Close vCenter connection
    Disconnect-VIServer -Server $vcenter -Confirm:$false
}

Function CheckPortGroup {
    param (
     [Parameter()]
     [string]$vcenter,
     [Parameter()]
     [string]$vcenter_host,
     [Parameter()]
     [string]$portgroup,
     [Parameter()]
     [string]$portgroup_check,
     [Parameter()]
     [string]$portgroup_replace,
     [Parameter()]
     [string]$standard_switch
    )

    $portgroup = $portgroup -replace $portgroup_check, $portgroup_replace

    $pg = Get-VirtualPortGroup -Server $vcenter -VMHost $vcenter_host -Name $portgroup -VirtualSwitch $standard_switch -ErrorAction SilentlyContinue

    return $pg
}

Function NewPortGroup {
    param (
     [Parameter()]
     [string]$vcenter,
     [Parameter()]
     [string]$vcenter_host,
     [Parameter()]
     [string]$portgroup,
     [Parameter()]
     [string]$portgroup_check,
     [Parameter()]
     [string]$portgroup_replace,
     [Parameter()]
     [string]$standard_switch,
     [Parameter()]
     [string]$vlan_id
    )

    $portgroup = $portgroup -replace $portgroup_check, $portgroup_replace

    $pg = New-VirtualPortGroup -Server $vcenter -Name $portgroup -VirtualSwitch $standard_switch -VLanId $vlan_id -ErrorAction SilentlyContinue

    return $pg
}

Function CheckStandardSwitch {
    param (
     [Parameter()]
     [string]$vcenter,
     [Parameter()]
     [string]$vcenter_host,
     [Parameter()]
     [string]$standard_switch
    )

    $svs = Get-VirtualSwitch -VMHost $vcenter_host -Name $standard_switch -Server $vcenter -ErrorAction SilentlyContinue

    return $svs
}

Function NewStandardSwitch {
    param (
     [Parameter()]
     [string]$vcenter,
     [Parameter()]
     [string]$vcenter_host,
     [Parameter()]
     [string]$standard_switch
    )

    $svs = New-VirtualSwitch -VMHost $vcenter_host -Name $standard_switch -Server $vcenter -ErrorAction SilentlyContinue

    return $svs
}

$connection = OpenConnection -vcenter $vcenter -username $svc_account_username -password $svc_account_password

if ($connection) {
    # Get the vm that are running on a host
    #$vms = GetHostVM -vcenter_host $vcenter_host
    $vms = GetVM -vcenter_host $vcenter_host


    # Get the distinct portgroups for those vm
    $dpgs =  $vms.portgroupname -split {$_ -like "*|*"} | Select-Object $_ -Unique | Sort-Object $_

    # Check if the temporary standard switch exists
    $vs_exists = CheckStandardSwitch -vcenter $vcenter -vcenter_host $vcenter_host -standard_switch $standard_switch

    if (!$vs_exists) {
        # Create the temporary standard switch
        $svs = NewStandardSwitch -vcenter $vcenter -vcenter_host $vcenter_host -standard_switch $standard_switch
    }

    foreach ($dpg in $dpgs) {
        # Check if the standard portgroup exists on the host
        if ($dpg -like @('*'+$portgroup_check+'*')) {
            $pg_exists = CheckPortGroup -vcenter $vcenter -vcenter_host $vcenter_host -portgroup $dpg -portgroup_check $portgroup_check -portgroup_replace $portgroup_replace -standard_switch $standard_switch

            if (!$pg_exists) {
                # Create standard portgroup on the host
                $svpg = NewPortGroup -vcenter $vcenter -vcenter_host $vcenter_host -portgroup $dpg -portgroup_check $portgroup_check -portgroup_replace $portgroup_replace -standard_switch $standard_switch -vlan_id $dpg.Substring($dpg.Length-3,3)
            }
        } else {
            Write-Host "Portgroup" $dpg "out of range." -ForegroundColor Red
        }
    }

    CloseConnection -vcenter $vcenter
}