VMware PowerCLI Module 12.4 Update Error

With the release of VMware PowerCLI 12.4 I was updating the module on my LAB servers so I could check out the new command.

When upgrading I was getting the below error which is due to VMware changing the certificate authority used to publish the new module.

Authenticode issuer ‘E=noreply@vmware.com, CN=”VMware, Inc.”, O=”VMware, Inc.”, L=Palo Alto, S=California, C=US’ of the new module ‘VMware.VimAutomation.Sdk’ with version ‘12.4.0.18627054’ from root
certificate authority.

In the release notes from VMware there is a know issue with the certificate. The fix is to remove and re-install the module.

https://blogs.vmware.com/PowerCLI/2021/09/powercli-12-4-whats-new.html

I though I would do a quick post on this incase anyone doesn’t know the process.

To check where the module is installed run the below command.

Get-Module -ListAvailable VMware.PowerCLI
This image has an empty alt attribute; its file name is image-21.png

In my case the module was installed in C:\Program Files\WindowsPowerShell\Modules.

Next we need to remove the old modules, below is a list of the folders that make up the VMware PowerCLI 12.0 module.

After removing the folder we now have to install the new module using Install-Module

Install-Module VMware.PowerCLI

Once completed we should now have PowerCLI version 12.4

Using VMware PowerCLI Get-EsxCli

In this post we will be going through using the PowerCLI Get-EsxCli commandlet, which is used to call EsxCli command from PowerShell instead of having to SSH directly to the the ESXi host.

This can be useful when trying to gather information from multiple hosts instead of connecting on to each host with SSH or updating configuration settings.

First step is to connect to vCenter, once connected we can run the Get-EsxCli command and this will return the list of namespaces that can be use to gather information or set configuration settings.

We will be using -V2 as this sets Get-EsxCli to use version 2 interface as version 1 is being deprecated and will be removed in a later version.

Get-EsxCli -VMHost esxihost -V2

We can set the Get-EsxCli command to a variable so that we can call the namespaces, below will call the network namespace, once called there will be a list of addtiaonl namespaces that can be called.

$vmhostesxcli = Get-EsxCli -VMHost esxihost -V2
$vmhostesxcli.network

Once we select a namespace there should also be a list of methods that can be called these can be used to run specific actions like list or get to return information.

To list all nic’s we can call the nic and then list namespace

$vmhostesxcli.network.nic.list.invoke()

To gather info on details like driver version we can use the get method, in v1 you use to be able to call the nic name directly but if you try this in v2 the below error will be returned.

If specified, the arguments parameter must contain a single value of type Hashtable.

This is due to the method requiring a hash table parameter. If we call the help method it should return the valid parameters. If the parameter has a hyphens remove this or the command wont work.

For the nic namespace the parameter is nicname and needs to be called as a hash table.

$vmhostesxcli.network.nic.get.invoke(@{nicname="vmnic1"})

There might be some namespaces that have multiple properties like driverinfo under nic, these can be called by adding the property like below.

$vmhostesxcli.network.nic.get.invoke(@{nicname="vmnic1"}).DriverInfo

Now that we have the command we can start to build out a script to export information.

In this case we will be getting all hosts, listing all NIC’s and getting the drivers info.

The above shows that Get-EsxCli can be very good for retrieving information, if we wanted to set a configuration we can use similar command syntax but use the set method.

In this case we will update the Power policy settings on all host, we could do this manually by going to the Configure > Hardware > Overview > Power Management one each host and update the power policy settings

but doing this on a larger cluster is a lot of effort. To update using PowerCli we can first create the script to report on the current host power policy.

To update the policy settings we will use the set method like the below.

$vmhostesxcli.hardware.Power.policy.set.Invoke(@{id="1")})

Below are the 4 different power policy’s.

IDPower Policy
1High Performance
2Balanced
3Lower Power
4Custom

We can then take the above and create a re-usable script to report or set the power policy on all host in one go.

