The CreatorCon Call for Content is officially open! Get started here.

How to automate File Attachment from MID Server to ServiceNow Incident?

PrinceK78263780
Kilo Contributor

Scenario:

  • There are approximately 2 lakh (200,000+) files stored on the MID Server.

  • Each file needs to be automatically attached to its respective Incident record in ServiceNow.

  • The file types vary — .msg, .png, .jpeg, .csv, .xml, .pdf, etc.

  • The file name follows a convention such as:
    INC12345_SampleFile.pdf
    where INC12345 represents the Incident Number.

Is it possible to achieve?

1 ACCEPTED SOLUTION

Vishal_Jaiswal
Mega Guru

Hi @PrinceK78263780 ,

 

Please follow the steps below.

Roadmap:
1. Go to the MID Server where the data is present, create 2 folders, one named
Source and the other Destination. Copy all files that are required from the MID
server and paste them into the Source folder.
2. Create a Scheduled Job in ServiceNow that runs at a defined interval and calls a Script Include, which creates a command in the ECC Queue.
4. The MID Server picks up the command and executes a PowerShell script.
5. The PowerShell script copies a file from a source folder, parses its name to find a
Incident Number finds the corresponding record in ServiceNow via the REST API and attaches the file to that record.

 

Step 1:
→ Go to the table MID Server Script File(ecc_agent_script_file) and create a new record.
→ Specify Name as Sample.ps1, make sure to add dot ps1 as suffix in Name.
→ Specify Parent as ‘Powershell’
→ Add the below PowerShell script
→ The logic in this PowerShell script is - Copy file/attachment from the Source folder to the destination
folder once at a time, and in the next iteration, it will delete the previously copied file from the destination
folder, then copy the next file/attachment from the source folder to the destination folder, and upload the
each file to the corresponding Incident record based on the file name through the Table API, and
Attachment API, where the file naming convention is IncidentNumber_FileName.Extension
Example: INC123456_Sample.pdf
→ In PowerShell, the Hash # symbol is used to define comments.

Powershell script:

# 1. ServiceNow Instance URL
$instanceUrl = "https://your-instance.service-now.com" # <-- IMPORTANT: UPDATE
THIS
# 2. ServiceNow User Credentials for REST API, which have read/write access to the incident table

$user = "Integration"
$pass = "YOUR_PASSWORD_HERE" # <-- IMPORTANT: UPDATE THIS
# 3. ServiceNow Incident Table Name
$Table = "incident" # <-- IMPORTANT: VERIFY THIS
# 4. File Paths on the MID Server
$sourcePath = "D:\src" # The source folder on the MID Server
$destinationPath = "D:\dest" # A Destination folder on the MID Server
$lastProcessedFilePath = "D:\dest\lastProcessedFile.txt" # Tracks the last
processed file

# --- End of Configuration ---
# Build authentication header for ServiceNow API calls
$base64AuthInfo =
[Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,
$pass)))
$headers = @{Authorization = ("Basic {0}" -f $base64AuthInfo)}

# Create the destination directory if it doesn't exist
if (-Not (Test-Path -Path $destinationPath)) {
New-Item -ItemType Directory -Path $destinationPath
}

# Get all files from the source path
$files = Get-ChildItem -Path $sourcePath -File

# Initialize last processed index from the tracking file
$lastIndex = 0
if (Test-Path $lastProcessedFilePath) {
$lastIndex = [int](Get-Content $lastProcessedFilePath)
}

