SEあれこれ

インフラもコードもデータも。SEが仕事で出会う技術のあれこれを書き留めています。

Strands Agentsで作るモデル非依存のAIエージェント

この記事で分かること
  • Strands AgentsがBedrock Agents・Pydantic AIと何を分担する立ち位置か
  • @toolデコレータによるツール定義とエージェントループの仕組み
  • MCPサーバーをツールとして取り込む方法とマルチエージェント構成の考え方
記事の検証情報
確認日
2026年9月1日
検証環境
Python 3.12 / strands-agents 0.x系 / Amazon Bedrock(Claude)・MCPサーバー連携での動作確認
対象読者
Bedrock AgentsのAction Group定義の窮屈さを感じており、Pythonコードでより柔軟にエージェントの挙動を組み立てたいSE

Strands Agentsとは何か

 (1) 以前Bedrock Agentsの回では、AWSコンソール上でAction Groupを定義するマネージドなエージェント構築を扱いました。Strands Agentsは、AWSがオープンソースとして公開しているPythonのエージェント構築SDKで、コードベースでエージェントのロジックをより直接的にコントロールしたい場合の選択肢です。

 (2) 名前の通りBedrockとの親和性は高いものの、モデルプロバイダを特定のものに固定しない「モデル非依存」の設計になっており、Bedrock経由のClaudeだけでなく、他プロバイダのモデルに切り替えて動かすことも想定されています。Pydantic AIの回で扱った型安全なエージェント構築と近い系譜のフレームワークですが、Strands AgentsはAWSのエコシステム(Bedrock Guardrails、MCP等)との連携により重点を置いています。

観点Bedrock AgentsPydantic AIStrands Agents
実行環境AWSマネージド自前ホスティング自前ホスティング
ツール定義Action Group(OpenAPIスキーマ)@agent.toolデコレータ@toolデコレータ
出力の型付けスキーマ定義に依存pydanticモデルで厳密指定柔軟(構造化出力も可)
MCP連携限定的個別実装が必要標準サポート

最小構成のエージェント

from strands import Agent, tool

@tool
def get_stock_level(product_code: str) -> dict:
    """指定した商品コードの在庫数を取得する"""
    stock_data = {"A-1001": 42, "A-1002": 0}
    return {"product_code": product_code, "stock": stock_data.get(product_code, -1)}

agent = Agent(
    model="anthropic.claude-sonnet-4-5",
    tools=[get_stock_level],
    system_prompt="あなたは在庫管理を支援するアシスタントです",
)

response = agent("A-1001の在庫状況を教えてください")
print(response)

 (3) @toolデコレータの書き味はMCPサーバー自作の回で扱ったFastMCPの@mcp.tool()とよく似ており、型ヒントとdocstringからツールのスキーマが自動生成されます。エージェントは、ユーザーからの問いかけに応じて必要なツールを自律的に選択・実行し、その結果を踏まえて最終応答を組み立てます。

補足

Strands Agentsのエージェントループは「モデルへの問い合わせ→ツール呼び出しが必要か判断→必要なら実行して結果を返す→再度モデルへ」というシンプルな反復構造です。LangGraphのような複雑なグラフ構造を明示的に組む必要がなく、素直なツール呼び出し中心のエージェントであれば少ないコード量で実現できます。

MCPサーバーのツールとしての取り込み

 (4) Strands Agentsは、外部のMCPサーバーが提供するツールを、あたかも自前のツールであるかのようにエージェントへ取り込む機能を標準で持っています。MCPサーバー自作の回で構築したサーバーを、そのまま接続できます。

from strands.tools.mcp import MCPClient
from mcp.client.stdio import stdio_client, StdioServerParameters

mcp_client = MCPClient(
    lambda: stdio_client(
        StdioServerParameters(command="uv", args=["run", "--directory", "/path/to/inventory-server", "python", "server.py"])
    )
)

with mcp_client:
    tools = mcp_client.list_tools_sync()
    agent = Agent(model="anthropic.claude-sonnet-4-5", tools=tools)
    response = agent("在庫が少ない商品を教えて")

 (5) 自作ツールと外部MCPサーバー由来のツールを、同じエージェントの中に混在させて使えます。社内システム連携用のMCPサーバーを一度作れば、Strands AgentsだけでなくClaude Desktopなど他のMCPクライアントからも使い回せる、という前回の記事で扱った利点がそのまま活きてきます。

構造化出力

 (6) 最終応答をpydanticモデルとして受け取りたい場合、Pydantic AIの回と同様に出力の型を指定できます。

from pydantic import BaseModel

class IncidentSummary(BaseModel):
    severity: str
    summary: str

agent = Agent(model="anthropic.claude-sonnet-4-5", tools=[get_stock_level])
result = agent.structured_output(IncidentSummary, "障害内容を要約してください: ...")
print(result.severity)
注意

構造化出力とツール呼び出しを同一エージェントで多用すると、モデルが「ツールを呼ぶべきか、構造化出力に整形すべきか」の判断で迷い、意図しない挙動になることがあります。役割ごとにエージェントインスタンスを分ける設計の方が、挙動の予測がしやすくなります。

マルチエージェント構成

 (7) Strands Agentsは、1つのエージェントを別のエージェントの「ツール」として登録する、Agents as Toolsというパターンをサポートしています。専門特化した複数のエージェントを組み合わせる構成です。

@tool
def inventory_specialist(query: str) -> str:
    """在庫に関する専門的な質問に回答する"""
    sub_agent = Agent(model="anthropic.claude-sonnet-4-5", tools=[get_stock_level])
    return str(sub_agent(query))

orchestrator = Agent(
    model="anthropic.claude-sonnet-4-5",
    tools=[inventory_specialist],
    system_prompt="必要に応じて専門エージェントに問い合わせて回答してください",
)

 (8) この構成は、Bedrock Agentsのマルチエージェントコラボレーション機能に近い発想ですが、コードとして直接オーケストレーションロジックを書けるため、条件分岐やリトライの制御が自由に組み込めます。

