Streamlit in Snowflakeで誰でもSnowflakeユーザーを作成できるアプリを作ってみた。

# Streamlit in Snowflakeで誰でもSnowflakeユーザーを作成できるアプリを作ってみた。
[Snowflake Summit 2022](https://www.snowflake.com/blog/summit-2022-guide/?lang=ja)でも紹介されていたStreamlit in Snowflakeが遂にパブリックプレビューされました！みなさんのSnowsight上にもStreamlitタブが追加されているはずです。
今回は、**Streamlit in Snowflakeを用いてStreamlit上でユーザー作成する方法**について紹介したいと思います。

### 実装イメージ
必要な情報を記載して・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342101/e55fd6fb-b70d-eaec-573d-bc75395bc31c.png)
**実行！**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342101/47e363a1-5e4d-c811-4b2b-39be3d6c5046.png)
こんな感じです。これがあればビジネスユーザーでも簡単にユーザー作成ができますね。

# 使用ツール

### [Snowflake](https://www.snowflake.com/ja/)
クラウドベースのデータウェアハウスであり、大量のデータを高速かつスケーラブルに処理できます。[30日間の無料トライアル](https://signup.snowflake.com/?_l=ja)もあります。
Streamlit in Snowflakeでは、Snowflake内のGUI画面で簡単にStreamlitコードを編集・実行できるので、**自環境にPythonのインストール等の準備は一切不要**です。
環境準備をせずとも、すぐに機能を使えるのは良いですね。

# 設計内容
今回のアプリでは以下の処理を行います。
- 以下の情報を設定したユーザーを作成する。
　・ユーザー名
　・パスワード
　・姓
　・名
　・メールアドレス
　・デフォルトロールはSYSADMIN
- 作成したユーザーにロールの権限を付与する。
　・追加でいくつかのシステムロールを付与

Snowflakeの[システム定義ロール](https://docs.snowflake.com/ja/user-guide/security-access-control-overview)の設計に基づいて、クエリを実行します。
- ユーザー作成はUSERADMINロールで実行
- ロール権限付与はSECURITYADMINロールで実行
```mermaid
graph TD
  Start([Start])--"USERADMIN"-->A[ユーザー作成]--"SECURITYADMIN"-->B[ロール権限付与]-->End([End])
```
アプリの所有者は、どのロールでも良いのですが、今回はSYSADMINを所有者に設定します。

## 引っかかった箇所
今回は、一つのStreamlit in Snowflakeアプリの中で、複数のロール(USERADMIN/SECURITYADMIN)の使い分けを行います。
しかし、Streamlit in Snowflakeアプリでロールの切替を行おうとすると・・・
```Python:Streamlit
session.use_role('USERADMIN')
```
以下のエラーが発生します。
```Python:stderr
ProgrammingError: 090236 (42601): Stored procedure execution error: Unsupported statement type 'USE'.
Traceback:
File "/usr/lib/python_udf/b18d1e71149c9c3c8b8a98c1a67ef6568c01649ca843bd6fe38efda48ed13587/lib/python3.8/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 565, in _run_script
    exec(code, module.__dict__)
File "/home/udf/122239981/streamlit_app.py", line 47, in <module>
    session.use_role('USERADMIN')
File "/usr/lib/python_udf/b18d1e71149c9c3c8b8a98c1a67ef6568c01649ca843bd6fe38efda48ed13587/lib/python3.8/site-packages/snowflake/snowpark/session.py", line 1792, in use_role
    self._use_object(role, "role")
File "/usr/lib/python_udf/b18d1e71149c9c3c8b8a98c1a67ef6568c01649ca843bd6fe38efda48ed13587/lib/python3.8/site-packages/snowflake/snowpark/session.py", line 1812, in _use_object
    self._run_query(f"use {object_type} {object_name}")
File "/usr/lib/python_udf/b18d1e71149c9c3c8b8a98c1a67ef6568c01649ca843bd6fe38efda48ed13587/lib/python3.8/site-packages/snowflake/snowpark/session.py", line 1240, in _run_query
    return self._conn.run_query(
File "/usr/lib/python_udf/b18d1e71149c9c3c8b8a98c1a67ef6568c01649ca843bd6fe38efda48ed13587/lib/python3.8/site-packages/snowflake/snowpark/_internal/server_connection.py", line 102, in wrap
    raise ex
File "/usr/lib/python_udf/b18d1e71149c9c3c8b8a98c1a67ef6568c01649ca843bd6fe38efda48ed13587/lib/python3.8/site-packages/snowflake/snowpark/_internal/server_connection.py", line 96, in wrap
    return func(*args, **kwargs)
File "/usr/lib/python_udf/b18d1e71149c9c3c8b8a98c1a67ef6568c01649ca843bd6fe38efda48ed13587/lib/python3.8/site-packages/snowflake/snowpark/_internal/server_connection.py", line 365, in run_query
    raise ex
File "/usr/lib/python_udf/b18d1e71149c9c3c8b8a98c1a67ef6568c01649ca843bd6fe38efda48ed13587/lib/python3.8/site-packages/snowflake/snowpark/_internal/server_connection.py", line 346, in run_query
    results_cursor = self._cursor.execute(query, params=params, **kwargs)
File "/usr/lib/python_udf/b18d1e71149c9c3c8b8a98c1a67ef6568c01649ca843bd6fe38efda48ed13587/lib/python3.8/site-packages/snowflake/connector/cursor.py", line 829, in execute
    Error.errorhandler_wrapper(
File "/usr/lib/python_udf/b18d1e71149c9c3c8b8a98c1a67ef6568c01649ca843bd6fe38efda48ed13587/lib/python3.8/site-packages/snowflake/connector/errors.py", line 232, in errorhandler_wrapper
    handed_over = Error.hand_to_other_handler(
File "/usr/lib/python_udf/b18d1e71149c9c3c8b8a98c1a67ef6568c01649ca843bd6fe38efda48ed13587/lib/python3.8/site-packages/snowflake/connector/errors.py", line 287, in hand_to_other_handler
    cursor.errorhandler(connection, cursor, error_class, error_value)
File "/usr/lib/python_udf/b18d1e71149c9c3c8b8a98c1a67ef6568c01649ca843bd6fe38efda48ed13587/lib/python3.8/site-packages/snowflake/connector/errors.py", line 165, in default_errorhandler
    raise error_class(
```
どうやらStreamlit in Snowflakeでは、"USE"ステートメントは利用できないようです。

何か**実行ロールを指定したままクエリを実行する方法**は無いかな・・・

・・・

ということで、[所有者権限のストアドプロシージャ](https://docs.snowflake.com/ja/sql-reference/stored-procedures-rights)を使って実装していきたいと思います！
ご存知ない方は是非リンクから確認してみてください。とても便利な機能です。


# 作成手順
環境作成から機能実行までのクエリを説明していきます。
コードはモックとして簡単に書いています。(実際にはエラー制御しないといけないはず)

## 1.環境作成
Streamlit in Snowflakeでは、Streamlitを実行するためのデータベース・スキーマを用意する必要があります。今回はSTREAMLITデータベースとCREATE_USERスキーマを作成します。
必要な権限はStreamlit in Snowflakeのドキュメントを参照してください。
```SQL:SQL
create database streamlit;
create schema create_user;

grant usage on database streamlit to role useradmin;
grant usage on schema streamlit.create_user to role useradmin;
grant create procedure on schema streamlit.create_user to role useradmin;
grant usage on warehouse compute_wh to role useradmin;

grant usage on database streamlit to role securityadmin;
grant usage on schema streamlit.create_user to role securityadmin;
grant create procedure on schema streamlit.create_user to role securityadmin;
grant usage on warehouse compute_wh to role securityadmin;
```
## 2.ストアドプロシージャの作成
所有者権限のストアドプロシージャを作成します。"execute as owner"の記載を忘れないように・・・。

まずは、ユーザー作成用のストアドプロシージャです。
今回はDEFAULT_ROLEをSYSADMINとして設定しています。
```SQL:SQL
use role useradmin;
create or replace procedure create_user(username string, password string, first_name string, last_name string, email string)
  returns string
  language python
  runtime_version = '3.8'
  packages = ('snowflake-snowpark-python')
  handler = 'run'
  execute as owner
as
$$
def run(session, username, password, first_name, last_name, email):
    query = 'CREATE USER ' + username + \
        ' PASSWORD =\'' + password + '\'' + \
        ' FIRST_NAME=\'' + first_name + '\'' + \
        ' LAST_NAME=\'' + last_name + '\'' + \
        ' EMAIL=\'' + email + '\'' + \
        ' DEFAULT_ROLE=SYSADMIN' + \
        ' MUST_CHANGE_PASSWORD=TRUE' + \
        ';'
    session.sql(query).collect()
    return "SUCCESS"
$$;
```
次に、ロール権限付与用のストアドプロシージャです。
```SQL:SQL
use role securityadmin;
create or replace procedure grant_role(role string, username string)
  returns string
  language python
  runtime_version = '3.8'
  packages = ('snowflake-snowpark-python')
  handler = 'run'
  execute as owner
as
$$
def run(session, role, username):
    query = 'GRANT ROLE ' + role + ' TO USER ' + username +';'
    session.sql(query).collect()
    return "SUCCESS"
$$;
```

最後に、作成したストアドプロシージャの権限を、本機能の所有者とするSYSADMINに付与します。
```SQL:SQL
grant usage on procedure streamlit.create_user.create_user(string,string,string,string,string) to role sysadmin;
grant usage on procedure streamlit.create_user.grant_role(string,string) to role sysadmin;
```
## 3.Streamlitアプリの作成
Snowsightの左タブにある**Streamlit**からページ遷移し、Streamlitアプリを作成します。
※Streamlit in Snowflakeを利用するには、Anacondaの規約に合意する必要があります。ORGADMINロールで[管理者]-[請求と利用規約]に進み、Anacondaの規約の承認を確認してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342101/850c935a-76ac-fc29-cff0-f87b3d249b6a.png)

今回は、SYSADMINでアプリを実行したいので、SYSADMINロールになっていることを確認してから、Streamlitアプリを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342101/ee1b8aa2-e49c-a2f9-83ab-df6e35c28174.png)
ここで指定するウェアハウスは**Streamlitを動作させるウェアハウス**なので、上記のストアドプロシージャで指定したものと異なっても構いません。

## 4.Streamlitコードの記載

#### ライブラリをインポートします。
```Python:Streamlit
import streamlit as st
from snowflake.snowpark.context import get_active_session
```
#### ユーザー作成やロール権限付与に必要な情報を記載していきます。

初期パスワードを固定で払い出していい場合はデフォルトで記載しておきましょう。
メールアドレスのドメインも毎回書くのは面倒なのでその部分だけでもデフォルトで記載しておきます。

権限付与想定のロールはシステムロールを4つ記載しています。
```Python:Streamlit
session = get_active_session()

username = st.text_input(
    'ユーザー名を入力してください')

password = st.text_input(
    'パスワードを入力してください',
    value = 'password')

last_name = st.text_input(
    '姓を入力してください')

first_name = st.text_input(
    '名を入力してください')

email = st.text_input(
    'メールアドレスを入力してください',
    value = '@nttdata.com')

role = st.multiselect(
    '必要なロールを指定して下さい',
    ('ACCOUNTADMIN','ORGADMIN','USERADMIN','SECURITYADMIN'))
```
#### 実行ボタンとその処理を記載していきます。
先程作成したストアドプロシージャを呼び出して、ユーザーを作成します。
**st.snow**は面白い機能ですので、実行ボタンの処理に付けることをおすすめします。
```Python:Streamlit
submitted = st.button("❄️ Query to Snowflake")
if submitted:
    try:
        session.call('create_user',username, password, first_name, last_name, email)
        session.call('grant_role','SYSADMIN',username)
        for element in role:
            session.call('grant_role',element,username)
        st.write('ユーザー ' + username + ' は作成されました。')
        st.snow()
    except:
        st.write('something wrong about query.')
```

これでアプリの作成は完了です。

# アプリ実行
必要な情報を記載して・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342101/e55fd6fb-b70d-eaec-573d-bc75395bc31c.png)
**実行！**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342101/8d9286a0-df65-ae8d-bfe2-ffa80890cbd4.png)
・・・雪。
ユーザー作成と共に雪が降るのはなぜか、ソースを見て調べてみてください。