# Check if there are more files to process
if ($lastIndex -lt $files.Count) {
$fileToCopy = $files[$lastIndex]

# Copy the next file to the destination path
Copy-Item -Path $fileToCopy.FullName -Destination $destinationPath -Force
Write-Host "Copied file: $($fileToCopy.Name) to $destinationPath"
try {
# Parse the filename (expected format: Incidentnumber_fileName.pdf)
$fileNameParts = $fileToCopy.Name -split '_'
if ($fileNameParts.Count -ge 2) {
$IncidentNumber = $fileNameParts[0]
Write-Host "Found Incident Number: $IncidentNumber"
# 1. Find the Incident record's sys_id using the Table API
$tableApiUrl =
"$($instanceUrl)/api/now/table/$($Table)?sysparm_query=number=$($IncidentNumber)&sysparm_limit=1&sysparm_fields=sys_id"
Write-Host "DEBUG: Querying ServiceNow with URL: $tableApiUrl"
$IncidentResponse = Invoke-RestMethod -Uri $tableApiUrl -Method Get
-Headers $headers
if ($IncidentResponse.result.Count -gt 0) {
$IncidentSysId = $IncidentResponse.result[0].sys_id
Write-Host "Found Incident Sys_ID: $IncidentSysId"
# 2. Attach the file using the Attachment API
$attachmentApiUrl =
"$($instanceUrl)/api/now/attachment/file?table_name=$($Table)&table_sys_id
=$($IncidentSysId)&file_name=$($fileToCopy.Name)"
$filePathOnMid = Join-Path -Path $destinationPath -ChildPath
$fileToCopy.Name
$attachmentResponse = Invoke-RestMethod -Uri $attachmentApiUrl
-Method Post -Headers $headers -InFile $filePathOnMid -ContentType
'application/octet-stream'
Write-Host "Successfully attached $($fileToCopy.Name) to $IncidentNumber.
Attachment Sys_ID: $($attachmentResponse.result.sys_id)"
}
else {
Write-Host "ERROR: Could not find Incident record for number:
$IncidentNumber. Verify table name and permissions."
}
}
else {
Write-Host "ERROR: Filename '$($fileToCopy.Name)' is not in the expected
'Incidentnumber_fileName.pdf' format."
}
}
catch {
Write-Host "A critical error occurred during the ServiceNow API interaction. Full
error: $_"
}
# Update the tracking file with the new index
$lastIndex++
Set-Content -Path $lastProcessedFilePath -Value $lastIndex
}
# Optional: Delete the previously processed file from the temp folder
if ($lastIndex -gt 1) {
$previousFileIndex = $lastIndex - 2
$previousFile = $files[$previousFileIndex]
$previousFilePath = Join-Path -Path $destinationPath -ChildPath
$previousFile.Name
if (Test-Path $previousFilePath) {
Remove-Item -Path $previousFilePath -Force
Write-Host "Deleted previous file: $($previousFile.Name) from $destinationPath"
}
}

 

 

Step 2: The Script Include
This Script Include makes the logic reusable and separates it from the scheduling
mechanism.
Action: In ServiceNow, create a new Script Include (System Definition > Script
Includes).
● Name: FileProcessorUtil
● API Name: (will be auto-generated)
● Accessible from: All application scopes
● Script:

var FileProcessorUtil = Class.create();
FileProcessorUtil.prototype = {
initialize: function() {
},
/**
* Creates an ECC Queue entry to trigger the PowerShell script on the MID Server.
*/
triggerFileProcessor: function() {
try {
var midServer = 'mid.server.<name>'; // Your MID Server name
var gr = new GlideRecord('ecc_queue');
gr.initialize();
gr.agent = midServer;
gr.name = "powershell.exe \"&
.\\scripts\\PowerShell\\ExecutePowerShellScript.ps1\"";
gr.topic = 'Command';
gr.queue = 'output';
gr.state = 'ready';
gr.source = 'ServiceNow';
gr.payload = "<?xml version='1.0' encoding='UTF-8'?><parameters><parameter
name='skip_sensor' value='true'/></parameters>";
var eccSysId = gr.insert();
if (eccSysId) {
gs.info("File Processor job successfully created in ECC Queue. Sys ID: " +
eccSysId);
} else {
gs.error("Failed to create File Processor job in ECC Queue.");
}
} catch (e) {
gs.error("An error occurred in FileProcessorUtil.triggerFileProcessor: " +
e.getMessage());
}
},
type: 'FileProcessorUtil'
};


Step 3: The Scheduled Job
This job runs periodically to kick off the entire process.
Action: In ServiceNow, create a new Scheduled Job (System Definition > Scheduled
Jobs). Select "Automatically run a script of your choosing".
● Name: Trigger File Processor Script
● Active: Checked
● Run: Periodically
● Repeat Interval: 00:01:00 (1 minute is recommended)
● Run this script:

