Public/attachment/New-SNOWAttachment.ps1
function New-SNOWAttachment { <# .SYNOPSIS Upload a file attachment to servicenow .DESCRIPTION Uploads a file as an attachment to a table record .NOTES System limitations on uploaded files, such as maximum file size and allowed attachment types are configured within ServiceNow. Default max file size is 1024MB. .LINK https://github.com/insomniacc/PSSnow/blob/main/docs/functions/New-SNOWAttachment.md .LINK https://docs.servicenow.com/csh?topicname=c_AttachmentAPI.html&version=latest .EXAMPLE $response = Get-SNOWUser -user_name "bruce.wayne" | New-SNOWAttachment -file "C:\temp\test.txt" -PassThru Write-Host "File attached: $($response.download_link)" Attaches test.txt to the user_record for bruce.wayne and returns the download link. #> [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory)] [ValidateScript({ if($_ | Test-Path){ if($_ | Test-Path -PathType Leaf){ $true }else{ Throw "Filepath cannot be a directory." } }else{ Throw "Unable to find file." } })] [System.IO.FileInfo] $File, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [ValidateScript({ $_ | Confirm-SysID -ValidateScript })] [string] [alias('table_sys_id')] [alias('SysID')] #The sys_id of the parent record to associate with the new attachment $Sys_ID, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [alias('table_name')] [alias('table')] [string] #The table associated with the parent record $Sys_Class_Name, [Parameter()] [switch] #The newly created attachment record will be returned $PassThru, [Parameter(DontShow)] [string] #Used to alter the filename passed to servicenow. $AttachedFilename, [Parameter(DontShow)] [switch] $AsBatchRequest ) begin { $BaseURL = "https://$($script:SNOWAuth.instance).service-now.com/api/now/v1/attachment/file" $RestMethod = 'POST' } process { if($AttachedFilename){ $Filename = $AttachedFilename }else{ $Filename = $File.Name } $URI = "$BaseURL`?file_name=$Filename&table_name=$Sys_Class_Name&table_sys_id=$Sys_ID" try{ if($PSVersionTable.PSEdition -eq "Core"){ #I'm unsure currently of the best method to get the mime type on core. #Setting to a generic octet stream is generally accepted for anything but will cause classifications issues, e.g user photo uploads. #For now I've put a simple mapping based on ext $MimeType = switch ($File.Extension) { '.aac' {'audio/aac'} '.abw' {'application/x-abiword'} '.arc' {'application/x-freearc'} '.avif' {'image/avif'} '.avi' {'video/x-msvideo'} '.azw' {'application/vnd.amazon.ebook'} '.bin' {'application/octet-stream'} '.bmp' {'image/bmp'} '.bz' {'application/x-bzip'} '.bz2' {'application/x-bzip2'} '.cda' {'application/x-cdf'} '.csh' {'application/x-csh'} '.css' {'text/css'} '.csv' {'text/csv'} '.doc' {'application/msword'} '.docx' {'application/vnd.openxmlformats-officedocument.wordprocessingml.document'} '.eot' {'application/vnd.ms-fontobject'} '.epub' {'application/epub+zip'} '.gz' {'application/gzip'} '.gif' {'image/gif'} '.htm' {'text/html'} '.html' {'text/html'} '.ico' {'image/vnd.microsoft.icon'} '.ics' {'text/calendar'} '.jar' {'application/java-archive'} '.jpeg' {'image/jpeg'} '.jpg' {'image/jpeg'} '.js' {'text/javascript'} '.json' {'application/json'} '.jsonld' {'application/ld+json'} '.mid' {'audio/midi'} '.midi' {'audio/midi'} '.mjs' {'text/javascript'} '.mp3' {'audio/mpeg'} '.mp4' {'video/mp4'} '.mpeg' {'video/mpeg'} '.mpkg' {'application/vnd.apple.installer+xml'} '.odp' {'application/vnd.oasis.opendocument.presentation'} '.ods' {'application/vnd.oasis.opendocument.spreadsheet'} '.odt' {'application/vnd.oasis.opendocument.text'} '.oga' {'audio/ogg'} '.ogv' {'video/ogg'} '.ogx' {'application/ogg'} '.opus' {'audio/opus'} '.otf' {'font/otf'} '.png' {'image/png'} '.pdf' {'application/pdf'} '.php' {'application/x-httpd-php'} '.ppt' {'application/vnd.ms-powerpoint'} '.pptx' {'application/vnd.openxmlformats-officedocument.presentationml.presentation'} '.rar' {'application/vnd.rar'} '.rtf' {'application/rtf'} '.sh' {'application/x-sh'} '.svg' {'image/svg+xml'} '.tar' {'application/x-tar'} '.tif' {'image/tiff'} '.tiff' {'image/tiff'} '.ts' {'video/mp2t'} '.ttf' {'font/ttf'} '.txt' {'text/plain'} '.vsd' {'application/vnd.visio'} '.wav' {'audio/wav'} '.weba' {'audio/webm'} '.webm' {'video/webm'} '.webp' {'image/webp'} '.woff' {'font/woff'} '.woff2' {'font/woff2'} '.xhtml' {'application/xhtml+xml'} '.xls' {'application/vnd.ms-excel'} '.xlsx' {'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'} '.xml' {'application/xml'} '.xul' {'application/vnd.mozilla.xul+xml'} '.zip' {'application/zip'} '.3gp' {'video/3gpp'} '.3g2' {'video/3gpp2'} '.7z' {'application/x-7z-compressed'} default {"application/octet-stream"} } }else{ $MimeType = [System.Web.MimeMapping]::GetMimeMapping($File) } }catch{ $MimeType = "application/octet-stream" } $Body = [System.IO.File]::ReadAllBytes($File) if($AsBatchRequest.IsPresent){ if($URI -match "(?<=service-now.com).*"){ return @{ id = (new-guid).guid url = $Matches[0] method = $RestMethod headers = @( @{ 'name' = 'Content-Type' 'value' = $MimeType }, @{ 'name' = 'Accept' 'value' = 'application/json' } ) exclude_response_headers = $False body = [convert]::ToBase64String($Body) } } } if($PSCmdlet.ShouldProcess($URI,$RestMethod)){ $Headers = @{ 'Content-Type' = $MimeType 'Accept' = 'application/json' } $RestSplat = @{ Headers = $Headers Method = $RestMethod URI = $URI Body = $Body } if($PSVersionTable.PSEdition -eq "Core"){ $RestSplat += @{SkipHeaderValidation = $true} } $Response = Invoke-SNOWWebRequest -UseRestMethod @RestSplat if($PassThru.IsPresent){ Return $Response.Result } } } } |