Skip to main content

· 11 min read

This post uses code from CyberDrain

This post draws on multiple posts, click the link below to check out CyberDrain.com and support Kelvin's fantastic work for the MSP community.

You will find more excellent uses for NinjaOne custom fields on the Dojo, on Stephen Murphy's blog and on Luke Whitelock's blog.

Custom fields are a great way to store arbitrary data from your devices in NinjaOne. In this post I will explore a few examples, some using code from CyberDrain, which store data in NinjaOne custom fields.

This post was updated on 2022/12/22 to add a new script to run a speedtest on a device and store the results in NinjaOne.

Battery Health

Creating Fields

Creating custom fields in NinjaOne
To create a custom field in NinjaOne go to Administration > Devices and select either Role Custom Fields or Global Custom Fields then select Add.

  • Role Custom Fields are custom fields that are specific to a device role.
  • Global Custom Fields are custom fields that are applicable to all devices and/or to a location and/or organisation
Make sure you add the fields to the roles you want to use them in at Administration > Devices > Roles (for role custom fields).

When you create your custom field you need to make sure that you set the Scripts permission to ensure that you can read or write to the field from your scripts - as appropriate for the script you're using.

We're adding seven role custom fields for devices with the Windows Laptop role:

Field NameField TypeDescription
Has BatteriesCheckboxWhether the device has batteries
Battery IdentifierTextThe ID of the battery being reported on.
Battery Design CapacityIntegerThe original design capacity of the laptop's battery.
Battery Full Charge CapacityIntegerThe current fully charged capacity of the laptop's battery.
Battery Health PercentIntegerThe current percentage of battery health.
Battery Cycle CountIntegerThe number of times the battery has been cycled. That is drained fully and charged.
Additional BatteryCheckboxThe system has an additional battery, only the first is reported in the fields above.

The Script

Working on it
Ninja have confirmed that they are working on native functionality which will replace this script. Stay tuned to Ninja's release notes on the [NinjaOne Dojo](https://ninjarmm.zendesk.com) for more information.
This Script Was Updated

This script was updated after being published, if you're using it please compare the version you have with the version available here.

This script was last updated on 2022/03/26.

This script accepts a single parameter to set the data storage directory for the script. This is where the script will store the battery information it gathers. The script will create the directory if it doesn't exist. The default is C:\RMM\Data use parameter -OutputPath to override.

Get-BatteryHealth.ps1
loading...

Source: https://www.cyberdrain.com/monitoring-with-powershell-monitoring-battery-health/

The Results

Battery Related Custom Fields

You can set this up to run on a schedule - we run this script monthly to keep up-to-date battery information in NinjaOne.

Domain Join Status

Creating Fields

Creating custom fields in NinjaOne
To create a custom field in NinjaOne go to Administration > Devices and select either Role Custom Fields or Global Custom Fields then select Add.

  • Role Custom Fields are custom fields that are specific to a device role.
  • Global Custom Fields are custom fields that are applicable to all devices and/or to a location and/or organisation
Make sure you add the fields to the roles you want to use them in at Administration > Devices > Roles (for role custom fields).

When you create your custom field you need to make sure that you set the Scripts permission to ensure that you can read or write to the field from your scripts - as appropriate for the script you're using.

We're adding three role custom fields for devices with the Windows Laptop role:

Field NameField TypeDescription
Domain Join StatusDrop-downThe current domain join status of the device.
Domain NameTextThe name of the currently joined AD domain.
Tenant NameTextThe name of the currently joined Azure AD tenant.

The domain join status field should be configured with the following option values:

  • Azure AD Domain
  • AD Domain
  • Hybrid Azure AD/AD Domain
  • On-premise DRS
  • No Domain

Options

We've created a dropdown field for this script, we can't set the options by name with these - so first we need to find out how NinjaOne wants us to give the selected option. To do that we're going to open a PowerShell session to any device in the role that has the field assigned and run:

Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned
Import-Module NJCliPSh
Ninja-Property-Options domainJoinStatus

