ServiceNow 添付ファイルAPI（Attachment API）の検証 - 添付ファイルを一括で移行する

# ServiceNow 添付ファイル（Attachment API）の検証

## 1.背景
他システムからServiceNowへ移行する際、テーブルデータ（レコード・カラム）はインポートセットを使って一括取り込みができます。一方で、レコードに紐づく添付ファイルについては、標準機能のインポートセットでは対応できず、別途移行方法を検討する必要があります。

数ファイルであれば問題ないですが、数百、数千レベルになってくると一括で取り込む需要も生まれてきます。
以前その需要があり、対応した経緯があるので記録がてら本記事で「Attachment API」を活用して、添付ファイルを大量かつ効率的に移行する方法と、その検証内容を紹介します。

※そもそもそんなテーブル設計と移行設計をしない方が良いのかもしれない、というツッコミも大いにあると思いますが、本記事はあくまで技術的にどうかという検証を目的としています。


筆者の環境は、
・ServiceNowバージョンはWashington D.C.
・コマンド実行のPCはWindows10もしくは11(curlコマンドインストール済み)
となります。


## 2.実際にやってみる
### 2-1. レコードの添付ファイルとしてのインポート（Attachment API）

基本的には公式docsのAPIリファレンスの記述を参考に、curlコマンドを作ってそれを実行するという流れです。
[添付ファイルAPI -ServiceNow](https://www.servicenow.com/docs/ja-JP/bundle/vancouver-api-reference/page/integrate/inbound-rest/concept/c_AttachmentAPI.html)

前提としては、
・添付したいファイルのローカルパスを控えておく。
・base64でServiceNowの認証情報をエンコードしておく
・添付先のテーブル物理名とレコードのsysidを控えておく
となります。

PDFファイルを添付する場合、以下のようなコマンドになります。
（認証情報、ファイルパス、sysidはダミーです。コピペして使う際は編集してください）
```
curl.exe -k -X POST -H "Authorization:basic user:pass" -H "Accept: application/json" -H "Content-Type: application/pdf" --data-binary "@C:\Users\dummy\Downloads\test\test.pdf" -o "C:\dummy\Downloads\test\log\log.txt" "https://XXXXX.service-now.com/api/now/attachment/file?table_name=u_attachment_api_test&table_sys_id=aaaabbbb12345678abcdefgh11112222&file_name=test.pdf"
```

＜ぱっと見で編集箇所をわかりやすく太字に＞
<code>curl.exe -k -X POST -H "Authorization:basic **user:pass**" -H "Accept: application/json" -H "Content-Type: **application/pdf**" --data-binary "**@C:\Users\dummy\Downloads\test\test.pdf**" -o "**C:\dummy\Downloads\test\log\log.txt**" "https://**XXXXX**.service-now.com/api/now/attachment/file?table_name=**u_attachment_api_test**&table_sys_id=**aaaabbbb12345678abcdefgh11112222**&file_name=test.pdf"
</code>

参考までに、Base64形式の変換は私はこのサイトを利用しました。
[Base64エンコード/デコードツール](https://www.en-pc.jp/tech/base64/)
※認証情報の共有範囲にはご留意ください。

ContentTypeも送りたいファイルによって変更する必要があります。
[よく利用するContent-Type一覧](https://apidog.com/jp/blog/content-type-introduction/)

私は、固定部分と可変部分の列をExcel（スプレッドシート可）にまとめ、関数で各列を結合してコマンドを作成していました。
大量に数がある場合は、コマンド直実行だけでなく、バッチファイル化してリストを読み込む形にしても良いかもしれません。
**また、1ファイルあたりのファイルサイズが大きすぎると、まれにタイムアウトになりアップロードが失敗するため、エラーハンドリングや、サイズが大きいものは別途それだけ手動で移行するなどの検討も必要でした。**

＜コマンドが成功した時の例 キャプチャ＞
実行するとこんな感じです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3989988/95f2eb0c-1379-4bcb-a2d8-35ba815292cd.png)
プロンプトにはPOSTの実行時間などが、-oオプションで指定したログにはJSON形式でログが格納されます。

いつもの「ここ」に添付されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3989988/5949c29e-ae26-414f-8e1e-9cafa4c60b63.png)

<br>

### 2-2.（超力技応用編）「ファイルの添付」フィールドへの添付
ここまではレコードの添付ファイル（フォーム画面の上部のクリップマーク部分に入るあれです）としてAPIで添付しましたが、ServiceNowのフォームには「ファイルの添付」フィールドという型があります。システムの使い方によりますが、既に存在する「ファイルの添付」フィールドへのファイルの大量移行は可能なのかも確認し、検証しました。

こういうフィールドのことです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3989988/ba97141c-a5c2-4e28-a8ac-7eea9db0b1da.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3989988/7f398868-c621-41aa-ad19-4098c2d377da.png)


