[{"data":1,"prerenderedAt":1216},["ShallowReactive",2],{"post:\u002F2019\u002F01\u002F09\u002Fpowershell-connectwise-documents-api-uploading-a-document-or-attachment-to-a-ticket\u002F":3},{"post":4,"newer":1176,"older":1186,"related":1194,"series":1214},{"id":5,"title":6,"body":7,"canonical":1155,"categories":1156,"date":1159,"description":1160,"extension":1161,"featured":1162,"hero":1163,"image":20,"meta":1164,"navigation":557,"path":1165,"readingTime":436,"seo":1166,"series":1163,"seriesOrder":1163,"sites":1167,"source":1169,"stem":1170,"tags":1171,"updated":1173,"url":1174,"__hash__":1175},"blog\u002Fblog\u002F2019\u002F01\u002F09\u002Fpowershell-connectwise-documents-api-uploading-a-document-or-attachment-to-a-ticket.md","PowerShell: ConnectWise Documents API, Uploading a Document or Attachment to a Ticket",{"type":8,"value":9,"toc":1144},"minimark",[10,14,21,24,29,64,68,71,248,252,257,260,270,273,280,283,289,292,330,333,356,360,363,369,380,384,391,403,416,420,423,1122,1126,1140],[11,12,13],"p",{},"Phew, this one took a minute to figure out. ConnectWise has a form based documents API (technically not really API, but it’s the way you get yourself a document into a CW ticket). First is the really amazing documentation that CW provides around the documents API",[11,15,16],{},[17,18],"img",{"alt":19,"src":20},"ConnectWise Document API documentation showing the uploadsample GET endpoint and its Upload a document form","\u002Fuploads\u002F2019\u002F01\u002Fimage.png",[11,22,23],{},"Second is then working with PowerShell to handle streamed encoding correctly, build a multipart form data payload, and then getting it to actually send the correct thing. Ultimately there were some good learning steps here. Mainly on how to construct a proper content type of “multipart\u002Fform-data”. I’m writing this in hopes that many of you that are out there that are facing a similar challenge on getting documents to upload into CW via PowerShell aren’t faced with the same 2 day challenge I just had.",[25,26,28],"h2",{"id":27},"requirements","Requirements",[30,31,32,41,50,61],"ul",{},[33,34,35,36,40],"li",{},"PowerShell 5.1 or later, with ",[37,38,39],"code",{},"Invoke-RestMethod",".",[33,42,43,44,49],{},"A ConnectWise API member with its public key, private key and your company identifier. Creating one is covered in ",[45,46,48],"a",{"href":47},"\u002F2018\u002F12\u002F27\u002Fpowershell-connectwise-rest-api-query-contacts-by-email-address\u002F","Query Contacts by Email Address",". The member's security role must be allowed to add documents to the record you upload to.",[33,51,52,53,56,57,60],{},"Outbound HTTPS to the ConnectWise documents endpoint on ",[37,54,55],{},"na.myconnectwise.net",". Use the regular host here, not the ",[37,58,59],{},"api-"," one.",[33,62,63],{},"The file to upload, readable from the machine running the script, and the ID of the record (for example a ticket) it should be attached to.",[25,65,67],{"id":66},"parameters","Parameters",[11,69,70],{},"The script has no parameters. Edit these values in it before running.",[72,73,74,93],"table",{},[75,76,77],"thead",{},[78,79,80,84,87,90],"tr",{},[81,82,83],"th",{},"Name",[81,85,86],{},"Type",[81,88,89],{},"Required",[81,91,92],{},"Description",[94,95,96,116,133,150,171,188,210,229],"tbody",{},[78,97,98,104,107,110],{},[99,100,101],"td",{},[37,102,103],{},"$global:CWcompany",[99,105,106],{},"string",[99,108,109],{},"Yes",[99,111,112,113,40],{},"Your CW company identifier, ",[37,114,115],{},"\u003Ccompany-id>",[78,117,118,123,125,127],{},[99,119,120],{},[37,121,122],{},"$global:CWprivate",[99,124,106],{},[99,126,109],{},[99,128,129,130,40],{},"The API member's private key, ",[37,131,132],{},"\u003Cprivate-key>",[78,134,135,140,142,144],{},[99,136,137],{},[37,138,139],{},"$global:CWpublic",[99,141,106],{},[99,143,109],{},[99,145,146,147,40],{},"The API member's public key, ",[37,148,149],{},"\u003Cpublic-key>",[78,151,152,157,159,161],{},[99,153,154],{},[37,155,156],{},"$global:CWserver",[99,158,106],{},[99,160,109],{},[99,162,163,164,167,168,170],{},"The documents endpoint URL, ",[37,165,166],{},"https:\u002F\u002Fna.myconnectwise.net\u002Fv4_6_release\u002Fapis\u002F3.0\u002Fsystem\u002Fdocuments",". Despite the name it is the full endpoint, and it must not use the ",[37,169,59],{}," host.",[78,172,173,178,180,182],{},[99,174,175],{},[37,176,177],{},"$FilePath",[99,179,106],{},[99,181,109],{},[99,183,184,185,40],{},"Full path of the file to upload, for example ",[37,186,187],{},"C:\\path\\to\\file.jpg",[78,189,190,195,197,199],{},[99,191,192],{},[37,193,194],{},"recordType",[99,196,106],{},[99,198,109],{},[99,200,201,202,205,206,209],{},"Form field in ",[37,203,204],{},"$bodyLines",": the kind of record to attach to. ",[37,207,208],{},"Ticket"," in this script.",[78,211,212,217,219,221],{},[99,213,214],{},[37,215,216],{},"recordId",[99,218,106],{},[99,220,109],{},[99,222,201,223,225,226,40],{},[37,224,204],{},": the ID of that record, ",[37,227,228],{},"\u003Crecord-id>",[78,230,231,236,238,240],{},[99,232,233],{},[37,234,235],{},"Title",[99,237,106],{},[99,239,109],{},[99,241,201,242,244,245,40],{},[37,243,204],{},": the title of the document in CW, ",[37,246,247],{},"\u003Cdocument-title>",[25,249,251],{"id":250},"walkthrough","Walkthrough",[253,254,256],"h3",{"id":255},"encoding-issues","Encoding Issues",[11,258,259],{},"Mainly the Encoding Issues were around reading a file into PowerShell and then using Invoke-RestMethod to send it off. Typically you’d work in UTF-8, while that’s great in PS when working, sending that encoding via the Invoke-RestMethod seems to break things a little and none of the characters are correct, thus resulting in a data stream sent to your destination being garbled.",[11,261,262,266],{},[17,263],{"alt":264,"src":265},"Two Notepad windows of multipart form data: readable JPEG headers on the left, garbled characters on the right","\u002Fuploads\u002F2019\u002F01\u002Fimage-1.png",[267,268,269],"em",{},"Left – Proper Data | Right – Garbled Data",[11,271,272],{},"I happened to stumble, and by stumble, I’ve been searching the Google masters for quite a while trying to understand why the encoding wasn’t working correctly, upon this article:",[11,274,275],{},[45,276,277],{"href":277,"rel":278},"https:\u002F\u002Fsocial.technet.microsoft.com\u002FForums\u002Fen-US\u002F26f6a32e-e0e0-48f8-b777-06c331883555\u002Finvokewebrequest-encoding?forum=winserverpowershell",[279],"nofollow",[11,281,282],{},"which nicely pointed me here:",[11,284,285],{},[45,286,287],{"href":287,"rel":288},"https:\u002F\u002Fwindowsserver.uservoice.com\u002Fforums\u002F301869-powershell\u002Fsuggestions\u002F13685217-invoke-restmethod-and-invoke-webrequest-encoding-b",[279],[11,290,291],{},"Taking from this, I modified the following from:",[293,294,299],"pre",{"className":295,"code":296,"language":297,"meta":298,"style":298},"language-powershell shiki shiki-themes github-dark","$fileEnc = [System.Text.Encoding]::GetEncoding('UTF-8').GetString($fileBytes)\n","powershell","",[37,300,301],{"__ignoreMap":298},[302,303,306,310,314,317,320,323,327],"span",{"class":304,"line":305},"line",1,[302,307,309],{"class":308},"s95oV","$fileEnc ",[302,311,313],{"class":312},"snl16","=",[302,315,316],{"class":308}," [",[302,318,319],{"class":312},"System.Text.Encoding",[302,321,322],{"class":308},"]::GetEncoding(",[302,324,326],{"class":325},"sU2Wk","'UTF-8'",[302,328,329],{"class":308},").GetString($fileBytes)\n",[11,331,332],{},"To using the ISO 8859-1 encoding type of 28591. Converting this line to:",[293,334,336],{"className":295,"code":335,"language":297,"meta":298,"style":298},"$fileEnc = [System.Text.Encoding]::GetEncoding(28591).GetString($fileBytes)\n",[37,337,338],{"__ignoreMap":298},[302,339,340,342,344,346,348,350,354],{"class":304,"line":305},[302,341,309],{"class":308},[302,343,313],{"class":312},[302,345,316],{"class":308},[302,347,319],{"class":312},[302,349,322],{"class":308},[302,351,353],{"class":352},"sDLfK","28591",[302,355,329],{"class":308},[253,357,359],{"id":358},"multipart-boundaries","Multipart boundaries",[11,361,362],{},"The rest of the time was learning to deal with boundaries in a multipart\u002Fform-data payload. Essentially finding this article:",[11,364,365],{},[45,366,367],{"href":367,"rel":368},"https:\u002F\u002Fgist.github.com\u002Fweipah\u002F19bfdb14aab253e3f109",[279],[11,370,371,372,375,376,379],{},"This taught me a bit about the boundaries that need to be set and more-so having to use ",[37,373,374],{},"`r`n"," in different places, you’ll see this referenced the same way as in the link in my script below using the ",[37,377,378],{},"$LF"," variable.",[25,381,383],{"id":382},"usage","Usage",[11,385,386,387,390],{},"Edit the values listed above, save the script as ",[37,388,389],{},"Add-CWDocument.ps1"," and run it from a PowerShell prompt.",[293,392,394],{"className":295,"code":393,"language":297,"meta":298,"style":298},".\\Add-CWDocument.ps1\n",[37,395,396],{"__ignoreMap":298},[302,397,398,400],{"class":304,"line":305},[302,399,40],{"class":308},[302,401,402],{"class":352},"\\Add-CWDocument.ps1\n",[11,404,405,407,408,410,411,413,414,40],{},[37,406,39],{}," prints whatever the CW documents API returns for the upload. To attach to a different record, change ",[37,409,194],{}," and ",[37,412,216],{}," in ",[37,415,204],{},[25,417,419],{"id":418},"script","Script",[11,421,422],{},"Enjoy, here’s the full code layout:",[293,424,426],{"className":295,"code":425,"language":297,"meta":298,"style":298},"\u003C#\n.SYNOPSIS\n    Uploads a file to a ConnectWise record (for example a ticket) through the Documents API.\n.DESCRIPTION\n    Reads the file at $FilePath, re-encodes its bytes as ISO 8859-1 (code page 28591) so\n    Invoke-RestMethod does not garble them, builds a multipart\u002Fform-data body by hand\n    (recordType, recordId, Title and the file) and posts it to the ConnectWise\n    system\u002Fdocuments endpoint using an API member's keys.\n.EXAMPLE\n    .\\Add-CWDocument.ps1\n.NOTES\n    Author  : Thomas Lasswell (https:\u002F\u002Fwww.techcolumnist.com)\n    Version : 1.1 (2026-09-20)\n    History : 1.0 2019-01-09 Initial version.\n              1.1 2026-09-20 The uploaded file name now comes from $FilePath.\n    Requires: PowerShell 5.1 or later; ConnectWise API member with permission to add documents\n.LINK\n    https:\u002F\u002Fgist.github.com\u002Fweipah\u002F19bfdb14aab253e3f109\n#>\n\n# Initializations\n$global:CWcompany    = \"\u003Ccompany-id>\"\n$global:CWprivate    = \"\u003Cprivate-key>\"\n$global:CWpublic     = \"\u003Cpublic-key>\"\n# Don't use the api- URL here for the server.\n$global:CWserver     = \"https:\u002F\u002Fna.myconnectwise.net\u002Fv4_6_release\u002Fapis\u002F3.0\u002Fsystem\u002Fdocuments\"\n\n# CW auth string\n[string]$Authstring  = $CWcompany + '+' + $CWpublic + ':' + $CWprivate\n$encodedAuth         = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(($Authstring)))\n\n# CW headers\n$headers = New-Object \"System.Collections.Generic.Dictionary[[String],[String]]\"\n$headers.Add(\"Authorization\", \"Basic $encodedAuth\")\n\n# Read the file and build the multipart body\n$FilePath = 'C:\\path\\to\\file.jpg'\n$fileName = [System.IO.Path]::GetFileName($FilePath)\n$fileBytes = [System.IO.File]::ReadAllBytes($FilePath)\n$fileEnc = [System.Text.Encoding]::GetEncoding(28591).GetString($fileBytes)\n$boundary = [System.Guid]::NewGuid().ToString()\n$LF = \"`r`n\"\n\n$bodyLines = (\n    \"--$boundary\",\n    \"Content-Disposition: form-data; name=`\"recordType`\"$LF\",\n    \"Ticket\",\n    \"--$boundary\",\n    \"Content-Disposition: form-data; name=`\"recordId`\"$LF\",\n    \"\u003Crecord-id>\",\n    \"--$boundary\",\n    \"Content-Disposition: form-data; name=`\"Title`\"$LF\",\n    \"\u003Cdocument-title>\",\n    \"--$boundary\",\n    \"Content-Disposition: form-data; name=`\"file`\"; filename=`\"$fileName`\"\",\n    \"Content-Type: application\u002Foctet-stream$LF\",\n    $fileEnc,\n    \"--$boundary--$LF\"\n) -join $LF\n\n# Upload\nInvoke-RestMethod -Uri $CWserver -Method Post -ContentType \"multipart\u002Fform-data; boundary=`\"$boundary`\"\" -Body $bodyLines -Headers $headers\n",[37,427,428,434,442,448,456,462,468,474,480,488,494,502,508,514,520,526,532,540,546,552,559,565,582,597,612,618,633,638,644,682,704,709,715,729,753,758,764,775,791,807,824,840,856,861,872,886,905,913,924,941,949,960,977,985,996,1022,1034,1042,1056,1068,1073,1079],{"__ignoreMap":298},[302,429,430],{"class":304,"line":305},[302,431,433],{"class":432},"sAwPA","\u003C#\n",[302,435,437,439],{"class":304,"line":436},2,[302,438,40],{"class":352},[302,440,441],{"class":312},"SYNOPSIS\n",[302,443,445],{"class":304,"line":444},3,[302,446,447],{"class":432},"    Uploads a file to a ConnectWise record (for example a ticket) through the Documents API.\n",[302,449,451,453],{"class":304,"line":450},4,[302,452,40],{"class":352},[302,454,455],{"class":312},"DESCRIPTION\n",[302,457,459],{"class":304,"line":458},5,[302,460,461],{"class":432},"    Reads the file at $FilePath, re-encodes its bytes as ISO 8859-1 (code page 28591) so\n",[302,463,465],{"class":304,"line":464},6,[302,466,467],{"class":432},"    Invoke-RestMethod does not garble them, builds a multipart\u002Fform-data body by hand\n",[302,469,471],{"class":304,"line":470},7,[302,472,473],{"class":432},"    (recordType, recordId, Title and the file) and posts it to the ConnectWise\n",[302,475,477],{"class":304,"line":476},8,[302,478,479],{"class":432},"    system\u002Fdocuments endpoint using an API member's keys.\n",[302,481,483,485],{"class":304,"line":482},9,[302,484,40],{"class":352},[302,486,487],{"class":312},"EXAMPLE\n",[302,489,491],{"class":304,"line":490},10,[302,492,493],{"class":432},"    .\\Add-CWDocument.ps1\n",[302,495,497,499],{"class":304,"line":496},11,[302,498,40],{"class":352},[302,500,501],{"class":312},"NOTES\n",[302,503,505],{"class":304,"line":504},12,[302,506,507],{"class":432},"    Author  : Thomas Lasswell (https:\u002F\u002Fwww.techcolumnist.com)\n",[302,509,511],{"class":304,"line":510},13,[302,512,513],{"class":432},"    Version : 1.1 (2026-09-20)\n",[302,515,517],{"class":304,"line":516},14,[302,518,519],{"class":432},"    History : 1.0 2019-01-09 Initial version.\n",[302,521,523],{"class":304,"line":522},15,[302,524,525],{"class":432},"              1.1 2026-09-20 The uploaded file name now comes from $FilePath.\n",[302,527,529],{"class":304,"line":528},16,[302,530,531],{"class":432},"    Requires: PowerShell 5.1 or later; ConnectWise API member with permission to add documents\n",[302,533,535,537],{"class":304,"line":534},17,[302,536,40],{"class":352},[302,538,539],{"class":312},"LINK\n",[302,541,543],{"class":304,"line":542},18,[302,544,545],{"class":432},"    https:\u002F\u002Fgist.github.com\u002Fweipah\u002F19bfdb14aab253e3f109\n",[302,547,549],{"class":304,"line":548},19,[302,550,551],{"class":432},"#>\n",[302,553,555],{"class":304,"line":554},20,[302,556,558],{"emptyLinePlaceholder":557},true,"\n",[302,560,562],{"class":304,"line":561},21,[302,563,564],{"class":432},"# Initializations\n",[302,566,568,571,574,577,579],{"class":304,"line":567},22,[302,569,570],{"class":308},"$",[302,572,573],{"class":312},"global",[302,575,576],{"class":308},":CWcompany    ",[302,578,313],{"class":312},[302,580,581],{"class":325}," \"\u003Ccompany-id>\"\n",[302,583,585,587,589,592,594],{"class":304,"line":584},23,[302,586,570],{"class":308},[302,588,573],{"class":312},[302,590,591],{"class":308},":CWprivate    ",[302,593,313],{"class":312},[302,595,596],{"class":325}," \"\u003Cprivate-key>\"\n",[302,598,600,602,604,607,609],{"class":304,"line":599},24,[302,601,570],{"class":308},[302,603,573],{"class":312},[302,605,606],{"class":308},":CWpublic     ",[302,608,313],{"class":312},[302,610,611],{"class":325}," \"\u003Cpublic-key>\"\n",[302,613,615],{"class":304,"line":614},25,[302,616,617],{"class":432},"# Don't use the api- URL here for the server.\n",[302,619,621,623,625,628,630],{"class":304,"line":620},26,[302,622,570],{"class":308},[302,624,573],{"class":312},[302,626,627],{"class":308},":CWserver     ",[302,629,313],{"class":312},[302,631,632],{"class":325}," \"https:\u002F\u002Fna.myconnectwise.net\u002Fv4_6_release\u002Fapis\u002F3.0\u002Fsystem\u002Fdocuments\"\n",[302,634,636],{"class":304,"line":635},27,[302,637,558],{"emptyLinePlaceholder":557},[302,639,641],{"class":304,"line":640},28,[302,642,643],{"class":432},"# CW auth string\n",[302,645,647,650,652,655,657,660,663,666,669,672,674,677,679],{"class":304,"line":646},29,[302,648,649],{"class":308},"[",[302,651,106],{"class":312},[302,653,654],{"class":308},"]$Authstring  ",[302,656,313],{"class":312},[302,658,659],{"class":308}," $CWcompany ",[302,661,662],{"class":312},"+",[302,664,665],{"class":325}," '+'",[302,667,668],{"class":312}," +",[302,670,671],{"class":308}," $CWpublic ",[302,673,662],{"class":312},[302,675,676],{"class":325}," ':'",[302,678,668],{"class":312},[302,680,681],{"class":308}," $CWprivate\n",[302,683,685,688,690,692,695,698,701],{"class":304,"line":684},30,[302,686,687],{"class":308},"$encodedAuth         ",[302,689,313],{"class":312},[302,691,316],{"class":308},[302,693,694],{"class":312},"Convert",[302,696,697],{"class":308},"]::ToBase64String([",[302,699,700],{"class":312},"Text.Encoding",[302,702,703],{"class":308},"]::ASCII.GetBytes(($Authstring)))\n",[302,705,707],{"class":304,"line":706},31,[302,708,558],{"emptyLinePlaceholder":557},[302,710,712],{"class":304,"line":711},32,[302,713,714],{"class":432},"# CW headers\n",[302,716,718,721,723,726],{"class":304,"line":717},33,[302,719,720],{"class":308},"$headers ",[302,722,313],{"class":312},[302,724,725],{"class":352}," New-Object",[302,727,728],{"class":325}," \"System.Collections.Generic.Dictionary[[String],[String]]\"\n",[302,730,732,735,738,741,744,747,750],{"class":304,"line":731},34,[302,733,734],{"class":308},"$headers.Add(",[302,736,737],{"class":325},"\"Authorization\"",[302,739,740],{"class":312},",",[302,742,743],{"class":325}," \"Basic ",[302,745,746],{"class":308},"$encodedAuth",[302,748,749],{"class":325},"\"",[302,751,752],{"class":308},")\n",[302,754,756],{"class":304,"line":755},35,[302,757,558],{"emptyLinePlaceholder":557},[302,759,761],{"class":304,"line":760},36,[302,762,763],{"class":432},"# Read the file and build the multipart body\n",[302,765,767,770,772],{"class":304,"line":766},37,[302,768,769],{"class":308},"$FilePath ",[302,771,313],{"class":312},[302,773,774],{"class":325}," 'C:\\path\\to\\file.jpg'\n",[302,776,778,781,783,785,788],{"class":304,"line":777},38,[302,779,780],{"class":308},"$fileName ",[302,782,313],{"class":312},[302,784,316],{"class":308},[302,786,787],{"class":312},"System.IO.Path",[302,789,790],{"class":308},"]::GetFileName($FilePath)\n",[302,792,794,797,799,801,804],{"class":304,"line":793},39,[302,795,796],{"class":308},"$fileBytes ",[302,798,313],{"class":312},[302,800,316],{"class":308},[302,802,803],{"class":312},"System.IO.File",[302,805,806],{"class":308},"]::ReadAllBytes($FilePath)\n",[302,808,810,812,814,816,818,820,822],{"class":304,"line":809},40,[302,811,309],{"class":308},[302,813,313],{"class":312},[302,815,316],{"class":308},[302,817,319],{"class":312},[302,819,322],{"class":308},[302,821,353],{"class":352},[302,823,329],{"class":308},[302,825,827,830,832,834,837],{"class":304,"line":826},41,[302,828,829],{"class":308},"$boundary ",[302,831,313],{"class":312},[302,833,316],{"class":308},[302,835,836],{"class":312},"System.Guid",[302,838,839],{"class":308},"]::NewGuid().ToString()\n",[302,841,843,846,848,851,853],{"class":304,"line":842},42,[302,844,845],{"class":308},"$LF ",[302,847,313],{"class":312},[302,849,850],{"class":325}," \"",[302,852,374],{"class":352},[302,854,855],{"class":325},"\"\n",[302,857,859],{"class":304,"line":858},43,[302,860,558],{"emptyLinePlaceholder":557},[302,862,864,867,869],{"class":304,"line":863},44,[302,865,866],{"class":308},"$bodyLines ",[302,868,313],{"class":312},[302,870,871],{"class":308}," (\n",[302,873,875,878,881,883],{"class":304,"line":874},45,[302,876,877],{"class":325},"    \"--",[302,879,880],{"class":308},"$boundary",[302,882,749],{"class":325},[302,884,885],{"class":312},",\n",[302,887,889,892,895,897,899,901,903],{"class":304,"line":888},46,[302,890,891],{"class":325},"    \"Content-Disposition: form-data; name=",[302,893,894],{"class":352},"`\"",[302,896,194],{"class":325},[302,898,894],{"class":352},[302,900,378],{"class":308},[302,902,749],{"class":325},[302,904,885],{"class":312},[302,906,908,911],{"class":304,"line":907},47,[302,909,910],{"class":325},"    \"Ticket\"",[302,912,885],{"class":312},[302,914,916,918,920,922],{"class":304,"line":915},48,[302,917,877],{"class":325},[302,919,880],{"class":308},[302,921,749],{"class":325},[302,923,885],{"class":312},[302,925,927,929,931,933,935,937,939],{"class":304,"line":926},49,[302,928,891],{"class":325},[302,930,894],{"class":352},[302,932,216],{"class":325},[302,934,894],{"class":352},[302,936,378],{"class":308},[302,938,749],{"class":325},[302,940,885],{"class":312},[302,942,944,947],{"class":304,"line":943},50,[302,945,946],{"class":325},"    \"\u003Crecord-id>\"",[302,948,885],{"class":312},[302,950,952,954,956,958],{"class":304,"line":951},51,[302,953,877],{"class":325},[302,955,880],{"class":308},[302,957,749],{"class":325},[302,959,885],{"class":312},[302,961,963,965,967,969,971,973,975],{"class":304,"line":962},52,[302,964,891],{"class":325},[302,966,894],{"class":352},[302,968,235],{"class":325},[302,970,894],{"class":352},[302,972,378],{"class":308},[302,974,749],{"class":325},[302,976,885],{"class":312},[302,978,980,983],{"class":304,"line":979},53,[302,981,982],{"class":325},"    \"\u003Cdocument-title>\"",[302,984,885],{"class":312},[302,986,988,990,992,994],{"class":304,"line":987},54,[302,989,877],{"class":325},[302,991,880],{"class":308},[302,993,749],{"class":325},[302,995,885],{"class":312},[302,997,999,1001,1003,1006,1008,1011,1013,1016,1018,1020],{"class":304,"line":998},55,[302,1000,891],{"class":325},[302,1002,894],{"class":352},[302,1004,1005],{"class":325},"file",[302,1007,894],{"class":352},[302,1009,1010],{"class":325},"; filename=",[302,1012,894],{"class":352},[302,1014,1015],{"class":308},"$fileName",[302,1017,894],{"class":352},[302,1019,749],{"class":325},[302,1021,885],{"class":312},[302,1023,1025,1028,1030,1032],{"class":304,"line":1024},56,[302,1026,1027],{"class":325},"    \"Content-Type: application\u002Foctet-stream",[302,1029,378],{"class":308},[302,1031,749],{"class":325},[302,1033,885],{"class":312},[302,1035,1037,1040],{"class":304,"line":1036},57,[302,1038,1039],{"class":308},"    $fileEnc",[302,1041,885],{"class":312},[302,1043,1045,1047,1049,1052,1054],{"class":304,"line":1044},58,[302,1046,877],{"class":325},[302,1048,880],{"class":308},[302,1050,1051],{"class":325},"--",[302,1053,378],{"class":308},[302,1055,855],{"class":325},[302,1057,1059,1062,1065],{"class":304,"line":1058},59,[302,1060,1061],{"class":308},") ",[302,1063,1064],{"class":312},"-join",[302,1066,1067],{"class":308}," $LF\n",[302,1069,1071],{"class":304,"line":1070},60,[302,1072,558],{"emptyLinePlaceholder":557},[302,1074,1076],{"class":304,"line":1075},61,[302,1077,1078],{"class":432},"# Upload\n",[302,1080,1082,1084,1087,1090,1093,1096,1098,1101,1104,1106,1108,1110,1112,1114,1117,1119],{"class":304,"line":1081},62,[302,1083,39],{"class":352},[302,1085,1086],{"class":312}," -",[302,1088,1089],{"class":308},"Uri $CWserver ",[302,1091,1092],{"class":312},"-",[302,1094,1095],{"class":308},"Method Post ",[302,1097,1092],{"class":312},[302,1099,1100],{"class":308},"ContentType ",[302,1102,1103],{"class":325},"\"multipart\u002Fform-data; boundary=",[302,1105,894],{"class":352},[302,1107,880],{"class":308},[302,1109,894],{"class":352},[302,1111,749],{"class":325},[302,1113,1086],{"class":312},[302,1115,1116],{"class":308},"Body $bodyLines ",[302,1118,1092],{"class":312},[302,1120,1121],{"class":308},"Headers $headers\n",[25,1123,1125],{"id":1124},"notes","Notes",[30,1127,1128],{},[33,1129,1130,1131,1134,1135,1137,1138,40],{},"Update 2026-09-20 (v1.1): the file name in the multipart body was hard-coded as ",[37,1132,1133],{},"image001.jpg",", so every upload was named that way whatever ",[37,1136,177],{}," pointed at. It is now taken from ",[37,1139,177],{},[1141,1142,1143],"style",{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}",{"title":298,"searchDepth":436,"depth":436,"links":1145},[1146,1147,1148,1152,1153,1154],{"id":27,"depth":436,"text":28},{"id":66,"depth":436,"text":67},{"id":250,"depth":436,"text":251,"children":1149},[1150,1151],{"id":255,"depth":444,"text":256},{"id":358,"depth":444,"text":359},{"id":382,"depth":436,"text":383},{"id":418,"depth":436,"text":419},{"id":1124,"depth":436,"text":1125},"techcolumnist",[1157,1158],"ps","scripts","2019-01-09T09:46:57Z","PowerShell: upload a document or attachment to a ConnectWise ticket through the form-based Documents API.","md",false,null,{},"\u002Fblog\u002F2019\u002F01\u002F09\u002Fpowershell-connectwise-documents-api-uploading-a-document-or-attachment-to-a-ticket",{"title":6,"description":1160},[1168,1155],"lasswell","both","blog\u002F2019\u002F01\u002F09\u002Fpowershell-connectwise-documents-api-uploading-a-document-or-attachment-to-a-ticket",[1172,297],"connectwise","2026-09-20T00:00:00Z","\u002F2019\u002F01\u002F09\u002Fpowershell-connectwise-documents-api-uploading-a-document-or-attachment-to-a-ticket\u002F","z7WTqr0Vcs3unSRDBzgxRnfC2Q7ymk4vwZwNqXzmCeM",{"title":1177,"description":1178,"date":1179,"url":1180,"categories":1181,"tags":1182,"image":1163,"readingTime":305,"canonical":1155,"sites":1185,"series":1163,"seriesOrder":1163},"PowerShell: Get Exchange Mailboxes Over XXGB","Simple command turned crazy. I ended up coming up with this due to the fact we have duplicate display names and needed to update for Exchange Online to get mailbox sizes.","2021-02-18T14:46:29Z","\u002F2021\u002F02\u002F18\u002Fpowershell-get-exchange-mailboxes-over-xxgb\u002F",[1157,1158],[297,1183,1184],"exchange","exchange-online",[1168,1155],{"title":1187,"description":1188,"date":1189,"url":47,"categories":1190,"tags":1191,"image":1192,"readingTime":436,"canonical":1155,"sites":1193,"series":1163,"seriesOrder":1163},"PowerShell: ConnectWise REST API Query Contacts by Email Address","A PowerShell script that queries ConnectWise contacts by email address through the REST API.","2018-12-27T10:56:24Z",[1157,1158],[1172,297],"\u002Fuploads\u002F2018\u002F12\u002Fimage.png",[1168,1155],[1195,1186,1204],{"title":1196,"description":1197,"date":1198,"url":1199,"categories":1200,"tags":1201,"image":1163,"readingTime":450,"canonical":1155,"sites":1203,"series":1163,"seriesOrder":1163},"PowerShell: ConnectWise – Sync Configurations from Discovery Scans","Reconcile a network discovery CSV against ConnectWise PSA configurations over the REST API: match by serial, MAC or name, patch what changed, create the rest.","2025-08-20T14:00:00Z","\u002F2025\u002F08\u002F20\u002Fpowershell-connectwise-sync-configuration-items-from-discovery-scans\u002F",[1157,1158],[297,1172,1202],"discovery",[1155],{"title":1205,"description":1206,"date":1207,"url":1208,"categories":1209,"tags":1210,"image":1163,"readingTime":458,"canonical":1155,"sites":1213,"series":1163,"seriesOrder":1163},"PowerShell: Active Directory – Report Group Membership Sprawl","Rank AD security groups by nesting depth, transitive member count, ownership and staleness for an access review, plus a per-user Kerberos token size estimate.","2025-12-03T14:00:00Z","\u002F2025\u002F12\u002F03\u002Fpowershell-active-directory-report-group-membership-sprawl\u002F",[1157,1158],[297,1211,1212],"active-directory","reporting",[1155],{"doc":1163,"posts":1215},[],1790223957476]