• 0
Dominique

Replace IP by DNS Name

Question

Hello, 

I got from Eric and Josh a script which is having the basic code to do the task I need. I would like to replace the IP Address/DNS Name by the DNS Name for all our servers.

#########################      API Function Script      #########################
#------------------------------------------------------------------------------------------------------------
# Prerequisites:
#
# Requires -Version 3
#------------------------------------------------------------------------------------------------------------
# Initialize Variables
<# account info #>
$accessId = "1234567890-"
$accessKey = '1234567890-'
$company = "xxxxxxxx"
#------------------------------------------------------------------------------------------------------------
# Functionize the reusable code that builds and executes the query
function Send-Request() {
    Param(
        [Parameter(position = 0, Mandatory = $true)]
        [string]$path,
        [Parameter(position = 1, Mandatory = $false)]
        [string]$httpVerb = 'GET',
        [Parameter(position = 2, Mandatory = $false)]
        [string]$queryParams,
        [Parameter(position = 3, Mandatory = $false)]
        [PSObject]$data
    )
    # Use TLS 1.2
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    <# Construct URL #>
    $url = "https://$company.logicmonitor.com/santaba/rest$path$queryParams"
    <# Get current time in milliseconds #>
    $epoch = [Math]::Round((New-TimeSpan -start (Get-Date -Date "1/1/1970") -end (Get-Date).ToUniversalTime()).TotalMilliseconds)
    <# Concatenate Request Details #>
    $requestVars = $httpVerb + $epoch + $data + $path
    <# Construct Signature #>
    $hmac = New-Object System.Security.Cryptography.HMACSHA256
    $hmac.Key = [Text.Encoding]::UTF8.GetBytes($accessKey)
    $signatureBytes = $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($requestVars))
    $signatureHex = [System.BitConverter]::ToString($signatureBytes) -replace '-'
    $signature = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($signatureHex.ToLower()))
    <# Construct Headers #>
    $auth = 'LMv1 ' + $accessId + ':' + $signature + ':' + $epoch
    $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $headers.Add("Authorization", $auth)
    $headers.Add("Content-Type", 'application/json')
    $headers.Add("X-version", '2')
    <# Make request & retry if failed due to rate limiting #>
    $Stoploop = $false
    do {
        try {
            <# Make Request #>
            $response = Invoke-RestMethod -Uri $url -Method $httpVerb -Body $data -Header $headers
            $Stoploop = $true
        } catch {
            switch ($_) {
                { $_.Exception.Response.StatusCode.value__ -eq 429 } {
                    Write-Host "Request exceeded rate limit, retrying in 60 seconds..."
                    Start-Sleep -Seconds 60
                    $response = Invoke-RestMethod -Uri $url -Method $httpVerb -Body $data -Header $headers
                }
                { $_.Exception.Response.StatusCode.value__ } {
                    Write-Host "Request failed, not as a result of rate limiting"
                    # Dig into the exception to get the Response details.
                    # Note that value__ is not a typo.
                    Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
                    Write-Host "StatusDescription:" $_.Exception.Response.StatusCode
                    $_.ErrorDetails.Message -match '{"errorMessage":"([\d\S\s]+)","errorCode":(\d+),'
                    Write-Host "LM ErrorMessage" $matches[1]
                    Write-Host "LM ErrorCode" $matches[2]
                    $response = $null
                    $Stoploop = $true
                }
                default {
                    Write-Host "An Unknown Exception occurred:"
                    Write-Host $_ | Format-List -Force
                $response = $null
                $Stoploop = $true
            }
        }
    }
} While ($Stoploop -eq $false)
Return $response
}
# Get list of Resources

$members         = {VRPSCCMRS01}

$httpVerb = 'GET'
$resourcePath = "/device/devices/$member.id"
$queryParams = $null
$data = $null
$results = Send-Request $resourcePath $httpVerb $queryParams $data
foreach ($hostname in $results.items) {
    $sysname = (($hostname.systemProperties) | Where-Object -Property "Name" -eq "system.sysname").value
    if ($sysname -match "\.") {
        $newName = $sysname.split(".")[0]
    } else {
        $newName = $sysname
    }
    if ($hostname.Name -eq $newName) {
        Write-Host "Name `"$($hostname.displayName)`" matched no change needed"
    } else {
        $httpVerb = 'PATCH'
        $resourcePath = "/device/devices/$($deviceId)"
        $queryParams = "?patchFields=Name&opType=replace"
        $data = @{
            Name = $newName
        } | ConvertTo-Json
        Send-Request $resourcePath $httpVerb $queryParams $data
        Write-Host "Changed host id $($hostname.id) Name from `"$($hostname.ips)`" to `"$newName`"."
    }
}

