Skip to content
Skillv1.0.0

iam-security

AWS IAM(Identity and Access Management)のセキュリティ設計パターン。最小権限の原則に基づく ポリシー設計、IAMロールの構成、クロスアカウントアクセス、条件キーによるアクセス制御、 サービスリンクロール、権限境界(Permissions Boundary)を網羅する。 セキュアなAWS環境構築において最も重要な基盤であり、全てのAWSリソース設計の前提となる。

by engineers-hub-ltd-in-house-project(0) 0 installs
Free
Sign in to install

Free account. Installing gives you the manifest plus copy-paste snippets.

See reviews

About

Imported from engineers-hub-ltd-in-house-project/eh-skills (skills/aws-infrastructure/iam-security/SKILL.md). Install upstream with npx skills add engineers-hub-ltd-in-house-project/eh-skills --skill iam-security. Copyright stays with the author (Apache-2.0).

IAM セキュリティ設計パターン

このスキルを使うタイミング

  • AWS リソースへのアクセス制御ポリシーを設計するとき
  • IAM ロールを作成しサービスやユーザーに権限を付与するとき
  • クロスアカウントアクセスを構成するとき
  • CI/CD パイプラインの実行ロールを設計するとき
  • 条件キーで細粒度のアクセス制御を実装するとき
  • 権限境界(Permissions Boundary)で権限の上限を設定するとき

基本パターン

最小権限ポリシー設計

# アプリケーション用 IAM ロール(ECS タスクロールの例)
resource "aws_iam_role" "app_task" {
  name = "${var.project}-${var.environment}-app-task-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          Service = "ecs-tasks.amazonaws.com"
        }
        Action = "sts:AssumeRole"
        Condition = {
          ArnLike = {
            "aws:SourceArn" = "arn:aws:ecs:${var.region}:${var.account_id}:*"
          }
          StringEquals = {
            "aws:SourceAccount" = var.account_id
          }
        }
      }
    ]
  })

  tags = {
    Name        = "${var.project}-${var.environment}-app-task-role"
    Environment = var.environment
  }
}

# S3 バケットへのアクセスポリシー(最小権限)
resource "aws_iam_role_policy" "app_s3_access" {
  name = "${var.project}-${var.environment}-s3-access"
  role = aws_iam_role.app_task.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "ReadFromInputBucket"
        Effect = "Allow"
        Action = [
          "s3:GetObject",
          "s3:ListBucket",
        ]
        Resource = [
          aws_s3_bucket.input.arn,
          "${aws_s3_bucket.input.arn}/*",
        ]
      },
      {
        Sid    = "WriteToOutputBucket"
        Effect = "Allow"
        Action = [
          "s3:PutObject",
          "s3:DeleteObject",
        ]
        Resource = [
          "${aws_s3_bucket.output.arn}/*",
        ]
      }
    ]
  })
}

# Secrets Manager からのシークレット取得
resource "aws_iam_role_policy" "app_secrets" {
  name = "${var.project}-${var.environment}-secrets-access"
  role = aws_iam_role.app_task.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "GetSecrets"
        Effect = "Allow"
        Action = [
          "secretsmanager:GetSecretValue",
        ]
        Resource = [
          "arn:aws:secretsmanager:${var.region}:${var.account_id}:secret:${var.project}/${var.environment}/*",
        ]
        Condition = {
          StringEquals = {
            "aws:ResourceTag/Environment" = var.environment
          }
        }
      }
    ]
  })
}

ECS タスク実行ロール

# タスク実行ロール(ECR からのイメージ取得、CloudWatch Logs への書き込み)
resource "aws_iam_role" "ecs_execution" {
  name = "${var.project}-${var.environment}-ecs-execution-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          Service = "ecs-tasks.amazonaws.com"
        }
        Action = "sts:AssumeRole"
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "ecs_execution_base" {
  role       = aws_iam_role.ecs_execution.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}