Bedrockへのデプロイ

 (9) 開発したエージェントを本番運用する場合、Lambda関数としてパッケージ化し、Lambda Powertoolsの回で扱ったような構造化ログ・トレーシングを組み込みつつ、API Gateway経由で公開する構成が一般的です。ECS/Fargateで常駐プロセスとして動かす選択肢もあり、レイテンシ要件や実行時間の長さに応じて使い分けます。

確認結果

自作MCPサーバーと組み合わせたエージェントを検証したところ、「在庫が少ない商品を教えて」という自然文の指示に対し、MCPサーバー側のツールが自動的に呼び出され、在庫データを踏まえた回答が生成されることを確認しました。自作ツールと変わらない体験でMCPツールを扱えています。

重要な注意

マルチエージェント構成では、サブエージェントの応答をそのまま上位エージェントが信頼して処理を進めるため、サブエージェント側の出力にも入力検証やGuardrails(Bedrock Guardrailsの回参照)を適用しておくことが重要です。信頼できない外部データを扱うツールでは特に注意してください。

まとめ

 (10) Strands Agentsは、Bedrock Agentsのマネージドな手軽さとPydantic AIのようなコードベースの柔軟性の中間に位置し、特にMCPサーバーとの連携を前提にした設計が特徴です。Bedrock AgentsMCPサーバー自作Pydantic AIで扱ってきたエージェント構築の知見を組み合わせながら、用途に応じて使い分けていくのがおすすめです。

参考資料

対応モデルプロバイダやAPIは活発に更新されているため、実装前に公式ドキュメントで最新のバージョン情報を確認してください。

Marimo応用編:SQLセル・キャッシュ・WASM書き出しで分析ノートブックを一段強化する

この記事で分かること
  • Marimo内蔵のSQLセルでDuckDBを直接呼び出す方法
  • mo.cache/mo.stateでリアクティブモデルの弱点を補う考え方
  • marimo exportによるWASM書き出しでサーバーレス共有を実現する流れ
記事の検証情報
確認日
2026年9月1日
検証環境
Python 3.12 / marimo 0.x系 / DuckDB連携 / marimo export(WASM)でのブラウザ動作確認
対象読者
前回のMarimoリアクティブノートブック入門で基本操作は把握済みで、実務のデータ分析ノートブックをより実践的に組み立てたいSE

前回の振り返りと今回の位置づけ

 (1) 前回Marimoリアクティブノートブック入門の回では、セル間の依存関係を自動解決するリアクティブな実行モデルと、UI部品による簡易ダッシュボード化を扱いました。今回はその応用として、実務でよく直面する「SQLで集計したい」「毎回同じ重い処理を再計算したくない」「Pythonが動く環境がない相手にも共有したい」という3つの課題への対処法を扱います。

内蔵SQLセルの活用

 (2) Marimoは、Pythonセルとは別に「SQLセル」を持っており、DuckDBの回で扱ったDuckDBエンジンを裏側で使ってSQLを直接実行できます。Python変数として存在するDataFrameを、FROM句にそのまま書ける点が特徴です。

@app.cell
def _():
    import polars as pl
    orders = pl.read_csv("orders_2026.csv")
    return (orders,)

@app.cell
def _(mo, orders):
    result = mo.sql(
        f"""
        SELECT category, COUNT(*) AS cnt, SUM(amount) AS total
        FROM orders
        GROUP BY category
        ORDER BY total DESC
        """
    )
    return (result,)

 (3) mo.sqlで実行した結果は、自動的にPolarsのDataFrameとして次のセルに渡されます。SQLの結果に対してさらにPython側でグラフ描画を行う、といった組み合わせが1つのノートブック内で自然に書けます。

補足

SQLセルもリアクティブモデルの対象です。ordersを作るセルの内容が変われば、それに依存するSQLセルも自動的に再実行されます。前回扱った依存関係の自動解決は、SQLセルとPythonセルの間でも同様に機能します。

重い処理のキャッシュ:mo.cache

 (4) リアクティブな実行モデルは便利な一方、依存する変数が変わるたびに重い処理(大規模データの読み込み、モデルの推論等)まで再実行されてしまうと、逆に開発体験を損ないます。@mo.cacheデコレータで、引数が変わらない限り結果を再利用できます。

import marimo as mo

@mo.cache
def load_heavy_dataset(path: str):
    import polars as pl
    return pl.read_parquet(path)

@app.cell
def _():
    df = load_heavy_dataset("large_dataset.parquet")
    return (df,)

 (5) セルをまたいで再実行が走っても、path引数が同じであればキャッシュされた結果がそのまま返されます。Ray入門の回で扱った分散処理と組み合わせる場合も、重い前処理部分だけをキャッシュ化しておくと、UI操作時のレスポンスが大きく改善します。

注意

キャッシュはメモリ上に保持されるため、大量の異なる引数でキャッシュを積み重ねるとメモリを圧迫します。実行時間の長い処理に絞って適用し、軽量な処理にまでキャッシュを付けるのは避けるのが無難です。

非リアクティブな状態管理:mo.state

 (6) カウンターやトグルスイッチのように、「別のセルの値には依存せず、UI操作によって独立に変化する状態」を扱いたい場合、通常の変数の再代入ではリアクティブモデルとかみ合わないことがあります。こうした場面ではmo.stateを使います。

get_count, set_count = mo.state(0)

@app.cell
def _(mo):
    button = mo.ui.button(label="カウントアップ", on_click=lambda _: set_count(get_count() + 1))
    button
    return (button,)

@app.cell
def _(get_count):
    mo.md(f"現在のカウント: {get_count()}")
    return

 (7) mo.stateで作った getter/setterは、ReactのuseStateフックに近い役割です。ボタンクリックのような「ある時点のイベント」をトリガーに状態を更新したい場合に使う、と覚えておくと使い分けがしやすくなります。

