

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

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

本教學課程會逐步引導您將 ServiceNow 執行個體連線至 AWS DevOps 代理程式，讓它在建立票證時自動啟動事件回應調查，並將其金鑰調查結果發佈至原始票證。它還包含如何設定 ServiceNow 執行個體僅將特定票證傳送至 DevOps 代理程式空間，以及如何協調多個 DevOps 代理程式空間之間的票證路由的範例。

## 初始設定
<a name="initial-setup"></a>

第一步是在 ServiceNow 中建立 OAuth 應用程式用戶端， AWS DevOps 可用來存取您的 ServiceNow 執行個體。

### 建立 ServiceNow OAuth 應用程式用戶端
<a name="create-a-servicenow-oauth-application-client"></a>

1. 啟用執行個體的用戶端登入資料系統屬性

   1. `sys_properties.list` 在篩選條件搜尋方塊中搜尋，然後按 Enter 鍵 （它不會顯示 選項，但按 Enter 鍵有效）

   1. 選擇新增

   1. 將名稱新增為 ，`glide.oauth.inbound.client.credential.grant_type.enabled`並將值新增為 true，類型為 true \$1 false

![\[alt text not found\]](http://docs.aws.amazon.com/zh_tw/devopsagent/latest/userguide/images/09ed6d5ff911.png)


1. 從篩選條件搜尋方塊中導覽至系統 OAuth > 應用程式登錄檔

1. 選擇「新」＞「新傳入整合體驗」＞「新整合」＞「OAuth - 用戶端憑證授予」

1. 選擇名稱並將 OAuth 應用程式使用者設定為「問題管理員」，然後按一下「儲存」

![\[alt text not found\]](http://docs.aws.amazon.com/zh_tw/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 Agent 使用您剛建立的 OAuth 應用程式用戶端存取您的 ServiceNow 執行個體。

![\[alt text not found\]](http://docs.aws.amazon.com/zh_tw/devopsagent/latest/userguide/images/3db5a9aafc5f.png)

+ 遵循後續步驟，並儲存 Webhook 的產生資訊 

**重要**  
您不會再看到此資訊

![\[alt text not found\]](http://docs.aws.amazon.com/zh_tw/devopsagent/latest/userguide/images/80d0a319f87e.png)


### 設定您的 ServiceNow 業務規則
<a name="configure-your-servicenow-business-rule"></a>

建立連線後，您需要在 ServiceNow 中設定商業規則，將票證傳送至 DevOps Agent Space(s)。

1. 導覽至活動訂閱 → 管理 → 業務規則，然後按一下新增。

1. 將「資料表」欄位設定為「事件 【事件】」，勾選「進階」方塊，並將規則設定為在插入、更新和刪除之後執行。

![\[alt text not found\]](http://docs.aws.amazon.com/zh_tw/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 中建立新的事件，並將事件的來電者欄位設定為「問題管理員」，以測試這一點。

![\[alt text not found\]](http://docs.aws.amazon.com/zh_tw/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 表單建置器建立新的來源 (u\$1source) 欄位。這將允許追蹤事件來源，並使用它將請求從特定來源路由到 DevOps 代理程式空間。路由的完成方式是建立 Service Now Business Rule，並在何時執行索引標籤中設定「何時」觸發條件和「篩選條件」。在此範例中，篩選條件的設定如下：

![\[alt text not found\]](http://docs.aws.amazon.com/zh_tw/devopsagent/latest/userguide/images/fac7a186beee.png)


### 案例：跨多個 DevOps 代理程式空間路由事件
<a name="scenario-routing-incidents-across-multiple-devops-agent-spaces"></a>

此範例示範如何在緊急程度為 `1`、類別為 `Software` 或服務為 時觸發 DevOps Agent Space B 中的調查`AWS`，以及在服務為 `AWS`且來源為 時觸發 DevOps Agent Space A 中的調查`Dynatrace`。

此案例可以透過兩種方式完成。Webhook 指令碼本身可以更新為包含此商業邏輯。在此案例中，我們將示範如何使用 ServiceNow 業務規則來完成它，以實現透明度並簡化偵錯。路由是透過建立兩個 Service Now Business 規則來完成。
+ 在 ServiceNow for DevOps Agent Space A 中建立業務規則，並使用 條件建置器建立條件，只根據我們指定的條件傳送事件。

![\[alt text not found\]](http://docs.aws.amazon.com/zh_tw/devopsagent/latest/userguide/images/bca2f3928bf0.png)

+ 接著，在 ServiceNow for AgentSpace B 中建立另一個業務規則，只有在服務為 AWS 且來源為 Dynatrace 時，才會觸發業務規則。

![\[alt text not found\]](http://docs.aws.amazon.com/zh_tw/devopsagent/latest/userguide/images/bc29e4db1a76.png)


現在，當您建立符合指定條件的新事件時，它會觸發對 DevOps Agent Space A 或 DevOps Agent Space B 的調查，為您提供對事件路由的精細控制。