🙌

Snowflake Summitで発表されたExternal Network Accessを使用し、Slack通知をしてみた

2023/07/16に公開
1

(Zennに初投稿です)
6月に行われたSnowflake Summit@Las Vegasで紹介されたExternal Network Access(プレビュー)機能を使用し、個人用Slackチャネルに通知してみました。

背景

運用時に何かしらのエラーが発生した際、それらをSlackに通知する方は多いのではないのでしょうか。従来のSnowflakeでは、外部関数やLambdaなどを通じて、Slackに通知していたのではないかと思います。しかし、今回、発表されたExternal Network Accessを使用することで外部関数やLambdaなどを設定することなく、よりシンプルにSlack通知機能を実装することが可能となりました。

External Network Accessとは?

External Network Accessとは、UDFやStored Procedure(SPROC)からSlack、OpenAI、 Googleなどの特定の外部ネットワークに対して、セキュアにアクセスできる機能となっております。ドキュメントからの抜粋ではありますが、機能に関して、このように記述しております。

  • Write UDF and procedure handlers that access external locations.
  • Allow or block access to locations on a network external to Snowflake.
  • Use secrets that represent stored credentials, rather than using literal values, within handler code to authenticate with external network locations.
  • Specify which secrets are allowed for use with external network locations.

こちらの機能を使用し、Python UDFから私の個人用Slackチャネルに通知してみたいと思います。なお、制限事項としてPython/Java UDFでのみ、使用することが可能です。

コード全体

まず先にコードを、以下の通り、載せておきます。

-- 1. Network Ruleの作成
CREATE OR REPLACE NETWORK RULE slack_rule
  MODE = EGRESS
  TYPE = HOST_PORT
  VALUE_LIST = ('slack.com');

--2. Secretの作成
CREATE OR REPLACE SECRET slack_token
  TYPE = GENERIC_STRING
  SECRET_STRING = '<your-access-token>';

-- 3. External Access Integrationの作成
CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION slack_apis_access
  ALLOWED_NETWORK_RULES = (slack_rule)
  ALLOWED_AUTHENTICATION_SECRETS = (slack_token)
  ENABLED = true;

--4. Slack通知するPython UDFの作成
CREATE OR REPLACE FUNCTION post_to_slack(channel varchar ,message varchar)
RETURNS variant
LANGUAGE python
RUNTIME_VERSION = 3.8
HANDLER = 'main'
EXTERNAL_ACCESS_INTEGRATIONS = (slack_apis_access)
PACKAGES = ('requests')
SECRETS = ('cred' = slack_token)
AS
$$
import _snowflake
import requests
import json
def main(p_channel ,p_message):
     api_key = _snowflake.get_generic_secret_string('cred')
     url = 'https://slack.com/api/chat.postMessage'
     v_headers = {{"Authorization": "Bearer "+ api_key}}
     v_data_json = {{
          'channel': p_channel
          ,'text': p_message
     }}
     response = requests.post(url ,headers=v_headers ,json = v_data_json)
     return response.json()
$$;

--5. テスト
SELECT
'<your-channel-id>' as channel
,'TEST!!!' as message
,post_to_slack(channel ,message) as slack_notify

作成手順

Slack通知を設定するための手順を示します。

0. SlackのAccess Tokenを用意する

こちらの記事を参考しながら、Slackのアクセストークンを発行します。また、私の場合、今回のテストは自分のDMに送るため、自ユーザのuser_idも取得しています。(こちらは任意です)

1. Network Ruleの作成

アクセスの許可/制限したい外部ネットワークに関する情報を設定するのが、このNetwork Ruleオブジェクトになります。。このオブジェクトにおけるオプションを、下記の通り、説明します。

  1. MODEではIGRESS or EGRESSを指定
  2. TYPEではHOST_PORT、IPV4、AWSVPCEID、AZURELINKIDの4つのいずれかをを指定
  3. VALUE_LISTでは、TYPEに連動して具体的なホスト名やIPアドレスを指定する。今回はslackに通知するため、slack.comを指定している。
-- 1. Network Ruleの作成
CREATE OR REPLACE NETWORK RULE slack_rule
  MODE = EGRESS
  TYPE = HOST_PORT
  VALUE_LIST = ('slack.com');

2. Secretの作成