but for now I am getting an error:

Request failed, not as a result of rate limiting
StatusCode: 404
StatusDescription: NotFound
LM ErrorMessage HTTP 404 Not Found
LM ErrorCode 1400

Any idea of the issue?

Test in progress

Thanks,
Dom

2020-07-29_22-24-26 LM Screen.png

Share this post


Link to post
Share on other sites

Recommended Posts

  • 0

I have added:

# Get list of Resources

$members         = {VRPSCCMRS01}

As I would like to test the script on only one machine VRPSCCMRS01

Thanks,
Dom

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)

Hello,

I am trying 

$resourcePath = "/device/devices/128"

considering the system.deviceid of VRPSCCMRS01 is 128

but the script does not give anything ... no message that it failed, no message that the replacement was successfully done!

Thanks,

Dom

Edited by Dominique

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)

Hello,

The current status of the script is:
#########################      API Function Script      #########################
#------------------------------------------------------------------------------------------------------------
# Prerequisites:
#
# Requires -Version 3
#------------------------------------------------------------------------------------------------------------
# Initialize Variables
<# account info #>
$accessId = "xxxxxxxxxxxxxxxxxxxxxxxx"
$accessKey = 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'
$company = "12345678"
#------------------------------------------------------------------------------------------------------------
# Functionize the reusable code that builds and executes the query
function Send-Request() {
    Param(
        [Parameter(position = 0, Mandatory = $true)]
        [string]$path,
        [Parameter(position = 1, Mandatory = $false)]
        [string]$httpVerb = 'GET',
        [Parameter(position = 2, Mandatory = $false)]
        [string]$queryParams,
        [Parameter(position = 3, Mandatory = $false)]
        [PSObject]$data
    )
    # Use TLS 1.2
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    <# Construct URL #>
#    $url = "https://$company.logicmonitor.com/santaba/rest$path$queryParams"
    $url = "https://$company.logicmonitor.com/santaba/rest/device/devices/128"
    <# Get current time in milliseconds #>
    $epoch = [Math]::Round((New-TimeSpan -start (Get-Date -Date "1/1/1970") -end (Get-Date).ToUniversalTime()).TotalMilliseconds)
    <# Concatenate Request Details #>
    $requestVars = $httpVerb + $epoch + $data + $path
    <# Construct Signature #>
    $hmac = New-Object System.Security.Cryptography.HMACSHA256
    $hmac.Key = [Text.Encoding]::UTF8.GetBytes($accessKey)
    $signatureBytes = $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($requestVars))
    $signatureHex = [System.BitConverter]::ToString($signatureBytes) -replace '-'
    $signature = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($signatureHex.ToLower()))
    <# Construct Headers #>
    $auth = 'LMv1 ' + $accessId + ':' + $signature + ':' + $epoch
    $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $headers.Add("Authorization", $auth)
    $headers.Add("Content-Type", 'application/json')
    $headers.Add("X-version", '2')
    <# Make request & retry if failed due to rate limiting #>
    $Stoploop = $false
    do {
        try {
            <# Make Request #>
            $response = Invoke-RestMethod -Uri $url -Method $httpVerb -Body $data -Header $headers
            $Stoploop = $true
        } catch {
            switch ($_) {
                { $_.Exception.Response.StatusCode.value__ -eq 429 } {
                    Write-Host "Request exceeded rate limit, retrying in 60 seconds..."
                    Start-Sleep -Seconds 60
                    $response = Invoke-RestMethod -Uri $url -Method $httpVerb -Body $data -Header $headers
                }
                { $_.Exception.Response.StatusCode.value__ } {
                    Write-Host "Request failed, not as a result of rate limiting"
                    # Dig into the exception to get the Response details.
                    # Note that value__ is not a typo.
                    Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
                    Write-Host "StatusDescription:" $_.Exception.Response.StatusCode
                    $_.ErrorDetails.Message -match '{"errorMessage":"([\d\S\s]+)","errorCode":(\d+),'
                    Write-Host "LM ErrorMessage" $matches[1]
                    Write-Host "LM ErrorCode" $matches[2]
                    $response = $null
                    $Stoploop = $true
                }
                default {
                    Write-Host "An Unknown Exception occurred:"
                    Write-Host $_ | Format-List -Force
                $response = $null
                $Stoploop = $true
            }
        }
    }
} While ($Stoploop -eq $false)
Return $response
}
$httpVerb = 'GET'
$resourcePath = "device/devices/128"
$queryParams = $null
$data = $null
$results = Send-Request $resourcePath $httpVerb $queryParams $data
foreach ($hostname in $results.items) {
    $sysname = (($hostname.systemProperties) | Where-Object -Property "name" -eq "system.sysname").value
    if ($sysname -match "\.") {
        $newName = $sysname.split(".")[0]
    } else {
        $newName = $sysname
    }
    if ($hostname.Name -eq $newName) {
        Write-Host "Name `"$($hostname.displayName)`" matched no change needed"
    } else {
        $httpVerb = 'PATCH'
        $resourcePath = "/device/devices/128"
        $queryParams = "?patchFields=Name&opType=replace"
        $data = @{
            Name = $newName
        } | ConvertTo-Json
        Send-Request $resourcePath $httpVerb $queryParams $data
        Write-Host "Changed host id $($hostname.id) Name from `"$($hostname.ips)`" to `"$newName`"."
    }
}