This will print a list that looks something like this:

1989092a-1ae0-4e6e-b274-50ea9618d436=Azure AD Domain
53745d33-fd5e-4c7d-bfd5-fea174a46781=No Domain
bb4c5215-ead7-47a3-8f36-f711c81580ce=On-premises DRS
c23486e9-49eb-4acb-8f9d-7f5d8711c284=AD Domain
c6e7ef80-68a1-4591-8055-5cee585f4f79=Hybrid Azure AD/AD Domain

When we want to pass the option to the field we need to use the GUID - for example 1989092a-1ae0-4e6e-b274-50ea9618d436 for Azure AD Domain.

The Script

This Script Requires Input
This script requires user input, whether in the form of variables, parameters or edits to the script itself before you can run it. Areas where you need to provide input will be indicated with:
### Inline Comments
and / or
'<MARKED STRINGS>'
Parameters will be indicated before the script block.
This Script Was Updated

This script was updated after being published, if you're using it please compare the version you have with the version available here.

This script was last updated on 2022/05/25.

Get-DomainJoinStatus.ps1
loading...

The Results

Domain Related Custom Fields

You can set this up to run on a schedule - we run this script monthly to keep up-to-date domain information in NinjaOne.

Autopilot Hardware Identifier

Creating Fields

Creating custom fields in NinjaOne
To create a custom field in NinjaOne go to Administration > Devices and select either Role Custom Fields or Global Custom Fields then select Add.

  • Role Custom Fields are custom fields that are specific to a device role.
  • Global Custom Fields are custom fields that are applicable to all devices and/or to a location and/or organisation
Make sure you add the fields to the roles you want to use them in at Administration > Devices > Roles (for role custom fields).

When you create your custom field you need to make sure that you set the Scripts permission to ensure that you can read or write to the field from your scripts - as appropriate for the script you're using.
"Field length"

Make sure when creating the custom field below that you set the character limit for the field to allow over 4000 characters using the "Advanced Settings" link on the field creation/edit page.

Advanced Settings Character Limit

We're adding one role custom field for devices with the Windows Desktops and Laptops role:

Field NameField TypeDescription
Autopilot HWIDSecureThe autopilot hardware identifier.

The Script

Get-AutopilotHWID.ps1
loading...

The Results

HWID Related Custom Fields

We run this after hardware changes or post onboarding.

Wireless LAN Monitoring

Creating Fields

Creating custom fields in NinjaOne
To create a custom field in NinjaOne go to Administration > Devices and select either Role Custom Fields or Global Custom Fields then select Add.

  • Role Custom Fields are custom fields that are specific to a device role.
  • Global Custom Fields are custom fields that are applicable to all devices and/or to a location and/or organisation
Make sure you add the fields to the roles you want to use them in at Administration > Devices > Roles (for role custom fields).

When you create your custom field you need to make sure that you set the Scripts permission to ensure that you can read or write to the field from your scripts - as appropriate for the script you're using.

We're adding three role custom fields for devices with the Windows Desktops and Laptops role:

Field NameField TypeDescription
WLAN Disconnect ReasonsMulti-lineA list of the WLAN disconnect reasons with frequency. JSON
WLAN FailuresIntegerThe number of WLAN failures in the report.
WLAN WarningsIntegerThe number of WLAN warnings in the report.

The Script

Get-WLANStatus.ps1
loading...

The Results

WLAN Related Custom Fields

You can set this up to run on a schedule - we run this script twice daily.

Windows 11 Readiness

Creating Fields

Creating custom fields in NinjaOne
To create a custom field in NinjaOne go to Administration > Devices and select either Role Custom Fields or Global Custom Fields then select Add.

  • Role Custom Fields are custom fields that are specific to a device role.
  • Global Custom Fields are custom fields that are applicable to all devices and/or to a location and/or organisation
