0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWSへのアクセスエラー対処をしていたら、変な仕様に気づいた話

0
Posted at

この記事は2026年10月に書かれたものです。
皆さんがご覧になる時期によってはAWSやSnowflakeの仕様が大きく変わっている可能性があります。
ご了承ください。

AWSとSnowflakeを繋げようとしただけなんです。
それがまさかこんなことになるとは。

1.5日ほど続いた私の戦い、誰かに見てもらわなければ割に合わないと思ったため、ここに記録します。

ここで取り扱うのはAWSとSnowflakeですが、Snowflake以外との接続エラーにおいても参考になるかもしれません。
ぜひ目を通してみてください。

SnowflakeからAWS S3にアクセスしたい!

データレイクに貯めこまれたデータにアクセスし、データウェアハウス上で様々な処理をする。実に一般的な構成です。

データウェアハウスとしてSnowflake使いたいなー、データレイクといえばS3かなー、なんて軽い気持ちでこのアクセスを試みました。

この瞬間、戦いの火蓋が切って落とされたことには、まだ気が付くはずもありませんでした。


S3にアクセスするため、Snowflake上でこんなSQLを実行しました。

--Storage Integrationの設定
CREATE OR REPLACE STORAGE INTEGRATION S3_INT
    TYPE = EXTERNAL_STAGE
    STORAGE_PROVIDER = 'S3'
    ENABLED = TRUE
    STORAGE_AWS_ROLE_ARN = 'arn:aws:iam::<AWSアカウントID>:role/<IAMロール名>'
    STORAGE_ALLOWED_LOCATIONS = ('s3://<S3のバケット>/');

--Storage Integrationの中身の確認
DESC INTEGRATION S3_INT;

--外部ステージ作成
CREATE OR REPLACE STAGE S3_STAGE
    URL='s3://<S3のバケット>/'
    STORAGE_INTEGRATION = S3_INT;

--ステージにあるものをリストアップ
LIST @S3_STAGE;

バケットというのはS3のデータ格納場所です。
また、ロールやポリシーというのは「誰が・何に・どんな操作をできるか」をAWS上で制御する仕組みです。
ロールとポリシーも自分で作る必要がありますが、調べればいくらでも解説が出てくるため、ここでは詳しく述べないことにします。
(私もちゃんと理解できているか怪しいので)

上から順に実行し、最後のLIST @S3_STAGEによってS3上のデータを閲覧できるはずでした。
しかし、その代わりに出たのは、無情にも以下のエラーでした。

Error assuming AWS_ROLE: User: <Snowflake側のARN> is not authorized to perform:
sts:AssumeRole on resource: arn:aws:iam::<AWSアカウントID>:role/<IAMロール名>

このエラー、見る人が見れば、真っ先に疑うべきところはすぐにわかります。
「SnowflakeのStorage Integrationと、AWSのロールの設定がうまくいかなかった」です。

設定を作る際、AWSのロールにはSnowflakeの情報を、SnowflakeのStorage IntegrationにはAWSのロールの情報を渡します。
そして、AWSのSecurity Token Service(STS)が提供する AssumeRole APIを使ってロールをSnowflakeが一時的に引き受け、一時的なセキュリティ認証情報を取得します。

そのロールを受け取れなかった、というエラーなので、当然その設定がうまくいっていないのでは?となります。

しかし、どこをどう見たって設定に問題はありませんでした。

じゃあ何が邪魔をしているの?ということで、ChatGPTにも頼りながら様々な検証をしていったわけですが。

この検証が長かった...。本当に長かった...。

ロールの設定を変えたり、ロールやStorage Integrationを作り直してみたり、Snowflakeの別アカウントを作ってそっちからアクセスしてみたり、AWSを有料プランに切り替えたり...。

ずっと暖簾に腕押しという感じで時間が過ぎていき、空虚感に満たされるようでしたが、ご安心ください。
皆さんに同じ思いはさせません。

では、真の原因はなんだったのかを見ていきましょう。

元凶:リソースコントロールポリシー(RCP)

原因が全く分からず、ChatGPTくんにも「サポートに問い合わせたほうがいい」と言われるところまで行きました。
そうしたほうがいいかな、と思いつつ、「よく考えたらなんか変かも?」と感じることがあったため、最後にそれを聞いてみることにしました。
そのやり取りがこちら。

situmonn.jpg

革命が起きました。ChatGPTくんも大興奮です。

