From 0d0ed0881c2f1ea92138a9079f6c6f5f973f36dd Mon Sep 17 00:00:00 2001 From: Mikey O'Toole Date: Fri, 24 May 2024 09:30:54 +0100 Subject: [PATCH] Fix #38 - Missing $Resource definition in Set-NinjaOneOrganisationPolicies. Fixes for some other small bugs/issues also included. --- .editorconfig | 2 + NinjaOne.code-workspace | 8 +- PSScriptAnalyzerSettings.psd1 | 245 ++++--- Source/Private/ConvertTo-UnixEpoch.ps1 | 2 +- Source/Public/Connect-NinjaOne.ps1 | 660 +++++++++--------- .../Get/Entities/Get-NinjaOneActivities.ps1 | 3 +- .../Get/Entities/Get-NinjaOneAlerts.ps1 | 6 +- Source/Public/New/New-NinjaOneInstaller.ps1 | 140 ++-- .../Set/Set-NinjaOneOrganisationPolicies.ps1 | 217 +++--- 9 files changed, 665 insertions(+), 618 deletions(-) diff --git a/.editorconfig b/.editorconfig index e69de29..52ba89b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -0,0 +1,2 @@ +indent_style = tab +indent_size = 1 \ No newline at end of file diff --git a/NinjaOne.code-workspace b/NinjaOne.code-workspace index 1d3e8ea..33b87e3 100644 --- a/NinjaOne.code-workspace +++ b/NinjaOne.code-workspace @@ -5,7 +5,10 @@ } ], "settings": { - "editor.tabSize": 4, + "editor.indentSize": "tabSize", + "editor.tabSize": 1, + "editor.detectIndentation": true, + "editor.autoIndent": "advanced", "editor.formatOnSave": true, "editor.formatOnSaveMode": "modificationsIfAvailable", "azure-pipelines.customSchemaFile": ".\\DevOps\\azurepipelines.schema.json", @@ -48,8 +51,7 @@ { "name": "PowerShell Attach Interactive Session Runspace", "type": "PowerShell", - "request": "attach", - "processId": "current" + "request": "attach" }, { "name": "PowerShell: Launch Current File", diff --git a/PSScriptAnalyzerSettings.psd1 b/PSScriptAnalyzerSettings.psd1 index c95a9b0..87bac42 100644 --- a/PSScriptAnalyzerSettings.psd1 +++ b/PSScriptAnalyzerSettings.psd1 @@ -1,105 +1,148 @@ <# - .SYNOPSIS - Based on the PowerShell Script Analyzer (PSSA) presets. - .LINK - https://github.com/PowerShell/PSScriptAnalyzer/blob/master/Engine/Settings/ + .SYNOPSIS + Based on the PowerShell Script Analyzer (PSSA) presets. + .LINK + https://github.com/PowerShell/PSScriptAnalyzer/blob/master/Engine/Settings/ #> @{ - Severity = @( - 'Error', - 'Warning', - 'Information' - ) - IncludeRules = @( - 'PSAlignAssignmentStatement', - 'PSAvoidAssignmentToAutomaticVariable', - 'PSAvoidDefaultValueForMandatoryParameter', - 'PSAvoidDefaultValueSwitchParameter', - 'PSAvoidGlobalAliases', - 'PSAvoidGlobalVars', - 'PSAvoidUsingCmdletAliases', - 'PSAvoidUsingComputerNameHardcoded', - 'PSAvoidUsingConvertToSecureStringWithPlainText', - 'PSAvoidUsingDoubleQuotesForConstantString', - 'PSAvoidUsingEmptyCatchBlock', - 'PSAvoidUsingInvokeExpression', - 'PSAvoidUsingPlainTextForPassword', - 'PSAvoidUsingPositionalParameters', - 'PSAvoidUsingUserNameAndPasswordParams', - 'PSAvoidUsingWMICmdlet', - 'PSAvoidUsingWriteHost', - 'PSAvoidUsingWMICmdlet', - 'PSDSC*', - 'PSMissingModuleManifestField', - 'PSPlaceCloseBrace', - 'PSPlaceOpenBrace', - 'PSProvideCommentHelp', - 'PSReservedCmdletChar', - 'PSReservedParams', - 'PSShouldProcess', - 'PSUseApprovedVerbs', - 'PSUseCmdletCorrectly', - 'PSUseConsistentIndentation', - 'PSUseConsistentWhitespace', - 'PSUseCorrectCasing', - 'PSUseDeclaredVarsMoreThanAssignments', - 'PSUsePSCredentialType', - 'PSUseShouldProcessForStateChangingFunctions', - #'PSUseSingularNouns', - 'PSUseLowerCaseForBooleanVariables', - 'PSUseLowerCaseForBuiltInFunctions', - 'PSUseLowerCaseForParameters', - 'PSUseTitleCaseForNonConstants', - 'PSUseTitleCaseFunctionNames', - 'PSUseTitleCaseVerbs', - 'PSUseTitleCaseForCmdletParameters', - 'PSUseTitleCaseForConstants' - ) - Rules = @{ - PSPlaceOpenBrace = @{ - Enable = $true - OnSameLine = $true - NewLineAfter = $true - IgnoreOneLineBlock = $true - } - - PSPlaceCloseBrace = @{ - Enable = $true - NewLineAfter = $false - IgnoreOneLineBlock = $true - NoEmptyLineBefore = $false - } - - PSUseConsistentIndentation = @{ - Enable = $true - Kind = 'space' - PipelineIndentation = 'IncreaseIndentationForFirstPipeline' - IndentationSize = 4 - } - - PSUseConsistentWhitespace = @{ - Enable = $true - CheckInnerBrace = $true - CheckOpenBrace = $true - CheckOpenParen = $true - CheckOperator = $true - CheckPipe = $true - CheckPipeForRedundantWhitespace = $false - CheckSeparator = $true - CheckParameter = $false - IgnoreAssignmentOperatorInsideHashTable = $true - } - - PSUseCorrectCasing = @{ - Enable = $true - } - - PSAvoidUsingDoubleQuotesForConstantString = @{ - Enable = $true - } - - PSAlignAssignmentStatement = @{ - Enable = $false - } - } + Severity = @( + 'Error', + 'Warning', + 'Information' + ) + IncludeRules = @( + 'PSAlignAssignmentStatement', + 'PSAvoidAssignmentToAutomaticVariable', + 'PSAvoidDefaultValueForMandatoryParameter', + 'PSAvoidDefaultValueSwitchParameter', + 'PSAvoidGlobalAliases', + 'PSAvoidGlobalFunctions', + 'PSAvoidGlobalVars', + 'PSAvoidInvokingEmptyMembers', + 'PSAvoidNullOrEmptyHelpMessageAttribute', + 'PSAvoidSemicolonsAsLineTerminators', + 'PSAvoidShouldContinueWithoutForce', + 'PSAvoidTrailingWhitespace', + 'PSAvoidUsingBrokenHashAlgorithms', + 'PSAvoidUsingCmdletAliases', + 'PSAvoidUsingComputerNameHardcoded', + 'PSAvoidUsingConvertToSecureStringWithPlainText', + 'PSAvoidUsingDeprecatedManifestFields', + 'PSAvoidUsingDoubleQuotesForConstantString', + 'PSAvoidUsingEmptyCatchBlock', + 'PSAvoidUsingInvokeExpression', + 'PSAvoidUsingPlainTextForPassword', + 'PSAvoidUsingPositionalParameters', + 'PSAvoidUsingUserNameAndPasswordParams', + 'PSAvoidUsingWMICmdlet', + 'PSAvoidUsingWriteHost', + 'PSDSC*', + 'PSMisleadingBacktick', + 'PSMissingModuleManifestField', + 'PSPlaceCloseBrace', + 'PSPlaceOpenBrace', + 'PSPossibleIncorrectComparisonWithNull', + 'PSPossibleIncorrectUsageOfAssignmentOperator', + 'PSPossibleIncorrectUsageOfRedirectionOperator' + 'PSProvideCommentHelp', + 'PSReservedCmdletChar', + 'PSReservedParams', + 'PSReviewUnusedParameter', + 'PSShouldProcess', + 'PSUseApprovedVerbs', + 'PSUseBOMForUnicodeEncodedFile', + 'PSUseCmdletCorrectly', + 'PSUseCompatibleCmdlets', + 'PSUseCompatibleCommands', + 'PSUseCompatibleSyntax', + 'PSUseCompatibleTypes', + 'PSUseConsistentIndentation', + 'PSUseConsistentWhitespace', + 'PSUseCorrectCasing', + 'PSUseDeclaredVarsMoreThanAssignments', + 'PSUseLiteralInitializerForHashtable', + 'PSUseOutputTypeCorrectly', + 'PSUseProcessBlockForPipelineCommand', + 'PSUsePSCredentialType', + 'PSUseShouldProcessForStateChangingFunctions', + 'PSUseLowerCaseForBooleanVariables', + 'PSUseLowerCaseForBuiltInFunctions', + 'PSUseLowerCaseForParameters', + 'PSUseSingularNouns', + 'PSUseTitleCaseForCmdletParameters', + 'PSUseTitleCaseForConstants', + 'PSUseTitleCaseForNonConstants', + 'PSUseTitleCaseFunctionNames', + 'PSUseTitleCaseVerbs' + ) + Rules = @{ + PSAlignAssignmentStatement = @{ + Enable = $false + } + PSPlaceCloseBrace = @{ + Enable = $true + NewLineAfter = $false + IgnoreOneLineBlock = $true + NoEmptyLineBefore = $false + } + PSPlaceOpenBrace = @{ + Enable = $true + OnSameLine = $true + NewLineAfter = $true + IgnoreOneLineBlock = $true + } + PSReviewUnusedParameter = @{ + Enable = $true + CommandsToTraverse = @( + 'New-NinjaOneQuery' + ) + } + PSUseCompatibleCmdlets = @{ + Enable = $true + Compatibility = @( + 'desktop-5.1.14393.206-windows', + 'core-6.1.0-windows', + 'core-6.1.0-linux', + 'core-6.1.0-linux-arm' + 'core-6.1.0-macos' + ) + } + PSUseCompatibleCommands = @{ + Enable = $false + } + PSUseCompatibleSyntax = @{ + Enable = $true + TargetVersions = @( + '5.1', + '6.0', + '7.0' + ) + } + PSUseConsistentIndentation = @{ + Enable = $true + Kind = 'tab' + PipelineIndentation = 'IncreaseIndentationForFirstPipeline' + } + PSUseConsistentWhitespace = @{ + Enable = $true + CheckInnerBrace = $true + CheckOpenBrace = $true + CheckOpenParen = $true + CheckOperator = $true + CheckPipe = $true + CheckPipeForRedundantWhitespace = $true + CheckSeparator = $true + CheckParameter = $true + IgnoreAssignmentOperatorInsideHashTable = $true + } + PSUseCorrectCasing = @{ + Enable = $true + } + PSUseSingularNouns = @{ + Enable = $false + } + PSAvoidUsingDoubleQuotesForConstantString = @{ + Enable = $true + } + } } \ No newline at end of file diff --git a/Source/Private/ConvertTo-UnixEpoch.ps1 b/Source/Private/ConvertTo-UnixEpoch.ps1 index 8a98639..1c22a6a 100644 --- a/Source/Private/ConvertTo-UnixEpoch.ps1 +++ b/Source/Private/ConvertTo-UnixEpoch.ps1 @@ -21,7 +21,7 @@ function ConvertTo-UnixEpoch { if ($DateTime -is [String]) { $DateTime = [DateTime]::Parse($DateTime) } elseif ($DateTime -is [Int]) { - (Get-Date 01.01.1970).AddSeconds($unixTimeStamp) + (Get-Date 01.01.1970).AddSeconds($unixTimeStamp) } elseif ($DateTime -is [DateTime]) { $DateTime = $DateTime } else { diff --git a/Source/Public/Connect-NinjaOne.ps1 b/Source/Public/Connect-NinjaOne.ps1 index 0e687e4..9f97f91 100644 --- a/Source/Public/Connect-NinjaOne.ps1 +++ b/Source/Public/Connect-NinjaOne.ps1 @@ -1,337 +1,337 @@ function Connect-NinjaOne { - <# - .SYNOPSIS - Creates a new connection to a NinjaOne instance. - .DESCRIPTION - Creates a new connection to a NinjaOne instance and stores this in a PowerShell Session. - .FUNCTIONALITY - NinjaOne - .EXAMPLE - PS> Connect-NinjaOne -Instance 'eu' -ClientId 'AAaaA1aaAaAA-aaAaaA11a1A-aA' -ClientSecret '00Z00zzZzzzZzZzZzZzZZZ0zZ0zzZ_0zzz0zZZzzZz0Z0ZZZzz0z0Z' -UseClientAuth + <# + .SYNOPSIS + Creates a new connection to a NinjaOne instance. + .DESCRIPTION + Creates a new connection to a NinjaOne instance and stores this in a PowerShell Session. + .FUNCTIONALITY + NinjaOne + .EXAMPLE + PS> Connect-NinjaOne -Instance 'eu' -ClientId 'AAaaA1aaAaAA-aaAaaA11a1A-aA' -ClientSecret '00Z00zzZzzzZzZzZzZzZZZ0zZ0zzZ_0zzz0zZZzzZz0Z0ZZZzz0z0Z' -UseClientAuth - This logs into NinjaOne using the client credentials flow. - .EXAMPLE - PS> Connect-NinjaOne -Instance 'eu' -ClientId 'AAaaA1aaAaAA-aaAaaA11a1A-aA' -ClientSecret '00Z00zzZzzzZzZzZzZzZZZ0zZ0zzZ_0zzz0zZZzzZz0Z0ZZZzz0z0Z' -Port 9090 -UseWebAuth + This logs into NinjaOne using the client credentials flow. + .EXAMPLE + PS> Connect-NinjaOne -Instance 'eu' -ClientId 'AAaaA1aaAaAA-aaAaaA11a1A-aA' -ClientSecret '00Z00zzZzzzZzZzZzZzZZZ0zZ0zzZ_0zzz0zZZzzZz0Z0ZZZzz0z0Z' -Port 9090 -UseWebAuth - This logs into NinjaOne using the authorization code flow. - .EXAMPLE - PS> Connect-NinjaOne -Instance 'eu' -ClientId 'AAaaA1aaAaAA-aaAaaA11a1A-aA' -ClientSecret '00Z00zzZzzzZzZzZzZzZZZ0zZ0zzZ_0zzz0zZZzzZz0Z0ZZZzz0z0Z' -RefreshToken 'a1a11a11-aa11-11a1-a111-a1a111aaa111.11AaaAaaa11aA-AA1aaaAAA111aAaaaaA1AAAA1_AAa' -UseTokenAuth + This logs into NinjaOne using the authorization code flow. + .EXAMPLE + PS> Connect-NinjaOne -Instance 'eu' -ClientId 'AAaaA1aaAaAA-aaAaaA11a1A-aA' -ClientSecret '00Z00zzZzzzZzZzZzZzZZZ0zZ0zzZ_0zzz0zZZzzZz0Z0ZZZzz0z0Z' -RefreshToken 'a1a11a11-aa11-11a1-a111-a1a111aaa111.11AaaAaaa11aA-AA1aaaAAA111aAaaaaA1AAAA1_AAa' -UseTokenAuth - This logs into NinjaOne using the refresh token flow. - .EXAMPLE - PS> Connect-NinjaOne -UseSecretManagement -VaultName 'NinjaOneVault' -WriteToSecretVault -Instance 'eu' -ClientId 'AAaaA1aaAaAA-aaAaaA11a1A-aA' -ClientSecret '00Z00zzZzzzZzZzZzZzZZZ0zZ0zzZ_0zzz0zZZzzZz0Z0ZZZzz0z0Z' -UseClientAuth + This logs into NinjaOne using the refresh token flow. + .EXAMPLE + PS> Connect-NinjaOne -UseSecretManagement -VaultName 'NinjaOneVault' -WriteToSecretVault -Instance 'eu' -ClientId 'AAaaA1aaAaAA-aaAaaA11a1A-aA' -ClientSecret '00Z00zzZzzzZzZzZzZzZZZ0zZ0zzZ_0zzz0zZZzzZz0Z0ZZZzz0z0Z' -UseClientAuth - This logs into NinjaOne using the client credentials flow and writes the connection information to the secret vault. - .EXAMPLE - PS> Connect-NinjaOne -UseSecretManagement -VaultName 'NinjaOneVault' -ReadFromSecretVault + This logs into NinjaOne using the client credentials flow and writes the connection information to the secret vault. + .EXAMPLE + PS> Connect-NinjaOne -UseSecretManagement -VaultName 'NinjaOneVault' -ReadFromSecretVault - This reads the connection information from the secret vault. - .OUTPUTS - Sets two script-scoped variables to hold connection and authentication information. - .LINK - https://docs.homotechsual.dev/modules/ninjaone/commandlets/Connect/ninjaone - #> - [CmdletBinding( DefaultParameterSetName = 'Authorisation Code' )] - [OutputType([System.Void])] - [Alias('cno')] - [MetadataAttribute('IGNORE')] - Param ( - # Use the "Authorisation Code" flow with your web browser. - [Parameter( Mandatory, ParameterSetName = 'Authorisation Code')] - [Parameter( ParameterSetName = 'Secret Vault Write' )] - [Switch]$UseWebAuth, - # Use the "Token Authentication" flow - useful if you already have a refresh token. - [Parameter( Mandatory, ParameterSetName = 'Token Authentication' )] - [Parameter( ParameterSetName = 'Secret Vault Write' )] - [switch]$UseTokenAuth, - # Use the "Client Credentials" flow - useful if you already have a client ID and secret. - [Parameter( Mandatory, ParameterSetName = 'Client Credentials' )] - [Parameter( ParameterSetName = 'Secret Vault Write' )] - [switch]$UseClientAuth, - # The NinjaOne instance to connect to. Choose from 'eu', 'oc' or 'us'. - [Parameter( Mandatory, ParameterSetName = 'Authorisation Code' )] - [Parameter( Mandatory, ParameterSetName = 'Token Authentication' )] - [Parameter( Mandatory, ParameterSetName = 'Client Credentials' )] - [Parameter( Mandatory, ParameterSetName = 'Secret Vault Write' )] - [ValidateSet('eu', 'oc', 'us', 'ca', 'us2')] - [string]$Instance, - # The Client Id for the application configured in NinjaOne. - [Parameter( Mandatory, ParameterSetName = 'Authorisation Code' )] - [Parameter( Mandatory, ParameterSetName = 'Token Authentication' )] - [Parameter( Mandatory, ParameterSetName = 'Client Credentials' )] - [Parameter( Mandatory, ParameterSetName = 'Secret Vault Write' )] - [String]$ClientId, - # The Client Secret for the application configured in NinjaOne. - [Parameter( Mandatory, ParameterSetName = 'Authorisation Code' )] - [Parameter( Mandatory, ParameterSetName = 'Token Authentication' )] - [Parameter( Mandatory, ParameterSetName = 'Client Credentials' )] - [Parameter( Mandatory, ParameterSetName = 'Secret Vault Write' )] - [String]$ClientSecret, - # The API scopes to request, if this isn't passed the scope is assumed to be "all". Pass a string or array of strings. Limited by the scopes granted to the application in NinjaOne. - [Parameter( ParameterSetName = 'Authorisation Code' )] - [Parameter( ParameterSetName = 'Token Authentication' )] - [Parameter( ParameterSetName = 'Client Credentials' )] - [Parameter( ParameterSetName = 'Secret Vault Write' )] - [ValidateSet('monitoring', 'management', 'control', 'offline_access')] - [String[]]$Scopes, - # The redirect URI to use. If not set defaults to 'http://localhost'. Should be a full URI e.g. https://redirect.example.uk:9090/auth - [Parameter( ParameterSetName = 'Authorisation Code' )] - [Parameter( ParameterSetName = 'Secret Vault Write' )] - [URI]$RedirectURL, - # The port to use for the redirect URI. Must match with the configuration set in NinjaOne. If not set defaults to '9090'. - [Parameter( ParameterSetName = 'Authorisation Code' )] - [Parameter( ParameterSetName = 'Secret Vault Write' )] - [Int]$Port = 9090, - # The refresh token to use for "Token Authentication" flow. - [Parameter( ParameterSetName = 'Token Authentication' )] - [Parameter( ParameterSetName = 'Secret Vault Write' )] - [String]$RefreshToken, - # Output the tokens - useful when using "Authorisation Code" flow - to use with "Token Authentication" flow. - [Parameter( ParameterSetName = 'Authorisation Code' )] - [Parameter( ParameterSetName = 'Token Authentication' )] - [Parameter( ParameterSetName = 'Client Credentials' )] - [Switch]$ShowTokens, - # Use the secret management module to retrieve credentials and store tokens. Check the docs on setting up the secret management module at https://docs.homotechsual.dev/common/secretmanagement. - [Parameter( ParameterSetName = 'Authorisation Code' )] - [Parameter( ParameterSetName = 'Token Authentication' )] - [Parameter( ParameterSetName = 'Client Credentials' )] - [Parameter( Mandatory, ParameterSetName = 'Secret Vault Write' )] - [Parameter( Mandatory, ParameterSetName = 'Secret Vault Read' )] - [Switch]$UseSecretManagement, - # The name of the secret vault to use. - [Parameter( ParameterSetName = 'Authorisation Code' )] - [Parameter( ParameterSetName = 'Token Authentication' )] - [Parameter( ParameterSetName = 'Client Credentials' )] - [Parameter( Mandatory, ParameterSetName = 'Secret Vault Write' )] - [Parameter( Mandatory, ParameterSetName = 'Secret Vault Read' )] - [String]$VaultName, - # Write updated credentials to secret management vault. - [Parameter( ParameterSetName = 'Authorisation Code' )] - [Parameter( ParameterSetName = 'Token Authentication' )] - [Parameter( ParameterSetName = 'Client Credentials' )] - [Parameter( Mandatory, ParameterSetName = 'Secret Vault Write' )] - [Parameter( ParameterSetName = 'Secret Vault Read')] - [Switch]$WriteToSecretVault, - # Read the authentication information from secret management vault. - [Parameter( ParameterSetName = 'Secret Vault Read' )] - [Switch]$ReadFromSecretVault, - # The prefix to add to the name of the secrets stored in the secret vault. - [Parameter( ParameterSetName = 'Secret Vault Write' )] - [Parameter( ParameterSetName = 'Secret Vault Read' )] - [String]$SecretPrefix = 'NinjaOne' - ) - process { - # Run the pre-flight check. - Invoke-NinjaOnePreFlightCheck -SkipConnectionChecks - # Test for secret management module. - if ($UseSecretManagement -or $Script:NRAPIConnectionInformation.UseSecretManagement) { - if (-not (Get-Module -Name 'Microsoft.PowerShell.SecretManagement' -ListAvailable)) { - Write-Error 'Secret management module not installed, please install the module and try again.' - exit 1 - } - if (-not (Get-SecretVault)) { - Write-Error 'No secret vaults found, please create a secret vault and try again.' - exit 1 - } - if ($ReadFromSecretVault -or $Script:NRAPIConnectionInformation.ReadFromSecretVault) { - Write-Verbose 'Reading authentication information from secret vault.' - Get-NinjaOneSecrets -VaultName $VaultName - } - } - # Set the default scopes if they're not passed. - if ($UseClientAuth -and $null -eq $Scopes) { - Write-Verbose 'Setting default scopes for client credentials auth.' - $Scopes = @('monitoring', 'management', 'control') - } elseif (($UseWebAuth -or $UseTokenAuth) -and $null -eq $Scopes) { - Write-Verbose 'Setting default scopes for authorisation code or token auth.' - $Scopes = @('monitoring', 'management', 'control', 'offline_access') - } - # Convert scopes to space separated string if it's an array. - if ($Scopes -is [System.Array]) { - Write-Verbose ('Scopes are an array, converting to space separated string.') - $AuthScopes = $Scopes -Join ' ' - } else { - Write-Verbose ('Scopes are a string, using as is.') - $AuthScopes = $Scopes - } - # Get the NinjaOne instance URL. - if ($Instance) { - Write-Verbose "Using instance $($Instance) with URL $($Script:NRAPIInstances[$Instance])" - $URL = $Script:NRAPIInstances[$Instance] - } - # Generate a GUID to serve as our state validator. - $GUID = ([GUID]::NewGuid()).Guid - # Build the redirect URI, if we need one. - if ($RedirectURL) { - $RedirectURI = [System.UriBuilder]$RedirectURL - } else { - $RedirectURI = New-Object System.UriBuilder -ArgumentList 'http', 'localhost', $Port - } - # Determine the authentication mode. - if ($UseWebAuth -and $Scopes -notcontains 'offline_access') { - $AuthMode = 'Authorisation Code' - } elseif ($UseTokenAuth -or ($UseWebAuth -and $Scopes -contains 'offline_access')) { - $AuthMode = 'Token Authentication' - } elseif ($UseClientAuth) { - $AuthMode = 'Client Credentials' - } - # Build a script-scoped variable to hold the connection information. - if ($null -eq $Script:NRAPIConnectionInformation) { - $ConnectionInformation = @{ - AuthMode = $AuthMode - URL = $URL - Instance = $Instance - ClientId = $ClientId - ClientSecret = $ClientSecret - AuthListenerPort = $Port - AuthScopes = $AuthScopes - RedirectURI = $RedirectURI - UseSecretManagement = $UseSecretManagement - VaultName = $VaultName - WriteToSecretVault = $WriteToSecretVault - SecretPrefix = $SecretPrefix - } - Set-Variable -Name 'NRAPIConnectionInformation' -Value $ConnectionInformation -Visibility Private -Scope Script -Force - } - Write-Verbose "Connection information set to: $($Script:NRAPIConnectionInformation | Format-List | Out-String)" - if ($null -eq $Script:NRAPIAuthenticationInformation) { - $AuthenticationInformation = [HashTable]@{} - # Set a script-scoped variable to hold authentication information. - Set-Variable -Name 'NRAPIAuthenticationInformation' -Value $AuthenticationInformation -Visibility Private -Scope Script -Force - } - if ($Script.NRAPIConnectionInformation.AuthMode -eq 'Token Authentication' -and $null -eq $UseTokenAuth) { - $UseTokenAuth = $true - } - if ($Script.NRAPIConnectionInformation.AuthMode -eq 'Client Credentials' -and $null -eq $UseClientAuth) { - $UseClientAuth = $true - } - if ($Script.NRAPIConnectionInformation.AuthMode -eq 'Authorisation Code' -and $null -eq $UseWebAuth) { - $UseWebAuth = $true - } - if ($UseWebAuth) { - # NinjaOne authorisation request query params. - $AuthRequestParams = @{ - response_type = 'code' - client_id = $Script:NRAPIConnectionInformation.ClientId - client_secret = $Script:NRAPIConnectionInformation.ClientSecret - redirect_uri = $Script:NRAPIConnectionInformation.RedirectURI.ToString() - state = $GUID - } - if ($Script:NRAPIConnectionInformation.AuthScopes) { - $AuthRequestParams.scope = $Script:NRAPIConnectionInformation.AuthScopes - } - # Build the authentication URI. - # Start with the query string. - $AuthRequestQuery = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) - $AuthRequestParams.GetEnumerator() | ForEach-Object { - $AuthRequestQuery.Add($_.Key, $_.Value) - } - # Now the authentication URI - $AuthRequestURI = [System.UriBuilder]$URL - $AuthRequestURI.Path = 'oauth/authorize' - $AuthRequestURI.Query = $AuthRequestQuery.ToString() - Write-Verbose "Authentication request query string is $($AuthRequestQuery.ToString())" - try { - $OAuthListenerParams = @{ - OpenURI = $AuthRequestURI - } - if ($VerbosePreference = 'Continue') { - $OAuthListenerParams.Verbose = $true - } - if ($DebugPreference = 'Continue') { - $OAuthListenerParams.Debug = $true - } - $OAuthListenerResponse = Start-OAuthHTTPListener @OAuthListenerParams - $Script:NRAPIAuthenticationInformation.Code = $OAuthListenerResponse.Code - } catch { - New-NinjaOneError -ErrorRecord $_ - } - } - if (($UseTokenAuth) -or ($OAuthListenerResponse.GotAuthorisationCode) -or ($UseClientAuth)) { - Write-Verbose 'Getting authentication token.' - try { - if ($OAuthListenerResponse.GotAuthorisationCode) { - Write-Verbose 'Using token authentication.' - $TokenRequestBody = @{ - grant_type = 'authorization_code' - client_id = $Script:NRAPIConnectionInformation.ClientId - client_secret = $Script:NRAPIConnectionInformation.ClientSecret - code = $Script:NRAPIAuthenticationInformation.Code - redirect_uri = $Script:NRAPIConnectionInformation.RedirectURI.toString() - scope = $Script:NRAPIConnectionInformation.AuthScopes - } - } elseif ($UseTokenAuth) { - Write-Verbose 'Using refresh token.' - $TokenRequestBody = @{ - grant_type = 'refresh_token' - client_id = $Script:NRAPIConnectionInformation.ClientId - client_secret = $Script:NRAPIConnectionInformation.ClientSecret - refresh_token = $RefreshToken - scope = $Script:NRAPIConnectionInformation.AuthScopes - } - } elseif ($UseClientAuth) { - Write-Verbose 'Using client authentication.' - $TokenRequestBody = @{ - grant_type = 'client_credentials' - client_id = $Script:NRAPIConnectionInformation.ClientId - client_secret = $Script:NRAPIConnectionInformation.ClientSecret - redirect_uri = $Script:NRAPIConnectionInformation.RedirectURI.toString() - scope = $Script:NRAPIConnectionInformation.AuthScopes - } - } - Write-Verbose "Token request body is $($TokenRequestBody | Format-List | Out-String)" - # Using our authorisation code or refresh token let's get an auth token. - $TokenRequestUri = [System.UriBuilder]$URL - $TokenRequestUri.Path = 'oauth/token' - Write-Verbose "Making token request to $($TokenRequestUri.ToString())" - $TokenRequestParams = @{ - Uri = $TokenRequestUri.ToString() - Method = 'POST' - Body = $TokenRequestBody - ContentType = 'application/x-www-form-urlencoded' - } - $TokenResult = Invoke-WebRequest @TokenRequestParams - $TokenPayload = $TokenResult.Content | ConvertFrom-Json - Write-Verbose "Token payload is $($TokenPayload | Format-List | Out-String)" - # Update our script-scoped NRAPIAuthenticationInformation variable with the token. - $Script:NRAPIAuthenticationInformation.Type = $TokenPayload.token_type - $Script:NRAPIAuthenticationInformation.Access = $TokenPayload.access_token - $Script:NRAPIAuthenticationInformation.Expires = Get-TokenExpiry -ExpiresIn $TokenPayload.expires_in - $Script:NRAPIAuthenticationInformation.Refresh = $TokenPayload.refresh_token - Write-Verbose 'Got authentication token information from NinjaOne.' - Write-Verbose "Authentication information set to: $($Script:NRAPIAuthenticationInformation | Format-List | Out-String)" - if ($ShowTokens) { - Write-Output '================ Auth Tokens ================' - Write-Output $($Script:NRAPIAuthenticationInformation | Format-Table -AutoSize) - Write-Output ' SAVE THESE IN A SECURE LOCATION ' - } - } catch { - throw - } - } - # If we're using secret management, store the authentication information we need. - if ($Script:NRAPIConnectionInformation.UseSecretManagement -and $Script:NRAPIConnectionInformation.WriteToSecretVault) { - $SecretManagementParams = @{ - AuthMode = $Script:NRAPIConnectionInformation.AuthMode - URL = $Script:NRAPIConnectionInformation.URL - Instance = $Script:NRAPIConnectionInformation.Instance - ClientId = $Script:NRAPIConnectionInformation.ClientId - ClientSecret = $Script:NRAPIConnectionInformation.ClientSecret - AuthListenerPort = $Script:NRAPIConnectionInformation.AuthListenerPort - AuthScopes = $Script:NRAPIConnectionInformation.AuthScopes - RedirectURI = $Script:NRAPIConnectionInformation.RedirectURI.ToString() - UseSecretManagement = $Script:NRAPIConnectionInformation.UseSecretManagement - VaultName = $Script:NRAPIConnectionInformation.VaultName - WriteToSecretVault = $Script:NRAPIConnectionInformation.WriteToSecretVault - ReadFromSecretVault = $Script:NRAPIConnectionInformation.ReadFromSecretVault - Type = $Script:NRAPIAuthenticationInformation.Type - Access = $Script:NRAPIAuthenticationInformation.Access - Expires = $Script:NRAPIAuthenticationInformation.Expires - Refresh = $Script:NRAPIAuthenticationInformation.Refresh - SecretPrefix = $Script:NRAPIConnectionInformation.SecretPrefix - } - Write-Verbose 'Using secret management to store credentials.' - Set-NinjaOneSecrets @SecretManagementParams - } - } + This reads the connection information from the secret vault. + .OUTPUTS + Sets two script-scoped variables to hold connection and authentication information. + .LINK + https://docs.homotechsual.dev/modules/ninjaone/commandlets/Connect/ninjaone + #> + [CmdletBinding( DefaultParameterSetName = 'Authorisation Code' )] + [OutputType([System.Void])] + [Alias('cno')] + [MetadataAttribute('IGNORE')] + Param ( + # Use the "Authorisation Code" flow with your web browser. + [Parameter( Mandatory, ParameterSetName = 'Authorisation Code')] + [Parameter( ParameterSetName = 'Secret Vault Write' )] + [Switch]$UseWebAuth, + # Use the "Token Authentication" flow - useful if you already have a refresh token. + [Parameter( Mandatory, ParameterSetName = 'Token Authentication' )] + [Parameter( ParameterSetName = 'Secret Vault Write' )] + [switch]$UseTokenAuth, + # Use the "Client Credentials" flow - useful if you already have a client ID and secret. + [Parameter( Mandatory, ParameterSetName = 'Client Credentials' )] + [Parameter( ParameterSetName = 'Secret Vault Write' )] + [switch]$UseClientAuth, + # The NinjaOne instance to connect to. Choose from 'eu', 'oc' or 'us'. + [Parameter( Mandatory, ParameterSetName = 'Authorisation Code' )] + [Parameter( Mandatory, ParameterSetName = 'Token Authentication' )] + [Parameter( Mandatory, ParameterSetName = 'Client Credentials' )] + [Parameter( Mandatory, ParameterSetName = 'Secret Vault Write' )] + [ValidateSet('eu', 'oc', 'us', 'ca', 'us2')] + [string]$Instance, + # The Client Id for the application configured in NinjaOne. + [Parameter( Mandatory, ParameterSetName = 'Authorisation Code' )] + [Parameter( Mandatory, ParameterSetName = 'Token Authentication' )] + [Parameter( Mandatory, ParameterSetName = 'Client Credentials' )] + [Parameter( Mandatory, ParameterSetName = 'Secret Vault Write' )] + [String]$ClientId, + # The Client Secret for the application configured in NinjaOne. + [Parameter( Mandatory, ParameterSetName = 'Authorisation Code' )] + [Parameter( Mandatory, ParameterSetName = 'Token Authentication' )] + [Parameter( Mandatory, ParameterSetName = 'Client Credentials' )] + [Parameter( Mandatory, ParameterSetName = 'Secret Vault Write' )] + [String]$ClientSecret, + # The API scopes to request, if this isn't passed the scope is assumed to be "all". Pass a string or array of strings. Limited by the scopes granted to the application in NinjaOne. + [Parameter( ParameterSetName = 'Authorisation Code' )] + [Parameter( ParameterSetName = 'Token Authentication' )] + [Parameter( ParameterSetName = 'Client Credentials' )] + [Parameter( ParameterSetName = 'Secret Vault Write' )] + [ValidateSet('monitoring', 'management', 'control', 'offline_access')] + [String[]]$Scopes, + # The redirect URI to use. If not set defaults to 'http://localhost'. Should be a full URI e.g. https://redirect.example.uk:9090/auth + [Parameter( ParameterSetName = 'Authorisation Code' )] + [Parameter( ParameterSetName = 'Secret Vault Write' )] + [URI]$RedirectURL, + # The port to use for the redirect URI. Must match with the configuration set in NinjaOne. If not set defaults to '9090'. + [Parameter( ParameterSetName = 'Authorisation Code' )] + [Parameter( ParameterSetName = 'Secret Vault Write' )] + [Int]$Port = 9090, + # The refresh token to use for "Token Authentication" flow. + [Parameter( ParameterSetName = 'Token Authentication' )] + [Parameter( ParameterSetName = 'Secret Vault Write' )] + [String]$RefreshToken, + # Output the tokens - useful when using "Authorisation Code" flow - to use with "Token Authentication" flow. + [Parameter( ParameterSetName = 'Authorisation Code' )] + [Parameter( ParameterSetName = 'Token Authentication' )] + [Parameter( ParameterSetName = 'Client Credentials' )] + [Switch]$ShowTokens, + # Use the secret management module to retrieve credentials and store tokens. Check the docs on setting up the secret management module at https://docs.homotechsual.dev/common/secretmanagement. + [Parameter( ParameterSetName = 'Authorisation Code' )] + [Parameter( ParameterSetName = 'Token Authentication' )] + [Parameter( ParameterSetName = 'Client Credentials' )] + [Parameter( Mandatory, ParameterSetName = 'Secret Vault Write' )] + [Parameter( Mandatory, ParameterSetName = 'Secret Vault Read' )] + [Switch]$UseSecretManagement, + # The name of the secret vault to use. + [Parameter( ParameterSetName = 'Authorisation Code' )] + [Parameter( ParameterSetName = 'Token Authentication' )] + [Parameter( ParameterSetName = 'Client Credentials' )] + [Parameter( Mandatory, ParameterSetName = 'Secret Vault Write' )] + [Parameter( Mandatory, ParameterSetName = 'Secret Vault Read' )] + [String]$VaultName, + # Write updated credentials to secret management vault. + [Parameter( ParameterSetName = 'Authorisation Code' )] + [Parameter( ParameterSetName = 'Token Authentication' )] + [Parameter( ParameterSetName = 'Client Credentials' )] + [Parameter( Mandatory, ParameterSetName = 'Secret Vault Write' )] + [Parameter( ParameterSetName = 'Secret Vault Read')] + [Switch]$WriteToSecretVault, + # Read the authentication information from secret management vault. + [Parameter( ParameterSetName = 'Secret Vault Read' )] + [Switch]$ReadFromSecretVault, + # The prefix to add to the name of the secrets stored in the secret vault. + [Parameter( ParameterSetName = 'Secret Vault Write' )] + [Parameter( ParameterSetName = 'Secret Vault Read' )] + [String]$SecretPrefix = 'NinjaOne' + ) + process { + # Run the pre-flight check. + Invoke-NinjaOnePreFlightCheck -SkipConnectionChecks + # Test for secret management module. + if ($UseSecretManagement -or $Script:NRAPIConnectionInformation.UseSecretManagement) { + if (-not (Get-Module -Name 'Microsoft.PowerShell.SecretManagement' -ListAvailable)) { + Write-Error 'Secret management module not installed, please install the module and try again.' + exit 1 + } + if (-not (Get-SecretVault)) { + Write-Error 'No secret vaults found, please create a secret vault and try again.' + exit 1 + } + if ($ReadFromSecretVault -or $Script:NRAPIConnectionInformation.ReadFromSecretVault) { + Write-Verbose 'Reading authentication information from secret vault.' + Get-NinjaOneSecrets -VaultName $VaultName + } + } + # Set the default scopes if they're not passed. + if ($UseClientAuth -and $null -eq $Scopes) { + Write-Verbose 'Setting default scopes for client credentials auth.' + $Scopes = @('monitoring', 'management', 'control') + } elseif (($UseWebAuth -or $UseTokenAuth) -and $null -eq $Scopes) { + Write-Verbose 'Setting default scopes for authorisation code or token auth.' + $Scopes = @('monitoring', 'management', 'control', 'offline_access') + } + # Convert scopes to space separated string if it's an array. + if ($Scopes -is [System.Array]) { + Write-Verbose ('Scopes are an array, converting to space separated string.') + $AuthScopes = $Scopes -Join ' ' + } else { + Write-Verbose ('Scopes are a string, using as is.') + $AuthScopes = $Scopes + } + # Get the NinjaOne instance URL. + if ($Instance) { + Write-Verbose "Using instance $($Instance) with URL $($Script:NRAPIInstances[$Instance])" + $URL = $Script:NRAPIInstances[$Instance] + } + # Generate a GUID to serve as our state validator. + $GUID = ([GUID]::NewGuid()).Guid + # Build the redirect URI, if we need one. + if ($RedirectURL) { + $RedirectURI = [System.UriBuilder]$RedirectURL + } else { + $RedirectURI = New-Object System.UriBuilder -ArgumentList 'http', 'localhost', $Port + } + # Determine the authentication mode. + if ($UseWebAuth -and $Scopes -notcontains 'offline_access') { + $AuthMode = 'Authorisation Code' + } elseif ($UseTokenAuth -or ($UseWebAuth -and $Scopes -contains 'offline_access')) { + $AuthMode = 'Token Authentication' + } elseif ($UseClientAuth) { + $AuthMode = 'Client Credentials' + } + # Build a script-scoped variable to hold the connection information. + if ($null -eq $Script:NRAPIConnectionInformation) { + $ConnectionInformation = @{ + AuthMode = $AuthMode + URL = $URL + Instance = $Instance + ClientId = $ClientId + ClientSecret = $ClientSecret + AuthListenerPort = $Port + AuthScopes = $AuthScopes + RedirectURI = $RedirectURI + UseSecretManagement = $UseSecretManagement + VaultName = $VaultName + WriteToSecretVault = $WriteToSecretVault + SecretPrefix = $SecretPrefix + } + Set-Variable -Name 'NRAPIConnectionInformation' -Value $ConnectionInformation -Visibility Private -Scope Script -Force + } + Write-Verbose "Connection information set to: $($Script:NRAPIConnectionInformation | Format-List | Out-String)" + if ($null -eq $Script:NRAPIAuthenticationInformation) { + $AuthenticationInformation = [HashTable]@{} + # Set a script-scoped variable to hold authentication information. + Set-Variable -Name 'NRAPIAuthenticationInformation' -Value $AuthenticationInformation -Visibility Private -Scope Script -Force + } + if ($Script.NRAPIConnectionInformation.AuthMode -eq 'Token Authentication' -and $null -eq $UseTokenAuth) { + $UseTokenAuth = $true + } + if ($Script.NRAPIConnectionInformation.AuthMode -eq 'Client Credentials' -and $null -eq $UseClientAuth) { + $UseClientAuth = $true + } + if ($Script.NRAPIConnectionInformation.AuthMode -eq 'Authorisation Code' -and $null -eq $UseWebAuth) { + $UseWebAuth = $true + } + if ($UseWebAuth) { + # NinjaOne authorisation request query params. + $AuthRequestParams = @{ + response_type = 'code' + client_id = $Script:NRAPIConnectionInformation.ClientId + client_secret = $Script:NRAPIConnectionInformation.ClientSecret + redirect_uri = $Script:NRAPIConnectionInformation.RedirectURI.ToString() + state = $GUID + } + if ($Script:NRAPIConnectionInformation.AuthScopes) { + $AuthRequestParams.scope = $Script:NRAPIConnectionInformation.AuthScopes + } + # Build the authentication URI. + # Start with the query string. + $AuthRequestQuery = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) + $AuthRequestParams.GetEnumerator() | ForEach-Object { + $AuthRequestQuery.Add($_.Key, $_.Value) + } + # Now the authentication URI + $AuthRequestURI = [System.UriBuilder]$URL + $AuthRequestURI.Path = 'oauth/authorize' + $AuthRequestURI.Query = $AuthRequestQuery.ToString() + Write-Verbose "Authentication request query string is $($AuthRequestQuery.ToString())" + try { + $OAuthListenerParams = @{ + OpenURI = $AuthRequestURI + } + if ($VerbosePreference -eq 'Continue') { + $OAuthListenerParams.Verbose = $true + } + if ($DebugPreference -eq 'Continue') { + $OAuthListenerParams.Debug = $true + } + $OAuthListenerResponse = Start-OAuthHTTPListener @OAuthListenerParams + $Script:NRAPIAuthenticationInformation.Code = $OAuthListenerResponse.Code + } catch { + New-NinjaOneError -ErrorRecord $_ + } + } + if (($UseTokenAuth) -or ($OAuthListenerResponse.GotAuthorisationCode) -or ($UseClientAuth)) { + Write-Verbose 'Getting authentication token.' + try { + if ($OAuthListenerResponse.GotAuthorisationCode) { + Write-Verbose 'Using token authentication.' + $TokenRequestBody = @{ + grant_type = 'authorization_code' + client_id = $Script:NRAPIConnectionInformation.ClientId + client_secret = $Script:NRAPIConnectionInformation.ClientSecret + code = $Script:NRAPIAuthenticationInformation.Code + redirect_uri = $Script:NRAPIConnectionInformation.RedirectURI.toString() + scope = $Script:NRAPIConnectionInformation.AuthScopes + } + } elseif ($UseTokenAuth) { + Write-Verbose 'Using refresh token.' + $TokenRequestBody = @{ + grant_type = 'refresh_token' + client_id = $Script:NRAPIConnectionInformation.ClientId + client_secret = $Script:NRAPIConnectionInformation.ClientSecret + refresh_token = $RefreshToken + scope = $Script:NRAPIConnectionInformation.AuthScopes + } + } elseif ($UseClientAuth) { + Write-Verbose 'Using client authentication.' + $TokenRequestBody = @{ + grant_type = 'client_credentials' + client_id = $Script:NRAPIConnectionInformation.ClientId + client_secret = $Script:NRAPIConnectionInformation.ClientSecret + redirect_uri = $Script:NRAPIConnectionInformation.RedirectURI.toString() + scope = $Script:NRAPIConnectionInformation.AuthScopes + } + } + Write-Verbose "Token request body is $($TokenRequestBody | Format-List | Out-String)" + # Using our authorisation code or refresh token let's get an auth token. + $TokenRequestUri = [System.UriBuilder]$URL + $TokenRequestUri.Path = 'oauth/token' + Write-Verbose "Making token request to $($TokenRequestUri.ToString())" + $TokenRequestParams = @{ + Uri = $TokenRequestUri.ToString() + Method = 'POST' + Body = $TokenRequestBody + ContentType = 'application/x-www-form-urlencoded' + } + $TokenResult = Invoke-WebRequest @TokenRequestParams + $TokenPayload = $TokenResult.Content | ConvertFrom-Json + Write-Verbose "Token payload is $($TokenPayload | Format-List | Out-String)" + # Update our script-scoped NRAPIAuthenticationInformation variable with the token. + $Script:NRAPIAuthenticationInformation.Type = $TokenPayload.token_type + $Script:NRAPIAuthenticationInformation.Access = $TokenPayload.access_token + $Script:NRAPIAuthenticationInformation.Expires = Get-TokenExpiry -ExpiresIn $TokenPayload.expires_in + $Script:NRAPIAuthenticationInformation.Refresh = $TokenPayload.refresh_token + Write-Verbose 'Got authentication token information from NinjaOne.' + Write-Verbose "Authentication information set to: $($Script:NRAPIAuthenticationInformation | Format-List | Out-String)" + if ($ShowTokens) { + Write-Output '================ Auth Tokens ================' + Write-Output $($Script:NRAPIAuthenticationInformation | Format-Table -AutoSize) + Write-Output ' SAVE THESE IN A SECURE LOCATION ' + } + } catch { + throw + } + } + # If we're using secret management, store the authentication information we need. + if ($Script:NRAPIConnectionInformation.UseSecretManagement -and $Script:NRAPIConnectionInformation.WriteToSecretVault) { + $SecretManagementParams = @{ + AuthMode = $Script:NRAPIConnectionInformation.AuthMode + URL = $Script:NRAPIConnectionInformation.URL + Instance = $Script:NRAPIConnectionInformation.Instance + ClientId = $Script:NRAPIConnectionInformation.ClientId + ClientSecret = $Script:NRAPIConnectionInformation.ClientSecret + AuthListenerPort = $Script:NRAPIConnectionInformation.AuthListenerPort + AuthScopes = $Script:NRAPIConnectionInformation.AuthScopes + RedirectURI = $Script:NRAPIConnectionInformation.RedirectURI.ToString() + UseSecretManagement = $Script:NRAPIConnectionInformation.UseSecretManagement + VaultName = $Script:NRAPIConnectionInformation.VaultName + WriteToSecretVault = $Script:NRAPIConnectionInformation.WriteToSecretVault + ReadFromSecretVault = $Script:NRAPIConnectionInformation.ReadFromSecretVault + Type = $Script:NRAPIAuthenticationInformation.Type + Access = $Script:NRAPIAuthenticationInformation.Access + Expires = $Script:NRAPIAuthenticationInformation.Expires + Refresh = $Script:NRAPIAuthenticationInformation.Refresh + SecretPrefix = $Script:NRAPIConnectionInformation.SecretPrefix + } + Write-Verbose 'Using secret management to store credentials.' + Set-NinjaOneSecrets @SecretManagementParams + } + } } \ No newline at end of file diff --git a/Source/Public/Get/Entities/Get-NinjaOneActivities.ps1 b/Source/Public/Get/Entities/Get-NinjaOneActivities.ps1 index 8ee246c..5016ff0 100644 --- a/Source/Public/Get/Entities/Get-NinjaOneActivities.ps1 +++ b/Source/Public/Get/Entities/Get-NinjaOneActivities.ps1 @@ -58,7 +58,7 @@ function Get-NinjaOneActivities { #> [CmdletBinding()] [OutputType([Object])] - [Alias('gnoac')] + [Alias('gnoac', 'Get-NinjaOneActivity')] [MetadataAttribute( '/v2/device/{id}/activities', 'get', @@ -164,7 +164,6 @@ function Get-NinjaOneActivities { } process { try { - if ($deviceId) { Write-Verbose 'Getting device from NinjaOne API.' $Device = Get-NinjaOneDevices -deviceId $deviceId diff --git a/Source/Public/Get/Entities/Get-NinjaOneAlerts.ps1 b/Source/Public/Get/Entities/Get-NinjaOneAlerts.ps1 index 0945584..846cff9 100644 --- a/Source/Public/Get/Entities/Get-NinjaOneAlerts.ps1 +++ b/Source/Public/Get/Entities/Get-NinjaOneAlerts.ps1 @@ -10,7 +10,7 @@ function Get-NinjaOneAlerts { Alerts .EXAMPLE PS> Get-NinjaOneAlerts - + Gets all alerts. .EXAMPLE PS> Get-NinjaOneAlerts -sourceType 'CONDITION_CUSTOM_FIELD' @@ -20,14 +20,14 @@ function Get-NinjaOneAlerts { PS> Get-NinjaOneAlerts -deviceFilter 'status eq APPROVED' Gets alerts for all approved devices. - .OUTPUTS + .OUTPUTS A powershell object containing the response. .LINK https://docs.homotechsual.dev/modules/ninjaone/commandlets/Get/alerts #> [CmdletBinding()] [OutputType([Object])] - [Alias('gnoal')] + [Alias('gnoal', 'Get-NinjaOneAlert')] [MetadataAttribute( '/v2/alerts', 'get', diff --git a/Source/Public/New/New-NinjaOneInstaller.ps1 b/Source/Public/New/New-NinjaOneInstaller.ps1 index 7a4485d..732b9d7 100644 --- a/Source/Public/New/New-NinjaOneInstaller.ps1 +++ b/Source/Public/New/New-NinjaOneInstaller.ps1 @@ -1,72 +1,72 @@ function New-NinjaOneInstaller { - <# - .SYNOPSIS - Creates a new installer using the NinjaOne API. - .DESCRIPTION - Create a new installer download link using the NinjaOne v2 API. - .FUNCTIONALITY - Installer - .OUTPUTS - A powershell object containing the response. - .LINK - https://docs.homotechsual.dev/modules/ninjaone/commandlets/New/installer - #> - [CmdletBinding( SupportsShouldProcess, ConfirmImpact = 'Medium' )] - [OutputType([Object])] - [Alias('nnoi')] - [MetadataAttribute( - '/v2/organization/generate-installer', - 'post' - )] - [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'Uses dynamic parameter parsing.')] - Param( - # The organization id to use when creating the installer. - [Parameter(Mandatory, Position = 0, ValueFromPipelineByPropertyName)] - [Alias('id', 'organizationId')] - [Int]$organisationId, - # The location id to use when creating the installer. - [Parameter(Mandatory, Position = 1, ValueFromPipelineByPropertyName)] - [Int]$locationId, - # The installer type to use when creating the installer. - [Parameter(Mandatory, Position = 2, ValueFromPipelineByPropertyName)] - [ValidateSet('WINDOWS_MSI', 'MAC_DMG', 'MAC_PKG', 'LINUX_DEB', 'LINUX_RPM')] - [String]$installerType, - # The number of uses permitted for the installer. - [Parameter(Position = 3, ValueFromPipelineByPropertyName)] - [Int]$usageLimit, - # The node role id to use when creating the installer. - [Parameter(Position = 4, ValueFromPipelineByPropertyName)] - [ValidateNodeRoleId()] - [Object]$nodeRoleId = 'auto' - ) - process { - try { - $Resource = 'v2/organization/generate-installer' - $InstallerBody = @{ - organization_id = $organisationId - location_id = $locationId - installer_type = $installerType - usage_limit = $usageLimit - content = @{ - nodeRoleId = $nodeRoleId - } - } - $RequestParams = @{ - Resource = $Resource - Body = $InstallerBody - } - $OrganisationExists = (Get-NinjaOneOrganisations -organisationId $organisationId).Count -gt 0 - $LocationExists = (Get-NinjaOneLocations -organisationId $organisationId | Where-Object { $_.id -eq $locationId }).Count -gt 0 - if ($OrganisationExists -and $LocationExists) { - if ($PSCmdlet.ShouldProcess('Installer', 'Create')) { - $InstallerCreate = New-NinjaOnePOSTRequest @RequestParams - return $InstallerCreate.url - } - } else { - throw "Organisation '$organisationId' or location '$locationId' does not exist." - } - } catch { - New-NinjaOneError -ErrorRecord $_ - } - } + <# + .SYNOPSIS + Creates a new installer using the NinjaOne API. + .DESCRIPTION + Create a new installer download link using the NinjaOne v2 API. + .FUNCTIONALITY + Installer + .OUTPUTS + A powershell object containing the response. + .LINK + https://docs.homotechsual.dev/modules/ninjaone/commandlets/New/installer + #> + [CmdletBinding( SupportsShouldProcess, ConfirmImpact = 'Medium' )] + [OutputType([Object])] + [Alias('nnoi')] + [MetadataAttribute( + '/v2/organization/generate-installer', + 'post' + )] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'Uses dynamic parameter parsing.')] + Param( + # The organization id to use when creating the installer. + [Parameter(Mandatory, Position = 0, ValueFromPipelineByPropertyName)] + [Alias('id', 'organizationId')] + [Int]$organisationId, + # The location id to use when creating the installer. + [Parameter(Mandatory, Position = 1, ValueFromPipelineByPropertyName)] + [Int]$locationId, + # The installer type to use when creating the installer. + [Parameter(Mandatory, Position = 2, ValueFromPipelineByPropertyName)] + [ValidateSet('WINDOWS_MSI', 'MAC_DMG', 'MAC_PKG', 'LINUX_DEB', 'LINUX_RPM')] + [String]$installerType, + # The number of uses permitted for the installer. + [Parameter(Position = 3, ValueFromPipelineByPropertyName)] + [Int]$usageLimit, + # The node role id to use when creating the installer. + [Parameter(Position = 4, ValueFromPipelineByPropertyName)] + [ValidateNodeRoleId()] + [Object]$nodeRoleId = 'auto' + ) + process { + try { + $Resource = 'v2/organization/generate-installer' + $InstallerBody = @{ + organization_id = $organisationId + location_id = $locationId + installer_type = $installerType + usage_limit = $usageLimit + content = @{ + nodeRoleId = $nodeRoleId + } + } + $RequestParams = @{ + Resource = $Resource + Body = $InstallerBody + } + $OrganisationExists = (Get-NinjaOneOrganisations -organisationId $organisationId).Count -gt 0 + $LocationExists = (Get-NinjaOneLocations -organisationId $organisationId | Where-Object { $_.id -eq $locationId }).Count -gt 0 + if ($OrganisationExists -and $LocationExists) { + if ($PSCmdlet.ShouldProcess('Installer', 'Create')) { + $InstallerCreate = New-NinjaOnePOSTRequest @RequestParams + return $InstallerCreate.url + } + } else { + throw "Organisation '$organisationId' or location '$locationId' does not exist." + } + } catch { + New-NinjaOneError -ErrorRecord $_ + } + } } \ No newline at end of file diff --git a/Source/Public/Set/Set-NinjaOneOrganisationPolicies.ps1 b/Source/Public/Set/Set-NinjaOneOrganisationPolicies.ps1 index 89a689c..eb08b77 100644 --- a/Source/Public/Set/Set-NinjaOneOrganisationPolicies.ps1 +++ b/Source/Public/Set/Set-NinjaOneOrganisationPolicies.ps1 @@ -1,110 +1,111 @@ function Set-NinjaOneOrganisationPolicies { - <# - .SYNOPSIS - Sets policy assignment for node role(s) for an organisation. - .DESCRIPTION - Sets policy assignment for node role(s) for an organisation using the NinjaOne v2 API. - .FUNCTIONALITY - Organisation Policies - .OUTPUTS - A powershell object containing the response. - .LINK - https://docs.homotechsual.dev/modules/ninjaone/commandlets/set/organisationpolicies - #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] - [OutputType([Array])] - [Alias('snoop', 'Set-NinjaOneOrganizationPolicies', 'snorpa', 'Set-NinjaOneNodeRolePolicyAssignment', 'unorpa', 'Update-NinjaOneNodeRolePolicyAssignment')] - [MetadataAttribute( - '/v2/organization/{id}/policies', - 'put' - )] - [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'Uses dynamic parameter parsing.')] - Param( - # The organisation to update the policy assignment for. - [Parameter(Mandatory, ParameterSetName = 'Single', Position = 0, ValueFromPipelineByPropertyName)] - [Parameter(Mandatory, ParameterSetName = 'Multiple', Position = 0, ValueFromPipelineByPropertyName)] - [Alias('id', 'organizationId')] - [Int]$organisationId, - # The node role id to update the policy assignment for. - [Parameter(Mandatory, ParameterSetName = 'Single', Position = 1, ValueFromPipelineByPropertyName)] - [Int]$nodeRoleId, - # The policy id to assign to the node role. - [Parameter(Mandatory, ParameterSetName = 'Single', Position = 2, ValueFromPipelineByPropertyName)] - [Int]$policyId, - # The node role policy assignments to update. Should be an array of objects with the following properties: nodeRoleId, policyId. - [Parameter(Mandatory, ParameterSetName = 'Multiple', Position = 1, ValueFromPipelineByPropertyName)] - [Object[]]$policyAssignments - ) - process { - function ValidateNodeRoleAndPolicy { - [CmdletBinding()] - param( - [Int]$nodeRoleId, - [Int]$policyId - ) - Write-Verbose ('Getting node role {0} from NinjaOne API.' -f $nodeRoleId) - $Role = Get-NinjaOneRoles | Where-Object { $_.id -eq $nodeRoleId } - if ($Role) { - Write-Verbose ('Getting policy {0} from NinjaOne API.' -f $policyId) - $Policy = Get-NinjaOnePolicies | Where-Object { $_.id -eq $policyId } - if ($Policy) { - return $Policy - } else { - throw ('Policy with id {0} not found.' -f $policyId) - } - } else { - throw ('Node role with id {0} not found in organisation {1}' -f $nodeRoleId, $Organisation.Name) - } - } - try { - $Organisation = Get-NinjaOneOrganisations -OrganisationId $organisationId - if ($Organisation) { - if ($PSCmdlet.ParameterSetName -eq 'Single') { - try { - ValidateNodeRoleAndPolicy -nodeRoleId $nodeRoleId -policyId $policyId - $Body = @{ - 'nodeRoleId' = $nodeRoleId - 'policyId' = $policyId - } - } catch { - New-NinjaOneError -ErrorRecord $_ - } - } elseif ($PSCmdlet.ParameterSetName -eq 'Multiple') { - $Body = [System.Collections.Generic.List[Object]]::new() - $policyAssignments | ForEach-Object { - try { - ValidateNodeRoleAndPolicy -nodeRoleId $_.nodeRoleId -policyId $_.policyId - $Body.Add( - @{ - 'nodeRoleId' = $_.nodeRoleId - 'policyId' = $_.policyId - } - ) | Out-Null - } catch { - New-NinjaOneError -ErrorRecord $_ - } - } - } - } else { - throw ('Organisation with id {0} not found.' -f $organisationId) - } - $RequestParams = @{ - Resource = $Resource - Body = $Body - } - if ($PSCmdlet.ParameterSetName -eq 'Single') { - $RequestParams.AsArray = $true - } elseif ($PSCmdlet.ParameterSetName -eq 'Multiple') { - $RequestParams.AsArray = $false - } - if ($PSCmdlet.ShouldProcess(('Assign policy {0} to role {1} for {2}.' -f $Policy.Name, $Role.Name, $Organisation.Name), 'Update')) { - $NodeRolePolicyAssignment = New-NinjaOnePUTRequest @RequestParams - if ($NodeRolePolicyAssignment -eq 204) { - Write-Information ('Policy {0} assigned to role {1} for {2}.' -f $Policy.Name, $Role.Name, $Organisation.Name) - } - } - } catch { - New-NinjaOneError -ErrorRecord $_ - } - } + <# + .SYNOPSIS + Sets policy assignment for node role(s) for an organisation. + .DESCRIPTION + Sets policy assignment for node role(s) for an organisation using the NinjaOne v2 API. + .FUNCTIONALITY + Organisation Policies + .OUTPUTS + A powershell object containing the response. + .LINK + https://docs.homotechsual.dev/modules/ninjaone/commandlets/set/organisationpolicies + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + [OutputType([Array])] + [Alias('snoop', 'Set-NinjaOneOrganizationPolicies', 'snorpa', 'Set-NinjaOneNodeRolePolicyAssignment', 'unorpa', 'Update-NinjaOneNodeRolePolicyAssignment')] + [MetadataAttribute( + '/v2/organization/{id}/policies', + 'put' + )] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'Uses dynamic parameter parsing.')] + Param( + # The organisation to update the policy assignment for. + [Parameter(Mandatory, ParameterSetName = 'Single', Position = 0, ValueFromPipelineByPropertyName)] + [Parameter(Mandatory, ParameterSetName = 'Multiple', Position = 0, ValueFromPipelineByPropertyName)] + [Alias('id', 'organizationId')] + [Int]$organisationId, + # The node role id to update the policy assignment for. + [Parameter(Mandatory, ParameterSetName = 'Single', Position = 1, ValueFromPipelineByPropertyName)] + [Int]$nodeRoleId, + # The policy id to assign to the node role. + [Parameter(Mandatory, ParameterSetName = 'Single', Position = 2, ValueFromPipelineByPropertyName)] + [Int]$policyId, + # The node role policy assignments to update. Should be an array of objects with the following properties: nodeRoleId, policyId. + [Parameter(Mandatory, ParameterSetName = 'Multiple', Position = 1, ValueFromPipelineByPropertyName)] + [Object[]]$policyAssignments + ) + process { + function ValidateNodeRoleAndPolicy { + [CmdletBinding()] + param( + [Int]$nodeRoleId, + [Int]$policyId + ) + Write-Verbose ('Getting node role {0} from NinjaOne API.' -f $nodeRoleId) + $Role = Get-NinjaOneRoles | Where-Object { $_.id -eq $nodeRoleId } + if ($Role) { + Write-Verbose ('Getting policy {0} from NinjaOne API.' -f $policyId) + $Policy = Get-NinjaOnePolicies | Where-Object { $_.id -eq $policyId } + if ($Policy) { + return $Policy + } else { + throw ('Policy with id {0} not found.' -f $policyId) + } + } else { + throw ('Node role with id {0} not found in organisation {1}' -f $nodeRoleId, $Organisation.Name) + } + } + try { + $Organisation = Get-NinjaOneOrganisations -OrganisationId $organisationId + if ($Organisation) { + if ($PSCmdlet.ParameterSetName -eq 'Single') { + try { + ValidateNodeRoleAndPolicy -nodeRoleId $nodeRoleId -policyId $policyId + $Body = @{ + 'nodeRoleId' = $nodeRoleId + 'policyId' = $policyId + } + } catch { + New-NinjaOneError -ErrorRecord $_ + } + } elseif ($PSCmdlet.ParameterSetName -eq 'Multiple') { + $Body = [System.Collections.Generic.List[Object]]::new() + $policyAssignments | ForEach-Object { + try { + ValidateNodeRoleAndPolicy -nodeRoleId $_.nodeRoleId -policyId $_.policyId + $Body.Add( + @{ + 'nodeRoleId' = $_.nodeRoleId + 'policyId' = $_.policyId + } + ) | Out-Null + } catch { + New-NinjaOneError -ErrorRecord $_ + } + } + } + $Resource = ('v2/organization/{0}/policies' -f $organisation.id) + } else { + throw ('Organisation with id {0} not found.' -f $organisationId) + } + $RequestParams = @{ + Resource = $Resource + Body = $Body + } + if ($PSCmdlet.ParameterSetName -eq 'Single') { + $RequestParams.AsArray = $true + } elseif ($PSCmdlet.ParameterSetName -eq 'Multiple') { + $RequestParams.AsArray = $false + } + if ($PSCmdlet.ShouldProcess(('Assign policy {0} to role {1} for {2}.' -f $Policy.Name, $Role.Name, $Organisation.Name), 'Update')) { + $NodeRolePolicyAssignment = New-NinjaOnePUTRequest @RequestParams + if ($NodeRolePolicyAssignment -eq 204) { + Write-Information ('Policy {0} assigned to role {1} for {2}.' -f $Policy.Name, $Role.Name, $Organisation.Name) + } + } + } catch { + New-NinjaOneError -ErrorRecord $_ + } + } } \ No newline at end of file