Make sure you add the fields to the roles you want to use them in at Administration > Devices > Roles (for role custom fields).

When you create your custom field you need to make sure that you set the Scripts permission to ensure that you can read or write to the field from your scripts - as appropriate for the script you're using.

We're adding three role custom fields for devices with the Windows Desktops and Laptops role:

Field NameField TypeDescription
Windows 11 CapableCheckboxWhether the device is Windows 11 capable as determined by our checks.
Windows 11 Check DetailsMulti-lineThe details checked to determine Windows 11 compatibility. JSON

The Script

This Script Was Updated

This script was updated after being published, if you're using it please compare the version you have with the version available here.

This script was last updated on 2024/05/16.

Test-Windows11Readiness.ps1
loading...

Source: https://techcommunity.microsoft.com/t5/microsoft-endpoint-manager-blog/understanding-readiness-for-windows-11-with-microsoft-endpoint/ba-p/2770866

Thanks to @gavsto for invaluable input on a couple of the checks in here - he's helped make them more robust and accurate.

The script's detailed output has changed and will now show two metrics related to Secure Boot. Previously it showed in SecureBootSuitable whether the state of Secure Boot was suitable for Windows 11 installation. This has been enhanced with a new SecureBootCapable metric which will show whether it's likely that Secure Boot can be enabled. A "No" to both metrics will indicate that Secure Boot is not enabled or not capable of being enabled and thus the device is not Windows 11 capable. A "Yes" to SecureBootCapable indicates that Secure Boot can likely be enabled and that this should be done to make the device Windows 11 capable.

The Results

Windows 11 Related Custom Fields

We ran this as a one-off job to evaluate our managed estate.

Limitations

This script won't detect compatible devices if the TPM is not enabled in the BIOS. It may not detect all compatible devices. This script may not accurately detect Secure Boot capability on all devices.

Windows OS Support Status

Creating Fields

Creating custom fields in NinjaOne
To create a custom field in NinjaOne go to Administration > Devices and select either Role Custom Fields or Global Custom Fields then select Add.

  • Role Custom Fields are custom fields that are specific to a device role.
  • Global Custom Fields are custom fields that are applicable to all devices and/or to a location and/or organisation
Make sure you add the fields to the roles you want to use them in at Administration > Devices > Roles (for role custom fields).

When you create your custom field you need to make sure that you set the Scripts permission to ensure that you can read or write to the field from your scripts - as appropriate for the script you're using.

We're adding two role custom fields for devices with the Windows Desktops and Laptops and the Windows Server role:

Field NameField TypeDescription
Windows Active SupportCheckboxWhether the OS version is supported for feature / quality updates.
Windows Security SupportCheckboxWhether the OS version is supported for security updates.

The Script

"Shoutout"

Thanks to Khristos from the MSP Geek community for keeping an eye on EndOfLife.Date's endlessly changing property for holding the Windows build...

This Script Was Updated

This script was updated after being published, if you're using it please compare the version you have with the version available here.

This script was last updated on 2023/01/26.

Get-WindowsOSSupportStatus.ps1
loading...

The Results

Windows Support Status Related Custom Fields

We run this on a schedule so it runs just after our OS patching applies.

Limitations

Won't work with Insider/Preview versions - hasn't been tested on Windows 7 / 8 or 8.1. Has been tested on Server 2012 R2, 2022 and Windows 10 and 11.

SpeedTest (LibreSpeed)

Creating Fields

Creating custom fields in NinjaOne
To create a custom field in NinjaOne go to Administration > Devices and select either Role Custom Fields or Global Custom Fields then select Add.

  • Role Custom Fields are custom fields that are specific to a device role.
  • Global Custom Fields are custom fields that are applicable to all devices and/or to a location and/or organisation
Make sure you add the fields to the roles you want to use them in at Administration > Devices > Roles (for role custom fields).

