Use Cases

Claude Code × AWS IAM 완전 가이드 | 최소 권한 정책 자동 생성

Claude Code로 AWS IAM 정책 설계를 효율화하세요. 최소 권한 원칙에 기반한 정책 자동 생성, 기존 정책의 보안 리뷰, CDK 구현까지 실제 코드 예시로 해설합니다.

IAM 정책 설계는 “눈에 띄지 않지만 잘못되면 치명적인” 작업입니다. 와일드카드(*)를 남용한 과도한 권한 정책은 보안 사고의 온상이 됩니다. 그렇다고 처음부터 최소 권한 정책을 작성하는 것은 시간이 걸립니다.

저는 업무에서 Lambda, S3, DynamoDB, CloudWatch를 조합한 서버리스 아키텍처를 운영하고 있습니다. IAM 정책 설계 및 리뷰를 Claude Code에 맡김으로써 작업 시간을 약 70% 절감했습니다. 이 글에서는 실전적인 방법을 설명합니다.


IAM 기초를 3분 만에 복습

정책의 구조

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": "arn:aws:s3:::my-bucket/*",
      "Condition": {
        "StringEquals": {
          "s3:prefix": ["uploads/"]
        }
      }
    }
  ]
}
  • Effect: Allow (허용) 또는 Deny (거부)
  • Action: 허용/거부할 API 액션
  • Resource: 대상 리소스의 ARN (구체적으로 지정할수록 안전)
  • Condition: 추가 조건 (IP 주소 제한, MFA 필수 등)

최소 권한의 원칙

“필요한 작업만, 필요한 리소스에만 허용한다”가 IAM의 철칙입니다.

❌ 위험: 와일드카드 남용
Action: "*"
Resource: "*"

✅ 안전: 구체적으로 지정
Action: ["s3:GetObject", "s3:PutObject"]
Resource: "arn:aws:s3:::my-uploads-bucket/user-files/*"

Step 1: 용도를 설명하고 정책 자동 생성

Lambda용 정책 생성

claude -p "
다음 Lambda 함수에 필요한 최소 권한 IAM 정책을 JSON으로 생성해.

[Lambda 처리 내용]
- S3 버킷 'user-uploads-prod'에서 이미지 읽기
- 가공 후 'user-thumbnails-prod'에 저장
- DynamoDB 테이블 'image-metadata'에 결과 기록
- 오류 시 SNS 토픽 'alert-topic'에 알림
- CloudWatch Logs에 로그 출력

[제약 조건]
- 삭제 작업은 불필요
- 리소스 ARN은 계정ID 123456789012, 리전 ap-northeast-1 사용
- Lambda에는 IP 제한 Condition 불필요
"

생성 예시:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "S3ReadSourceBucket",
      "Effect": "Allow",
      "Action": ["s3:GetObject"],
      "Resource": "arn:aws:s3:::user-uploads-prod/*"
    },
    {
      "Sid": "S3WriteThumbnailBucket",
      "Effect": "Allow",
      "Action": ["s3:PutObject"],
      "Resource": "arn:aws:s3:::user-thumbnails-prod/*"
    },
    {
      "Sid": "DynamoDBWriteMetadata",
      "Effect": "Allow",
      "Action": ["dynamodb:PutItem", "dynamodb:UpdateItem"],
      "Resource": "arn:aws:dynamodb:ap-northeast-1:123456789012:table/image-metadata"
    },
    {
      "Sid": "SNSPublishAlert",
      "Effect": "Allow",
      "Action": ["sns:Publish"],
      "Resource": "arn:aws:sns:ap-northeast-1:123456789012:alert-topic"
    },
    {
      "Sid": "CloudWatchLogs",
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws/lambda/*"
    }
  ]
}

Step 2: 기존 정책의 보안 리뷰

기존 정책에 문제가 없는지 리뷰를 받습니다.

claude -p "
다음 IAM 정책을 보안 관점에서 리뷰해줘.
문제점과 수정안을 구체적으로 알려줘.

$(cat policy.json)
"

Claude Code가 지적하는 전형적인 문제:

문제1: Resource: "*"로 S3에 대한 전체 액세스 허용
  → 대상 버킷과 프리픽스를 구체적으로 지정할 것

문제2: s3:DeleteObject가 포함되어 있지만 처리상 불필요
  → 삭제 작업 제거

문제3: iam:PassRole을 와일드카드 리소스로 허용
  → PassRole은 특정 역할 ARN에만 허용할 것 (권한 상승 위험)

문제4: CloudTrail 활성화 확인이 안 됨
  → IAM 작업 로그 기록 활성화 권장

Step 3: 역할 설계 자동화

여러 서비스에 걸친 역할 설계

claude -p "
다음 아키텍처에서 필요한 IAM 역할을 모두 설계해:

[아키텍처]
- API Gateway → Lambda (인증)
- Lambda → DynamoDB (사용자 데이터 읽기/쓰기)
- Lambda → S3 (파일 저장)
- Lambda → SES (이메일 발송)
- CloudWatch Events → Lambda (배치 처리)
- CodePipeline → S3, CodeBuild, Lambda (배포)

[출력 형식]
- 각 역할의 이름과 용도
- 신뢰 정책 (Trust Policy)
- 액세스 정책
- AWS CDK의 TypeScript 코드로 출력
"

CDK로 역할 구현

// lib/iam-stack.ts
import * as cdk from "aws-cdk-lib";
import * as iam from "aws-cdk-lib/aws-iam";
import * as lambda from "aws-cdk-lib/aws-lambda";

export class IamStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Lambda 실행 역할 (최소 권한)
    const lambdaRole = new iam.Role(this, "ApiLambdaRole", {
      assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
      roleName: "api-lambda-role",
      description: "API Lambda용 최소 권한 역할",
    });

    // DynamoDB 읽기/쓰기 권한 (특정 테이블만)
    lambdaRole.addToPolicy(
      new iam.PolicyStatement({
        sid: "DynamoDBAccess",
        effect: iam.Effect.ALLOW,
        actions: [
          "dynamodb:GetItem",
          "dynamodb:PutItem",
          "dynamodb:UpdateItem",
          "dynamodb:Query",
        ],
        resources: [
          `arn:aws:dynamodb:${this.region}:${this.account}:table/users`,
          `arn:aws:dynamodb:${this.region}:${this.account}:table/users/index/*`,
        ],
      })
    );

    // S3 쓰기 권한 (특정 버킷·프리픽스만)
    lambdaRole.addToPolicy(
      new iam.PolicyStatement({
        sid: "S3FileUpload",
        effect: iam.Effect.ALLOW,
        actions: ["s3:PutObject", "s3:GetObject"],
        resources: [
          `arn:aws:s3:::user-files-${this.account}/uploads/*`,
        ],
      })
    );

    // SES 이메일 발송 권한
    lambdaRole.addToPolicy(
      new iam.PolicyStatement({
        sid: "SESSendEmail",
        effect: iam.Effect.ALLOW,
        actions: ["ses:SendEmail", "ses:SendRawEmail"],
        resources: ["*"],
        conditions: {
          StringEquals: {
            "ses:FromAddress": "[email protected]",
          },
        },
      })
    );

    // CloudWatch Logs 쓰기 권한
    lambdaRole.addManagedPolicy(
      iam.ManagedPolicy.fromAwsManagedPolicyName(
        "service-role/AWSLambdaBasicExecutionRole"
      )
    );
  }
}

Step 4: IAM Access Analyzer 결과 리뷰

AWS IAM Access Analyzer 출력을 Claude Code에 리뷰받습니다.

# Access Analyzer 결과 가져오기
aws accessanalyzer list-findings --analyzer-arn arn:aws:access-analyzer:... \
  --output json > analyzer-findings.json

claude -p "
다음 IAM Access Analyzer 검출 결과를 보고:
1. 심각도별 (HIGH/MEDIUM/LOW) 분류
2. 즉각 수정이 필요한 항목 특정
3. 각각의 수정 방법을 구체적으로 알려줘

$(cat analyzer-findings.json)
"

Step 5: SCP 설계 (조직 전체 가드레일)

AWS Organizations를 사용하는 경우 SCP (Service Control Policy)로 계정 전체의 가드레일을 설정합니다.

claude -p "
프로덕션 환경 AWS 계정에 적용할 SCP를 설계해.
다음 요건을 충족할 것:

1. ap-northeast-1 (도쿄)와 us-east-1 (버지니아) 이외 리전 비활성화
2. root 계정의 API 작업 금지
3. CloudTrail 비활성화·삭제 금지
4. GuardDuty 비활성화 금지
5. 특정 IAM 역할 ('OrganizationAdminRole') 변경 금지
"
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyUnsupportedRegions",
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:RequestedRegion": ["ap-northeast-1", "us-east-1"]
        },
        "ArnNotLike": {
          "aws:PrincipalARN": [
            "arn:aws:iam::*:role/OrganizationAdminRole"
          ]
        }
      }
    },
    {
      "Sid": "DenyCloudTrailModification",
      "Effect": "Deny",
      "Action": [
        "cloudtrail:DeleteTrail",
        "cloudtrail:StopLogging",
        "cloudtrail:UpdateTrail"
      ],
      "Resource": "*"
    }
  ]
}

함정 4선

1. iam:PassRole 과도한 권한

// ❌ 위험: 어떤 역할이든 전달 가능
{
  "Action": "iam:PassRole",
  "Resource": "*"
}

// ✅ 안전: 특정 역할만
{
  "Action": "iam:PassRole",
  "Resource": "arn:aws:iam::123456789:role/MyLambdaRole",
  "Condition": {
    "StringEquals": {
      "iam:PassedToService": "lambda.amazonaws.com"
    }
  }
}

2. 관리형 정책 남용

AmazonS3FullAccess 등의 AWS 관리형 정책은 편리하지만 필요 이상의 권한을 포함합니다. Lambda가 S3에서 읽기만 한다면 s3:GetObject만의 커스텀 정책을 사용하세요.

3. 인라인 정책과 커스텀 관리형 정책 혼용

인라인 정책은 재사용이 불가하고 추적이 어렵습니다. 관리형 정책으로 통일하고 버전 관리하세요.

4. Condition 없이 MFA 필수화 누락

관리자 역할로 전환할 때는 반드시 MFA를 필수로 설정합니다.

{
  "Condition": {
    "BoolIfExists": {
      "aws:MultiFactorAuthPresent": "true"
    }
  }
}

정리

작업Claude Code의 기여
정책 생성용도 설명만으로 최소 권한 정책 생성
보안 리뷰기존 정책의 문제점과 수정안 지적
역할 설계아키텍처 전체의 역할 구성 설계
CDK 구현TypeScript IAM 코드 자동 생성
SCP 설계조직 전체 가드레일 정책 생성

IAM은 “만들고 끝”이 아니라 정기적인 리뷰가 필요합니다. 월 1회 Claude Code에 기존 정책을 리뷰시키는 습관을 들이는 것만으로 보안 리스크를 지속적으로 낮출 수 있습니다.

관련 기사

참고 자료

#claude-code #aws #iam #security #typescript #infrastructure

Claude Code 워크플로우를 한 단계 업그레이드하세요

지금 바로 Claude Code에 복사해 쓸 수 있는 검증된 프롬프트 템플릿 50선.

무료 제공

무료 PDF: 5분 완성 Claude Code 치트시트

이메일 주소만 등록하시면 A4 한 장짜리 치트시트 PDF를 즉시 보내드립니다.

개인정보는 엄격하게 관리하며 스팸은 보내지 않습니다.

Masa

이 글을 작성한 사람

Masa

Claude Code를 적극 활용하는 엔지니어. 10개 언어, 2,000페이지 이상의 테크 미디어 claudecode-lab.com을 운영 중.