条件付き中断:mo.stop

 (8) 前のセルの入力が未確定の段階で後続セルが実行されエラーになるのを防ぎたい場合、mo.stopで早期リターン的にセルの実行を止められます。

@app.cell
def _(mo, file_upload):
    mo.stop(file_upload.value is None, mo.md("ファイルをアップロードしてください"))
    df = pl.read_csv(file_upload.value)
    return (df,)
確認結果

ファイルアップロードのUI部品と組み合わせて検証したところ、アップロード前は案内メッセージのみが表示され、アップロード後に後続のCSV読み込み処理が自動的に走ることを確認しました。エラーメッセージが一瞬表示される、といった前回の入門編にはなかった作り込みが可能です。

WASM書き出しによるサーバーレス共有

 (9) marimo export html-wasmコマンドを使うと、ノートブックをPyodide(WebAssembly版Python)ベースの静的HTMLファイルとして書き出せます。サーバーを立てずに、ブラウザだけでノートブックを開いて操作してもらえるようになります。

uv run marimo export html-wasm analysis.py -o output/index.html --mode run

 (10) 書き出したファイルは、S3設計パターンの回で扱った静的サイトホスティング構成(S3+CloudFront)にそのままアップロードするだけで公開できます。閲覧者側にPython環境が不要な点は、社内の非エンジニアに分析結果を触ってもらう際に効果を発揮します。

重要な注意

WASM版はブラウザ内で完結して実行されるため、対応していないライブラリ(C拡張に強く依存するもの等)があります。書き出し前に、使用しているライブラリがPyodide対応かどうかを必ず確認してください。また、機密データを含むノートブックをWASM化して公開する場合、データそのものも静的ファイルに含まれてしまう点に注意が必要です。

まとめ

 (11) 今回はSQLセル・キャッシュ・状態管理・WASM書き出しという4つの応用機能を扱いました。前回のMarimo入門で押さえた基本のリアクティブモデルに、これらを組み合わせることで、単なる分析ノートブックから一歩進んだ「共有可能な分析ツール」へと発展させられます。

参考資料

WASM対応状況や新機能は活発に更新されているため、導入前に公式ドキュメントの最新版を確認してください。

AWS Firewall Managerで組織全体のWAF/セキュリティグループを一括統制する

この記事で分かること
  • Firewall ManagerがWAF単体運用・Organizations SCPと何を分担するのか
  • ポリシータイプ(WAF/セキュリティグループ/Shield Advanced)の使い分け
  • 新規アカウント・新規リソースへのポリシー自動適用の仕組み
記事の検証情報
確認日
2026年9月1日
検証環境
AWS CLI v2 / Firewall Manager(AWS Organizations管理アカウント配下) / 対象: AWS WAF、VPCセキュリティグループ
対象読者
複数アカウント・複数チームでAWSを運用しており、WAFルールやセキュリティグループの設定が各アカウントでバラバラになってしまっているSE

Firewall Managerが解決する課題

 (1) 以前AWS WAFの回では、単一のWebアプリケーションに対するWAFルールの設定を扱いました。組織にアカウントが増えてくると、各チームがそれぞれ個別にWAFルールを設定するようになり、あるアカウントではSQLインジェクション対策が入っているのに、別のアカウントでは入っていない、といったばらつきが生まれがちです。

 (2) Firewall Managerは、Organizationsマルチアカウント管理の回で扱った組織構造を前提に、WAFルール・セキュリティグループ・Shield Advancedの保護設定などを、組織全体または特定のOU(組織単位)に対して一括で強制適用できるサービスです。

観点各アカウントで個別にWAF設定Firewall Manager
設定の一貫性アカウントごとにばらつきやすいポリシーとして一元定義、全アカウントに強制適用
新規アカウントへの適用手動で都度設定が必要ポリシーのスコープに入れば自動適用
準拠状況の把握アカウントごとに個別確認コンプライアンスダッシュボードで一覧可視化
対象範囲WAFのみWAF/セキュリティグループ/Shield Advanced/Network Firewall等
補足

Firewall Managerを使うには、AWS Organizationsが有効化されており、Firewall Manager管理者アカウントとして委任されたアカウントが必要です。単一アカウント運用では出番がなく、複数アカウント運用になって初めて価値が出るサービスです。

ポリシータイプの種類

 (3) Firewall Managerで作成できるポリシーには複数の種類があり、代表的なものは以下の通りです。

ポリシータイプ対象
WAFALB/CloudFront/API GatewayへのWAF Web ACL適用
Security GroupVPCセキュリティグループのルール統制、未使用SGの検出
Shield AdvancedDDoS対策の自動有効化
Network FirewallVPCレベルのネットワークトラフィック検査
DNS Firewall悪意あるドメインへの名前解決のブロック

WAFポリシーの作成

 (4) 組織共通で適用したいWAFルールをまとめたポリシーを作成し、適用範囲(OUまたはアカウントのリスト)を指定します。

aws fms put-policy \
  --policy '{
    "PolicyName": "org-baseline-waf-policy",
    "SecurityServicePolicyData": {
      "Type": "WAFV2",
      "ManagedServiceData": "{\"type\":\"WAFV2\",\"preProcessRuleGroups\":[{\"managedRuleGroupIdentifier\":{\"vendorName\":\"AWS\",\"managedRuleGroupName\":\"AWSManagedRulesCommonRuleSet\"},\"overrideAction\":{\"type\":\"NONE\"}}]}"
    },
    "ResourceType": "AWS::ElasticLoadBalancingV2::LoadBalancer",
    "IncludeMap": {"ORG_UNIT": ["ou-abcd-11111111"]},
    "RemediationEnabled": true
  }'

 (5) RemediationEnabledをtrueにすると、対象OU配下のALBに対して、まだWAFが適用されていないリソースを自動的に検出し、このポリシーで定義したWeb ACLを自動アタッチします。手動での適用漏れを防げる点が大きなメリットです。

注意

