ServiceNowの構築前に行うべきある重要な作業

## はじめに

いきなりですが、お手元のインスタンス（PDIでも開発インスタンスでもなんでもいいです。正当なアクセス権のあるインスタンスをご利用ください。できるだけOOTBに近いもので）で、ユーザーテーブル（`sys_user`）のアクセス制御リストを表示してみてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491737/e3c4969c-fe94-96d1-c284-fc99d6230ccb.png)

現行のWashington DCリリースのインスタンスで、OOTBに何も追加していなければ、テーブルレベルのアクセス制御リストが6つあるのではないでしょうか？内訳としては、createのものが1つ、readのものが2つ、writeのものが1つ、deleteのものが1つ、そしてreport_viewのものが1つです。この6つのACLのシステムID（Sys ID）を書き出してみていただけないでしょうか？

私のインスタンスではこうでした。

| 名前 | 操作 | システムID |
|:-:|:-:|:-:|
| sys_user | create | 2e3085317db5311076daf29f52070d34 |
| sys_user | read | 936dc648eb3630003623666cd206fecc |
| sys_user | read | cfa2be6167230200fba9f1d557415a3a |
| sys_user | write | d6ce5a60932003009c8579b4f47ffb3f |
| sys_user | delete | 93c234f1072200000ca55720e1021e5f |
| sys_user | report_view | 7f4d61b0b701211047293307fe11a92c |

どうでしょうか。私の予想では、Createだけ違うIDになっているのではと思うのですが、いかがでしょうか？

「小ネタ」のタグをつけるにはいささか重大な話なのですが、今日はこの件について取り上げたいと思います。

## ServiceNowインスタンスの運用とその課題

### インスタンスの使い分け

ServiceNowは業務用のクラウドサービスなので、業務を確実に運行するために、本番インスタンスだけではなく、複数の非本番インスタンスを使い分けます。細かい話は別の機会にしますが、最小構成だと、実装作業を行うための開発インスタンスと、実装したものをテストするためのテストインスタンスを用意することになると思います。

この使い分けをする目的は、**実装した成果物のステータスを管理して、プロジェクトに不要な混乱を招かないため**です。確実に開発できたものだけをテストインスタンスへ移行し、テストが確実に終わったものだけを本番インスタンスで運用することにより、本番の業務を品質よく運用できると言うことです。

一方で、この使い分けが可能なのは何故か。それは、**実装した成果物をあるインスタンスから別のインスタンスに移行できるため**です。開発インスタンスで作ったビジネスルールやスクリプトインクルード、あるいはフォームレイアウトを、テストインスタンスに移行しても、同じように動くという合理的な保証があるからこそ、開発インスタンスとテストインスタンスを分けて運用することができます。この保証がなかったらどうなるか。開発インスタンス上で実装したものは別のインスタンスに持って行くと動くかどうか分からないということになると、そもそも上で述べた、インスタンス使い分けの目的が達成できません。

実装した成果物がインスタンスを変えても動作するという性質、ここでは仮に**可搬性（portability）** としますが、それがあるからこそ、インスタンスは使い分けられて、開発プロジェクトを円滑に行うことができることになります。

### ServiceNowが実装成果物の可搬性を実現する仕組み

ServiceNowで実装した成果物が可搬なのは、実装した成果物や、その成果物が依存する別の成果物が、ユニークなIDを持っており、かつそのIDがインスタンス間で同じになっているためです。

例えば、会社[`core_company`]テーブルのビジネスルール「Prevent Recursion」というものを考えてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491737/34e7481a-bd56-7a96-04f9-0bb72b522519.png)

これはOOTBのビジネスルールです。このビジネスルールを変更する---たとえばActiveのチェックを外して無効にする---としましょう。（現実のインスタンスでやることはお勧めしません）[^1]

変更された新しい「Prevent Recursion」を別のインスタンスに持っていって、更新セットを反映するだけで変更を反映させられるのは、このビジネスルールがインスタンスによらず同一のIDを持っているためです。新しいバージョンを持ち込んだときに、持ち込み先インスタンスのどのビジネスルールの新バージョンなのか、システムIDが同じであるために曖昧さなく特定できるためです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3491737/edd9c3ff-0956-fd31-e57c-df0fc0a82887.png)