私のAWSのアカウントは今回のSnowflakeとの連携のために新しく作ったもので、AWSに触るのも初めてでした。
ですので、「Touch grass later」とかいう謎のプロジェクトに初めから割り当てられているという異常に気が付きませんでした。

ここから、ChatGPTくんが堰を切ったように新情報を出してくれました。
この時のチャッピー、いつもよりちょっと早口だった気がする。

まとめると、どうやら私は新しいサインアップ方式で登録したため、従来とは異なる仕様のアカウント設定になっていたようです。

上記公式文書にある通り、現在サインアップ方式はnewとadvancedの2種類存在します。

スクリーンショット 2026-10-08 131741.png

少なくとも私に表示されたこの画面では、メインの導線から進むとnewになり、
右下に小さくあるSign up (advanced)を押すと、advancedで登録できるようです。

AWS初心者の私は当然右下の文字など気にせず、newで登録してしまったというわけでした。

newだと何が問題なの?

newとadvancedの違いは色々ありまして、先ほどの謎プロジェクトもその一つなのですが、今回のエラーにつながったのは超厳しい外部からのアクセス制限です。

newで登録したアカウントは、自動的に作られた組織(Organization)に所属する形となり、その組織にはリソースコントロールポリシー (RCP) やサービスコントロールポリシー (SCP) といったものが自動で割り当てられています。

これらは組織を管理する上でのルールみたいなものなのですが、今回はリソースコントロールポリシー (以下、RCPと呼びます) が悪さをしていました。

RCPはJSON形式で定義されています。こんな感じです

{
  "Sid": "DenyAnyoneOutsideMyOrgAndAWS",
  "Effect": "Deny",
  "Principal": "*",
  "Action": [
    "s3:*",
    "sts:*"
  ],
  "Resource": "*",
  "Condition": {
    "StringNotEqualsIfExists": {
      "aws:PrincipalOrgID": "${aws:ResourceOrgID}"
    },
    "Null": {
      "aws:PrincipalARN": "false"
    },
    "BoolIfExists": {
      "aws:PrincipalIsAWSService": "false"
    }
  }
}

説明のために簡略化していますが、"Effect": "Deny" かつ "Action":["sts:*"]となっていますね。

自分の組織の外から、組織内リソースに対して行われる "sts:*" のうち、記載された"Condition"に該当するアクセスを拒否する設定です。

Snowflakeからの "sts:AssumeRole" はこの条件に該当していたようです。

おさらいですが、"sts:AssumeRole"というのは、一時的な認証情報を発行するAWSのAPIです。
そのAssumeRoleを拒否してるんだから、そりゃあエラーが起きるはずですよねぇ...。

解決編

原因はわかりました。じゃあ、エラーを解消するにはどうしたらいいのか。
実際に私が行った対処を、順を追って説明します。

当然、RCPをちょっと書き換えてやる必要があるわけですが、そもそもこの設定はセキュリティの一つなので、安易に書き換えることは推奨されません。
私はChatGPTくんと相談しながら、これならいいかな、という形に落とし込みましたが、これが正解とも限りません。あくまで一例としてご覧ください。

というか、間違っている可能性すらあると思っています。
もし、以下で述べる私の設定に危ない部分があればご指摘くださると大変ありがたいです。

そもそもの話、Sign up for AWS (new) の初期状態では、OrganizationsのRCP/SCPはAWS側で管理されています。
私の環境では、まずPaid Planへ変更したうえで Advanced AWS featuresを有効化することで、RCPを自分で編集できるようになりました。
Advanced featuresの有効化は元に戻せないため、公式ドキュメントを確認したうえで実施してください。

まずはRCPの設定を見に行きましょう。

  1. AWS Setting(設定)に移動
  2. プロジェクト - 管理アカウント に表示されているアカウント名をクリック
  3. 右上のほうにあるAWS Organizations をクリック
  4. 左のメニューから、ポリシー - リソースコントロールポリシー へ移動
  5. ManagedAccountResourceControlPolicy を開く

これでJSONを編集できるところまで行きました。

変更内容

初期設定では、"DenyAnyoneOutsideMyOrgAndAWS"と"DenyAWSWhenItsNotMyOrgsSourceAccount"という二つのStatementがあるはずです。
その両方の"Action"から、"sts:*"を削除します。

