Powershell & Automation Articles - Altaro DOJO | Microsoft Hyper-V blog https://www.altaro.com/hyper-v Hyper-V guides, how-tos, tips, and expert advice for system admins and IT professionals Fri, 03 Jun 2022 16:42:05 +0000 en-US hourly 1 Top 10 PowerShell Tasks in Exchange Online https://www.altaro.com/hyper-v/10-tasks-online-powershell/ https://www.altaro.com/hyper-v/10-tasks-online-powershell/#respond Fri, 03 Jun 2022 16:39:12 +0000 https://www.altaro.com/hyper-v/?p=24075 IT admins of today are busier today than ever, juggling multiple tasks and responsibilities. Enter PowerShell for Exchange Online Powershell

The post Top 10 PowerShell Tasks in Exchange Online appeared first on Altaro DOJO | Hyper-V.

]]>

Today, there is no question that IT admins are busier than ever, juggling multiple tasks and responsibilities. These include managing and administering Exchange email services, both on-premises and in the cloud. Exchange Online is an extremely popular solution for organizations to host mail services as many businesses have migrated email and file storage to the public cloud. PowerShell is a great scripting language that allows admins to make the best use of their time by automating common tasks and day-to-day activities.

Why use PowerShell?

Before considering PowerShell specifically in the context of Exchange Online, why should admins consider using PowerShell in general? Today, PowerShell has quickly become one of the most popular and fully-featured scripting languages. Many software vendors are developing and releasing their own PowerShell modules, allowing admins to control, configure, and manage many different solutions across the board with the familiar PowerShell syntax.

IT admins, especially Windows admins, are familiar with PowerShell as version 1.0 was released in 2006 for Windows Server 2003, Windows XP SP2, and Windows Vista. In addition, Windows PowerShell is included in modern Windows Server and client operating systems, with the newer PowerShell Core as an optional download.

PowerShell is both familiar and understandable for many admins, given its verb-noun constructs and very human-readable syntax. However, even for non-developers, writing simple PowerShell one-liner scripts can significantly reduce the number of manual tasks performed daily.

PowerShell is also very extensible. As mentioned, third-party software vendors can write their own PowerShell snap-ins and modules to integrate into the PowerShell framework, allowing PowerShell to be customized to work with many different software solutions. Third-party vendors are not the only ones that have extensively used Powershell modules and cmdlets. Most modern Microsoft software and cloud solutions have their own PowerShell modules, allowing for seamless automation, including configuration and management.

What is Exchange Online (EXO)?

Microsoft Exchange Online (EXO) is a hosted unified messaging solution that provides email, calendaring, contacts, and task management from a wide range of devices. Exchange Online is a modern counterpart to the traditional Exchange on-premises solutions organizations have used for decades. In addition, Exchange Online can leverage modern Microsoft technologies, including Azure Active Directory. With Exchange Online’s Azure integration, organizations have the tools needed to support the modern hybrid workforce worldwide.

Exchange Online is the email component included in an Office 365 or Microsoft 365 subscription. However, you can purchase Exchange Online services without the other components of Office/Microsoft 365. With Exchange Online, you retain control over the messaging services offered to your users.

Microsoft Exchange Online PowerShell

Exchange Online includes the ability to administer, configure, and manage your Exchange Online environment using PowerShell. In addition, Exchange Online Powershell provides many robust cmdlets allowing administrators to automate many common tasks.

The Exchange Online PowerShell V2 module is the latest iteration and release of the Exchange Online module and provides modern features, such as the ability to work with multi-factor authentication (MFA). With MFA, organizations can greatly bolster the security of their PowerShell sessions by requiring more than one authentication factor, such as a one-time code delivered via an authenticator app or text message.

Automated Configuration and Benefits of Exchange Online PowerShell

IT admins may ask why they would want to use PowerShell instead of simply using the GUI that is familiar and does most of what they way to do. When performing specific tasks one time or only a few times during a day on one object, the GUI tools are well suited to carry out these tasks and are quite efficient at carrying out a single job or a few tasks in an ad-hoc way. However, there are multiple reasons why you would use PowerShell instead of the Exchange Online GUI management tools. These include:

    • Bulk operations
    • Data filtering
    • Data piping

Bulk operations

GUI management tools do not scale well when dealing with tasks that may need to be performed on multiple users or other objects. Also, what if you need to carry out specific tasks on hundreds of objects on a schedule? GUI management tools are not suited for doing this. For example, can you imagine manually changing an attribute on hundreds of Exchange Online users through the GUI? It would be extremely time-consuming and not very efficient.

When needing to perform bulk operations on multiple objects, PowerShell is much better suited at doing this than the Exchange Online GUI. For example, when manually changing values and attributes on an object numerous times through a GUI, there is a high likelihood a mistake can be made. However, if you use PowerShell to make the changes, the actions are repeated precisely each time the code updates the object, eliminating mistakes due to human error.

Making changes using a PowerShell script on hundreds of users might take minutes or less, whereas making the same changes manually through the GUI might take hours. It can save many hours and manual labour for low-level administrative tasks.

Data filtering

One of the powerful reasons to use PowerShell with Exchange Online is the data filtering capabilities of PowerShell. Powershell is a powerful object-oriented scripting language that can pull out objects and filter data in ways that may not be available in the Exchange Online Management GUI.

When you think about it, GUI tools only allow filtering by the specific criteria built into the GUI tool or management console. If the specific filter you need is not available, you can’t see the information in the way you need it displayed. In addition, GUI tools generally do not provide IT admins with the filtering and data extraction capabilities of command-line tools and scripting languages.

With the filtering capabilities built into PowerShell for Exchange Online, IT admins can query and filter data as needed. PowerShell is an object-oriented scripting language that can return various data objects. For example, let’s say you want to get the archivestatus attribute from all your user mailboxes. You could do that with a simple PowerShell one-liner as follows:

    • get-mailbox | select name, archivestatus

With Exchange Online PowerShell, getting the value of any mailbox attribute is the same as following this simple syntax shown above. Now, things get more interesting by piping returned values and data into other PowerShell cmdlets.

Data piping

Another powerful capability of data filtering with PowerShell is to take the data returned from a data query with a filter and then pipe the return into another PowerShell command. This simple feature contained natively in PowerShell allows querying for specific matching objects such as mailboxes and then doing something with those returned objects, such as running another Exchange Online PowerShell cmdlet on them.

A very simple example of piping your return data into another PowerShell cmdlet is a simple “out-file” cmdlet. It allows you to export your returned data to a simple text file.

    • get-mailbox | select name, archivestatus | out-file c:\archivestatus.txt

But, you can do anything you want with the pipe from a get-mailbox, get-user, or other PowerShell “get” command. You can think of the workflow like this: you are querying for a specific list of objects that match the filter criteria you have specified and then take that set of matching objects and then feed these into another PowerShell cmdlet.

Manually Configuring Exchange Online PowerShell

To get started using Exchange Online PowerShell cmdlets, you need first to install the required PowerShell modules to work with Exchange Online PowerShell. The Exchange Online PowerShell module is part of several modules that fall under the umbrella of services contained in Microsoft 365. As mentioned earlier, the Exchange Online service can be purchased as a standalone product or included with the mail services offered by Microsoft 365.

Each of the Microsoft 365 services has its own PowerShell modules, including:

    • Azure Active Directory (Azure AD)
    • Exchange Online
    • SharePoint Online
    • Skype for Business Online
    • Teams

If you are explicitly working with Exchange Online (EXO), two modules are needed to interact with the low-level Azure AD user objects and the Exchange Online mailboxes:

    • Azure Active Directory (Azure AD) PowerShell – Allows querying the Azure Active Directory environment users, attributes, etc
    • Exchange Online PowerShell – Allows querying and performing critical tasks at the mailbox level for users with Exchange Online mailboxes

Let’s see how to install both of these PowerShell modules for specifically interacting with Exchange Online via PowerShell.

Azure Active Directory (Azure AD)

First, we are going to install the AzureAD PowerShell module. As a note. It does not matter if you install the AzureAD module first or the ExchangeOnline module. To install the module, run the following cmdlet:

    • Install-Module AzureAD
    • Accept the warning message displayed regarding the untrusted repository by typing “Y.” Learn more about AzureAD PowerShell module cmdlet reference here: AzureAD Module | Microsoft Docs.

Installing AzureAD PowerShell module using Windows Terminal
Installing AzureAD PowerShell module using Windows Terminal

Installing Exchange Online PowerShell Module

Now, installing the Exchange Online PowerShell module is the same process. To install the Exchange Online Powershell module, run the following cmdlet:

    • Install-Module ExchangeOnlineManagement

Installing the ExchangeOnlineManagement PowerShell module
Installing the ExchangeOnlineManagement PowerShell module

Accept the warning message displayed regarding the untrusted repository by typing “Y.” For details on using the Exchange Online Management PowerShell, look at Microsoft’s Exchange Online PowerShell documentation here: Exchange Online PowerShell | Microsoft Docs.

Allowing all of the features of Exchange Online to PowerShell

By default, all accounts you create in Microsoft 365 can connect to and use Exchange Online PowerShell. However, IT admins can use Exchange Online PowerShell to enable or disable a user’s ability to use Exchange Online PowerShell in the environment.

As a security note, just because a user can connect to Exchange Online PowerShell, it does not give them administrator access. A user’s permissions in Exchange Online are defined by the built-in role-based access control (RBAC) used by Exchange Online.

Using the Exchange Online PowerShell cmdlets shown below, Exchange administrators can enable or disable users’ access to Exchange Online PowerShell.

    • Disable Exchange Online PowerShell – Set-User -Identity myuser@mydomain.com -RemotePowerShellEnabled $false
    • Enable Exchange Online PowerShell – Set-User -Identity myuser@mydomain.com -RemotePowerShellEnabled $true

To enable or disable for multiple users based on a user attribute, you can also use the filtering and piping features discussed above with Exchange Online PowerShell. To enable Exchange Online Powershell for users with a specific Title, like “Manager,” you can do the following:

    • $managers = Get-User -ResultSize unlimited -Filter “(RecipientType -eq ‘UserMailbox’) -and (Title -like ‘Manager*’)”
    • $managers | foreach {Set-User -Identity $_.WindowsEmailAddress -RemotePowerShellEnabled $true}

Connecting to Exchange Online PowerShell with Basic Authentication

If you search for connecting to Exchange Online PowerShell, you will see reference to basic authentication and modern authentication. To follow best practices, don’t attempt to use Basic Authentication any longer. All organizations at this point need to be switching to modern authentication with MFA enabled.

There is an additional reason. Microsoft is deprecating Basic Authentication access to Exchange Online on October 1, 2022. With this announcement, starting on October 1, 2022, they will begin disabling Basic Authentication for Outlook, EWS, RPS, POP, IMAP, and EAS protocols in Exchange Online. SMTP Auth will also be disabled if it is not being used. Read the official announcement here.

If you want to use the older Exchange Online Remote connection using Basic Authentication, you can view those instructions from Microsoft here. Again, note this method will be deprecated later this year.

Connecting to Exchange Online PowerShell with Modern Authentication

To connect to Exchange Online, use the Exchange Online PowerShell V2 module (installation shown above) to connect to your Exchange Online environment. The EXO PowerShell V2 module uses modern authentication and works with multi-factor authentication (MFA) for securing your Exchange Online PowerShell environment.

To connect to your Exchange Online environment, you need to import the ExchangeOnlineManagement module and then use the Connect-ExchangeOnline cmdlet.

    • Import-Module ExchangeOnlineManagement
    • Connect-ExchangeOnline -ShowProgress $true

 

 

Connecting to Exchange Online using the Connect-ExchangeOnline cmdlet
Connecting to Exchange Online using the Connect-ExchangeOnline cmdlet

It will bring up the login box to log into your Office/Microsoft 365 account. It allows taking advantage of the MFA configured for the account, etc.

Logging into Exchange Online with the Exchange Online PowerShell management module
Logging into Exchange Online with the Exchange Online PowerShell management module

The Top 10 Most Common Tasks in Exchange Online PowerShell

Now that we have installed the Exchange Online PowerShell module, what are some common tasks we can accomplish using Exchange Online PowerShell? Let’s take a look at the following:

    1. Getting Migration information
    2. Getting mailboxes
    3. Viewing mailbox statistics
    4. Increasing deleted items retention
    5. Enable Mailbox Audit Logging
    6. Identify inactive mailboxes
    7. Identify mailboxes enabled with forwarding
    8. Setting mailbox autoreply configuration
    9. Assigning roles to users
    10. Identifying ActiveSyncDevices

1. Getting Migration Information

You may be migrating users from one Exchange Server, such as on-premises, to another Exchange Server (Exchange Online). The Get-MigrationUser cmdlet is a great command to check the status of a migration batch used to migrate user batches.

    • Get-MigrationUser -BatchId Marketing | Get-MigrationUserStatistics

Using the Get-MigrationUser
Using the Get-MigrationUser

2. Getting Mailboxes

One of the most basic tasks an Exchange admin needs to carry out is getting information about mailboxes. The most basic cmdlet to use for this use case is the Get-Mailbox cmdlet. The Get-Mailbox cmdlet is generally used with piping into other cmdlets to pull mailboxes meeting specific filters and then performing configuration on the mailboxes queried with the Get-Mailbox cmdlet.

Using the Get-Mailbox cmdlet to get mailbox information in Exchange Online
Using the Get-Mailbox cmdlet to get mailbox information in Exchange Online

3. Viewing mailbox statistics

A common task of Exchange admins is keeping an eye on the size of mailboxes in the environment, so these do not become unwieldy. Using the Get-MailboxStatistics cmdlet allows getting the size information, the number of messages it contains, and the last time it was accessed.

    • Get-MailboxStatistics -identity <username>

Using the Get-MailboxStatistics cmdlet in Exchange Online to get mailbox information
Using the Get-MailboxStatistics cmdlet in Exchange Online to get mailbox information

4. Increasing deleted items retention

By default, Exchange Online is configured to retain deleted items for 14 days. However, this limit can be increased easily for users using the Exchange Online PowerShell module cmdlet Set-Mailbox.

    • Set-Mailbox -Identity “John Doe” -RetainDeletedItemsFor 30

The Set-Mailbox cmdlet allows configuring many aspects of the user mailbox in Exchange Online
The Set-Mailbox cmdlet allows configuring many aspects of the user mailbox in Exchange Online

5. Enable Mailbox Audit Logging

Even though audit logging is on by default for all organizations in Microsoft 365, only users with E5 licenses will return mailbox audit log events in audit log searches. If you want to retrieve audit log events for users without an E5 license, PowerShell is a great way to do that. You can use the Exchange Online PowerShell cmdlet one-liner:

    • Set-Mailbox -Identity <mailbox> -AuditEnabled $true

Using the Set-Mailbox cmdlet to turn on the AuditEnabled flag
Using the Set-Mailbox cmdlet to turn on the AuditEnabled flag

6. Identity mailboxes that are inactive

Using a combination of Exchange Online PowerShell cmdlets and a simple foreach loop, we can see when each user last logged into their mailbox.

    • Get-Mailbox -ResultSize Unlimited | Foreach {Get-MailboxStatistics -Identity $_.UserPrincipalName | Select DisplayName, LastLogonTime}

Getting the last logon time using Exchange Online PowerShell
Getting the last logon time using Exchange Online PowerShell

7. Identify mailboxes enabled with forwarding

What if you want to identify mailboxes enabled with a forwarding address as these have not been documented? You can easily do this with another useful Exchange Online PowerShell one-liner:

    • Get-mailbox -ResultSize Unlimited| where {$_.ForwardingAddress -ne $Null} | select DisplayName,ForwardingAddress

8. Setting mailbox autoreply configuration

A user may forget to set their autoreply configuration. If they go away on vacation or if there is a need to set the autoreply on a user mailbox for other reasons, you can easily accomplish this using PowerShell. It eliminates the need to log in as that user and do this interactively in Outlook.

To do this, you can use the Set-MailboxAutoReplyConfiguration cmdlet. It allows setting both an internal message and an external message for the mailbox.

Setting autoreply messages using PowerShell
Setting autoreply messages using PowerShell

9. Manage roles for groups

Using the New-ManagementRoleAssignment cmdlet, you can assign a management role to a management role group, management role assignment policy, user, or universal security group.

    • New-ManagementRoleAssignment -Role “Mail Recipients” -SecurityGroup “Tier 2 Help Desk”

Assigning management roles using the New-ManagementRoleAssignment cmdlet
Assigning management roles using the New-ManagementRoleAssignment cmdlet

10. Identifying ActiveSync Devices

Identifying and seeing ActiveSync Devices in use in the organization can easily be accomplished with Exchange Online PowerShell using the Get-MobileDevice cmdlet.

Getting mobile devices paired with Exchange Online Users
Getting mobile devices paired with Exchange Online Users

 

To properly protect your Hyper-V virtual machines, use Altaro VM Backup to securely backup and replicate your virtual machines. We work hard perpetually to give our customers confidence in their Hyper-V backup strategy.

To keep up to date with the latest Hyper-V best practices, become a member of the DOJO | Hyper-V now (it’s free).

The Future is Automated

Many organizations are now migrating and hosting their mail services in the cloud. Exchange Online provides businesses with a great way to host their mail services in Microsoft’s cloud infrastructure, either as a standalone subscription or part of their Office/Microsoft 365 subscription.

While Exchange admins can undoubtedly use the GUI management tools for daily tasks, Exchange Online PowerShell provides a great way to automate and carry out everyday tasks much more quickly, efficiently, and using automation. The Exchange Online PowerShell module is easy to install. In addition, it provides quick time to value by allowing Exchange admins to easily query and configure multiple objects in their Exchange Online environments.

Used in automated processes, Exchange Online PowerShell allows Exchange admins to carry out tasks consistently and in a way that helps to eliminate human error from mundane low-level tasks.

The post Top 10 PowerShell Tasks in Exchange Online appeared first on Altaro DOJO | Hyper-V.

]]>
https://www.altaro.com/hyper-v/10-tasks-online-powershell/feed/ 0
How to Quickly Connect to Exchange Online Powershell https://www.altaro.com/hyper-v/exchange-online-powershell/ https://www.altaro.com/hyper-v/exchange-online-powershell/#respond Fri, 18 Jun 2021 09:08:01 +0000 https://www.altaro.com/hyper-v/?p=22988 This article details how to connect to Exchange Online using PowerShell on three platforms: browser, Windows and MacOS.

The post How to Quickly Connect to Exchange Online Powershell appeared first on Altaro DOJO | Hyper-V.

]]>

PowerShell is an effective and powerful way to achieve administrative tasks that either require some degree of automation or perform actions not available in the Exchange Online administration GUI and connect to Exchange Online Powershell environment easily. Those of us who worked on Exchange 2010, remember how the Exchange MMC would generate PowerShell for us and allowed us to learn as we worked. On-premises versions of Exchange still support Exchange Management Shell, however, this article focuses on how to connect to Exchange Online PowerShell.

This article details how to connect to Exchange Online PowerShell on three platforms, including browser only, Windows and MacOS. The version of Exchange Online PowerShell is quite important, as in this article we reference Exchange Online PowerShell Version 2, which can connect to Exchange Online PowerShell using modern authentication (allows you to connect to Exchange Online PowerShell MFA), as opposed to Exchange Online PowerShell Version 1 which uses basic authentication and is considerably less secure as a result with basic authentication as the only available authentication method. However, each can be used to connect PowerShell to Exchange Online. Most will want to connect to Exchange Online PowerShell without Basic Authentication, using MFA.

How do I connect to Exchange Online PowerShell?

In the following section, we are going to be using Exchange Online PowerShell V2 module, which is vastly more optimized for bulk operations than version one, and also supports more than just Windows PowerShell, however more on other modalities further below to connect to Exchange Online using remote PowerShell

The process to connect to Exchange Online Powershell starts with, well, with PowerShell, however, there are a few options that are beyond old fashioned Windows PowerShell. The Exchange Online PowerShell V2 module (referred to as EXO V2 module) is supported in Windows PowerShell 5.1. If you are using PowerShell 7 on Windows, you need version 2.0.4 or later of the EXO V2 module to connect to Exchange Online Powershell.

PowerShell 7 uses browser-based single sign-on (SSO) as the default authentication method, which allows us to take advantage of Multi Factor Authentication (MFA) secured accounts.

Multi Factor Authentication (MFA) secured accounts

Multi Factor Authentication (MFA) secured accounts

PowerShell in a Browser

You read that right, you can launch PowerShell from any modern browser and be connected to Office 365 and Azure using Azure Cloud Shell. What’s even more exciting is that Azure Cloud Shell is natively integrated with the Office 365 Admin Console, allowing you to break out into a PowerShell Console as required.

Browse to https://admin.microsoft.com , login and notice the icon for Cloud Shell on the top right-hand corner.

How to launch Cloud Shell

How to launch Cloud Shell

Clicking on the icon causes Cloud Shell to launch and allows you to choose your command line language of choice. Since we are working with PowerShell, we go ahead and choose PowerShell over Bash, if it isn’t our current default language

Powershell not BASH

Choosing PowerShell as Default Language

We can prove that it is indeed PowerShell and not Bash, by typing in:

Get-Help

And pressing Enter

Browser Cloud Shell

Confirming it’s PowerShell

PowerShell in Windows

PowerShell is built into Windows. Launching PowerShell is as easy as clicking start, typing “PowerShell” and choosing to run it as an administrator, since we will be installing the Exchange Online PowerShell module further on.

PowerShell in Windows

How To Run PowerShell

PowerShell Launches and presents as follows:

PowerShell Launches and presents as follows

How PowerShell looks like

PowerShell on MacOS

PowerShell runs on MacOS allowing you to install modules and run cmdlets as required.

First, install PowerShell on your Mac, however, read the entire article and understand the implications of using one installation method versus another.

The details are beyond the scope of this article; however, the takeaway is that PowerShell runs on your Mac and you’re able to administer Exchange Online using PowerShell.

Once PowerShell is installed, launch a terminal and type:

pwsh

And press enter.

PowerShell on MacOS

Run PowerShell on MacOS

How do I install the Exchange Online PowerShell Module

Installing the Exchange Online Module is relatively straightforward once we have an instance of PowerShell.

In a Browser – Cloud Shell

Cloud Shell has a massive advantage. Authentication and module installation is done for us, continue reading in the next session to see how to connect the PowerShell instance in Cloud Shell to Exchange Online.

PowerShell on Windows

Run the following commands in order:

Set-ExecutionPolicy RemoteSigned

Install-Module PowershellGet -Force

Update-Module PowershellGet

Install-Module -Name ExchangeOnlineManagement

PowerShell on Windows

Connect to Exchange Online

PowerShell on MacOS

Similar to Windows, however with one less command run the following command in order

Run the following commands in order:

Install-Module PowershellGet -Force

Update-Module PowershellGet

Install-Module -Name ExchangeOnlineManagement -force

PowerShell on MacOS

Connect to Exchange Online on MacOS

How do I log into Exchange Online PowerShell

We noted earlier that Exchange Online PowerShell v2 can use PowerShell 7, which uses browser-based single sign-on as the default authentication method. When we invoke the Connect-ExchangeOnline command, it opens the Azure AD login page in your default browser to gather credentials and return the authentication token to the PowerShell session. I am able to specify credentials or use device-based authentication where I may not have a browser or the ability to launch a browser. So, if you are wondering how I access Microsoft Exchange Online, this is it.

PowerShell in a Browser

With Cloud Shell, the prerequisites to install and be able to launch Exchange Online PowerShell are done for us. I’m also authenticated already in my admin session and do not need to log-on to PowerShell again, unless I wish to specify different credentials or manage another organisation.

All I need to do is type:

Connect-EXOPSSession

And hit Enter.

PowerShell in a Browser

Connecting to Exchange Online 1

The Exchange cmdlets download and install, and I am done.

Exchange cmdlets download and install

Connecting to Exchange Online 2

PowerShell on Windows

To recap, we created a PowerShell session in Windows and installed the Exchange Online v2 module. Next we connect to Exchange Online PowerShell by typing in:

ConnectExchangeOnline

And hitting Enter.

PowerShell session in Windows and installed the Exchange Online v2 module

Connect to Exchange Online PowerShell

Once authentication is successful, we are able to issue a Get-Mailbox command to prove that we are connected:

Get-Mailbox command to prove that we are connected

Issue Get-Mailbox command

PowerShell on MacOS

It seems reasonable that just like Cloud-Shell and Windows PowerShell, we will attempt to use the default browser-based SSO method to attempt to log in. At a PowerShell prompt type in

Connect-ExchangeOnline

And hit Enter

browser based SSO

Connect to Exchange Online PowerShell on MacOS – 1

It successfully launches the browser and performs an SSO login:

SSO login

Connect to Exchange Online PowerShell on MacOS – 2

PowerShell SSO login

Connect to Exchange Online PowerShell on MacOS – 3

Review of commands to connect to Exchange Online PowerShell

The following is a review of the PowerShell commands to connect to Exchange Online:

  • Install-module ExchangeOnlineManagement
    • If you want to limit the scope to the current user: Install-Module -Name ExchangeOnlineManagement -Scope CurrentUser
  • To update the module: Update-Module -Name ExchangeOnlineManagement
  • Import-module ExchangeOnlineManagement
  • Connect-ExchangeOnline

To properly protect your Hyper-V virtual machines, use Altaro VM Backup to securely backup and replicate your virtual machines. We work hard perpetually to give our customers confidence in their Hyper-V backup strategy.

 

To keep up to date with the latest Hyper-V best practices, become a member of the Altaro DOJO | Hyper-V now (it’s free).