# Secrets Manager / SSM Parameter Store からの値取得(タスク定義の secrets で参照)
resource "aws_iam_role_policy" "ecs_execution_secrets" {
  name = "${var.project}-${var.environment}-execution-secrets"
  role = aws_iam_role.ecs_execution.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "GetSecretsForTaskDefinition"
        Effect = "Allow"
        Action = [
          "secretsmanager:GetSecretValue",
        ]
        Resource = [
          "arn:aws:secretsmanager:${var.region}:${var.account_id}:secret:${var.project}/${var.environment}/*",
        ]
      },
      {
        Sid    = "GetSSMParameters"
        Effect = "Allow"
        Action = [
          "ssm:GetParameters",
        ]
        Resource = [
          "arn:aws:ssm:${var.region}:${var.account_id}:parameter/${var.project}/${var.environment}/*",
        ]
      }
    ]
  })
}

クロスアカウントアクセス

# 本番アカウントの S3 バケットへ開発アカウントからアクセスするパターン

# --- 本番アカウント側 ---
resource "aws_iam_role" "cross_account_reader" {
  name = "${var.project}-cross-account-reader"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          AWS = "arn:aws:iam::${var.dev_account_id}:root"
        }
        Action = "sts:AssumeRole"
        Condition = {
          StringEquals = {
            "sts:ExternalId" = var.external_id
          }
        }
      }
    ]
  })

  max_session_duration = 3600
}

resource "aws_iam_role_policy" "cross_account_reader_policy" {
  name = "read-only-access"
  role = aws_iam_role.cross_account_reader.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "s3:GetObject",
          "s3:ListBucket",
        ]
        Resource = [
          aws_s3_bucket.shared_data.arn,
          "${aws_s3_bucket.shared_data.arn}/*",
        ]
      }
    ]
  })
}
// --- 開発アカウントのアプリケーションコード ---
import { STSClient, AssumeRoleCommand } from "@aws-sdk/client-sts";
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";

async function getS3ClientForProductionAccount(): Promise<S3Client> {
  const stsClient = new STSClient({ region: process.env.AWS_REGION });

  const assumeRoleResponse = await stsClient.send(
    new AssumeRoleCommand({
      RoleArn: `arn:aws:iam::${process.env.PRODUCTION_ACCOUNT_ID}:role/${process.env.PROJECT}-cross-account-reader`,
      RoleSessionName: "dev-account-reader",
      ExternalId: process.env.EXTERNAL_ID,
      DurationSeconds: 3600,
    }),
  );

  const credentials = assumeRoleResponse.Credentials;
  if (!credentials) {
    throw new Error("Failed to assume cross-account role");
  }

  return new S3Client({
    region: process.env.AWS_REGION,
    credentials: {
      accessKeyId: credentials.AccessKeyId!,
      secretAccessKey: credentials.SecretAccessKey!,
      sessionToken: credentials.SessionToken!,
    },
  });
}

権限境界(Permissions Boundary)

# 開発者が作成できるロールの権限上限を定義
resource "aws_iam_policy" "developer_boundary" {
  name = "${var.project}-developer-permissions-boundary"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "AllowCommonServices"
        Effect = "Allow"
        Action = [
          "s3:*",
          "dynamodb:*",
          "sqs:*",
          "sns:*",
          "lambda:*",
          "logs:*",
          "cloudwatch:*",
          "ecr:*",
          "ecs:*",
          "secretsmanager:GetSecretValue",
        ]
        Resource = "*"
      },
      {
        Sid    = "DenyIAMEscalation"
        Effect = "Deny"
        Action = [
          "iam:CreateUser",
          "iam:CreateAccessKey",
          "iam:AttachUserPolicy",
          "iam:PutUserPolicy",
          "organizations:*",
          "account:*",
        ]
        Resource = "*"
      },
      {
        Sid      = "DenyCriticalResourceDeletion"
        Effect   = "Deny"
        Action   = [
          "rds:DeleteDBInstance",
          "rds:DeleteDBCluster",
          "s3:DeleteBucket",
        ]
        Resource = "*"
        Condition = {
          StringEquals = {
            "aws:ResourceTag/Protected" = "true"
          }
        }
      }
    ]
  })
}

