

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 正在连接 ServiceNow
<a name="connecting-to-ticketing-and-chat-connecting-servicenow"></a>

本教程将引导您完成将 ServiceNow 实例连接到 AWS DevOps 代理的过程，使其能够在创建票证时自动启动事件响应调查，并将其关键发现发布到原始票证中。它还包含一些示例，说明如何将您的 ServiceNow 实例配置为仅向 DevOps 代理空间发送特定票证，以及如何协调跨多个 DevOps 代理空间的票证路由。

## 初始设置
<a name="initial-setup"></a>

第一步是在 AWS DevOps 可用于访问您的 ServiceNow ServiceNow 实例的 OAuth 应用程序客户端中创建。

### 创建 ServiceNow OAuth 应用程序客户端
<a name="create-a-servicenow-oauth-application-client"></a>

1. 启用您的实例的客户端凭证系统属性

   1. `sys_properties.list`在过滤器搜索框中搜索然后按回车键（它不会显示该选项，但按回车起作用）

   1. 选择 “新建”

   1. 将名称添加为，将值添加为 `glide.oauth.inbound.client.credential.grant_type.enabled` true，类型为 true \| false

![系统属性表单显示已启用 OAuth 入站客户端凭证授予类型。](http://docs.aws.amazon.com/zh_cn/devopsagent/latest/userguide/images/09ed6d5ff911.png)


1. 从筛选器搜索框中导航到 “系统 OAuth” > “应用程序注册表”

1. 选择 “新建” > “新入站集成体验” > “新集成” > “OAuth-客户凭证授予”

1. 选择一个名称并将 OAuth 应用程序用户设置为 “问题管理员”，单击 “保存”

![新的 OAuth 客户端凭证记录表，其中包含姓名、客户端 ID 和密钥字段。](http://docs.aws.amazon.com/zh_cn/devopsagent/latest/userguide/images/aeff4c127f7c.png)


### 将你的 ServiceNow OAuth 客户端连接到 AWS DevOps 代理人
<a name="connect-your-servicenow-oauth-client-to-aws-devops-agent"></a>

1. 你可以从两个地方开始这个过程。首先，导航到 “**能力提供者**” 页面并在 “**通信**” **ServiceNow**下找到，然后单击 “**注册**”。或者，您可以选择您可能已创建的任何 DevOps 座席空间，然后导航至权能 → 通信 → 添加 →， ServiceNow 然后单击注册。

1. 接下来，授权 DevOps 代理使用您刚刚创建的 OAuth 应用程序客户端访问您的 ServiceNow 实例。

![ServiceNow 注册表单，其中包含 “客户名称”、“客户端 ID” 和 “实例 URL” 字段。](http://docs.aws.amazon.com/zh_cn/devopsagent/latest/userguide/images/3db5a9aafc5f.png)

+ 按照以下步骤操作，保存生成的有关 webhook 的信息 

**重要**  
您将不会再看到此信息

![Webhook 配置面板显示连接状态和 Webhook 网址。](http://docs.aws.amazon.com/zh_cn/devopsagent/latest/userguide/images/80d0a319f87e.png)


### 配置您的 ServiceNow 业务规则
<a name="configure-your-servicenow-business-rule"></a>

建立连接后，您需要在中配置业务规则， ServiceNow 以便将票证发送到您的 DevOps 座席空间。

1. 导航至 “活动订阅” → “管理” → “业务规则”，然后单击 “新建”。

1. 将 “表” 字段设置为 “事件 [事件]”，选中 “高级” 复选框，然后将规则设置为在插入、更新和删除之后运行。

![“事件表上 CloudSmith 集成的业务规则” 表单。](http://docs.aws.amazon.com/zh_cn/devopsagent/latest/userguide/images/6f2a7370e2c0.png)


1. 导航到 “高级” 选项卡并添加以下 webhook 脚本，在指示的地方插入您的 webhook 密钥和 URL，然后单击 “提交”。

```
(function executeRule(current, previous /*null when async*/ ) {

    var WEBHOOK_CONFIG = {
        webhookSecret: GlideStringUtil.base64Encode('<<< INSERT WEBHOOK SECRET HERE >>>'),
        webhookUrl: '<<< INSERT WEBHOOK URL HERE >>>'
    };

    function generateHMACSignature(payloadString, secret) {
        try {
            var mac = new GlideCertificateEncryption();
            var signature = mac.generateMac(secret, "HmacSHA256", payloadString);
            return signature;
        } catch (e) {
            gs.error('HMAC generation failed: ' + e);
            return null;
        }
    }

    function callWebhook(payload, config) {
        try {
            var timestamp = new Date().toISOString();
            var payloadString = JSON.stringify(payload);
            var payloadWithTimestamp =`${timestamp}:${payloadString}`;

            var signature = generateHMACSignature(payloadWithTimestamp, config.webhookSecret);

            if (!signature) {
                gs.error('Failed to generate signature');
                return false;
            }

            gs.info('Generated signature: ' + signature);

            var request = new sn_ws.RESTMessageV2();
            request.setEndpoint(config.webhookUrl);
            request.setHttpMethod('POST');

            request.setRequestHeader('Content-Type', 'application/json');
            request.setRequestHeader('x-amzn-event-signature', signature);
            request.setRequestHeader('x-amzn-event-timestamp', timestamp);

            request.setRequestBody(payloadString);

            var response = request.execute();
            var httpStatus = response.getStatusCode();
            var responseBody = response.getBody();

            if (httpStatus >= 200 && httpStatus < 300) {
                gs.info('Webhook sent successfully. Status: ' + httpStatus);
                return true;
            } else {
                gs.error('Webhook failed. Status: ' + httpStatus + ', Response: ' + responseBody);
                return false;
            }

        } catch (ex) {
            gs.error('Error sending webhook: ' + ex.getMessage());
            return false;
        }
    }

    function createReference(field) {
        if (!field || field.nil()) {
            return null;
        }

        return {
            link: field.getLink(true),
            value: field.toString()
        };
    }

    function getStringValue(field) {
        if (!field || field.nil()) {
            return null;
        }
        return field.toString();
    }

    function getIntValue(field) {
        if (!field || field.nil()) {
            return null;
        }
        var val = parseInt(field.toString());
        return isNaN(val) ? null : val;
    }

    var eventType = (current.operation() == 'insert') ? "create" : "update";

    var incidentEvent = {
        eventType: eventType.toString(),
        sysId: current.sys_id.toString(),
        priority: getStringValue(current.priority),
        impact: getStringValue(current.impact),
        active: getStringValue(current.active),
        urgency: getStringValue(current.urgency),
        description: getStringValue(current.description),
        shortDescription: getStringValue(current.short_description),
        parent: getStringValue(current.parent),
        incidentState: getStringValue(current.incident_state),
        severity: getStringValue(current.severity),
        problem: createReference(current.problem),
        additionalContext: {}
    };

    incidentEvent.additionalContext = {
        number: current.number.toString(),
        opened_at: getStringValue(current.opened_at),
        opened_by: current.opened_by.nil() ? null : current.opened_by.getDisplayValue(),
        assigned_to: current.assigned_to.nil() ? null : current.assigned_to.getDisplayValue(),
        category: getStringValue(current.category),
        subcategory: getStringValue(current.subcategory),
        knowledge: getStringValue(current.knowledge),
        made_sla: getStringValue(current.made_sla),
        major_incident: getStringValue(current.major_incident)
    };

    for (var key in incidentEvent.additionalContext) {
        if (incidentEvent.additionalContext[key] === null) {
            delete incidentEvent.additionalContext[key];
        }
    }

    gs.info(JSON.stringify(incidentEvent, null, 2)); // Pretty print for logging only

    if (WEBHOOK_CONFIG.webhookUrl && WEBHOOK_CONFIG.webhookSecret) {
        callWebhook(incidentEvent, WEBHOOK_CONFIG);
    } else {
        gs.info('Webhook not configured.');
    }

})(current, previous);
```

如果您选择从 “**能力提供者**” 页面注册 ServiceNow 连接，则现在需要导航到要调查 ServiceNow 事件单的 DevOps 代理空间，选择 “功能” → “通信”，然后在 “能力提供者” 页面上注册的 ServiceNow 实例。现在，一切都应该设置完毕，所有将呼叫者设置为 “问题管理员”（模仿您授予 AWS DevOps OAuth 客户端的权限）的事件都将在已配置 DevOps 的代理空间中触发事件响应调查。您可以通过在中创建新事件 ServiceNow 并将事件的 “来电者” 字段设置为 “问题管理员” 来测试这一点。

![ServiceNow 事件表显示了问题管理员来电者的号码 INC0010001。](http://docs.aws.amazon.com/zh_cn/devopsagent/latest/userguide/images/4c7d24a85f88.png)


### ServiceNow 门票更新
<a name="servicenow-ticket-updates"></a>

在所有触发的事件响应调查中，您的 DevOps 代理将向原始工单提供其主要发现、根本原因分析和缓解计划的更新。特工的调查结果将发布在事件评论中，我们目前只会发布类型为`finding`、、`cause``investigation_summary``mitigation_summary`、的特工记录和调查状态更新（例如`AWS DevOps Agent started/finished its investigation`）。

## 工单路由和编排示例
<a name="ticket-routing-and-orchestration-examples"></a>

### 场景：筛选哪些事件被发送到 DevOps 代理空间
<a name="scenario-filtering-which-incidents-are-sent-to-a-devops-agent-space"></a>

这是一个简单的场景，但需要进行一些配置 ServiceNow 才能在中创建 ServiceNow 用于跟踪事件源的字段。在本示例中，使用 SNOW 表单生成器创建一个新的 Source (u\_source) 字段。这将允许跟踪事件源，并使用它来将来自特定来源的请求路由到 DevOps 代理空间。路由是通过创建 Service Now Business 规则以及在 “何时运行” 选项卡中设置 “何时” 触发器和 “筛选条件” 来完成的。在此示例中，筛选条件设置如下：

![何时在触发器之前运行 tab 键和 Source 的过滤器包含 Dynatrace。](http://docs.aws.amazon.com/zh_cn/devopsagent/latest/userguide/images/fac7a186beee.png)


### 场景：跨多个 DevOps 座席空间路由事件
<a name="scenario-routing-incidents-across-multiple-devops-agent-spaces"></a>

此示例说明当紧急程度为`1`、类别为或服务为时，如何在 DevOps 代理空间 B 中触发调查，当服务为`AWS`、来源为时`AWS`，如何在 DevOps 代理空间 A 中触发调查`Dynatrace`。`Software`

这种情况可以通过两种方式实现。可以更新 webhook 脚本本身以包含此业务逻辑。在本场景中，我们将展示如何使用 ServiceNow 业务规则来实现这一目标，以提高透明度并简化调试。路由是通过创建两个 Service Now 业务规则来完成的。
+ 在 ServiceNow DevOps Agent Space A 中创建业务规则，然后使用条件生成器创建仅根据我们指定的条件发送事件的条件。

![带有 “紧急情况” 和 “类别” 筛选条件的业务规则配置表单。](http://docs.aws.amazon.com/zh_cn/devopsagent/latest/userguide/images/bca2f3928bf0.png)

+ 接下来，在 AgentSpace B 中 ServiceNow 创建另一条业务规则，只有当服务为 Dynatrace AWS 且来源为 Dynatrace 时，才会触发该业务规则。

![用于向 Agent Space B 发送事件的业务规则配置表单](http://docs.aws.amazon.com/zh_cn/devopsagent/latest/userguide/images/bc29e4db1a76.png)


现在，当您创建符合指定条件的新事件时，它将触发对 DevOps 代理空间 A 或 DevOps 座席空间 B 的调查，从而为您提供对事件路由的精细控制。