各種サービスにアクセス時に必要なCredentials情報を含むのが、このSecretオブジェクトとなります。このSecretオブジェクト自体はGAされた既存の機能であります。こちらを機能の一部を拡張してります。

  1. TYPEに記載しているGENERIC_STRINGを指定。
  2. SECRET_STRINGにはAPIキーやアクセストークンなどを指定する。今回は0.で取得したSlackのアクセストークンを指定している。
--2. Secretの作成
CREATE OR REPLACE SECRET slack_token
  TYPE = GENERIC_STRING
  SECRET_STRING = '<your-access-token>';

3. External Access Integrationの作成

Integrationという言葉の通り、Network RuleとSecretの2つのオブジェクトを統合するのが、External Access Integrationオブジェクトとなります。コードの通り、オプションのALLOWED_NETWORK_RULESALLOWED_AUTHENTICATION_SECRETSに1.と2.で作成した各オブジェクトを指定しています。なお、ドキュメント内に、External Access Integrationの取り扱いに関するセキュリティ上の注意点がございますので、ご利用になる場合は注意してください。

External Access Networkは外部リソースに情報流出させることができる機能となりますので、適切な権限管理を徹底して頂ければと思います。

-- 3. External Access Integrationの作成
CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION slack_apis_access
  ALLOWED_NETWORK_RULES = (slack_rule)
  ALLOWED_AUTHENTICATION_SECRETS = (slack_token)
  ENABLED = true;

4. Python UDFの作成

UDFのオプションについて説明します。通常UDFに下記2つオプションを追加で指定しています。

  1. external_access_integrationsで3.で作成したslack_apis_access
  2. secretで2.で作成したslack_token

これらオプションの指定により、当該UDFが外部ネットワークにアクセスが可能となります。

UDFのコンテンツについて説明します。1番最初のimportにある通り、_snowflakeを指定することで、Secret Access関連のメソッドを使用することができます。次にget_generic_secret_stringを使用し、3.で設定したSlackのAccess Tokenを取得します。なお、get_generic_secret_string以外に3つメソッドがございます。(2023/7現在)

  • get_oauth_access_token(oauth_secret_name)
  • get_secret_type(secret_name)
  • get_username_password(username_password_secret_name)

SlackのWeb APIの仕様に従い、ヘッダーとデータの情報を設定した上でAPIにPostする流れとなっております。(詳細はコード参照)

--4. Slack通知するPython UDFの作成
CREATE OR REPLACE FUNCTION post_to_slack(channel varchar ,message varchar)
RETURNS variant
LANGUAGE python
RUNTIME_VERSION = 3.8
HANDLER = 'main'
EXTERNAL_ACCESS_INTEGRATIONS = (slack_apis_access)
PACKAGES = ('requests')
SECRETS = ('cred' = slack_token)
AS
$$
import _snowflake
import requests
import json
def main(p_channel ,p_message):
     api_key = _snowflake.get_generic_secret_string('cred')
     url = 'https://slack.com/api/chat.postMessage'
     v_headers = {{"Authorization": "Bearer "+ api_key}}
     v_data_json = {{
          'channel': p_channel
          ,'text': p_message
     }}
     response = requests.post(url ,headers=v_headers ,json = v_data_json)
     return response.json()
$$;

5. テスト

下記の通り、通知するチャネルIDとテキストメッセージを指定し、UDFを実行しています。

SELECT
'<your-channel-id>' as channel
,'TEST!!!' as message
,post_to_slack(channel ,message) as slack_notify

添付のようにメッセージが届いたら成功です。
テスト結果

まとめ

本記事では、Summitで発表されたExternal Network Accessを使用し、Slack通知をする手順を示しました。数行のSQLコードでSlack通知機能をシンプルに実装できるように、従来とは比較にならないほど簡単になったのではないかと思います。個人的には、今後はアラート機能と新たに出てきたトリガータスク機能と併用することで、エラー発生、検知、通知までの一連の動作をSnowflake内でイベントドリブンで実装することが可能になると考えています。また、外部ネットワークの候補先として、Slack以外にもGoogleやOpen AIなど様々なユースケースで利用が考えられ、今後もこちらの機能は重宝しそうだと思います。(セキュリティには気をつけましょう)

Discussion

ShintaroAmaikeShintaroAmaike

Traceback (most recent call last): File "_udf_code.py", line 8, in main TypeError: unhashable type: 'dict'
エラーでした。
下記修正したらうまくいきました。記事ありがとうございました。

v_headers = {"Authorization": "Bearer "+ api_key}
     v_data_json = {
          'channel': p_channel
          ,'text': p_message
     }