# まとめ
今回は、Streamlit in Snowflakeでユーザー作成機能を作ってみました。
環境準備も何も要らずにStreamlitが使えるのは、とても取っ掛かり易いです。
Snowflake内のデータを使って可視化する以外にも、こういった使い方も面白いですね。


# 仲間募集

NTTデータ テクノロジーコンサルティング事業本部 では、以下の職種を募集しています。

<details><summary>1. クラウド技術を活用したデータ分析プラットフォームの開発・構築(ITアーキテクト/クラウドエンジニア)</summary>

クラウド／プラットフォーム技術の知見に基づき、DWH、BI、ETL領域におけるソリューション開発を推進します。
https://enterprise-aiiot.nttdata.com/recruitment/career_sp/cloud_engineer

</details>

<details><summary>2. データサイエンス領域（データサイエンティスト／データアナリスト）</summary>

データ活用／情報処理／AI／BI／統計学などの情報科学を活用し、よりデータサイエンスの観点から、データ分析プロジェクトのリーダーとしてお客様のDX／デジタルサクセスを推進します。
https://enterprise-aiiot.nttdata.com/recruitment/career_sp/datascientist

</details>

<details><summary>3.お客様のAI活用の成功を推進するAIサクセスマネージャー</summary>

DataRobotをはじめとしたAIソリューションやサービスを使って、
お客様のAIプロジェクトを成功させ、ビジネス価値を創出するための活動を実施し、
お客様内でのAI活用を拡大、NTTデータが提供するAIソリューションの利用継続を推進していただく人材を募集しています。
https://nttdata.jposting.net/u/job.phtml?job_code=804