以下は"DenyAnyoneOutsideMyOrgAndAWS"の実際の設定です。
頑張って"sts:*"を探しましょう。

 {
      "Sid" : "DenyAnyoneOutsideMyOrgAndAWS",
      "Effect" : "Deny",
      "Principal" : "*",
      "Action" : [
        "aoss:*",
        "appconfig:*",
        "appstream:*",
        "autoscaling:*",
        "codebuild:*",
        "codecommit:*",
        "cognito-identity:*",
        "cognito-idp:*",
        "comprehend:*",
        "comprehendmedical:*",
        "dax:*",
        "dynamodb:*",
        "ecr:*",
        "health:*",
        "kinesisvideo:*",
        "kms:*",
        "logs:*",
        "s3:*",
        "secretsmanager:*",
        "sqs:*",
        "sts:*",
        "support:*",
        "textract:*",
        "transcribe:*",
        "translate:*"
      ],
      "Resource" : "*",
      "Condition" : {
        "BoolIfExists" : {
          "aws:PrincipalIsAWSService" : "false"
        },
        "Null" : {
          "aws:PrincipalARN" : "false"
        },
        "StringNotEqualsIfExists" : {
          "aws:PrincipalOrgID" : "${aws:ResourceOrgID}"
        }
      }
    }

削除するだけでも、エラーは解消されます。
しかし、これではただRCPが担っていた"sts:*"による認証の部分のガードを緩めただけ。

認証を許すのは、あくまで最小限の範囲にとどめておくのが安全でしょう。
というわけで、"sts:*"を削除した後のJSONのStatementに、以下を追記しました。

{
  "Sid": "DenyExternalSTSExceptSnowflakeRole",
  "Effect": "Deny",
  "Principal": "*",
  "Action": "sts:*",
  "NotResource": "arn:aws:iam::<AWSアカウントID>:role/<IAMロール名>",
  "Condition": {
    "StringNotEqualsIfExists": {
      "aws:PrincipalOrgID": "${aws:ResourceOrgID}"
    },
    "Null": {
      "aws:PrincipalARN": "false"
    },
    "BoolIfExists": {
      "aws:PrincipalIsAWSService": "false"
    }
  }
}

"DenyAnyoneOutsideMyOrgAndAWS"の設定を、"sts:*"だけで新たに書き直したものです。実際には、"DenyAWSWhenItsNotMyOrgsSourceAccount"の設定も同じように書き直す必要があります。

ポイントは"NotResource": "<AWSロールのARN>/<AWSロール>"。

"NotResource"は、このStatementの対象外にするリソースを指定するためのものです。
要するに、今回のSnowflakeとの接続で使うロールだけ、認証を拒否しないという状態を作りました。

なお、これはSnowflakeへのアクセスを直接許す設定ではありません。
実際に AssumeRole できるかどうかは、ロール自体の設定およびSnowflakeのStorage Integrationの設定、その他のアクセス制御も含めて判定されます。

これで、セキュリティを担保しつつ、エラーの解消も達成できました。

繰り返しになりますが、"DenyAnyoneOutsideMyOrgAndAWS"と"DenyAWSWhenItsNotMyOrgsSourceAccount"という二つのStatementがあることに注意しましょう。
両方から"sts:*"を削除し、両方の分の設定を新たに追記します。
初期状態ではStatementは二つ、最終的には四つになります。

終わりに

この話をまとめると、「AWSの新しい方式でアカウントを作ったためにセキュリティがガチガチ過ぎて、Snowflakeからのアクセスが拒否されていた」ということになるでしょうか。

私が探した限り、同じようなエラーで困った経験のある人は見つかりませんでした。
ChatGPTくんがあれだけ困っていたことからも、ネット上にもあまり情報が落ちていないのではないかと思います。(2026年10月現在)

だからこそ、この記事を書くことにしました。
新しくアカウントを作るAWS初心者ほどこのトラップにかかりやすいと思うので、せめて一つの手がかりになれば嬉しいです。


ちなみに、AWSサインアップについての公式文書にはこんなことが書いてあります。

現在、限られた数のお客様に新しいエクスペリエンスをリリースしています。このエクスペリエンスにまだアクセスできない場合があります。

つまり、人によってはnew方式でのサインアップ画面が出ないってこと...?
私はその限られた数の内の一人?
なんで私の元に来ちゃったんですか??

この文の正確な意味や、そもそもこの仕様になっている意図はよくわかりませんが、実際にエラーに見舞われた身からすると、ずいぶんなトラップだなぁと思います。

まあでも、エンジニアとして駆け出しの私にとってはいい経験になりました。
図らずもAWSやSnowflakeの理解も深まった気がします。

皆さんの良きAWSライフ、及びSnowflakeライフの一助になれば幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?