Conclusion

Exchange Online PowerShell Version 2 is the current version of the Exchange cmdlets. It runs on PowerShell 7 which is able to leverage modern authentication on multiple platforms, including Azure Cloud Shell, Microsoft Windows and Apple MacOS. Installation is quick and straightforward on all platforms and in the case of Azure cloud Shell is simply built in.

Connecting to Exchange Online is an identical experience on all three platforms, using browser based SSO, which allows us to integrate with Multi Factor Authentication. Once you connect to Exchange Online Powershell you can administer Exchange Online, irrespective of platform or operating system in a supported manner.

The post How to Quickly Connect to Exchange Online Powershell appeared first on Altaro DOJO | Hyper-V.

]]>
https://www.altaro.com/hyper-v/exchange-online-powershell/feed/ 0
PowerShell Cmdlets: What they are and how to use them – Part 2 https://www.altaro.com/hyper-v/powershell-cmdlets-part-2/ https://www.altaro.com/hyper-v/powershell-cmdlets-part-2/#respond Thu, 01 Apr 2021 17:28:48 +0000 https://www.altaro.com/hyper-v/?p=19737 Learn how to write your own cmdlets, how you can share them with others and much more in our second part of the PowerShell cmdlets guide.

The post PowerShell Cmdlets: What they are and how to use them – Part 2 appeared first on Altaro DOJO | Hyper-V.

]]>

In the first part of the PowerShell Cmdlets guide, we had a detailed look at what a PowerShell cmdlet is, how to use cmdlets, built-in cmdlets, and useful cmdlets. If you want to give that one a look, head here. As we get into the second part of this guide, we will begin by looking at how to write your own PowerShell cmdlet. We will then have a discussion on modules and what they are used for, after which we will have a look at PowerShell, PowerShell Core and their differences. Let’s dig in! 

Writing your own PowerShell cmdlet

While many PowerShell cmdlets are built into Windows 10 and they’ll most likely perform the task you are looking to accomplish, there may still be a need to write your own PowerShell cmdlet to harness the power of its capabilities. Microsoft does a good job describing the process of writing your own PowerShell cmdlet in six steps, so let’s have a look at them: 

  1. Use the Cmdlet attribute to declare the class as a cmdlet. This attribute identifies the verb and noun for the cmdlet name.
  2. Identify the name of the class
  3. Specify whether or not the cmdlet comes from either of the following classes:
    1. System.Management.Automation.Cmdlet
    2. System.Management.Automation.PSCmdlet
  4. Use the Parameter attribute to define the parameters for the cmdlet
  5. Override the input processing method that processes the input
  6. Use the method System.Management.Automation.Cmdlet.WriteObject

Now that we have covered the basics, let’s take a practical example and have a walkthrough of creating the PowerShell Send-Greeting cmdlet, documented here. We are using Visual Studio 2017 to create a new project, import the code and all dependencies, build the .dll file and import it into PowerShell Core (Note: We will discuss the differences between PowerShell and PowerShell Core in the following section.)

Create a new Class Library (.NET Core).

Create a new ClassLibrary (.NET Core) project in Visual Studio
Create a new ClassLibrary (.NET Core) project in Visual Studio

In the Solution Explorer, right-click the Dependencies and select Manage NuGet Packages.

Managing dependencies
Managing dependencies

Click the “green plus” sign to add a new package source. We will define a PowerShell Core source.

Add a PowerShell package source
Add a PowerShell package source

Click Browse on the Nuget Package Manager and type system.management.automation. Then click the “down arrow” next to the version. This will install the package.

Add System.Management.Automation
Add System.Management.Automation

Next, click the Class1.cs tab and paste the Microsoft-provided code for the example cmdlet.

Paste the Microsoft example PowerShell cmdlet code into Visual Studio
Paste the Microsoft example PowerShell cmdlet code into Visual Studio

Hit F6 or use the Build menu to build the project. Note below after you build the project, you will see the path to the resulting .dll file.

Build the .dll file for the PowerShell cmdlet
Build the .dll file for the PowerShell cmdlet

All that is left to do is import the .dll file into PowerShell and the cmdlet will be available. Here we are testing the new Send-greeting cmdlet. It works as expected.

Import the module and run the new cmdlet

Import the module and run the new cmdlet

Modules

After getting the hang of writing your own cmdlet, you will probably want to write more cmdlets that may be related. For those of you who write cmdlets, it’s useful to know that you can use PowerShell modules to share these with others. A module is simply a package of PowerShell cmdlets and providers. Interestingly, all cmdlets and providers in your PowerShell session are added by way of modules or snap-ins and are stored this way.

Snap-ins are the older way of importing cmdlets into PowerShell and have limitations such as having to be available as an Assembly and Windows registry requirements. Modules do not have many of the limitations of snap-ins and are not reliant on being registered in the Windows registry.

There are a wide variety of readily available modules by way of PowerShell Package Providers. These provide a unified way to install various modules available for PowerShell outside of what is included by default in Windows. Later versions of PowerShell introduced cmdlets to interact with, import, install and remove various Powershell Package Providers. These cmdlets include:

  • Get-PackageProvider
  • Get-PackageSource
  • Register-PackageSource
  • Set-PackageSource
  • Unregister-PackageSource
  • Get-Package
  • Find-Package
  • Install-Package
  • Save-Package
  • Uninstall-Package

You can learn more about Package Management in Powershell in the official Microsoft documentation for Package Management.

Installing Modules

As mentioned earlier, Windows 10 includes many different PowerShell cmdlets that can be used by default. However, you can add additional PowerShell cmdlets to your PowerShell capabilities by installing Windows features. A classic example of how easy this is to do is adding the Hyper-V Module for Windows PowerShell. If you want to use a Windows 10 management workstation, adding the Hyper-V Powershell cmdlets allows you to easily interact with a Hyper-V environment and perform tasks in an automated way from the command line.

Adding Hyper-V PowerShell Module using Windows Features
Adding Hyper-V PowerShell Module using Windows Features

You can also use PowerShell to add the Hyper-V PowerShell Module for Hyper-V. You can do this by using the following PowerShell cmdlet:

  • Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-Management-PowerShell

There are many other Microsoft products and solutions that provide an easy way to interact with PowerShell. You will find PowerShell modules for the likes of:

  • Microsoft Exchange Server
  • Microsoft SharePoint
  • Microsoft System Center (SCCM Powershell cmdlets)
  • Microsoft Azure

Aside from enabling Windows 10 features along with PowerShell cmdlets as shown with installing Hyper-V and its management tools, PowerShell also allows easily installing module from online resources. The PowerShell Gallery is one such online resource. What is the PowerShell Gallery?

  • The PowerShell Gallery is a repository of PowerShell modules and content. You can find myriads of useful PowerShell modules and Desired State Configuration (DSC) resources. Scripts are also available directly from Microsoft, as well as those in the community.

The Microsoft Azure module can be installed using the PowerShell Gallery. The Microsoft Azure PowerShell module allows easy interaction with Azure from an on-premises management workstation. Microsoft has provided a script that allows installing the Azure PowerShell Module in Windows 10.

if ($PSVersionTable.PSEdition -eq ‘Desktop’ -and (Get-Module -Name AzureRM -ListAvailable)) {

Write-Warning -Message (‘Az module not installed. Having both the AzureRM and ‘ +

‘Az modules installed at the same time is not supported.’)

} else {

Install-Module -Name Az -AllowClobber -Scope CurrentUser

}

By default, the PowerShell gallery is not a trusted repository for PowerShellGet.

  • PowerShellGet is a module that is used for all tasks related to finding, installing, updating PowerShell modules, among other PowerShell artifacts.

The first time you use the PSGallery, you see the following prompt:

Trusting the PowerShell gallery during the installation of the Azure PowerShell Module
Trusting the PowerShell gallery during the installation of the Azure PowerShell Module

Interacting with Microsoft Azure using the Azure PowerShell cmdlets provides a far superior experience when performing automated tasks or operations at scale.

Using Find-Module and Install-Module cmdlets

You can easily find PowerShell modules from the gallery by using the Find-Module cmdlet. By using the Find-Module cmdlet, you can search for a particular PowerShell module to install. There are thousands of modules to pull from using the online PowerShell gallery.

Note below. You can use the “| more” pipeline to see the results a section at a time.

Using the Find-Module cmdlet to find specific modules in the PowerShell gallery
Using the Find-Module cmdlet to find specific modules in the PowerShell gallery

When you find a specific module that you would like to install, you can use the Install-Module cmdlet to install the PowerShell module. As an example, the PSWindowsUpdate PowerShell module allows you to use PowerShell to install Windows Updates in an automated fashion, which is extremely powerful.

Finding the PSWindowsUpdate Powershell module from the PowerShell Gallery
Finding the PSWindowsUpdate Powershell module from the PowerShell Gallery

To install the module, use the Install-Module cmdlet to install the module.

Using the PSWindowsUpdate module to query and install Windows Updates in Windows 10
Using the PSWindowsUpdate module to query and install Windows Updates in Windows 10

Powershell vs. PowerShell Core

Before beginning to interact with PowerShell and before creating your own PowerShell cmdlets, it will come in handy to understand the differences between PowerShell and PowerShell Core as choosing to use either will have implications on the capabilities and functionality of PowerShell.

If you start looking at PowerShell today, you will undoubtedly notice two different PowerShell environments referenced – PowerShell and PowerShell Core. What are the differences between the two? When should you use PowerShell Core?

Understanding differences between PowerShell and PowerShell Core

You certainly want to understand the differences between the two as you start getting into using PowerShell in your environment. PowerShell Core (version 6.x and higher) is the newer technology of the two. PowerShell Core receives its name from .NET Core as this is its underlying .NET architecture, and it is the way of PowerShell moving forward. Microsoft is shifting its focus to .NET Core and PowerShell Core as the newer architecture that will see new capabilities and features in future updates.

Legacy PowerShell 5.x will continue to receive enhancements and security updates. Still, the focus will be shifting applications and scripts to use PowerShell Core in most environments unless there is a current incompatibility between a specific application and the newer PowerShell Core platform. One of the significant features of PowerShell Core, being built on .NET Core, is that it is a cross-platform architecture. In other words, with PowerShell Core, you can now have PowerShell on Windows, Mac, and Linux!

Another nice feature with PowerShell Core is that it can be installed side-by-side with PowerShell 5.x in Windows. It means you can use both products on a Windows installation, which is a great way to transition scripts over to using PowerShell Core as this is the way forward. As a side note, the executable for Powershell Core is pwsh.exe to support the side-by-side execution of both PowerShell platforms. Legacy PowerShell retains the powershell.exe name.

Installing PowerShell Core

One of the differences that you will notice from the start with PowerShell Core is the installation. While Powershell 5.x comes with modern Windows versions, PowerShell Core is readily available from the official Github page. It helps to underscore the fact that it is a truly openly available platform. While it is openly available to anyone, it is an officially sanctioned release maintained by Microsoft.

After downloading the official release from the Github page, it is as simple as running the installation of PowerShell Core. The installation is a straightforward “next, next, finish” process.

Installing the latest version of PowerShell Core
Installing the latest version of PowerShell Core

There are several options available to configure when installing PowerShell Core. Below, the default settings are listed. As you can see, you can configure PowerShell remoting and other optional features during the installation.

Optional Actions when installing PowerShell Core
Optional Actions when installing PowerShell Core

After installing, you can launch PowerShell Core along with the legacy version of PowerShell 5.x, all from the same Windows 10 machine. You will note a difference between the two PowerShell windows. The new PowerShell Core prompt has a black background by default, while the standard PowerShell has a blue background.

Comparing PowerShell Core and legacy PowerShell environments
Comparing PowerShell Core and legacy PowerShell environments

Of interest on PowerShell cmdlets, with the newest version of PowerShell Core, PowerShell Core 7.x, Microsoft has improved backwards compatibility with legacy Window PowerShell modules. It makes use of .NET Core 3.1 and allows PowerShell users to make use of many of the modules on Windows that require GUI functionality like Out-GridView and Show-Command.

A new PowerShell Core switch parameter has been added to the Import-Module cmdlet, called UseWindowsPowerShell. This new parameter is a proxy module in PowerShell Core that uses the local Windows PowerShell process to run cmdlets written for legacy PowerShell. It dramatically improves the experience of natively using the PowerShell Core interface.

Are PowerShell cmdlets worth using?

Spoiler alert, yes they are! PowerShell and, by extension, PowerShell cmdlets provide a powerful way to automate and interact with modern infrastructure. You can use PowerShell effectively on-premises or in the cloud. PowerShell cmdlets offer an easy and intuitive way to write object-oriented code, with little coding experience in other languages. The verb-noun syntax is easily understood and “makes sense” to most. IT administrators across many different disciplines have embraced PowerShell and using PowerShell cmdlets for writing scripts to manage and automate their environments. Most modern software and hardware vendors are providing PowerShell modules for interacting with and automating their respective platforms.

Windows PowerShell contains hundreds of built-in PowerShell cmdlets out of the box. You can extend Windows PowerShell features by adding components in Windows features containing PowerShell management modules. Hyper-V PowerShell Windows Management Module is one such feature. Additionally, you can add new PowerShell cmdlets by way of installing new PowerShell modules. You can easily use the Find-Module and Install-Module cmdlets to search for new cmdlets needed and install these in your PowerShell environment. The easily accessible PowerShell Gallery contains thousands of modules, cmdlets, DSC configurations, and even PowerShell scripts that administrators can easily take advantage of in their environments.

PowerShell comes in two different versions today. These include legacy PowerShell 5.x that is included in current Windows versions as well as PowerShell Core. PowerShell Core is the newest release of PowerShell that will see continued development and new features added. The PowerShell Core platform will be the platform receiving major new features and capabilities, so keep that in mind for the future.

I hope you found this guide useful, and if you have started writing your own PowerShell Cmdlets by now, congratulations! Be sure to share them in the comments and let me know if you have any questions.

The post PowerShell Cmdlets: What they are and how to use them – Part 2 appeared first on Altaro DOJO | Hyper-V.

]]>
https://www.altaro.com/hyper-v/powershell-cmdlets-part-2/feed/ 0
PowerShell Cmdlets: What they are and how to use them – Part 1 https://www.altaro.com/hyper-v/powershell-cmdlets-part-1/ https://www.altaro.com/hyper-v/powershell-cmdlets-part-1/#respond Thu, 18 Mar 2021 15:33:09 +0000 https://www.altaro.com/hyper-v/?p=19736 PowerShell cmdlets provide many powerful capabilities for automation, management and configuration. But what are they and what do they do? Let's have a closer look.

The post PowerShell Cmdlets: What they are and how to use them – Part 1 appeared first on Altaro DOJO | Hyper-V.

]]>

From the early days of PowerShell 1.0, released in 2006, PowerShell has revolutionized the way system administrators carry out automation tasks in Windows environments. Swiftly, it has replaced the beloved command shell in Windows as the preferred command-line tool of choice. PowerShell provides a relatively easy to learn, powerful, and flexible automation tool for many different use cases.

Part of the success of PowerShell has been a result of the PowerShell cmdlet. In this two-part series, we will take a detailed look at the PowerShell cmdlet. In part one, we will discuss what the PowerShell cmdlet is and how it can be used. In part two, we will take a closer look at how you write one, how they are installed, PowerShell vs. PowerShell Core, and installing PowerShell Core.

What are PowerShell cmdlets?

Let’s first take a look and see what a PowerShell cmdlet is precisely. A PowerShell cmdlet (pronounced commandlet) is a lightweight command used in PowerShell to perform an action. A PowerShell cmdlet is not like a DOS script that you may have written in the past. DOS scripts are known as uncompiled code. PowerShell cmdlets are like tiny executable programs that take advantage of all the constructs and built-in facilities found in .NET Framework.

Since it utilizes .NET framework, this means that PowerShell is object-oriented. It makes PowerShell much more robust than the standard command-line environments we have used traditionally. It can return more than simple strings. Instead, it can return objects and can be further used to perform other useful tasks. Since PowerShell works with objects, these objects have attributes and methods.

The robust framework of .NET makes writing a useful PowerShell cmdlet relatively easy to do. However, Windows 10 includes many useful PowerShell cmdlets already. When you access PowerShell for the first time, you can see many readily available cmdlets that can be used by default. The inclusion of this large number of cmdlets makes the “time to value” very quick, and it becomes immediately useful in most environments.

You can use the Get-Command to display the built-in cmdlets that are available. There are so many that you will need to use the Get-Command | more >parameter to scroll through the extensive list effectively.

Get-Command displaying a large number of built-in PowerShell cmdlets
Get-Command displaying a large number of built-in PowerShell cmdlets

With many built-in cmdlets available in Windows 10, you don’t have to know how to write a PowerShell cmdlet, or even know any .NET coding to begin working with PowerShell. You can start interacting with the built-in cmdlets and quickly using it to automate various processes and tasks.

Very human-readable

One of the strengths of using PowerShell as an IT administrator is it is very “human-readable.” Unlike other programming languages that may take years to become proficient, PowerShell is a very easy to learn language due to the way cmdlets follow an intuitive verb-noun pattern. PowerShell uses these intuitive verbs to perform operations in the environment. When you use the verb-noun pattern cmdlets such as Get-Help or Get-Process you are getting information returned to you.

When you use the verb-noun cmdlets such as Set-Disk, Set-DnsServer, and others, you set or modify the environment’s configuration. An action is performed. Again, these verb-noun patterns used with PowerShell are easy to understand and grasp for those new to the language. These are not the only verbs in PowerShell. Many others can be used, including Add, Convert, Copy, Debug, Disable, Export, and Move, to name a few.

Glossary of PowerShell Terms

To further understand PowerShell cmdlets, let’s define the following PowerShell terms to get a better picture of the underlying components.

  • Classes – Classes specify details what PowerShell objects contain. The class is directly responsible for determining the actions that can be performed on an object
  • Parameters – Parameters are extremely useful as they allow the PowerShell end-user to provide input to a PowerShell cmdlet
  • Modules – Think of a module as a group of related PowerShell functionalities. This can be a set of assemblies, related resources, and script files that can be loaded
  • Alias – An Alias is an alternate name that refers to a cmdlet or command. These can be user-defined
  • Objects – PowerShell is based on .NET which is an object-oriented programming language. Everything contained in PowerShell is an object
  • Methods – Methods are basically the instructions that define the actions that can be performed on PowerShell objects

Tab completion

There is a convenient way to enter the full names of the various PowerShell cmdlets available with PowerShell. Some of the cmdlets may contain several words in the verb-noun pattern. To help with the challenge of entering very long PowerShell cmdlets, you can use tab completion to take care of the process for you. In other words, you have to enter enough of the cmdlet to be unique from other cmdlets with similar names, hit Tab, and it will automatically complete the cmdlet name for you. When you press Tab continuously, it will cycle through the matching cmdlet names.

This process eliminates the tedious nature of typing out the full name of very long PowerShell cmdlets and saves time writing PowerShell code. Interestingly, tab completion is also effective with PowerShell cmdlet parameters. It means you can start typing a parameter for the cmdlet in question, and it will automatically complete the parameter name for you.

Built-in aliases and case

One of the great things Microsoft has built into PowerShell is aliases. Aliases in PowerShell provide access to the same functionality that many are familiar with in a DOS prompt. An example of how this is implemented in PowerShell is the dir command. As we all know, the dir command displays a directory listing of the files and folders in a particular directory.

Using the "dir" command in a command prompt
Using the “dir” command in a command prompt

Using the dir command at a PowerShell prompt will return relatively the same information.

Running the dir command in PowerShell
Running the dir command in PowerShell

Is PowerShell calling the dir command in a PowerShell environment? No, it is using the native Powershell Get-ChildItem cmdlet, aliased to the dir command. If you want to see this preconfigured alias, you can use the cmdlet Get-Alias.

Using Get-Alias to view aliases in PowerShell
Using Get-Alias to view aliases in PowerShell

With aliases in PowerShell, this often helps maintain and make use of a large vocabulary of DOS commands and other command-line tools without necessarily learning the PowerShell equivalent. However, one must keep in mind that a familiar command is an alias to a native PowerShell cmdlet. It means there are differences between the two in terms of parameters. You can’t use the same DOS parameters for the aliased cmdlets in PowerShell. Notice when attempting to use dir command parameters in PowerShell, you receive an error.

PowerShell aliases can't use the same parameters as DOS command equivalents
PowerShell aliases can’t use the same parameters as DOS command equivalents

What about uppercase vs. lowercase in PowerShell? You don’t have to worry with PowerShell about case. The PowerShell interpreter views the following as the same cmdlet:

  • Get-Process, get-Process, get-process, Get-process

It helps alleviate any issues that you might see with PowerShell code based on upper and lower case cmdlets. Again, this reduces the barrier to entry when learning how to write PowerShell code.

Useful PowerShell cmdlets

There is a virtually unlimited number of useful PowerShell cmdlets. You can find a cmdlet or snippet of PowerShell code to do just about anything you want to do. Let’s list a few useful PowerShell cmdlets to note.

  • Get-Help – This is one of the first basic cmdlets that you should become familiar with as it will help you understand what the different cmdlets in PowerShell can do. Get-Help will prompt you to update your help files online to ensure you have the latest version of help regarding the latest cmdlets. After you update, you can use Get-Help to get what is essentially a “man page” in the PowerShell world about any other cmdlet.

Using the Get-Help cmdlet to understand the Get-Service cmdlet
Using the Get-Help cmdlet to understand the Get-Service cmdlet

  • Get-Service – The Get-Service cmdlet helps to quickly get a listing of your system’s various installed services. It includes their Status, Name, and DisplayName.
  • Get-Process – Get-Process pulls a current list of running processes as well as their ProcessName and ID. You can easily pipe the returned process objects into the Stop-Process cmdlet to kill a running process.
  • Restart-Computer – This cmdlet can restart both local and remote computers
  • Shutdown-Computer – Use this cmdlet to shut down a local or remote computer
  • Select-Object – The Select-Object cmdlet allows you to select a specific returned object from another PowerShell cmdlet. It will enable parsing down returned PowerShell objects to only those objects you need
  • Stop-Process – Kill a specific running process on your system
  • Get-ExecutionPolicy – You will need to understand the execution level of the system you are working with using PowerShell. It is a security mechanism to help define the various scripts that will be trusted to run.
  • Set-ExecutionPolicy – Use the Set-ExecutionPolicy to configure the execution of PowerShell scripts on an end-user system.
  • -WhatIf – The “WhatIf” parameter is not a cmdlet in its own right but rather a parameter that can provide the cmdlet action results before you run it. It is a handy parameter to use as it will detail the particular actions taken when running a specific cmdlet without actually running the cmdlet.

In this first part of the series, we have taken a detailed look at what a PowerShell cmdlet is, aliases, built-in cmdlets, useful PowerShell cmdlets, and how these are used. Stay tuned for the next part, where we will detail how you can go about writing your own PowerShell cmdlet, modules, PowerShell vs. PowerShell Core, installing PowerShell Core, and many other topics.

The post PowerShell Cmdlets: What they are and how to use them – Part 1 appeared first on Altaro DOJO | Hyper-V.

]]>
https://www.altaro.com/hyper-v/powershell-cmdlets-part-1/feed/ 0
Top 10 PowerShell commands for Hyper-V https://www.altaro.com/hyper-v/powershell-commands-hyper-v/ https://www.altaro.com/hyper-v/powershell-commands-hyper-v/#respond Fri, 19 Feb 2021 07:42:09 +0000 https://www.altaro.com/hyper-v/?p=19618 Microsoft Hyper-V is a robust hypervisor that can easily be automated using PowerShell. Here is a look at the top 10 PowerShell commands for Hyper-V.

The post Top 10 PowerShell commands for Hyper-V appeared first on Altaro DOJO | Hyper-V.

]]>

Microsoft’s Hyper-V hypervisor is an extremely powerful enterprise-ready hypervisor that has grown and matured in recent Windows Server versions. Hyper-V contains relative feature parity with other hypervisors on the market and allows businesses to maintain alignment with current Windows Server technologies they are already using on-premises and in the cloud.

One of the powerful features offered by Hyper-V is management with PowerShell. PowerShell is a relatively straightforward scripting language built on top of .NET. Utilizing .NET framework provides a fully object-oriented underlay that makes the language extremely robust. Combining Hyper-V and PowerShell features allows IT administrators to have the features and capabilities needed to configure, manage, and administer their Hyper-V environment. This post will feature the top 10 PowerShell commands for Hyper-V, why they are useful, and how to use them.

What is PowerShell?

For those who have not heard of or used Microsoft PowerShell, it is an object-oriented scripting language built on top of .NET framework. In the case of PowerShell Core, .NET Core is the underlying object-oriented language. PowerShell provides a very “human-readable” verb-noun construct that is relatively intuitive and easy to learn, even for beginners.

Even outside the Microsoft ecosystem, most solutions today provide PowerShell integration to provide easy automation and configuration. PowerShell’s command-line capabilities provide quick and easy ways to accomplish many tasks and actions in a much more efficient and effortless way than using the GUI-based counterparts.

Why Use PowerShell with Hyper-V?

Managing, configuring, and administering Windows Server Hyper-V on a day-to-day basis includes performing many different tasks. Like other hypervisors, Hyper-V provides the ability to carry out needed tasks in GUI tools that can be used with Hyper-V. What are the GUI tools that administrators typically make use of with Hyper-V? These include:

  • Hyper-V Manager – Managing individual Hyper-V hosts, creating virtual switches
  • Failover Cluster Administrator – The Failover Cluster Administrator tool is used to manage Windows Server Failover Clusters with the Hyper-V clustered role
  • System Center Virtual Machine Manager (SCVMM) – Built on top of Microsoft System Center, SCVMM provides the “Cadillac” of features for managing Hyper-V clusters using GUI tools.
  • Windows Admin Center – The newest GUI tool on the block. It provides robust GUI management of Hyper-V and Windows Failover Clusters. Certain management capabilities are not yet available in Windows Admin Center.