</details>

<details><summary>4.DX／デジタルサクセスを推進するデータサイエンティスト《管理職/管理職候補》</summary>
データ分析プロジェクトのリーダとして、正確な課題の把握、適切な評価指標の設定、分析計画策定や適切な分析手法や技術の評価・選定といったデータ活用の具現化、高度化を行い分析結果の見える化・お客様の納得感醸成を行うことで、ビジネス成果・価値を出すアクションへとつなげることができるデータサイエンティスト人材を募集しています。

https://nttdata.jposting.net/u/job.phtml?job_code=898

</details>


# ソリューション紹介

<details><summary> Trusted Data Foundationについて</summary><div>

～データ資産を分析活用するための環境をオールインワンで提供するソリューション～
https://enterprise-aiiot.nttdata.com/tdf/
最新のクラウド技術を採用して弊社が独自に設計したリファレンスアーキテクチャ（Datalake+DWH+AI/BI）を顧客要件に合わせてカスタマイズして提供します。
可視化、機械学習、DeepLearningなどデータ資産を分析活用するための環境がオールインワンで用意されており、これまでとは別次元の量と質のデータを用いてアジリティ高くDX推進を実現できます。

</div></details>


<details><summary> TDFⓇ-AM（Trusted Data Foundation - Analytics Managed Service）について</summary><div>