When you create your custom field you need to make sure that you set the Scripts permission to ensure that you can read or write to the field from your scripts - as appropriate for the script you're using.

We're adding three role custom fields for devices with the Windows Desktops and Laptops and the Windows Server role:

Field NameField TypeDescription
Server UsedTextWhich server was used for the last speedtest run.
Download SpeedDecimalThe download speed from the last speedtest run in megabits per second.
Upload SpeedDecimalThe upload speed from the last speedtest run in megabits per second.

The Script

"Issues found"

This script seems to have some issues where the librespeed-cli will return null for tests intermittently - this was due to service issues with LibreSpeed themselves. An alternative version using the Ookla speedtest CLI is available below.

This Script Was Updated

This script was updated after being published, if you're using it please compare the version you have with the version available here.

This script was last updated on 2022/12/31.

Test-ConnectionSpeedwithLibreSpeed.ps1
loading...

Parameters

You can pass a few different parameters to this script to tune it's behaviour. These are:

Parameter NameTypeDescription
LibreSpeedEXEPathStringThe path to the LibreSpeed executable. Defaults to C:\RMM\Bin\
NoUpdateSwitchIf specified, the script will not attempt to update or download LibreSpeed.
ForceUpdateSwitchIf specified, the script will download LibreSpeed even if it's already up to date.
CLISwitchesStringA string of switches to pass to the LibreSpeed CLI. Defaults to --json.

For documentation on the CLI switches available see the LibreSpeed CLI documentation.

The Results

Speed Test Related Custom Fields

In this screenshot we've added a separator and a nice custom title with an emoji to make it look a bit nicer.

⏱️SPEEDTEST

We run this on demand when we want to refresh information on the device - we don't run it on a schedule at present.

Limitations

Requires the ability to run the LibreSpeed CLI tool on the device. The script can fetch the latest version and will do so automatically.

SpeedTest (Ookla Speedtest)

Creating Fields

Creating custom fields in NinjaOne
To create a custom field in NinjaOne go to Administration > Devices and select either Role Custom Fields or Global Custom Fields then select Add.

  • Role Custom Fields are custom fields that are specific to a device role.
  • Global Custom Fields are custom fields that are applicable to all devices and/or to a location and/or organisation
Make sure you add the fields to the roles you want to use them in at Administration > Devices > Roles (for role custom fields).

When you create your custom field you need to make sure that you set the Scripts permission to ensure that you can read or write to the field from your scripts - as appropriate for the script you're using.

We're adding three role custom fields for devices with the Windows Desktops and Laptops and the Windows Server role:

Field NameField TypeDescription
Server UsedTextWhich server was used for the last speedtest run.
Download SpeedDecimalThe download speed from the last speedtest run in megabits per second.
Upload SpeedDecimalThe upload speed from the last speedtest run in megabits per second.

The Script

Test-ConnectionSpeedwithOoklaSpeedtest.ps1
loading...

Parameters

You can pass a few different parameters to this script to tune it's behaviour. These are:

Parameter NameTypeDescription
OoklaSpeedtestURIStringThe URI to the Ookla Speedtest CLI. Defaults to the 1.2.0 version current when written.
OoklaSpeedtestEXEPathStringThe path to the Ookla Speedtest executable. Defaults to C:\RMM\Bin\
NoUpdateSwitchIf specified, the script will not attempt to update or download Ookla Speedtest.
ForceUpdateSwitchIf specified, the script will download Ookla Speedtest even if it's already up to date.
CLISwitchesStringA string of switches to pass to the Ookla Speedtest CLI. Defaults to --format=json --accept-license --accept-gdpr.

For documentation on the CLI switches available you should download the latest version of the CLI and check out the speedtest.md file in the Zip or run speedtest.exe --help.

The Results

Speed Test Related Custom Fields

In this screenshot we've added a separator and a nice custom title with an emoji to make it look a bit nicer.

