AWSTemplateFormatVersion: "2010-09-09" Description: > Bootstrap IAM for the sh-openswe-traces app stack. Deployed ONCE, manually, under admin (CAPABILITY_NAMED_IAM). Creates two named roles so CI never touches the shared github-cfn-execution-role: - DeployRole (githubdeploy-sh-openswe-traces): assumed by this repo's GitHub Actions via OIDC (main branch only); can drive CloudFormation for THIS stack and pass the exec role. - ExecRole (sh-openswe-traces-cfn-exec-role): assumed by CloudFormation to create the app stack's resources; least-privilege to exactly this stack's resource set. Resources: ExecRole: Type: AWS::IAM::Role Properties: RoleName: sh-openswe-traces-cfn-exec-role Description: CloudFormation execution role for the sh-openswe-traces app stack. AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: cloudformation.amazonaws.com Action: sts:AssumeRole Condition: StringEquals: "aws:SourceAccount": !Ref "AWS::AccountId" Policies: - PolicyName: manage-sh-openswe-traces-resources PolicyDocument: Version: "2012-10-17" Statement: # Defense-in-depth: this role can create sh-openswe-traces-* users, so # explicitly forbid the actions that would turn one into a usable/escalated # principal (credentials, console login, extra policies, boundary removal). - Sid: DenyUserCredentialAndEscalation Effect: Deny Action: - "iam:CreateAccessKey" - "iam:CreateLoginProfile" - "iam:UpdateLoginProfile" - "iam:AttachUserPolicy" - "iam:CreateServiceSpecificCredential" - "iam:PutUserPermissionsBoundary" - "iam:DeleteUserPermissionsBoundary" Resource: "*" - Sid: Buckets Effect: Allow Action: - "s3:CreateBucket" - "s3:DeleteBucket" - "s3:PutBucketPolicy" - "s3:DeleteBucketPolicy" - "s3:GetBucketPolicy" - "s3:PutEncryptionConfiguration" - "s3:GetEncryptionConfiguration" - "s3:PutBucketVersioning" - "s3:GetBucketVersioning" - "s3:PutBucketPublicAccessBlock" - "s3:GetBucketPublicAccessBlock" - "s3:PutBucketOwnershipControls" - "s3:GetBucketOwnershipControls" - "s3:PutLifecycleConfiguration" - "s3:GetLifecycleConfiguration" - "s3:PutBucketLogging" - "s3:GetBucketLogging" - "s3:PutBucketTagging" - "s3:GetBucketTagging" - "s3:GetBucketLocation" - "s3:GetBucketAcl" Resource: - "arn:aws:s3:::sh-openswe-traces" - "arn:aws:s3:::sh-openswe-traces-logs" # CreateKey/CreateAlias cannot be resource-scoped (the key does not yet # exist). Only CloudFormation can assume this role, and only to deploy this # stack, so the blast radius is bounded to this stack's deployments. - Sid: Kms Effect: Allow Action: - "kms:CreateKey" - "kms:CreateAlias" - "kms:DeleteAlias" - "kms:UpdateAlias" - "kms:PutKeyPolicy" - "kms:GetKeyPolicy" - "kms:EnableKeyRotation" - "kms:DisableKeyRotation" - "kms:GetKeyRotationStatus" - "kms:DescribeKey" - "kms:TagResource" - "kms:UntagResource" - "kms:ListResourceTags" - "kms:ScheduleKeyDeletion" - "kms:EnableKey" Resource: "*" - Sid: Secret Effect: Allow Action: - "secretsmanager:CreateSecret" - "secretsmanager:DeleteSecret" - "secretsmanager:DescribeSecret" - "secretsmanager:UpdateSecret" - "secretsmanager:TagResource" - "secretsmanager:UntagResource" - "secretsmanager:GetResourcePolicy" - "secretsmanager:PutResourcePolicy" Resource: !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:sh-openswe/*" - Sid: ExportUser Effect: Allow Action: - "iam:CreateUser" - "iam:DeleteUser" - "iam:GetUser" - "iam:TagUser" - "iam:UntagUser" - "iam:PutUserPolicy" - "iam:DeleteUserPolicy" - "iam:GetUserPolicy" - "iam:ListUserPolicies" - "iam:ListUserTags" - "iam:ListAttachedUserPolicies" - "iam:ListGroupsForUser" Resource: !Sub "arn:aws:iam::${AWS::AccountId}:user/sh-openswe-traces-*" # Required because template.yaml uses Transform: AWS::Serverless-2016-10-31. # CloudFormation (as this exec role) must CreateChangeSet on the AWS-managed # SAM transform macro. Scoped to only that transform ARN. - Sid: SamTransform Effect: Allow Action: "cloudformation:CreateChangeSet" Resource: !Sub "arn:aws:cloudformation:${AWS::Region}:aws:transform/Serverless-2016-10-31" DeployRole: Type: AWS::IAM::Role Properties: RoleName: githubdeploy-sh-openswe-traces Description: GitHub Actions OIDC deploy role for the sh-openswe-traces app stack. MaxSessionDuration: 3600 AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Federated: !Sub "arn:aws:iam::${AWS::AccountId}:oidc-provider/token.actions.githubusercontent.com" Action: sts:AssumeRoleWithWebIdentity Condition: StringEquals: "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" "token.actions.githubusercontent.com:sub": "repo:Sea-Haven-Industries/sh-openswe-traces:ref:refs/heads/main" Policies: - PolicyName: deploy-sh-openswe-traces PolicyDocument: Version: "2012-10-17" Statement: - Sid: AppStack Effect: Allow Action: - "cloudformation:CreateChangeSet" - "cloudformation:ExecuteChangeSet" - "cloudformation:DescribeChangeSet" - "cloudformation:DeleteChangeSet" - "cloudformation:CreateStack" - "cloudformation:UpdateStack" - "cloudformation:DescribeStacks" - "cloudformation:DescribeStackEvents" - "cloudformation:DescribeStackResource" - "cloudformation:DescribeStackResources" - "cloudformation:ListStackResources" - "cloudformation:GetTemplate" - "cloudformation:GetTemplateSummary" Resource: - !Sub "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/sh-openswe-traces/*" - !Sub "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:changeSet/*/*" # SAM's --resolve-s3 looks up (does not recreate) the pre-existing managed # artifact stack + bucket. - Sid: SamManagedStackRead Effect: Allow Action: - "cloudformation:DescribeStacks" Resource: !Sub "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/aws-sam-cli-managed-default/*" - Sid: SamArtifactBucket Effect: Allow Action: - "s3:GetObject" - "s3:PutObject" - "s3:GetBucketLocation" - "s3:ListBucket" Resource: - "arn:aws:s3:::aws-sam-cli-managed-default-*" - "arn:aws:s3:::aws-sam-cli-managed-default-*/*" - Sid: CfnValidate Effect: Allow Action: - "cloudformation:ValidateTemplate" Resource: "*" - Sid: PassExecRoleToCfn Effect: Allow Action: "iam:PassRole" Resource: !GetAtt ExecRole.Arn Condition: StringEquals: "iam:PassedToService": cloudformation.amazonaws.com Outputs: DeployRoleArn: Description: Set as the repo secret AWS_DEPLOY_ROLE_ARN. Value: !GetAtt DeployRole.Arn ExecRoleArn: Description: Set as cfn-role-arn in .github/workflows/deploy.yaml. Value: !GetAtt ExecRole.Arn