To download a copy of the either of the above script use the below link to my Github these can be used as reference if you want to create your own scripts for other settings.

https://github.com/TheSleepyAdmin/Scripts/tree/master/VMware/EsxCli

Report on users MFA status in Office 365 using PowerShell

During a recent audit we wanted to confirm what users had MFA enabled in Office 365. We use conditional access policy to enforce MFA.

We wanted to check each users to see if they had setup MFA and had a method configured. We also wanted to get information on licensing status and assigned licenses.

The only pre-req for using the script is that the MSOnline Powershell module is installed.

To install the MSOline module open and admin PowerShell windows and run

Install-Module -Name MSOnline

To confirm the module is installed run the below command.

Get-Module -ListAvailable MSOnline
This image has an empty alt attribute; its file name is image-26.png

First we need to connect to MS Online to do this run

Connect-MsolService 

Once connected to check the MFA status I will be using the StrongAuthenticationMethods properties as if MFA is configured for the user there will be a default method set.

For users that haven’t configured MFA no StrongAuthenticationMethods is set.

Below are the 4 methods available for MFA.

OneWaySMS
TwoWayVoiceMobile
PhoneAppOTP
PhoneAppNotification

In the script I only want to return the default method.

There is only one mandatory parameter for the export path where the report will be exported to.

The below is an example of how to run the report.

.\Office365_MFA_Report.ps1 -ExportPath C:\temp

Below is what the output will look like.

The full script can be downloaded from the below link.

Scripts/Office365_MFA_Report.ps1 at master · TheSleepyAdmin/Scripts (github.com)

Weekly Active Directory Audit Report PowerShell

Recently a request came in from our security team to audit recently create, deleted AD object, accounts due to expire (this is for third party users) and modified / created group policy objects so that they would be able to trace the changes happening in Active Directory.

I decided to write a PowerShell script that will export the required information and then send a the csv export to the user that require the information.

This could also be used to import the data to a dashboard by either using the CSV files or if the dashboard can use direct PowerShell script like PowerBI.

First there are some mandatory parameters. Exportpath and domain.

To allow the script to be run without emailing the csv I have left the smtpserver, to and from address as not mandatory parameters.

The script used two different modules

Group Policy:

ActiveDirectory:

To install these go on a Windows server go to add roles and features and select Group policy Management

and under RSAT enabled the Active Directory module.

Once all the features are enable we can run the script.

I have set the default time to last 7 days but if you want to go back further then update the date value.

To run the script so that it just export local without email the reports use the below.

.\WeeklyAD_AuditReport_V1.ps1 -exportPath c:\Temp\AD_Audit\ -domains domian.local

To email the report use the below

.\WeeklyAD_AuditReport_V1.ps1 -SMTPServer mailserver.domain.local -toAddress administrator@domain.local -FromAddress ADreport@domain.local -exportPath c:\Temp\AD_Audit\ -domains domian.local

Once the script completes we can check that the csv files have been created.

If the SMTP server parameter is set, the script will send a email and add the csv as attachments.

Below is what the outputs should look like.

GPO:

Deleted Objects:

Account expire:

The full script can be downloaded from the below link to my GitHub.

Scripts/ActiveDirectory/WeeklyReport at master · TheSleepyAdmin/Scripts (github.com)

The script can then be set to run as a scheduled task to run on a weekly scheduled.

VMware Daily Health Check HTML Report PowerShell

I have been working on a daily check report for our VMware environment so that we don’t have to manually check each morning.

The report uses PowerCli to generate information and then output the results to a HTML file.

The report requires a few that either the old PowerCLI snapin is available or preferably the PowerCLi PowerShell module.

The script can either be run directly by a users with rights to query vCenter or by setting up a scheduled task.

The following prerequisite will be needed for the script to run.

  • PowerShell V4 or V 5
  • PowerCLI 6.0 or later version
  • vCenter 6.0 or later version

There will also need to be a mail server or relay server available for the report to be emailed.

This has been tested on PowerCLI version 6.0 and above. The version on the server I will be running from is 12.3.0 which is the latest release at this time.