Outside of the GUI tools mentioned above, Microsoft has provided Hyper-V PowerShell cmdlets that allow interacting with Hyper-V hosts, clusters, and virtual machines with ease. PowerShell provides a quick and easy way to do many common tasks and activities from the command line instead of using GUI tools for the same purpose. Also, PowerShell provides a powerful way to automate your Hyper-V configurations. Using PowerShell, configurations and settings changes can be made consistently and accurately across many Hyper-V hosts simultaneously. You can perform management operations on multiple Hyper-V virtual machines and enforce configuration management to ensure the environment’s desired state.

While the Hyper-V GUI tools are valuable and useful for one-off management activities, effective and proficient Hyper-V administrators should be familiar with PowerShell commands for Hyper-V. These take managing and configuring Hyper-V environments to the next level of consistency and efficiency.

Installing Hyper-V PowerShell Cmdlets

How do you take advantage of Microsoft Hyper-V PowerShell commands? As with most PowerShell functionality, these are available by way of a PowerShell module. When you install the Windows Server Hyper-V Role, you want to make sure to also enable the Remote Server Administration Tools > Role Administration Tools > Hyper-V Management Tools.

As part of the Hyper-V Management tools that include Hyper-V Manager, you also get the Hyper-V PowerShell module installed to interact with your Hyper-V environment by using PowerShell. If you use Server Manager to install the Hyper-V Role, the Hyper-V Management Tools are found under the Features installation in the path mentioned above.

Installing the Hyper-V Management Tools including the PowerShell module
Installing the Hyper-V Management Tools including the PowerShell module

If you install the Windows Server Hyper-V Role using PowerShell, you will want to use the following cmdlet to install both the Role as well as the Management Tools.

Install-WindowsFeature -Name Hyper-V -ComputerName <computer_name> -IncludeManagementTools -Restart

If you are installing on the Hyper-V server itself, you can omit the -Computername parameter:

Install-WindowsFeature -Name Hyper-V -IncludeManagementTools -Restart

How do I run a PowerShell command?

Now that you have Hyper-V installed on a host and have the Hyper-V Management Tools installed, how do you run a PowerShell command? Microsoft has made this easy in recent versions of Microsoft Windows with easy access to the PowerShell command prompt. Windows PowerShell has been included natively in Windows versions since Windows 7 and Windows Server 2008.

The Windows PowerShell prompt is easily accessible in modern variants of Windows. Below is Windows Server 2019. When you right-click on the Windows button, you get the option for Windows PowerShell and Windows PowerShell (Admin). Launching either of these options will launch the PowerShell environment for interacting with Hyper-V PowerShell cmdlets.

Windows PowerShell in Windows Server 2019
Windows PowerShell in Windows Server 2019

There is another option for running PowerShell and interacting with Hyper-V cmdlets. PowerShell Core is the newest version of PowerShell. It is not embedded by default in Windows operating systems like Windows PowerShell and is a cross-platform variant that can also run on Linux and macOS. It will provide the newest functionality and capabilities moving forward.

Installing PowerShell Core
Installing PowerShell Core

You can use either platform to interact with Hyper-V PowerShell modules. Below is an example of running the same Hyper-V Get-VM cmdlet using either Windows PowerShell or PowerShell Core on the same server returns the same results. The PowerShell Core prompt has a black background and displays PowerShell 7.1.1. Windows PowerShell has a blue background.

Comparing PowerShell Core with Windows PowerShell
Comparing PowerShell Core with Windows PowerShell

Top 10 PowerShell commands for Hyper-V

PowerShell provides a tremendously powerful way to manage, configure, and automate Hyper-V environments. Using PowerShell for Hyper-V need not be intimidating or complicated. Often, it starts by merely learning a few PowerShell commands in the form of Hyper-V cmdlets that allow performing actions in the Hyper-V environment. Let’s start with the top 10 PowerShell commands for Hyper-V to begin forming the knowledge required to interact with Microsoft Hyper-V using PowerShell.

The top 10 PowerShell commands for Hyper-V include:

  1. Get-Help
  2. Get-VM
  3. Get-VMHost
  4. New-VM
  5. Start-VM
  6. Stop-VM
  7. Checkpoint-VM
  8. Measure-VM
  9. Export-VM
  10. New-VHD

We will look at each of the above PowerShell commands for Hyper-V to see what they are used for, how they are used, and other details regarding the cmdlets.

1. Get-Help

The Get-Help PowerShell cmdlet is not a specific Hyper-V PowerShell cmdlet. However, it is one that you need to be familiar with when working with Hyper-V or any other solution by way of PowerShell. The Get-Help cmdlet intuitively provides “help” to allow you to understand what a particular PowerShell cmdlet does, its syntax, and examples of how to use it. If you forget what a certain Hyper-V cmdlet does, you can use the Get-Help cmdlet to gather information about the cmdlet in question.

When you first run Get-Help on a server in a PowerShell session, it will ask you to run the Update-Help cmdlet.

Running Update Help to ensure the most current version of get-help is installed

Running Update-Help to ensure the most current version of get-help is installed

Below is some of the output of the Get-Help cmdlet as displayed in the PowerShell environment.

 

GET-HELP

The Get-Help cmdlet displays help at the command line from content in help files on your computer. Without help files, Get-Help displays basic help about cmdlets and functions. You can also use Get-Help to display online help for cmdlets and functions.

To get help for a cmdlet, type:

Get-Help <cmdlet-name>

To get online help, type:

Get-Help <cmdlet-name> -Online

The titles of conceptual topics begin with “About_”.

To get help for a concept or language element, type:

Get-Help About_<topic-name>

To search for a word or phrase in all help files, type:

Get-Help <search-term>

For more information about the Get-Help cmdlet, type:

Get-Help Get-Help -Online

EXAMPLES:

Get-Help Get-Process: Displays help about the Get-Process cmdlet.

Get-Help Get-Process -Online: Opens online help for the Get-Process cmdlet.

Help Get-Process: Displays help about Get-Process one page at a time.

Get-Process -? : Displays help about the Get-Process cmdlet.

Get-Help About_Modules: Displays help about Windows PowerShell modules.

Get-Help remoting: Searches the help topics for the word “remoting.”

2. Get-VM

One of the most basic and needed Hyper-V cmdlets that Hyper-V administrators will use daily as a top PowerShell command for Hyper-V is the Get-VM cmdlet. The Hyper-V hypervisor runs virtual machines. You use virtual machines in the environment to run workloads that house services, applications, and APIs. Getting information about your Hyper-V virtual machines in PowerShell allows working with the virtual machines on your Hyper-V hosts. The Get-VM PowerShell cmdlet does just that.

Below, running the Get-VM cmdlet returns the VM running on a Hyper-V host. You see information such as:

  • Name, State, CPU Usage, Memory Assigned, Uptime, Status, and Version
  • Using the | fl pipe, you see a bit more information, including the Memory Demand, Memory Status, VM generation, and Replication State.

PowerShell Get-VM cmdlet

Output from running the Get-Help command on Get-VM shows the following.

NAME

Get-VM

SYNOPSIS

Gets the virtual machines from one or more Hyper-V hosts.

SYNTAX

Get-VM [[-Name] <String[]>] [-CimSession <CimSession[]>] [-ComputerName <String[]>] [-Credential <PSCredential[]>][<CommonParameters>]

Get-VM [[-Id] <Guid>] [-CimSession <CimSession[]>] [-ComputerName <String[]>] [-Credential <PSCredential[]>][<CommonParameters>]

 

Get-VM [-ClusterObject] <PSObject> [<CommonParameters>]

 

DESCRIPTION

The Get-VM cmdlet gets the virtual machines from one or more Hyper-V hosts

 

3. Get-VMHost

One of the basic requirements of managing a Hyper-V environment is getting information about your Hyper-V host. The Get-VMHost cmdlet is useful to get high-level information regarding the hardware and other features of your Hyper-V host. Just running the Get-VMHost cmdlet will return minimal information.

Get-VMHost cmdlet general information about the Hyper-V host
Get-VMHost cmdlet general information about the Hyper-V host

Below, using the Format-List or | fl pipe with the Get-VMHost cmdlet provides much more information returned regarding the Hyper-V host.

Get-VMHost with the Format-list pipe
Get-VMHost with the Format-list pipe

NAME

Get-VMHost

 

SYNOPSIS

Gets a Hyper-V host.


SYNTAX

Get-VMHost [-CimSession] <CimSession[]> [<CommonParameters>]

Get-VMHost [[-ComputerName] <String[]>] [[-Credential] <PSCredential[]>] [<CommonParameters>]

DESCRIPTION

The Get-VMHost cmdlet gets a Hyper-V host.

4. New-VM

When working with Hyper-V, one of the tasks that administrators will be tasked with is created new Hyper-V virtual machines. It can be accomplished through the GUI tools mentioned earlier. However, a simple and straightforward way to create new virtual machines is to use the New-VM PowerShell cmdlet.

New-VM cmdlet creating a new Hyper-V virtual machine
New-VM cmdlet creating a new Hyper-V virtual machine

With the New-VM cmdlet, you can create a new Hyper-V virtual machine and customize many aspects of the virtual machine in the process of creating it. Below is the output of the Get-Help New-VM cmdlet.

NAME

New-VM

SYNOPSIS

Creates a new virtual machine.

SYNTAX

New-VM [[-Name] <String>] [[-MemoryStartupBytes] <Int64>] [[-Generation] {1 | 2}] [-AsJob][-BootDevice {Floppy | CD | IDE | LegacyNetworkAdapter | NetworkAdapter | VHD}] [-CimSession <CimSession[]>] [-ComputerName <String[]>] [-Confirm] [-Credential <PSCredential[]>] [-Experimental][-Force] -NewVHDPath <String> -NewVHDSizeBytes <UInt64> [-Path <String>] [-Prerelease] [-SwitchName <String>] [-Version <Version>] [-WhatIf] [<CommonParameters>]

New-VM [[-Name] <String>] [[-MemoryStartupBytes] <Int64>] [[-Generation] {1 | 2}] [-AsJob][-BootDevice {Floppy | CD | IDE | LegacyNetworkAdapter | NetworkAdapter | VHD}] [-CimSession <CimSession[]>] [-ComputerName <String[]>] [-Confirm] [-Credential <PSCredential[]>] [-Experimental][-Force] [-NoVHD] [-Path <String>] [-Prerelease] [-SwitchName <String>] [-Version <Version>][-WhatIf] [<CommonParameters>]

New-VM [[-Name] <String>] [[-MemoryStartupBytes] <Int64>] [[-Generation] {1 | 2}] [-AsJob][-BootDevice {Floppy | CD | IDE | LegacyNetworkAdapter | NetworkAdapter | VHD}] [-CimSession <CimSession[]>] [-ComputerName <String[]>] [-Confirm] [-Credential <PSCredential[]>] [-Experimental][-Force] [-Path <String>] [-Prerelease] [-SwitchName <String>] -VHDPath <String> [-Version <Version>][-WhatIf] [<CommonParameters>]

5. Start-VM

Performing power operations on Hyper-V virtual machines is a crucial task involved with day-to-day Hyper-V administration activities. It includes powering on virtual machines either through a scripting process or using ad-hoc operations. The Start-VM cmdlet is the Hyper-V PowerShell command used to power on a Hyper-V virtual machine and allows it to boot.

Examples of the Start-VM cmdlet:

  • Start-VM -Name TestVM3 – Starts the specific VM “TestVM3”
  • Start-VM -Name Win* – This cmdlet syntax starts all VMs that start “Win”

NAME

Start-VM

SYNOPSIS

Starts a virtual machine.

SYNTAX

Start-VM [-Name] <String[]> [-AsJob] [-CimSession <CimSession[]>] [-ComputerName <String[]>][-Confirm] [-Credential <PSCredential[]>] [-Passthru] [-WhatIf] [<CommonParameters>]

Start-VM [-VM] <VirtualMachine[]> [-AsJob] [-Confirm] [-Passthru] [-WhatIf] [<CommonParameters>]

DESCRIPTION

The Start-VM cmdlet starts a virtual machine.

6. Stop-VM

As we have discussed above, the Start-VM cmdlet allows starting a Hyper-V virtual machine. The counterpart to starting or booting up a Hyper-V virtual machine is stopping the VM. To do this, you use the Stop-VM Hyper-V cmdlet. An important point to note is that the Stop-VM cmdlet has a few parameters that control whether the operating system’s shutdown is graceful or is not graceful. An ungraceful shutdown is similar to pulling the power plug on a physical server. Using the Stop-VM cmdlet covers a wide range of scenarios that provides granular control over how VMs are powered down.

With the Stop-VM cmdlet, graceful operating system shutdown depends on Hyper-V Integration Services being installed in the guest operating system. Hyper-V Integration Services provide enhancements and integration between Hyper-V and the guest operating system. It is required for actions such as graceful operating system shutdown signalling.

There are three examples to note with the Stop-VM cmdlet covering the range of virtual machine shutdown scenarios.

  • Stop-VM -Name TestVM – When using the Stop-VM cmdlet with the name of the VM
  • Stop-VM -Name VM1 -Force – Using the Force parameter invokes the five minute grace period to allow the operating system to save application data before shutdown gracefully. After the five minutes have elapsed, the VM is forcefully shutdown.
  • Stop-VM -Name TestVM -TurnOff – This performs an ungraceful shutdown of the virtual machine similar to pulling the “power cable.”

Stop-VM and message regarding Integration Services
Stop-VM and message regarding Integration Services

Integration services installed in Windows 10
Integration services installed in Windows 10

NAME

Stop-VM

SYNOPSIS

Shuts down, turns off, or saves a virtual machine.

SYNTAX

Stop-VM [-Name] <String[]> [-AsJob] [-CimSession <CimSession[]>] [-ComputerName <String[]>]

[-Confirm] [-Credential <PSCredential[]>] [-Force] [-Passthru] [-Save] [-TurnOff] [-WhatIf]

[<CommonParameters>]

Stop-VM [-VM] <VirtualMachine[]> [-AsJob] [-Confirm] [-Force] [-Passthru] [-Save] [-TurnOff] [-WhatIf] [<CommonParameters>]

DESCRIPTION

The Stop-VM cmdlet shuts down, turns off, or saves a virtual machine.

7. Checkpoint-VM

One of the Hyper-V virtual machine features that has provided a paradigm shift in IT administrators’ capabilities is the Checkpoint mechanism. What is a checkpoint? In the early days of Hyper-V before Windows Server 2012 R2, checkpoints were called snapshots. Microsoft transitioned from the term snapshots to using the term checkpoints at that time. You can think of a Hyper-V checkpoint as a “picture” of the operating system and data at a particular point in time. Checkpoints allow creating an ad-hoc point in time “snapshot” of a server used for quick, short-term rollbacks. A prevalent use case for Checkpoints is for creating a rollback before installing software, driver updates, Windows Updates, or any other change made to the server that may introduce issues.

Microsoft Hyper-V has two types of checkpoints available for creation. These include the standard and production checkpoint. What is the difference?

  • Standard – Standard checkpoints capture data, memory state, and hardware configuration settings for the Hyper-V virtual machine. Microsoft recommends this checkpoint type for test and development scenarios that need recreating. This type of checkpoint restores the server exactly when the checkpoint was created, including the memory state.
  • Production – Production checkpoints are images of the production server. Microsoft supports these types of checkpoints for production use as they use VSS backup technology inside the guest to create the checkpoint and not the saved state method used in the standard checkpoint.

Examples of the CheckPoint-VM cmdlet:

  • Checkpoint-VM -Name TestVM -SnapshotName BeforeUpdates
  • Get-VM Test-VM Hyper1 | Checkpoint-VM

Creating a Checkpoint on a Hyper-V VM named TestVM3
Creating a Checkpoint on a Hyper-V VM named TestVM3

The output of the Get-Help Checkpoint-VM cmdlet:

NAME

Checkpoint-VM

SYNOPSIS

Creates a checkpoint of a virtual machine.

SYNTAX

Checkpoint-VM [-Name] <String[]> [[-SnapshotName] <String>] [-AsJob] [-CimSession <CimSession[]>][-ComputerName <String[]>] [-Confirm] [-Credential <PSCredential[]>] [-Passthru] [-WhatIf][<CommonParameters>]

Checkpoint-VM [-VM] <VirtualMachine[]> [[-SnapshotName] <String>] [-AsJob] [-Confirm] [-Passthru][-WhatIf] [<CommonParameters>]

DESCRIPTION

The Checkpoint-VM cmdlet creates a checkpoint of a virtual machine. Note: In Windows Server 2012 R2, virtual machine snapshots were renamed to virtual machine checkpoints. For clarity, this document will refer to virtual machine snapshots as checkpoints.

8. Measure-VM

Collecting performance and other vitals from production virtual machines running on top of a hypervisor help provide key performance metrics for the applications, services, and other resources housed inside the VM. The Measure-VM Hyper-V cmdlet includes information on processor usage, memory usage, network traffic, and disk capacity running in Hyper-V.

It is essential to note the Measure-VM cmdlet relies on enabling resource metering for the Hyper-V virtual machine. You can allow Measure-VM resource metering using the Enable-VMResourceMetering.

NAME

Measure-VM

SYNOPSIS

Reports resource utilization data for one or more virtual machines.

SYNTAX

Measure-VM [-Name] <String[]> [-CimSession <CimSession[]>] [-ComputerName <String[]>] [-Credential <PSCredential[]>] [<CommonParameters>]

Measure-VM [-VM] <VirtualMachine[]> [<CommonParameters>]

DESCRIPTION

The Measure-VM cmdlet reports data on processor usage, memory usage, network traffic, and disk capacity for one or more virtual machines.

Note: Data is available for reporting through the Measure-VM cmdlet only after resource metering is enabled for a virtual machine.

The report has the following fields:

— ComputerName: The name of the virtual machine host.

— VMId: The unique identifier of the virtual machine.

— VMName: The friendly name of the virtual machine.

— HardDiskMetrics: Information about the performance and throughput of the storage subsystem.

— MeteringDuration: The duration over which resource utilization data is being reported.

— AverageProcessorUsage: The average processor usage, in megahertz, of the virtual machine over the period reported in the MeteringDuration field.

— AverageMemoryUsage: The average memory usage, in megabytes, of the virtual machine over the period reported in the MeteringDuration field.

— MaximumMemoryUsage: The maximum memory usage, in megabytes, of the virtual machine over the period reported in the MeteringDuration field.

— MinimumMemoryUsage: The minimum memory usage, in megabytes, of the virtual machine over the time period reported in the MeteringDuration field.

— TotalDiskAllocation: The maximum disk capacity, in megabytes, allocated to the virtual machine over the time period reported in the MeteringDuration field. For more information, see the ‘Notes’ following the field descriptions.

— NetworkMeteredTrafficReport: An array whose elements report the traffic through each NetworkAdapterAcl on the virtual machine or machines over the time period reported in the MeteringDuration field. Each array element has the following properties:

— NetworkAdapter: The virtual machine network adapter object on which the NetworkAdapterAcl has been configured.

— LocalAddress: for an inbound packet, the destination IP address in the packet header; for an outbound packet, the source IP address in the packet header.

— RemoteAddress: for an inbound packet, the source IP address in the packet header; for an outbound packet, the destination IP address in the packet header.

— Direction: the direction of the network traffic to which the ACL applies. Allowed values are Inbound, Outbound, or Both.

— TotalTraffic: the amount of network traffic, in megabytes, through the NetworkAdapterAcl.

Notes:

— The disk capacity allocated to the virtual machine is reported as the sum of two totals – the total storage capacity of all attached virtual hard disks, and the total amount of physical storage consumed by the virtual machine’s snapshots.

— If the virtual machine has more than one virtual hard disk, then the TotalDiskAllocation property displays the sum of disk capacity allocated to all virtual hard disks.

— Resource utilization is not reported for disks attached through a virtual Fiber Channel connection or network adapters configured to use single-root I/O virtualization (SR-IOV).

— If the virtual machine is configured with static memory rather than Dynamic Memory, then AverageMemoryUsage, MinimumMemoryUsage, and MaxiumumMemoryUsage metrics equals the memory amount configured for the virtual machine.

The default display of a resource pool resource utilization report includes the following columns:

— VMName: The name of the virtual machine.

— AvgCPU(Mhz): The average processor usage, in megahertz, of the virtual machine.

— TotalDisk(M): The average disk usage, in megabytes, of the virtual machine. For more information, see the ‘Notes’ following the field descriptions.

— NetworkInbound(M): Total incoming network traffic, in megabytes, to the virtual machine.

— NetworkOutbound(M): Total outgoing network traffic, in megabytes, from the virtual machine.

9. Export-VM

There will always be the need to export a Hyper-V VM to move the files to another environment or for other reasons. The Export-VM cmdlet allows a Hyper-V administrator to export a virtual machine to disk, including the virtual machine’s configuration, virtual disks, and checkpoints. The process will create three subfolders under the parent export folder housing the exported virtual machine’s various elements.

Examples of running the Export-VM cmdlet include

  • Export-VM -Name TestVM -Path D:VMexports
  • Get-VM TestVM | Export-VM -Path D: VMexports
  • Export-VM -Name TestVM -Path D:VMexports -AsJob – The “AsJob” parameter will allow the export to run as a non-interactive job which is helpful since the export


Using Export-VM for a VM to a specific folderUsing Export-VM for a VM to a specific folder

Navigating to the exported VM folder and various subfolders

Navigating to the exported VM folder and various subfolders

The output from Get-Help Export-VM includes:

NAME

Export-VM

SYNOPSIS

Exports a virtual machine to disk.

SYNTAX

Export-VM [-Name] <String[]> [-Path] <String> [-AsJob] [-CaptureLiveState {CaptureCrashConsistentState | CaptureSavedState | CaptureDataConsistentState}] [-CimSession <CimSession[]>] [-ComputerName <String[]>][-Confirm] [-Credential <PSCredential[]>] [-Passthru] [-WhatIf] [<CommonParameters>]

Export-VM [-VM] <VirtualMachine[]> [-Path] <String> [-AsJob] [-CaptureLiveState {CaptureCrashConsistentState | CaptureSavedState | CaptureDataConsistentState}] [-Confirm] [-Passthru] [-WhatIf] [<CommonParameters>]

DESCRIPTION

The Export-VM cmdlet exports a virtual machine to disk. This cmdlet creates a folder at a specified location having three subfolders: Snapshots, Virtual Hard Disks, and Virtual Machines. The Snapshots and Virtual Hard Disks folders contain the snapshots of and virtual hard disks of the specified virtual machine respectively. The Virtual Machines folder contains the configuration XML of the specified virtual machine.

10. New-VHD

The final entry on the top 10 PowerShell commands for Hyper-V relates to creating virtual disks for Hyper-V virtual machines. When creating a Hyper-V virtual machine using Hyper-V Manager as an example does not give any options to customize the creation of the Hyper-V virtual machine hard disk type. By default, Hyper-V creates new virtual machine disks as dynamically expanding or thin-provisioned disks. If you want to create a VM with a fixed disk format or thick-provisioned, you have to use the new disk creation wizard in Hyper-V Manager, which is cumbersome.

Disk options when creating a new Hyper-V virtual machine in Hyper-V Manager
Disk options when creating a new Hyper-V virtual machine in Hyper-V Manager

The New-VHD Hyper-V PowerShell cmdlet provides the ability to create Hyper-V virtual disks with the desired options easily. It can create one or more new virtual hard disks in either the VHD or newer VHDX format and can create disks as Fixed Disk size.

Examples of using the NewVHD cmdlet include:

  • New-VHD -Path c:win2019.vhdx -SizeBytes 80GB – This is an example of a dynamic hard disk created as a VHDX type disk.
  • New-VHD -ParentPath c:win10.vhdx -Path c:Diff.vhdx -Differencing – An example of creating a differencing hard disk with the parent disk path
  • New-VHD -Path C:fixeddisk.vhd -Fixed -SourceDisk 3 -SizeBytes 1TB – A fixed VHD disk populated with the disk identified by the number 3

Creating a new VHDX file in a directory using New-VHD
Creating a new VHDX file in a directory using New-VHD

The output from Get-Help New-VHD:

NAME

New-VHD

SYNOPSIS

Creates one or more new virtual hard disks.

SYNTAX

New-VHD [-Path] <String[]> [-ParentPath] <String> [[-SizeBytes] <UInt64>] [-AsJob] [-BlockSizeBytes <UInt32>]

[-CimSession <CimSession[]>] [-ComputerName <String[]>] [-Confirm] [-Credential <PSCredential[]>]

[-Differencing] [-PhysicalSectorSizeBytes {512 | 4096}] [-WhatIf] [<CommonParameters>]

 

New-VHD [-Path] <String[]> [-SizeBytes] <UInt64> [-AsJob] [-BlockSizeBytes <UInt32>] [-CimSession

<CimSession[]>] [-ComputerName <String[]>] [-Confirm] [-Credential <PSCredential[]>] [-Dynamic]

[-LogicalSectorSizeBytes {512 | 4096}] [-PhysicalSectorSizeBytes {512 | 4096}] [-WhatIf] [<CommonParameters>]

 

New-VHD [-Path] <String[]> [-AsJob] [-BlockSizeBytes <UInt32>] [-CimSession <CimSession[]>] [-ComputerName

<String[]>] [-Confirm] [-Credential <PSCredential[]>] -Dynamic -SourceDisk <UInt32> [-WhatIf] [<CommonParameters>]