自動適用が有効な状態でポリシー内容を変更すると、対象範囲の全リソースに変更が波及します。本番影響のあるルール変更は、まず一部アカウントを対象にしたテストポリシーで動作確認してから、組織全体へ展開する順序を踏んでください。

セキュリティグループポリシー

 (6) セキュリティグループ向けのポリシーでは、「全ポートを0.0.0.0/0に開放しているセキュリティグループを検出する」「特定の共通セキュリティグループを全インスタンスに強制アタッチする」といった統制が可能です。VPC設計パターンの回で扱った最小権限のネットワーク設計を、組織全体でチェックする仕組みとして機能します。

aws fms put-policy \
  --policy '{
    "PolicyName": "org-common-security-group",
    "SecurityServicePolicyData": {"Type": "SECURITY_GROUPS_COMMON"},
    "ResourceType": "AWS::EC2::Instance",
    "IncludeMap": {"ORG_UNIT": ["ou-abcd-11111111"]},
    "RemediationEnabled": false
  }'
補足

セキュリティグループポリシーは、まずRemediationEnabled: falseで違反状況を可視化するところから始めるのが安全です。いきなり自動是正を有効にすると、既存の正当な通信経路を意図せず塞いでしまうリスクがあります。

コンプライアンス状況の確認

aws fms get-compliance-detail \
  --policy-id "12345678-abcd-1234-abcd-1234567890ab" \
  --member-account "210987654321"

 (7) このAPIで、特定アカウントがポリシーに準拠しているか、準拠していないリソースは何かを確認できます。CloudWatch監視設計パターンの回で扱ったダッシュボードと同様に、定期的にこの結果をSNS通知やSlack通知(Slack自動化の回参照)に流すことで、逸脱の早期発見につなげられます。

確認結果

検証用のOU配下に新規アカウントを追加したところ、そのアカウント内でALBを作成した時点で、追加設定なしにベースラインWAFポリシーが自動適用されることを確認しました。新規アカウント作成時のセキュリティ設定漏れを防ぐ運用が実現できています。

SCPとの役割の違い

 (8) Organizationsマルチアカウント管理の回で扱ったSCP(Service Control Policy)は「特定の操作を禁止する」IAMレベルの統制です。一方Firewall Managerは「特定のセキュリティ設定を積極的に適用する」統制であり、禁止ではなく強制・是正という点で役割が異なります。両者を組み合わせることで、「危険な操作を禁止しつつ、安全な設定を自動的に敷く」という二重の防御ラインを構築できます。

重要な注意

Firewall Manager管理者権限を持つアカウントは、組織内の全アカウントのセキュリティ設定に影響を及ぼせる強力な権限を持ちます。この管理者権限自体へのアクセスを、IAMロールで厳格に絞り込むことを忘れないでください。

まとめ

 (9) Firewall Managerは、複数アカウント運用でどうしても発生しがちな「セキュリティ設定のばらつき」を、組織単位のポリシーとして一元管理・自動是正できるサービスです。AWS WAFOrganizationsマルチアカウント管理で積み上げてきた個別の知見を、組織全体のガバナンスへとつなげる位置づけと言えます。

参考資料

対応ポリシータイプや設定項目は更新が続いているため、導入前に公式ドキュメントの最新情報を確認してください。

Amazon Verified Permissionsでアプリの認可ロジックをコードから切り離す

この記事で分かること
  • Verified Permissionsが解決する「if文だらけの認可ロジック」問題
  • Cedarポリシー言語の基本構文とPolicy Storeの構成
  • アプリケーションからのisAuthorized呼び出しとGuardrails的な位置づけ
記事の検証情報
確認日
2026年9月1日
検証環境
AWS CLI v2 / boto3 1.34系 / Verified Permissions Policy Store / Cognitoユーザープール連携
対象読者
アプリのコード内に「if role == 'admin' or (role == 'editor' and owner_id == user_id)」のような認可条件が散らばり、仕様変更のたびにコード修正が必要になっているSE

認可ロジックをコードに埋め込む問題

 (1) 以前IAMロール設計ベストプラクティスCognitoカスタム認証の回では、「誰であるか」を確認する認証(Authentication)を扱いました。認可(Authorization)、つまり「その人が何をしてよいか」の判定ロジックは、アプリケーションのコード内にif文として直接書かれることが多く、権限ルールが複雑になるほどコードの見通しが悪くなっていきます。

 (2) Verified Permissionsは、この認可ロジックをアプリケーションコードの外に切り出し、Cedarというポリシー言語で宣言的に記述できるようにするマネージドサービスです。ポリシーの変更が、アプリのデプロイなしに反映できる点が大きな利点です。

補足

「認可ロジックの外部化」という発想自体は、OPA(Open Policy Agent)のような既存のOSSでも実現できます。Verified Permissionsは、AWSのマネージドサービスとしてCognitoやIAMと連携しやすい形でこれを提供している、という位置づけです。

Cedarポリシー言語の基本

 (3) Cedarは「誰が(Principal)」「何を(Action)」「何に対して(Resource)」行えるかを、シンプルな構文で記述します。

permit (
  principal,
  action == Action::"viewDocument",
  resource
) when {
  resource.owner == principal || principal.role == "admin"
};

 (4) このポリシーは「文書の所有者本人、または管理者ロールを持つユーザーは、文書を閲覧できる」ことを表しています。SQLAlchemyの回で扱ったORMのクエリ条件のような感覚に近いですが、Cedarはアプリのコードとは独立したファイルとして管理されます。

ロールベースとリソースベースの組み合わせ

permit (
  principal in Role::"editor",
  action == Action::"editDocument",
  resource
) when {
  resource.department == principal.department
};

forbid (
  principal,
  action == Action::"deleteDocument",
  resource
) unless {
  principal.role == "admin"
};

 (5) permitで許可、forbidで明示的に拒否を書けます。Cedarはデフォルト拒否(何も許可されなければアクセス不可)を前提としており、forbidルールはpermitルールより優先されます。この明示性により、「うっかり許可しすぎる」事故を防ぎやすい設計になっています。