Then I am getting an error:
PS C:\WINDOWS\system32> C:\Users\dduchemin\LogicMonitor_v3.ps1
Request failed, not as a result of rate limiting
StatusCode: 401
StatusDescription: Unauthorized
LM ErrorMessage Authentication failed
LM ErrorCode 1401

If I believe the documentation https://www.logicmonitor.com/support/rest-api-developers-guide/v2 this is an authentication failure

 

Does this mean this is the part:

# Initialize Variables

<# account info #>

$accessId = "xxxxxxxxxxxxxxx"

$accessKey = 'yyyyyyyyyyyyyyyyyyyyyyyyyyyy'

$company = "123456"

 

Which is incorrect?

Or is it something else?

 

Thanks,

Dom

Edited by Dominique
content

Share this post


Link to post
Share on other sites
  • 0
On 7/30/2020 at 9:57 PM, Dominique said:

How will i limit this script to run on the device VRPSCCMRS01?

How will i limit this script to run on the group  IT Ops Systems Management Group > Production > SCCM Servers?

 

Just getting back from vacation, so let me know if what questions are still pending. 

I assume you will be running this script as a propertysource? If so, then all you need to do is set the AppliesTo either to that one device or to that one group. You can use the AppliesTo wizard to help set that.

Instead of just writing the result to screen, I'd write it out as a property so that each device that has been changed will have a message in a custom property (ip2dns.renamed for example) detailing the result of the run.

You don't need to do an API call to get the device ID nor to get the address/name. Use tokens. You can simply set a variable (e.g. $hostname) to ##HOSTNAME##. When the collector runs the posh, it will replace those tokens where available. So, ##SYSTEM.HOSTNAME## would be replaced with the 10.6.195.211 and ##SYSTEM.DISPLAYNAME## would get replaced with VRPSCCMRS01. You can get the device id as well, using ##SYSTEM.DEVICEID##. 

You can also store the script credentials as properties, so they're not hard coded into the script. So it would become something like this:

# Initialize Variables

<# account info #>

$accessId = ##ip2dns.accessid##

$accessKey = ##ip2dns.accesskey##

$company = ##ip2dns.company##

I like to have a specific API token for each LogicModule that uses the API. It makes it really easy to track down the cause of a problem when something goes awry.

I would say that definitely looks like an authentication failure.

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)

Hello,

With this script

