--- AWSTemplateFormatVersion: "2010-09-09" Description: |- CloudFormation service role to deploy github.com/sqlxpert/lights-off-aws/ GPLv3 Copyright Paul Marcelin Parameters: PlaceholderSuggestedStackName: Type: String Default: "LightsOffPrereq" PlaceholderSuggestedStackPolicyBody: Type: String Default: "https://github.com/sqlxpert/lights-off-aws/blob/main/cloudformation/lights_off_aws_prereq_policy.json?raw=true" PlaceholderHelp: Type: String Default: "github.com/sqlxpert/lights-off-aws#least-privilege-installation" PlaceholderAdvancedParameters: Type: String Default: "" AllowedValues: - "" StackNameLike: Type: String Description: >- When a stack is created using the deployment role, its name must match this StringLike/ArnLike pattern. Examples: "LightsOff" only allows a stack of that name; "LightsOff*" also allows stacks with names such as "LightsOff2" and "LightsOffTest"; and "StackSet-LightsOff-*" allows a StackSet named "LightsOff", whose StackSet instances receive names beginning "StackSet-LightsOff-". See https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html Default: "LightsOff*" Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: For Reference Parameters: - PlaceholderSuggestedStackName - PlaceholderSuggestedStackPolicyBody - PlaceholderHelp - Label: default: Advanced Options Parameters: - PlaceholderAdvancedParameters - Label: default: For stacks created using the deployment role... Parameters: - StackNameLike ParameterLabels: PlaceholderSuggestedStackName: default: Suggested stack name PlaceholderSuggestedStackPolicyBody: default: Copy suggested stack policy from PlaceholderHelp: default: For help with this stack, see PlaceholderAdvancedParameters: default: Do not change parameters below, unless necessary! StackNameLike: default: Allowed stack name pattern Resources: DeploymentRole: Type: AWS::IAM::Role Properties: Description: >- Resources in Lights Off CloudFormation stack: create, update, delete AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: { Service: cloudformation.amazonaws.com } Action: sts:AssumeRole # In-line policies apply only to this role, which, in turn, can only be # assumed by CloudFormation. Separate, "managed" policies could be # attached to other roles or users, allowing permission escalation. # Administrator should restrict iam:PassRole to prevent use of this role # with arbitrary CloudFormation stacks. Policies: - PolicyName: LightsOffCloudFormationStackDeploy PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - lambda:CreateFunction # Also iam:PassRole - lambda:GetFunction - lambda:DeleteFunction - lambda:UpdateFunctionCode - lambda:GetFunctionConfiguration - lambda:UpdateFunctionConfiguration - lambda:AddPermission - lambda:RemovePermission - lambda:PutFunctionConcurrency - lambda:DeleteFunctionConcurrency - lambda:PutFunctionEventInvokeConfig - lambda:ListFunctionEventInvokeConfigs - lambda:GetFunctionEventInvokeConfig - lambda:UpdateFunctionEventInvokeConfig - lambda:DeleteFunctionEventInvokeConfig - lambda:TagResource - lambda:UntagResource Resource: - !Sub "arn:${AWS::Partition}:lambda:*:${AWS::AccountId}:function:${StackNameLike}-*" - Effect: Allow Action: - lambda:CreateEventSourceMapping - lambda:GetEventSourceMapping - lambda:UpdateEventSourceMapping - lambda:DeleteEventSourceMapping Resource: "*" Condition: ArnLikeIfExists: "lambda:FunctionArn": - !Sub "arn:${AWS::Partition}:lambda:*:${AWS::AccountId}:function:${StackNameLike}-*" - Effect: Allow Action: - lambda:TagResource - lambda:UntagResource Resource: - !Sub "arn:${AWS::Partition}:lambda:*:${AWS::AccountId}:event-source-mapping:*" - Effect: Allow Action: - lambda:ListTags Resource: "*" - Effect: Allow Action: - logs:CreateLogGroup - logs:DeleteLogGroup - logs:PutRetentionPolicy - logs:DeleteRetentionPolicy - logs:AssociateKmsKey - logs:DisassociateKmsKey - logs:TagLogGroup - logs:TagResource - logs:UntagLogGroup - logs:UntagResource Resource: - !Sub "arn:${AWS::Partition}:logs:*:${AWS::AccountId}:log-group:${StackNameLike}-*" - Effect: Allow Action: - logs:DescribeLogGroups - logs:ListTagsForResource Resource: "*" - Effect: Allow Action: - scheduler:CreateScheduleGroup - scheduler:DeleteScheduleGroup - scheduler:TagResource - scheduler:UntagResource Resource: - !Sub "arn:${AWS::Partition}:scheduler:*:${AWS::AccountId}:schedule-group/${StackNameLike}-*" - Effect: Allow Action: - scheduler:CreateSchedule # Also iam:PassRole - scheduler:UpdateSchedule # Also iam:PassRole - scheduler:DeleteSchedule # Possible future requirement: - scheduler:TagResource - scheduler:UntagResource Resource: # Compare CloudWatch Logs and EventBridge Scheduler. # Right-trimming the stream name from a LogStream ARN gives # the ARN of the parent LogGroup, but unfortunately, # right-trimming the schedule name from a schedule ARN does # not give the ARN of the parent ScheduleGroup. # arn:PARTITION:logs:REGION:ACCOUNT:log-group:GROUP_NAME:log-stream:STREAM_NAME # arn:PARTITION:scheduler:REGION:ACCOUNT:schedule-group/GROUP_NAME # arn:PARTITION:scheduler:REGION:ACCOUNT:schedule/GROUP_NAME/SCHED_NAME - !Sub "arn:${AWS::Partition}:scheduler:*:${AWS::AccountId}:schedule/${StackNameLike}-*/*" - Effect: Allow Action: - scheduler:ListScheduleGroups - scheduler:GetScheduleGroup - scheduler:ListSchedules - scheduler:GetSchedule - scheduler:ListTagsForResource Resource: "*" # Left so that existing users can maintain, and eventually, # delete, the old EventBridge rule (replaced by an EventBridge # schedule) - Effect: Allow Action: - events:PutRule - events:DescribeRule - events:EnableRule - events:DisableRule - events:DeleteRule - events:PutTargets - events:ListTargetsByRule - events:RemoveTargets - events:TagResource - events:UntagResource Resource: - !Sub "arn:${AWS::Partition}:events:*:${AWS::AccountId}:rule/${StackNameLike}-*" - Effect: Allow Action: - events:ListTagsForResource Resource: "*" - Effect: Allow Action: - sqs:CreateQueue - sqs:SetQueueAttributes - sqs:GetQueueAttributes - sqs:GetQueueUrl - sqs:ListDeadLetterSourceQueues - sqs:DeleteQueue - sqs:AddPermission - sqs:RemovePermission - sqs:TagQueue - sqs:UntagQueue Resource: - !Sub "arn:${AWS::Partition}:sqs:*:${AWS::AccountId}:${StackNameLike}-*" - Effect: Allow Action: - sqs:ListQueues - sqs:ListQueueTags Resource: "*" - Effect: Allow Action: iam:PassRole Resource: - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${StackNameLike}-*" Condition: StringEquals: "iam:PassedToService": - lambda.amazonaws.com - scheduler.amazonaws.com ArnLike: "iam:AssociatedResourceArn": - !Sub "arn:${AWS::Partition}:lambda:*:${AWS::AccountId}:function:${StackNameLike}-*" - !Sub "arn:${AWS::Partition}:scheduler:*:${AWS::AccountId}:schedule/${StackNameLike}-*/${StackNameLike}-*" - Effect: Allow Action: - iam:CreateRole - iam:UpdateRoleDescription - iam:GetRole - iam:DeleteRole - iam:UpdateAssumeRolePolicy - iam:ListRolePolicies - iam:GetRolePolicy - iam:AttachRolePolicy - iam:DetachRolePolicy - iam:PutRolePolicy - iam:DeleteRolePolicy - iam:ListEntitiesForPolicy - iam:TagRole - iam:UntagRole Resource: - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${StackNameLike}-*" - Effect: Allow Action: - iam:ListRoles - iam:ListRoleTags - iam:ListAttachedRolePolicies Resource: "*" - Effect: Allow Action: - kms:ListKeys - kms:ListAliases - kms:ListResourceTags - kms:DescribeKey Resource: "*" SampleDeploymentRolePassRolePol: Type: "AWS::IAM::ManagedPolicy" Properties: Description: Fn::Sub: >- ${DeploymentRole}: pass to CloudFormation. Demonstrates a privilege that non-adminstrators need before they can create a Lights Off CloudFormation stack using the deployment role. PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: "iam:PassRole" Resource: !GetAtt DeploymentRole.Arn Outputs: DeploymentRoleName: Description: >- Name (not ARN) of the service role (deployment role) that will be used to deploy the Lights Off CloudFormation stack. Value: !Ref DeploymentRole