// Calls the Script Include to create the ECC Queue entry
new FileProcessorUtil().triggerFileProcessor();

Please modify above scripts as per your requirement and try.

View solution in original post

2 REPLIES 2

Vishal_Jaiswal
Mega Guru

Hi @PrinceK78263780 ,

 

Please follow the steps below.

Roadmap:
1. Go to the MID Server where the data is present, create 2 folders, one named
Source and the other Destination. Copy all files that are required from the MID
server and paste them into the Source folder.
2. Create a Scheduled Job in ServiceNow that runs at a defined interval and calls a Script Include, which creates a command in the ECC Queue.
4. The MID Server picks up the command and executes a PowerShell script.
5. The PowerShell script copies a file from a source folder, parses its name to find a
Incident Number finds the corresponding record in ServiceNow via the REST API and attaches the file to that record.

 

Step 1:
→ Go to the table MID Server Script File(ecc_agent_script_file) and create a new record.
→ Specify Name as Sample.ps1, make sure to add dot ps1 as suffix in Name.
→ Specify Parent as ‘Powershell’
→ Add the below PowerShell script
→ The logic in this PowerShell script is - Copy file/attachment from the Source folder to the destination
folder once at a time, and in the next iteration, it will delete the previously copied file from the destination
folder, then copy the next file/attachment from the source folder to the destination folder, and upload the
each file to the corresponding Incident record based on the file name through the Table API, and
Attachment API, where the file naming convention is IncidentNumber_FileName.Extension
Example: INC123456_Sample.pdf
→ In PowerShell, the Hash # symbol is used to define comments.

Powershell script:

# 1. ServiceNow Instance URL
$instanceUrl = "https://your-instance.service-now.com" # <-- IMPORTANT: UPDATE
THIS
# 2. ServiceNow User Credentials for REST API, which have read/write access to the incident table

$user = "Integration"
$pass = "YOUR_PASSWORD_HERE" # <-- IMPORTANT: UPDATE THIS
# 3. ServiceNow Incident Table Name
$Table = "incident" # <-- IMPORTANT: VERIFY THIS
# 4. File Paths on the MID Server
$sourcePath = "D:\src" # The source folder on the MID Server
$destinationPath = "D:\dest" # A Destination folder on the MID Server
$lastProcessedFilePath = "D:\dest\lastProcessedFile.txt" # Tracks the last
processed file

# --- End of Configuration ---
# Build authentication header for ServiceNow API calls
$base64AuthInfo =
[Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,
$pass)))
$headers = @{Authorization = ("Basic {0}" -f $base64AuthInfo)}

# Create the destination directory if it doesn't exist
if (-Not (Test-Path -Path $destinationPath)) {
New-Item -ItemType Directory -Path $destinationPath
}

# Get all files from the source path
$files = Get-ChildItem -Path $sourcePath -File

# Initialize last processed index from the tracking file
$lastIndex = 0
if (Test-Path $lastProcessedFilePath) {
$lastIndex = [int](Get-Content $lastProcessedFilePath)
}