注意

デフォルト拒否の仕組み上、新しいアクションを追加した際に対応するpermitポリシーを書き忘れると、そのアクションは常に拒否されます。新機能追加時はポリシーストアの更新をデプロイ手順に組み込んでおく必要があります。

Policy Storeの作成

aws verifiedpermissions create-policy-store \
  --validation-settings mode=STRICT \
  --description "document-management-app-policies"

aws verifiedpermissions create-policy \
  --policy-store-id "PS-abc123" \
  --definition file://view-document-policy.json

 (6) validation-settings mode=STRICTを指定すると、後述するスキーマに沿わないポリシーの登録がエラーとして弾かれます。誤字や存在しない属性名によるポリシーの取りこぼしを防げるため、本番運用では有効化しておくのが無難です。

スキーマ定義

 (7) Cedarのスキーマは、Principal(ユーザー)・Resource(文書等)・Actionの型と属性を事前に定義します。TypeScriptの型定義に近い役割です。

{
  "DocumentApp": {
    "entityTypes": {
      "User": {"shape": {"type": "Record", "attributes": {"role": {"type": "String"}, "department": {"type": "String"}}}},
      "Document": {"shape": {"type": "Record", "attributes": {"owner": {"type": "String"}, "department": {"type": "String"}}}}
    },
    "actions": {
      "viewDocument": {"appliesTo": {"principalTypes": ["User"], "resourceTypes": ["Document"]}}
    }
  }
}

アプリケーションからの呼び出し

 (8) 認可判定が必要な処理の直前で、isAuthorized APIを呼び出します。FastAPIの回で扱ったミドルウェアやDependsの仕組みに組み込むイメージです。

import boto3

client = boto3.client("verifiedpermissions")

def check_permission(user_id: str, role: str, action: str, document_id: str, owner_id: str) -> bool:
    response = client.is_authorized(
        policyStoreId="PS-abc123",
        principal={"entityType": "DocumentApp::User", "entityId": user_id},
        action={"actionType": "DocumentApp::Action", "actionId": action},
        resource={"entityType": "DocumentApp::Document", "entityId": document_id},
        context={"contextMap": {}},
        entities={"entityList": [
            {"identifier": {"entityType": "DocumentApp::User", "entityId": user_id},
             "attributes": {"role": {"string": role}}},
            {"identifier": {"entityType": "DocumentApp::Document", "entityId": document_id},
             "attributes": {"owner": {"string": owner_id}}},
        ]},
    )
    return response["decision"] == "ALLOW"
確認結果

ownerが一致しないユーザーからのviewDocument呼び出しで検証したところ、ポリシー通りにDENYが返り、アプリ側の追加のif文なしに正しくアクセス拒否されることを確認しました。ポリシーの変更のみでアクセス条件を調整できる点は、コード修正・再デプロイのサイクルに比べて明らかに反応が速くなります。

Cognitoとの連携

 (9) Cognitoカスタム認証の回で構築したCognitoユーザープールと連携させると、CognitoのIDトークンに含まれるグループ情報をPrincipalの属性として自動的に取り込めます。認証(Cognito)と認可(Verified Permissions)を分離しつつ、シームレスに連携させる構成です。

他の認可アプローチとの比較

方式特徴
コード内if文手軽だが複雑化・散在しやすい
IAMポリシーAWSリソースへのアクセス制御に強いが、アプリ独自の業務ロジックには不向き
Verified Permissions(Cedar)アプリ独自の認可ルールを宣言的に外部管理、動的更新が容易
重要な注意

認可判定をVerified Permissions呼び出しに任せきりにすると、そのAPI呼び出し自体が失敗した場合の挙動(フェイルオープンかフェイルクローズか)がセキュリティ上重要になります。API呼び出しがエラーになった場合は必ずアクセス拒否側に倒す実装にしてください。

まとめ

 (10) Verified Permissionsは、アプリケーションのコードに散らばりがちな認可ロジックを、Cedarという宣言的な言語でひとつの場所に集約できるサービスです。Cognitoカスタム認証IAMロール設計で整えてきた認証・権限管理の延長として、業務固有の複雑な認可ルールを持つアプリで導入を検討する価値があります。

参考資料

Cedar言語の構文や対応機能は継続的にアップデートされているため、導入前に公式ドキュメントの最新版を確認してください。

リアクティブノートブックMarimoでJupyterの実行順序問題から解放される

この記事で分かること
  • MarimoがJupyter Notebookと何が違うのか、リアクティブとはどういうことか
  • セル間の依存関係の自動解決とUI要素の組み込み方
  • .pyファイルとして保存されることによるGit管理・実行のしやすさ
記事の検証情報
確認日
2026年9月1日
検証環境
Python 3.12 / marimo 0.x系 / uvパッケージマネージャでのインストール確認
対象読者
Jupyter Notebookで「セルを実行し忘れて古い変数のまま集計してしまった」経験があり、ノートブックのGit差分の扱いにくさにも悩んでいるSE

Jupyter Notebookが抱える構造的な問題

 (1) Jupyter Notebookは対話的なデータ分析の定番ツールですが、セルの実行順序が自由なために「上から順に実行しないと結果が再現できない」「あるセルの変数を書き換えたのに、それを参照する下のセルを再実行し忘れて古い結果のまま気づかない」といった事故が起きやすい構造を持っています。Polars入門Pythonデータ分析系の回で紹介してきたライブラリも、実際の検証作業ではこの問題と隣り合わせでした。

 (2) Marimoは、この実行順序の問題を「リアクティブ」という仕組みで解決するノートブック環境です。あるセルの変数を変更すると、その変数に依存する他のセルが自動的に再実行されます。スプレッドシートのセル参照に近い感覚です。

観点Jupyter NotebookMarimo
実行順序手動、自由な順序で実行可能依存関係に基づき自動的に決定
変数変更の反映関連セルを手動で再実行依存するセルが自動的に再実行
ファイル形式.ipynb(JSON形式).py(通常のPythonファイル)
Git差分メタデータ込みで見づらい通常のコード差分として見やすい