～データ活用基盤の段階的な拡張支援（Quick Start) と保守運用のマネジメント（Analytics Managed）をご提供することでお客様のDXを成功に導く、データ活用プラットフォームサービス～
https://enterprise-aiiot.nttdata.com/service/tdf/tdf_am
TDFⓇ-AMは、データ活用をQuickに始めることができ、データ活用の成熟度に応じて段階的に環境を拡張します。プラットフォームの保守運用はNTTデータが一括で実施し、お客様は成果創出に専念することが可能です。また、日々最新のテクノロジーをキャッチアップし、常に活用しやすい環境を提供します。なお、ご要望に応じて上流のコンサルティングフェーズからAI/BIなどのデータ活用支援に至るまで、End to Endで課題解決に向けて伴走することも可能です。

</div></details>


<details><summary>NTTデータとTableauについて </summary><div>

ビジュアル分析プラットフォームのTableauと2014年にパートナー契約を締結し、自社の経営ダッシュボード基盤への採用や独自のコンピテンシーセンターの設置などの取り組みを進めてきました。さらに2019年度にはSalesforceとワンストップでのサービスを提供開始するなど、積極的にビジネスを展開しています。

これまでPartner of the Year, Japanを4年連続で受賞しており、2021年にはアジア太平洋地域で最もビジネスに貢献したパートナーとして表彰されました。
また、2020年度からは、Tableauを活用したデータ活用促進のコンサルティングや導入サービスの他、AI活用やデータマネジメント整備など、お客さまの企業全体のデータ活用民主化を成功させるためのノウハウ・方法論を体系化した「デジタルサクセス」プログラムを提供開始しています。
https://enterprise-aiiot.nttdata.com/service/tableau