#### 2-2-1 先に結論を
ただ、まずは結論として
**「ファイルの添付」フィールドに直接アップロードするAPIは存在しない**です。
コミュニティでも議論されている通り、「ファイルの添付」フィールドに対して直接ファイルをアップロードするための専用APIはありません。
理由は、「ファイルの添付」フィールドは内部的に<code>sys_attachment</code>テーブルへの参照を保持しており、フィールドそのものにバイナリが格納されるわけではないためです。

めっちゃ参考にしたコミュニティ
[About Attachment API and "File Attachment" type fields](https://www.servicenow.com/community/developer-forum/about-attachment-api-and-quot-file-attachment-quot-type-fields/m-p/1425399/page/2)
ソリューションとなったコメントだけでなく、「Load more replies」のを押してほかのコメントも見ると良いです。（今回の場合、「‎01-26-2022 07:04 PM」に投稿されているリプライで、わかる人にはわかる結論になっています。）

#### 2-2-2 結論を踏まえ、やるとしたらという実装例
上記コミュニティを参考に、
以下のような手順で「ファイルの添付」フィールドを最終的に更新することが可能です。
　1)(通常の)Attachment API で一旦レコードにファイルを添付する。（2-1で説明した内容）
　2)BG Scriptなどを利用して<code>sys_attachment</code>テーブルのレコードをリネームする。
　　例えば、添付したファイル名test.xlsxとしてアップロードした後、　　
  　<code>sys_attachment</code>テーブルの<code>table_name</code>フィールドを「ZZ_YYtest.xlsx」のように変更する 。（ファイ名の頭に「ZZ_YY」をつける）
   こうすることでフォーム上部の「Attachments」欄には表示されないようになります。
   (標準レコード添付としては扱われない)
   
　3)ファイルの添付フィールドに対して、添付ファイルを紐づける。

1)は2-1と同様なため割愛しますが、
2)と3)のスクリプト例を以下に記載します。

2)ZZ_YYの付与:
```2)ZZ_YYの付与:
var gr = new GlideRecord('sys_attachment');
gr.get('629a26d94760021085f2b477536d43a6'); //該当のファイルのsys_attachmentテーブルのレコードsysid
gr.table_name ='ZZ_YYtest.xlsx'; //ZZ_YY付きにリネーム
gr.update ();
```

3)ファイルの添付フィールドに紐づけ:
```3)ファイルの添付フィールドに紐づけ:
var gr = new GlideRecord('u_attachment_api_test');//移行先のテーブル
gr.addQuery('sys_id','3d69e6194760021085f2b477536d43d0');//移行先レコードのsysid
gr.query();
if (gr.next()) {
  gr.u_file_attachment = '629a26d94760021085f2b477536d43a6';//「ファイルの添付」フィールド(今回はu_file_attachmentとしています)に対して、ZZ_YYを付けたsys_attachmentテーブルのレコードのsysidを紐づける
  gr.update();
}
```

#### 2-2-3. なぜ「ZZ_YY」を付与する必要があるのか
sys_attachmentテーブルの「table_name」フィールドに「ZZ_YY」で始まる値をセットすると、ServiceNowがそれを「通常の添付ファイル」と見なさなくなるため、フォーム上部の“添付ファイル一覧”部分には表示されなくなります。以下のKBが参考になります。

参考：
[KB0868420 - Why do some attachment records have ZZ_YY appended before the table name column in sys_attachment table?](https://support.servicenow.com/kb?id=kb_article_view&sysparm_article=KB0868420)

<br>

## 3.まとめ
ServiceNowへの添付ファイルの移行は、一般的なテーブルデータの移行(インポートセット)とは手順が異なります。大量の添付ファイルを一括で再現性高く取り込むには、今回記載した手段も参考にしてみてください。

APIとテーブル構造を理解すればできなくなないですが、やはり書いてみてこれをやらないように新システムの設計や移行要件、移行設計を導ける方がトータルでServiceNowを使っていく、ServiceNowを使って業務を改善しようとするという目的には適ってる気がします。今回はAPIを使った技術に着目したため省略しましたが、実際はやるとなると「（旧システムからの）添付ファイルとServiceNowのレコードの紐づけ」も事前に実施する必要があり、付帯作業も多く、いろんな意味でのコストがかかります。

どうしても……というときに本記事が、皆様のServiceNow移行・運用の参考になれば幸いです。

それでは。