😎

External Network Access 〜 Slack通知(デコ版)を作ってみる

がく@ちゅらデータです。
ご無沙汰しております、前にちゃんとしたのを書いてから知らない間に秋がすぎてました(汗

さて、先日、沖縄SnowVillageにて下記のようなLTをさせていただきました。

https://zenn.dev/churadata/articles/a12cae787d0fc5

でやったのですが

な感じで、未完成と感じておった次第です。
なので、今回作ってみました。

External Network Access機能を使って、Slack通知をする という記事は
国内では
https://zenn.dev/tf_takada/articles/d5d2c6c03b39a8

海外Mediumでは
https://medium.com/snowflake/send-slack-messages-from-snowflake-with-snowpark-external-network-access-8e3e42a7cde2

があります。

こちらを参考にさせていただこうかな

Slack側の準備

https://api.slack.com/apps へアクセス

Slack Apps のページから Incomming Webhook URLを生成する


※情シスの承認が必要でしたので、ちょっと承認まで動作検証が遅れましたが、すぐにやってくれた、ありがとうー > 弊社情シス担当のみんな

Snowflake 側の設定

SnowflakeのNetwork ruleの作成

CREATE OR REPLACE NETWORK RULE slack_webhook_network_rule
  MODE = EGRESS
  TYPE = HOST_PORT
  VALUE_LIST = ('hooks.slack.com');

Webhook URL を secret へ登録

CREATE OR REPLACE SECRET slack_app_webhook_url
    type = GENERIC_STRING
    secret_string = 'https://hooks.slack.com/services/*********/*********'
    comment = 'Slack Webhook URL you have created from the Slack App UI';

Externao Access Integrationの作成

CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION slack_webhook_access_integration
  ALLOWED_NETWORK_RULES = (slack_webhook_network_rule)
  ALLOWED_AUTHENTICATION_SECRETS = (slack_app_webhook_url)
  ENABLED = true;

simple message (非デコ) をSlackに送るストアドプロシージャの作成

CREATE OR REPLACE PROCEDURE send_slack_message(MSG string)
RETURNS STRING
LANGUAGE PYTHON
RUNTIME_VERSION = 3.10
HANDLER = 'main'
EXTERNAL_ACCESS_INTEGRATIONS = (slack_webhook_access_integration)
SECRETS = ('slack_url' = slack_app_webhook_url)
PACKAGES = ('snowflake-snowpark-python', 'requests')
EXECUTE AS CALLER
AS
$$
import snowflake.snowpark as snowpark
import json
import requests
import _snowflake
from datetime import date

def main(session, msg): 
    # Retrieve the Webhook URL from the SECRET object
    webhook_url = _snowflake.get_generic_secret_string('slack_url')

    slack_data = {
     "text": f"Snowflake says: {msg}"
    }

    response = requests.post(
        webhook_url, data=json.dumps(slack_data),
        headers={'Content-Type': 'application/json'}
    )
    if response.status_code != 200:
        raise ValueError(
            'Request to slack returned an error %s, the response is:\n%s'
            % (response.status_code, response.text)
        )
    
    return "SUCCESS"
$$;
CALL send_slack_message('Hello world!');

を実行すると

WebUIにて、成功

Slackにも シンプルな通知ができました

通知をデコってみよう

CREATE OR REPLACE PROCEDURE send_slack_deco_message(LEVEL string, MSG string, CHANNEL_NAME string)
RETURNS STRING
LANGUAGE PYTHON
RUNTIME_VERSION = 3.10
HANDLER = 'main'
EXTERNAL_ACCESS_INTEGRATIONS = (slack_webhook_access_integration)
SECRETS = ('slack_url' = slack_app_webhook_url)
PACKAGES = ('snowflake-snowpark-python', 'requests')
EXECUTE AS CALLER
AS
$$
import snowflake.snowpark as snowpark
import json
import requests
import _snowflake
from datetime import date

def main(session, level, msg, channel_name): 
    # Retrieve the Webhook URL from the SECRET object
    webhook_url = _snowflake.get_generic_secret_string('slack_url')

    color = "good"
    if level == "info":
      color = "good"
    elif level == "warn":
      color = "warning"
    elif level == "crit":
      color = "danger"
    else:
      color = "good"
    
    slack_data = {
      "channel" : f"{channel_name}",
      "attachments":[
      {
         "fallback":f"Snowflake ({level})",
         "pretext":f"Snowflake ({level})",
         "color":color,
         "fields":[
            {
               "title":f"Snowflake ({level})",
               "value":f"{msg}"
            }
         ]
      }
      ]
    }   

    response = requests.post(
        webhook_url, data=json.dumps(slack_data),
        headers={'Content-Type': 'application/json'}
    )
    if response.status_code != 200:
        raise ValueError(
            'Request to slack returned an error %s, the response is:\n%s'
            % (response.status_code, response.text)
        )
    
    return "SUCCESS"
$$;
CALL send_slack_deco_message('warn','Hello world!','_gaku_t');

CALL send_slack_deco_message('warn','Hello world!','_gaku_t_2');

CALL send_slack_deco_message('crit','Hello world!','_gaku_t');

まとめ

External Network Access をつかってみましたが、設定、めちゃくちゃ簡単です!
あとは、Slack通知のJSONで attachmentに入れてやればいい感じで書けます

info, warn, critでそれぞれ色がつけることができました

今後改善するとしたら

  • send_slack_deco_message_info(message)
  • send_slack_deco_message_warn(message)
  • send_slack_ddeco_message_crit(message)

みたいなオーバーラップさせるのを作るとよりわかりやすいかもな〜

あとまだ試していないですが

  • 通知先のチャンネルを指定したい
    • slack_dataの中で、 { "channel": "#new_channel" }
    • ↑うまく行かなかった。何かSlackAppの設定が必要かも
  • ポストごとにBotの表示名を変えたい
    • slack_data の中で "username": "がーにゃbot"
    • 追加すればできそう
      とかでできそう

出典

https://api.slack.com/messaging/composing/layouts#attachments

ちゅらデータ株式会社

Discussion