Skip to main content

Styling & Conventions

This page documents the coding conventions and style guidelines for the NinjaOne PowerShell module.

Naming Conventions

Functions

Follow PowerShell's Verb-Noun naming pattern:

# Good
Get-NinjaOneDevice
Set-NinjaOneDeviceName
New-NinjaOneTag
Remove-NinjaOneTag
Invoke-NinjaOneDeviceRestart

# Bad
GetDevice # Missing module prefix
NinjaDevice # Missing verb
Get-Device-Details # Too many hyphens, unclear scope
GetNinjaOneDevice # No hyphens (not PowerShell style)

Rules:

  • Use Approved Verbs from PowerShell's official list (Get-Verb)
  • Approved verbs for NinjaOne context: Get, Set, New, Remove, Invoke, Enable, Disable, Test, Update, Restore, Backup
  • Always include the module prefix: NinjaOne
  • Separate with hyphens: Verb-NounNoun

Function Parameters

Use PascalCase for parameter names:

# Good
param(
[string]$deviceId,
[string]$organisationId,
[int]$pageSize,
[switch]$includeDetails
)

# Bad
param(
[string]$device_id, # Snake case
[string]$DeviceID, # All caps (use camelCase for multi-word)
[int]$page_size, # Snake case
[switch]$IncludeDetails # Should be camelCase not PascalCase
)

Rules:

  • Start with lowercase (except switches which are descriptors)
  • Use camelCase for multi-word parameters
  • Avoid underscores; use overlapping words or abbreviations
  • Switches should be descriptive: $includeInactive, $skipValidation, not $i or $s

Variables

Use camelCase for local variables:

# Good
$deviceName = $device.name
$requestUri = "https://api/devices/$deviceId"
$processedItems = @()

# Bad
$DeviceName = $device.name # PascalCase
$_requestUri = $requestUri # Leading underscore (private members)
$processed_items = @() # Snake case

Rules:

  • Use camelCase consistently
  • Private/internal variables: consider context clarity over convention
  • Avoid single letters except in loops: for ($i = 0; ...)

Classes and Enums

Use PascalCase:

# Good
class NinjaOneOrganisationDocument { }
enum DeviceStatus { Active; Inactive; Archived }

# Bad
class ninjaOneOrganisationDocument { }
enum device_status { }

Code Style

Parameter Declarations

Align parameter attributes for readability:

# Good - aligned
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$deviceId,

[Parameter(ValueFromPipeline)]
[AllowNull()]
[object]$device,

[ValidateRange(1, 100)]
[int]$pageSize = 50
)

# Acceptable - compact
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$deviceId,
[object]$device,
[int]$pageSize = 50
)