The report checks

  • vCenter connection
  • VMware tools check
  • Snapshot older than the specified snapshots days
  • Host Alarms
  • VM Alarms
  • vCenter Alerts over the last 12 hours
  • Datastore under specified % free space

There are mandatory parameter that are required for the script to run and send the report.

  • VCServer = vCenter Server address
  • SMTPServer = Mail server address
  • Toaddress = destination email
  • Fromaddress = sending address
  • Report Export = folder report will be exported to

There are some variables at the start of the script that can be set to customize the report to only show the required snapshots days and datastore % free. In my case I wanted 3 days and below 20% free on datastores.

I have embedded the html CSS format in the script so it can be update to change the color, font size or font type.

Example of how to run the script is below



.\VMwareDailReportv1.ps1 -VCServer vcenter.domain.local  -SMTPServer mail.doamin.local -FromAddress VMwareReport@domain.local -toAddress Administrator@domain.local -ReportExport D:\Scripts\VMware\Daily_Report

Once completed the report should be emailed to the specified to address.

Below is an example of the report export.

The full script can be downloaded from.

Scripts/VMwareDailyReport.ps1 at master · TheSleepyAdmin/Scripts (github.com)

To create a scheduled task to run the report each morning go to scheduled task on the server or client that has PowerCLI installed.

Create a new task

Set the schedule.

Next we need to set PowerShell as the program to start and set the argument to similar to the below, updating the parameters and script location

-ExecutionPolicy Bypass -NoProfile -File D:\Scripts\VMware\Daily_Report\VMwareDailReportv1.ps1 -VCServer vcenter.domain.local -SMTPServer mail.doamin.local -FromAddress VMwareReport@domain.local -toAddress Administrator@domain.local -ReportExport D:\Scripts\VMware\Daily_Report

I don’t change anything on conditions tab and only update that stop task if running longer than an hour in the settings tab.

Once completed run the task to confirm all is working.

I will probable added to the script but this is just the initial version and thought it might be helpful to anyone who want to try automate some of there manual checks.

Manage Calendar Permissions in Office 365 PowerShell

I had been asked to update all users calendar permission to allow a certain application to be able to edit users calendar so it can automate adding days off and other calendar items.

Currently the only way this can be done that I know off is either using PowerShell or using MS Graph.

In this post we will be going through how to report calendar permission for all users and how to bulk update. We will be using the exchange online PowerShell module.

https://docs.microsoft.com/en-us/powershell/exchange/exchange-online-powershell-v2?view=exchange-ps#install-the-exo-v2-module

Install-Module -Name ExchangeOnlineManagement

To get the list of mailboxes and permissions we will use two command Get-Mailbox and Get-MailboxFolderPermission. Then we will be formatting the results for export out to a CSV.

To get calendar permission we will need to add :\Calendar to after the UserPrincipalName.

First step is that we need to connect to Exchange Online using PowerShell.

Connect-ExchangeOnline

To get a single user’s permission use

Get-MailboxFolderPermission -Identity user@domain.com:\calendar 

To check for a specific users permission we can add the -user parameter.

To get all users permission we need to get a list of all users using Get-Mailbox and then loop through each user to get the permission.

## Get all users mailboxes
$Mailboxes = Get-Mailbox -ResultSize Unlimited | Select-Object UserPrincipalName

## Loop through each mailbox
foreach ($mailbox in $Mailboxes) {

## Get default user calendar permissions
Write-Warning "Checking Permission on $($mailbox.UserPrincipalName)"
Get-MailboxFolderPermission -Identity "$($mailbox.UserPrincipalName):\Calendar" | 
Select-Object @{Name="UserPrincipalName";E={$mailbox.UserPrincipalName}},FolderName,User,AccessRights |
Export-Csv C:\temp\Office_365_Export\Calendar_Report\CalenarPermissionReport.csv -NoTypeInformation -Append
}

The export should look like the below.

