Automating Nerdio Manager for MSP with the NMM-PSModule
Nerdio Manager for MSP (NMM) already provides a very complete portal experience for managing Azure Virtual Desktop, Windows 365, scripted actions, users, host pools, backups, cost management, and many other operational areas. For day-to-day work, the portal is often the right place to be. But as soon as the same action needs to be repeated across multiple customers, tenants, accounts, or host pools, PowerShell becomes the better place to standardize the workflow.
The existing NMM-PS PowerShell module is a great community-maintained module created by Nerdio Sales Engineers, but doesn’t contain the full list of operations made possible by the NMM API. This is where the NMM-PSModule becomes useful.
The module is a PowerShell client generated from the Nerdio Manager for MSP OpenAPI specification. Instead of writing raw REST calls every time, the module exposes the NMM API as discoverable PowerShell functions. Authentication, base URL handling, request construction, body serialization, path escaping, and client-side filtering are handled by the module, allowing the automation script itself to focus on the operational task.
In this post, we will walk through the differences with NMM-PS, module structure, authentication options, command discovery, filtering, and a few practical automation patterns that make the module interesting for MSP environments.
Important: Be aware that the NMM-PSModule is currently a 0.1.0 (alpha) release and has the status “work in progress”. Please keep that in mind when testing this.
NMM-PS vs NMM-PSModule
The NMM-PS PowerShell contains about 67 commands and has some cleaner names compaired to NMM-PSModule. Why? NMM-PSModule uses the literal API operation names for all the commands. The API operations are mapped to familiar PowerShell verbs:
- GET operations become Get-NMM... commands.
- POST operations become New-NMM... commands.
- PUT operations become Set-NMM... commands.
- PATCH operations become Update-NMM... commands.
- DELETE operations become Remove-NMM... commands.
NMM-PSModule currently has 353 commands coming from the OpenAPI/Swagger spec ( swagger.json ) and is more comprehensive. Updating swagger.json allows the NMM-PSModule to be updated quickly with the latest changes and additions to the NMM API.
So does the NMM-PSModule replace NMM-PS? That’s up to you to decide and probably depends on your preference. But to make it a little bit easier, let me give my honest view on both. This hopefully makes it a little bit easier to decide which one fits best for you.
| NMM-PS | NMM-PSModule |
|---|---|
| A more curated, operator-friendly module. It has fewer commands, about 67 public .ps1 functions in the repo, but the names are cleaner. | A broader API-coverage module. It is generated from the NMM OpenAPI/Swagger spec and exposes about 353 public commands generated from the Nerdio Manager for MSP OpenAPI specification and currently 0.1.0 alpha / work in progress. |
| Easier to read, easier to teach, easier for day-to-day scripts. It also has PowerShell Gallery installation, certificate authentication, reporting helpers, tests, documentation, and an experimental hidden API/browser-extension path. | Generated command names are often long because of the literal translation from the API. |
| Smaller API surface. If Nerdio exposes something in the REST API that has not been hand-wrapped yet, you may need raw requests or custom work. | Much wider REST API coverage and traceability to the Swagger/OpenAPI contract. Better if your goal is automation coverage across many endpoints, including newer or less commonly wrapped areas. |
| Easy to update, based on the provided swagger.json content. |
Important: Both NMM-PS and NMM-PSModule are not officially supported by Nerdio and should be used at your own risk.
Where to get NMM-PSModule
The NMM-PSModule is hosted as a public repository on GitHub, which can be found here:
https://github.com/ivandemes/NMM-PSModule
Getting Started
Before using the module, the NMM REST API needs to be enabled and configured in Nerdio Manager for MSP. In the NMM portal, this is done from the REST API integration area where the required application registration, API scope, and permissions are configured. Once the API is available, the module can authenticate by using either an existing bearer token or OAuth 2.0 client credentials.
After the module is available locally, import it into a PowerShell 7 session:
|
1 2 3 |
Import-Module '.\NMM-PSModule\NMM-PSModule.psd1' |
For production use, the module would typically be installed into a module path or packaged through an internal automation repository. For testing and development, importing it directly from the repository is usually enough.
Authenticating to NMM
The most important command to start with is Connect-NMMApi . This command creates an authenticated connection object containing the NMM base URI and access token. By default, it stores the connection as the current module connection, so later commands can use it automatically. If you prefer to pass connection objects explicitly, the -NoDefault switch can be used.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
NAME Connect-NMMApi SYNOPSIS Creates and optionally saves an authenticated NMM API connection. SYNTAX Connect-NMMApi -BaseUri <Uri> -AccessToken <String> [-NoDefault] [<CommonParameters>] Connect-NMMApi -BaseUri <Uri> -ClientId <String> -ClientSecret <Object> [-TenantId <String>] [-OAuthTokenUri <Uri>] [-TokenPath <String>] [-Scope <String>] [-NoDefault] [<CommonParameters>] DESCRIPTION Uses an existing bearer token or obtains one with the OAuth 2.0 client credentials flow. PARAMETERS -BaseUri <Uri> The absolute URI of the NMM instance, for example https://example.getnerdio.com. Required? true Position? named Default value Accept pipeline input? false Aliases Accept wildcard characters? false -AccessToken <String> An existing OAuth bearer token. Required? true Position? named Default value Accept pipeline input? false Aliases Accept wildcard characters? false -ClientId <String> The OAuth client identifier. Required? true Position? named Default value Accept pipeline input? false Aliases Accept wildcard characters? false -ClientSecret <Object> The OAuth client secret. Accepts a plain string for automation compatibility or a SecureString to reduce accidental exposure. Required? true Position? named Default value Accept pipeline input? false Aliases Accept wildcard characters? false -TenantId <String> The Microsoft Entra tenant ID shown in the NMM REST API credentials. When supplied, tokens are requested directly from Microsoft Entra ID. Required? false Position? named Default value Accept pipeline input? false Aliases Accept wildcard characters? false -OAuthTokenUri <Uri> An explicit OAuth token endpoint. This takes precedence over TenantId and TokenPath. Required? false Position? named Default value Accept pipeline input? false Aliases Accept wildcard characters? false -TokenPath <String> The legacy token endpoint path relative to BaseUri. For unattended authentication, use TenantId or OAuthTokenUri. Required? false Position? named Default value /api/v1/msp/rest-api/token Accept pipeline input? false Aliases Accept wildcard characters? false -Scope <String> An optional OAuth scope. Required? false Position? named Default value Accept pipeline input? false Aliases Accept wildcard characters? false -NoDefault [<SwitchParameter>] Returns the connection without making it the module's current connection. Required? false Position? named Default value False Accept pipeline input? false Aliases Accept wildcard characters? false <CommonParameters> This cmdlet supports the common parameters: Verbose, Debug, ErrorAction, ErrorVariable, WarningAction, WarningVariable, OutBuffer, PipelineVariable, and OutVariable. For more information, see about_CommonParameters (https://go.microsoft.com/fwlink/?LinkID=113216). |
Connect-NMMApi supports an existing access token as well as OAuth client credentials, including a Tenant ID based Microsoft Entra token endpoint.
If you already have a bearer token, authentication can be as simple as this:
|
1 2 3 4 5 |
$connection = Connect-NMMApi ` -BaseUri 'https://example.getnerdio.com' ` -AccessToken $accessToken |
For unattended automation, the client credentials flow is usually the more interesting option. When the Tenant ID is supplied, the module requests a token directly from Microsoft Entra ID:
|
1 2 3 4 5 6 7 8 9 10 |
$clientSecret = Read-Host 'NMM API client secret' -AsSecureString $connection = Connect-NMMApi ` -BaseUri 'https://example.getnerdio.com' ` -TenantId '00000000-0000-0000-0000-000000000000' ` -ClientId '11111111-1111-1111-1111-111111111111' ` -ClientSecret $clientSecret ` -Scope 'api://your-nmm-api-scope/.default' |
The module also accepts a plain string for -ClientSecret , which can be useful in automation systems where secrets are injected at runtime. In production, avoid hardcoding secrets in scripts. Store them in a secure location such as Azure Key Vault, an automation account secret store, a CI/CD secret variable, or another credential management solution used by your organization.
The connection can be checked without exposing the access token:
|
1 2 3 |
Get-NMMApiConnection |
The local tests for the module explicitly validate that Get-NMMApiConnection shows connection status and base URI information without returning the access token as a visible property. That is a small but important detail when working in shared terminals, screen recordings, or support sessions.
Discovering Available Commands
A module with more than 350 exported commands needs good discovery. The helper command Get-NMMCommand provides a searchable command index. It can show commands grouped by category, filter by name, verb, category, or API path, and return either formatted text or structured objects.
|
1 2 3 4 5 6 |
Get-NMMCommand Get-NMMCommand -Name '*HostPool*' -Verb Get Get-NMMCommand -ApiPath '*/backup/*' Get-NMMCommand -Category HostPool -AsObject |
The command discovery view is especially useful when looking for the right command family, such as HostPool, Backup, UAM, Security, Billing, or Infrastructure.
When -AsObject is used, the results can be filtered, exported, grouped, or searched further:
|
1 2 3 4 5 |
Get-NMMCommand -AsObject | Where-Object Category -eq 'Backup' | Select-Object Name, Method, ApiPath, Synopsis |
This is useful when building runbooks because it lets you discover the PowerShell command and the underlying API path at the same time. If you already know the REST endpoint from the NMM Swagger documentation, searching by API path is often the fastest way to find the generated PowerShell function.
Reading Accounts and Filtering Results
A common first test after connecting is retrieving the NMM accounts list:
|
1 2 3 |
Get-NMMAccounts |
Many generated Get-NMM... commands include a -Filter parameter when the API response is an array. In the local module tests, 75 GET commands are expected to include this client-side filter. The filter accepts either a script block or a simple string expression.
|
1 2 3 4 5 |
Get-NMMAccounts -Filter { $_.name -like 'Production*' } Get-NMMAccounts -Filter "name -eq 'Contoso Demo'" |
Client-side filtering should not replace API-native query parameters when the API provides them, but it is very convenient for quick operational workflows. For example, an MSP runbook can first retrieve the account by name and then pipe the selected account into the next account-scoped command.
Using Pipeline Binding for Account-Scoped Operations
One of the nicest usability details in the module is pipeline binding for path parameters. The test suite verifies that an account object returned by Get-NMMAccounts can be piped into an account-scoped command, where the account object’s Id is bound to the AccountId path parameter.
|
1 2 3 4 |
Get-NMMAccounts -Filter "name -eq 'Contoso Demo'" | Get-NMMAccountsByAccountIdSecureVariables |
This pattern is very useful when writing customer-centric scripts. Instead of manually copying account IDs between commands, the pipeline can carry the selected object into the next step. That makes the script easier to read and reduces the chance of running an action against the wrong customer account.
Building a Practical Automation Flow
A typical MSP automation flow can be built in layers. First, connect to the NMM API. Then identify the customer account. After that, retrieve or update the specific resource you need to manage.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$connection = Connect-NMMApi ` -BaseUri 'https://example.getnerdio.com' ` -TenantId $tenantId ` -ClientId $clientId ` -ClientSecret $clientSecret ` -Scope $scope $account = Get-NMMAccounts -Filter "name -eq 'Contoso Demo'" $hostPools = $account | Get-NMMAccountsByAccountIdHostPool $hostPools | Select-Object name, resourceGroup, subscriptionId |
From there, the same pattern can be extended into host pool operations, scripted actions, backup validation, user management, secure variable maintenance, or UAM workflows. The key benefit is that the raw REST details are handled consistently by the module, while the automation can stay readable for the engineers who need to maintain it later.
Useful Command Areas
The command set is broad, but the following areas are especially relevant for MSP automation:
- Account provisioning: link tenants, connect existing Active Directory, link networks, configure storage, and refresh PSA customer mappings.
- Host pools: create host pools, retrieve settings, manage hosts, run scripts, control sessions, configure autoscale, update tags, and manage FSLogix settings.
- Desktop images: create images from VMs or gallery images, start or stop images, run scripts, validate, clone, schedule, and set images.
- Automation: work with autoscale profiles, schedules, scripted actions, and NMM jobs.
- Backup and recovery: inspect protected items, recovery points, vaults, policies, and backup operations.
- Security: manage app role assignments, secure variables, MFA-related actions, vulnerabilities, and security recommendations.
- UAM: manage application groups, policies, private repositories, WinGet repositories, shell apps, assignments, and deployment state.
- Billing and usage: retrieve invoices, usage, cost estimates, reservation recommendations, and reservation details.
That coverage makes the module useful beyond a single onboarding task. It can become the foundation for scheduled compliance checks, customer baseline validation, standardized host pool reporting, bulk maintenance, or integration with ITSM and documentation systems.
Regenerating the Module
The repository includes build tooling and the original swagger.json file. The module is designed to be regenerated from the OpenAPI definition, which is important because REST APIs evolve over time. When Nerdio adds or changes endpoints, the generated command set can be rebuilt from an updated specification instead of manually maintaining hundreds of wrapper functions.
The repository also contains tests that validate the generated command contract. These tests check that one command exists for every OpenAPI operation, command names are unique, HTTP methods map to the expected PowerShell verbs, generated help exists, filtering is added where expected, and connection details avoid exposing the access token. For a generated module, those checks are valuable because they help catch broad regressions quickly.
Important Considerations
- Use secure secret storage. The module supports SecureString and runtime-provided secrets, but secret handling is still the responsibility of the automation platform.
- Prefer API-side filtering when available. Client-side filtering is useful, but API-native query parameters are usually more efficient for large datasets.
- Be careful with generated command names. Some names are long because they mirror API paths. Use Get-NMMCommand to find the right command rather than guessing.
- Use -WhatIf and test tenants. Many commands can change customer environments. Validate scripts against non-production accounts first.
- Plan for job polling. Some NMM operations start asynchronous jobs. Scripts should check job status before continuing to dependent steps.
- Keep the Swagger source current. The module is only as current as the OpenAPI definition used to generate it.
Wrapping Up
The NMM-PSModule is a practical way to bring Nerdio Manager for MSP automation into PowerShell. It does not remove the need to understand the NMM API, but it does remove a lot of repetitive REST plumbing. For MSPs managing multiple customers, that can make the difference between a one-off script and a repeatable operational workflow.
The combination of generated API coverage, OAuth authentication, discoverable commands, connection reuse, generated documentation, filtering, and pipeline-friendly account handling makes the module a solid foundation for real-world Nerdio automation. Whether the goal is customer onboarding, reporting, host pool management, backup validation, UAM maintenance, or scheduled operational checks, the module gives PowerShell engineers a consistent starting point.
As with any automation that can affect customer environments, start small, test carefully, and build reusable patterns around authentication, logging, job polling, and error handling. Once those basics are in place, the NMM REST API becomes much easier to use at scale from PowerShell.