# Avoid - hard to read
param([Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$deviceId,[object]$device,[int]$pageSize = 50)

Rules:

  • One parameter per line
  • Attributes above parameter name
  • Descriptive comments for clarity
  • Include defaults when appropriate

Function Structure

Standard layout for NinjaOne functions:

<#
.SYNOPSIS
Brief description.
.DESCRIPTION
Detailed description.
.PARAMETER deviceId
The device ID.
.EXAMPLE
Get-NinjaOneDevice -deviceId 123
.OUTPUTS
[PSCustomObject] Device information.
#>
function Get-NinjaOneDevice {
[CmdletBinding()]
[OutputType([object])]
param(
[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[string]$deviceId # The device ID to retrieve
)

begin {
# Setup code
}

process {
# Main logic
try {
$null = Invoke-NinjaOnePreFlightCheck
$requestUri = "https://api.ninjarmm.com/v2/devices/$deviceId"
$response = Invoke-NinjaOneGETRequest -Uri $requestUri
return $response
}
catch {
$PSCmdlet.ThrowTerminatingError($_)
}
}

end {
# Cleanup code
}
}

Structure:

  1. Comment-based help (<#...#>)
  2. [CmdletBinding()] attribute
  3. [OutputType()] attribute (when return type is consistent)
  4. param() block with inline parameter descriptions
  5. begin { }, process { }, end { } sections (if needed)
  6. Use try/catch for error handling
  7. Throw terminating errors with $PSCmdlet.ThrowTerminatingError()

Conditionals

Use consistent formatting:

# Good - readable
if ($device.status -eq 'Active') {
Write-Verbose "Device is active"
} elseif ($device.status -eq 'Inactive') {
Write-Verbose "Device is inactive"
} else {
Write-Verbose "Device is archived"
}

# Avoid - cramped
if ($device.status -eq 'Active') { Write-Verbose "Device is active" } elseif ($device.status -eq 'Inactive') { Write-Verbose "Device is inactive" } else { Write-Verbose "Device is archived" }

Rules:

  • Open brace on same line
  • One statement per line in block
  • Use elseif (not else if)
  • For simple conditions, parentheses optional but recommended

String Formatting

Prefer backticks or string interpolation over concatenation:

# Good - string interpolation
$message = "Device $deviceId not found"
$uri = "https://api.ninjarmm.com/v2/devices/$deviceId/details"

# Good - multiline (backticks)
$message = `
"Processing device $deviceId " +
"in organisation $organisationId"

# Avoid - hard to read concatenation
$message = "Device " + $deviceId + " not found"

# Avoid - mixed styles
$uri = "https://api.ninjarmm.com/v2/devices/" + $deviceId + "/details?filter=" + $filter

Rules:

  • Use "string with $variable" for interpolation
  • Use @" ... "@ for multiline strings
  • Use + for complex expressions only
  • Avoid excessive concatenation

Collections

Use array syntax clearly:

# Good - explicit array
$ids = @(1, 2, 3)
$items = @()

# Good - pipeline
$items = Get-Item -Path *.ps1

# Avoid - ambiguous
$ids = 1, 2, 3
$items = @

# Avoid - unnecessary conversion
$ids = @(@(1, 2, 3))

Rules:

  • Use @() for empty arrays: $items = @()
  • Use @(value1, value2) for multi-item arrays
  • Single items don't need @() prefix
  • Avoid nested @() unless necessary

Error Handling

Use structured error handling:

# Good
try {
$response = $url | Invoke-WebRequest
}
catch [System.Net.HttpRequestException] {
Write-Error "HTTP request failed: $_"
return $null
}
catch {
$PSCmdlet.ThrowTerminatingError($_)
}

# Acceptable for internal functions
try {
$response = $url | Invoke-WebRequest
}
catch {
throw "Failed to fetch: $_"
}

Rules:

  • Catch specific exceptions when possible
  • Log/write error before returning early
  • Use $PSCmdlet.ThrowTerminatingError($_) for fatal errors
  • Include $_ or $_.Exception.Message in error output

API Design Patterns

Parameter Guidelines

Required Parameters: Use [Parameter(Mandatory)]

param(
[Parameter(Mandatory)]
[string]$deviceId # Always required
)

Pipeline Support: Enable pipeline binding when logical

param(
[Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
[object]$device # Accept device objects from pipeline
)

Common Parameter Sets: Define overlapping parameters

param(
[Parameter(Mandatory, ParameterSetName = 'ByDeviceId')]
[string]$deviceId,

[Parameter(Mandatory, ParameterSetName = 'ByName')]
[string]$deviceName,

[Parameter(Mandatory, ParameterSetName = 'ByObject')]
[object]$device
)

Return Values

Always be explicit about return types:

[OutputType([object])]
function Get-NinjaOneDevice {
return $response
}

[OutputType([bool])]
function Test-NinjaOneDeviceAccess {
return $true
}

# Returns $null - no [OutputType] attribute
function Initialize-NinjaOneSession {
# No return statement
}

Rules:

  • Specify [OutputType()] with type in brackets
  • Return consistent types (not sometimes array, sometimes single item)
  • Use [object] for complex/heterogeneous returns
  • For multiple types: [OutputType([object[]], [bool])]

Filtering and Selection

For functions that filter or retrieve collections:

# Good - logical parameters
param(
[ValidateSet('Active', 'Inactive', 'Archived')]
[string]$status, # Filter by status

[ValidateRange(1, 1000)]
[int]$limit = 100, # Limit results

[int]$offset = 0, # Pagination offset

[string]$filter # OData filter expression
)

# Bad - too many optional numeric parameters
param(
[int]$skip,
[int]$take,
[int]$pageNumber,
[int]$pageSize,
[int]$offset
)

Rules:

  • Consolidate related filtering into one parameter when possible
  • Use ValidateSet for predefined options
  • Support pagination with consistent naming: offset/limit or page/pageSize
  • Document filter syntax in help

Documentation Impact

Code style directly affects generated documentation:

'Well-styled function:
function Get-NinjaOneDevice {
<#
.SYNOPSIS
Retrieves a specific NinjaOne device.
.PARAMETER deviceId
The device ID to retrieve # ← In generated docs
#>
param(
[Parameter(Mandatory)]
[string]$deviceId # The device ID to retrieve # ← Also in generated docs
)
}

# Generates:
# Get-NinjaOneDevice [-deviceId] <string>
#
# Retrieves a specific NinjaOne device.
#
# Parameters:
# -deviceId <string>
# The device ID to retrieve

Always write code as if documentation will be auto-generated—because it will be.

PR Checklist for Style

Before submitting, verify:

  • Function names follow Verb-NounNoun pattern
  • Parameter names use camelCase
  • All parameters have descriptions (inline comment or .PARAMETER)
  • Return type specified with [OutputType()]
  • Comment-based help complete (SYNOPSIS, DESCRIPTION, examples)
  • Error handling uses try/catch with $PSCmdlet.ThrowTerminatingError()
  • No PSScriptAnalyzer violations
  • Code passes style checks in test suite

See Also