#########################      API Function Script      #########################
#------------------------------------------------------------------------------------------------------------
# Prerequisites:
#
# Requires -Version 3
#------------------------------------------------------------------------------------------------------------
# Initialize Variables
<# account info #>
$accessId = '8eNu95BddC4T2UkWgKcn'
$accessKey = 'blahblahblah'
$company = 'uclahealth'
#------------------------------------------------------------------------------------------------------------
# Functionize the reusable code that builds and executes the query
function Send-Request() {
    Param(
        [Parameter(position = 0, Mandatory = $true)]
        [string]$path,
        [Parameter(position = 1, Mandatory = $false)]
        [string]$httpVerb = 'GET',
        [Parameter(position = 2, Mandatory = $false)]
        [string]$queryParams,
        [Parameter(position = 3, Mandatory = $false)]
        [PSObject]$data
    )
    # Use TLS 1.2
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    <# Construct URL #>
#    $url = "https://$company.logicmonitor.com/santaba/rest$path$queryParams"
#    $url = "https://$company.logicmonitor.com/santaba/rest/device/devices/128"
    $url = "https://uclahealth.logicmonitor.com/santaba/uiv3/device/index.jsp#tree/-5-d-128"
    <# Get current time in milliseconds #>
    $epoch = [Math]::Round((New-TimeSpan -start (Get-Date -Date "1/1/1970") -end (Get-Date).ToUniversalTime()).TotalMilliseconds)
    <# Concatenate Request Details #>
#    $requestVars = $httpVerb + $epoch + $data + $path
    $requestVars = $httpVerb + $epoch
    <# Construct Signature #>
    $hmac = New-Object System.Security.Cryptography.HMACSHA256
    $hmac.Key = [Text.Encoding]::UTF8.GetBytes($accessKey)
    $signatureBytes = $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($requestVars))
    $signatureHex = [System.BitConverter]::ToString($signatureBytes) -replace '-'
    $signature = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($signatureHex.ToLower()))
    <# Construct Headers #>
    $auth = 'LMv2 ' + $accessId + ':' + $signature + ':' + $epoch
    $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $headers.Add("Authorization", $auth)
    $headers.Add("Content-Type", 'application/json')
    $headers.Add("X-version", '2')
    <# Make request & retry if failed due to rate limiting #>
    $Stoploop = $false
    do {
        try {
            <# Make Request #>
            $response = Invoke-RestMethod -Uri $url -Method $httpVerb -Body $data -Header $headers
            $Stoploop = $true
        } catch {
            switch ($_) {
                { $_.Exception.Response.StatusCode.value__ -eq 429 } {
                    Write-Host "Request exceeded rate limit, retrying in 60 seconds..."
                    Start-Sleep -Seconds 60
                    $response = Invoke-RestMethod -Uri $url -Method $httpVerb -Body $data -Header $headers
                }
                { $_.Exception.Response.StatusCode.value__ } {
                    Write-Host "Request failed, not as a result of rate limiting"
                    # Dig into the exception to get the Response details.
                    # Note that value__ is not a typo.
                    Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
                    Write-Host "StatusDescription:" $_.Exception.Response.StatusCode
                    $_.ErrorDetails.Message -match '{"errorMessage":"([\d\S\s]+)","errorCode":(\d+),'
                    Write-Host "LM ErrorMessage" $matches[1]
                    Write-Host "LM ErrorCode" $matches[2]
                    $response = $null
                    $Stoploop = $true
                }
                default {
                    Write-Host "An Unknown Exception occurred:"
                    Write-Host $_ | Format-List -Force
                $response = $null
                $Stoploop = $true
            }
        }
    }
} While ($Stoploop -eq $false)
Return $response
}
$httpVerb = 'GET'
$resourcePath = 'device/devices/128'
$queryParams = $null
$data = $null
$results = Send-Request $resourcePath $httpVerb $queryParams $data
foreach ($hostname in $results.items) {
    $sysname = (($hostname.systemProperties) | Where-Object -Property "name" -eq "system.sysname").value
    if ($sysname -match "\.") {
        $newName = $sysname.split(".")[0]
    } else {
        $newName = $sysname
    }
    if ($hostname.Name -eq $newName) {
        Write-Host "Name `"$($hostname.displayName)`" matched no change needed"
    } else {
        $httpVerb = 'PATCH'
        $resourcePath = '/device/devices/128'
        $queryParams = "?patchFields=Name&opType=replace"
        $data = @{
            Name = $newName
        } | ConvertTo-Json
        Send-Request $resourcePath $httpVerb $queryParams $data
        Write-Host "Changed host id $($hostname.id) Name from `"$($hostname.ips)`" to `"$newName`"."
    }
}

there is no more error but it is not changing the IPs to DNS names...

Thanks,
Dom

