logo

NJP

Retrieving Attachments from ServiceNow to Local Server using powershell

Import · Dec 09, 2021 · article

PowerShell script to fetch the attachments from ServiceNow to local server. Though exception handling is not implemented yet , this will do the job. Internally this script uses "Aggregate API , Table API , Attachment API" to dump the files.

Pre execution steps - Update below variables with relevant details

  • $userName : with User id of ServiceNow user record
  • $password : with User password
  • $logFilePath : with a log file path , all the execution log will be stored in it
  • $instanceUrl : with ServiceNow Instance to connect
  • $baseFolderPath: with initial folder where we need to create subfolder , by default that folder should exist
  • $tables : with list of table names for attachments need to be retrieved
  • $relativeTablePath: with table and its relevant subdirectory names, script will automatically create if folder doesnt exist
  • $tableFilters: with encoded queries to be applied on tables

Note:

  • Exception handling is not yet implemented
  • In some instances , huge files download might be incomplete, In such cases retrigger script
$userName = "XXXXXXXX"; #service user id
$password = "XXXXXXX"; #servicenow user password
$AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $userName, $password)));
$method ="get"; #http method

$logFilePath = 'XXXXXXXXXXXXX'; #stores the log generated by script

$instanceUrl = "https://XXXXXXX.service-now.com"; #instance url 
$stepCount =20; #step count to be used for pagination 

$baseFolderPath ="XXXXXXXXXX"; #base folder path , all sub folder will be created under this path


$tables = @('incident','change_request'); #list of tables for which attachments need to be dumped

#directory names to be created in base folder
$relativeTablePath = @{
    incident = "Incident"
    change_request = "Change Requests"
}
#encoded query to be applied when retrieving data from ServiceNow using table API
$tableFilters =@{
incident = "number=INC0010071"
change_request = "number=CHG0030151"        
}

function Get-NewFileName{

    param(
    [string]$suffix,
    [string]$fileName
    )
     $timeStamp =Get-Date -Format s | ForEach-Object { $_ -replace ":", "." } ;

    $newFileName = $suffix+"_"+$timeStamp+"_"+$fileName; 

    if($newFileName.Length -lt 255){
    return $newFileName ;
    }else{

      $newFileName =   $suffix+"_"+$fileName;

      if($newFileName.Length -lt 255) {
      return $newFileName;
      }else{
      return $fileName;

      }


    }


}


function Get-Dircreated{

param(
$folderPath
)

if( !(Test-Path $folderPath) ){

New-Item $folderPath -ItemType "directory"

}


}


function Logger{

    param(
    [string] $message
    )
    $timestamp = Get-Date -Format s;
    $message =  $timestamp +' >> ' +  $message;

    Add-content $logFilePath -value $message;

}


Write-Host "------- Started Processing , Check  "+ $logFilePath +" for execution details.. ---------" ;

Logger -message ("################  STARTED ##################");

ForEach($table in $tables){

Logger -message ("Processing data for table: " + $table); 

$aggHeaders = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$aggHeaders.Add('Authorization',('Basic {0}' -f $AuthInfo))
$aggHeaders.Add('Accept','application/json');
$aggHeaders.Add('Content-Type','application/json');

$aggBody = @{
sysparm_count=$true
}

#append encoded query if exists
if($tableFilters[$table] -and ($tableFilters[$table] -ne $null)){
$aggBody.sysparm_query = $tableFilters[$table]  
}

$aggUrl = $instanceUrl + "/api/now/stats/" + $table;


#calls aggregate API and fetches total record count
$aggResponse = Invoke-RestMethod  -Headers $aggHeaders -Method $method -Uri $aggUrl -Body $aggBody;

$totalRecordCount = $aggResponse.result.stats.count;

Logger -message ("Total Records found in table :: " + $table + " :: " +$totalRecordCount);


for(($step =0) ; $step -lt $totalRecordCount ; $step = $step+$stepCount){

Logger -message ("Fetching records from table :: "+ $table + " offset :: "+ $step + " to " +($step+$stepCount));

#handles record pagination
$recordHeaders = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$recordHeaders.Add('Authorization',('Basic {0}' -f $AuthInfo))
$recordHeaders.Add('Accept','application/json');
$recordHeaders.Add('Content-Type','application/json');

$recordUrl = $instanceUrl + "/api/now/table/" + $table;

$recordBody = @{
 sysparm_fields = "number,sys_id"
 sysparm_limit = $stepCount
 sysparm_offset = $step
}

#append encoded query if exists
if($tableFilters[$table] -and ($tableFilters[$table] -ne $null)){
$recordBody.sysparm_query = $tableFilters[$table]   
}


#get records using table api 

$recordResponses = Invoke-RestMethod  -Headers $recordHeaders -Method $method -Uri $recordUrl -Body $recordBody;

forEach($recordResp in $recordResponses.result){

    #fetch attachment meta data for each record

$attachMetaHeaders = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$attachMetaHeaders.Add('Authorization',('Basic {0}' -f $AuthInfo))
$attachMetaHeaders.Add('Accept','application/json');
$attachMetaHeaders.Add('Content-Type','application/json');

$attachMetaBody = @{
 sysparm_query = "table_name="+$table+"^table_sys_id="+$recordResp.sys_id
};

$attachmentMetaUrl = $instanceUrl + "/api/now/attachment" ;

Logger -message ("Fetching Attachment meta data for record :: "+ $recordResp.number +"  sys_id :: "+ $recordResp.sys_id);

$attachMetaResponses = Invoke-RestMethod  -Headers $attachMetaHeaders -Method $method -Uri $attachmentMetaUrl -Body $attachMetaBody;

forEach($attachMetaResp in $attachMetaResponses.result){

$attachContentHeaders = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$attachContentHeaders.Add('Authorization',('Basic {0}' -f $AuthInfo))
$attachContentHeaders.Add('Accept','*/*');
$attachContentHeaders.Add('Content-Type','application/json');

$attachContentUrl = $instanceUrl + "/api/now/attachment/" +$attachMetaResp.sys_id +"/file"; 

Logger -message ("Started downloading file :: "+ $attachMetaResp.file_name +"  from endpoint :: "+ $attachContentUrl);

$folderPath = Join-Path $baseFolderPath  $relativeTablePath[$table];

Get-Dircreated -folderPath $folderPath

$newFileName = Get-NewFileName -suffix $recordResp.number -fileName $attachMetaResp.file_name;
$filePath = Join-Path $folderPath $newFileName;

Invoke-RestMethod -Uri $attachContentUrl -Method $method -Headers $attachContentHeaders -OutFile $filePath ;

Logger -message ("File Downloaded successfully , New file name :: "+$newFileName  + " Path to file :: "+$filePath);

}   
}   
}
}
Logger -message ("################  COMPLETED ###################");
Write-Host "------- Processing Complete ---------" ;

Thanks !!

image

View original source

https://www.servicenow.com/community/now-platform-articles/retrieving-attachments-from-servicenow-to-local-server-using/ta-p/2321270