New-VHD [-Path] <String[]> [-SizeBytes] <UInt64> [-AsJob] [-BlockSizeBytes <UInt32>] [-CimSession <CimSession[]>] [-ComputerName <String[]>] [-Confirm] [-Credential <PSCredential[]>] -Fixed [-LogicalSectorSizeBytes {512 | 4096}] [-PhysicalSectorSizeBytes {512 | 4096}] [-WhatIf] [<CommonParameters>]

New-VHD [-Path] <String[]> [-AsJob] [-BlockSizeBytes <UInt32>] [-CimSession <CimSession[]>] [-ComputerName

<String[]>] [-Confirm] [-Credential <PSCredential[]>] -Fixed -SourceDisk <UInt32> [-WhatIf] [<CommonParameters>]

DESCRIPTION

The New-VHD cmdlet creates one or more new virtual hard disks in either VHD format or the newer VHDX format. The file name extension you specify determines the format.

Wrapping Up

PowerShell provides an extremely robust and easy to understand scripting language that provides organizations with powerful cmdlets to interact with Microsoft Windows Server Hyper-V environments. PowerShell commands for Hyper-V are easy to consume. These are installed with the management tools with Hyper-V and are instantly accessible from the Windows PowerShell prompt.

PowerShell Core is the newer PowerShell version built on top of .NET Core and is a cross-platform solution that can be used in Windows, Linux, and macOS. PowerShell Core is the newer PowerShell version moving forward, however, it is not installed in Windows by default. It is a free download from the official PowerShell Core GitHub site and can be installed and used to interact with Hyper-V cmdlets the same as Windows PowerShell.

The top 10 PowerShell commands for Hyper-V include cmdlets covering a wide range of administrative tasks and capabilities that allow Hyper-V administrators to streamline configuration, management, and other duties. While not all-inclusive, they represent many of the command management tasks needed from day-to-day. These also allow effectively automating Hyper-V environments by way of PowerShell scripts that can be scheduled or triggered by certain actions. PowerShell scripts can also be used for configuration management and remediating configuration drift in the Hyper-V environment.

The post Top 10 PowerShell commands for Hyper-V appeared first on Altaro DOJO | Hyper-V.

]]>
https://www.altaro.com/hyper-v/powershell-commands-hyper-v/feed/ 0
How to Clean Up After a Failed Hyper-V Checkpoint https://www.altaro.com/hyper-v/clean-up-hyper-v-checkpoint/ https://www.altaro.com/hyper-v/clean-up-hyper-v-checkpoint/#comments Thu, 13 Aug 2020 14:43:40 +0000 https://www.altaro.com/hyper-v/?p=18844 This article explains the formation of Hyper-V's lingering checkpoints and how to safely clean them after a failed checkpoint

The post How to Clean Up After a Failed Hyper-V Checkpoint appeared first on Altaro DOJO | Hyper-V.

]]>

Hyper-V’s checkpointing system typically does a perfect job of coordinating all its moving parts. However, it sometimes fails to completely clean up afterward. That can cause parts of a checkpoint, often called “lingering checkpoints”, to remain. You can easily take care of these leftover bits, but you must proceed with caution. A misstep can cause a full failure that will require you to rebuild your virtual machine. Read on to find out how to clean up after a failed checkpoint.

Avoid Mistakes When Cleaning up a Hyper-V Checkpoint

The most common mistake is starting your repair attempt by manually merging the AVHDX file into its parent. If you do that, then you cannot use any of Hyper-V’s tools to clean up. You will have no further option except to recreate the virtual machine’s files. The “A” in “AVHDX” stands for “automatic”. An AVHDX file is only one part of a checkpoint. A manual file merge violates the overall integrity of a checkpoint and renders it unusable. A manual merge of the AVHDX files should almost be the last thing that you try.

Also, do not start off by deleting the virtual machine. That may or may not trigger a cleanup of AVHDX files. Don’t take the gamble.

Before you try anything, check your backup application. If it is in the middle of a backup or indicates that it needs attention from you, get through all of that first. Interrupting a backup can cause all sorts of problems.

How to Cleanup a Failed Hyper-V Checkpoint

We have multiple options to try, from simple and safe to difficult and dangerous. Start with the easy things first and only try something harder if that doesn’t work.

Method 1: Delete the Checkpoint

If you can, right-click the checkpoint in Hyper-V Manager and use the Delete Checkpoint or Delete Checkpoint Subtree option:

Delete a Hyper-V checkpoint in the GUI

This usually does not work on lingering checkpoints, but it never hurts to try.

Sometimes the checkpoint does not present a Delete option in Hyper-V Manager.

Missing checkpoint delete option in Hyper-V GUI

Sometimes, the checkpoint doesn’t even appear.

In any of these situations, PowerShell can usually see and manipulate the checkpoint.

Easiest way:

Remove-VMCheckpoint -VMName demovm

You can remove all checkpoints on a host at once:

Remove-VMCheckpoint -VMName *

If the script completes without error, you can verify in Hyper-V Manager that it successfully removed all checkpoints. You can also use PowerShell:

Get-VMCheckpoint

This clears up the majority of leftover checkpoints.

Method 2: Create a New Checkpoint and Delete It

Everyone has had one of those toilets that won’t stop running. Sometimes, you get lucky, and you just need to jiggle the handle to remind the mechanism that it needs to drop the flapper ALL the way over the hole. Method 3 is something of a “jiggle the handle” fix. We just tap Hyper-V’s checkpointing system on the shoulder and remind it what to do.

In the Hyper-V Manager interface, right-click on the virtual machine (not a checkpoint), and click Checkpoint:

Create a new Hyper-V checkpoint

Now, at the root of all of the VM’s checkpoints, right-click on the topmost and click Delete checkpoint subtree:

Delete a Hyper-V checkpoint subtree

If this option does not appear, then our “jiggle the handle” fix won’t work. Try to delete the checkpoint that you just made, if possible.

The equivalent PowerShell is Checkpoint-VM -VMName demovm followed by Remove-VMCheckpoint -VMName demovm.

Regroup Before Proceeding

I do not know how pass-through disks or vSANs affect these processes. If you have any and the above didn’t work, I recommend shutting the VM down, disconnecting those devices, and starting the preceding steps over. You can reconnect your devices afterward.

If your checkpoint persists after trying the above, then you now face some potentially difficult choices. If you can, I would first try shutting down the virtual machine, restarting the Hyper-V Virtual Machine Management service, and trying the above steps while the VM stays off. This is a bit more involved “jiggle the handle” type of fix, but it’s also easy. If you want to take a really long shot, you can also restart the host. I do not expect that to have any effect, but I have not yet seen everything.

Take a Backup!

Up to this point, we have followed non-destructive procedures. The remaining fixes involve potential data loss. If possible, back up your virtual machine. Unfortunately, you might only have this problem because of a failed backup. In that case, export the virtual machine. I would personally shut the VM down beforehand so as to only capture the most recent data.

Virtual Machine Backup

If you have a good backup or an export, then you cannot lose anything else except time.

Method 3: Reload the Virtual Machine’s Configuration

This method presents a moderate risk of data loss. It is easy to make a mistake. Check your backup! This is a more involved “jiggle the handle” type of fix.

Procedure:

  1. Shut the VM down
  2. Take note of the virtual machine’s configuration file location, its virtual disk file names and locations, and the virtual controller positions that connect them (IDE 1 position 0, SCSI 2 position 12, etc.)
    Hyper-V Manager virtual disk information
  3. On each virtual disk, follow the AVHDX tree, recording each file name, until you find the parent VHDX. In Hyper-V Manager, do this with the Inspect button on the VM’s disk sheet, then the Inspect Parent on each subsequent dialog box that opens.
    Hyper-V's inspect virtual disk dialog
  4. Modify the virtual machine to remove all of its hard disks. If the virtual machine is clustered, you’ll need to do this in Failover Cluster Manager (or PowerShell). It will prompt to create a checkpoint, but since you already tried that, I would skip it.
    Remove virtual hard disk in Hyper-V Manager
  5. Export the virtual machine configuration
    Export a VM in Hyper-V
  6. Delete the virtual machine. If the VM is clustered, record any special clustering properties (like Preferred Hosts), and delete it from Failover Cluster Manager.
    Delete virtual machine in Hyper-V
  7. Import the virtual machine configuration from step 5 into the location you recorded in step 3. When prompted, choose the Restore option.
    Start a virtual machine import in Hyper-VRestore mode of Hyper-V Manager's virtual machine import function
  8. This will bring back the VM with its checkpoints. Start at method 1 and try to clean them up.
  9. Reattach the VHDX. If, for some reason, the checkpoint process did not merge the disks, do that manually first. If you need instructions, look at the section after the final method.
  10. Re-establish clustering, if applicable.

We use this method to give Hyper-V one final chance to rethink the error of its ways. After this, we start invoking manual processes.

Method 4: Restore the VM Configuration and Manually Merge the Disks

For this one to work, you need a single good backup of the virtual machine. It does not need to be recent. We only care about its configuration. This process has a somewhat greater level of risk as method 4. Once we introduce the manual merge process, the odds of human error increase dramatically.

  1. Follow steps 1, 2, and 3 from method 3 (turn VM off and record configuration information). If you are not certain about the state of your backup, follow steps 5 and 6 (export and delete the VM). If you have confidence in your backup, or if you already followed step 4 and still have the export, then you can skip step 5 (export the VM).
  2. Manually merge the VM’s virtual hard disk(s) (see the section after the methods for directions). Move the final VHDX(s) to a safe location. It can be temporary.
  3. Restore the virtual machine from backup. I don’t think that I’ve ever seen a Hyper-V backup application that will allow you to only restore the virtual machine configuration files, but if one exists and you happen to have it, use that feature.
  4. Follow whatever steps your backup application needs to make the restored VM usable. For instance, Altaro VM Backup for Hyper-V restores your VM as a clone with a different name and in a different location unless you override the defaults.
  5. Remove the restored virtual disks from the VM (see step 4 of Method 3). Then, delete the restored virtual hard disk file(s) (they’re older and perfectly safe on backup).
  6. Copy or move the merged VHDX file from step 2 back to its original location.
  7. On the virtual machine’s Settings dialog, add the VHDX(s) back to the controllers and locations that you recorded in step 1.
    Add a virtual disk to a virtual machine in Hyper-V Manager.
  8. Check on any supporting tools that identify VMs by ID instead of name (like backup). Rejoin the cluster, if applicable.

This particular method can be time-consuming since it involves restoring virtual disks that you don’t intend to keep. As a tradeoff, it retains the major configuration data of the virtual machine. Altaro VM Backup for Hyper-V will use a different VM ID from the original to prevent collisions, but it retains all of the VM’s hardware IDs and other identifiers such as the BIOS GUID. I assume that other Hyper-V backup tools exhibit similar behavior. Keeping hardware IDs means that your applications that use them for licensing purposes will not trigger an activation event after you follow this method.

Method 5: Rebuild the VM’s Configuration and Manually Merge the Disks

If you’ve gotten to this point, then you have reached the “nuclear option”. The risk of data loss is about the same as method 5. This process is faster to perform but has a lot of side effects that will almost certainly require more post-recovery action on your part.

  1. Access the VM’s settings page and record every detail that you can from every property sheet. That means CPU, memory, network, disk, file location settings… everything. You definitely must gather the VHDX/AVHDX connection and parent-child-grandchild (etc.) order (method 3, step 3). If your organization utilizes special BIOSGUID settings and other advanced VM properties, then record those as well. I assume that if such fields are important to you that you already know how to retrieve them. If not, you can use my free tool.
  2. Check your backups and/or make an export.
  3. Delete the virtual machine (Method 3 step 6 has a screenshot, mind the note about failover clustering as well).
  4. Recreate the virtual machine from the data that you collected in step 1, with the exception of the virtual hard disk files. Leave those unconnected for now.
  5. Follow the steps in the next section to merge the AVHDX files into the root VHDX
  6. Connect the VHDX files to the locations that you noted in step 1 (Method 5 step 7 has a screenshot).
  7. Check on any supporting tools that identify VMs by ID instead of name (like backup). Rejoin the cluster, if applicable.
  8. In the VM’s guest operating system, check for and deal with any problems that arise from changing all of the hardware IDs.

Since you don’t have to perform a restore operation, it takes less time to get to the end of this method than method 5. Unfortunately, swapping out all of your hardware IDs can have negative impacts. Windows will need to activate again, and it will not re-use the previous licensing instance. Other software may react similarly, or worse.

How to Manually Merge AVHDX Files

I put this part of the article near the end for a reason. I cannot over-emphasize that you should not start here.

Prerequisites for Merging AVHDX Files

If you precisely followed one of the methods above that redirected you here, then you already satisfied these requirements. Go over them again anyway. If you do not perform your merges in precisely the correct order, you will permanently orphan data.

  1. Merge the files in their original location. I had you merge the files before moving or copying them for a reason. Each differencing disk (the AVHDXs) contains the FULL path of their parent. If you relocate them, they will throw errors when you attempt to merge them. If you can’t get them back to their original location, then read below for steps on updating each of the files.
  2. You will have the best results if you merge the files in the order that they were created. A differencing disk knows about its parent, but no parent virtual disk file knows about its children. If you merge them out of order, you can correct it — with some effort. But, if any virtual hard disk file changes while it has children, you will have no way to recover the data in those children.

If merged in the original location and in the correct order, AVHDX merging poses no risks.

Manual AVHDX Merge Process in PowerShell

I recommend that you perform merges with PowerShell because you can do it more quickly. Starting with the AVHDX that the virtual machine used as its active disk, issue the following command:

Merge-VHD -Path 'C:LocalVMsdemovmVirtual Hard Disksdemo-data_8EFF0E79-2711-4115-A704-45046FE6C536.avhdx'

Once that finishes, move to the next file in your list. Use tab completion! Double-check the file names from your list!

Once you have nothing left but the root VHDX, you can attach it to the virtual machine.

Manual AVHDX Merge Process in Hyper-V Manager

Hyper-V Manager has a wizard for merging differencing disks. If you have more than a couple of disks to merge, you will find this process tedious.

  1. In Hyper-V Manager, click Edit disk in the far right pane.
    Edit disk in Hyper-V Manager
  2. Click Next on the wizard’s intro page if it appears.
  3. Browse to the last AVHDX file in the chain.
    Browse to virtual disk in Hyper-V Manager
  4. Choose the Merge option and click Next.
    Disk merge option in Hyper-V Manager
  5. Choose to merge directly to the parent disk and click Next.
    Option to merge virtual disk to parent in Hyper-V Manager
  6. Click Finish on the last screen.
  7. Repeat until you only have the root VHDX left. Reattach it to the VM.

Fixing Parent Problems with AVHDX Files

In this section, I will show you how to correct invalid parent chains. If you have merged virtual disk files in the incorrect order or moved them out of their original location, you can correct it.

Set-VHD -Path c:tempdemo-data_8eff0e79-2711-4115-a704-45046fe6c536.avhdx -ParentPath c:tempdemo-data_535bbf6b-5190-4383-ae19-ab7a6d44b2eb.avhdx

The above cmdlet will work if the disk files have moved from their original locations. If you had a disk chain of A->B->C and merged B into A, then you can use the above to set the parent of C to A, provided that nothing else happened to A in the interim.

The virtual disk system uses IDs to track valid parentage. If a child does not match to a parent, you will get the following error:

Set-VHD : Failed to set new parent for the virtual disk.
There exists ID mismatch between the differencing virtual hard disk and the parent disk.

You could use the IgnoreIdMismatch switch to ignore this message, but a merge operation will almost certainly cause damage.

Alternatively, if you go through the Edit Disk wizard as shown in the manual merge instructions above, then at step 4, you can sometimes choose to reconnect the disk. Sometimes though, the GUI crashes. I would not use this tool.

Errors Encountered on AVHDX Files with an Invalid Parent

The errors that you get when you have an AVHDX with an invalid parent usually do not help you reach that conclusion.

In PowerShell:

Merge-VHD : Failed to merge the virtual disk.
The system failed to merge 'c:tempdemo-data_8EFF0E79-2711-4115-A704-45046FE6C536.avhdx'.
Failed to open virtual disk 'c:tempdemo-data_8EFF0E79-2711-4115-A704-45046FE6C536.avhdx' because a problem was
encountered opening a virtual hard disk in the chain of differencing disks, '': 'The system cannot find the file
specified.' (0x80070002).

Because it lists the child AVHDX in both locations, along with an empty string where the parent name should appear, it might seem that the child file has the problem.

In Hyper-V Manager, you will get an error about “one of the command line parameters”. It will follow that up with a really unhelpful “Property ‘MaxInternalSize’ does not exist in class ‘Msvm_VirtualHardDiskSettingData’. All of this just means that it can’t find the parent disk.

Use Set-VHD as shown above to correct these errors.

Other Checkpoint Cleanup Work

Checkpoints involve more than AVHDX files. Checkpoints also grab the VM configuration and sometimes its memory contents. To root these out, look for folders and files whose names contain GUIDs that do not belong to the VM or any surviving checkpoint. You can safely delete them all. If you do not feel comfortable doing this, then use Storage Migration to move the VM elsewhere. It will only move active files. You can safely delete any files that remain.

What Causes Checkpoints to Linger?

I do not know that anyone has ever determined the central cause of this problem. We do know that Hyper-V-aware backups will trigger Hyper-V’s checkpointing mechanism to create a special backup checkpoint. Once the program notifies VSS that the backup has completed, it should automatically merge the checkpoint. Look in the event viewer for any clues as to why that didn’t happen.

The post How to Clean Up After a Failed Hyper-V Checkpoint appeared first on Altaro DOJO | Hyper-V.

]]>
https://www.altaro.com/hyper-v/clean-up-hyper-v-checkpoint/feed/ 7
How to Install the Hyper-V PowerShell Module (updated for Windows Server 2019) https://www.altaro.com/hyper-v/install-hyper-v-powershell-module/ https://www.altaro.com/hyper-v/install-hyper-v-powershell-module/#comments Thu, 05 Mar 2020 15:49:05 +0000 http://www.altaro.com/hyper-v/?p=10825 PowerShell gives you the best combination of power and simplicity when managing Hyper-V. Use this guide to install the PowerShell Module on Hyper-V.

The post How to Install the Hyper-V PowerShell Module (updated for Windows Server 2019) appeared first on Altaro DOJO | Hyper-V.

]]>

The best combination of power and simplicity for controlling Hyper-V is its PowerShell module. The module’s installable component is distinct from the Hyper-V role, and the two are not automatically installed together. Even if you have installed the free Hyper-V Server product that ships with the Hyper-V role already enabled, you’ll still need to install the PowerShell module separately. This short guide will explain how to install that module and understand its basic structure. If you need to use directly control Windows Server 2012/R2 or Hyper-V Server 2012/R2 using the PowerShell module as it ships in Windows 10/Windows Server 2016 or 2019, instructions are at the very end of this post.

How to Install the Hyper-V PowerShell Module with PowerShell

The quickest way to install the module is through PowerShell. There are several ways to do that, depending on your operating system and your goal.

Using PowerShell to Install the Hyper-V PowerShell Module in Windows 10

All of the PowerShell cmdlets for turning off and on Windows features and roles are overlays for the Deployment Image Servicing and Management (DISM) subsystem. Windows 10 does include a PowerShell module for DISM, but it uses a smaller cmdlet set than what you’ll find on your servers. The server module’s cmdlets are simpler, so I’m going to separate out the more involved cmdlets into the Windows 10 section. The cmdlets that I’m about to show you will work just as well on a server operating system as on Windows 10, although the exact names of the features that you’ll use might be somewhat different. All cmdlets must be run from an elevated PowerShell prompt.

As I mentioned in the preamble to this section, there a few different ways that you can enable the Hyper-V PowerShell module. There is only a single cmdlet, and you will only need to use it to enable a single feature. However, the module appears in a few different features, so you’ll need to pick the one that is most appropriate to you. You can see all of the available options like this:

Get-WindowsOptionalFeature -Online -FeatureName *hyper-v* | select DisplayName, FeatureName

The reason that you see so many different objects is that it’s showing a flat display of the hierarchical tree that you’d get if you opened the Windows Features window instead. Unfortunately, this cmdlet does not have a nicely formatted display (even if you don’t pare it down with any filters), so it might not be obvious. Compare the output of the cmdlet to the Windows Features screen:

Windows 10 Hyper-V Features

Windows 10 Hyper-V Features

You have three options if you want to install the PowerShell Module on Windows 10. The simplest is to install only the module by using its feature name. Installing either of the two options above it (Hyper-V Management Tools or the entire Hyper-V section) will include the module. I trimmed off the feature name in the images above, so all three possibilities are shown below:

# Install only the PowerShell module
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-Management-PowerShell

# Install the Hyper-V management tool pack (Hyper-V Manager and the Hyper-V PowerShell module)
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-Tools-All

# Install the entire Hyper-V stack (hypervisor, services, and tools)
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-All

Tab completion will work for everything except the specific feature name. But, don’t forget that copy/paste works perfectly well in a PowerShell window (click/drag to highlight, [Enter] to copy, right-click to paste). You can use the output from Get-WindowsOptionalFeature so that you don’t need to type any feature names.

It’s fine to install a higher-level item even if some of its subcomponents are already installed. For example, if you enabled the Hyper-V platform but not the tools, you can enable Microsoft-Hyper-V-All and it will not hurt anything.

The Enable-WindowsOptionalFeature cmdlet does not have a ComputerName parameter, but it can be used in explicit PowerShell Remoting.

Using PowerShell to Install the Hyper-V PowerShell Module in Windows Server or Hyper-V Server 2012, 2016 & 2019

The DISM PowerShell tools on the server platforms are a bit cleaner to use than in Windows 10. If you’d like, the cmdlets shown in the Windows 10 section will work just as well on the servers (already the feature names are different). The cmdlets shown in this section will only work on server SKUs. They must be run from an elevated prompt.

Get-WindowsFeature *hyper-v*

Ordinarily, I don’t show cmdlets using positional parameters, but I wanted you to see how easy this cmdlet is to use. The full version of the shown cmdlet is Get-WindowsFeature -Name *hyper-v*. Its output looks much nicer than Get-WindowsOptionalFeature:

Server Hyper-V Features

Server Hyper-V Features

There is a difference, though. Under Windows 10, all the items live under the same root. In the Windows SKUs, Hyper-V is under the Roles branch but all of the tools are under the Features branch. The output indentation, when filtered, is misleading.

The Server SKUs have an Install-WindowsFeature cmdlet. Its behavior is similar to Enable-WindowsOptionalFeature, but it is not quite the same. Enabling the root Hyper-V feature will not automatically select all of the tools (as you might have already found out). These are all of the possible ways to install the Hyper-V PowerShell Module using PowerShell on a Server product:

# Install only the PowerShell module
Install-WindowsFeature -Name Hyper-V-PowerShell

# Install Hyper-V Manager and the PowerShell module (HVM only available on GUI systems)
Install-WindowsFeature -Name RSAT-Hyper-V-Tools

# Install the Hyper-V hypervisor and all tools (method #1)
Install-WindowsFeature -Name Hyper-V -IncludeManagementTools

# Install the Hyper-V hypervisor and all tools (method #2)
Install-WindowsFeature -Name Hyper-V, RSAT-Hyper-V-Tools

Tab completion will work for everything except the specific feature name. But, don’t forget that copy/paste works perfectly well in a PowerShell window (click/drag to highlight, [Enter] to copy, right-click to paste). You can use the output from Get-WindowsFeature so that you don’t need to type any feature names.

Installation of the Hyper-V PowerShell module on Windows Server 2019

Installation of the Hyper-V PowerShell module on Windows Server 2019

If the Hyper-V role is already enabled, you can still use either of the last two options safely. If the Hyper-V role is not installed and you are using one of those options, the system will need to be restarted. If you like, you can include the -Restart parameter and DISM will automatically reboot the system as soon as the installation is complete.

The Install-WindowsFeature cmdlet does have a ComputerName parameter, so it can be used with implicit PowerShell Remoting to enable the feature on multiple computers simultaneously. For example, use -ComputerName svhv01, svhv02, svhv03, svhv04 to install the feature(s) on all four of the named hosts simultaneously. If you are running your PowerShell session from a Windows 10 machine that doesn’t have that cmdlet, you can still use explicit PowerShell Remoting.

How to Install the Hyper-V PowerShell Module Using the GUI

It seems a bit sacrilegious to install a PowerShell module using a GUI, and it certainly takes longer than using PowerShell, but I suppose someone has a reason.

Using the GUI to Install the Hyper-V PowerShell Module on Windows 10

Follow these steps in Windows 10:

  1. Right-click on the Start button and click Programs and Features.

    Windows 10 Start-X

    Windows 10 Start-X

  2. In the Programs and Features dialog, click Turn Windows features on or off

    Windows 10 Programs and Features

    Windows 10 Programs and Features

  3. In the Windows Features dialog, check the box for Hyper-V Module for Windows PowerShell (and anything else that you’d like) and click OK.

    Windows 10 PS Module Selection

    Windows 10 PS Module Selection

  4. The dialog will signal completion and the module will be installed.

Using Server Manager to Install the Hyper-V PowerShell Module on Windows Server or Hyper-V Server 2012, 2016 & 2019