Edited by Stuart Weenig
Removing API key

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)
#########################      API Function Script      #########################
#------------------------------------------------------------------------------------------------------------
# Prerequisites:
#
# Requires -Version 3
#------------------------------------------------------------------------------------------------------------
# Initialize Variables
<# account info #>
$accessId = '8eNu95BddC4T2UkWgKcn'
$accessKey = 'blahblahblah'
$company = 'uclahealth'
#------------------------------------------------------------------------------------------------------------
# Functionize the reusable code that builds and executes the query
function Send-Request() {
    Param(
        [Parameter(position = 0, Mandatory = $true)]
        [string]$path,
        [Parameter(position = 1, Mandatory = $false)]
        [string]$httpVerb = 'GET',
        [Parameter(position = 2, Mandatory = $false)]
        [string]$queryParams,
        [Parameter(position = 3, Mandatory = $false)]
        [PSObject]$data
    )
    # Use TLS 1.2
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    <# Construct URL #>
#    $url = "https://$company.logicmonitor.com/santaba/rest$path$queryParams"
#    $url = "https://$company.logicmonitor.com/santaba/rest/device/devices/128"
    $url = "https://uclahealth.logicmonitor.com/santaba/uiv3/device/index.jsp#tree/-5-d-128"
    <# Get current time in milliseconds #>
    $epoch = [Math]::Round((New-TimeSpan -start (Get-Date -Date "1/1/1970") -end (Get-Date).ToUniversalTime()).TotalMilliseconds)
    <# Concatenate Request Details #>
#    $requestVars = $httpVerb + $epoch + $data + $path
    $requestVars = $httpVerb + $epoch
    <# Construct Signature #>
    $hmac = New-Object System.Security.Cryptography.HMACSHA256
    $hmac.Key = [Text.Encoding]::UTF8.GetBytes($accessKey)
    $signatureBytes = $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($requestVars))
    $signatureHex = [System.BitConverter]::ToString($signatureBytes) -replace '-'
    $signature = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($signatureHex.ToLower()))
    <# Construct Headers #>
    $auth = 'LMv2 ' + $accessId + ':' + $signature + ':' + $epoch
    $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $headers.Add("Authorization", $auth)
    $headers.Add("Content-Type", 'application/json')
    $headers.Add("X-version", '2')
    <# Make request & retry if failed due to rate limiting #>
    $Stoploop = $false
    do {
        try {
            <# Make Request #>
            $response = Invoke-RestMethod -Uri $url -Method $httpVerb -Body $data -Header $headers
            $Stoploop = $true
        } catch {
            switch ($_) {
                { $_.Exception.Response.StatusCode.value__ -eq 429 } {
                    Write-Host "Request exceeded rate limit, retrying in 60 seconds..."
                    Start-Sleep -Seconds 60
                    $response = Invoke-RestMethod -Uri $url -Method $httpVerb -Body $data -Header $headers
                }
                { $_.Exception.Response.StatusCode.value__ } {
                    Write-Host "Request failed, not as a result of rate limiting"
                    # Dig into the exception to get the Response details.
                    # Note that value__ is not a typo.
                    Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
                    Write-Host "StatusDescription:" $_.Exception.Response.StatusCode
                    $_.ErrorDetails.Message -match '{"errorMessage":"([\d\S\s]+)","errorCode":(\d+),'
                    Write-Host "LM ErrorMessage" $matches[1]
                    Write-Host "LM ErrorCode" $matches[2]
                    $response = $null
                    $Stoploop = $true
                }
                default {
                    Write-Host "An Unknown Exception occurred:"
                    Write-Host $_ | Format-List -Force
                $response = $null
                $Stoploop = $true
            }
        }
    }
} While ($Stoploop -eq $false)
Return $response
}
$httpVerb = 'GET'
#$resourcePath = 'device/devices/128'
$resourcePath = 'https://uclahealth.logicmonitor.com/santaba/uiv3/device/index.jsp#tree/-5-d-128'
$queryParams = $null
$data = $null
$results = Send-Request $resourcePath $httpVerb $queryParams $data
foreach ($hostname in $results.items) {
    $sysname = (($hostname.systemProperties) | Where-Object -Property "name" -eq "system.sysname").value
    if ($sysname -match "\.") {
        $newName = $sysname.split(".")[0]
    } else {
        $newName = $sysname
    }
    if ($hostname.Name -eq $newName) {
        Write-Host "Name `"$($hostname.displayName)`" matched no change needed"
    } else {
        $httpVerb = 'PATCH'
#        $resourcePath = '/device/devices/128'
        $resourcePath = 'https://uclahealth.logicmonitor.com/santaba/uiv3/device/index.jsp#tree/-5-d-128'
        $queryParams = "?patchFields=Name&opType=replace"
        $data = @{
            Name = $newName
        } | ConvertTo-Json
        Send-Request $resourcePath $httpVerb $queryParams $data
        Write-Host "Changed host id $($hostname.id) Name from `"$($hostname.ips)`" to `"$newName`"."
    }
}

Replacing

$resourcePath = '/device/devices/128'
by $resourcePath = 'https://uclahealth.logicmonitor.com/santaba/uiv3/device/index.jsp#tree/-5-d-128'...

No error either but still no update it seems that the loop : "foreach $hostname in $results.items" never starts...

it looks like the:

$results = Send-Request $resourcePath $httpVerb $queryParams $data

does not contain the correct information???? not sure what the Get should be followed by...

Get 'https://uclahealth.logicmonitor.com/santaba/uiv3/device/index.jsp#tree/-5-d-128' 'Name' 'Name'