⏱️SPEEDTEST

We run this on demand when we want to refresh information on the device - we don't run it on a schedule at present.

Limitations

Requires the ability to run the Ookla Speedtest CLI tool on the device. The script cannot automatically determine the latest version at this time - please pass an updated URL if you wish to use a later version before the script is updated.

· 2 min read

Background information

Ninja doesn't currently support native AV monitoring via Windows Security Center, integrated AV packages are monitored but what if you need more?

Creating Fields

Creating custom fields in NinjaOne
To create a custom field in NinjaOne go to Administration > Devices and select either Role Custom Fields or Global Custom Fields then select Add.

  • Role Custom Fields are custom fields that are specific to a device role.
  • Global Custom Fields are custom fields that are applicable to all devices and/or to a location and/or organisation
Make sure you add the fields to the roles you want to use them in at Administration > Devices > Roles (for role custom fields).

When you create your custom field you need to make sure that you set the Scripts permission to ensure that you can read or write to the field from your scripts - as appropriate for the script you're using.

We're going to create one role custom field for devices with the Windows Desktop or Windows Laptop role:

Field NameField TypeDescription
Detailed AV StatusMulti-LineOutput for each configured AV on the system including name and status information.

The Script

Windows Server

This script won't work on Windows Server operating systems as they lack a CIM or WMI interface to the Windows Security Center which allows us to easily programmatically query the AV status.

Get-WindowsAVStatus.ps1
loading...

Monitoring

The script includes support for two monitors.

AV Not Enabled

Setup a script result condition monitor that runs this script with a check for an exit code of 1.

AV Not Enabled Condition

AV Not Enabled Monitor

AV Not Up-To-Date

Setup a second script result condition monitor that runs this script with a check for an exit code of 2.

AV Not Enabled Condition

AV Not Enabled Monitor

· 4 min read

Secure Application Model

For this post you'll want a secure application model Azure AD application configured. Use these instructions from Gavin Stone's Gavsto.com blog.

CIPP Inspired

The code in this script was inspired by functionality in the CyberDrain Improved Partner Portal (CIPP). Need to manage M365 tenants - CIPP is a fantastic open-source solution for M365 management for single tenants and Microsoft CSP Partners alike.

Background information

In my quest to stop having to use Windows Sandbox or VMs to access Exchange Online via PowerShell (we have WinRM basic auth disabled) I've been looking at solutions using the Exchange Online REST API to run commands against our customer tenants. The result of my efforts is a pretty complex script that introduces a method to run any PowerShell script in the context of a set of customer tenants.

caution

You need to have configured the Secure Application Model according to the instructions linked above, you will need your SAM tokens etc accessible for this guide.

Securing Credentials in Scripts
Do not store credentials directly in scripts. You should utilise services like Azure KeyVault or other PAM solutions to secure credentials used in scripts, including this one.

The script

Invoke-EORESTDelegated.ps1
loading...

Building Parameters

Personally I find passing parameters as a HashTable (splatting) easier to read and cleaner see what's happening so that's what I'm doing to explain the parameters for the script:

Parameter Splat
$EORESTParameters = @{
PartnerTenantId = (Get-AzKeyVaultSecret -VaultName 'Homotechsual' -Name 'EORESTTenantId' -AsPlainText) # Your partner tenant id.
ApplicationId = (Get-AzKeyVaultSecret -VaultName 'Homotechsual' -Name 'EORESTClientId' -AsPlainText) # Your SAM application / client id.
ApplicationSecret = (Get-AzKeyVaultSecret -VaultName 'Homotechsual' -Name 'EORESTClientSecret' -AsPlainText) # Your SAM application / client secret.
GraphRefreshToken = (Get-AzKeyVaultSecret -VaultName 'Homotechsual' -Name 'EORESTRefreshToken' -AsPlainText) # The Graph refresh t oken provided by the SAM app creation script.
ExchangeRefreshToken = (Get-AzKeyVaultSecret -VaultName 'Homotechsual' -Name 'EORESTExchangeRefreshToken' -AsPlainText) # The Exchange refresh token provided by the SAM app creation script.
UPN = '[email protected]' # The UPN of the user used to authorise your SAM tokens.
IncludeTenants = @('test1.onmicrosoft.com') # Run only on the tenants listed - using the tenant's `DefaultDomainName`. This input is NOT validated.
ExcludeTenants = @('test1.onmicrosoft.com') # Run on the all tenant except those listed - using the tenant's `DefaultDomainName`.
}