Once I had a report of the current users calendar permission, I needed to add the user account that the application would use to add the calendar items.

To add calendar permission we use the Add-MailboxFolderPermission,

The roles that are available, along with the permissions that they assign are below.

  • Author: CreateItems, DeleteOwnedItems, EditOwnedItems, FolderVisible, ReadItems
  • Contributor: CreateItems, FolderVisible
  • Editor: CreateItems, DeleteAllItems, DeleteOwnedItems, EditAllItems, EditOwnedItems, FolderVisible, ReadItems
  • None: FolderVisible
  • NonEditingAuthor: CreateItems, FolderVisible, ReadItems
  • Owner: CreateItems, CreateSubfolders, DeleteAllItems, DeleteOwnedItems, EditAllItems, EditOwnedItems, FolderContact, FolderOwner, FolderVisible, ReadItems
  • PublishingEditor: CreateItems, CreateSubfolders, DeleteAllItems, DeleteOwnedItems, EditAllItems, EditOwnedItems, FolderVisible, ReadItems
  • PublishingAuthor: CreateItems, CreateSubfolders, DeleteOwnedItems, EditOwnedItems, FolderVisible, ReadItems
  • Reviewer: FolderVisible, ReadItems

To add a single user use the command below.

Add-MailboxFolderPermission -Identity user@domain.com:\calendar -User "Account to be added" -AccessRights Editor 

I also wanted to be able to run this script again and not have to modify users that already have the correct permission as this will return an error, so we will be using a if statement to only apply to users who don’t have the permission.

Below is the full script.

## Calendar Permission Variable
$Account = "Account to be added"
## Get all users mailboxes
$Mailboxes = Get-Mailbox -ResultSize Unlimited | Select-Object UserPrincipalName

## Loop through each mailbox
foreach ($mailbox in $Mailboxes) {

## Get default user calendar permissions
$permission = Get-MailboxFolderPermission -Identity "$($mailbox.UserPrincipalName):\Calendar"

## Report on Users without correct calendar permissions and set permissions
if ($permission.User.DisplayName  -notcontains $Account) {
Write-Warning "Calender permission not set on $($mailbox.UserPrincipalName) adding Permission"
Add-MailboxFolderPermission -Identity "$($mailbox.UserPrincipalName):\Calendar" -User $Account -AccessRights Editor | Out-Null
} 

## calendar permission are correct
else {Write-Host "Calender Permission are set on $($mailbox.UserPrincipalName)" -ForegroundColor Green}

}

Checking LAPS Password Cross-Forest Using PowerShell

We have been using LAPS for the last year or so and for the most part it is a great tool for managing local admins password.

In our environment we have multiple forest with trusts relationships and the one main issue that we have is that the LAPS UI client can’t check the local admin password of devices in other domains, unless we logon as a user in that domain and run the LAPS UI as that user.

We can use PowerShell but this requires either the commands to be run from a devices in the domain with the LAPS PowerShell module or can be done using remote ps session but this is not something everyone on the service desk would know how to do or will have rights to do.

Enter-PSSession dc.domain.local
Get-AdmPwdPassword -ComputerName "computer" | Select-Object Password,ExpirationTimestamp

We could also get the AD attributes by using get-adcomputer comandlet but this requires the AD PowerShell module.

Get-ADComputer -Identity "computer" -Server "DC" -properties ms-mcs-admpwd,ms-mcs-admpwdexpirationtime | select-object ms-mcs-admpwd,ms-mcs-admpwdexpirationtime

I wanted to try create a script that would allow them to check the other domains without the needing to know how to do PS remoting or having modules installed.

To get around using the AD module we will be using ADSI search instead as this a default part of PowerShell. I used the below blog to get started.

Use the PowerShell [adsiSearcher] Type Accelerator to Search Active Directory | Scripting Blog (microsoft.com)

Hey, Scripting Guy! How Can I Search Active Directory from Within Windows PowerShell? | Scripting Blog (microsoft.com)