Thanks,

Dom

Edited by Stuart Weenig
Removing API key

Share this post


Link to post
Share on other sites
  • 0
7 hours ago, Dominique said:

by $resourcePath = 'https://uclahealth.logicmonitor.com/santaba/uiv3/device/index.jsp#tree/-5-d-128'...

 

This wouldn't work because it's not calling the API. What's the name of the propertysource? I can take a look on the back end to see what's up.

Share this post


Link to post
Share on other sites
  • 0

Hello,

This is to access the machine VRPSCCMRS01 for the testing of the script to limit it to one machine at the beginning, not impacting all machines...

The Property is system.deviceId.

Thanks,

Dom

2020-08-04_7-14-30 VRPSCCMRS01 Manage.png

2020-08-04_7-18-01 VRPSCCMRS01 Property.png

Share this post


Link to post
Share on other sites
  • 0
7 hours ago, Dominique said:

# $url = "https://$company.logicmonitor.com/santaba/rest$path$queryParams"

This is the URL that should be used, however, it has to be in the proper format. So, let's look at $path and $queryParams to make sure they contain what they should so that the path is a properly formed URI.

$path would need to start with a forward slash (/). $queryParams would need to start with a question mark and be a list of ampersand separated key value pairs (e.g. ?size=1&page=3). It looks like both of those are good, but commented out currently.

FYI, you can't change the display name if you're doing a GET. You'll have to do PATCH. More info here. It would be something like this:

#Request Info
httpVerb ='PATCH'
resourcePath = '/device/devices/435'
queryParams = '?patchFields=enableNetflow,netflowCollectorId'
data = '{"enableNetflow":true,"netflowCollectorId":85}'
#Request Info
httpVerb ='PATCH'
resourcePath = '/device/devices/435'
queryParams = '?patchFields=displayName'
data = '{"displayName":"newName"}'

 

Share this post


Link to post
Share on other sites
  • 0
13 minutes ago, Stuart Weenig said:

This is the URL that should be used, however, it has to be in the proper format. So, let's look at $path and $queryParams to make sure they contain what they should so that the path is a properly formed URI.

$path would need to start with a forward slash (/). $queryParams would need to start with a question mark and be a list of ampersand separated key value pairs (e.g. ?size=1&page=3). It looks like both of those are good, but commented out currently.

FYI, you can't change the display name if you're doing a GET. You'll have to do PATCH. More info here. It would be something like this:


#Request Info
httpVerb ='PATCH'
resourcePath = '/device/devices/435'
queryParams = '?patchFields=enableNetflow,netflowCollectorId'
data = '{"enableNetflow":true,"netflowCollectorId":85}'

#Request Info
httpVerb ='PATCH'
resourcePath = '/device/devices/435'
queryParams = '?patchFields=displayName'
data = '{"displayName":"newName"}'

 

I tried this before but I think it was erroring let me try it again..

Thanks,

Dom

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)

Hello,

