└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # How To: N-Central API Automation 2 | 3 | ## Table of Content 4 | 5 | - [How To: N-Central API Automation](#how-to--n-central-api-automation) 6 | * [Table of Content](#table-of-content) 7 | - [Overview](#overview) 8 | - [Connecting](#connecting) 9 | * [PS-NCentral](#ps-ncentral) 10 | + [Multiple PS-NCentral server connections](#multiple-ps-ncentral-server-connections) 11 | * [PowerShell WebserviceProxy](#powershell-webserviceproxy) 12 | - [Performing Queries](#performing-queries) 13 | * [PS-NCentral](#ps-ncentral-1) 14 | + [Advanced PS-NCentral querying](#advanced-ps-ncentral-querying) 15 | * [PowerShell WebserviceProxy](#powershell-webserviceproxy-1) 16 | + [Bind to the namespace, using the Webserviceproxy](#bind-to-the-namespace--using-the-webserviceproxy) 17 | - [Updating a Value](#updating-a-value) 18 | * [PS-NCentral](#ps-ncentral-2) 19 | + [Updating with pipelining](#updating-with-pipelining) 20 | + [Updating Custom Device Properties](#updating-custom-device-properties) 21 | + [Custom Property options](#custom-property-options) 22 | - [Encoding](#encoding) 23 | - [Comma-Separated Values](#comma-separated-values) 24 | - [Format-properties](#format-properties) 25 | * [PowerShell WebserviceProxy](#powershell-webserviceproxy-2) 26 | + [Registration token injection](#registration-token-injection) 27 | + [Gather organization property ID](#gather-organization-property-id) 28 | + [Update customer property](#update-customer-property) 29 | + [Add new a new Customer](#add-a-new-customer) 30 | + [Add new a new User](#add-a-new-user) 31 | - [Appendix A – N-Central Web Service members](#appendix-a---n-central-web-service-members) 32 | - [Appendix B - PS-NCentral cmdlets](#appendix-b---ps-ncentral-cmdlets) 33 | - [Appendix C – GetAllCustomerProperties.ps1](#appendix-c---getallcustomerpropertiesps1) 34 | - [Appendix D – Customer Property variables](#appendix-d---customer-property-variables) 35 | - [Appendix E - All PS-Central Methods](#appendix-e---all-ps-central-methods) 36 | - [Appendix F - Common Error Codes](#appendix-f---common-error-codes) 37 | - [Appendix G - Issue Status](#appendix-g---issue-status) 38 | - [Appendix H - WebserviceProxy Alternative](#appendix-h---webserviceproxy-alternative) 39 | - [Credits](#credits) 40 | 41 | Table of contents generated with markdown-toc 42 | 43 | 44 | # Overview 45 | 46 | N-Central's API is a flexible, programmatic, object oriented, Java based interface by which developers can achieve integration and automation via native SOAP API calls. 47 | 48 | For the purposes of this guide we'll be covering connectivity and basic usage with PowerShell based automation through the PS-NCentral module, as well as native WebserviceProxy cmdlet. 49 | 50 | The information covering the PS-NCentral is useful for those with starting some experience with PowerShell or need to quickly put together code where module dependency isn't an issue, while the usage of the WebserviceProxy method is for those more familiar with object oriented coding or need code portatability. As of version 1.2 PS-NCentral uses PowerShell 7 for cross compatability to be able to run Windows/Linux or in an Azure function. 51 | 52 | PS-NCentral provides cmdlets for 17 Get cmdlets and 4 Set cmdlets (See Appendix B) that cover the majority, so should cover the majority of automation. This can be downloaded from: [https://github.com/ToschAutomatisering/PS-NCentral](https://github.com/ToschAutomatisering/PS-NCentral) 53 | 54 | Or installed from PS-Gallery with the cmdlet 55 | 56 | ```powershell 57 | Install-Module PS-NCentral 58 | ``` 59 | 60 | # Connecting 61 | 62 | The first step required before connecting is to create a new automation account with appropriate role permissions. With N-Central 2020 or 12.3 HF4 and later you must **disable the MFA** requirement for the account, so use a long and complex password. The (preferred) API-Only account in N-Central 2021 needs to be set to \'mfa not needed\'. 63 | 64 | Once the account is created, select the API Authentication tab and click on the ' **Generate JSON Web Token**' button, save this **JWT** token somewhere secure, if you lose your JWT, you can generate another one at any time, but it will invalidate the previous one. If you update/change role permissions for the account automation account you will need to regenerate the token, as the asserted permissions are in the JWT. 65 | 66 | Note: Activating the MFA-setting after generating the token still blocks JWT-access, as well as an expired password for the originating account (90 days default). 67 | 68 | ## PS-NCentral 69 | 70 | Connecting to your N-Central service with PS-NCentral only needs to be done once per session. You first require the following information: 71 | 72 | - The fqdn of your N-Central server, ie: `n-central.mydomain.com` 73 | - The JWT from above 74 | - Username/Password (no MFA) for versions **before 1.2**. 75 | 76 | Then enter the following: 77 | 78 | **All versions** 79 | 80 | ```powershell 81 | #Import the PS-NCentral module 82 | import-module .\PS-NCentral.psm1 -Verbose 83 | 84 | #$credential = Get-Credential ## This line can be used for a dialog. Skip 2 below. 85 | $password = ConvertTo-SecureString "" -AsPlainText -Force 86 | $credential = New-Object System.Management.Automation.PSCredential ("", $password) 87 | 88 | #Connect to NC 89 | New-NCentralConnection -ServerFQDN "YOUR SERVER FQDN" -PSCredential $credential 90 | ``` 91 | 92 | \* Might generate an error on some complex passwords. Avoid using a ^. 93 | 94 | **Version 1.2 and later** 95 | 96 | ```powershell 97 | #Import the PS-NCentral module 98 | import-module .\PS-NCentral.psm1 -Verbose 99 | 100 | #Connect to NC using the JWT directly 101 | New-NCentralConnection -ServerFQDN "YOUR SERVER FQDN" -JWT "YOUR JWT TOKEN" 102 | ``` 103 | 104 | which is equivalent to: 105 | 106 | ```powershell 107 | #Import the PS-NCentral module 108 | import-module .\PS-NCentral.psm1 -Verbose 109 | 110 | #Credentials with JWT 111 | $password = ConvertTo-SecureString "YOUR JWT TOKEN" -AsPlainText -Force 112 | $credential = New-Object System.Management.Automation.PSCredential ("_JWT", $password) 113 | 114 | #Connect to NC 115 | New-NCentralConnection -ServerFQDN "YOUR SERVER FQDN" -PSCredential $credential 116 | ``` 117 | If successful you will get an output similar to the below: 118 | 119 | |Property | Value| 120 | |--------|-----| 121 | | ConnectionURL | `n-central.mydomain.com` | 122 | | BindingURL | `https://n-central.mydomain.com/dms2/services2/ServerEI2?wsdl' | 123 | | AllProtocols | tls12,tls13 | 124 | | IsConnected | True | 125 | | RequestTimeOut | 100 | 126 | | NCVersion | 2020.1.5.425 | 127 | | DefaultCustomerID | 50 | 128 | | Error | | 129 | 130 | The session is now stored in the global **$\_NCSession** variable, and will automatically be used as default for following PS-NCentral commands. 131 | 132 | ### Multiple PS-NCentral server connections 133 | 134 | If you are an MSP with multiple N-Central servers, or have an NFC server for testing you can leverage the **-NCSession** parameter available on PS-NCentral cmdlets to quickly call other servers, this is available in all versions of PS-NCentral, but we'll use 1.2 as the example for brevity. 135 | 136 | Then enter the following: 137 | ```powershell 138 | #Connect to NC 139 | $Connection1 = New-NCentralConnection "$NCentralFQDN1" -JWT "$JWT1" 140 | $Connection2 = New-NCentralConnection "$NCentralFQDN2" -JWT "$JWT2" 141 | 142 | #Get the customer list from each server for later processing 143 | $NC1Customers = Get-NCCustomerList -NcSession $Connection1 144 | $NC2Customers = Get-NCCustomerList -NcSession $Connection2 145 | ``` 146 | 147 | Another useful pameter when connecting is the **DefaultCustomerID**, this sets the default scope for when calling cmdlets such as Get-NCDeviceList without a parameter, so if I were to perform the following connection and function call it would only give me all devices associated with CustomerID 333 148 | 149 | ``` powershell 150 | New-NCentralConnection "$NCentralFQDN" -JWT "$JWT1" -DefaultCustomerID 333 151 | $Customer333Devices = Get-NCDeviceList 152 | ``` 153 | 154 | You can use **Set-NCCustomerDefault** to change the value afterwards. 155 | 156 | ## PowerShell WebserviceProxy 157 | 158 | Notice the WebserviceProxy is discontinued after PowerShell version 5.1. You can find alternative code in [Appendix H - WebserviceProxy Alternative](#appendix-h---webserviceproxy-alternative) 159 | 160 | 161 | As a preface to the usage of the New-WebserviceProxy cmdlet, we will focus on the v2 rather than v1 legacy API as the v1 endpoint maybe deprecated at some point. 162 | 163 | The main differences between the v1 and v2 endpoints are: 164 | 165 | - The WSDL endpoint 166 | - Different classes, including the KeyPair constructor class used for adding custom settings for queries and update/change methods 167 | - V2 has all the newer methods available 168 | 169 | It will be necessary to review the Javadocs provided on your server for the lastest information on the classes and constructors, you can find them under your own N-Central server under `https://n-central.mydomain.com/dms/` 170 | 171 | If reviewing other WebserviceProxy powershell code on the internet, you can identify **v1**/legacy code as it will have the following in the binding URL string: **/dms/services/ServerEI?wsdl** while **v2** has **/dms2/services2/ServerEI2?wsdl** 172 | 173 | For connecting to webservice you will need the same information as with the PS-NCentral which connects in the same way underneath: 174 | 175 | - The fqdn of your N-Central server, ie: n-central.myserver.com 176 | - The JWT for the account 177 | 178 | With our examples we'll use v2 connections and classes, below is a common method seen in examples: 179 | ```powershell 180 | #Example host 181 | $serverHost = "n-central.myserver.com" 182 | 183 | # Bind to the namespace, using the Webserviceproxy 184 | $NWSNameSpace = "NAble" + ([guid]::NewGuid()).ToString().Substring(25) 185 | $bindingURL = "https://" + $serverHost + "/dms2/services2/ServerEI2?wsdl" 186 | $nws = New-Webserviceproxy $bindingURL -Namespace ($NWSNameSpace) 187 | ``` 188 | 189 | ```$NWSNameSpace``` here can be most anything of your choosing, the point of the GUID generation is to ensure the namespace for the classes to be used inside the webservice are _unique_ to anything else on your system or current context, you could use a static namespace such as MyNCentralConnection or PowerShellIsAwesome. 190 | 191 | After you've run this the $nws variable will contain all the available public methods from the endpoint, you can interrogate this by running 192 | ```powershell 193 | $nws | Get-Member 194 | ``` 195 | From this you will see a lot of | Event |s, Methods and Properties ie. 196 | 197 | |Name | MemberType | 198 | |----| ----------| 199 | |versionInfoGetCompleted || Event || 200 | | Abort| Method | 201 | | accessGroupAdd | Method | 202 | | BeginaccessGroupAdd | Method | 203 | | BeginaccessGroupGet | Method | 204 | 205 | 206 | The above output has been shortened, see [Appendix A – N-Central Web Service members](#appendix-a---n-central-web-service-members) members for the complete output. In addition you will have a **Definition** column, and you will observe that your `$NWSNameSpace` is seen prefixed to the methods/classes noted in them. All classes/methods/constructors available in the Javadocs can be created and called upon through the `$nws` variable. Eg. The customerListmethod would be called with 207 | 208 | ```powershell 209 | $nws.customerList("", $JWT, $settings) 210 | ``` 211 | 212 | As you will note when connecting with the `$nws` variable, at no point did you use your username or JWT, as you will observe in the `$nws.customerList` method called above, the $JWT is used in every get or set, and the username is simply `""` as the username is inside of the JWT string. 213 | 214 | Underneath the PS-NCentral module it saves these variables with each connection and re-uses each time a cmdlet is used. 215 | 216 | # Performing Queries 217 | 218 | ## PS-NCentral 219 | 220 | Performing queries with the PS-Central module is quick and easy, and several examples are provided in it's own documentation [here](https://github.com/ToschAutomatisering/PS-NCentral/blob/master/PS-NCentral_Examples.ps1) . The outcomes of the examples are fairly self explanatory. For our example we'll take a common query like the Customer table and join it with the Organisation properties table using the PS-NCentral cmdlets, then in the advanced section we'll give the same example using native cmdlets. 221 | ```powershell 222 | Import-Module PS-NCentral.psm1 -Verbose 223 | 224 | $ServerFQDN = n-central.myserver.com 225 | $JWT = "JWT TOKEN" 226 | 227 | #Connect to NC 228 | New-NCentralConnection -ServerFQDN $ServerFQDN -JWT $JWT 229 | 230 | #Grab the customer list/details 231 | $CustomerList = Get-NCCustomerList 232 | 233 | #Get the customer properties 234 | $CustomerPropertyList = Get-NCCustomerPropertyList 235 | 236 | #Create array list for table 237 | $CustomerReport = New-Object System.Collections.ArrayList 238 | 239 | #Merge 240 | foreach ($Customer in $CustomerList) { 241 | $Properties = $CustomerPropertyList | ?{$_.CustomerID -eq $Customer.customerid} 242 | $CustomerHashtable = [Ordered]@{} 243 | $Customer.psobject.properties | ?{$_.Name -ne 'customerid'} | %{$CustomerHashtable[$_.Name] = $_.Value} 244 | $PropertiesHashTable = [Ordered]@{} 245 | $Properties.psobject.properties | %{$PropertiesHashtable[$_.Name] = $_.Value} 246 | $ReportItem = $CustomerHashtable + $PropertiesHashTable 247 | $ReportItem = [PSCustomObject]$ReportItem 248 | $CustomerReport.Add($ReportItem) > $Null 249 | } 250 | 251 | #Output the report to the screen/Gridview 252 | $CustomerReport | Out-GridView 253 | ``` 254 | 255 | The important parts of this example are the simple one line calls for the **New-CentralConnection** , **Get-NCCustomerList** and **Get-NCCustomerPropertyList**. With very little effort we can connect, retrieve the data then process into a single table for review. 256 | 257 | ### Advanced PS-NCentral querying 258 | The PS-NCentral module provides ease of access to N-Central API calls with normal **verb-noun** functions, but you can also perform a direct call through the internal connection class. We could replace the above function calls with these methods: 259 | 260 | ```powershell 261 | # Connect to NC 262 | $NCSession = New-NCentralConnection -ServerFQDN $ServerFQDN -JWT $JWT 263 | 264 | # Grab the customer list/details 265 | $CustomerList = $NCSession.CustomerList() 266 | 267 | # Get the customer properties 268 | $CustomerPropertyList = $NCSession.OrganizationPropertyList() 269 | ``` 270 | 271 | We can get the list of all the underlying class connection methods by enumerating the members with `$_NCSession | Get-Member -MemberType Method` to see all 'inside' methods, which reflect the API-methods of N-Central ('http://mothership.n-able.com/dms/javadoc_ei2/com/nable/nobj/ei2/ServerEI2_PortType.html') where applicable. 272 | 273 | Most methods have 'Overloads'. These are selected based on the parameter-pattern eg. `([String], [String])` or `([String],[Int])`. 274 | 275 | ```powershell 276 | #Get the devices for customerid 100 277 | $Customer100Devices = $NCSession.DeviceList(100) 278 | #or 279 | $Customer100Devices = $NCSession.DeviceList(100,$true,$false) 280 | 281 | #Get the probes for customerid 100 282 | $Customer100Probes = $NCSession.DeviceList(100,$false,$true) 283 | ``` 284 | 285 | For a list of all methods see [Appendix E - All PS-Central Methods](#appendix-e---all-ps-central-methods) 286 | 287 | ## PowerShell WebserviceProxy 288 | 289 | In this section we'll perform the same example as above but using the native cmdlets we'll go through an example of a fully functioning cmdlet that uses native cmdlet calls. 290 | 291 | ```powershell 292 | # Define the command-line parameters to be used by the script 293 | [CmdletBinding()] 294 | Param( 295 | [Parameter(Mandatory = $true)]$serverHost, 296 | [Parameter(Mandatory = $true)]$JWT 297 | ) 298 | ``` 299 | 300 | Here we establish the mandatory variables we'll be using to connect, in this case the username of the automation account used, the server URI and the Java Web Token 301 | 302 | We'll then connect using a static namespace, you can equally use the pseudo-unique namespace above. 303 | 304 | ```powershell 305 | $NWSNameSpace = "NAbleAPI" 306 | ``` 307 | 308 | ### Bind to the namespace, using the Webserviceproxy 309 | ```powershell 310 | $bindingURL = "https://" + $serverHost + "/dms2/services2/ServerEI2?wsdl" 311 | $nws = New-Webserviceproxy $bindingURL -Namespace ($NWSNameSpace) 312 | ``` 313 | For many API calls a list of settings are required, in the case of the `CustomerList()` method we need to specify if the service organisation should be listed or not. The JavaDocs specify you have to use the `EiKeyValue` KeyPair type or array of `EiKeyValuesList` per the Javadocs, but it is simpler to create an ArrayList and add a generic hashtable Key/Pair that will be automatically cast to the `EiKeyValue`. 314 | 315 | ```powershell 316 | $settings = New-Object System.Collections.ArrayList 317 | $settings.Add(@{key = "listSOs"; value = "True" }) 318 | ``` 319 | 320 | Next we wrap the steps of retrieving the Customer List and Organisation properties list with a try/catch block that exits if there is an error with retrieving the data. 321 | ```powershell 322 | #Attempt to connect 323 | Try { 324 | $CustomerList = $nws.customerList("", $JWT, $Settings) 325 | $OrgPropertiesList = $nws.organizationPropertyList("", $JWT, $null, $false) 326 | } 327 | 328 | Catch { 329 | Write-Host Could not connect: $($_.Exception.Message) 330 | exit 331 | } 332 | ``` 333 | 334 | We then create a hash table of all the customer properties with the Customer ID as the Key and the Properties as the value. 335 | 336 | ```powershell 337 | $OrgPropertiesHashTable = @{} 338 | foreach ($Org in $OrgPropertiesList) { 339 | $CustomerOrgProps = @{} 340 | foreach ($p in $Org.properties) { $CustomerOrgProps[$p.label] = $p.value } 341 | $OrgPropertiesHashTable[$Org.customerId] = $CustomerOrgProps 342 | } 343 | ``` 344 | 345 | In the next step we take create an ArrayList in preference to a simple array to increase performance on inserts, then enumerate through the customer list match a custom list of tables and join it to the properties Hash table and output it to the screen. 346 | 347 | ```powershell 348 | #Create customer report ArrayList 349 | $CustomersReport = New-Object System.Collections.ArrayList 350 | 351 | ForEach ($Entity in $CustomerList) { 352 | $CustomerAssetInfo = @{} 353 | #Custom select the required columns, use Ordered to keep them in the order we list them in 354 | 355 | ForEach ($item in $Entity.items) { $CustomerAssetInfo[$item.key] = $item.Value } 356 | $o = [Ordered]@{ 357 | ID = $CustomerAssetInfo["customer.customerid"] 358 | Name = $CustomerAssetInfo["customer.customername"] 359 | parentID = $CustomerAssetInfo["customer.parentid"] 360 | RegistrationToken = $CustomerAssetInfo["customer.registrationtoken"] 361 | } 362 | 363 | #Retrieve the properties for the given customer ID 364 | $p = $OrgPropertiesHashTable[[int]$CustomerAssetInfo[customer.customerid]] 365 | 366 | #Merge the two hashtables 367 | $o = $o + $p 368 | 369 | #Cast the hashtable to a PSCustomObject 370 | $o = [PSCustomObject]$o 371 | 372 | #Add the PSCustomObject to our CustomersReport ArrayList 373 | $CustomersReport.Add($o) > $null 374 | } 375 | 376 | #Output to the screen 377 | $CustomersReport | Out-GridView 378 | ``` 379 | 380 | For the complete script see [Appendix C – GetAllCustomerProperties.ps1](#appendix-c---getallcustomerpropertiesps1) 381 | 382 | 383 | 384 | Note that **WebServiceProxy** is **discontinued** by Microsoft in PowerShell versions after 5.1. You can find alternative code in [Appendix H - WebserviceProxy Alternative](#appendix-h---webserviceproxy-alternative) 385 | 386 | 387 | # Updating a Value 388 | 389 | A common case for updating a value would be automating the update/change of Organisation or Device properties. Examples of Organisation properties could be: tokens/keys for MSP applications deployed to devices, the customer name to pass through to a script for output or the N-Central API registration token for installation of the agent. Updating these properties is straightforward with either the web proxy or PS-NCentral cmdlets. 390 | 391 | At time of writing the normal way for a Registration Token to be generated is through the UI, requiring an administrator to navigate to every _customer_ and every _site_ and click on the **Get Registration Token button** under **Download Agent/Probe** ; this will be changed in future. 392 | 393 | If you do need to perform mass registration token updating/refreshing there is an AMP provided as a part of the InstallAgent 6 suite that has a workaround and can be found at [https://github.com/AngryProgrammerInside/InstallAgent/tree/master/AMPs](https://github.com/AngryProgrammerInside/InstallAgent/tree/master/AMPs) 394 | 395 | ## PS-NCentral 396 | 397 | In the example for PS-NCentral we'll take the Customer name from the Get-NCCustomerList and inject it into _custom_ property called **Agent – Registration Token**, this is useful if we need to programmatically inject token information into files or registry settings for later use. We'll assume we already have a connection to N-Central: 398 | 399 | ```powershell 400 | $CustomerList = Get-NCCustomerList 401 | foreach ($Customer in $CustomerList){ 402 | Set-NCCustomerProperty -CustomerIDs $Customer.customerid -PropertyLabel "Agent - RegistrationToken" -PropertyValue $Customer.registrationtoken 403 | } 404 | ``` 405 | 406 | Or if we wanted to take the customer's name and inject it into a custom property like **Reporting – Customer Name** to inject it into an AMP's output for easier to identify the device AMPs run across a service organization: 407 | 408 | ```powershell 409 | $CustomerList = Get-NCCustomerList 410 | 411 | foreach ($Customer in $CustomerList){ 412 | Set-NCCustomerProperty -CustomerIDs $Customer.customerid -PropertyLabel "Reporting – Customer Name" -PropertyValue $Customer.customername 413 | } 414 | ``` 415 | An advantage of the Set-NCCustomerProperty cmdlet is that it can distinguish between the default _customer_ properties, ie. zip/postalcode, street1, externalid, externalid2 and will use the appropriate method to update that. You can find the list of key names in the Java Docs, or refer to **Appendix D – Customer Property variables**. 416 | 417 | For our example, you may have a PSA or perhaps a spreadsheet, and we want to refresh the information from that data source into _customer_ properties. In our example we'll use a spreadsheet/CSV as our datasource, and assume we have already matched the CustomerID with the company name and we have a dataset as below: 418 | 419 | | **customerid** | **firstname** | **lastname** | **email** | 420 | | --- | --- | --- | --- | 421 | | 1 | Claire | Young | Claire.Young@email.com | 422 | | 2 | Benjamin | Metcalfe | Bmetacalfe@usa.com | 423 | | 3 | Kimberly | King | kk@asia.com | 424 | | 4 | Michael | Mills | Mmills@engineer.com | 425 | | 5 | Anthony | Jackson | 008Jac@mail.com | 426 | 427 | \ 428 | You could then update the respective values in N-Central 429 | ```powershell 430 | $custData = Import-CSV C:\Temp\customerData.csv 431 | 432 | foreach ($Customer in $custData){ 433 | #Gather properties to update 434 | $Properties = $Customer.psobject.properties | ?{$_.Name -ne 'customerid'} 435 | 436 | foreach ($Property in $Properties){ 437 | #Update the property 438 | Set-NCCustomerProperty -CustomerIDs $Customer.customerid -PropertyLabel $Property -PropertyValue $Property.Value 439 | } 440 | } 441 | ``` 442 | ### Updating with pipelining 443 | Another advantage of PS-NCentral is that you can easily pipeline information through and set it as a customer property, in the first example we will update the **Reporting - Customer Name** again except this time utilising the pipe: 444 | ```powershell 445 | Get-NCCustomerList | Set-NCCustomerProperty -PropertyLabel 'Reporting – Customer Name' -PropertyValue $_.customername 446 | ``` 447 | 448 | In the second example we may have a custom table from a CSV or other source that has the following properties and values: 449 | 450 | | **customerid** | **CustomerSLA** | 451 | | --- | --- | 452 | | 123 | 1H | 453 | | 124 | 4H | 454 | | 221 | 8H | 455 | | 233 | 8H | 456 | | 321 | 8H | 457 | 458 | \ 459 | We then have this table in a variable `$CustomerProps` and use it to populate a custom property called **'Reporting - Customer SLA'** 460 | ```powershell 461 | Get-NCCustomerList | 462 | Select-Object customerid, @{n="CustomerSLA"; e={$CustomerID = $_.customerid; (@($CustomerProps).where({ $_.customerID -eq $CustomerID })).CustomerSLA}} ` 463 | | Where-Object {$_.CustomerSLA} ` 464 | | % { Set-NCCustomerProperty -CustomerIDs $_.CustomerID -PropertyLabel 'Reporting – Customer SLA' -PropertyValue ($_.CustomerSLA -join ',') } 465 | ``` 466 | When multiple records for a customerid are found in $Customerprops all values will be added comma-separated. 467 | 468 | The important parts of this example are the table-lookup 469 | 470 | ``` 471 | (@().where( -eq )). 472 | ``` 473 | 474 | and the filter to only return objects which had values added 475 | 476 | ``` 477 | Where-Object {$_.} 478 | ``` 479 | 480 | before setting the properties. 481 | 482 | ### Updating Custom Device Properties 483 | Another example would be where we may want to populate a Custom Device Property, in this case **'External ID'** based upon the CustomerID using in a customer table `$Customers` 484 | | **customerid** | **ExternalID** | 485 | | --- | --- | 486 | | 123 | 78409377 | 487 | | 124 | 78405890 | 488 | | 221 | 78404905 | 489 | | 233 | 78402984 | 490 | | 321 | 38940384 | 491 | 492 | ```powershell 493 | Get-NCDeviceList | ` 494 | Select-Object DeviceID, ` 495 | @{n="ExternalID"; e={$CustomerID = $_.customerid; (@($Customers).where({ $_.customerID -eq $CustomerID })).ExternalID}} | ` 496 | Where-Object {$_.ExternalID} | %{Set-NCDeviceProperty -DeviceIDs $_.DeviceID -PropertyLabel 'ExternalID' -PropertyValue ($_.ExternalID -join ',')} 497 | ``` 498 | 499 | 500 | 501 | ### Custom Property options 502 | 503 | These options are introduced in PS-NCentral version **1.3**. 504 | 505 | #### Encoding 506 | 507 | Use the **-Base64** option to Encode/Decode a CustomProperty when using Set or Get. Unicode (utf16) by default, -utf8 option available. 508 | 509 | **Convert-Base64** is available as a seperate command too. 510 | 511 | 512 | 513 | #### Comma-Separated Values 514 | 515 | When a Custom Property holds a string of (unique) Comma-seperated values you can easily Add or Remove a single value with the commands: 516 | 517 | - Add-NCCustomerPropertyValue 518 | - Add-NCDevicePropertyValue 519 | - Remove-NCCustomerPropertyValue 520 | - Remove-NCDevicePropertyValue 521 | 522 | Use Get-help \ to see the options. 523 | 524 | 525 | 526 | #### Format-properties 527 | 528 | Sometimes not all objects in a list have the same properties. This can become an issue when using **Format-Table** or **Output-CSV**, which only use the properties of the first object in the list. 529 | 530 | The **Format-Properties** cmdlet ensures all unique properties are added to all objects in a list. It is integrated in several PS-NCentral list-commands but also available as a seperate command. 531 | 532 | 533 | 534 | ## PowerShell WebserviceProxy 535 | 536 | Updating customer properties and without the PS-NCentral cmdlets can take several additional steps as PS-NCentral takes care of some busy work underneath. 537 | 538 | ### Registration token injection 539 | 540 | Let's first take the example of injecting taking the registration token from the **customerList** method and injecting it via the **organizationPropertyModify** method. As above we'll assume we have a connection `$nws` already and our list of customers is in the variable $CustomerList, take note of the line where gathering the value of the custom property with the id **123456789**. 541 | ```powershell 542 | ForEach ($Entity in $CustomerList) { 543 | $CustomerAssetInfo = @{} 544 | ForEach ($item in $Entity.items) { $CustomerAssetInfo[$item.key] = $item.Value } 545 | 546 | #Create a custom object for the data 547 | $CustomerObject = [Ordered]@{ 548 | ID = $CustomerAssetInfo[customer.customerid] 549 | Name = $CustomerAssetInfo[customer.customername] 550 | parentID = $CustomerAssetInfo[customer.parentid] 551 | RegistrationToken = $CustomerAssetInfo[customer.registrationtoken] 552 | } 553 | 554 | #Skip any Registration tokens that are null/empty 555 | if ($null -eq $CustomerObject.RegistrationToken -or -eq $CustomerObject.RegistrationToken) {continue} 556 | 557 | #Gather the property value for the specific property ID 558 | $CustomerProperty = ($OrgPropertiesList | ?{$_.customerId -eq $CustomerObject.ID}).properties | ?{$_.propertyid -eq 123456789} 559 | $CustomerProperty.value = $CustomerObject.RegistrationToken 560 | 561 | #Create a new OrganizationProperties object and populate it 562 | $ModOrgProperties = New-Object $NWSNameSpace.OrganizationProperties 563 | $ModOrgProperties.customerId = $CustomerObject.ID 564 | $ModOrgProperties.customerIdSpecified = $true 565 | $ModOrgProperties.properties = $CustomerProperty 566 | 567 | #Inject the property 568 | $nws.organizationPropertyModify("",$JWT,$ModOrgProperties) 569 | } 570 | ``` 571 | ### Gather organization property ID 572 | 573 | While the above code works in updating the specific customer org property, one must first interrogate the properties and their associated values in advance. While this is fine for scripts where you will always be updating the same **propertyid** , you may wish to implement a function that takes care of searching and retrieving the **propertyid**. 574 | 575 | PS-NCentral cmdlets use a class to retrieve this, we can convert it to a function for our use: 576 | ```powershell 577 | function Get-OrganizationPropertyID( 578 | [Int]$OrganizationID, 579 | [String]$PropertyName, 580 | [String]$JWT, 581 | [System.Web.Services.Protocols.SoapHttpClientProtocol]$NcConnection){ 582 | 583 | ## Returns 0 (zero) if not found. 584 | $OrganizationPropertyID = 0 585 | $results = $null 586 | Try{ 587 | #Gets the organization and all custom properties 588 | $results = $NcConnection.OrganizationPropertyList("", $JWT, $OrganizationID, $false) 589 | } 590 | Catch { 591 | $_.ErrorHandler 592 | } 593 | 594 | #Search through all properties and match the one with the same name 595 | ForEach ($OrganizationProperty in $results.properties){ 596 | If($OrganizationProperty.label -eq $PropertyName){ 597 | $OrganizationPropertyID = $OrganizationProperty.PropertyID 598 | } 599 | } 600 | Return $OrganizationPropertyID 601 | } 602 | ``` 603 | We can then use the function to gather the property ID 604 | 605 | ```powershell 606 | #Get the property id for org 123 with the property name Agent – Registration token 607 | Get-OrganizationPropertyID -OrganizationID 123 -PropertyName "Agent - Registration Token" -JWT $JWT -NcConnection $nws 608 | ``` 609 | 610 | The author notes that at time of writing, the **propertyid** appears to be the same for all customers/sites created at the same hierarchical level (System/SO/Customer/Site). For cases where you have multiple service organizations with the same named custom property created at the SO level they should be a different propertyid. 611 | 612 | For single SO deployments where the custom properties are created at the SO level they are globally unique, you could also create a lookup table for optimising your code to avoid performing a **propertyid** lookup for each update of a custom property, though we won't be covering that in this document. 613 | 614 | ### Update customer property 615 | 616 | Updating a _customer_ property such as the contact details or externalid values is done through the **customerModify** method, the method is called with the form: 617 | 618 | ```customerModify([string]username,[string]password,[ListEiKeyValue]settings)``` 619 | 620 | You may note in the PS-NCentral example it can update one property, either *custom* or customer, in a single call; whereas the KeyValue list can contain one or all of the customer values shown in [Appendix D – Customer Property variables](#appendix-d---customer-property-variables) to be updated in a single call. 621 | 622 | We'll use an expanded data set from the PS-NCentral as we have more mandatory fields **customername** , **customerid** and **parentid** that are otherwise looked up by an internal helper function in PS-NCentral: 623 | 624 | | parentid | customerid | customername | firstname | lastname | email | 625 | | --- | --- | --- | --- | --- | --- | 626 | | 50 | 1 | Contoso | Claire | Young | `Claire.Young@email.com` | 627 | | 50 | 2 | Volcano Coffee | Benjamin | Metcalfe | `Bmetacalfe@usa.com` | 628 | | 50 | 3 | Northwind Traders | Kimberly | King | `kk@asia.com` | 629 | | 50 | 4 | WW Importers | Michael | Mills | `Mmills@engineer.com` | 630 | | 50 | 5 | Blue Yonder | Anthony | Jackson | `008Jac@mail.com` | 631 | 632 | You can retrieve the mandatory fields mentioned in the above table by using the `CustomerList()` covered previously. 633 | 634 | In the below example the data is imported, then we generate the appropriate KeyValue array and assuming use the same $nwsconnection variable and $NWSNameSpace from previous examples to connect and update the modified keys. 635 | ```powershell 636 | $custData = Import-CSV C:\Temp\customerData.csv 637 | foreach ($Customer in $custData){ 638 | #Gather properties to update 639 | $Properties = $Customer.psobject.properties 640 | #Create an Arraylist of HashTables to update 641 | $ModifiedKeyList = New-Object System.Collections.ArrayList 642 | $Properties | ForEach-Object{ 643 | $KeyPair = @{} 644 | $KeyPair.key = $_.Name 645 | $KeyPair.value = $_.Value 646 | $ModifiedKeyList.Add($KeyPair) 647 | } 648 | $nws.customerModify("",$JWT,$ModifiedKeyList) 649 | } 650 | ``` 651 | ### Add a new Customer 652 | 653 | Not every cmdlet is currently available in PS-NCentral, one such cmdlet that could be useful is the automation of the creation of customer accounts. In the below example we use the ```$nws``` connection from before and pass through a hashtable of some of the customer properties in in [Appendix D – Customer Property variables](#appendix-d---customer-property-variables), note there are two required fields: **customername** and **parentid** 654 | 655 | Combining the hashtable `$newcustomer` with the `$JWT` and `$nws` to the cmdlet it will create the customer. It will return the new CustomerID value once the job is completed. 656 | ```powershell 657 | $newcustomer = @{ 658 | customername = "New Customer" 659 | parentid = "50" 660 | firstname = "john" 661 | lastname = "doe" 662 | email = "john.doe@contoso.com" 663 | city = "Melbourne" 664 | telephone = "0312345678" 665 | country = "AU" 666 | } 667 | 668 | function Add-NCCustomer( 669 | [Hashtable]$CustomerTable, 670 | [String]$JWT, 671 | [System.Web.Services.Protocols.SoapHttpClientProtocol]$NcConnection) { 672 | $CustomerAttributeList = New-Object System.Collections.ArrayList 673 | foreach ($key in $CustomerTable.Keys){ 674 | $setting = @{key = $key; value = $CustomerTable[$key]} 675 | $CustomerAttributeList.Add($setting) > $null 676 | } 677 | $NcConnection.customerAdd("", $JWT, $CustomerAttributeList) 678 | } 679 | 680 | Add-NCCustomer -CustomerTable $newcustomer -JWT $JWT -NcConnection $nws 681 | ``` 682 | 683 | At time of writing with PS-NCentral version 1.2 it is possible to use the CustomerAdd() method as it is exists inside the core class object now. While there is currently no Powershell function to call this, create a customer with it in the following way: 684 | 685 | ```powershell 686 | #Connect to NC 687 | $NCSession = New-NCentralConnection -ServerFQDN n-central.myserver.com -JWT $JWT 688 | $ParentId = 50 689 | $NewCustomerAttributes = @{ 690 | firstname = "john" 691 | lastname = "doe" 692 | email = "john.doe@contoso.com" 693 | city = "Melbourne" 694 | telephone = "0312345678" 695 | country = "AU" 696 | } 697 | 698 | $NCSession.CustomerAdd("NewCustomerName",$ParentId,$NewCustomerAttributes) 699 | ``` 700 | As of version 1.6 you can add all fields (including the mandatory customername and parentid) directly to $NewCustomerAttributes and use this as a single parameter for the CustomerAdd() method. 701 | 702 | Similar to the UserAdd-command documented below it is also possible to import a csv with multiple customers, which has the fieldnames as the columnheader. The following command can be used to create a csv with all possible (standard) headers first. 703 | 704 | ```powershell 705 | $NCSession.CustomerValidation -join "," | set-content .\CustomerTemplate.csv 706 | ``` 707 | 708 | You can also create the customer without attributes and fill them out later if you wish by simply calling 709 | 710 | ```powershell 711 | $NCSession.CustomerAdd("NewCustomerName",$ParentId)` 712 | ``` 713 | 714 | The CustomerAdd function will return the value for the new Customer ID, you can then use that Id to perform further automation if needed. 715 | 716 | ### Add a new User 717 | 718 | Not every cmdlet is currently available in PS-NCentral, one such cmdlet that could be useful is the automation of the creation of user accounts. 719 | 720 | At time of writing with PS-NCentral version 1.6 it is possible to use the CustomerAdd() method as it is exists inside the core class object now. While there is currently no Powershell function to call this, create a user with it in the following way: 721 | 722 | ```powershell 723 | #Connect to NC 724 | $NCSession = New-NCentralConnection -ServerFQDN n-central.myserver.com -JWT $JWT 725 | $NewUserAttributes = @{ 726 | email = "john.doe@contoso.com" 727 | customerID = 50 728 | password = "ComplexityCompliant" 729 | firstname = "John" 730 | lastname = "Doe" 731 | } 732 | 733 | $NCSession.UserAdd($NewUserAttributes) 734 | ``` 735 | 736 | Above are the required attributes only. To see the additional attributes you can type 737 | 738 | ```powershell 739 | $NCSession.UserValidation 740 | ``` 741 | 742 | It is also possible to import a csv with multiple accounts, which has the fieldnames as the columnheader. The command above can be used to create a csv with all possible headers first. 743 | 744 | ```powershell 745 | $_ncsession.UserValidation -join "," | set-content .\UserTemplate.csv 746 | ``` 747 | 748 | After modifying the CSV and saving it as AccountsList.csv you can import 749 | 750 | ```powershell 751 | #Connect to NC 752 | $NCSession = New-NCentralConnection -ServerFQDN n-central.myserver.com -JWT $JWT 753 | #Import list of accounts 754 | Import-CSV C:\Temp\AccountsList.csv | $NCSession.UserAdd($_) 755 | ``` 756 | 757 | 758 | 759 | The UserAdd function will return the value for the new User ID, you can then use that Id to perform further automation if needed. 760 | 761 | It will return **-1** when the addition fails. Make sure the email is unique across the system (even if deleted) and the password meets the complexity requirements. 762 | 763 | **Note:** It is not possible to change or add information for the user-account by API after creation. 764 | 765 | 766 | 767 | # Appendix A – N-Central Web Service members 768 | 769 | |Name |MemberType| 770 | |---- |----------| 771 | |accessGroupAddCompleted | Event | 772 | |accessGroupGetCompleted | Event | 773 | |accessGroupListCompleted | Event | 774 | |acknowledgeNotificationCompleted | Event | 775 | |activeIssuesListCompleted | Event | 776 | |customerAddCompleted | Event | 777 | |customerDeleteCompleted | Event | 778 | |customerListChildrenCompleted | Event | 779 | |customerListCompleted | Event | 780 | |customerModifyCompleted | Event | 781 | |deviceAssetInfoExportDeviceCompleted | Event | 782 | |deviceAssetInfoExportDeviceWithSettingsCompleted | Event | 783 | |deviceGetCompleted | Event | 784 | |deviceGetStatusCompleted | Event | 785 | |deviceListCompleted | Event | 786 | |devicePropertyListCompleted | Event | 787 | |devicePropertyModifyCompleted | Event | 788 | |Disposed | Event | 789 | |jobStatusListCompleted | Event | 790 | |lastExportResetCompleted | Event | 791 | |organizationPropertyListCompleted | Event | 792 | |organizationPropertyModifyCompleted | Event | 793 | |psaCreateCustomTicketCompleted | Event | 794 | |psaCredentialsValidateCompleted | Event | 795 | |psaGetCustomTicketCompleted | Event | 796 | |psaReopenCustomTicketCompleted | Event | 797 | |psaResolveCustomTicketCompleted | Event | 798 | |SOAddCompleted | Event | 799 | |taskPauseMonitoringCompleted | Event | 800 | |taskResumeMonitoringCompleted | Event | 801 | |userAddCompleted | Event | 802 | |userRoleAddCompleted | Event | 803 | |userRoleGetCompleted | Event | 804 | |userRoleListCompleted | Event | 805 | |versionInfoGetCompleted | Event | 806 | |Abort | Method | 807 | |accessGroupAdd | Method | 808 | |accessGroupAddAsync | Method | 809 | |accessGroupGet | Method | 810 | |accessGroupGetAsync | Method | 811 | |accessGroupList | Method | 812 | |accessGroupListAsync | Method | 813 | |acknowledgeNotification | Method | 814 | |acknowledgeNotificationAsync | Method | 815 | |activeIssuesList | Method | 816 | |activeIssuesListAsync | Method | 817 | |BeginaccessGroupAdd | Method | 818 | |BeginaccessGroupGet | Method | 819 | |BeginaccessGroupList | Method | 820 | |BeginacknowledgeNotification | Method | 821 | |BeginactiveIssuesList | Method | 822 | |BegincustomerAdd | Method | 823 | |BegincustomerDelete | Method | 824 | |BegincustomerList | Method | 825 | |BegincustomerListChildren | Method | 826 | |BegincustomerModify | Method | 827 | |BegindeviceAssetInfoExportDevice | Method | 828 | |BegindeviceAssetInfoExportDeviceWithSettings | Method | 829 | |BegindeviceGet | Method | 830 | |BegindeviceGetStatus | Method | 831 | |BegindeviceList | Method | 832 | |BegindevicePropertyList | Method | 833 | |BegindevicePropertyModify | Method | 834 | |BeginjobStatusList | Method | 835 | |BeginlastExportReset | Method | 836 | |BeginorganizationPropertyList | Method | 837 | |BeginorganizationPropertyModify | Method | 838 | |BeginpsaCreateCustomTicket | Method | 839 | |BeginpsaCredentialsValidate | Method | 840 | |BeginpsaGetCustomTicket | Method | 841 | |BeginpsaReopenCustomTicket | Method | 842 | |BeginpsaResolveCustomTicket | Method | 843 | |BeginSOAdd | Method | 844 | |BegintaskPauseMonitoring | Method | 845 | |BegintaskResumeMonitoring | Method | 846 | |BeginuserAdd | Method | 847 | |BeginuserRoleAdd | Method | 848 | |BeginuserRoleGet | Method | 849 | |BeginuserRoleList | Method | 850 | |BeginversionInfoGet | Method | 851 | |CancelAsync | Method | 852 | |CreateObjRef | Method | 853 | |customerAdd | Method | 854 | |customerAddAsync | Method | 855 | |customerDelete | Method | 856 | |customerDeleteAsync | Method | 857 | |customerList | Method | 858 | |customerListAsync | Method | 859 | |customerListChildren | Method | 860 | |customerListChildrenAsync | Method | 861 | |customerModify | Method | 862 | |customerModifyAsync | Method | 863 | |deviceAssetInfoExportDevice | Method | 864 | |deviceAssetInfoExportDeviceAsync | Method | 865 | |deviceAssetInfoExportDeviceWithSettings | Method | 866 | |deviceAssetInfoExportDeviceWithSettingsAsync | Method | 867 | |deviceGet | Method | 868 | |deviceGetAsync | Method | 869 | |deviceGetStatus | Method | 870 | |deviceGetStatusAsync | Method | 871 | |deviceList | Method | 872 | |deviceListAsync | Method | 873 | |devicePropertyList | Method | 874 | |devicePropertyListAsync | Method | 875 | |devicePropertyModify | Method | 876 | |devicePropertyModifyAsync | Method | 877 | |Discover | Method | 878 | |Dispose | Method | 879 | |EndaccessGroupAdd | Method | 880 | |EndaccessGroupGet | Method | 881 | |EndaccessGroupList | Method | 882 | |EndacknowledgeNotification | Method | 883 | |EndactiveIssuesList | Method | 884 | |EndcustomerAdd | Method | 885 | |EndcustomerDelete | Method | 886 | |EndcustomerList | Method | 887 | |EndcustomerListChildren | Method | 888 | |EndcustomerModify | Method | 889 | |EnddeviceAssetInfoExportDevice | Method | 890 | |EnddeviceAssetInfoExportDeviceWithSettings | Method | 891 | |EnddeviceGet | Method | 892 | |EnddeviceGetStatus | Method | 893 | |EnddeviceList | Method | 894 | |EnddevicePropertyList | Method | 895 | |EnddevicePropertyModify | Method | 896 | |EndjobStatusList | Method | 897 | |EndlastExportReset | Method | 898 | |EndorganizationPropertyList | Method | 899 | |EndorganizationPropertyModify | Method | 900 | |EndpsaCreateCustomTicket | Method | 901 | |EndpsaCredentialsValidate | Method | 902 | |EndpsaGetCustomTicket | Method | 903 | |EndpsaReopenCustomTicket | Method | 904 | |EndpsaResolveCustomTicket | Method | 905 | |EndSOAdd | Method | 906 | |EndtaskPauseMonitoring | Method | 907 | |EndtaskResumeMonitoring | Method | 908 | |EnduserAdd | Method | 909 | |EnduserRoleAdd | Method | 910 | |EnduserRoleGet | Method | 911 | |EnduserRoleList | Method | 912 | |EndversionInfoGet | Method | 913 | |Equals | Method | 914 | |GetHashCode | Method | 915 | |GetLifetimeService | Method | 916 | |GetType | Method | 917 | |InitializeLifetimeService | Method | 918 | |jobStatusList | Method | 919 | |jobStatusListAsync | Method | 920 | |lastExportReset | Method | 921 | |lastExportResetAsync | Method | 922 | |organizationPropertyList | Method | 923 | |organizationPropertyListAsync | Method | 924 | |organizationPropertyModify | Method | 925 | |organizationPropertyModifyAsync | Method | 926 | |psaCreateCustomTicket | Method | 927 | |psaCreateCustomTicketAsync | Method | 928 | |psaCredentialsValidate | Method | 929 | |psaCredentialsValidateAsync | Method | 930 | |psaGetCustomTicket | Method | 931 | |psaGetCustomTicketAsync | Method | 932 | |psaReopenCustomTicket | Method | 933 | |psaReopenCustomTicketAsync | Method | 934 | |psaResolveCustomTicket | Method | 935 | |psaResolveCustomTicketAsync | Method | 936 | |SOAdd | Method | 937 | |SOAddAsync | Method | 938 | |taskPauseMonitoring | Method | 939 | |taskPauseMonitoringAsync | Method | 940 | |taskResumeMonitoring | Method | 941 | |taskResumeMonitoringAsync | Method | 942 | |ToString | Method | 943 | |userAdd | Method | 944 | |userAddAsync | Method | 945 | |userRoleAdd | Method | 946 | |userRoleAddAsync | Method | 947 | |userRoleGet | Method | 948 | |userRoleGetAsync | Method | 949 | |userRoleList | Method | 950 | |userRoleListAsync | Method | 951 | |versionInfoGet | Method | 952 | |versionInfoGetAsync | Method | 953 | |AllowAutoRedirect | Property | 954 | |ClientCertificates | Property | 955 | |ConnectionGroupName | Property | 956 | |Container | Property | 957 | |CookieContainer | Property | 958 | |Credentials | Property | 959 | |EnableDecompression | Property | 960 | |PreAuthenticate | Property | 961 | |Proxy | Property | 962 | |RequestEncoding | Property | 963 | ||Site | Property | 964 | |SoapVersion | Property | 965 | |Timeout | Property | 966 | |UnsafeAuthenticatedConnectionSharing | Property | 967 | |Url | Property | 968 | |UseDefaultCredentials | Property | 969 | |UserAgent | Property | 970 | 971 | # Appendix B - PS-NCentral cmdlets 972 | 973 | | Command | Synopsis | 974 | | --- | --- | 975 | | Get-NCAccessGroupList | Returns the list of AccessGroups at the specified CustomerID level. | 976 | | Get-NCActiveIssuesList | Returns the Active Issues on the CustomerID-level and below. | 977 | | Get-NCCustomerList | Returns a list of all customers and their data. ChildrenOnly when CustomerID is specified. | 978 | | Get-NCCustomerPropertyList | Returns a list of all Custom-Properties for the selected CustomerID(s). | 979 | | Get-NCDeviceID | Returns the DeviceID(s) for the given DeviceName(s). Case Sensitive, No Wildcards. | 980 | | Get-NCDeviceInfo | Returns the General details for the DeviceID(s). | 981 | | Get-NCDeviceList | Returns the Managed Devices for the given CustomerID(s) and Sites below. | 982 | | Get-NCDeviceLocal | Returns the DeviceID, CustomerID and some more Info for the Local Computer. | 983 | | Get-NCDeviceObject | Returns a Device and all asset-properties as an object. | 984 | | Get-NCDevicePropertyList | Returns the Custom Properties of the DeviceID(s). | 985 | | Get-NCDevicePropertyListFilter | Returns the Custom Properties of the Devices within the Filter(s). | 986 | | Get-NCDeviceStatus | Returns the Services for the DeviceID(s). | 987 | | Get-NCHelp | Shows a list of available PS-NCentral commands and the synopsis. | 988 | | Get-NCJobStatusList | Returns the Scheduled Jobs on the CustomerID-level and below. | 989 | | Get-NCProbeList | Returns the Probes for the given CustomerID(s). | 990 | | Get-NCServiceOrganizationList | Returns a list of all ServiceOrganizations and their data. | 991 | | Get-NCTimeOut | Returns the max. time in seconds to wait for data returning from a (Synchronous) NCentral API-request. | 992 | | Get-NCUserRoleList | Returns the list of Roles at the specified CustomerID level. | 993 | | NcConnected | Checks or initiates the NCentral connection. | 994 | | New-NCentralConnection | Connect to the NCentral server. | 995 | | Set-NCCustomerProperty | Fills the specified property(name) for the given CustomerID(s). | 996 | | Set-NCDeviceProperty | Fills the Custom Property for the DeviceID(s). | 997 | | Set-NCTimeOut | Sets the max. time in seconds to wait for data returning from a (Synchronous) NCentral API-request. | 998 |
999 | 1000 | # Appendix C – GetAllCustomerProperties.ps1 1001 | 1002 | ```powershell 1003 | # Define the command-line parameters to be used by the script 1004 | [CmdletBinding()] 1005 | Param( 1006 | [Parameter(Mandatory = $true)]$serverHost, 1007 | [Parameter(Mandatory = $true)]$JWT 1008 | ) 1009 | # Generate a pseudo-unique namespace to use with the New-WebServiceProxy 1010 | $NWSNameSpace = NAble + ([guid]::NewGuid()).ToString().Substring(25) 1011 | 1012 | # Bind to the namespace, using the Webserviceproxy 1013 | $bindingURL = "https://" + $serverHost + "/dms2/services2/ServerEI2?wsdl" 1014 | $nws = New-Webserviceproxy $bindingURL -Namespace ($NWSNameSpace) 1015 | 1016 | # Set up and execute the query 1017 | $Settings = New-Object System.Collections.ArrayList 1018 | $Settings.Add(@{key = "listSOs"; value = "True" }) 1019 | 1020 | #Attempt to connect 1021 | Try { 1022 | $CustomerList = $nws.customerList("", $JWT, $Settings) 1023 | $OrgPropertiesList = $nws.organizationPropertyList("", $JWT, $null, $false) 1024 | } 1025 | Catch { 1026 | Write-Host Could not connect: $($_.Exception.Message) 1027 | exit 1028 | } 1029 | 1030 | $OrgPropertiesHashTable = @{} 1031 | foreach ($Org in $OrgPropertiesList) { 1032 | $CustomerOrgProps = @{} 1033 | foreach ($p in $Org.properties) { $CustomerOrgProps[$p.label] = $p.value } 1034 | $OrgPropertiesHashTable[$Org.customerId] = $CustomerOrgProps 1035 | 1036 | } 1037 | 1038 | #Create customer report ArrayList 1039 | $CustomersReport = New-Object System.Collections.ArrayList 1040 | ForEach ($Entity in $CustomerList) { 1041 | $CustomerAssetInfo = @{} 1042 | 1043 | #Custom select the required columns, us Ordered to keep them in the order we list them in 1044 | ForEach ($item in $Entity.items) { $CustomerAssetInfo[$item.key] = $item.Value } 1045 | $o = [Ordered]@{ 1046 | ID = $CustomerAssetInfo[customer.customerid] 1047 | Name = $CustomerAssetInfo[customer.customername] 1048 | parentID = $CustomerAssetInfo[customer.parentid] 1049 | RegistrationToken = $CustomerAssetInfo[customer.registrationtoken] 1050 | } 1051 | 1052 | #Retrieve the properties for the given customer ID 1053 | $p = $OrgPropertiesHashTable[[int]$CustomerAssetInfo[customer.customerid]] 1054 | 1055 | #Merge the two hashtables 1056 | $o = $o + $p 1057 | #Cast the hashtable to a PSCustomObject 1058 | $o = [PSCustomObject]$o 1059 | 1060 | #Add the PSCustomObject to our CustomersReport ArrayLIst 1061 | $CustomersReport.Add($o) > $null 1062 | } 1063 | 1064 | #Output to the screen 1065 | $CustomersReport | Out-GridView 1066 | ``` 1067 | 1068 | # Appendix D – Customer Property variables 1069 | 1070 | These are the default properties returned in the customerlist. After connecting using PS-NCentral this list can also be found in the customervalidation property. 1071 | 1072 | ```powershell 1073 | $_ncsession.customervalidation 1074 | ``` 1075 | 1076 | - **postalcode** - (Value) Customer's zip/ postal code. 1077 | - **street1** - (Value) Address line 1 for the customer. Maximum of 100 characters. 1078 | - **street2** - (Value) Address line 2 for the customer. Maximum of 100 characters. 1079 | - **city** - (Value) Customer's city. 1080 | - **stateprov** - (Value) Customer's state/ province. 1081 | - **phone** - (Value) Phone number of the customer. 1082 | - **country** - (Value) Customer's country. Two character country code, see http://en.wikipedia.org/wiki/ISO\_3166-1\_alpha-2 for a list of country codes. 1083 | - **externalid** - (Value) An external reference id. 1084 | - **externalid2** - (Value) A second external reference id. 1085 | - **contactfirstname** - (Value) Customer contact's first name. 1086 | - **contactlastname** - (Value) Customer contact's last name. 1087 | - **contacttitle** - (Value) Customer contact's title. 1088 | - **contactdepartment** - (Value) Customer contact's department. 1089 | - **contactphonenumber** - (Value) Customer contact's telephone number. 1090 | - **contactext** - (Value) Customer contact's telephone extension. 1091 | - **contactemail** - (Value) Customer contact's email. Maximum of 100 characters. 1092 | - **registrationtoken** - (ReadOnly) For agent/probe-install validation. 1093 | - **licensetype** - (Value) The default license type of new devices for the customer. Must be Professional or Essential. Default is Essential. 1094 | 1095 | # Appendix E - All PS-Central Methods 1096 | | Name | 1097 | | --- | 1098 | |AccessGroupGet| 1099 | |AccessGroupList| 1100 | |ActiveIssuesList| 1101 | |Connect| 1102 | |CustomerAdd| 1103 | |CustomerList| 1104 | |CustomerListChildren| 1105 | |CustomerModify| 1106 | |DeviceAssetInfoExportDevice| 1107 | |DeviceAssetInfoExportDeviceWithSettings| 1108 | |DeviceGet| 1109 | |DeviceGetAppliance| 1110 | |DeviceGetStatus| 1111 | |DeviceList| 1112 | |DevicePropertyID| 1113 | |DevicePropertyList| 1114 | |DevicePropertyModify| 1115 | |ErrorHandler| 1116 | |GetHashCode| 1117 | |JobStatusList| 1118 | |OrganizationPropertyID| 1119 | |OrganizationPropertyList| 1120 | |OrganizationPropertyModify| 1121 | |ProcessData1| 1122 | |ProcessData2| 1123 | |UserAdd| 1124 | |UserRoleGet| 1125 | |UserRoleList| 1126 | 1127 | # Appendix F - Common Error Codes 1128 | 1129 | \# Connection-error (https): There was an error downloading .. 1130 | 1131 | \# 1012 - Thrown when mandatory settings are not present in "settings". 1132 | 1133 | \# 2001 - Required parameter is null - Thrown when null values are entered as inputs. 1134 | 1135 | \# 2001 - Unsupported version - Thrown when a version not specified above is entered as input. 1136 | 1137 | \# 2001 - Thrown when a bad username-password combination is input, or no PSA integration has been set up. 1138 | 1139 | \# 2100 - Thrown when invalid MSP N-central credentials are input. 1140 | 1141 | \# 2100 - Thrown when MSP-N-central credentials with MFA are used. 1142 | 1143 | \# 3010 - Maximum number of users reached. 1144 | 1145 | \# 3012 - Specified email address is already assigned to another user. 1146 | 1147 | \# 3014 - Creation of a user for the root customer (CustomerID 1) is not permitted. 1148 | 1149 | \# 3014 - When adding a user, must not be an LDAP user. 1150 | 1151 | \# 3020 - Account is locked 1152 | 1153 | \# 3022 - Customer/Site already exists. 1154 | 1155 | \# 3026 - Customer name length has exceeded 120 characters. 1156 | 1157 | \# 4000 - SessionID not found or has expired. 1158 | 1159 | \# 5000 - An unexpected exception occurred. 1160 | 1161 | \# 5000 - Query failed. 1162 | 1163 | \# 5000 - javax.validation.ValidationException: Unable to validate UI session --> often an expired password, even when using JWT. 1164 | 1165 | \# 9910 - Service Organization already exists. 1166 | 1167 | 1168 | 1169 | # Appendix G - Issue Status 1170 | 1171 | These codes can be used with the **-IssueStatus** option of the **Get-NCActiveIssuesList** command. In v1.3 code 1 and 5 naming was incorrect (swapped) 1172 | 1173 | ``` 1174 | 1 No Data 1175 | 2 Stale 1176 | 3 Normal --> Nothing returned 1177 | 4 Warning 1178 | 5 Failed 1179 | 6 Misconfigured 1180 | 7 Disconnected 1181 | 1182 | 11 Unacknowledged 1183 | 12 Acknowledged 1184 | ``` 1185 | 1186 | The API does not allow combinations of these filters. 1187 | 1188 | - **1-7** are reflected in the **notifstate**-property. 1189 | - **11** and **12** relate to the properties **numberofactivenotification** and **numberofacknowledgednotification**. 1190 | 1191 | 1192 | 1193 | # Appendix H - WebserviceProxy Alternative 1194 | 1195 | The command **WebServiceProxy** is **discontinued** by Microsoft in PowerShell versions after 5.1. Below you can find an example of retrieving data with **Invoke-RestMethod** and compare it to the use of WebserviceProxy. 1196 | 1197 | ```PowerShell 1198 | ## Retrieving data from N-Central by SOAP-API 1199 | 1200 | # Settings 1201 | $Servername = "ServerFQDN" 1202 | $UserName = "API-User-noMFA" ## Or "" when using JWT 1203 | $Password = "P@ssword" ## Or JWT (preferred) 1204 | 1205 | # Function for retrieving data without Webserviceproxy. 1206 | # Works for most (but not all!!) Methods in 1207 | # https://mothership.n-able.com/dms/javadoc_ei2/com/nable/nobj/ei2/ServerEI2_PortType.html 1208 | # Note: Complex passwords may contain characters that invalidate the SOAP-request. 1209 | Function GetNCData([String]$APIMethod,[String]$Username,[String]$PassOrJWT,$KeyPairs){ 1210 | 1211 | ## Process Keys 1212 | $MyKeys="" 1213 | ForEach($KeyPair in $KeyPairs){ 1214 | $MyKeys = $MyKeys + (" 1215 | 1216 | {0} 1217 | {1} 1218 | " -f ($KeyPair.Key),($KeyPair.Value)) 1219 | } 1220 | 1221 | ## Build SoapRequest (last line must be left-lined for terminating @") 1222 | $MySoapRequest =(@" 1223 | 1224 | 1225 | 1226 | 1227 | {1} 1228 | {2}{3} 1229 | 1230 | 1231 | 1232 | "@ -f $APIMethod, $Username, $PassOrJWT, $MyKeys) 1233 | #Write-Host $MySoapRequest ## For Debug/Educational purpose 1234 | 1235 | ## Request DataSet 1236 | $FullResponse = $null 1237 | Try{ 1238 | $FullResponse = Invoke-RestMethod -Uri $BindingURL -body $MySoapRequest -Method POST 1239 | } 1240 | Catch{ 1241 | Write-Host ("Could not connect: {0}." -f $_.Exception.Message ) 1242 | exit 1243 | } 1244 | 1245 | ## Process Returned DataSet 1246 | $ReturnClass = $FullResponse.envelope.body | Get-Member -MemberType Property 1247 | $ReturnProperty = $ReturnClass[0].Name 1248 | 1249 | Return $FullResponse.envelope.body.$ReturnProperty.return 1250 | } 1251 | # End of Function GetNCData 1252 | 1253 | #Start of Main Script 1254 | # Extract local configuration Info. 1255 | $ApplianceConfig = ("{0}\N-able Technologies\Windows Agent\config\ApplianceConfig.xml" -f ${Env:ProgramFiles(x86)}) 1256 | # Get appliance id 1257 | $ApplianceXML = [xml](Get-Content -Path $ApplianceConfig) 1258 | $ApplianceID = $ApplianceXML.ApplianceConfig.ApplianceID 1259 | 1260 | # Prepare Connection 1261 | $BindingURL = ("https://{0}/dms2/services2/ServerEI2?wsdl" -f $ServerName) 1262 | 1263 | ## Add one or more keypairs to the array, depending on method parameter needs. 1264 | $KeyPairs = @() 1265 | $KeyPair = [PSObject]@{Key='applianceID'; Value=$ApplianceID;} 1266 | $KeyPairs += $KeyPair 1267 | 1268 | ## Pre Powershell 7 : Method using WebserviceProxy 1269 | #$Connection = New-Webserviceproxy $BindingURL 1270 | 1271 | # Get data from N-Central. Old and New line for comparison and easy conversion of existing scripts. 1272 | #$rc = $Connection.deviceGet($UserName, $Password, $KeyPairs) 1273 | $rc = GetNCData 'deviceGet' $UserName $Password $KeyPairs 1274 | 1275 | # Extract the Customer-ID and display. 1276 | $CustomerID = ($rc.info | where-object {$_.key -eq "device.customerid"}).value 1277 | $CustomerID 1278 | ``` 1279 | 1280 | 1281 | 1282 | 1283 | 1284 | # Credits 1285 | Special Thanks go to the following Partners and Community Members for their contributions to the **NC-API-Documentation** 1286 | * David Brooks of Premier Technology Solutions 1287 | * Adriaan Sluis of Tosch for PS-NCentral and notes 1288 | * Joshua Bennet of Impact Networking for notes on EiKeyValue usage 1289 | --------------------------------------------------------------------------------