ScriptBlock

The -ScriptBlock parameter passed to the script runs the commands given against each selected tenant's Exchange Online. You should use the Invoke-EORequest cmdlet in the script block to send commands to ExchangeOnline. For example to get mailboxes:

-ScriptBlock { Invoke-EORequest -Commandlet 'Get-Mailbox' }

More examples are given below.

Result structure

Before returning values we add a property to object containing the customer's tenant id. This property is called EORCustomerId and is the tenant GUID. You can use this to group results by the tenant they relate to.

| Group-Object -Property 'EORCustomerId'

Example uses

These examples assume use of the parameter splat above

Get Mailbox Plans

.\Invoke-EORESTDelegated.ps1 @EORESTParameters -ScriptBlock { Invoke-EORequest -Commandlet 'Get-MailboxPlan' }

Get Shared, Room, Equipment, Group and Team mailboxes

.\Invoke-EORESTDelegated.ps1 @EORESTParameters -ScriptBlock { Invoke-EORequest -Commandlet 'Get-Mailbox' -Parameters @{ Filter = "((RecipientTypeDetails -eq 'SharedMailbox') -or (RecipientTypeDetails -eq 'RoomMailbox') -or (RecipientTypeDetails -eq 'EquipmentMailbox') -or (RecipientTypeDetails -eq 'GroupMailbox') -or (RecipientTypeDetails -eq 'TeamMailbox'))" } }

Get last login time.

.\Invoke-EORESTDelegated.ps1 @EORESTParameters -ScriptBlock {
$Mailboxes = Invoke-EORequest -Commandlet 'Get-Mailbox' -Parameters @{ ResultSize = 'Unlimited' }
ForEach ($Mailbox in $Mailboxes) { Invoke-EORequest -Commandlet 'Get-MailboxStatistics' -Parameters @{ Identity = $Mailbox.UserPrincipalName } | Select-Object -Property DisplayName, LastLoggedOnUserAccount, LastLogonTime }
}

Get mailboxes not logged into in the last 90 days.

.\Invoke-EORESTDelegated.ps1 @EORESTParameters -ScriptBlock {
$Mailboxes = Invoke-EORequest -Commandlet 'Get-Mailbox' -Parameters @{ ResultSize = 'Unlimited' }
ForEach ($Mailbox in $Mailboxes) { Invoke-EORequest -Commandlet 'Get-MailboxStatistics' -Parameters @{ Identity = $Mailbox.UserPrincipalName } | Where-Object { $_.LastLogonTime -lt (Get-Date).AddDays(-90) } | Select-Object -Property DisplayName, LastLoggedOnUserAccount, LastLogonTime }
}

Get inactive mailboxes.

.\Invoke-EORESTDelegated.ps1 @EORESTParameters -ScriptBlock {
Invoke-EORequest -Commandlet 'Get-Mailbox' -Parameters @{ InactiveMailboxOnly = $True }
}

Set hidden from address list for all mailboxes.

.\Invoke-EORESTDelegated.ps1 @EORESTParameters -ScriptBlock {
$Mailboxes = Invoke-EORequest -Commandlet 'Get-Mailbox' -Parameters @{ ResultSize = 'Unlimited' }
ForEach ($Mailbox in $Mailboxes) { Invoke-EORequest -Commandlet 'Set-Mailbox' -Parameters @{ Identity = $Mailbox.UserPrincipalName; HiddenFromAddressListEnabled = $True } }
}