最小構成での起動

uv add marimo
uv run marimo edit analysis.py

 (3) marimo editを実行するとブラウザが開き、Jupyter同様のセル形式でコードを書けます。最大の違いは保存時の挙動で、analysis.pyは通常のPythonファイルとして保存され、各セルは関数のような形でファイルに記述されます。

import marimo

app = marimo.App()

@app.cell
def _():
    import polars as pl
    df = pl.read_csv("sales_2026.csv")
    return df, pl

@app.cell
def _(df):
    summary = df.group_by("category").agg(pl.col("amount").sum())
    summary
    return (summary,)

 (4) 2つ目のセルがdfを引数として受け取っている点がポイントです。Marimoはこの引数からセル間の依存関係を静的に解析し、dfを作るセルが実行されるまで下のセルの実行を待たせ、dfが変更されれば自動的に再実行します。

補足

グローバル変数を暗黙的に共有するJupyterとは異なり、Marimoは各セルの入出力変数が明示的にコードとして表れます。慣れないうちは冗長に感じますが、この明示性が依存関係の自動解決と、後述するGit管理のしやすさの両方を支えています。

UI要素の組み込み

 (5) Marimoは、スライダーやドロップダウンなどのUI部品を標準で持っており、これらの値の変更もリアクティブな再実行のトリガーになります。Streamlitのような簡易ダッシュボードを、ノートブックの延長として作れる点が特徴です。

@app.cell
def _():
    import marimo as mo
    threshold = mo.ui.slider(0, 100000, value=10000, label="金額のしきい値")
    threshold
    return mo, threshold

@app.cell
def _(df, threshold, pl):
    filtered = df.filter(pl.col("amount") > threshold.value)
    filtered
    return (filtered,)

 (6) スライダーを動かすと、そのしきい値を参照するfilteredセルが即座に再計算されます。QuickSightの回で扱ったBIツールほど本格的な可視化基盤は不要だが、簡単なインタラクティブ分析はしたい、という中間的なニーズにちょうど合う使い方です。

アプリとしての実行(Webアプリ化)

 (7) 同じ.pyファイルを、編集モードではなく実行専用のアプリとして起動することもできます。

uv run marimo run analysis.py
確認結果

スライダー付きのノートブックをmarimo runで起動したところ、コードセルは非表示になりUI部品と出力結果のみが表示される、簡易ダッシュボードとして動作することを確認しました。分析用のノートブックをそのまま社内向け簡易ツールとして共有できる手軽さがあります。

Git管理のしやすさ

 (8) Jupyterの.ipynbファイルは、実行結果やメタデータを含むJSON形式のため、diffが読みにくくコンフリクトも起きやすいという課題がありました。Marimoの.pyファイルは通常のPythonコードなので、GitHub Actionsの回で扱ったようなCI上でのlint(Ruffの回参照)やコードレビューがそのまま適用できます。

注意

依存関係の自動解析は変数名ベースで行われるため、同じ変数名を複数のセルで再定義すると意図しない依存関係エラーになることがあります。Jupyterのように同じ変数名を使い回す書き方から移行する際は、変数名の設計を少し見直す必要があります。

既存資産との組み合わせ

 (9) MarimoはPolars・pandas・matplotlib等、既存のデータ分析ライブラリをそのまま利用できます。DuckDBの回で扱ったSQLベースの分析も、Marimoのセル内でそのまま実行して結果をリアクティブに扱うことが可能です。

重要な注意

marimo runでアプリとして公開する場合、Jupyterの共有と同様にコードや接続情報が意図せず外部からアクセス可能にならないよう、ネットワーク設定やアクセス制御を必ず確認してください。特に社内DBの認証情報をノートブック内に直書きする運用は避けるべきです。

まとめ

 (10) Marimoは「ノートブックのはずが、いつのまにか実行順序に振り回される」というJupyterの積年の課題に、リアクティブな実行モデルという形で答えを出したツールです。Polars入門DuckDBで扱ってきたデータ分析の作業環境として、通常のPythonコードとして扱えるノートブックを試してみる価値は十分にあります。

参考資料

機能やAPIは活発に更新されている段階のため、導入前に公式ドキュメントで最新のバージョン情報を確認してください。

AWS Transfer FamilyでSFTP/FTPSサーバーの自前運用から卒業する

この記事で分かること
  • Transfer FamilyがDataSyncやS3直接公開と何を分担するのか
  • SFTPサーバー作成からユーザー認証・S3マッピングまでの流れ
  • Lambda連携によるカスタム認証とアップロード後の自動処理
記事の検証情報
確認日
2026年9月1日
検証環境
AWS CLI v2 / Transfer Family(SFTPプロトコル) / 転送先: S3、Lambdaによるカスタムアイデンティティプロバイダ
対象読者
取引先からのファイル受け渡しに自前運用のSFTPサーバー(EC2上のvsftpd等)を使っており、パッチ適用や可用性確保の運用負荷に悩んでいるSE

Transfer Familyが解決する課題

 (1) 前回AWS DataSyncの回では、AWSとオンプレミス間の大規模ファイル転送を扱いました。Transfer Familyは似た文脈のサービスですが目的が異なり、「取引先や外部パートナーが使い慣れたSFTP/FTPS/FTPクライアントから、S3やEFSに直接ファイルを送受信できるようにする」ためのマネージドサービスです。

 (2) 多くの企業間データ連携では、今なおSFTPが標準的なプロトコルとして使われ続けています。従来はEC2上にSFTPサーバーソフトウェアを構築し、OSパッチ適用や可用性確保を自前で担う必要がありましたが、Transfer Familyはこのサーバー部分を丸ごとマネージド化します。

観点EC2上の自前SFTPサーバーAWS Transfer Family
可用性確保Multi-AZ構成を自前設計マネージドで自動的に高可用
パッチ適用自前でOS/ミドルウェアを維持不要(AWS側が管理)
保存先EBS等のブロックストレージS3またはEFSに直接マッピング
認証方式OSユーザー/公開鍵を自前管理Service管理型、AD連携、Lambdaカスタム認証から選択