Server Manager is the tool to use for graphically adding roles and features on Windows Server and Hyper-V Server systems. Of course, you’re not going to be able to directly open Server Manager on Hyper-V Server systems, but you can add a system running Hyper-V Server to the console of any same-level system running a GUI edition of Windows Server (security restrictions apply). The RSAT package for Windows 10 includes Server Manager and can remotely control servers (security restrictions apply there, as well). While Server Manager can be remotely connected to multiple systems, it can only install features on one host at a time.

To use Server Manager to enable Hyper-V’s PowerShell module, open the Add Roles and Features wizard and proceed through to the Features page. Navigate to Remote Server Administration Tools -> Role Administration Tools -> Hyper-V Management Tools and check Hyper-V Module for Windows PowerShell. Proceed through the wizard to complete the installation.

Windows Server PS Module Selection

Windows Server PS Module Selection

The module will be immediately available to use once the wizard completes.

A Brief Explanation of the Hyper-V PowerShell Module

Once installed, you can find the module’s files at C:WindowsSystem32WindowsPowerShellv1.0ModulesHyper-V. Its location will ensure that the module is automatically loaded every time PowerShell starts up. That means that you don’t need to use Import-Module — you can start right away with your scripting.

If you browse through and look at the files a bit, you might notice that the PowerShell module files reference a .DLL. This means that this particular PowerShell module is a binary module. Microsoft wrote it in a .Net language and compiled it. Its cmdlets will run faster than they would in a text-based module, but you won’t be able to see how it does its work (at least, not by using any sanctioned methods).

Connecting to Windows/Hyper-V Server 2012, 2016 & 2019 from PowerShell in Windows 10/Server 2016 & 2019

If you are using Windows 10 and Windows/Hyper-V Server 2016 or 2019, there’s an all-new version 2.0.0 of the Hyper-V PowerShell module. That’s a good thing, right? Well, usually. The thing is, the 2012 and 2012 R2 versions aren’t going away any time soon, and we still need to control those. Version 2 of the PowerShell module will throw an error when you attempt to control these down-level systems. The good news is that you can work around this limitation fairly easily. If you browsed the folder tree on one of these newer OS releases, you may have noticed that there is a 1.1 folder as well as a 2.0.0 folder. The earlier binary module is still included!

So, does that mean that you can happily kick off some scripts on those “old” machines? Let’s see:

Error Using Hyper-V PS Module on Downlevel Target

Error Using Hyper-V PS Module on Downlevel Target

The error is: “Get-VM : The Hyper-V module used in this Windows PowerShell session cannot be used for remote management of the server ‘SVHV2’. Load a compatible version of the Hyper-V module, or use Powershell remoting to connect directly to the remote server. For more information, see http://go.microsoft.com/fwlink/p/?LinkID=532650.

What to do?

The answer lies in a new feature of PowerShell 5, which fortunately comes with these newer OSs. We will first get a look at what our options are:

Get-Module -Name Hyper-V -ListAvailable
Available Hyper-V Modules

Available Hyper-V Modules

You could run this without ListAvailable to determine which, if any, version is already loaded. You already know that PowerShell auto-loads the module and, if you didn’t already know, I’m now informing you that it will always load the highest available version unless instructed otherwise. So, let’s use the new RequiredVersion parameter to instruct it otherwise:

Remove-Module Hyper-V
Import-Module Hyper-V -RequiredVersion 1.1
Get-Module Hyper-V

The results of this operation:

Successfully Controlling Down-level Hyper-V Hosts in PowerShell

Successfully Controlling Down-level Hyper-V Hosts in PowerShell

Is this good? Well, it’s OK, but not great. Popping a module in and out isn’t the worst thing in the world, but can you imagine scripting that to work against hosts of various levels? While possible, the experience would certainly be unpleasant. If you’re going to interactively control some down-level Hyper-V hosts, this approach would work well enough. For serious scripting work, I’d stick with the WMI/CIM cmdlets and explicit remoting.

If you have any questions about using the Hyper-V PowerShell module including installation, optimization or anything else, let me know in the comments below and I’ll help you out!

This blog was originally published on July 2017 but has been updated with corrections and new content to be relevant from March 2020.

The post How to Install the Hyper-V PowerShell Module (updated for Windows Server 2019) appeared first on Altaro DOJO | Hyper-V.

]]>
https://www.altaro.com/hyper-v/install-hyper-v-powershell-module/feed/ 6
Free Script – Convert Legacy Teamed Hyper-V vSwitch to SET https://www.altaro.com/hyper-v/free-script-vswitch-set/ https://www.altaro.com/hyper-v/free-script-vswitch-set/#comments Thu, 16 Jan 2020 16:25:25 +0000 https://www.altaro.com/hyper-v/?p=18335 Microsoft does not provide any way to convert an existing team configuration to SET. This free PowerShell script does exactly that. Enjoy!

The post Free Script – Convert Legacy Teamed Hyper-V vSwitch to SET appeared first on Altaro DOJO | Hyper-V.

]]>

With Windows Server 2012, Microsoft changed the networking game by introducing native teaming. It gave us a supported way to balance traffic for several virtual machines and, if desired, the host. In Windows Server 2016, Microsoft changed the game again with the switch-embedded team (SET). This singular logical construct combines the traffic balancing technology of a team into the Hyper-V virtual switch. It includes several other improvements, making it a worthwhile choice over the traditional team. Unfortunately, Microsoft does not provide any way to gracefully convert an existing team configuration to the new SET. I have solved this problem with a new PowerShell script.

What Benefits Does a Switch-Embedded Team Provide Over Native Teaming?

SET improves on the load-balancing failover (LBFO) team and virtual switch combination in two major ways: configuration simplicity and performance.

SET Configuration Simplicity

With the LBFO and team combination, you must create the team and virtual switch separately. Once created, you must manage them separately. Even experienced Hyper-V administrators can forget which construct implements a specific feature. With SET, you only need to create one item.

One point of contention: SET has no UI at this time. You can only create or manipulate a SET with PowerShell.

SET Performance Benefits

SET provides new technologies that allow them to outperform the older LBFO+team configuration:

  • Internal improvements, some stemming from the integration of teaming with the virtual switch into a single construct, others from technology maturation
  • LBFO cannot work with RDMA capabilities; SET can
  • SET introduces virtual machine multi-queue (VMMQ) capability. Among other things, VMMQ lifts the single-core restriction for VMQ. As a result, VNICs on SET do not have the same upper inbound traffic limit of about 5.5 Gbps as they do on LBFO.
  • The latest software-defined networking capabilities

Microsoft has a short article explaining SET’s benefits.

Who Should Not Convert to a Switch-Embedded Team?

For new installations, SET almost always makes more sense than LBFO. It doesn’t work for everyone, though. You also may not want to make changes mid-stream. I recommend that you take some time to ensure that the above benefits apply to your situation before deciding to use this script.

I can think of three situations where you should hesitate to switch from LBFO:

  • VMM installations. VMM does not deal with change well. It also does not have any way to ignore host conditions that it does not like. If you change the virtual switch on one host, VMM will exclude it from any clusters. You have options, such as temporarily removing hosts from VMM. Any process that you choose will take time and planning. Think it through before doing anything.
  • Hardware LAG installations. SET does not support static or LACP teaming modes. In my experience, most people do not understand LACP, incorrectly believing that it grants universal aggregation capability. Even without LACP, SET will result in a flat or increased performance profile in almost all cases. However, if you have one of the few cases where LACP enables your virtual adapters to exceed 10Gbps, then think twice about giving it up. Even if you don’t really benefit from LACP, moving to SET requires you to change the hardware configuration to allow a switch-independent team.
  • Gigabit installations. Most of SET’s enhanced powers benefit systems with 10 gigabit or faster adapters. Gigabit adapters do not cause significant processing load. You won’t see any improvements from SET. I would suggest that you should just leave your configuration as-is.

Script Warnings

If you use VMM, make certain to read the above note. This script can do everything that I promised and still cause problems for VMM.

Unfortunately, it might not work as promised. I performed a great deal of testing during development, and my final testing rounds all worked exactly as expected. However, early iterations caused a lot of problems. Some were significant, including one that I could only fix with a reinstall of Hyper-V. Even if I have managed to completely clear the worst of these problems, you might still encounter errors on your hosts that occur after it deletes your existing configuration but before it builds up a replacement.

As a precaution, record your existing configuration. You need to keep track of the team, virtual switch, and all management operating system virtual NIC settings. You can use my host configuration script as a starting ground to quickly rebuild your networking if necessary.

Script Explanation

I intended for this script to perform a complete replacement of your LBFO+virtual switch with a functionally identical SET configuration. That includes virtual NICs. As you may have already discovered, disconnecting a virtual NIC used by the management operating system destroys the vNIC. So, you can’t just temporarily disconnect them like you can with virtual machine vNICs. I took that in mind when building this script.

This script does the following:

  • Detects if the host belongs to a cluster; places nodes into maintenance mode and waits for VMs to drain
  • Disconnects remaining VMs from the virtual switch
  • Destroys the virtual switch(es) and team(s)
  • Creates a SET with the same settings as the original LBFO and switch (where applicable)
  • Recreates the original management OS vNICs and re-attaches them. Recreated settings include:
    • The IP address and gateway information
    • Advanced adapter settings (such as jumbo frames)
    • QoS settings
  • Reconnects VM network adapters
  • Ends maintenance mode on cluster members

It will only touch LBFO+virtual switch teams. It will skip over external virtual switches on single adapters, private switches, internal switches, and SET switches.

If you just run the script by name, it will auto-convert all LBFO+team combinations. It does have a few optional parameters. You can specify specific switches. You can give each switch a new name. You can change QoS and load-balancing modes.

Script Listing

I have included the complete 1.0 script listing below for your convenience. I will not maintain this listing. You will find any updates on my GitHub page. If you find problems, please open issues there.

<#
.SYNOPSIS
	Converts LBFO+Virtual Switch combinations to switch-embedded teams.
.DESCRIPTION
	Converts LBFO+Virtual Switch combinations to switch-embedded teams.
	Performs the following steps:
	1. Saves information about virtual switches and management OS vNICs (includes IPs, QoS settings, jumbo frame info, etc.)
	2. If system belongs to a cluster, sets to maintenance mode
	3. Disconnects attached virtual machine vNICs
	4. Deletes the virtual switch
	5. Deletes the LBFO team
	6. Creates switch-embedded team
	7. Recreates management OS vNICs
	8. Reconnects previously-attached virtual machine vNICs
	9. If system belongs to a cluster, ends maintenance mode
	If you do not specify any overriding parameters, the new switch uses the same settings as the original LBFO+team.
.PARAMETER Id
	The unique identifier(s) for the virtual switch(es) to convert.
.PARAMETER Name
	The name(s) of the virtual switch(es) to convert.
.PARAMETER VMSwitch
	The virtual switch(es) to convert.
.PARAMETER NewName
	Name(s) to assign to the converted virtual switch(es). If blank, keeps the original name.
.PARAMETER UseDefaults
	If specified, uses defaults for all values on the converted switch(es). If not specified, uses the same parameters as the original LBFO+switch or any manually-specified parameters.
.PARAMETER LoadBalancingAlgorithm
	Sets the load balancing algorithm for the converted switch(es). If not specified, uses the same setting as the original LBFO+switch or the default if UseDefaults is set.
.PARAMETER MinimumBandwidthMode
	Sets the desired QoS mode for the converted switch(es). If not specified, uses the same setting as the original LBFO+switch or the default if UseDefaults is set.
	None: No network QoS
	Absolute: minimum bandwidth values specify bits per second
	Weight: minimum bandwidth values range from 1 to 100 and represent percentages
	Default: use system default
	WARNING: Changing the QoS mode may cause guest vNICS to fail to re-attach and may inhibit Live Migration. Use carefully if you have special QoS settings on guest virtual NICs.
.PARAMETER Notes
	A note to associate with the converted switch(es). If not specified, uses the same setting as the original LBFO+switch or the default if UseDefaults is set.
.PARAMETER Force
	If specified, bypasses confirmation.
.NOTES
	Author: Eric Siron
	Version 1.0, December 22, 2019
	Released under MIT license
.EXAMPLE
	ConvertTo-SwitchEmbeddedTeam
	Converts all existing LBFO+switch combinations to switch embedded teams. Copies settings from original switches and management OS virtual NICs to new switch and vNICs.
.EXAMPLE
	ConvertTo-SwitchEmbeddedTeam -Name vSwitch
	Converts the LBFO+switch combination of the virtual switch named "vSwitch" to a switch embedded teams. Copies settings from original switch and management OS virtual NICs to new switch and vNICs.
.EXAMPLE
	ConvertTo-SwitchEmbeddedTeam -Force
	Converts all existing LBFO+team combinations without prompting.
.EXAMPLE
	ConvertTo-SwitchEmbeddedTeam -NewName NewSET
	If the system has one LBFO+switch, converts it to a switch-embedded team with the name "NewSET".
	If the system has multiple LBFO+switch combinations, fails due to mismatch (see next example).
.EXAMPLE
	ConvertTo-SwitchEmbeddedTeam -NewName NewSET1, NewSET2
	If the system has two LBFO+switches, converts them to switch-embedded team with the name "NewSET1" and "NEWSET2", IN THE ORDER THAT GET-VMSWITCH RETRIEVES THEM.
.EXAMPLE
	ConvertTo-SwitchEmbeddedTeam OldSwitch1, OldSwitch2 -NewName NewSET1, NewSET2
	Converts the LBFO+switches named "OldSwitch1" and "OldSwitch2" to SETs named "NewSET1" and "NewSET2", respectively.
.EXAMPLE
	ConvertTo-SwitchEmbeddedTeam -UseDefaults
	Converts all existing LBFO+switch combinations to switch embedded teams. Discards non-default settings for the switch and Hyper-V-related management OS vNICs. Keeps IP addresses and advanced settings (ex. jumbo frames).
.EXAMPLE
	ConvertTo-SwitchEmbeddedTeam -MinimumBandwidthMode Weight
	Converts all existing LBFO+switch combinations to switch embedded teams. Forces the new SET to use "Weight" for its minimum bandwidth mode.
	WARNING: Changing the QoS mode may cause guest vNICS to fail to re-attach and may inhibit Live Migration. Use carefully if you have special QoS settings on guest virtual NICs.
.LINK
https://ejsiron.github.io/Posher-V/ConvertTo-SwitchEmbeddedTeam
#>

#Requires -RunAsAdministrator
#Requires -Module Hyper-V
#Requires -Version 5

[CmdletBinding(DefaultParameterSetName = 'ByName', ConfirmImpact = 'High')]
param(
	[Parameter(Position = 1, ParameterSetName = 'ByName')][String[]]$Name = @(''),
	[Parameter(Position = 1, ParameterSetName = 'ByID', Mandatory = $true)][System.Guid[]]$Id,
	[Parameter(Position = 1, ParameterSetName = 'BySwitchObject', Mandatory = $true)][Microsoft.HyperV.PowerShell.VMSwitch[]]$VMSwitch,
	[Parameter(Position = 2)][String[]]$NewName = @(),
	[Parameter()][Switch]$UseDefaults,
	[Parameter()][Microsoft.HyperV.PowerShell.VMSwitchLoadBalancingAlgorithm]$LoadBalancingAlgorithm,
	[Parameter()][Microsoft.HyperV.PowerShell.VMSwitchBandwidthMode]$MinimumBandwidthMode,
	[Parameter()][String]$Notes = '',
	[Parameter()][Switch]$Force
)

BEGIN
{
	Set-StrictMode -Version Latest
	$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
	$IsClustered = $false
	if(Get-CimInstance -Namespace root -ClassName __NAMESPACE -Filter 'Name="MSCluster"')
	{
		$IsClustered = [bool](Get-CimInstance -Namespace root/MSCluster -ClassName mscluster_cluster -ErrorAction SilentlyContinue)
		$ClusterNode = Get-CimInstance -Namespace root/MSCluster -ClassName MSCluster_Node -Filter ('Name="{0}"' -f $env:COMPUTERNAME)
	}

	function Get-CimAdapterConfigFromVirtualAdapter
	{
		param(
			[Parameter()][psobject]$VNIC
		)
		$VnicCim = Get-CimInstance -Namespace root/virtualization/v2 -ClassName Msvm_InternalEthernetPort -Filter ('Name="{0}"' -f $VNIC.AdapterId)
		$VnicLanEndpoint1 = Get-CimAssociatedInstance -InputObject $VnicCim -ResultClassName Msvm_LANEndpoint
		$NetAdapter = Get-CimInstance -ClassName Win32_NetworkAdapter -Filter ('GUID="{0}"' -f $VnicLANEndpoint1.Name.Substring(($VnicLANEndpoint1.Name.IndexOf('{'))))
		Get-CimAssociatedInstance -InputObject $NetAdapter -ResultClassName Win32_NetworkAdapterConfiguration
	}

	function Get-AdvancedSettingsFromAdapterConfig
	{
		param(
			[Parameter()][psobject]$AdapterConfig
		)
		$MSFTAdapter = Get-CimInstance -Namespace root/StandardCimv2 -ClassName MSFT_NetAdapter -Filter ('InterfaceIndex={0}' -f $AdapterConfig.InterfaceIndex)
		Get-CimAssociatedInstance -InputObject $MSFTAdapter -ResultClassName MSFT_NetAdapterAdvancedPropertySettingData
	}

	class NetAdapterDataPack
	{
		[System.String]$Name
		[System.String]$MacAddress
		[System.Int64]$MinimumBandwidthAbsolute = 0
		[System.Int64]$MinimumBandwidthWeight = 0
		[System.Int64]$MaximumBandwidth = 0
		[System.Int32]$VlanId = 0
		[Microsoft.Management.Infrastructure.CimInstance]$NetAdapterConfiguration
		[Microsoft.Management.Infrastructure.CimInstance[]]$AdvancedProperties
		[Microsoft.Management.Infrastructure.CimInstance[]]$IPAddresses
		[Microsoft.Management.Infrastructure.CimInstance[]]$Gateways

		NetAdapterDataPack([psobject]$VNIC)
		{
			$this.Name = $VNIC.Name
			$this.MacAddress = $VNIC.MacAddress
			if ($VNIC.BandwidthSetting -ne $null)
			{
				$this.MinimumBandwidthAbsolute = $VNIC.BandwidthSetting.MinimumBandwidthAbsolute
				$this.MinimumBandwidthWeight = $VNIC.BandwidthSetting.MinimumBandwidthWeight
				$this.MaximumBandwidth = $VNIC.BandwidthSetting.MaximumBandwidth
			}

			$this.VlanId = [System.Int32](Get-VMNetworkAdapterVlan -VMNetworkAdapter $VNIC).AccessVlanId
			$this.NetAdapterConfiguration = Get-CimAdapterConfigFromVirtualAdapter -VNIC $VNIC
			$this.AdvancedProperties = @(Get-AdvancedSettingsFromAdapterConfig -AdapterConfig $this.NetAdapterConfiguration  | Where-Object -FilterScript { (-not [String]::IsNullOrEmpty($_.DefaultRegistryValue)) -and (-not [String]::IsNullOrEmpty([string]($_.RegistryValue))) -and (-not [String]::IsNullOrEmpty($_.DisplayName)) -and ($_.RegistryValue[0] -ne $_.DefaultRegistryValue) })

			# alternative to the below: use Get-NetIPAddress and Get-NetRoute, but they treat empty results as errors
			$this.IPAddresses = @(Get-CimInstance -Namespace root/StandardCimv2 -ClassName MSFT_NetIPAddress -Filter ('InterfaceIndex={0} AND PrefixOrigin=1' -f $this.NetAdapterConfiguration.InterfaceIndex))
			$this.Gateways = @(Get-CimInstance -Namespace root/StandardCimv2 -ClassName MSFT_NetRoute -Filter ('InterfaceIndex={0} AND Protocol=3' -f $this.NetAdapterConfiguration.InterfaceIndex)) # documentation says Protocol=2 for NetMgmt, testing shows otherwise
		}
	}

	class SwitchDataPack
	{
		[System.String]$Name
		[Microsoft.HyperV.PowerShell.VMSwitchBandwidthMode]$BandwidthReservationMode
		[System.UInt64]$DefaultFlow
		[System.String]$TeamName
		[System.String[]]$TeamMembers
		[System.UInt32]$LoadBalancingAlgorithm
		[NetAdapterDataPack[]]$HostVNICs

		SwitchDataPack(
			[psobject]$VSwitch,
			[Microsoft.Management.Infrastructure.CimInstance]$Team,
			[System.Object[]]$VNICs
		)
		{
			$this.Name = $VSwitch.Name
			$this.BandwidthReservationMode = $VSwitch.BandwidthReservationMode
			switch ($this.BandwidthReservationMode)
			{
				[Microsoft.HyperV.PowerShell.VMSwitchBandwidthMode]::Absolute { $this.DefaultFlow = $VSwitch.DefaultFlowMinimumBandwidthAbsolute }
				[Microsoft.HyperV.PowerShell.VMSwitchBandwidthMode]::Weight { $this.DefaultFlow = $VSwitch.DefaultFlowMinimumBandwidthWeight }
				default { $this.DefaultFlow = 0 }
			}
			$this.TeamName = $Team.Name
			$this.TeamMembers = ((Get-CimAssociatedInstance -InputObject $Team -ResultClassName MSFT_NetLbfoTeamMember).Name)
			$this.LoadBalancingAlgorithm = $Team.LoadBalancingAlgorithm
			$this.HostVNICs = $VNICs
		}
	}

	function Set-CimAdapterProperty
	{
		param(
			[Parameter()][System.Object]$InputObject,
			[Parameter()][System.String]$MethodName,
			[Parameter()][System.Object]$Arguments,
			[Parameter()][System.String]$Activity,
			[Parameter()][System.String]$Url
		)

		Write-Verbose -Message $Activity
		$CimResult = Invoke-CimMethod -InputObject $InputObject -MethodName $MethodName -Arguments $Arguments -ErrorAction Continue

		if ($CimResult -and $CimResult.ReturnValue -gt 0 )
		{
			Write-Warning -Message ('CIM error from operation: {0}. Consult {1} for error code {2}' -f $Activity, $Url, $CimResult.ReturnValue) -WarningAction Continue
		}
	}
}