· One min read

I'm the author of the NinjaOne PowerShell module so when I was looking for a quick way to identify duplicate devices I turned to that module and the NinjaOne API to find a solution.

About this script

This script is a quick way to identify duplicate devices in your NinjaOne instance. It can return a full listing of all devices grouped by serial number, it can also just return the ID, last contact date and serial number for the device with the oldest last contact date.

The script

Get-NinjaOneDuplicateDevices.ps1
loading...

At this time it's not possible to remove devices via the NinjaOne API so this script will only return the duplicate devices - you'll have to arrange to remove these devices manually.

If you want all devices with duplicates:

Get-NinjaOneDuplicateDevices -All

or just the older duplicated devices:

Get-NinjaOneDuplicateDevices -Duplicates

· 2 min read

Did you know, when you have a retention policy configured for OneDrive in Microsoft 365 you lose the ability to recursively delete folders. The OneDrive client often handles this by recursing through the folder tree to remove the online copies but sometimes that doesn't work and in those cases you can end up stuck with a folder tree that you would have to manually recurse to remove the offending folders by first emptying them entirely before OneDrive will let you delete them.

Well that's never going to happen ;-)

Enter PowerShell.

Required Modules

You'll need the PnP.PowerShell module for this script. Install with Install-Module 'PnP.PowerShell'

The script

Remove-OneDriveFolderRecursively.ps1
loading...

Invoking the script

The script is looking for three parameters, an example invocation to remove a synced "Documents/PowerShell/Modules" folder in Satya Nadella's OneDrive would be:

Double the Documents

You'll notice below that the /Documents folder in the path is doubled. This is because the first folder in the OneDrive path is always /Documents then your path starts. In this example we're removing the /Documents/PowerShell/Modules folder.

Remove-OneDriveFolderRecursively -OneDriveHost 'https://microsoft-my.sharepoint.com' -SitePath '/personal/satya_nadella_microsoft_com' -FolderPath '/Documents/Documents/PowerShell/Modules'

Hope this helps!

This post was inspired by a script posted on Microsoft's TechCommunity here...

· 5 min read

Background information

The NinjaOne agent gives you the ability to customise your system tray and add things like about text, email links, a help request form, a URL and other things. Unfortunately HaloPSA doesn't currently support hooking into NinjaOne's help request form (don't worry, we're talking to NinjaOne and Halo Service Solutions about this!) so we're left with a bit of a workaround as a solution.

· 2 min read

About this script

HaloPSA is improving at a phenomenal rate - one of the latest enhancements relates to embedding the "customer portal" inside Teams for easier access by customers - this is early days for this enhancement to Halo and at present there isn't really a way to push this tab into customer environments... Until now!

Using the script below you can push any website as a tab to your customers' Teams environments. First a couple of configuration pre-requisites.

  1. You'll need to have setup the Secure App Model (Thanks Gav for the superb write-up!)
  2. You'll need to know the name of the Team and Channel you want to deploy the tab - currently the script will use the same details for all customers!

The fix

Securing Credentials in Scripts
Do not store credentials directly in scripts. You should utilise services like Azure KeyVault or other PAM solutions to secure credentials used in scripts, including this one.
This Script Was Updated

This script was updated after being published, if you're using it please compare the version you have with the version available here.

This script was last updated on 2023/03/25.

So here's the script:

New-TeamsWebTab.ps1
loading...

To invoke the script we need to satisfy it's required parameters, an example invocation would look like this:

$NTWTParams = @{
CustomerExclude = @('example1.onmicrosoft.com', 'example2.onmicrosoft.com')
TeamName = 'IT Service Desk'
ChannelName = 'General'
ClientId = '00000000-0000-0000-0000-000000000000'
ClientSecret = '00000000-0000-0000-0000-000000000000'
TenantId = '00000000-0000-0000-0000-000000000000'
TabName = 'Portal'
TabURL = 'https://portal.homotechsual.dev'
}
.\New-TeamsWebTab.ps1 @NTWTParams

