投稿者: sumito.tsukada

  • Warning: Failed to run dot.    Download dot graphvizVersion 2.26 or versions greater than 2.31

    Warning: Failed to run dot. Download dot graphvizVersion 2.26 or versions greater than 2.31

    この警告メッセージは、dot コマンドが利用できないか、または適切なバージョンがインストールされていないことを示しています。dot コマンドは Graphviz パッケージの一部です。

    エラーメッセージ

    状況としては、何らかのアプリケーションまたはスクリプトを実行しているときに次のような警告メッセージが表示されることがあります。

    Warning: Failed to run dot.
    Download dot graphvizVersion 2.26 or versions greater than 2.31
    from www.graphviz.org and make sure that dot is either in your path
    or point to where you installed Graphviz with the -gv option.
    Generated pages will not contain a diagramtic view of table relationships.

    このメッセージは、dot コマンドが利用できないか、または適切なバージョンがインストールされていないことを示しています。

    解決方法

    この問題は、Debian ベースの Linux ディストリビューション(Ubuntu、Debian など)でapt-get を使用して解決することができます。

    1.  リポジトリを更新

    システムのパッケージリストを更新して、最新の情報を取得します。

    sudo apt-get update

    2. Graphviz のインストール

    次に、Graphviz パッケージをインストールします。

    sudo apt-get install graphviz

    4. インストールの確認

    インストールが成功したかどうかを確認するため、dot コマンドのバージョンを確認します。

    dot -V

    これで、バージョン情報が表示されれば、Graphviz は正常にインストールされ、dot コマンドが利用可能な状態になっています。

     

  • PythonでSnowflakeの不要なログを抑制する

    PythonでSnowflakeの不要なログを抑制する

    Snowflake Pythonコネクタは標準のPythonロギングモジュールを使用しています。

    このため、ロギングレベルを変更することで出力されるメッセージの制御が可能です。

    ただし、ロギングレベルを変更することは、接続の状態に関する重要な情報が隠される可能性があります。

    そのため、この変更を行う前に、その意味を完全に理解し、接続が安全であることを確認する必要があります。

    以下に、Snowflakeのロギングレベルを変更する方法を示します。

    1. まず、snowflake.connectorをインポートするPythonモジュールで、コネクタのメソッドを呼び出す前に以下のコードを追加します。これにより、警告またはそれ以上のレベルのログのみが出力されます。
    import logging
    logging.getLogger('snowflake.connector').setLevel(logging.WARNING)
    1. 上記のソリューションでも、すべてのSnowflakeメッセージが消えない場合は、以下のコードを使用して、すべてのSnowflakeロガーをより高いレベルに設定できます。
    import logging
    for name in logging.Logger.manager.loggerDict.keys():
    if 'snowflake' in name:
    logging.getLogger(name).setLevel(logging.WARNING)
    logging.getLogger(name).propagate = False

    この変更により、以下のようなエラーメッセージが表示されなくなります。

    eventType: 'RevocationCheckFailure',
    eventSubType: 'OCSPResponseFailedToConnectCacheServer|OCSPResponseFetchException|OCSPResponseFetchException|OCSPResponseFetchException|OCSPResponseFetchException',
    sfcPeerHost: 'xxxxx.ap-northeast-1.privatelink.snowflakecomputing.com',
    certId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    ocspRequestBase64: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    ocspResponderURL: 'http://o.ss2.us/',
    errorMessage: "254003: Could not fetch OCSP Response from server. Consider checking your whitelists : Exception - HTTPConnectionPool(host='ocsp.xxxxx.ap-northeast-1.privatelink.snowflakecomputing.com', port=80): Max retries exceeded with url: /retry/o.ss2.us/xxxxx+xxx= (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0xffff9c1ec190>: Failed to establish a new connection: [Errno -2] Name or service not known'))",
    insecureMode: False,
    failOpen: True,
    cacheEnabled: True,
    cacheHit: False

    このメッセージは、OCSP(Online Certificate Status Protocol)の検証が失敗した場合に出力されます。OCSPは、TLS接続の設定中に、接続先の証明書が有効であることを確認するためのプロトコルです。このメッセージが表示されるということは、SnowflakeコネクタがOCSP応答を取得できなかったことを示します。このような場合、接続が安全であることを確認した上で、ロギングレベルを変更することを検討してください。

    これらのステップを適用すると、Snowflake Pythonコネクタの不要なログ出力を抑制できます。ただし、これによりエラー情報が失われる可能性があるため、注意が必要です。常に接続が安全であることを確認し、必要に応じて適切なロギングレベルを選択してください。

  • AWS CloudWatch Logsからユーザーエージェントデータを分析し、OSとブラウザのシェアを視覚化する

    AWS CloudWatch Logsからユーザーエージェントデータを分析し、OSとブラウザのシェアを視覚化する

    イントロダクション:

    AWS CloudWatch Logsは、AWSリソース、アプリケーション、サービスのログデータを監視して保存するためのサービスです。

    この記事では、CloudWatch Logsからユーザーエージェントデータを収集し、PythonとGoogle スプレッドシートを使用してOSとブラウザのシェアを視覚化する方法を解説します。

    これはウェブサイトやアプリケーションのユーザー体験を最適化するために非常に有益です。

    手順1: CloudWatch Logsからログを取得する

    まず、CloudWatch Logsからユーザーエージェントデータを含むログを取得する必要があります。

    AWS Management Consoleにログインし、CloudWatch Logsのページに移動します。

    1. ロググループを選択し、ログストリームを開きます。

    2. クエリを使用してログデータをフィルターします。以下は、ユーザーエージェント情報を含むログエントリを抽出するサンプルクエリです。

    fields @timestamp, @message
    | parse @message /(?<ip>[\d\.]+) - - \[(?<timestamp>[^\]]+)\] "(?<http_method>[^"]+)" (?<status_code>[^ ]+) (?<size>[^ ]+) "(?<referrer>[^"]*)" "(?<user_agent>[^"]+)"/
    | stats count(*) by user_agent
    1. 上記のクエリをCloudWatch Logs Insightsのクエリエディタに貼り付け、実行します。

    2. 結果をCSVファイルとしてエクスポートします。

    手順2: Pythonを使用してログデータを解析する

    ログデータが得られたら、Pythonを使用して分析します。

    この記事では、user_agentsというPythonライブラリを使用します。

    このライブラリは、ユーザーエージェント文字列を解析して、デバイス、OS、ブラウザに関する情報を抽出するのに役立ちます。

    user_agents ライブラリをインストールします。

    pip install pyyaml ua-parser user-agents

    次に、以下のPythonスクリプトを使用して、ユーザーエージェント文字列からデバイス、OSバージョン、ブラウザ、ブラウザバージョンを抽出し、CSVファイルに出力します。

    import csv
    from user_agents import parse
    # ログファイルの読み込み
    with open('logs.csv', 'r') as file:
    logs = csv.DictReader(file)
    # 結果を保存するためのリスト
    results = []
    # 各ログエントリを処理
    for log in logs:
    user_agent = log['user_agent']
    count = log['count']
    # user_agents ライブラリでユーザーエージェント文字列を解析
    ua = parse(user_agent)
    device_type = 'Mobile' if ua.is_mobile else 'Tablet' if ua.is_tablet else 'PC'
    os = f"{ua.os.family} {ua.os.version_string}"
    browser = f"{ua.browser.family}"
    browser_version = f"{ua.browser.version_string}"
    # 結果をリストに追加
    results.append([device_type, os, browser, browser_version, count])
    # 結果を新しいCSVファイルに出力
    with open('parsed_logs.csv', 'w', newline='') as file:
    writer = csv.writer(file)
    writer.writerow(['Device', 'OS Version', 'Browser', 'Browser Version', 'Count'])
    writer.writerows(results)

    手順3: Google スプレッドシートでデータを視覚化する

    最後に、Google スプレッドシートでデータを視覚化します。

    1. Google スプレッドシートを開き、手順2で生成された parsed_logs.csv をインポートします。

    2. ピボットテーブルを作成します。これには、データ範囲を選択し、「データ」メニューから「ピボットテーブル」を作成します。

    3. ピボットテーブルエディターで、「行」に分析したいカテゴリ(例:Device, OS Version, Browser)を追加し、「値」に「Count」を追加して合計を計算します。

    4. ピボットテーブルのデータを選択し、「挿入」メニューから「グラフ」を選択して円グラフを作成します。これで、OSやブラウザのシェアを視覚化することができます。

    結論:

    CloudWatch Logsからのログデータ分析は、アプリケーションのパフォーマンスとユーザー体験を向上させるのに役立つ重要なステップです。

    このチュートリアルでは、Pythonのuser_agents ライブラリを使用してユーザーエージェントデータを解析し、Google スプレッドシートで視覚化する方法を紹介しました。

    この情報は、デバイスやブラウザの最適化、または特定のOSバージョン向けの機能開発に役立てれば幸いです。

  • Cognito 認証情報を活用した AWS Lambda の効果的なユーザー管理

    Cognito 認証情報を活用した AWS Lambda の効果的なユーザー管理

    導入

    現代のビジネス環境において、アプリケーションのセキュリティとユーザー管理は重要な柱です。AWS Lambda と Amazon Cognito を組み合わせることで、高度にスケーラブルでセキュアなサービスを効率よく構築できます。本記事では、Amazon Cognito で認証されたユーザーの情報を AWS Lambda で取得し、ビジネスプロセスに統合する方法に焦点を当てます。

    ビジネスの利点

    Amazon Cognito と AWS Lambda の組み合わせは、以下のビジネス上の利点を提供します。

    • セキュリティの強化: ユーザー認証情報を適切に処理することで、不正アクセスを防ぎます。
    • コスト効率: サーバーレスアーキテクチャを利用することで、リソースの利用に応じて課金され、余分なコストを削減します。
    • 拡張性: 需要に応じて自動的にスケールします。これにより、ビジネスが拡大してもパフォーマンスが維持されます。

    実装

    以下は、AWS Lambda 関数で Cognito の認証が通ったユーザーからメールアドレスを取得するステップバイステップのガイドです。

    ステップ1: IDトークンの取得

    まず、AWS Lambda 関数で HTTP リクエストのヘッダーから ID トークンを取得します。これは、Amazon Cognito によって発行された JWT(JSON Web Token)です。

    id_token = request.headers.get("x-amzn-oidc-data", "")

    ステップ2: トークンのデコード

    次に、JWT のペイロード部分をデコードします。JWT は3つの部分(ヘッダー、ペイロード、署名)で構成されており、ペイロードにはユーザー情報が含まれています。

    token_parts = id_token.split('.')
    payload_encoded = token_parts[1]
    padding_needed = 4 - len(payload_encoded) % 4
    payload_encoded += "=" * padding_needed
    payload_decoded = base64.b64decode(payload_encoded)
    payload_json = json.loads(payload_decoded.decode("utf-8"))

    ステップ3: メールアドレスの抽出

    最後に、デコードされたペイロードからメールアドレスを抽出します。

    email = payload_json['email']

    まとめ

    Amazon CognitoとAWS Lambdaを組み合わせることで、セキュアでスケーラブルなユーザー管理を実現し、ビジネスプロセスを効率化することができます。ユーザー認証情報の適切な処理は、アプリケーションのセキュリティを強化し、顧客の信頼を築く上で不可欠です。

    今回解説した方法を使用すると、簡潔にユーザーのメールアドレスを取得でき、これをビジネスロジックやユーザー通知など、さまざまな用途で活用することができます。

  • Microsoft Authentication Library (MSAL) との連携時に発生する接続エラーとその回避方法

    Microsoft Authentication Library (MSAL) との連携時に発生する接続エラーとその回避方法

    PythonでMSAL (Microsoft Authentication Library) を利用してAzure AD認証を実施する際に、接続エラーに直面することがあります。この記事では、その一般的なエラーと、これを解消するための回避策について詳しく解説します。

    エラーの例

    MSALを使用してAzure ADのトークンを取得する際に、以下のようなエラーメッセージが表示されることがあります。

    requests.exceptions.ConnectionError: HTTPSConnectionPool(host='login.microsoftonline.com', port=443): Max retries exceeded with url: /yourtenant.onmicrosoft.com/v2.0/.well-known/openid-configuration (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7f84b146ad00>: Failed to establish a new connection: [Errno -3] Temporary failure in name resolution'))

    このエラーは、プログラムがMicrosoftの認証サーバーに接続しようとしたときに発生します。

    エラーの原因

    このエラーの原因は主に以下の通りです。

    1. network接続の問題: なんらかの問題で通信がうまくいかなかった

    2. DNSの問題: DNSが一時的に名前解決できない場合があります。これは一時的な問題である場合が多く、しばらく待ってから再試行すると解決することがあります。

    3. サーバーのオーバーロード: Microsoftのサーバーに対するリクエストが多すぎて、サーバーが反応しない場合があります。

    解決策

    上記のエラーに対処としてリトライを実装しました。

    コード例

    以下は、再試行ロジックを実装したPythonのコード例です。

    import time
    import requests
    import msal
    import logging
    logger = logging.getLogger(__name__)
    def acquire_token_with_retries(client_id, private_key, thumbprint, authority_url, max_retries=3, retry_delay=5):
    retries = 0
    while retries < max_retries:
    try:
    client_app = msal.ConfidentialClientApplication(
    client_id=client_id,
    client_credential={"private_key": private_key, "thumbprint": thumbprint},
    authority=authority_url
    )
    return client_app.acquire_token_on_behalf_of(user_assertion="YOUR_USER_ASSERTION", scopes=["https://graph.microsoft.com/.default"])
    except requests.exceptions.ConnectionError as e:
    logger.warning(f"Connection error occurred: {str(e)}. Retrying in {retry_delay} seconds...")
    retries += 1
    time.sleep(retry_delay)
    logger.error(f"Failed to acquire token after {max_retries} attempts.")
    return None

    このコードでは、トークン取得を試みる前に、最大リトライ回数を指定し、それを超えた場合はエラーを記録してNoneを返します。また、リトライの間に指定された秒数だけ遅延させます。

    まとめ

    MSALを使ってAzure AD認証を行う際に、接続エラーが発生する可能性があります。その際は、インターネット接続を確認し、再試行ロジックを実装することで、これらの一時的な問題を解決できる可能性があります。エラー発生時に適切なログ情報を収集することも重要です。

  • タイトル: S3バケットに関連付けられたLambda関数をAWS CLIで調査する

    タイトル: S3バケットに関連付けられたLambda関数をAWS CLIで調査する

    はじめに

    Amazon S3バケットにファイルがアップロードされたときにAWS Lambda関数を自動的にトリガーするのは、一般的な使用例です。しかし、プロジェクトが大規模になると、どのLambda関数が特定のS3バケットと関連付けられているのかを把握するのが難しくなることがあります。この記事では、AWS CLIを使用して、S3バケットに関連付けられたLambda関数を調査する方法について解説します。

    手順

    1. AWS CLIのインストールと設定

    AWS CLIがまだインストールされていない場合は、公式ドキュメントに従ってインストールしてください。次に、aws configureコマンドを使用してAWSアカウントの認証情報を設定します。

    2. S3バケットの通知設定を確認

    次に、以下のコマンドを使用してS3バケットの通知設定を取得します。このコマンドはバケットの名前を指定する必要があります。

    aws s3api get-bucket-notification-configuration --bucket YOUR_BUCKET_NAME

    このコマンドを実行すると、バケットに関連付けられた通知設定がJSON形式で表示されます。

    3. 結果の解析

    出力されたJSONの中で、LambdaFunctionConfigurations セクションに注目します。このセクションには、関連付けられたLambda関数のリストが含まれています。それぞれの項目では、Lambda関数のARN (Amazon Resource Name)、トリガーされるS3イベント、および任意のフィルタルールに関する情報が表示されます。

    実例

    以下は、実際の出力の例です(固有情報はマスクされています):

    {
    "LambdaFunctionConfigurations": [
    {
    "Id": "example-trigger-1",
    "LambdaFunctionArn": "arn:aws:lambda:region:account-id:function:example-function-1",
    "Events": [
    "s3:ObjectCreated:Put",
    "s3:ObjectCreated:Post"
    ],
    "Filter": {
    "Key": {
    "FilterRules": [
    {
    "Name": "Suffix",
    "Value": ".csv"
    }
    ]
    }
    }
    },
    {
    "Id": "example-trigger-2",
    "LambdaFunctionArn": "arn:aws:lambda:region:account-id:function:example-function-2",
    "Events": [
    "s3:ObjectCreated:Put",
    "s3:ObjectCreated:Post"
    ],
    "Filter": {
    "Key": {
    "FilterRules": [
    {
    "Name": "Suffix",
    "Value": ".txt"
    }
    ]
    }
    }
    }
    ]
    }

    この例では、2つのLambda関数がS3バケットに関連付けられています。

    それぞれが異なるファイルタイプ(.csvおよび.txt)のアップロード時にトリガーされます。

    まとめ

    AWS CLIを使用して、簡単にS3バケットに関連付けられているLambda関数の情報を取得することができます。これにより、システムの動作を理解し、必要に応じて設定を調整することが可能になります。

  • Office 365 APIのトークン認証エラーAADSTS500133: の対処

    Office 365 APIのトークン認証エラーAADSTS500133: の対処

    イントロダクション

    Office 365 APIは、ビジネスプロセスの効率化に大いに貢献しています。しかし、これらのAPIをPythonスクリプトで使用する際、トークン認証に関連するエラー AADSTS500133 が発生することがあります。

    エラーの理解

    エラー AADSTS500133 は、トークンの有効期限切れやシステム時刻の不整合などに起因することが多いです。具体的には、以下のようなメッセージが表示されます。

    ValueError: {'error': 'invalid_grant', ... 'AADSTS500133: Assertion is not within its valid time range. ...}

    解決策の展開

    Step 1: トークンのリフレッシュを確認

    ビジネス環境では、認証トークンが自動的に更新されるように、アプリケーションの設定を最適化してください。これにより、中断されることなく業務を継続できます。

    Step 2: システム時刻の整合性を確認

    システム時刻が正確であることを確認し、必要に応じて同期してください。これは、特にクラウドサービスや複数のサーバーが絡むビジネス環境で重要です。

    Step 3: セキュリティと権限の設定を見直す

    アプリケーションとOffice 365 APIとの連携に必要なセキュリティ設定や権限を確認し、適切に設定してください。これにより、組織のデータ保護とコンプライアンスを確保しながら、業務効率を向上させることができます。

    結論

    Office 365 APIのトークン認証エラー AADSTS500133 は、ビジネス環境において迅速かつ適切に対応する必要があります。上記の手順に従い、効率的なビジネスプロセスをサポートしながら、この課顾を解決してください。

  • テスト実行のトラブルシューティング: Pytest エラー「required field “lineno” missing from alias」を解決する

    テスト実行のトラブルシューティング: Pytest エラー「required field “lineno” missing from alias」を解決する

    概要

    Pythonのテストライブラリであるpytestを使用する際に、特定のエラーが発生することがある。この記事では、「required field “lineno” missing from alias」エラーの背景、原因、および解決方法について深堀りする。

    エラーの背景

    抽象構文木 (Abstract Syntax Tree)

    Pythonのコード解析には、通常、抽象構文木(AST)が使用される。ASTは、プログラムのソースコードを木構造で表現し、これにより構文解析や静的解析が行われる。このエラーはASTの構築時に関連して発生する。

    linenoとは?

    lineno は”line number”の略で、コード内の特定の行を参照する。ASTでは、特定の構造が存在する行番号を追跡するためにlineno情報が使われる。これはデバッグやエラー追跡に不可欠な情報である。

    エラーの原因

    このエラーは、PythonのパーサーまたはASTモジュールが、コードの行番号(lineno)に関する情報を必要としているが、何らかの理由で欠落している場合に発生する。

    主な原因は以下の通り:

    1. 依存ライブラリのバージョン不整合: 特定のライブラリが古いか、または互換性がない可能性がある。
    2. カスタムAST変換の問題: カスタムのAST変換コードが正しくないか、不完全な場合。
    3. Python自体のバグ: 非常にまれだが、Pythonインタープリタ自体の問題である可能性もある。

    解決策

    依存関係の更新

    依存するライブラリ、特にpytestのバージョンが古い場合、最新のバージョンに更新する。

    pip install --upgrade pytest

    プラグインの確認

    pytestのプラグインが原因の場合、プラグインを無効化または更新してみる。

    Pythonバージョンの確認

    使用しているPythonのバージョンが最新でない場合、更新を検討する。また、特定のバージョンでのみ発生するバグである可能性も考慮し、必要に応じてバージョンをダウングレードする。

    まとめ

    pytestで「required field “lineno” missing from alias」エラーに遭遇した場合、依存関係の更新、プラグインの確認、およびPythonバージョンの確認を行うことで解決する可能性がある。これにより、テストの実行がスムーズに行えるようになる。

  • M1 MacでDockerを使ってPowerShellを動作させる

    M1 MacでDockerを使ってPowerShellを動作させる

    M1 MacでDockerを使用してPowerShellを動作させる際には、いくつかの課題が生じる可能性があります。

    この記事では、DockerfileをM1 Macでビルドする際に遭遇する可能性がある問題とその解決策を説明します。

    問題の概要

    以下のDockerfileを使用してビルドを試みると、特定のエラーが発生する可能性があります。

    FROM --platform=linux/x86_64 mcr.microsoft.com/powershell:7.3-ubuntu-22.04
    RUN pwsh -Command Install-Module -Name Microsoft.Graph -Scope AllUsers -F

    エラーメッセージは以下の通りです。

    ---> [Warning] The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
    ---> Running in 28a633eea3df
    An error has occurred that was not properly handled. Additional information is shown below. The PowerShell process will exit.
    An error has occurred that was not properly handled. Additional information is shown below. The PowerShell process will exit.
    qemu: uncaught target signal 11 (Segmentation fault) - core dumped
    Segmentation fault
    The command '/bin/sh -c pwsh -Command Install-Module -Name Microsoft.Graph -Scope AllUsers -F' returned a non-zero code: 139

    このエラーは、使用しているイメージのプラットフォームがホストと一致しない場合に発生します。

    具体的には、このエラーはx86_64(AMD64)プラットフォーム向けのイメージをARM64プラットフォーム(M1 Macが使用しているプラットフォーム)で実行しようとしたときに発生します。

    さらに、Segmentation faultというエラーになることもあります。

    これは、プログラムが不正なメモリ領域にアクセスしようとしたときに発生します。

    このエラーは通常、プログラムのバグや、プログラムが不適切な方法でメモリを操作した結果として発生します。

    この場合、エラーはQEMU(異なるCPUアーキテクチャ間でのエミュレーションを提供するソフトウェア)から発生しており、x86_64プラットフォーム向けのコードをARM64プラットフォームで実行しようとしたことが原因と考えられます。

    解決策

    この問題を解決するためには、ARM64プラットフォーム向けのPowerShellを直接インストールする必要があります。

    https://github.com/GitSumito/CodeArsenal/blob/main/Powershell_On_Docker_For_M1Mac/Dockerfile

    上のリポジトリは build したDockerfile です。

    以下 Dockerfile の解説です。

    # Select the base image
    FROM arm64v8/ubuntu:latest

    この行では、ベースイメージとしてARM64プラットフォーム向けの最新のUbuntuイメージを選択しています。

    # Update the package lists for upgrades for security purposes
    RUN apt-get update && apt-get upgrade -y

    この行では、セキュリティのためにパッケージリストを更新し、アップグレードします。

    # Install necessary tools
    RUN apt-get install -y curl wget tar sudo
    # Install the ICU library
    RUN apt-get install -y libicu-dev

    この行では、ICU(International Components for Unicode)ライブラリをインストールしています。ICUは、Unicodeとグローバリゼーション(i18n)サポートを提供するC/C++およびJavaライブラリです。

    # Download the PowerShell binary
    RUN wget -O /tmp/powershell.tar.gz https://github.com/PowerShell/PowerShell/releases/download/v7.3.4/powershell-7.3.4-linux-arm64.tar.gz

    この行では、PowerShellのバイナリをダウンロードしています。wgetコマンドを使用して、指定したURLからPowerShellのtar.gzファイルをダウンロードし、/tmpディレクトリにpowershell.tar.gzという名前で保存します。

    # Create the target folder where PowerShell will be placed
    RUN mkdir -p /usr/local/microsoft/powershell/7.3.4

    この行では、PowerShellが配置されるターゲットフォルダを作成します。

    # Expand PowerShell to the target folder
    RUN tar zxf /tmp/powershell.tar.gz -C /usr/local/microsoft/powershell/7.3.4

    この行では、ダウンロードしたPowerShellのtar.gzファイルをターゲットフォルダに展開します。

    # Set execute permissions
    RUN chmod +x /usr/local/microsoft/powershell/7.3.4/pwsh

    この行では、PowerShell実行ファイルに実行権限を設定します。

    # Create a symbolic link that points to pwsh
    RUN ln -s /usr/local/microsoft/powershell/7.3.4/pwsh /usr/local/bin/pwsh

    この行では、pwshへのシンボリックリンクを作成します。これにより、どのディレクトリからでもpwshコマンドを使用してPowerShellを起動できるようになります。

    # Install necessary PowerShell modules
    RUN pwsh -Command Install-Module -Name ExchangePowerShell -F
    RUN pwsh -Command Install-Module -Name Microsoft.Graph -F

    最後に、必要なPowerShellモジュールをインストールします。この例では、ExchangePowerShellモジュールとMicrosoft.Graphモジュールをインストールします。

    Dockerfileのビルドが成功すると、以下のような出力が表示されます:

    ---> c6bd065a23d8
    Step 6/11 : RUN mkdir -p /usr/local/microsoft/powershell/7.3.4
    ---> Running in 77c3cfaf924e
    Removing intermediate container 77c3cfaf924e
    ---> 19263b7b1096
    Step 7/11 : RUN tar zxf /tmp/powershell.tar.gz -C /usr/local/microsoft/powershell/7.3.4
    ---> Running in 6f9b149944ae
    Removing intermediate container 6f9b149944ae
    ---> 86c0945e8a86
    Step 8/11 : RUN chmod +x /usr/local/microsoft/powershell/7.3.4/pwsh
    ---> Running in bed70b0443d1
    Removing intermediate container bed70b0443d1
    ---> b767beaee0b3
    Step 9/11 : RUN ln -s /usr/local/microsoft/powershell/7.3.4/pwsh /usr/local/bin/pwsh
    ---> Running in 26c1c7b4b1cf
    Removing intermediate container 26c1c7b4b1cf
    ---> ff72047deef1
    Step 10/11 : RUN pwsh -Command Install-Module -Name ExchangePowerShell -F
    ---> Running in e024612672c1
    Removing intermediate container e024612672c1
    ---> 08eb8f79bdd4
    Step 11/11 : RUN pwsh -Command Install-Module -Name Microsoft.Graph -F
    ---> Running in 2988a85004a6
    Removing intermediate container 2988a85004a6
    ---> 0205240f3f6f
    Successfully built 0205240f3f6f
    Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

    このDockerfileを使用することで、M1 MacでもDocker上でPowerShellを動作させることが可能になります。

  • SQLの再帰と組織階層の探索:WITH RECURSIVEとUNION ALLを理解する

    SQLの再帰と組織階層の探索:WITH RECURSIVEとUNION ALLを理解する

    組織のデータを扱う際、階層構造を理解し、それをデータベースで表現することがよくあります。

    しかし、階層的なデータの探索は一見複雑に見えることがあります。今日は、その一つの解決策としてSQLの再帰クエリを使った方法を解説します。

     

    create or replace TABLE Organization (
    Org_Code VARCHAR(11) NOT NULL,
    Org_Name VARCHAR(400),
    Parent_Org_Code VARCHAR(11),
    Layer_Number NUMBER(2),
    constraint Org_PK primary key (Org_Code)
    );

    データは以下の通り入っているものとします。

    Org_Code Org_Name Parent_Org_Code Layer_Number
    ORG-001 Org A ORG-001 1
    ORG-002 Org B ORG-001 2
    ORG-003 Org C ORG-001 2
    ORG-004 Org D ORG-002 3
    ORG-005 Org E ORG-002 3
    ORG-006 Org F ORG-003 3
    ORG-007 Org G ORG-004 4
    ORG-008 Org H ORG-004 4
    ORG-009 Org I ORG-005 4
    ORG-010 Org J ORG-006 4

    このテーブルは、組織コード Org_Code、組織の名称 Org_Name、親組織のコード Parent_Org_Code、およびその組織が階層の何層目に位置するか Layer_Number を格納しています。

    組織の階層を表示したい場合、親組織から子組織までのパスを表現するために、SQLの再帰クエリを使用します。

    再帰クエリは、自身を呼び出すクエリであり、これにより階層データのような複雑な構造を効果的に解析できます。

    以下のクエリでは、WITH RECURSIVE 句を使用して、階層構造を再帰的に探索します:

    WITH RECURSIVE org_path AS (
    SELECT
    Org_Code,
    Org_Name,
    Parent_Org_Code,
    CAST(Org_Code AS VARCHAR(500)) AS Org_Path,
    Layer_Number,
    Org_Name AS Layer_Name,
    Layer_Number AS Layer_Num
    FROM
    Organization
    WHERE
    Org_Code = Parent_Org_Code
    UNION ALL
    SELECT
    o.Org_Code,
    o.Org_Name,
    o.Parent_Org_Code,
    CONCAT(r.Org_Path, ' -> ', o.Org_Code),
    o.Layer_Number,
    CONCAT(r.Layer_Name, ' -> ', o.Org_Name),
    o.Layer_Number
    FROM
    Organization o
    JOIN
    org_path r
    ON
    o.Parent_Org_Code = r.Org_Code
    WHERE
    o.Org_Code <> o.Parent_Org_Code
    )
    SELECT * FROM org_path;

    この再帰クエリは2部構成で、最初の部分(ベースケース)では自身が親組織である組織(つまり最上位の組織)を選択しています。ここでは、Org_CodeとParent_Org_Codeが同じ行を選択します。これが再帰の開始点となります。

    次にUNION ALLでベースケースと再帰ケースを結合します。再帰ケースでは、再帰CTE org_path を再帰的に結合して、親組織から次の子組織へと移動します。この部分ではParent_Org_Code が先ほど選択した行のOrg_Code と一致する行を選択します。

    この操作を行うことで、親組織から子組織、孫組織、さらにその下の組織へとパスをたどることができます。

    そして最終的にSELECT * FROM org_pathで再帰CTEからすべての行を選択します。

    これにより、親組織から各組織へのパスを含む結果セットが得られます。

    Org_Code Org_Name Parent_Org_Code Org_Path Layer_Number Layer_Name Layer_Num
    ORG-001 Org A ORG-001 ORG-001 1 (1) Org A 1
    ORG-002 Org B ORG-001 ORG-001 -> ORG-002 2 (1) Org A -> (2) Org B 2
    ORG-003 Org C ORG-001 ORG-001 -> ORG-003 2 (1) Org A -> (2) Org C 2
    ORG-004 Org D ORG-002 ORG-001 -> ORG-002 -> ORG-004 3 (1) Org A -> (2) Org B -> (3) Org D 3
    ORG-005 Org E ORG-002 ORG-001 -> ORG-002 -> ORG-005 3 (1) Org A -> (2) Org B -> (3) Org E 3
    ORG-006 Org F ORG-003 ORG-001 -> ORG-003 -> ORG-006 3 (1) Org A -> (2) Org C -> (3) Org F 3
    ORG-007 Org G ORG-004 ORG-001 -> ORG-002 -> ORG-004 -> ORG-007 4 (1) Org A -> (2) Org B -> (3) Org D -> (4) Org G 4
    ORG-008 Org H ORG-004 ORG-001 -> ORG-002 -> ORG-004 -> ORG-008 4 (1) Org A -> (2) Org B -> (3) Org D -> (4) Org H 4
    ORG-009 Org I ORG-005 ORG-001 -> ORG-002 -> ORG-005 -> ORG-009 4 (1) Org A -> (2) Org B -> (3) Org E -> (4) Org I 4
    ORG-010 Org J ORG-006 ORG-001 -> ORG-003 -> ORG-006 -> ORG-010 4 (1) Org A -> (2) Org C -> (3) Org F -> (4) Org J 4

    以上がSQLの再帰クエリを使った組織の階層構造探索の基本的な考え方です。

     

    振る舞いについてもう少し踏み込む

    基本ケース

    基本ケースは、再帰が始まる地点を定義します。このクエリでは、基本ケースは自己参照(Org_Code = Parent_Org_Code)の組織を見つけます。これは通常、組織階層のルートを示します。

    SELECT
    Org_Code,
    Org_Name,
    Parent_Org_Code,
    CAST(Org_Code AS VARCHAR(500)) AS Org_Path,
    Layer_Number,
    Org_Name AS Layer_Name,
    Layer_Number AS Layer_Num
    FROM
    Organization
    WHERE
    Org_Code = Parent_Org_Code

    この部分のクエリを実行すると、以下の行が得られます。

    Org_Code Org_Name Parent_Org_Code Org_Path Layer_Number Layer_Name Layer_Num
    ORG-001 Org A ORG-001 ORG-001 1 Org A 1

    再帰ステップ

    再帰ステップは、基本ケースから始まり、その結果を用いて組織の子ノードを見つけます。このステップは、自己参照でないすべての組織(つまり、Org_Code <> Parent_Org_Code)を見つけます。

    SELECT
    o.Org_Code,
    o.Org_Name,
    o.Parent_Org_Code,
    CONCAT(r.Org_Path, ' -> ', o.Org_Code),
    o.Layer_Number,
    CONCAT(r.Layer_Name, ' -> ', o.Org_Name),
    o.Layer_Number
    FROM
    Organization o
    JOIN
    org_path r
    ON
    o.Parent_Org_Code = r.Org_Code
    WHERE
    o.Org_Code <> o.Parent_Org_Code

    この部分のクエリは、基本ケースから得られた結果(つまり、ORG-001)を用いて、その子ノード(ORG-002とORG-003)を見つけます。この結果は以下のようになります。

    Org_Code Org_Name Parent_Org_Code Org_Path Layer_Number Layer_Name Layer_Num
    ORG-002 Org B ORG-001 ORG-001 -> ORG-002 2 Org A -> Org B 2
    ORG-003 Org C ORG-001 ORG-001 -> ORG-003 2 Org A -> Org C 2

    このステップは、すべての組織が見つかるまで再帰的に繰り返されます。つまり、ORG-002とORG-003の子ノード(ORG-004, ORG-005, ORG-006)、その子ノード(ORG-007, ORG-008, ORG-009, ORG-010)を見つけます。

    最終的な結果は、基本ケースとすべての再帰ステップの結果を連結したものになります。これは、UNION ALLによって行われます。UNION ALLは、2つのSELECT文の結果を一つのテーブルに結合します。

    このクエリの結果は、組織の階層を表現するパスを生成します。各行は、特定の組織へのパス(Org_Path)、その組織の名前(Layer_Name)、およびその組織が階層の何層目に位置しているか(Layer_Num)を示します。

    以上が、このSQLクエリがどのように動作するかの詳細な説明です。各ステップで何が起こるかを理解することで、このクエリがどのように組織の階層を探索し、パスを生成するかを理解することができるはずです。

    このテクニックは、階層的なデータを持つ任意のシナリオに適用でき、特に組織構造やファイルシステム、ソーシャルネットワークの関係などで有用です。