PROCESS
{
	$VMSwitches = New-Object System.Collections.ArrayList
	$SwitchRebuildData = New-Object System.Collections.ArrayList

	switch ($PSCmdlet.ParameterSetName)
	{
		'ByID'
		{
			$VMSwitches.AddRange($Id.ForEach( { Get-VMSwitch -Id $_ -ErrorAction SilentlyContinue }))
		}
		'BySwitchObject'
		{
			$VMSwitches.AddRange($VMSwitch.ForEach( { $_ }))
		}
		default	# ByName
		{
			$NameList = New-Object System.Collections.ArrayList
			$NameList.AddRange($Name.ForEach( { $_.Trim() }))
			if ($NameList.Contains('') -or $NameList.Contains('*'))
			{
				$VMSwitches.AddRange(@(Get-VMSwitch -ErrorAction SilentlyContinue))
			}
			else
			{
				$VMSwitches.AddRange($NameList.ForEach( { Get-VMSwitch -Name $_ -ErrorAction SilentlyContinue }))
			}
		}
	}
	if ($VMSwitches.Count)
	{
		$VMSwitches = @(Select-Object -InputObject $VMSwitches -Unique)
	}
	else
	{
		throw('No virtual switches match the provided criteria')
	}

	Write-Progress -Activity 'Pre-flight' -Status 'Verifying operating system version' -PercentComplete 5 -Id 1
	Write-Verbose -Message 'Verifying operating system version'
	$OSVersion = [System.Version]::Parse((Get-CimInstance -ClassName Win32_OperatingSystem).Version)
	if ($OSVersion.Major -lt 10)
	{
		throw('Switch-embedded teams not supported on host operating system versions before 2016')
	}

	Write-Progress -Activity 'Pre-flight' -Status 'Loading virtual VMswitches' -PercentComplete 15 -Id 1

	if ($NewName.Count -gt 0 -and $NewName.Count -ne $VMSwitches.Count)
	{
		$SwitchNameMismatchMessage = 'Switch count ({0}) does not match NewName count ({1}).' -f $VMSwitches.Count, $NewName.Count
		if ($NewName.Count -lt $VMSwitches.Count)
		{
			$SwitchNameMismatchMessage += ' If you wish to rename some VMswitches but not others, specify an empty string for the VMswitches to leave.'
		}
		throw($SwitchNameMismatchMessage)
	}

	Write-Progress -Activity 'Pre-flight' -Status 'Validating virtual switch configurations' -PercentComplete 25 -Id 1
	Write-Verbose -Message 'Validating virtual switches'
	foreach ($VSwitch in $VMSwitches)
	{
		try
		{
			Write-Progress -Activity ('Validating virtual switch "{0}"' -f $VSwitch.Name) -Status 'Switch is external' -PercentComplete 25 -ParentId 1
			Write-Verbose -Message ('Verifying that switch "{0}" is external' -f $VSwitch.Name)
			if ($VSwitch.SwitchType -ne [Microsoft.HyperV.PowerShell.VMSwitchType]::External)
			{
				Write-Warning -Message ('Switch "{0}" is not external, skipping' -f $VSwitch.Name)
				continue
			}

			Write-Progress -Activity ('Validating virtual switch "{0}"' -f $VSwitch.Name) -Status 'Switch is not a SET' -PercentComplete 50 -ParentId 1
			Write-Verbose -Message ('Verifying that switch "{0}" is not already a SET' -f $VSwitch.Name)
			if ($VSwitch.EmbeddedTeamingEnabled)
			{
				Write-Warning -Message ('Switch "{0}" already uses SET, skipping' -f $VSwitch.Name)
				continue
			}

			Write-Progress -Activity ('Validating virtual switch "{0}"' -f $VSwitch.Name) -Status 'Switch uses LBFO' -PercentComplete 75 -ParentId 1
			Write-Verbose -Message ('Verifying that switch "{0}" uses an LBFO team' -f $VSwitch.Name)
			$TeamAdapter = Get-CimInstance -Namespace root/StandardCimv2 -ClassName MSFT_NetLbfoTeamNic -Filter ('InterfaceDescription="{0}"' -f $VSwitch.NetAdapterInterfaceDescription)
			if ($TeamAdapter -eq $null)
			{
				Write-Warning -Message ('Switch "{0}" does not use a team, skipping' -f $VSwitch.Name)
				continue
			}
			if ($TeamAdapter.VlanID)
			{
				Write-Warning -Message ('Switch "{0}" is bound to a team NIC with a VLAN assignment, skipping' -f $VSwitch.Name)
				continue
			}
		}
		catch
		{
			Write-Warning -Message ('Switch "{0}" failed validation, skipping. Error: {1}' -f $VSwitch.Name, $_.Exception.Message)
			continue
		}
		finally
		{
			Write-Progress -Activity ('Validating virtual switch "{0}"' -f $VSwitch.Name) -Completed -ParentId 1
		}

		Write-Progress -Activity ('Loading information from virtual switch "{0}"' -f $VSwitch.Name) -Status 'Team NIC' -PercentComplete 25 -ParentId 1
		Write-Verbose -Message 'Loading team'
		$Team = Get-CimAssociatedInstance -InputObject $TeamAdapter -ResultClassName MSFT_NetLbfoTeam


		Write-Progress -Activity ('Loading information from virtual switch "{0}"' -f $VSwitch.Name) -Status 'Host virtual adapters' -PercentComplete 50 -ParentId 1
		Write-Verbose -Message 'Loading management adapters connected to this switch'
		$HostVNICs = Get-VMNetworkAdapter -ManagementOS -SwitchName $VSwitch.Name

		Write-Verbose -Message 'Compiling virtual switch and management OS virtual NIC information'
		Write-Progress -Activity ('Loading information from virtual switch "{0}"' -f $VSwitch.Name) -Status 'Storing vSwitch data' -PercentComplete 75 -ParentId 1
		$OutNull = $SwitchRebuildData.Add([SwitchDataPack]::new($VSwitch, $Team, ($HostVNICs.ForEach({ [NetAdapterDataPack]::new($_) }))))
		Write-Progress -Activity ('Loading information from virtual switch "{0}"' -f $VSwitch.Name) -Completed
	}
	Write-Progress -Activity 'Pre-flight' -Status 'Cleaning up' -PercentComplete 99 -ParentId 1

	Write-Verbose -Message 'Clearing loop variables'
	$VSwitch = $Team = $TeamAdapter = $HostVNICs = $null

	Write-Progress -Activity 'Pre-flight' -Completed

	if($SwitchRebuildData.Count -eq 0)
	{
		Write-Warning -Message 'No eligible virtual switches found.'
		exit 1
	}

	$SwitchMark = 0
	$SwitchCounter = 1
	$SwitchStep = 1 / $SwitchRebuildData.Count * 100
	$ClusterNodeRunning = $IsClustered

	foreach ($OldSwitchData in $SwitchRebuildData)
	{
		$SwitchName = $OldSwitchData.Name
		if($NewName.Count -gt 0)
		{
			$SwitchName = $NewName[($SwitchCounter - 1)]
		}
		Write-Progress -Activity 'Rebuilding switches' -Status ('Processing virtual switch {0} ({1}/{2})' -f $SwitchName, $SwitchCounter, $SwitchRebuildData.Count) -PercentComplete $SwitchMark -Id 1
		$SwitchCounter++
		$SwitchMark += $SwitchStep
		$ShouldProcessTargetText = 'Virtual switch {0}' -f $OldSwitchData.Name
		$ShouldProcessOperation = 'Disconnect all virtual adapters, remove team and switch, build switch-embedded team, replace management OS vNICs, reconnect virtual adapters'
		if ($Force -or $PSCmdlet.ShouldProcess($ShouldProcessTargetText , $ShouldProcessOperation))
		{
			if($ClusterNodeRunning)
			{
				Write-Verbose -Message 'Draining cluster node'
				Write-Progress -Activity 'Draining cluster node' -Status 'Draining'
				$OutNull = Invoke-CimMethod -InputObject $ClusterNode -MethodName 'Pause' -Arguments @{DrainType=2;TargetNode=''}
				while($ClusterNodeRunning)
				{
					Start-Sleep -Seconds 1
					$ClusterNode = Get-CimInstance -InputObject $ClusterNode
					switch($ClusterNode.NodeDrainStatus)
					{
						0 { Write-Error -Message 'Failed to initiate cluster node drain' }
						2 { $ClusterNodeRunning = $false }
						3 { Write-Error -Message 'Failed to drain cluster roles' }
						# 1 is all that's left, will cause loop to continue
					}
				}
			}
			Write-Progress -Activity 'Draining cluster node' -Completed

			$SwitchProgressParams = @{Activity = ('Processing switch {0}' -f $OldSwitchData.Name); ParentId = 1; Id=2 }
			Write-Verbose -Message 'Disconnecting virtual machine adapters'
			Write-Progress @SwitchProgressParams -Status 'Disconnecting virtual machine adapters' -PercentComplete 10
			Write-Verbose -Message 'Loading VM adapters connected to this switch'
			$GuestVNICs = Get-VMNetworkAdapter -VMName * | Where-Object -Property SwitchName -EQ $OldSwitchData.Name
			if($GuestVNICs)
			{
				Disconnect-VMNetworkAdapter -VMNetworkAdapter $GuestVNICs
			}

			Start-Sleep -Milliseconds 250	# seems to prefer a bit of rest time between removal commands

			if($OldSwitchData.HostVNICs)
			{
				Write-Verbose -Message 'Removing management vNICs'
				Write-Progress @SwitchProgressParams -Status 'Removing management vNICs' -PercentComplete 20
				Remove-VMNetworkAdapter -ManagementOS
			}

			Start-Sleep -Milliseconds 250	# seems to prefer a bit of rest time between removal commands

			Write-Verbose -Message 'Removing virtual switch'
			Write-Progress @SwitchProgressParams -Status 'Removing virtual switch' -PercentComplete 30
			Remove-VMSwitch -Name $OldSwitchData.Name -Force

			Start-Sleep -Milliseconds 250	# seems to prefer a bit of rest time between removal commands

			Write-Verbose -Message 'Removing team'
			Write-Progress @SwitchProgressParams -Status 'Removing team' -PercentComplete 40
			Remove-NetLbfoTeam -Name $OldSwitchData.TeamName -Confirm:$false

			Start-Sleep -Milliseconds 250	# seems to prefer a bit of rest time between removal commands

			Write-Verbose -Message 'Creating SET'
			Write-Progress @SwitchProgressParams -Status 'Creating SET' -PercentComplete 50
			$SetLoadBalancingAlgorithm = $null
			if (-not $UseDefaults)
			{
				if ($OldSwitchData.LoadBalancingAlgorithm -eq 5)
				{
					$SetLoadBalancingAlgorithm = [Microsoft.HyperV.PowerShell.VMSwitchLoadBalancingAlgorithm]::Dynamic # 5 is dynamic; https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ndisimplatcimprov/msft-netlbfoteam
				}
				else # SET does not have LBFO's hash options for load-balancing; assume that the original switch used a non-Dynamic mode for a reason
				{
					$SetLoadBalancingAlgorithm = [Microsoft.HyperV.PowerShell.VMSwitchLoadBalancingAlgorithm]::HyperVPort
				}
			}
			if ($LoadBalancingAlgorithm)
			{
				$SetLoadBalancingAlgorithm = $LoadBalancingAlgorithm
			}

			$NewMinimumBandwidthMode = $null
			if(-not $UseDefaults)
			{
				$NewMinimumBandwidthMode = $OldSwitchData.BandwidthReservationMode
			}
			if ($MinimumBandwidthMode)
			{
				$NewMinimumBandwidthMode = $MinimumBandwidthMode
			}
			$NewSwitchParams = @{NetAdapterName=$OldSwitchData.TeamMembers}
			if($NewMinimumBandwidthMode)
			{
				$NewSwitchParams.Add('MinimumBandwidthMode', $NewMinimumBandwidthMode)
			}

			try
			{
				$NewSwitch = New-VMSwitch @NewSwitchParams -Name $SwitchName -AllowManagementOS $false -EnableEmbeddedTeaming $true -Notes $Notes
			}
			catch
			{
				Write-Error -Message ('Unable to create virtual switch {0}: {1}' -f $SwitchName, $_.Exception.Message) -ErrorAction Continue
				continue
			}

			if($SetLoadBalancingAlgorithm)
			{
				Write-Verbose -Message ('Setting load balancing mode to {0}' -f $SetLoadBalancingAlgorithm)
				Write-Progress @SwitchProgressParams -Status 'Setting SET load balancing algorithm' -PercentComplete 60
				Set-VMSwitchTeam -Name $NewSwitch.Name -LoadBalancingAlgorithm $SetLoadBalancingAlgorithm
			}

			$VNICCounter = 0

			foreach($VNIC in $OldSwitchData.HostVNICs)
			{
				$VNICCounter++
				Write-Progress @SwitchProgressParams -Status ('Configuring management OS vNIC {0}/{1}' -f $VNICCounter, $OldSwitchData.HostVNICs.Count) -PercentComplete 70

				$VNICProgressParams = @{Activity = ('Processing VNIC {0}' -f $VNIC.Name); ParentId = 2; Id=3 }

				Write-Verbose -Message ('Adding virtual adapter "{0}" to switch "{1}"' -f $VNIC.Name, $NewSwitch.Name)
				Write-Progress @VNICProgressParams -Status 'Adding vNIC' -PercentComplete 10
				$NewNic = Add-VMNetworkAdapter -SwitchName $NewSwitch.Name -ManagementOS -Name $VNIC.Name -StaticMacAddress $VNIC.MacAddress -Passthru
				$SetNicParams = @{ }
				if ((-not $UseDefaults) -and $VNIC.MinimumBandwidthAbsolute -and $NewSwitch.BandwidthReservationMode -eq [Microsoft.HyperV.PowerShell.VMSwitchBandwidthMode]::Absolute)
				{
					$SetNicParams.Add('MinimumBandwidthAbsolute', $VNIC.MinimumBandwidthAbsolute)
				}
				elseif ((-not $UseDefaults) -and $VNIC.MinimumBandwidthWeight -and $NewSwitch.BandwidthReservationMode -eq [Microsoft.HyperV.PowerShell.VMSwitchBandwidthMode]::Weight)
				{
					$SetNicParams.Add('MinimumBandwidthWeight', $VNIC.MinimumBandwidthWeight)
				}
				if ($VNIC.MaximumBandwidth)
				{
					$SetNicParams.Add('MaximumBandwidth', $VNIC.MaximumBandwidth)
				}
				Write-Verbose -Message ('Setting properties on virtual adapter "{0}" on switch "{1}"' -f $VNIC.Name, $NewSwitch.Name)

				Write-Progress @VNICProgressParams -Status 'Setting vNIC parameters' -PercentComplete 20
				Set-VMNetworkAdapter -VMNetworkAdapter $NewNic @SetNicParams -ErrorAction Continue
				if($VNIC.VlanId)
				{
					Write-Progress @VNICProgressParams -Status 'Setting VLAN ID' -PercentComplete 30
					Write-Verbose -Message ('Setting VLAN ID on virtual adapter "{0}" on switch "{1}"' -f $VNIC.Name, $NewSwitch.Name)
					Set-VMNetworkAdapterVlan -VMNetworkAdapter $NewNic -Access -VlanId $VNIC.VlanId
				}
				$NewNicConfig = Get-CimAdapterConfigFromVirtualAdapter -VNIC $NewNic

				if ($VNIC.IPAddresses.Count -gt 0)
				{
					Write-Progress @VNICProgressParams -Status 'Setting IP and subnet masks' -PercentComplete 40
					foreach($IPAddressData in $VNIC.IPAddresses)
					{
						Write-Verbose -Message ('Setting IP address {0}' -f $IPAddressData.IPAddress)
						$OutNull = New-NetIPAddress -InterfaceIndex $NewNicConfig.InterfaceIndex -IPAddress $IPAddressData.IPAddress -PrefixLength $IPAddressData.PrefixLength -SkipAsSource $IPAddressData.SkipAsSource -ErrorAction Continue
					}

					Write-Progress @VNICProgressParams -Status 'Setting DNS registration behavior' -PercentComplete 41
					Set-CimAdapterProperty -InputObject $NewNicConfig -MethodName 'SetDynamicDNSRegistration' `
					-Arguments @{ FullDNSRegistrationEnabled = $VNIC.NetAdapterConfiguration.FullDNSRegistrationEnabled; DomainDNSRegistrationEnabled = $VNIC.NetAdapterConfiguration.DomainDNSRegistrationEnabled } `
					-Activity ('Setting DNS registration behavior (dynamic registration: {0}, with domain name: {1}) on {2}' -f $VNIC.NetAdapterConfiguration.FullDNSRegistrationEnabled, $VNIC.NetAdapterConfiguration.DomainDNSRegistrationEnabled, $NewNic.Name) `
					-Url 'https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/setdynamicdnsregistration-method-in-class-win32-networkadapterconfiguration'

					foreach($GatewayData in $VNIC.Gateways)
					{
						Write-Verbose -Message ('Setting gateway address {0}' -f $GatewayData.NextHop)
						$OutNull = New-NetRoute -InterfaceIndex $NewNicConfig.InterfaceIndex -DestinationPrefix $GatewayData.DestinationPrefix -NextHop $GatewayData.NextHop -RouteMetric $GatewayData.RouteMetric
					}

					Write-Progress @VNICProgressParams -Status 'Setting gateways' -PercentComplete 42
					if ($VNIC.NetAdapterConfiguration.DefaultIPGateway)
					{
						Set-CimAdapterProperty -InputObject $NewNicConfig -MethodName 'SetGateways' `
						-Arguments @{ DefaultIPGateway = $VNIC.NetAdapterConfiguration.DefaultIPGateway } `
						-Activity ('Setting gateways {0} on {1}'  -f $VNIC.NetAdapterConfiguration.DefaultIPGateway, $NewNic.Name) `
						-Url 'https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/setgateways-method-in-class-win32-networkadapterconfiguration'
					}

					if($VNIC.NetAdapterConfiguration.DNSDomain)
					{
						Write-Progress @VNICProgressParams -Status 'Setting DNS domain' -PercentComplete 43
						Set-CimAdapterProperty -InputObject $NewNicConfig -MethodName 'SetDNSDomain' `
						-Arguments @{ DNSDomain = $VNIC.NetAdapterConfiguration.DNSDomain } `
						-Activity ('Setting DNS domain {0} on {1}' -f $VNIC.NetAdapterConfiguration.DNSDomain, $NewNic.Name) `
						-Url 'https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/setdnsdomain-method-in-class-win32-networkadapterconfiguration'
					}

					if ($VNIC.NetAdapterConfiguration.DNSServerSearchOrder)
					{
						Write-Progress @VNICProgressParams -Status 'Setting DNS servers' -PercentComplete 44
						Set-CimAdapterProperty -InputObject $NewNicConfig -MethodName 'SetDNSServerSearchOrder' `
						-Arguments @{ DNSServerSearchOrder = $VNIC.NetAdapterConfiguration.DNSServerSearchOrder } `
						-Activity ('setting DNS servers {0} on {1}' -f [String]::Join(', ', $VNIC.NetAdapterConfiguration.DNSServerSearchOrder), $NewNic.Name) `
						-Url 'https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/setdnsserversearchorder-method-in-class-win32-networkadapterconfiguration'
					}

					if($VNIC.NetAdapterConfiguration.WINSPrimaryServer)
					{
						Write-Progress @VNICProgressParams -Status 'Setting WINS servers' -PercentComplete 45
						Set-CimAdapterProperty -InputObject $NewNicConfig -MethodName 'SetWINSServer' `
						-Arguments @{ WINSPrimaryServer = $VNIC.NetAdapterConfiguration.WINSPrimaryServer; WINSSecondaryServer = $VNIC.NetAdapterConfiguration.WINSSecondaryServer }
						-Activity ('Setting WINS servers {0} on {1}' -f ([String]::Join(', ', $VNIC.NetAdapterConfiguration.WINSPrimaryServer, $VNIC.NetAdapterConfiguration.WINSSecondaryServer)), $NewNic.Name) `
						-Url 'https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/setwinsserver-method-in-class-win32-networkadapterconfiguration'
					}
				}
				if($VNIC.NetAdapterConfiguration.TcpipNetbiosOptions)	# defaults to 0
				{
					Write-Progress @VNICProgressParams -Status 'Setting NetBIOS over TCP/IP behavior' -PercentComplete 50
					Set-CimAdapterProperty -InputObject $NewNicConfig -MethodName 'SetTcpipNetbios' `
					-Arguments @{ TcpipNetbiosOptions = $VNIC.NetAdapterConfiguration.TcpipNetbiosOptions } `
					-Activity ('Setting NetBIOS over TCP/IP behavior on {0} to {1}' -f $NewNic.Name, $VNIC.NetAdapterConfiguration.TcpipNetbiosOptions) `
					-Url 'https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/settcpipnetbios-method-in-class-win32-networkadapterconfiguration'
				}

				Write-Progress @VNICProgressParams -Status 'Applying advanced properties' -PercentComplete 60
				$NewNicAdvancedProperties = Get-AdvancedSettingsFromAdapterConfig -AdapterConfig $NewNicConfig
				$PropertiesCounter = 0
				$PropertyProgressParams = @{Activity = 'Processing VNIC advanced properties'; ParentId = 3; Id=4 }
				foreach($SourceAdvancedProperty in $VNIC.AdvancedProperties)
				{
					foreach($NewNicAdvancedProperty in $NewNicAdvancedProperties)
					{
						if($SourceAdvancedProperty.ElementName -eq $NewNicAdvancedProperty.ElementName)
						{
							$PropertiesCounter++
							Write-Progress @PropertyProgressParams -PercentComplete ($PropertiesCounter / $VNIC.AdvancedProperties.Count * 100) -Status ('Applying property {0}' -f $SourceAdvancedProperty.DisplayName)
							Write-Verbose ('Setting advanced property {0} to {1} on {2}' -f $SourceAdvancedProperty.DisplayName, $SourceAdvancedProperty.DisplayValue, $VNIC.Name)
							$NewNicAdvancedProperty.RegistryValue = $SourceAdvancedProperty.RegistryValue
							Set-CimInstance -InputObject $NewNicAdvancedProperty -ErrorAction Continue
						}
					}
				}
			}

			Write-Progress @VNICProgressParams -Completed

			Write-Progress @SwitchProgressParams -Status 'Reconnecting guest vNICs' -PercentComplete 80

			if($GuestVNICs)
			{
				foreach ($GuestVNIC in $GuestVNICs)
				{
					try
					{
						Connect-VMNetworkAdapter -VMNetworkAdapter $GuestVNIC -VMSwitch $NewSwitch
					}
					catch
					{
						Write-Error -Message ('Failed to connect virtual adapter "{0}" with MAC address "{1}" to virtual switch "{2}": {3}' -f $GuestVNIC.Name, $GuestVNIC.MacAddress, $NewSwitch.Name, $_.Exception.Message) -ErrorAction Continue
					}
				}
			}

			Write-Progress @SwitchProgressParams -Completed
		}
	}
	if($IsClustered)
	{
		Write-Verbose -Message 'Resuming cluster node'
		$OutNull = Invoke-CimMethod -InputObject $ClusterNode -MethodName 'Resume' -Arguments @{FailbackType=1}
	}
}

The post Free Script – Convert Legacy Teamed Hyper-V vSwitch to SET appeared first on Altaro DOJO | Hyper-V.

]]>
https://www.altaro.com/hyper-v/free-script-vswitch-set/feed/ 3
How to Customize Hyper-V VMs using PowerShell https://www.altaro.com/hyper-v/customize-vm-powershell/ https://www.altaro.com/hyper-v/customize-vm-powershell/#respond Fri, 03 Jan 2020 17:25:47 +0000 https://www.altaro.com/hyper-v/?p=18251 A comprehensive guide to customizing Hyper-V virtual machines using PowerShell. Covers best practices for settings, automation, optimization, and more!

The post How to Customize Hyper-V VMs using PowerShell appeared first on Altaro DOJO | Hyper-V.

]]>

In this article, we’ll be covering all the main points for deploying and modifying virtual machines in Hyper-V using PowerShell.

You can create a Hyper-V virtual machine easily using a number of tools. The “easy” tool, Hyper-V Manager’s New Virtual Machine Wizard (and its near-equivalent in Failover Cluster Manager), creates only a basic system. It has a number of defaults that you might not like. If you forget to change something, then you might have to schedule downtime later to correct it. You have other choices for VM creation. Among these, PowerShell gives you the greatest granularity and level of control. We’ll take a tour of the capability at your fingertips. After the tour, we will present some ways that you can leverage this knowledge to make VM creation simpler, quicker, and less error-prone than any of the GUI methods.

Cmdlets Related to Virtual Machine Creation

Of course, you create virtual machines using New-VM. But, like the wizards, it has limits. You will use other cmdlets to finesse the final product into exactly what you need.

The above cmdlets encompass the features needed for a majority of cases. If you need something else, you can start with the complete list of Hyper-V-related cmdlets.

Note: This article was written using the cmdlets as found on Windows Server 2019.

Comparing PowerShell to the Wizards for Virtual Machine Creation

PowerShell makes some things a lot quicker than the GUI tools. That doesn’t always apply to virtual machine creation. You will need to consider the overall level of effort before choosing either approach. Start with an understanding of what each approach asks of you.

The GUI Wizard Outcome

Virtual machine configuration points you can control when creating a virtual machine using the wizard:

  • Name
  • Virtual machine generation
  • Storage location
  • Virtual switch connection
  • The startup memory quantity and whether the VM uses Dynamic Memory
  • Attach or create one VHD or VHDX to the default location
  • Initial boot configuration

If you used the wizard from within Failover Cluster Manager, it will have also added the VM to the cluster’s highly-available roles.

The wizard does fairly well at hitting the major configuration points for a new virtual machine. It does miss some things, though. Most notably, you only get vCPU. Once you finish using the wizard to create a virtual machine, you must then work through the VM’s properties to fix up anything else.

The Windows Admin Center Outcome

Virtual machine configuration points you can control when creating a virtual machine using Windows Admin Center:

  • Name
  • Virtual machine generation
  • The system that will host the VM (if you started VM creation from the cluster panel)
  • Base storage location — one override for both the VM’s configuration files and the VHDX(s)
  • Virtual switch connection
  • The number of virtual processors
  • The startup memory quantity, whether the VM uses Dynamic Memory, and DM’s minimum and maximum values
  • Attach or create one or more VHDs or VHDXs
  • Initial boot configuration

If you used the wizard from within Failover Cluster Manager, it will have also added the VM to the cluster’s highly-available roles.

Windows Admin Center does more than the MMC wizards, making it more likely that you can immediately use the created virtual machine. It does miss a few common configuration points, such as VLAN assignment and startup/shutdown settings.

The PowerShell Outcome

As for PowerShell, we have nothing missed on the outcome. It can do everything. Some parts take a bit more effort. You will often need two or more cmdlets to fully create a VM as desired. Before we demonstrate them, we need to cover the difference between PowerShell and the above GUI methods.

Why Should I Use PowerShell Instead of the GUI to Create a Virtual Machine?

So, if PowerShell takes more work, why would you do it? Well, if you have to create only one or two VMs, maybe you wouldn’t. In a lot of cases, it makes the most sense:

  • One-stop location for the creation of VMs with advanced settings
  • Repeatable VM creation steps
  • More precise control over the creation
  • Access to features and settings unavailable in the GUI

For single VM creation, PowerShell saves you from some double-work and usage of multiple interfaces. You don’t have to run a wizard to create the VM and then dig through property sheets to make changes. You also don’t have to start in a wizard and then switch to PowerShell if you want to change a setting not included in the GUI.

Understanding Permissions for Hyper-V’s Cmdlets

If you run these cmdlets locally on the Hyper-V host as presented in this article, then you must belong to the local Administrators group. I have personally never used the “Hyper-V Administrators” group, ever, just on principle. A Hyper-V host should not do anything else, and I have not personally encountered a situation where it made sense to separate host administration from Hyper-V administration. I have heard from others that membership in the “Hyper-V Administrators” group does not grant the powers that they expect. Your mileage may vary.

Additional Requirements for Remote Storage

If the storage location for your VMs or virtual hard disks resides on a remote system (SMB), then you have additional concerns that require you to understand the security model of Hyper-V’s helper services. Everything that you do with the Hyper-V cmdlets (and GUI tools) accesses a central CIM-based API. These APIs do their work by a two-step process:

  • The Hyper-V host verifies that your account has permission to access the requested API
  • Service on the Hyper-V host carries out the requested action within its security context

By default, these services run as the “Local System” account. They present themselves to other entities on the network as the Hyper-V host’s computer account, not your account. Changing the account that runs the services places you in an unsupported configuration. Just understand that they run under that account and act accordingly.

The Hyper-V host’s computer account must have at least Modify the permission on the remote NTFS/ReFS file system and at least Change on the SMB share.

Additional Requirements for Remote Sessions

If you run these cmdlets remotely, whether explicitly (inside a PSSession) or implicitly (using the ComputerName) parameter, and you do anything that depends on SMB storage (a second hop), then you must configure delegation.

The security points of a delegated operation:

  • The account that you use to run the cmdlet must have administrator privileges on the Hyper-V host
  • The Hyper-V host must allow delegation of credentials to the target location
  • You must configure the target SMB share as indicated in the last sentence of the preceding section

These rules apply whether you allow the commands to use the host’s configured default locations or if you override.

If you need help with the delegation part, we have a script for delegation.

Shortest Possible Use of New-VM

You can run New-VM with only one required parameter:

New-VM -Name 'demovm'

This creates a virtual machine on the local host with the following characteristics:

  • Name: “demovm”
  • Generation 1
  • 1 vCPU
  • No virtual hard disk
  • Virtual CD/DVD attached to virtual IDE controller 1, location 0
  • Synthetic adapter, not connected to a virtual switch
  • Boots from CD (only bootable device on such a system)

I do not use the cmdlet this way, ever. I personally create only Generation 2 machines now unless I have some overriding reason. You can change all other options after creation. Also, I tend to connect to the virtual switch right away, as it saves me a cmdlet later.

I showed this usage so that you understand the default behavior.

Simple VM Creation in PowerShell

We’ll start with a very basic VM, using simple but verbose cmdlets.

New-VM -Name 'demovm' -MemoryStartupBytes 2gb -Generation 2 -SwitchName 'vSwitch'

The above creates a new Generation 2 virtual machine in the host’s default location named “demovm” with 2 gigabytes of startup memory and connects it to the virtual switch named “vSwitch”. It uses static memory because New-VM cannot enable Dynamic Memory. It uses one vCPU because New-VM cannot override that. It does not have a VHDX. We can do that with New-VM, but I have a couple of things to note for that, and I wanted to start easy. Yes, you will have to issue more cmdlets to change the additional items, but you’re already in the right place to do that. No need to switch to another screen.

Before we move on to post-creation modifications, let’s look at uncommon creation options.

Create a Simple VM with a Specific Version in PowerShell

New-VM has one feature that the GUI cannot replicate by any means: it can create a VM with a specific configuration version. Without overriding, you can only create VMs that use the maximum supported version of the host that builds the VM. If you will need to migrate or replicate the VM to a host running an older version, then you must use New-VM and specify a version old enough to run on all necessary hosts.

To create the same diskless VM as above, but that can run on a Windows Server 2012 R2 host:

New-VM -Name 'demovm' -MemoryStartupBytes 2gb -Generation 2 -SwitchName 'vSwitch' -Version 5.0

You can see the possible versions and their compatibility levels with Get-VMHostSupportedVersion.

Be aware that creating a VM with a lower version may have unintended side effects. For instance, versions prior to 8 don’t support hardware thread counts so they won’t have access to Hyper-Threading when running on a Hyper-V host that uses the core scheduler. You can see the official matrix of VM features by version on the related Microsoft docs page.

Note: New-VM also exposes the Experimental and Prerelease switches, but these don’t work on regular release versions of Hyper-V. These switches create VMs with versions above the host’s normally supported maximum. Perhaps they function on Insider versions, but I have not tried.

Simple VM Creation with Positional Parameters

When we write scripts, we should always type out the full names of parameters. But, if you’re working interactively, take all the shortcuts you like. Let’s make that same “demovm”, but save ourselves a few keystrokes:

new-vm 'demovm' 2gb 2 -SwitchName 'vSwitch'

SwitchName is the only non-positional parameter that we used. You can tell from the help listing (Get-Help New-VM):

New-VM [[-Name] <String>] [[-MemoryStartupBytes] <Int64>] [[-Generation] {1 | 2}] [-AsJob] [-BootDevice {Floppy | CD | IDE | LegacyNetworkAdapter | NetworkAdapter | VHD}] [-CimSession <CimSession[]>] [-ComputerName <String[]>] [-Confirm] [-Credential <PSCredential[]>] [-Experimental] [-Force] -NewVHDPath <String> -NewVHDSizeBytes <UInt64> [-Path <String>] [-Prerelease] [-SwitchName <String>] [-Version <Version>] [-WhatIf] [<CommonParameters>]

Each parameter surrounded by double brackets ([[ParameterName]]) is positional. As long as you supply its value in the exact order that it appears, you do not need to type its name.

In only 43 characters, we have accomplished the same as all but one of the wizard’s tabs. If you want to make it even shorter, the quote marks around the VM and switch names are only necessary if they contain spaces. And, once the cmdlet completes, we can just keep typing to change anything that New-VM didn’t cover.

Create a Simple VM in a Non-Default Path

We can place the VM in a location other than the default with one simple change, but it has a behavioral side effect. First, the cmdlet:

New-VM -Name 'demovm' -MemoryStartupBytes 2gb -Generation 2 -SwitchName 'vSwitch' -Path 'C:LocalVMs'

The Path parameter overrides the placement of the VM from the host defaults. It does not impact the placement of any virtual hard disks.

As for the previously mentioned side effect, compare the value of the Path parameter of a VM created using the default (on my system):

Path                                : \svstore01vms

The Path parameter of a VM with the overriden path value:

Path                                : C:LocalVMsdemovm

When you do not specify a Path, the cmdlet will place all of the virtual machine’s files in the relevant subfolders of the host’s default path (Virtual Machines, Snapshots, etc.). When you specify a path, it first creates a subfolder with the name of the VM, then creates all those other subfolders inside. As far as I know, all of the tools exhibit this same behavior (I did not test WAC).

Create a VM with a VHDX, Single Cmdlet

To create the VM with a virtual hard disk in one shot, you must specify both the NewVHDPath and NewVHDSizeBytes parameter. NewVHDPath operates somewhat independently of Path.

Start with the easiest usage:

New-VM -Name 'demovm' -MemoryStartupBytes 2gb -Generation 2 -SwitchName 'vSwitch' -NewVHDPath 'demovm.vhdx' -NewVHDSizeBytes 60gb

The above cmdlet does very nearly all the same things like the GUI wizard but in one line. It starts by doing the same things as the first simple cmdlet that I showed you. Then  It also creates a VHDX of the specified name. Since the NewVHDPath parameter only indicates the file name, the cmdlet creates it in the host’s default virtual hard disk storage location. To finish up, it attaches the new disk to the VM’s first disk boot location (IDE 0:0 or SCSI 0:0, depending on VM Generation).

Create a VM with a VHDX, Override the VHDX Storage Location

Don’t want the VHDX in the default location? Just change NewVHDPath so that it specifies the full path to the desired location:

New-VM -Name 'demovm' -MemoryStartupBytes 2gb -Generation 2 -SwitchName 'vSwitch' -NewVHDPath '\svstore02vmstoreVirtual Hard Disksdemovm.vhdx' -NewVHDSizeBytes 60gb

Create a VM with a VHDX, Override the Entire VM Location

Want to change the location of the entire VM, but don’t want to specify the path twice? Override placement of the VM using Path, but provide only the file name for NewVHDPath:

New-VM -Name 'demovm' -MemoryStartupBytes 2gb -Generation 2 -SwitchName 'vSwitch' -Path 'C:LocalVMs' -NewVHDPath 'demovm.vhdx' -NewVHDSizeBytes 60gb

The above cmdlet creates a “demovm” folder in “C:LocalVMs”. It places the virtual machine’s configuration files in a “Virtual Machines” subfolder and places the new VHDX in a “Virtual Hard Disks” subfolder.

Just as before, you can place the VHDX into an entirely different location just by providing the full path.

Notes on VHDX Creation with New-VM

A few points:

  • You must always supply the VHDX’s complete file name. New-VM will not guess at what you want to call your virtual disk, nor will it auto-append the .vhdx extension.
  • You must always supply a .vhdx extension. New-VM will not create a VHD formatted disk.
  • All the rules about second-hops and delegation apply.
  • Paths operate from the perspective of the Hyper-V host. When running remotely, a path like “C:LocalVMs” means the C: disk on the host, not on your remote system.
  • You cannot specify an existing file. The entire cmdlet will fail if the file already exists (meaning that, if you tell it to create a new disk and it cannot for some reason, then it will not create the VM, either).

As with the wizard, New-VM can create only one VHDX and it will always connect to the primary boot location (IDE controller 0 location 0 for Generation 1, SCSI controller 0 location 0 for Generation 2). You can issue additional PowerShell commands to create and attach more virtual disks. We’ll tackle that after we finish with the New-VM cmdlet.

Create a VM with a VHDX, Single Cmdlet, and Specify Boot Order

We have one more item to control with New-VHD: the boot device. Using the above cmdlets, your newly created VM will try to boot to the network first. If you used one of the variants that create a virtual hard disk, a failed network boot will fall through to the disk.

Let’s create a VM that boots to the virtual CD/DVD drive instead:

New-VM -Name 'demovm' -MemoryStartupBytes 2gb -Generation 2 -SwitchName 'vSwitch' -BootDevice CD

You have multiple options for the BootDevice parameter:

  • CD: attach a virtual drive and set it as the primary boot device
  • Floppy: set the virtual floppy drive as the primary boot device; Generation 1 only
  • IDE: set IDE controller 0, location 0 as the primary boot device; Generation 1 only
  • LegacyNetworkAdapter: attach a legacy network adapter and set it as the primary boot device; Generation 1 only
  • NetworkAdapter: set the network adapter as the primary boot device on a Generation 2 machine, attach a legacy network adapter and set it as the primary boot device on a Generation 1 machine
  • VHD: if you created a VHDX with New-VM, then this will set that disk as the primary boot device. Works for both Generation types

The BootDevice parameter does have come with a quirk: if you create a VHD and set the VM to boot from CD using New-VM, it will fail to create the VM. It tries to attach both the new VHD and the new virtual CD/DVD drive to the same location. The entire process fails. You will need to create the VHD with the VM, then attach a virtual CD/DVD drive and modify the boot order or vice versa.

Make Quick Changes to a New VM with PowerShell

You have your new VM, but you’d like to make some quick, basic changes. Set-VM includes all the common settings as well as a few rare options.

Adjust Processor and Memory Assignments

From New-VM, the virtual machine starts off with one virtual CPU and does not use static memory. My preferred new virtual machine, in two lines:

New-VM -Name 'demovm' -Generation 2 -SwitchName 'vSwitch' -NewVHDPath 'demovm.vhdx' -NewVHDSizeBytes 60gb
Set-VM -VMName 'demovm' -ProcessorCount 2 -MemoryStartupBytes 2gb -DynamicMemory -MemoryMinimumBytes 512mb -MemoryMaximumBytes 4gb

Both New-VM and Set-VM include the MemoryStartupBytes parameter. I used it with Set-VM to make the grouping logical.

Some operating systems do not work with Dynamic Memory, some applications do not work with Dynamic Memory, and some vendors (and even some administrators) just aren’t ready for virtualization. In any of those cases, you can do something like this instead:

New-VM -Name 'vm2003era' -Generation 1 -SwitchName 'vSwitch' -NewVHDPath 'vm2003era.vhdx' -NewVHDSizeBytes 60gb
Set-VM -VMName 'vm2003era' -ProcessorCount 2 -MemoryStartupBytes 4gb -StaticMemory

Technically, you can leave off the StaticMemory parameter in the preceding sequence. New-VM always creates a VM with static memory. Use it when you do not know the state of the VM.

Control Automatic Start and Stop Actions

When a Hyper-V host starts or shuts down, it needs to do something with its VMs. If it belongs to a cluster, it has an easy choice for highly-available VMs: move them. For non-HA VMs, it needs some direction. By default, new VMs will stay off when the host starts and save when the host shuts down. You can override these behaviors:

Set-VM -VMName 'demovm' -AutomaticStartAction Start -AutomaticStartDelay 20 -AutomaticStopAction ShutDown

You can use these parameters with any other parameter on Set-VM, and you do not need to include all three of them. If you use the Nothing setting for AutomaticStartAction or if you do not specify a value for AutomaticStartDelay, then it uses a value of 0. AutomaticStartDelay uses a time value of seconds.

AutomaticStartAction has these options (use [Tab] to cycle through):

  • Nothing: stay off
  • Start: always start with the host, after AutomaticStartDelay seconds
  • StartIfRunning: start the VM with the host after AutomaticStartDelay seconds, but only if it was running when the host shut down

Note: I am aware of what appears to be a bug in 2019 in which the VM might not start automatically.

AutomaticStopAction has these options (use [Tab] to cycle through):

  • Save: place the VM into a saved state
  • ShutDown: via the Hyper-V integration services/components, instruct the guest OS to shut down. If it does not respond or complete within the timeout period, force the VM off.
  • TurnOff: Hyper-V halts the virtual machine immediately (essentially like pulling the power on a physical system)

If you do not know what to do, take the safe route of Save. Hyper-V will wait for saves to complete.

Determine Checkpoint Behavior

By default, Windows 10 will take a checkpoint every time you turn on a virtual machine. That essentially gives you an Oops! button. Windows Server has that option, but leaves it off by default. Both Windows and Windows Server use the so-called “Production” checkpoint and fall back to “Standard” checkpoints. You can override all this behavior.

Applicable parameters:

  • CheckpointType: indicate which type of checkpoints to create. Use [Tab] to cycle through the possible values:
    • Disabled: the VM cannot have checkpoints. Does not impact backup’s use of checkpoints.
    • Production: uses VSS in the guest to signal VSS-aware applications to flush to disk, then takes a checkpoint of only the VM’s configuration and disks. Active threads and memory contents are not protected. If VSS in the guest does not respond, falls back to a “Standard” checkpoint.
    • ProductionOnly: same as Production, but fails the checkpoint operation instead of falling back to “Standard”
    • Standard: checkpoints the entire VM, including active threads and memory. Unlike a Production checkpoint, applications inside a VM have no way to know that a checkpoint operation took place.
  • SnaphotFileLocation: specifies the location for the configuration files of a virtual machine’s future checkpoints. Does not impact existing checkpoints. Does not affect virtual hard disk files (AVHD/X files are always creating alongside the parent).
  • AutomaticCheckpointsEnabled: Controls whether or not Hyper-V makes a checkpoint at each VM start. $true to enable, $false to disable.

Example:

Set-VM -VMName 'demovm' -AutomaticCheckpointsEnabled $false -CheckpointType Standard

Honestly, I dislike the names “Production” and “Standard”. I outright object to the way that Hyper-V Manager and Failover Cluster Manager use the term “application-consistent” to describe them. You can read my article about the two types to help you decide what to do.

Control the Automatic Response to Disconnected Storage

In earlier versions of Hyper-V, losing connection to storage meant disaster for the VMs. Hyper-V would wait out the host’s timeout value (sometimes), then kill the VMs. Now, it can pause the virtual machine’s CPU, memory and I/O, then wait a while for storage to reconnect.

Set-VM -VMName 'demovm' -AutomaticCriticalErrorAction Pause -AutomaticCriticalErrorActionTimeout 120

The value of AutomaticCriticalErrorActionTimeout is expressed in minutes. By default, Hyper-V will wait 30 minutes.

Alternatively, you can set AutomaticCriticalErrorAction to None and Hyper-V will kill the VM immediately, as it did in previous versions.

Attach Human-Readable Notes to a Virtual Machine

You can create notes for a virtual machine right on its properties.

Set-VM -VMName 'demovm' -Notes 'Showing off the notes feature'

Jeff Hicks gave this feature a full treatment and extended it.

Advanced VM Creation with PowerShell

To control all of the features of your new VM, you will need to use additional cmdlets. All of the cmdlets demonstrated in this section will follow a VM created with:

New-VM -Name 'demovm' -Generation 2 -SwitchName 'vSwitch' -NewVHDPath 'demovm.vhdx' -NewVHDSizeBytes 60gb

Starting from that base allows me to get where I want with the least level of typing and effort.

Prepare the VM to Use Discrete Device Assignment

Hyper-V has some advanced capabilities to pass through host hardware using Discrete Device Assignment (DDA). Set-VM has three parameters that impact DDA:

  • LowMemoryMappedIoSpace
  • HighMemoryMappiedIoSpace
  • GuestControlledCacheTypes

These have little purpose outside of DDA. Didier Van Hoye wrote a great article on DDA that includes practical uses for these parameters.

Specify Processor Settings on a New VM

All of the ways to create a VM result in a single vCPU with default settings. You can make some changes in the GUI, but only PowerShell reaches everything. Use the Set-VMProcessor cmdlet.

Changing a VM’s Virtual CPU Count in Hyper-V

I always use at least 2 vCPU because it allows me to leverage SMT and Windows versions past XP/2003 just seem to respond better. I do not use more than two without a demonstrated need or when I have an under-subscribed host. We have an article that dives much deeper into virtual CPUs on Hyper-V.

Give our new VM a second vCPU:

Set-VMProcessor -VMName 'demovm' -Count 2

You cannot change the virtual processor count on a running, saved, or paused VM.

Note: You can also change the vCPU count with Set-VM, shown earlier.

Set Hard Boundaries on a VM’s Access to CPU Resources

To set hard boundaries on the minimum and maximum percentage of host CPU resources the virtual machine can access, use the Reserve and Maximum parameters, respectively. These specify the total percentage of host processor resources to be used which depends on the number of vCPUs assigned. Calculate actual resource reservations/limits like this:

Parameter Value / Number of Host Logical Processors * Number of Assigned Virtual CPUs = Actual Value

So, a VM with 4 vCPUs set with a Reserve value of 25 on a host with 24 logical processors will lock about 4% of the host’s total CPU resources for itself. A VM with 6 vCPUs and a Limit of 75 on a host with 16 logical processors will use no more than about 28% of total processing power. Refer to the previously-linked article for an explanation of these settings.

To set all of these values:

Set-VMProcessor -VMName 'demovm' -Count 2 -Reserve 10 -Maximum 80

You do not need to specify any of these values. New-VM and all the GUI tools create a VM with a value of 1 for Count, a value of 0 for Reserve and a value of 100 for Maximum. If you do not specify one of these parameters for Set-VMProcessor, it leaves the value alone. So, you can set the processor Count in one iteration, then modify the Reserve at a later time without disturbing the Count, and then the Maximum at some other point without impacting either of the previous settings.

You can change these settings on a VM while it is off, on, or paused, but not while saved.

Prioritize a VM’s Access to CPU Resources

Instead of hard limits, you can prioritize a VM’s CPU access with Set-VMProcessor’s RelativeWeight parameter. As indicated, the setting is relative. Every VM has this setting. If every VM has the same priority value, then no one has priority. VM’s begin life with a default processor weight of 100. The host’s scheduler gives preference to VMs with a higher processor weight.

To set the VM’s vCPU count and relative processor weight:

Set-VMProcessor -VMName 'demovm' -Count 2 -RelativeWeight 150

You do not need to specify both values together; I only included the Count to show you how to modify both at once on a new VM. You can also include the Reserve and Maximum settings if you like.

Enable Auto-Throttle on a VM’s CPU Access

Tinkering with limits and reservations and weights can consume a great deal of administrative effort, especially when you only want to ensure that no VM runs off with your CPU and drags the whole thing down for everyone. Your first, best control on that is the number of vCPU assigned to a VM. But, when you start to work with high densities, that approach does not solve much. So, Microsoft designed Host Resource Protection. This feature does not look at raw CPU utilization so much as it monitors certain activities. If it deems them excessive, it enacts throttling. You get this feature with a single switch:

Set-VMProcessor -VMName 'demovm' -Count 2 -EnableHostResourceProtection $true

Microsoft does not fully document what this controls. You will need to test it in your environment to determine its benefits.

You can use the EnableHostResourceProtection parameter by itself or with any of the others.

Set VM Processor Compatibility

Hyper-V uses a CPU model that very nearly exposes the logical processor to VMs as-is. That means that a VM can access all of the advanced instruction sets implemented by a processor. However, Microsoft also designed VMs to move between hosts. Not all CPUs use the same instruction set. So, Microsoft implements a setting that hides all instruction sets except those shared by every supported CPU from a manufacturer. If you plan to Live Migrate a VM between hosts with different CPUs from the same manufacturer, use this cmdlet:

Set-VMProcessor -VMName 'demovm' -Count 2 -CompatibilityForMigrationEnabled $true

Employ a related parameter if you need to run unsupported versions of Windows (like NT 4.0 or 2000):

Set-VMProcessor -VMName 'demovm' -CompatibilityForOlderOperatingSystemsEnabled $true

This one time, I did not override Count. Older operating systems did not have the best support for multi-processing, and a lot of applications from that era perform worse with multiple processors.

You can specify $false to disable these features. You can only change them while the VM is turned off. As with the preceding demonstrations, you can use these parameters in any combination with the others, or by themselves.

Change a VM’s NUMA Processor Settings

I have not written much about NUMA. Even the poorest NUMA configuration would not hurt more than a few Hyper-V administrators. If you don’t know what NUMA is, don’t worry about it. I am writing these instructions for people that know what NUMA is, need it, and just want to know how to use PowerShell to configure it for a Hyper-V VM.

Set-VMProcessor provides two of the three available NUMA settings. We will revisit the other one in the Set-VMMemory section below. Use Set-VMProcessor to specify the maximum number of virtual CPUs per NUMA node or the maximum number of virtual NUMA nodes this VM sees per socket.

Set-VMProcessor -VMName 'demovm' -Count 16 -MaximumCountPerNumaNode 2 -MaximumCountPerNumaSocket 8

As before, you can use any combination of these parameters with each other and the previously-shown parameters. Unlike before, mistakes here can make things worse without making anything better.

Enable Hyper-V Nested Virtualization

Want to run Hyper-V on Hyper-V? No problem (anymore). Run this after you make your new VM:

Set-VMProcessor -VMName 'demovm' -Count 4 -ExposeVirtualizationExtensions $true

Note: Enabling virtualization extensions silently disables Dynamic Memory. Only Startup memory will apply.

I have not tested this setting with other hypervisors. It does pass the enabled virtualization features of your CPU down to the guest, so it might enable others. I also did not test this parameter with any parameter other than Count.

Change Memory Settings on a New VM

New-VM always leaves you with static memory. If you don’t provide a MemoryStartupBytes value, it will use a default of one gigabyte. The GUI wizards can enable Dynamic Memory, but will only set the Startup value. For all other memory settings, you must access the VM’s property sheets or turn to PowerShell. We will make these changes with Set-VMMemory.

Note: You can also change several memory values with Set-VM, shown earlier.

Setting Memory Quantities on a VM

A virtual machine’s memory quantities appear on three parameters:

  • Startup: How much memory the virtual machine will have at boot time. If the VM does not utilize Dynamic Memory, this value persists throughout the VM’s runtime
  • MinimumBytes: The minimum amount of memory that Dynamic Memory can assign to the virtual machine
  • MaximumBytes: The maximum amount of memory that Dynamic Memory can assign to the virtual machine

These values exist on all VMs. Hyper-V only references the latter two if you configure the VM to use Dynamic Memory.

Set-VMMemory -VMName 'demovm' -StartupBytes 2gb

This cmdlet sets the VM to use two gigabytes of memory at the start. It does not impact Dynamic Memory in any way; it leaves all of those settings alone. You can change this value at any time, although some guest operating systems will not reflect the change.

We will include the other two settings in the upcoming Dynamic Memory sections.

Enable Dynamic Memory on a VM

Control whether or not a VM uses Dynamic Memory with the DynamicMemoryEnabled parameter.

Set-VMMemory -VMName 'demovm' -DynamicMemoryEnabled $true

You can disable it with $false. The above usage does not modify any of the memory quantities. A new VM defaults to 512MB minimum and 1TB maximum.

You can only make this change while the VM is off.

You can also control the Buffer percentage that Dynamic Memory uses for this VM. The “buffer” refers to a behind-the-scenes memory reservation for memory expansion. Hyper-V sets aside a percentage of the amount of memory currently assigned to the VM for possible expansion. You control that percentage with this parameter.

Set-VMMemory -VMName 'demovm' -DynamicMemoryEnabled $true -Buffer 10

So, if Hyper-V assigns 2108 megabytes to this VM, it will also have up to 210.8 megabytes of buffered memory. Buffer only sets a maximum; Hyper-V will use less in demanding conditions or if the set size would exceed the maximum assigned value. Hyper-V ignores the Buffer setting when you disable Dynamic Memory on a VM. You can change the buffer size on a running VM.

Dynamic Memory Setting Demonstrations

Let’s combine the above settings into a few demonstrations.

Set-VMMemory -VMName 'demovm' -StartupBytes 2gb -DynamicMemoryEnabled $false
Set-VMMemory -VMName 'demovm' -StartupBytes 2gb -DynamicMemoryEnabled $true -MinimumBytes 512mb -MaximumBytes 4gb
Set-VMMemory -VMName 'demovm' -StartupBytes 2gb -DynamicMemoryEnabled $true -MinimumBytes 512mb -MaximumBytes 24gb -Buffer 5

Control a VM’s Memory Allocation Priority

If VMs have more total assigned memory than the Hyper-V host can accommodate, it will boot them by Priority order (higher first). Also, if Dynamic Memory has to choose between VMs, it will work from Priority.

Set-VMMemory -VMName 'demovm' -StartupBytes 2gb -DynamicMemoryEnabled $true -MinimumBytes 512mb -MaximumBytes 24gb -Buffer 5 -Priority 75

Valid values range from 0 to 100. New VMs default to 50. You can use Priority with any other valid combination of Set-VMMemory. You can change Priority at any time.

Note: The GUI tools call this property Memory weight and show its value as a 9-point slider from Low (0) to High (100).

Change a VM’s NUMA Memory Settings

We covered the processor-related NUMA settings above. Use Set-VMMemory to control the amount of memory in the virtual NUMA nodes presented to this VM:

Set-VMMemory -VMName 'demovm' -MaximumAmountPerNumaNodeBytes 1gb

As with the processor NUMA settings, I only included this to show you how. If you do not understand NUMA and know exactly why you would make this change, do not touch it.

Attach Virtual Disks and CD/DVD Drives to a Virtual Machine

You could use these cmdlets instead of the features of New-VM to attach drives. You can also use them to augment New-VM. Due to some complexities, I prefer the latter.

A Note on Virtual Machine Drive Controllers

On a physical computer, you have to use the physical drive controllers as you find them. If you run out of disk locations, you have to add physical controllers. With Hyper-V, you do not directly manage the controllers. Simply instruct the related cmdlets to attach the drive to a specific controller number and location. As long as the VM does not already have a drive in that location, it will use it.

On a Generation 1 virtual machine, you have two emulated Enhanced Integrated Drive Electronics (EIDE, or just IDE) controllers, numbered 0 and 1. Each has location 0 and location 1 available. That allows a total of four available IDE slots. When you set a Generation 1 VM to boot to IDE or VHD, it will always start with IDE controller 0, position 0. If you set it to boot to CD, it will walk down through 0:0, 0:1, 1:0, and 1:1 to find the first CD drive.

Both Generation types allow up to four synthetic SCSI controllers, numbered 0 through 4. Each controller can have up to 64 locations, numbered 0 through 63.

Unlike a physical system, you will not gain benefits from balancing drives across controllers.

Create a Virtual Hard Disk File to Attach

You can’t attach a disk file that you don’t have. You must know what you will call it, where you want to put it, and how big to make it.

New-VHD -Path '\svstore01vmsVirtual Hard Disksdemodisk.vhdx' -SizeBytes 20gb

By default, New-VHD creates a dynamically-expanding hard disk. For the handful of cases where fixed makes more sense, override with the Fixed parameter:

New-VHD -Path '\svstore01vmsVirtual Hard Disksdemodisk-fixed.vhdx' -SizeBytes 5gb -Fixed

By default a dynamically-expanding VHDX uses a 32 megabyte block size. For some file systems, like ext4, that can cause major expansion percentages over very tiny amounts of utilized space. Override the block size to a value as low as 1 megabyte:

New-VHD -Path '\svstore01vmsVirtual Hard Disksdemodisk.vhdx' -SizeBytes 20gb -BlockSizeBytes 1mb

You can also use LogicalSectorSizeBytes and PhysicalSectorSizeBytes to override defaults. Hyper-V will detect the underlying physical storage characteristics and choose accordingly, so do not override these values unless you intend to migrate the disk to a system with different values:

New-VHD -Path '\svstore01vmsVirtual Hard Disksdemodisk.vhdx' -SizeBytes 20gb -LogicalSectorSizeBytes 4096 -PhysicalSectorSizeBytes 4096

Create a Virtual Hard Disk from a Physical Disk

You can instruct Hyper-V to encapsulate the entirety of a physical disk inside a VHDX. First, use Get-Disk to find the disk number. Then use New-VHD to transfer its contents into a VHD:

New-VHD -SourceDisk 4 -Path '\svstore01vmsVirtual Hard Disksfromdisk4.vhdx'

You can combine this usage with Fixed or BlockSizeBytes (not both). The new VHDX will have a maximum size that matches the source disk.

Create a Child Virtual Hard Disk

In some cases, you might wish to use a differencing disk with a VM, perhaps as its primary disk. This usage allows the VM to operate normally, but prevent it from making changes to the base VHDX file.

New-VHD -Path '\svstore01vmsVirtual Hard Diskschild.vhdx' -ParentPath '\svstore01vmsVirtual Hard Disksbasevhdx.vhdx'

You can also specify the Differencing parameter, if you like.

Note: Any change to the base virtual hard disk invalidates all of its children.

Check a VM for Available Virtual Hard Disk and CD/DVD Locations

You do not need to decide in advance where to connect a disk. However, you sometimes want to have precise control. Before using any of the attach cmdlets, consider verifying that it has not already filled the intended location. Get-VMHardDiskDrive and Get-VMDvdDrive will show current attachments.

Attach a Virtual Hard Disk File to a Virtual Machine

You can add a disk very easily with Add-VMHardDiskDrive:

Add-VMHardDiskDrive -VMName 'demovm' -Path '\svstore01VMsVirtual Hard Diskssecondvhdx.vhdx'

Hyper-V will attach it to the next available location.

You can override to a particular location:

Add-VMHardDiskDrive -VMName 'demovm' -Path '\svstore01VMsVirtual Hard Disksbasevhdx.vhdx' -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 8

Technically, you can skip the ControllerType parameter; Generation 1 assumes IDE and Generation 2 has no other option.

If you want to attach a disk to another SCSI controller, but it does not have another, then add it first:

Add-VMScsiController -VMName 'demovm'
Add-VMHardDiskDrive -VMName 'demovm' -Path '\svstore01VMsVirtual Hard Disksbasevhdx.vhdx' -ControllerNumber 1

Notice that I did not specify a location on Add-VMHardDiskDrive. If you specify a controller but no location, it just uses the next available.

Attach a Virtual DVD Drive to a Virtual Machine

Take special note: this cmdlet applies to a virtual drive, not a virtual disk. Basically, it creates a place to put a CD/DVD image, but does not necessarily involve a disk image. It can do both, as you’ll see.

Add-VMDvdDrive -VMName 'demovm'

Add-VMDvdDrive uses all the same parameters as Add-VMHardDiskDrive above. If you do not specify the Path parameter, then the drive remains empty. If you do specify Path, it mounts the image immediately:

Add-VMDvdDrive -VMName 'demovm' -Path \svstore01isosen_windows_server_2019_x64_dvd_4cb967d8.iso

All the notes from the beginning about permissions and delegation apply here.

If you have a DVD drive already and just want to change its contents, use Set-VMDvdDrive:

Set-VMDvdDrive -VMName 'demovm' -Path \svstore01isossoftware_installer.iso

If you have more than one CD/DVD attached, you can use the ControllerTypeControllerNumber, and ControllerLocation parameters to specify.

If you want to empty the drive:

Set-VMDvdDrive -VMName 'demovm' -Path ''

Remove-VMDvdDrive completely removes the drive from the system.

Work with a New Virtual Machine’s Network Adapters

Every usage of New-VM should result in a virtual machine with at least one virtual network adapter. By default, it will not attach it to a virtual switch. You might need to modify a VLAN. If desired, you can change the name of the adapter. You can also add more adapters, if you want.

Attach the Virtual Adapter to a Virtual Switch

You can connect every adapter on a VM to the same switch:

Connect-VMNetworkAdapter -VMName 'demovm' -SwitchName vSwitch

If you want to specify the adapter, you have to work harder. I wrote up a more thorough guide on networking that includes that, and other advanced topics.

Connect the Virtual Adapter to a VLAN

All of the default vNIC creation processes leave the adapter as untagged. To specify a VLAN, use Set-VMNetworkAdapterVLAN:

Set-VMNetworkAdapterVlan -VMName 'demovm' -Access -VlanId 42

If you need help selecting a vNIC for this operation, use my complete guide for details. It does not have a great deal of information on other ways to use this cmdlet, such as for trunks, so refer to the official documentation.

Rename the Virtual Adapter

You could differentiate adapters for the previous cmdlets by giving adapters unique names. Otherwise, Hyper-V calls them all “Network Adapter”.

Rename-VMNetworkAdapter -VMName 'demovm' -NewName 'Adapter 1'

Like the preceding cmdlets, this usage will rename all vNICs on the VM. Use caution. But, if you do this on a system with only one adapter, then add another, you can filter against adapters not name “Adapter 1”, then later use the VMNetworkAdapterName parameter.

Add Another Virtual Adapter

You can use Add-VMNetworkAdapter to add further adapters to the VM:

Add-VMNetworkAdapter -VMName 'demovm'

Even better, you can name it right away:

Add-VMNetworkAdapter -VMName 'demovm' -Name 'Adapter 2'

Don’t forget to connect your new adapter to a virtual switch (you can still use Connect-VMNetworkAdapter, of course):

Add-VMNetworkAdapter -VMName 'demovm' -Name 'Adapter 2' -SwitchName 'vSwitch'

Add-VMNetworkAdapter has several additional parameters for adapter creation. Set-VMNetworkAdapter has a superset, show I will show them in its context. However, you might find it convenient to use StaticMacAddress when creating the adapter.

Set the MAC Address of a Virtual Adapter

You can set the MAC address to whatever you like:

Set-VMNetworkAdapter -VMName 'demovm' -StaticMacAddress 00155da182d2

If you need to override the MAC for spoofing (as in, for a software load-balancer):

Set-VMNetworkAdapter -VMName 'demovm' -StaticMacAddress 00155da182d2 -MacAddressSpoofing On

Other Virtual Network Adapter Settings

Virtual network adapters have a dizzying array of options. Check the official documentation or Get-Help Set-VMNetworkAdapter to learn about them.

Work with a New Virtual Machine’s Integration Services Settings

None of the VM creation techniques allow you to make changes to the Hyper-V integration services. Few VMs ever need such a change, so including them would amount to a nuisance for most of us. We do sometimes need to change these items, perhaps to disable time synchronization for virtualized domain controllers or to block attempts to signal VSS in Linux guests.

We do not use “Set” cmdlets to control integration services. We have Get-, Enable-, and Disable- for integration services. Every new VM enables all services except “Guest Services”. Ideally, the cmdlets would all have pre-set options for the integration services. Unfortunately, we have to either type them out or pipe them in from Get-VMIntegrationService. You can use it to get a list of the available services. You can then use the selection capabilities of the console to copy and paste the item that you need (draw over the letters to copy, then right-click to paste). You can also use a filter (Where-Object) to pick the one that you need. For now, we will see the simplest choices.

To disable the time synchronization service for a virtual machine:

Disable-VMIntegrationService -VMName demovm -Name 'Time Synchronization'

To enable guest services for a virtual machine:

Enable-VMIntegrationService -VMName demovm -Name 'Guest Service Interface'

Most of the integration service names contain spaces. Don’t forget to use single or double quotes around such a name.

Put it All Together: Use PowerShell to Make the Perfect VM

A very common concern: “How can I remember all of this?” Well, you can’t remember it all. I have used PowerShell to control VMs since the unofficial module for 2008. I don’t remember everything. But, you don’t need to try. In the general sense, you always have Get-Help and Get-Command -Module Hyper-V. But, even better, you probably won’t use the full range of capability. Most of us create VMs with a narrow range of variance. I will give you two general tips for making the VM customization process easier.

Use a Text Tool to Save Creation Components

In introductory, training, and tutorial materials, we often make a strong distinction between interactive PowerShell and scripted PowerShell. If you remember what you want, you can type it right in. If you make enough VMs to justify it, you can have a more thorough script that you guide by parameter. But, you can combine the two for a nice middle ground.

First, pick a tool that you like. Visual Studio Code has a lot of features to support PowerShell. Notepad++ provides a fast and convenient scratch location to copy and paste script pieces.

This tip has one central piece: as you come up with cmdlet configurations that you will, or even might, use again, save them. You don’t have to build everything into a full script. Sometimes, you need a toolbox with a handful of single-purpose snippets. More than once in my career, I’ve come up with a clever solution to solve a problem at hand. Later, I tried to recall it from memory, and couldn’t. Save those little things — you never know when you’ll need them.

Use PowerShell’s Pipeline and Variables with Your Components

In all the cmdlets that I showed you above, I spelled out the virtual machine’s name. You could do a lot of text replacement each time you wanted to use them. But, you have a better way. If you’ve run New-VM lately, you probably noticed that it emitted something to the screen:

customize vms using powershell

Instead of just letting all that go to the screen, you can capture it or pass it along to another cmdlet.

Pipeline Demo

Use the pipe character | to quickly ship output from one cmdlet to another. It works best to make relatively few and simple changes.

New-VM -VMName 'demovm' -Generation 2 -SwitchName 'vSwitch' -NewVHDPath 'demovm-c.vhdx' -NewVHDSizeBytes 60gb | Set-VM -ProcessorCount 2 -DynamicMemory -MemoryStartupBytes 2gb -MemoryMinimumBytes 512mb -MemoryMaximumBytes 4gb -Passthru | Enable-VMIntegrationService -Name 'Guest Service Interface'

The above has three separate commands that all chain from the first. You can copy this into your text manipulation tool and save it. You can then use it as a base pattern. You change the name of the VM and its VHDX in the text tool and then you can create a VM with these settings anytime you like. No need to step through a wizard and then flip a lot of switches after.

Warning: Some people in the PowerShell community develop what I consider an unhealthy obsession with pipelining, or “one liners”. You should know how the pipeline works, especially the movement of objects. But, extensive pipelining becomes impractical quite quickly. Somewhere along the way, it amounts to little more than showing off. Worse, because not every cmdlet outputs the same object, you quickly have to learn a lot of tricks that do nothing except keep the pipeline going. Most egregiously, “one liners” impose severe challenges to legibility and maintainability with no balancing benefit. Use the pipeline to the extent that it makes things easier, but no further.

Variables Demos

You can capture the output of any cmdlet into a variable, then use that output in succeeding lines. It requires more typing than the pipeline, but trades flexibility.

$NewVM = New-VM 'demovm' -Generation 2 -SwitchName 'vSwitch' -NewVHDPath "$VMName-c.vhdx" -NewVHDSizeBytes 60gb
Set-VMProcessor -VM $NewVM -Count 2 -CompatibilityForMigrationEnabled $true
Set-VMMemory -VM $NewVM -StartupBytes 2gb -MinimumBytes 512mb -MaximumBytes 4gb -DynamicMemoryEnabled $true -Buffer 5
Set-VMNetworkAdapterVlan -VM $NewVM -Access -VlanId 20

Each of the cmdlets in the above listing has a PassThru parameter, but, except for New-VM, none emits an object that any of the others can use. This script takes much more typing than the pipeline demo, but it does more and breaks each activity out into a single, easy comprehensible line. As with the pipeline version, you can set up each line to follow the pattern that you use most, then change only the name in the first line to suit each new VM. Notice that it automatically gives the VHDX a name that matches the VM, something that we couldn’t do in the pipeline version.

Combining Pipelines and Variables

You can use variables and pipelines together to maximize their capabilities.

$VMName = 'demovm'
$NewVM = New-VM -VMName $VMName -Generation 2 -SwitchName 'vSwitch' -NewVHDPath "$VMName-c.vhdx" -NewVHDSizeBytes 60gb
$NewVM | Set-VM -ProcessorCount 2 -DynamicMemory -MemoryStartupBytes 2gb -MemoryMinimumBytes 512mb -MemoryMaximumBytes 4gb -Passthru | Enable-VMIntegrationService -Name 'Guest Service Interface'
Set-VMNetworkAdapterVlan -VM $NewVM -Access -VlanId 20

With this one, you can implement your unique pattern but place all the changeable items right in the beginning. This sample only sets the VM’s name. If you want to make other pieces easily changeable, just break them out onto separate lines.

Making Your Own Processes

If you will make a single configuration of VM repeatedly, you should create a saved script or an advanced function in your profile. It should have at least one parameter to specify the individual VM name.

But, even though most people won’t create VMs with a particular wide variance of settings, neither will many people create VMs with an overly tight build. Using a script with lots of parameters presents its own challenges. So, instead of a straight-through script, make a collection of copy/pasteable components.

Use something like the following:

$VMName = 'demovm'

# Gen 1
$NewVM = New-VM -VMName $VMName -SwitchName 'vSwitch' -NewVHDPath 'demovm-c.vhdx' -NewVHDSizeBytes 60gb

# Gen 2
$NewVM = New-VM -VMName $VMName -Generation 2 -SwitchName 'vSwitch' -NewVHDPath 'demovm-c.vhdx' -NewVHDSizeBytes 60gb

# default parts
$NewVM | Set-VM -ProcessorCount 2 -DynamicMemory -MemoryStartupBytes 2gb -MemoryMinimumBytes 512mb -MemoryMaximumBytes 4gb -Passthru | Enable-VMIntegrationService -Name 'Guest Service Interface'
Set-VMNetworkAdapterVlan -VM $NewVM -Access -VlanId 20

# fixed memory
Set-VMMemory -VM $NewVM -DynamicMemoryEnabled $false -StartupBytes 4gb

# vlan
$VLANID = 12
Set-VMNetworkAdapterVlan -VM $NewVM -Access -VlanId $VLANID

# add data disk
$DataDiskSize = 20gb
$DataDisk = New-VHD -Path "\svstore01vmsVirtual Hard Disks$VMName-data.vhdx" -SizeBytes $DataDiskSize
Add-VMHardDiskDrive -VM $NewVM -Path $DataDisk.Path

# setup bootable disk
$BootDiskPath = '\svstore01ISOsWS2016setup.iso'
$BootDiskPath = '\svstore01ISOsWS2019setup.iso'
$BootDiskPath = '\svstore01ISOsUbuntu1904.iso'
$BootDisk = Add-VMDvdDrive -VM $NewVM -Path $BootDiskPath
Set-VMBios -VM $NewVM -StartupOrder CD
Set-VMFirmware -VM $NewVM -FirstBootDevice $BootDrive

Trying to run all of that as-is would cause some problems. Instead, copy out the chunks that you need and paste them as necessary. Add in whatever parts suit your needs.

Be sure to let us know how you super-charged your VM creation routines!

The post How to Customize Hyper-V VMs using PowerShell appeared first on Altaro DOJO | Hyper-V.

]]>
https://www.altaro.com/hyper-v/customize-vm-powershell/feed/ 0
How to Select a Placement Policy for Site-Aware Clusters https://www.altaro.com/hyper-v/placement-policy-site-aware-cluster/ https://www.altaro.com/hyper-v/placement-policy-site-aware-cluster/#respond Fri, 25 Oct 2019 12:50:23 +0000 https://www.altaro.com/hyper-v/?p=18069 Learn the automatic placements policies and advanced settings used to maximize the availability of your virtual machines (VMs) with site-aware clusters

The post How to Select a Placement Policy for Site-Aware Clusters appeared first on Altaro DOJO | Hyper-V.

]]>

One of the more popular failover clustering enhancements in Windows Server 2016 and 2019 is the ability to define the different fault domains in your infrastructure. A fault domain lets you scope a single point of failure in hardware, whether this is a Hyper-V host (a cluster node), its enclosure (chassis), its server rack or an entire datacenter. To configure these fault domains, check out the Altaro blog post on configuring site-aware clusters and fault domains in Windows Server 2016 & 2019. After you have defined the hierarchy between your nodes, chassis, racks, and sites then the cluster’s placement policies, failover behavior, and health checks will be optimized. This blog will explain the automatic placements policies and advanced settings you can use to maximize the availability of your virtual machines (VMs) with site-aware clusters.

Site-Aware Placement Based on Storage Affinity

From reading the earlier Altaro blog about fault-tolerance, you may recall that the resiliency is created by distributing identical (mirrored) storage spaces direct (S2D) disks across the different fault domains.  Each node, chassis, rack or site may contain a copy of a VM’s virtual hard disks. However, you always want the VM to be in the same site as its disk for performance reasons to avoid having the I/O transmitted across distance. In the event that a VM is forced to start in a separate site from its disk, then it will automatically live migrate the VM to the same site as its disk after about a minute.  With site-awareness, the automatic enforcement of storage affinity between a VM and its disk is given the highest site placement priority.

Configuring Preferred Sites with Site-Aware Clusters

If you have configured multiple sites in your infrastructure, then you should consider which site is your “primary” site and which should be used as a backup. Many organizations will designate their primary site as the location closest to their customers or with the best hardware, and the secondary site as the failover location which may have limited hardware to only support critical workloads.  Some enterprises may deploy identical datacenters, and distribute specific workloads to each location to balance their resources. If you are splitting your workloads across different sites you can assign each clustered workload or VM (cluster group) a preferred site. Let’s say that you want your US-East VM to run in your primary datacenter and your US-West VM to run in your secondary datacenter, you could configure the following settings via PowerShell:

PS C:> (Get-Cluster -Name “US-East”).PreferredSite=“Primary”
PS C:> (Get-Cluster -Name “US-West”).PreferredSite=“Secondary”

Designating a preferred site for the entire cluster will ensure that after a failure that the VMs will start in this location. After you defined your sites by creating a New-ClusterFaultDomain you can use the cluster-wide property PreferredSite to set the default location to launch VMs. Below is the PowerShell cmdlet:

PS C:> (Get-Cluster).PreferredSite=“Primary”

Be aware of your capacity if you are usually distributing your workloads across two sites and they are forced to run in a single location as performance will diminish with less hardware. Consider using the VM prioritization feature and disabling automatic VM restarts after a failure, as this will ensure that only the most important VMs will run. You can find more information from this Altaro blog on how to configure start order priority for clustered VMs.

To summarize, placement priority is based on:

  • Storage affinity
  • Preferred site for a cluster group or VM
  • Preferred site for the entire cluster

Site-Aware Placement Based on Failover Affinity

When site-awareness has been configured for a cluster, there are several automatic failover policies that are enforced behind the scenes. First, a clustered VM or group will always failover to a node, chassis or rack within the same site before it moves to a different site. This is because local failover is always faster than cross-site failover since it can bring the VM online faster by accessing the local disk and avoid any network latency between sites. Similarly, site-awareness is also honored by the cluster when a node is drained for maintenance. The VMs will automatically move to a local node, rather than a cross-site node.

Cluster Shared Volumes (CSV) disks are also site-aware. A single CSV disk can store multiple Hyper-V virtual hard disks while allowing their VMs to run simultaneously on different nodes.  However, it is important that these VMs are all running on nodes within the same site. This is because the CSV service coordinates disk write access across multiple nodes to a single disk. In the case of Storage Spaces Direct (S2D), the disks are mirrored so there are identical copies running in different locations (or sites). If VMs were writing to mirrored CSV disks in different locations and replicating their data without any coordination, it could lead to disk corruption. Microsoft ensures that this problem never occurs by enforcing all VMs which share a CSV disk to run on the local site and write to a single instance of that disk. Furthermore, CSV distributes the VMs across different nodes within the same site, balancing the workloads and write requests to that coordinate node.

Site-Aware Health Checks and Cluster Heartbeats

Advanced cluster administrators may be familiar with cluster heartbeats, which are health checks between cluster nodes. This is the primary way in which cluster nodes validate that their peers are healthy and functioning. The nodes will ping each other once per predefined interval, and if a node does not respond after several attempts it will be considered offline, failed or partitioned from the rest of the cluster. When this happens, the host is not considered an active node in the cluster and it does not provide a vote towards cluster quorum (membership).

If you have configured multiple sites in different physical locations, then you should configure the frequency of these pings (CrossSiteDelay) and the number of health check which can be missed (CrossSiteThreshold) before a node is considered failed. The greater the distance between sites, the more network latency will exist, so these values should be tweaked to minimize the chances of a false failover during times when there is high network traffic. By default, the pings are sent every 1 second (1000 milliseconds) and when 20 are missed, a node is considered unavailable and any workloads it was hosting will be redistributed. You should test your network latency and cross-site resiliency regularly to determine whether you should increase or reduce these default values. Below is an example to change the testing frequency from every 1 second to 5 seconds and the number of missed responses from 20 to 30.

PS C:> (Get-Cluster).CrossSiteDelay=5000
PS C:> (Get-Cluster).CrossSiteThreshold=30

By increasing these values, it will now take longer for a failure to be confirmed and failover to happen resulting in greater downtime. The default time is 1-second x 20 misses = 20 seconds, and this example extends it to 5 seconds x 30 misses = 150 seconds.

Site-Aware Quorum Considerations

Cluster quorum is an algorithm that clusters use to determine whether there are enough active nodes in the cluster to run its core operations. For additional information, check out this series of blogs from Altaro about multi-site cluster quorum configuration.  In a multi-site cluster, quorum becomes complicated since there could be a different number of nodes in each site. With site-aware clusters, “dynamic quorum” will be used to automatically rebalance the number of nodes which have votes. This means that as clusters nodes drop out of membership, the number of voting nodes changes. If there are two sites with an equal number of voting nodes, then the group of nodes that are assigned to be the preferred site will stay online and run the workloads, while the lower priority site will reduce their votes and not host any VMs.

Windows Server 2012 R2 introduced a setting known as the LowerQuorumPriorityNodeID, which allowed you to set a node in a site as the least important, but this was deprecated in Windows Server 2016 and should no longer be used. The idea behind this was to easily declare which location was the least important when there were two sites with the same number of voting nodes. The site with the lower priority node would stay offline while the other partition would run the clustered workloads. That caused some confusion since the setting was only applied to a single host, but you may still see this setting referenced in blogs such as Altaro’s https://www.altaro.com/hyper-v/quorum-microsoft-failover-clusters/.

The site-awareness features added to the latest version of Window Server will greatly enhance a cluster’s resilience through a combination of user-defined policies and automatic actions. By creating the fault domains for clusters, it is easy to provide even greater VM availability by moving the workloads between nodes, chassis, racks, and sites as efficiently as possible. Failover clustering further reduces the configuration overhead by automatically applying best practices to make failover faster and keep your workloads online for longer.

Wrap-Up

Useful information yes? How many of you are using multi-site clusters in your organizations? Are you finding it easy to configure and manage? Having issues? If so, let us know in the comments section below! We’re always looking to see what challenges and successes people in the industry are running into!

Thanks for reading!

The post How to Select a Placement Policy for Site-Aware Clusters appeared first on Altaro DOJO | Hyper-V.

]]>
https://www.altaro.com/hyper-v/placement-policy-site-aware-cluster/feed/ 0