このIDがインスタンスによって異なっている、つまり開発インスタンスで変更したビジネスルールと、テストインスタンスにおいてそれに相当するビジネスルールが異なるIDを持っているとすると、開発インスタンスでの変更をテストインスタンスのビジネスルールに反映することができず、何と何が同じであるのかという哲学的な問いに取り組まないとテストも始められなくなります。[^2]

### 例外がある件について

ここで冒頭の話に戻ってきます。

冒頭のアクセス制御リスト、ユーザーテーブルに`create`の操作を許可するためのレコードですが、このレコードはインスタンス間で同じIDを持っていません。このACLを変更して更新セットに記録して別インスタンスに持っていっても、正しく反映することはできません。納得がいくかはともかくとして、これは仕様であるとされています。

ServiceNowには、プラットフォームやストアアプリに属するOOTBのレコードでも時々このようなレコードがあります。その意味では、Now Platformはにおける実装成果物の可搬性は残念ながら完璧なものではないということになります。

推測に過ぎませんが、この現象が起きるのは、インスタンスのプロヴィジョニング時、あるいはアプリケーションのインストール時に、いくつかのアプリケーションファイルのレコードが動的に生成されているためだと考えられます。インストールパッケージに出来合いのアプリケーションファイルがあるのではなく、何らかの理由でインストール手続きの中でファイルを作っていると言うことです。OOTBのアプリケーションファイルで、作成日時がインスタンスのプロヴィジョニングを行った日時やストアアプリのインストール日時になっているレコードは要注意です。そのタイムスタンプは、これらのレコードが動的に作られたことを示唆しており、そういうファイルはシステムIDがインスタンスごとに異なる可能性が高いからです。先ほどのACLもタイムスタンプを確認してみてください。

## 対応方法
この問題を克服して、更新の可搬性を確実にする抜本的な方法は1つで、それは必要なストアアプリをインストールしたあと、実装作業を行う前にクローンを行って全インスタンスの内容を同じにすることです。通常は本番インスタンスを基準にして、本番から全ての非本番インスタンスにクローンを行います。これ以降、定期的に、特に新しいプラグインを導入したときにはクローンを行うことが推奨されます。

開発を始めた後からこの問題に気づくと厄介な問題になりますが、この時に取れる方法は2つ。ひとつは、既存の開発物を退避して本番インスタンスからのクローニングを行い、退避した中間成果物を開発インスタンスに戻すことです。ただ、戻すと言っても例のACLのようなレコードはシステムIDが変わっているので、単純に戻すことはできません。手作業でマージするしかありません。
もう一つは、可搬でないことがわかった成果物を特定し、そのレコードのみ、XMLd本番インスタンスから各インスタンスに移送することです。この場合も本番インスタンスから持ってきたレコードには、開発インスタンスでの変更内容を反映する必要があります。

ちなみに、インスタンスのクローンは、インスタンスをコピーして内容を同じにすることですが、これは開発のベースラインを確立するための非常に重要な作業です。出荷状態から変なことしてないんだからインスタンスの内容は当然同じだろう、と期待されるかもしれませんが、最初の例のように、まっさらなインスタンスでもすでにインスタンスごとに相違している箇所が存在します。結構大変な作業なのですが、プラットフォームの持続的な運営には必要不可欠です。できるだけ手順を整理して、短時間でできるように準備をしておくことが重要です。



[^1]: 会社レコードの「親」フィールドを使った親子関係が循環していないかどうかをチェックするためのルールです。これを無効にすると、「親の親が自分自身」というような関係のレコードができてしまいます。この状態で、「子で処理できないものは親で処理する」というような業務処理を自動で行うと、永遠に終わらず無限ループになってしまいます。

[^2]: これと同じような、「新しく入ってきたデータは既存のデータのどれと同一とみなすか」という問題に取り組まなければならない機能がServiceNowにはあります。ITOMで使われるIRE（Identification and Reconciliation Engine）がそれです。Discoveryで発見したCI情報が既存のCMDBに含まれるどのデータと同一であり、どのデータを上書き更新するのかを判定するための仕組みです。これは実際のIT環境の変化を正しく捉えるための仕組みとして必要なものですが、更新セットで同じ問題を考えなくてはいけなくなると開発生産性はガタ落ちです。