サーバーの作成

 (3) Transfer Familyでは、プロトコル(SFTP/FTPS/FTP/AS2)ごとにサーバーを作成します。ここではS3をバックエンドにしたSFTPサーバーの例です。

aws transfer create-server \
  --protocols SFTP \
  --identity-provider-type SERVICE_MANAGED \
  --endpoint-type VPC \
  --domain S3
補足

endpoint-typeをVPCにすると、取引先ごとに固定のプライベートIPを割り当てたり、VPC設計パターンの回で扱ったセキュリティグループでアクセス元IPを制限したりできます。パブリック公開が前提でない社内連携では、VPCエンドポイント構成が基本になります。

ユーザーの作成とS3マッピング

 (4) Service管理型の認証を選んだ場合、ユーザーごとに公開鍵(SSH鍵)を登録し、アクセス可能なS3パスとIAMロールを紐付けます。

aws transfer create-user \
  --server-id "s-0123456789abcdef0" \
  --user-name "partner-companyA" \
  --home-directory "/example-exchange-bucket/companyA/" \
  --role "arn:aws:iam::123456789012:role/TransferFamilyS3AccessRole" \
  --ssh-public-key-body "ssh-rsa AAAAB3NzaC1yc2EA..."

 (5) home-directoryを取引先ごとのプレフィックスに絞ることで、各取引先は自分専用のディレクトリしか見えません。IAMロール側でも、そのプレフィックス以外へのアクセスを許可しないポリシーを組み合わせるのが基本です。

ホームディレクトリの論理マッピング

 (6) より柔軟な制御が必要な場合、HomeDirectoryType=LOGICALを使い、S3上の実際のパスとSFTPクライアントから見えるパスを分離できます。取引先には/inboundという見た目のパスを見せつつ、実体はcompany-a/2026/09/のような内部パスにマッピングする、といった構成が可能です。

注意

複数の取引先が同一サーバーを共有する構成では、IAMロールのポリシー条件(${transfer:UserName}等のポリシー変数)を使って、ユーザーごとに動的にアクセス範囲を絞ることが重要です。ロールを使い回すと設定ミスで他社のディレクトリが見えてしまうリスクがあります。

Lambdaによるカスタム認証

 (7) 取引先の数が多く、既存の認証基盤(社内DB、Active Directory等)と連携したい場合は、identity-provider-typeAWS_LAMBDAにして、認証ロジックをLambda関数に委譲できます。

import json

def lambda_handler(event, context):
    username = event["username"]
    password = event.get("password")

    # 実際には社内の認証基盤へ問い合わせる想定
    if username == "partner-companyB" and password == "valid-password":
        return {
            "Role": "arn:aws:iam::123456789012:role/TransferFamilyS3AccessRole",
            "HomeDirectory": "/example-exchange-bucket/companyB/",
        }
    raise Exception("認証に失敗しました")

 (8) この仕組みはCognitoカスタム認証の回で扱ったトリガーLambdaの考え方と近く、既存の認証基盤を持つ企業では、Service管理型よりもこちらの方が自然に組み込めることが多いです。

アップロード完了後の自動処理

 (9) 取引先がファイルをアップロードした後、自動的に後続処理(検証、変換、Glueジョブ起動など)を走らせたい場合、S3イベント通知をトリガーにLambdaやStep Functionsを起動する構成が定番です。

aws s3api put-bucket-notification-configuration \
  --bucket example-exchange-bucket \
  --notification-configuration file://notification.json

 (10) notification.jsonでプレフィックス条件を取引先ディレクトリに絞り、Lambda関数のARNを指定します。アップロードされたファイルのウイルススキャンや、フォーマット検証を経てから正式なデータレイクに取り込む、という流れはGlue ETLStep Functionsの回で扱った処理と組み合わせやすい構成です。

確認結果

Service管理型のSFTPサーバーに対し、標準的なSFTPクライアント(WinSCP相当)から公開鍵認証で接続し、ファイルのアップロードが即座にS3バケットへ反映されることを確認しました。取引先側には従来のSFTP運用と操作感の変化がない点が、移行のハードルを下げるポイントです。

DataSyncとの役割の違い(再確認)

 (11) 前回のDataSyncは「AWS側が能動的にオンプレミスへ取りに行く/送り込む」大量データの同期が主眼でした。Transfer Familyは逆に「外部の取引先が能動的にSFTPクライアントから接続してくる」窓口を用意する立ち位置です。両者は競合するものではなく、データの流れの方向性で使い分けるサービスです。

重要な注意

パブリックエンドポイントでSFTPサーバーを公開する場合、既知の公開鍵以外からの接続を確実に拒否できているか、IAMロールが必要以上に広い権限を持っていないかを、本番運用開始前に必ず確認してください。取引先経由の窓口は攻撃の起点になりやすい箇所です。

まとめ

 (12) Transfer Familyは、取引先とのファイル授受という「昔ながらの要件」を、S3を裏側に持つモダンな構成でマネージド化できるサービスです。DataSyncGlue ETLで構築してきたデータ基盤の入口として、外部連携の窓口を整理する際の有力な選択肢になります。

参考資料

対応プロトコルや料金体系は更新されることがあるため、導入前に公式ドキュメントの最新情報を確認してください。

AWS DataSyncでオンプレミス⇔AWS間のデータ転送を自動化・省力化する

この記事で分かること
  • DataSyncがrsyncやAWS CLIでの手動コピーと何が違うのか
  • オンプレミス側エージェントのデプロイとLocation設定の流れ
  • Taskのスケジュール実行と差分転送・検証オプションの使い分け
記事の検証情報
確認日
2026年9月1日
検証環境
AWS CLI v2 / DataSyncエージェント(VMware版) / 転送先: S3標準ストレージクラス
対象読者
オンプレミスのファイルサーバーからS3への定期的なデータ移行・同期をスクリプトのcronで運用しており、転送漏れや再送処理の実装に疲れているSE