#########################      API Function Script      #########################
#------------------------------------------------------------------------------------------------------------
# Prerequisites:
#
# Requires -Version 3
#------------------------------------------------------------------------------------------------------------
# Initialize Variables
<# account info #>
$accessId = '8eNu95BddC4T2UkWgKcn'
$accessKey = 'blahblahblah'
$company = 'uclahealth'
#------------------------------------------------------------------------------------------------------------
# Functionize the reusable code that builds and executes the query
function Send-Request() {
    Param(
        [Parameter(position = 0, Mandatory = $true)]
        [string]$path,
        [Parameter(position = 1, Mandatory = $false)]
        [string]$httpVerb = 'GET',
        [Parameter(position = 2, Mandatory = $false)]
        [string]$queryParams,
        [Parameter(position = 3, Mandatory = $false)]
        [PSObject]$data
    )
    # Use TLS 1.2
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    <# Construct URL #>
#    $url = "https://$company.logicmonitor.com/santaba/rest$path$queryParams"
    $url = "https://$company.logicmonitor.com/santaba/rest/device/devices/128"
    <# Get current time in milliseconds #>
    $epoch = [Math]::Round((New-TimeSpan -start (Get-Date -Date "1/1/1970") -end (Get-Date).ToUniversalTime()).TotalMilliseconds)
    <# Concatenate Request Details #>
    $requestVars = $httpVerb + $epoch + $data + $path
    <# Construct Signature #>
    $hmac = New-Object System.Security.Cryptography.HMACSHA256
    $hmac.Key = [Text.Encoding]::UTF8.GetBytes($accessKey)
    $signatureBytes = $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($requestVars))
    $signatureHex = [System.BitConverter]::ToString($signatureBytes) -replace '-'
    $signature = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($signatureHex.ToLower()))
    <# Construct Headers #>
    $auth = 'LMv2 ' + $accessId + ':' + $signature + ':' + $epoch
    $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $headers.Add("Authorization", $auth)
    $headers.Add("Content-Type", 'application/json')
    $headers.Add("X-version", '2')
    <# Make request & retry if failed due to rate limiting #>
    $Stoploop = $false
    do {
        try {
            <# Make Request #>
            $response = Invoke-RestMethod -Uri $url -Method $httpVerb -Body $data -Header $headers
            $Stoploop = $true
        } catch {
            switch ($_) {
                { $_.Exception.Response.StatusCode.value__ -eq 429 } {
                    Write-Host "Request exceeded rate limit, retrying in 60 seconds..."
                    Start-Sleep -Seconds 60
                    $response = Invoke-RestMethod -Uri $url -Method $httpVerb -Body $data -Header $headers
                }
                { $_.Exception.Response.StatusCode.value__ } {
                    Write-Host "Request failed, not as a result of rate limiting"
                    # Dig into the exception to get the Response details.
                    # Note that value__ is not a typo.
                    Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
                    Write-Host "StatusDescription:" $_.Exception.Response.StatusCode
                    $_.ErrorDetails.Message -match '{"errorMessage":"([\d\S\s]+)","errorCode":(\d+),'
                    Write-Host "LM ErrorMessage" $matches[1]
                    Write-Host "LM ErrorCode" $matches[2]
                    $response = $null
                    $Stoploop = $true
                }
                default {
                    Write-Host "An Unknown Exception occurred:"
                    Write-Host $_ | Format-List -Force
                $response = $null
                $Stoploop = $true
            }
        }
    }
} While ($Stoploop -eq $false)
Return $response
}
$httpVerb = 'GET'
$resourcePath = 'device/devices/128'
$queryParams = $null
$data = $null
$results = Send-Request $resourcePath $httpVerb $queryParams $data
foreach ($hostname in $results.items) {
    $sysname = (($hostname.systemProperties) | Where-Object -Property "name" -eq "system.sysname").value
    if ($sysname -match "\.") {
        $newName = $sysname.split(".")[0]
    } else {
        $newName = $sysname
    }
    if ($hostname.Name -eq $newName) {
        Write-Host "Name `"$($hostname.displayName)`" matched no change needed"
    } else {
    #Request Info
        $httpVerb = 'PATCH'
        $resourcePath = '/device/devices/128'
#       $queryParams = "?patchFields=Name&opType=replace"
        $queryParams = '?patchFields=displayName'
#       $data = @{
#           Name = $newName
#       } | ConvertTo-Json
        $data = '{"displayName":"newName"}'
        Send-Request $resourcePath $httpVerb $queryParams $data
        Write-Host "Changed host id $($hostname.id) Name from `"$($hostname.ips)`" to `"$newName`"."
    }
}

is giving an error:

Request failed, not as a result of rate limiting
StatusCode: 401
StatusDescription: Unauthorized
LM ErrorMessage HTTP 401 Unauthorized
LM ErrorCode 1400
 

Thanks,

Dom

Edited by Stuart Weenig
Removing API key

Share this post


Link to post
Share on other sites
  • 0
22 minutes ago, Dominique said:

Not sure what you are looking for:

Perhaps we are getting to the root of the problem. How are you running this script? If you're not running it using "Test Script" in the LM GUI, then it won't work. You will have to build some sort of LogicModule to run this code. My suggestion is that it be a PropertySource. They run regularly and can store the output of the execution as a property that you can use for grouping/reporting to identify which ones are getting renamed.

Have you started building a PropertySource? This would also let you not embed your api key and token in your script. I'll go back and edit your posts to remove them, but I suggest destroying that token and generating a new one.  Like, you should do that right now. Like, right now.

Share this post


Link to post
Share on other sites
  • 0

Hello,

The script is running from my Laptop using an "Administrator: Windows Powershell ISE" prompt as recommended:

"Once modified, this script can run from any computer. Please take caution when doing large scale modification like this as this is a production system and mistake/bugs can be difficult to revert. Below is a screenshot of a demo Postman request showing the payload format."

I was looking to limit thescale by using the device ID 128... only

No PropertySource was built... 


Thanks,

Dom
 

2020-08-04_8-19-10 OISSDUCHEMINLT 01.png

Share this post


Link to post
Share on other sites
  • 0