# 開発者ロール作成時に Permissions Boundary を強制
resource "aws_iam_role" "developer" {
  name                 = "${var.project}-developer-role"
  permissions_boundary = aws_iam_policy.developer_boundary.arn

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          Federated = "arn:aws:iam::${var.account_id}:saml-provider/${var.idp_name}"
        }
        Action = "sts:AssumeRoleWithSAML"
        Condition = {
          StringEquals = {
            "SAML:aud" = "https://signin.aws.amazon.com/saml"
          }
        }
      }
    ]
  })
}

CI/CD パイプラインロール(GitHub Actions OIDC)

# GitHub Actions から OIDC で AssumeRole
resource "aws_iam_openid_connect_provider" "github" {
  url             = "https://token.actions.githubusercontent.com"
  client_id_list  = ["sts.amazonaws.com"]
  thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
}

resource "aws_iam_role" "github_actions" {
  name = "${var.project}-github-actions-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          Federated = aws_iam_openid_connect_provider.github.arn
        }
        Action = "sts:AssumeRoleWithWebIdentity"
        Condition = {
          StringEquals = {
            "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
          }
          StringLike = {
            "token.actions.githubusercontent.com:sub" = "repo:${var.github_org}/${var.github_repo}:ref:refs/heads/main"
          }
        }
      }
    ]
  })
}

必須ルール

  1. 最小権限の原則を徹底 -- * ワイルドカードのリソース指定は最終手段。具体的な ARN で制限する
  2. IAM ユーザーよりロールを優先 -- 長期的なアクセスキーは避け、AssumeRole を使用する
  3. 条件キーで追加制限 -- aws:SourceAccount, aws:SourceArn, aws:ResourceTag 等で制御を強化する
  4. ExternalId を使用 -- クロスアカウントアクセスでは混乱した代理人問題を防止する
  5. Permissions Boundary を活用 -- 開発者に IAM ロール作成を許可する場合は権限の上限を設定する
  6. OIDC を使用 -- CI/CD パイプラインでは長期クレデンシャルではなく OIDC 連携を採用する
  7. Sid を付与 -- ポリシーの各 Statement に意図を示す Sid を必ず設定する

アンチパターン

  • Action: "*"Resource: "*" の組み合わせ(管理者ポリシー以外では禁止)
  • IAM ユーザーにアクセスキーを発行して永続化する(ローテーション漏れのリスク)
  • 複数サービスで同一ロールを共有する(権限が肥大化し最小権限を維持できない)
  • インラインポリシーを多用する(管理ポリシーの方が再利用・監査が容易)
  • NotAction / NotResource の安易な使用(意図しない許可が生まれやすい)
  • 本番環境で AdministratorAccess マネージドポリシーを使用する
  • クロスアカウントアクセスで ExternalId を省略する(混乱した代理人攻撃のリスク)

テスト戦略

IAM セキュリティのテストでは以下のケースを検証する:

  1. 最小権限検証: 許可されたアクションのみ実行可能であること(iam:SimulatePrincipalPolicy を使用)
  2. 拒否検証: 明示的に拒否されたアクションが実行できないこと
  3. クロスアカウント検証: 正しい ExternalId でのみ AssumeRole が成功すること
  4. Permissions Boundary 検証: 境界を超える権限が付与されないこと
  5. 条件キー検証: 条件を満たさないリクエストが拒否されること
  6. OIDC 検証: 指定リポジトリ・ブランチからのみ AssumeRole が成功すること
  7. IAM Access Analyzer: 未使用の権限やパブリックアクセスを定期的にスキャンする

Use it

Copy one of these into your project. Installing also returns the manifest and these snippets.