# Check if there are more files to process
if ($lastIndex -lt $files.Count) {
$fileToCopy = $files[$lastIndex]

# Copy the next file to the destination path
Copy-Item -Path $fileToCopy.FullName -Destination $destinationPath -Force
Write-Host "Copied file: $($fileToCopy.Name) to $destinationPath"
try {
# Parse the filename (expected format: Incidentnumber_fileName.pdf)
$fileNameParts = $fileToCopy.Name -split '_'
if ($fileNameParts.Count -ge 2) {
$IncidentNumber = $fileNameParts[0]
Write-Host "Found Incident Number: $IncidentNumber"
# 1. Find the Incident record's sys_id using the Table API
$tableApiUrl =
"$($instanceUrl)/api/now/table/$($Table)?sysparm_query=number=$($IncidentNumber)&sysparm_limit=1&sysparm_fields=sys_id"
Write-Host "DEBUG: Querying ServiceNow with URL: $tableApiUrl"
$IncidentResponse = Invoke-RestMethod -Uri $tableApiUrl -Method Get
-Headers $headers
if ($IncidentResponse.result.Count -gt 0) {
$IncidentSysId = $IncidentResponse.result[0].sys_id
Write-Host "Found Incident Sys_ID: $IncidentSysId"
# 2. Attach the file using the Attachment API
$attachmentApiUrl =
"$($instanceUrl)/api/now/attachment/file?table_name=$($Table)&table_sys_id
=$($IncidentSysId)&file_name=$($fileToCopy.Name)"
$filePathOnMid = Join-Path -Path $destinationPath -ChildPath
$fileToCopy.Name
$attachmentResponse = Invoke-RestMethod -Uri $attachmentApiUrl
-Method Post -Headers $headers -InFile $filePathOnMid -ContentType
'application/octet-stream'
Write-Host "Successfully attached $($fileToCopy.Name) to $IncidentNumber.
Attachment Sys_ID: $($attachmentResponse.result.sys_id)"
}
else {
Write-Host "ERROR: Could not find Incident record for number:
$IncidentNumber. Verify table name and permissions."
}
}
else {
Write-Host "ERROR: Filename '$($fileToCopy.Name)' is not in the expected
'Incidentnumber_fileName.pdf' format."
}
}
catch {
Write-Host "A critical error occurred during the ServiceNow API interaction. Full
error: $_"
}
# Update the tracking file with the new index
$lastIndex++
Set-Content -Path $lastProcessedFilePath -Value $lastIndex
}
# Optional: Delete the previously processed file from the temp folder
if ($lastIndex -gt 1) {
$previousFileIndex = $lastIndex - 2
$previousFile = $files[$previousFileIndex]
$previousFilePath = Join-Path -Path $destinationPath -ChildPath
$previousFile.Name
if (Test-Path $previousFilePath) {
Remove-Item -Path $previousFilePath -Force
Write-Host "Deleted previous file: $($previousFile.Name) from $destinationPath"
}
}

 

 

Step 2: The Script Include
This Script Include makes the logic reusable and separates it from the scheduling
mechanism.
Action: In ServiceNow, create a new Script Include (System Definition > Script
Includes).
● Name: FileProcessorUtil
● API Name: (will be auto-generated)
● Accessible from: All application scopes
● Script:

var FileProcessorUtil = Class.create();
FileProcessorUtil.prototype = {
initialize: function() {
},
/**
* Creates an ECC Queue entry to trigger the PowerShell script on the MID Server.
*/
triggerFileProcessor: function() {
try {
var midServer = 'mid.server.<name>'; // Your MID Server name
var gr = new GlideRecord('ecc_queue');
gr.initialize();
gr.agent = midServer;
gr.name = "powershell.exe \"&
.\\scripts\\PowerShell\\ExecutePowerShellScript.ps1\"";
gr.topic = 'Command';
gr.queue = 'output';
gr.state = 'ready';
gr.source = 'ServiceNow';
gr.payload = "<?xml version='1.0' encoding='UTF-8'?><parameters><parameter
name='skip_sensor' value='true'/></parameters>";
var eccSysId = gr.insert();
if (eccSysId) {
gs.info("File Processor job successfully created in ECC Queue. Sys ID: " +
eccSysId);
} else {
gs.error("Failed to create File Processor job in ECC Queue.");
}
} catch (e) {
gs.error("An error occurred in FileProcessorUtil.triggerFileProcessor: " +
e.getMessage());
}
},
type: 'FileProcessorUtil'
};


Step 3: The Scheduled Job
This job runs periodically to kick off the entire process.
Action: In ServiceNow, create a new Scheduled Job (System Definition > Scheduled
Jobs). Select "Automatically run a script of your choosing".
● Name: Trigger File Processor Script
● Active: Checked
● Run: Periodically
● Repeat Interval: 00:01:00 (1 minute is recommended)
● Run this script:

// Calls the Script Include to create the ECC Queue entry
new FileProcessorUtil().triggerFileProcessor();

Please modify above scripts as per your requirement and try.

Thanks @Vishal_Jaiswal ,

It works.