To query the remote doamin using adsisearch we first need to have a way of converting the domain name to the format that adsisearch requires.

This can be done using DirectoryContect class

System.DirectoryServices.ActiveDirectory.DirectoryContext

System.DirectoryServices.ActiveDirectory.Domain

DirectoryContext Class (System.DirectoryServices.ActiveDirectory) | Microsoft Docs

The first part of the script gets the domain details and returns the objects back to the search variable.

Next part we will filter the results based on the client variable to return only the required computer details.

Once the script is working, I just needed to create some mandatory parameters and create a function called Check-LAPS so that it’s easier for people to use without having to hardcode anything in the script and has some help and examples.

Below is the link to the full script.

Scripts/ActiveDirectory/LAPS at master · TheSleepyAdmin/Scripts (github.com)

Below shows how the script is run and the results.

I used the above script with WPF to create a GUI that is now complied in to an exe. I though this would be a lot easier to pass on the service desk as all they need to do is run the exe.

This was compiled using ps2exe PowerShell module.

MScholtes/PS2EXE: Module to compile powershell scripts to executables (github.com)

All they need to do is put in the domain name and computer name.

This should then return the LAPS password.

I will do a different post on creating the GUI itself in as this was my first GUI and I would like to create a few other.

Exchange Online PowerShell Basic Authentication Connection Issue

Today I ran in to an issue where I couldn’t connect to Exchange Online PowerShell. I could connect to other service without issue (Office 365, Azure AD….).

When trying to connect to Exchange online I was getting an warning about basic auth.

At first I thought this was to do with Microsoft deprecating basic authentication

Exchange Online deprecating Basic Authentication – Microsoft Lifecycle | Microsoft Docs

but after reading the document it says that this doesn’t affect Exchange ActiveSync (EAS), IMAP, POP, and Remote PowerShell.

I then read the Exchange Online v2 module prerequisites

https://docs.microsoft.com/en-us/powershell/exchange/exchange-online-powershell-v2?view=exchange-ps#prerequisites-for-the-exo-v2-module

In this there is a part that winrm needs to be enabled for basic authentication even though the module uses modern authentication.

This had been working for a long time without issue so I didn’t know what exactly had changed so I decided to check the winrm config

To check the winrm configuration run the below command

winrm get winrm/config/client/auth

In my case it was being set by a GPO. We had recently applied the Windows 10 2004 baseline policy to all our Windows 10 devices so I decided to check the setting in that GPO.

Security baseline (FINAL): Windows 10 and Windows Server, version 2004 – Microsoft Tech Community

There is a spreadsheet in the document folder that has all the policy settings. In the computer policy we can see that Allow basic authentication is set to disabled.

Since this is a security risk (there is no encryption with basic authentication) and only a few device really need to connect, We decided to create a new policy that enabled this for only admins that will need to use Exchange Online PowerShell and use security filtering to only apply to a security group with the required devices.

Once basic authentication was re-enabled I was able to connect without issue.

If you have this issue and apply either the CIS or the Windows 10 baseline policy this could be the cause.

VMware PowerCLI Integrated Authentication Issues

Recently we have been having an issue with VMware PowerCLI not passing through the users credentials when running Connect-viserver to connect to our vCenter servers.

This has been causing problems when trying to use scheduled task to automated reports and run remediation task like removing old snapshots or reporting on VMware Tools versions as it is prompting for credentials.

For integrated authentication to work, the vCenter servers needs to be setup to allow single sign on for the domain that you will be connecting from, so confirm that your Active Directory Identity source is added and that SSO works from the web client. If not, complete this first before trying to use PowerCLI with integrated authentication.

I have SSO configured and tested so this wasn’t my issue.

When using the older version of PowerCli version 6.5 and below we had no issue with integrated authentication and would connecting to vCenter server without prompting me for credentials.

We could continue to use the old version of PowerCLI but we would be missing out on improvement and new commandlets so I wanted to try and get the newer version working for automated task.

First we install the VMware.PowerCLI module using Install-Module VMware.PowerCLI, the current version is 12.1