Breaking down the available parameters:

  • -CustomerExclude - An array of customer tenant domains to exclude from the script.
  • -CustomerInclude - An array of customer tenant domains to include in the script.
  • -TeamName - The name of the Team to add the tab to. Defaults to the default team.
  • -ChannelName - The name of the Channel to add the tab to. Defaults to "General".
  • -ClientId - The Client ID of your Secure Application Model App.
  • -ClientSecret - The Client Secret of your Secure Application Model App.
  • -TenantId - The Tenant ID of your partner tenant.
  • -TabName - The name of the tab to add.
  • -TabURL - The URL of the tab to add.

Once you've filled in the variables - you simply run the script and wait for the tabs to be added!

· 2 min read
Save yourself the headache - use the Microsoft Store on Windows 11

If you're using or managing this for Windows 11 you can now deploy Adobe Acrobat Reader DC using the Microsoft Store. This is preferable for many reasons mostly because this way of doing it with IntuneWin files is a complete pain in the ass.

About this script

If you're not familiar with the IntuneWin format and what it's used for/when it's used there's some good background reading from Microsoft here: Prepare a Win32 app to be uploaded to Microsoft Intune | Microsoft Docs.

In short, it's a format designed to package Windows application installers for deployment with Intune.

This post contains a script which downloads the latest version of the Adobe Reader DC installer and turns it into an IntuneWin package for Intune deployment.

Kelvin has published a module that automates publishing RMM installers to Intune across all your customer tenants - you can read about it here: Automating with PowerShell: uploading your RMM application to all Intune tenants - CyberDrain.

The fix

I'm planning a future blog post that integrates these steps into Kelvin's module's workflow providing a single script you can use to get the latest version of Adobe Reader DC and upload it to all your customers' Intune tenants.

The script
New-AdobeReaderDCInstallPackage.ps1
loading...

To invoke the script we need to satisfy it's required parameters, an example invocation would look like this:

.\New-AdobeReaderDCInstallPackage.ps1 -Language 'English (UK)' -Architechture 'x64' -InstallerSavePath 'X:\ARDeployment\Src\' -Win32ContentPrepToolPath 'I:\Microsoft\Win32 Content Prep Tool\IntuneWinAppUtil.exe' -PackageOutputPath 'X:\ARDeployment\Output\'

Breaking down the parameters there:

  • -Language: Specify the language for the installer. For English (US) use English. A list of valid values can be found at the top of the script.
  • -Architechture: The CPU architechture for the installer - valid values are *'x64'* and 'x86'.
  • -InstallerSavePath: The folder to save the installer executable into. If it doesn't exist, the script will try to create it.
  • -Win32ContentPrepToolPath: The full path to the Microsoft Win32-Content-Prep-Tool executable file.
  • -PackageOutputPath: The folder to save the resulting IntuneWin file into. If it doesn't exist, the script will try to create it.

· One min read

I don't handle Base64 strings very often - when I do I find myself searching for instructions every time. So I finally decided to add two small helper functions to my PowerShell profile.

PROFILE.ps1
function ConvertFrom-Base64String ([String]$InputString, [System.Text.Encoding]$Encoding) {
$DecodedString = $Encoding.GetString([System.Convert]::FromBase64String($InputString))
$DecodedString
}

function ConvertTo-Base64String ([String]$InputString, [System.Text.Encoding]$Encoding) {
$StringBytes = $Encoding.GetBytes($InputString)
$EncodedString = [System.Convert]::ToBase64String($StringBytes)
$EncodedString
}
tip

You can quickly edit your PowerShell profile file by typing code $PROFILE into your PowerShell window - this will pop up VS Code with your profile ready to edit.