DataSyncが解決する課題

 (1) オンプレミスからS3へのファイル転送は、シンプルにはaws s3 syncコマンドやrsyncで実現できます。しかしファイル数が数百万件規模になったり、転送の完了検証・再送処理・帯域制御・スケジュール管理まで含めると、自前のスクリプトはすぐに複雑になっていきます。DataSyncは、この「大規模なファイル転送」をマネージドサービスとして扱えるようにしたものです。

 (2) Transfer FamilyがSFTP/FTPSのプロトコル互換性に主眼を置くのに対し、DataSyncは「S3・EFS・FSx・オンプレミスNFS/SMB」間の大量データ転送そのものに特化しています。転送速度の最適化、チェックサムによる検証、差分転送(2回目以降は変更分のみ)を標準機能として持っている点が大きな違いです。

観点aws s3 sync / rsyncAWS DataSync
大規模ファイル数への対応自前でリトライ・並列化を実装並列転送・自動リトライを標準サポート
転送の検証自前でチェックサム比較を実装転送後の検証オプションを標準搭載
スケジュール管理cron等で自前管理Task Scheduleで管理
帯域制御自前で制御ロジックが必要帯域制限をTask設定で指定可能

構成要素:Agent/Location/Task

 (3) DataSyncは3つの要素で構成されます。オンプレミス側にデプロイする「Agent」(VMware/Hyper-V/EC2向けの仮想アプライアンス)、転送元・転送先を指す「Location」、実際の転送設定をまとめた「Task」です。

エージェントのアクティベーション

 (4) オンプレミス側にDataSyncエージェント(VMのOVAイメージ等)をデプロイし、AWSコンソールまたはCLIでアクティベーションキーを取得して紐付けます。

aws datasync create-agent \
  --activation-key "AAAA-BBBB-CCCC-DDDD" \
  --agent-name "onprem-fileserver-agent"
補足

アクティベーションキーは、エージェントVMのローカルWeb UIにアクセスし、そこに表示されるパブリックIPまたはVPCエンドポイント経由でAWS側と疎通確認を行うことで取得します。VPC設計パターンの回で扱ったプライベートリンク構成を使えば、インターネットを経由せずアクティベーションできます。

Locationの作成

オンプレミスNFS(転送元)

aws datasync create-location-nfs \
  --server-hostname "fileserver.internal.example.com" \
  --subdirectory "/export/data" \
  --on-prem-config AgentArns="arn:aws:datasync:ap-northeast-1:123456789012:agent/agent-0123456789abcdef0"

S3(転送先)

aws datasync create-location-s3 \
  --s3-bucket-arn "arn:aws:s3:::example-migration-bucket" \
  --subdirectory "/onprem-backup/" \
  --s3-config BucketAccessRoleArn="arn:aws:iam::123456789012:role/DataSyncS3AccessRole" \
  --s3-storage-class "STANDARD_IA"

 (5) 転送先のS3ストレージクラスをここで指定できるため、S3設計パターン・ライフサイクル管理の回で扱ったライフサイクルルールと組み合わせて、バックアップ用途ならIAクラスで受ける、といった設計が可能です。

Taskの作成とスケジュール実行

aws datasync create-task \
  --source-location-arn "arn:aws:datasync:...:location/loc-nfs-0123" \
  --destination-location-arn "arn:aws:datasync:...:location/loc-s3-0456" \
  --name "nightly-fileserver-backup" \
  --options 'VerifyMode=ONLY_FILES_TRANSFERRED,PreserveDeletedFiles=REMOVE' \
  --schedule ScheduleExpression="cron(0 18 * * ? *)"

 (6) ScheduleExpressionはEventBridgeのcron式と同じ書式です。EventBridgeの回で扱ったスケジュールルールと同様の感覚で設定できます。PreserveDeletedFiles=REMOVEを指定すると、転送元で削除されたファイルは転送先からも削除される、いわゆるミラーリング的な同期になります。

注意

PreserveDeletedFiles=REMOVEは強力な設定です。転送元のディレクトリ指定を誤ると、意図しないファイルが転送先から削除される事故につながります。初回実行時はPRESERVE(削除しない)で動作確認してから、運用が安定した後にREMOVEへ切り替えるのが安全です。

差分転送と検証

 (7) DataSyncは2回目以降の実行で、ファイルのタイムスタンプ・サイズを比較し変更があったファイルのみを転送します。加えてVerifyModeの設定により、転送後にチェックサムベースの検証を行い、破損転送を検知できます。

確認結果

約20万ファイル・500GB規模のオンプレミスディレクトリに対して初回フル転送を実行後、ファイルを数十件だけ更新して再実行したところ、変更のあったファイルのみが転送され、実行時間が初回の数十分の一に短縮されることを確認しました。

Lambdaによるタスク実行のフック

 (8) DataSyncのタスク完了はCloudWatch Eventsに通知されるため、EventBridgeLambdaと組み合わせて「転送完了後に後続のGlueジョブを起動する」といったパイプラインを構築できます。Glue ETLの回で扱った処理の入口として、DataSyncを位置付けることができます。

重要な注意

DataSyncの転送先IAMロール(BucketAccessRoleArn)には、対象S3バケットへの書き込み権限が必要です。過度に広い権限(s3:*等)を付与しないよう、対象バケット・プレフィックスに絞ったポリシーを設計してください。

まとめ

 (9) DataSyncは、自前のrsyncスクリプトやcron運用で担っていた「大規模ファイル転送の信頼性確保」を、マネージドサービスとして肩代わりしてくれます。S3設計パターンEventBridgeで構築してきたデータ基盤の入口として、オンプレミスとの接続点を整理する際の有力な選択肢です。

参考資料

対応プロトコルや料金体系は更新されることがあるため、導入前に公式ドキュメントの最新情報を確認してください。