> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flashcat.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# ServiceNow 同步

> 通过 ServiceNow 同步 Webhook，实现故障与 ServiceNow Incident 的关联。

<Tip>**版本要求**：此功能需要 On-call 专业版及以上订阅。[了解更多](https://flashcat.cloud/flashduty/price/)</Tip>

通过 ServiceNow 同步 Webhook，将 Flashduty 的故障与 ServiceNow Incident 进行关联同步，实现 Flashduty 与 ServiceNow 的联动。

## 在 ServiceNow

### 创建用户

需创建一个用户以连接 ServiceNow 实例，用于 Incident 的同步与更新。如果已有可用用户，请直接跳过本步骤。

1. 登录 ServiceNow 实例控制台，通过选择 `ALL` ，输入 `USERS` 选择`Organization`-`Users` 。
2. 点击 `New` 新建用户。
3. 在编辑页面中，`User ID` 输入：flashduty 。
4. `Password needs reset` 和 `Web service access only` 以及 `Internal Integration User` 保持取消勾选状态。
5. 提交保存。

![2025-09-24-16-19-47](https://docs-cdn.flashcat.cloud/images/png/3db86d07758819d61f4e7d2fc714347b.png)

### 配置用户

> **用户角色说明**
> **itil：** 该角色在 Flashduty 中的主要使用范围仅限于在同步 ServiceNow Incident 时，进行获取、创建、更新 ServiceNow Incident，不涉及其他任何操作。
>
> **personalize\_dictionary：** 该角色在 Flashduty 中的主要使用范围仅限于 ServiceNow Incident Table 的字段获取，不涉及其他任何操作。
>
> 关于以上两个角色更多权限范围，可以参考 ServiceNow [官方文档](https://www.servicenow.com/docs/bundle/washingtondc-platform-administration/page/administer/roles/reference/r_BaseSystemRoles.html#d130465e3182)

1. 在用户列表页面，找到新创建的 `flashduty` 用户并进到配置页面。
2. 在编辑页面中，点击 `Set Password` 设置一个密码。
3. 点击 `Roles` 添加 **personalize\_dictionary 和 itil** 角色（如果不需要配置自定义字段映射关系，可以不授予 personalize\_dictionary 权限）。
4. 点击 `Update` 更新配置。

![2025-09-24-16-29-05](https://docs-cdn.flashcat.cloud/images/png/a4416dff926e89b505224e03a4f774c6.png)
![2025-09-24-16-29-58](https://docs-cdn.flashcat.cloud/images/png/19371c16f4c516c095752f2a8e0d45bf.png)

## 在 Flashduty On-call

### 配置集成

将以上配置的用户名/密码以及实例名称输入到左侧集成信息中并点击下一步进行配置。

1. **集成名称：** 为当前集成定义一个名称。

2. **管理团队：** 当选择管理团队后，只有该团队成员以及租户管理员可以编辑此集成。

3. **协作空间**：选择该集成生效的协作空间，。

4. **同步方向：**

   * To\_ServiceNow：将 Flashduty 的故障同步至 ServiceNow。
   * From\_ServiceNow：将 ServiceNow 的 Incident 同步至 Flashduty。
   * Two-way：Flashduty 和 ServiceNow 互相同步。

5. **触发模式**：
   * 自动触发：需要配置相应的条件，Flashduty On-call 会自动将符合条件的故障同步到 ServiceNow 中。
   * 手动触发：需要在故障详情页的更多操作中手动触发 ServiceNow 同步（该集成配置的名称为触发器名称）。

6. **严重程度映射**：

   * ServiceNow 的 Priority 是由 Impact 和 Urgency 的值共同决定的，所以可以参考 ServiceNow 的 `Priority Lookup Rules` 进行配置。
   * 当 ServiceNow Incident 的 Urgency 发生变化时，才会触发 Flashduty 故障严重程度的更新。
   * 由于 Flashduty 在遵循最小权限的情况下，无法获取 ServiceNow 的 Impact 和 Urgency 列表，所以只提供了默认值，如果您需要自定义映射关系时，可以联系技术支持。

7. **自定义字段映射**：可以将故障中的标签或自定义字段，映射到 ServiceNow 工单中的对应文本字段，实现信息自动填充。该功能支持将常见上下文信息（如服务名、实例地址、指标名称等）同步至 ServiceNow，便于后续排查与跟踪。

   * 仅支持目标为单行文本或多行文本类型的字段。
   * 支持从故障标签（如 service、instance）或自定义属性中提取值。
   * 若源字段为空，目标字段也将保持为空，不会覆盖原有内容。
   * 映射配置在集成设置中统一管理，无需每次手动填写。

## 在 ServiceNow

当同步方向选择 From\_ServiceNow 或 Two-way 时，还需要在 ServiceNow 中做相应的配置，以便将 ServiceNow Incident 同步至 Flashduty， 在同步至 Flashduty 时，有以下两种方式，可根据实际需求选择即可。

### 手动同步

该方式依赖 ServiceNow 提供的 UI Action 和 Script Include 的配置，根据以下步骤配置完成的效果：当新建 Incident 或更新 Incident 时，可以在功能区看到发送同步请求的按钮，触发该按钮，可以将当前 Incident 的内容同步至 Flashduty。需要注意的是，如果触发请求时遇到失败的情况，请重试（重试时间间隔需大于 10 秒）。

#### 配置 UI Action

1. 登录 ServiceNow 实例控制台，通过选择 `ALL` ，输入 `UI Actions` 选择`System Definition`-`UI Actions` 。

2. 点击 `New` 新建 Action。

3. `Name` 输入： **Send To Flashduty**， `Table` 选择 **Incident** 。

4. `Form button` ，`Active` ，`Show insert` ，`Show update` ，`Client`， `List v2/3 Compatible` 保持勾选状态。

5. `Onclick` 输入：**onClick();**。

6. `Script` 输入：

   ```js theme={null}
   function onClick() {
     g_form.save();

     var ga = new GlideAjax("IncidentWebhookHelperAjax");
     ga.addParam("sysparm_name", "sendWebhook");
     ga.addParam("sysparm_sys_id", g_form.getUniqueValue());

     ga.getXMLAnswer(function (response) {
       alert("Webhook Triggered: " + response);
     });
   }
   ```

7. 提交保存。

#### 配置 Script Include

1. 登录 ServiceNow 实例控制台，通过选择 `ALL` ，输入 `Script Includes` 选择`System Definition`-`Script Includes` 。
2. 点击 `New` 新建 Script Include。
3. `Name` 输入：**IncidentWebhookHelper** ， `Accessible from` 选择 **All application scopes**。
4. `Client callable` 和 `Active` 保持勾选状态。
5. `Script` 输入以下内容，其中 **request.setEndpoint** 中需要补充集成的<span class="integration_url">推送地址</span>：

<div className="hide">
  注意： body 中配置的是默认接收字段，如果有自定义字段需要同步至 Flashduty ，需要额外手动补充内容到 body 中，比如希望添加一个字段名为：test\_001 的字段（该字段名可以在配置集成中添加自定义字段的时候获取，不要使用 ServiceNow Inident 表单中显示的字段名），那么需要在 body 中补充：test\_001: current.getDisplayValue("test\_001")。
</div>

```js theme={null}
var IncidentWebhookHelper = Class.create();
IncidentWebhookHelper.prototype = {
  initialize: function() {},

  sendIncidentWebhook: function(current) {
      function getLastComment(sysId) {
          var journalGR = new GlideRecord('sys_journal_field');
          journalGR.addQuery('element_id', sysId);
          journalGR.addQuery('element', 'comments');
          journalGR.orderByDesc('sys_created_on');
          journalGR.setLimit(1);
          journalGR.query();
          if (journalGR.next()) {
              return journalGR.getValue('value');
          }
          return '';
      }

      var body = {
          action_type: current.isNewRecord() ? 'insert' : 'update',
          number: current.getValue("number"),
          sys_id: current.getUniqueValue(),
          short_description: current.getValue("short_description"),
          description: current.getValue("description"),
          impact: current.getDisplayValue("impact"),
          urgency: current.getDisplayValue("urgency"),
          comments: getLastComment(current.getUniqueValue()),
<label name='field_mapping' tab='10'>{original.key}: current.getDisplayValue("{original.key}")</label>
      };

      try {
          var request = new sn_ws.RESTMessageV2();
          request.setHttpMethod("POST");
          request.setEndpoint("PUSH URL");
          request.setRequestHeader("Content-Type", "application/json");
          request.setRequestBody(JSON.stringify(body));
          request.executeAsync();
          } catch (ex) {
          gs.error("Webhook Call failed: " + ex.message);
      }
  },
  type: 'IncidentWebhookHelper'
};

```

6. 提交保存。

7. 回到 Script Includes 列表，继续创建。

8. 点击 `New` 新建 Script Include。

9. `Name` 输入：**IncidentWebhookHelperAjax** ， `Accessible from` 选择 **All application scopes**。

10. `Client callable` 和 `Active` 保持勾选状态。

11. `Script` 输入以下内容：

    ```js theme={null}
    var IncidentWebhookHelperAjax = Class.create();
    IncidentWebhookHelperAjax.prototype = Object.extendsObject(
      global.AbstractAjaxProcessor,
      {
        sendWebhook: function () {
          var sysId = this.getParameter("sysparm_sys_id");
          var gr = new GlideRecord("incident");
          if (gr.get(sysId)) {
            var helper = new IncidentWebhookHelper();
            helper.sendIncidentWebhook(gr);
            return "Success";
          }
          return "Request failed";
        },
      }
    );
    ```

12. 提交保存。

### 自动同步

该方式依赖 ServiceNow 提供的 Business Rules 的配置，使用该方式可以实现当有新建或更新事件时，自动将 Incident 同步至 Flashduty。

#### 配置 Business Rules

1. 登录 ServiceNow 实例控制台，通过选择 `ALL` ，输入 `Business Rules` 选择`System Definition`-`Business Rules` 。
2. 点击 `New` 新建 Business Rule。
3. `Name` 输入：**Send To Flashduty** ， `Table` 选择 **Incident**。
4. `Advanced` 和 `Active` 保持勾选状态。
5. 在 `When to run` 区域中，`When` 选择 **async**，`Insert` 和 `Upsert` 保持勾选状态，其他按需配置。
6. 在 `Advanced` 区域中，`Script` 填写以下内容，其中 **endpoint** 中需要补充集成的<span class="integration_url">推送地址</span>

<div className="hide">
  注意： body 中配置的是默认接收字段，如果有自定义字段需要同步至 Flashduty ，需要额外手动补充内容到 body 中，比如希望添加一个字段名为：test\_001 的字段（该字段名可以在配置集成中添加自定义字段的时候获取，不要使用 ServiceNow Inident 表单中显示的字段名），那么需要在 body 中补充：test\_001: current.getDisplayValue("test\_001")。
</div>

```js theme={null}
(function executeRule(current, previous) {
  function getLastComment(recordSysId) {
    var journalGR = new GlideRecord("sys_journal_field");
    journalGR.addQuery("element_id", recordSysId);
    journalGR.addQuery("element", "comments");
    journalGR.orderByDesc("sys_created_on");
    journalGR.setLimit(1);
    journalGR.query();
    if (journalGR.next()) {
      var comment = journalGR.getValue("value");
      return comment;
    }
    return "";
  }

  var operation = current.operation() || "unknown";
  var isPreviousNull = previous === null;
  var createdOn = current.getValue("sys_created_on");
  var updatedOn = current.getValue("sys_updated_on");
  var isNewRecord = createdOn === updatedOn;

  var action = "update";
  if (isPreviousNull && isNewRecord) {
    action = "insert";
  }

  var body = {
    action_type: action,
    number: current.getValue("number"),
    sys_id: current.getUniqueValue(),
    short_description: current.getValue("short_description"),
    description: current.getValue("description"),
    state: current.getDisplayValue("state"),
    impact: current.getDisplayValue("impact"),
    urgency: current.getDisplayValue("urgency"),
    comments: getLastComment(current.getUniqueValue()),
<label name='field_mapping' tab='4'>{original.key}: current.getDisplayValue("{original.key}")</label>

  };

  try {
    var endpoint = "";
    var request = new sn_ws.RESTMessageV2();
    request.setHttpMethod("POST");
    request.setEndpoint(endpoint);
    request.setRequestHeader("Content-Type", "application/json");
    request.setRequestBody(JSON.stringify(body));
    request.executeAsync();
  } catch (ex) {
    gs.error("Error sending webhook: " + ex.message);
  }
})(current, previous);
```

7. 提交保存。

## 同步信息

### 表单字段

| ServiceNow          | Flashduty     | 备注    |
| ------------------- | ------------- | ----- |
| Short\_description  | Title         | 标题    |
| Description         | Description   | 描述信息  |
| Additional comments | Comments      | 评论    |
| State               | Progress      | 状态    |
| Urgency             | Severity      | 严重程度  |
| Others              | Custom Fields | 自定义字段 |

### 状态映射

| ServiceNow  | Flashduty  | 备注        |
| ----------- | ---------- | --------- |
| New         | Triggered  | 触发        |
| In Progress | Processing | 待处理       |
| On Hold     | Snoozed    | 默认暂缓 2 小时 |
| Resolved    | Closed     | 关闭        |
| Closed      | Closed     | 关闭        |
| Canceled    | Closed     | 关闭        |

### 优先级映射

只有 ServiceNow 的 Urgency 值变化时，才会影响 Flashduty 的 Severity

| ServiceNow | Flashduty | 备注 |
| ---------- | --------- | -- |
| Low        | Info      | 提示 |
| Medium     | Warning   | 警告 |
| High       | Critical  | 灾难 |

## 常见问题

<details>
  <summary>新建 ServiceNow 用户时，UserID 是否可以自定义？</summary>
  可以自定义，文档指引中使用 flashduty 作为 UserID，是为了更好的标识该用户用于 Incident 同步
</details>

<details>
  <summary>配置集成时提示 401 错误 </summary>
  提示 401 一般是密码错误导致的，请检查密码是否正确，或者重新设置新的密码(在配置密码时，请勿勾选 Password needs reset 选项)
</details>