I went and disabled that API token. You should delete it and generate a new one.

I created two properties on VRPSCCMRS01: ip2dns.accessid and ip2dns.key, containing a new API token id and key. Your script should use ##IP2DNS.ACCESSID## ##IP2DNS.KEY## to pull those values into the script at runtime.

Your script should take on a form something like this:

$accessId = '##IP2DNS.ACCESSID##'
$accessKey = '##IP2DNS.KEY##'
$company = 'uclahealth'

$deviceID = '##SYSTEM.DEVICEID##'

$currentName = '##SYSTEM.HOSTNAME##'
$currentDisplayName = '##SYSTEM.DISPLAYNAME##'

if ($currentName -ne $currentDisplayName) {
  # do a patch here to update the Name
  # you need to patch to 
  # 'https://uclahealth.logicmonitor.com/santaba/rest/device/devices/$($deviceID)?patchFields=displayName'
  # and the payload of your HTTP request needs to have something like the following JSON:
  # {"displayName":"$currentName"}
  Write-Host "ip2dns.result=Changed IP $($currentName) to DNS $($currentDisplayName)"
} else {
  Write-Host "ip2dns.result=Did not change IP to DNS"
}

I'm not a Powershell guy, so while I can follow what your script is trying to do, I can't really help you write the function to do it.

Share this post


Link to post
Share on other sites
  • 0

Ok, perhaps I'm misunderstanding your goal. Do you want this to happen on an ongoing basis, or do you want something you can run once and be done, or run once every month?  By writing a PropertySource, you'll see it work much simpler (see above code) and it'll run continuously so all devices will be in the proper state.

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)

You understood perfectly the target it is to run it "usually" once to correct all IPs imported in the IP Address/DNS Name field by the DNS Name only

If they will be more later on except the scale is large it will be done manually...

Thanks,

Dom

Edited by Dominique

Share this post


Link to post
Share on other sites
  • 0

I think I was misunderstanding because I was going the route of running it continually, not once and done.

If you're just running it once, I think you're making it more difficult. By running it as a propertysource, you don't have to do one API GET to grab the displayName and name, you can grab those via tokens. Then all you have to do is a single patch for those devices that don't match.  By running it on your laptop, you have to do multiple API calls to get the details about what you want to compare, then another API call to make the change. Not only that, but by making it a PropertySource, you can easily limit it to just the one device without having to change your code.

For security reasons, if you're running it on your laptop, at least set the API key as an environment variable so that you don't accidentally post the token and key again to this publicly available forum.

Share this post


Link to post
Share on other sites
  • 0

I created the PropertySource "_Replace_IP_Address_by_DNSName"

Testing now...

Thanks,

Dom

Share this post


Link to post
Share on other sites
  • 0
auto.ip2dns.result

Changed IP 10.6.195.211 to DNS VRPSCCMRS01

No error during execution, the message at the end is successful but the change did  not happened.

Reviewing the script...

Thanks,

Dom

Share this post


Link to post
Share on other sites
  • 0
$accessId = '##IP2DNS.ACCESSID##'
$accessKey = '##IP2DNS.KEY##'
$company = 'uclahealth'

$deviceID = '##SYSTEM.DEVICEID##'

$currentName = '##SYSTEM.HOSTNAME##'
$currentDisplayName = '##SYSTEM.DISPLAYNAME##'

if ($currentName -ne $currentDisplayName) {
  # do a patch here to update the Name
  # you need to patch to 
  # 'https://uclahealth.logicmonitor.com/santaba/rest/device/devices/$($deviceID)?patchFields=displayName'
  # and the payload of your HTTP request needs to have something like the following JSON:
  # {"displayName":"$currentName"}
  #Request Info
        $httpVerb = 'PATCH'
        $resourcePath = '/device/devices/128'
        $queryParams = '?patchFields=displayName'
        $data = '{"displayName":"$currentName"}'
  Send-Request $resourcePath $httpVerb $queryParams $data
  Write-Host "ip2dns.result=Changed IP $($currentName) to DNS $($currentDisplayName)"
} else {
  Write-Host "ip2dns.result=Did not change IP to DNS"
}

this is the current script, I think I am missing something ...

BTW when doing "test script" in the PropertySource does it write the machine used during the test?

Thanks,

Dom

 

Share this post


Link to post
Share on other sites
  • 0

Hey, looking good. You should see the ip2dns.result property on the device now. 

It looks like you're trying to call the Send-Request function without actually having that function defined. It might be as simple as copying the Send-Request function from the script you were given. If that function is written properly, it should work. 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.