yaml
targets:
  - https://api.opensmartroute.ai/api/v1/registry/engineers-hub-ltd-in-house-project-eh-skills-iam-security/manifest   # or paste the manifest below

Manifest

An Open Capability Manifest: the router reads it to know what this does, what it costs and when to pick it.

engineers-hub-ltd-in-house-project-eh-skills-iam-security.ocm.jsonjson
{
  "ocm": "1",
  "id": "engineers-hub-ltd-in-house-project-eh-skills-iam-security",
  "kind": "skill",
  "name": "iam-security",
  "description": "AWS IAM(Identity and Access Management)のセキュリティ設計パターン。最小権限の原則に基づく ポリシー設計、IAMロールの構成、クロスアカウントアクセス、条件キーによるアクセス制御、 サービスリンクロール、権限境界(Permissions Boundary)を網羅する。 セキュアなAWS環境構築において最も重要な基盤であり、全てのAWSリソース設計の前提となる。",
  "publisher": "engineers-hub-ltd-in-house-project",
  "version": "1.0.0",
  "capabilities": {
    "domains": [
      "general"
    ],
    "tags": [
      "skill-md",
      "iam",
      "security",
      "policy",
      "role",
      "least-privilege",
      "cross-account",
      "permissions-boundary",
      "github"
    ],
    "languages": [
      "en"
    ]
  },
  "quality_prior": 0.6,
  "examples": [
    "AWS IAM(Identity and Access Management)のセキュリティ設計パターン。最小権限の原則に基づく ポリシー設計、IAMロールの構成、クロスアカウントアクセス、条件キーによるアクセス制御、 サービスリンクロール、権限境界(Permissions Boundary)を網羅する。 セキュアなAWS環境構築において最も重要な基盤であり、全てのAWSリソース設計の前提となる。"
  ],
  "primary": false,
  "metadata": {
    "source": {
      "provider": "github",
      "repository": "https://github.com/engineers-hub-ltd-in-house-project/eh-skills",
      "path": "skills/aws-infrastructure/iam-security/SKILL.md",
      "ref": "68cf6142c1667262fae60f1325217fd2ce00e88d",
      "url": "https://github.com/engineers-hub-ltd-in-house-project/eh-skills/blob/68cf6142c1667262fae60f1325217fd2ce00e88d/skills/aws-infrastructure/iam-security/SKILL.md",
      "key": "engineers-hub-ltd-in-house-project/eh-skills/skills/aws-infrastructure/iam-security/SKILL.md"
    },
    "license": "Apache-2.0"
  },
  "instructions": "# IAM セキュリティ設計パターン\n\n## このスキルを使うタイミング\n\n- AWS リソースへのアクセス制御ポリシーを設計するとき\n- IAM ロールを作成しサービスやユーザーに権限を付与するとき\n- クロスアカウントアクセスを構成するとき\n- CI/CD パイプラインの実行ロールを設計するとき\n- 条件キーで細粒度のアクセス制御を実装するとき\n- 権限境界(Permissions Boundary)で権限の上限を設定するとき\n\n## 基本パターン\n\n### 最小権限ポリシー設計\n\n```hcl\n# アプリケーション用 IAM ロール(ECS タスクロールの例)\nresource \"aws_iam_role\" \"app_task\" {\n  name = \"${var.project}-${var.environment}-app-task-role\"\n\n  assume_role_policy = jsonencode({\n    Version = \"2012-10-17\"\n    Statement = [\n      {\n        Effect = \"Allow\"\n        Principal = {\n          Service = \"ecs-tasks.amazonaws.com\"\n        }\n        Action = \"sts:As",
  "cost": {
    "context_tokens": 2530
  }
}

Fetch it by URL: GET /api/v1/registry/engineers-hub-ltd-in-house-project-eh-skills-iam-security/manifest?version=1.0.0

Reviews

Star ratings from people who tried it. One review per account; edit yours any time.

No reviews yet. Install it, try it, and be the first to rate it.