</div></details>


<details><summary>NTTデータとAlteryxについて </summary><div>
Alteryxは、業務ユーザーからIT部門まで誰でも使えるセルフサービス分析プラットフォームです。

Alteryx導入の豊富な実績を持つNTTデータは、最高位にあたるAlteryx Premiumパートナーとしてお客さまをご支援します。

導入時のプロフェッショナル支援など独自メニューを整備し、特定の業種によらない多くのお客さまに、Alteryxを活用したサービスの強化・拡充を提供します。

https://enterprise-aiiot.nttdata.com/service/alteryx

</div></details>

<details><summary>NTTデータとDataRobotについて </summary><div>
DataRobotは、包括的なAIライフサイクルプラットフォームです。

NTTデータはDataRobot社と戦略的資本業務提携を行い、経験豊富なデータサイエンティストがAI・データ活用を起点にお客様のビジネスにおける価値創出をご支援します。

https://enterprise-aiiot.nttdata.com/service/datarobot

</div></details>

<details><summary> NTTデータとInformaticaについて</summary><div>

データ連携や処理方式を専門領域として10年以上取り組んできたプロ集団であるNTTデータは、データマネジメント領域でグローバルでの高い評価を得ているInformatica社とパートナーシップを結び、サービス強化を推進しています。
https://enterprise-aiiot.nttdata.com/service/informatica

</div></details>

<details><summary>NTTデータとSnowflakeについて </summary><div>
NTTデータでは、Snowflake Inc.とソリューションパートナー契約を締結し、クラウド・データプラットフォーム「Snowflake」の導入・構築、および活用支援を開始しています。

NTTデータではこれまでも、独自ノウハウに基づき、ビッグデータ・AIなど領域に係る市場競争力のあるさまざまなソリューションパートナーとともにエコシステムを形成し、お客さまのビジネス変革を導いてきました。
Snowflakeは、これら先端テクノロジーとのエコシステムの形成に強みがあり、NTTデータはこれらを組み合わせることでお客さまに最適なインテグレーションをご提供いたします。

https://enterprise-aiiot.nttdata.com/service/snowflake

</div></details>