When I tried to connect using this version of PowerCLI I get prompted for a user name and password and this is what is stopping my automated task from running.

There are a few ways to workaround the prompt and this can also be used to confirm if SSO is working correctly. One way would be to add credential store item that can be used for connecting.

To add this use the VICredentialstore commandlet.

New-VICredentialStoreItem -User domain\username -Password Password -Host vc.domain.local -File C:\Temp\vicreds.xml

This outputs the credential to an xml file that can then be imported and called using Connect-VIserver.

$logon = Get-ViCredentialStoreItem -File C:\Temp\vicreds.xml
Connect-VIServer -Server $logon.Host -User $logon.User -Password $logon.Passwor

This works but I don’t really want to have a xml file that has information saved to it and someone might remove the folder or file by mistake.

The other way would be similar but using a txt file with the converted to a secure string password but again this relies on a file which is not ideal and not really all that secure.

The last option and one that I wouldn’t recommend at all is to hard code a username and password in the script.

So now that we have gone through some work arounds I decided to have a look at the actually problem .

The above proves that authenticating against AD is working so I knew it wasn’t an account or SSO issue so it had to be an issue with PowerCLI itself.

I connect using a my user name and passwords and it connect without issue.

I then check the VPX log under /storage/log/vmware/vpxd to see if there are an issue but I didn’t see any issues.

Next I tried to use the -verbose parameter to return more information on what exactly connect-viserver was doing,

This then returned an error for TLS.

I next checked the settings on PowerCLI configuration settings.

To check the PowerCLI configuration use

Get-PowerCLIConfiguration

When checking the configuration the Invalidcerificaeaction was set to unset.

I changed this setting to warn instead of unset

Once this setting was changed I can now connect to PowerCLI with integrated authentication, I do get a long warning message though I could set this to ignore and this returns no warning or error.

There are two ways around this instead of changing the above setting. One is to install the the certificate as a trusted root certificate so that the cert is trusted.

Or if you have an internal certificate authority you to replace the default VMware cert with an internal cert.

Once this is done the connection work without requiring manual intervention and my automated scripts can be run using scheduled tasks again with the latest version of PowerCLI. Hopefully this will be helpful to anyone else having this issue.

Windows Query Remote Group Membership Using PowerShell

We needed to do an audit on privileged group membership on workstations. There are many way to do this but using PowerShell to query WMI remotely was the method that we choose as we had to do a few thousand and I wanted to do the checks in groups. .

I decided to write a script that will take the list of devices to be checked from a txt file and then use parameter for the export path and groups to be search.

In this script we will use the class Win32_Group below is a link to the Microsoft Docs

Win32_Group class – Win32 apps | Microsoft Docs

When querying WMI we can use filters to limit the results. If you run query without filtering the command will return all groups even those in AD. See below command and the returned results.

Get-WmiObject  -Class Win32_GroupUser | Select-Object GroupComponent,PartComponent,PSComputerName

If we use a filter we can then reduce these by using domain which will be the local machine name and the local group name.

Get-WmiObject -Class Win32_GroupUser -Filter "GroupComponent=""Win32_Group.Domain='LAB-Host01',Name='Administrators'""" | Select-Object GroupComponent,PartComponent,PSComputerName

Now that we have a filter we can use variables to specific the host name from the text file with computer names and group names.

In the script these will be set using the complist and groups variables.

Below are two examples of how the script can be run against one group or multiple groups

.\Get-RemoteGroupMembers -CompList c:\Temp\Comps.txt c:\Temp\Results -groups “Administrators”

For multiple groups just add a , between the names and double or single quotes if there are spaces in the group name.

.\Get-RemoteGroupMembers -CompList .\Comps.txt -exportPath .\ -groups “Administrators”,”Remote Desktop Users”

The full script is on my github page. As always any scripts should be tested before run in production.

Scripts/Get-RemoteGroupMembers at master · TheSleepyAdmin/Scripts (github.com)