AWSTemplateFormatVersion: 2010-09-09 Description: Create Prowler EC2 with UserData (Shell Scripts, & AWS CLI Profiles) Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Prowler EC2 Instance Settings Parameters: - BuildNumber - ProwlerEc2Name - InstanceType - KeyPair - SubnetId - VpcId - Ec2Role - LatestAmazonLinux2AmiId - ProwlerCron - Label: default: S3 Settings Parameters: - ProwlerS3 - ProwlerS3Account - Label: default: CrossAccount Role Parameters: - AwsOrgId - CrossAccountRole Parameters: BuildNumber: Type: String Description: Enter Build Number (increment with Updates for cfn-init) AllowedPattern: ^\d*$ ConstraintDescription: Build Number must be a numeric string. Default: 1 ProwlerEc2Name: Type: String Description: Enter Name for Prowler EC2 Instance to create AllowedPattern: ^[\w\s_.\/=+-]{1,128}$ ConstraintDescription: Max 128 alphanumeric characters. Also special characters supported [whitespace, _, ., /, =, +, -] Default: Prowler-EC2 InstanceType: Description: Enter Instance Type Type: String Default: t2.micro KeyPair: Description: Choose a KeyPair Type: AWS::EC2::KeyPair::KeyName SubnetId: Description: Choose Subnet Type: AWS::EC2::Subnet::Id VpcId: Description: Choose VPC Type: AWS::EC2::VPC::Id Ec2Role: Description: Enter Name for EC2 Instance Role to create and attach to Prowler EC2 Instance Type: String AllowedPattern: ^[\w+=,.@-]{1,64}$ ConstraintDescription: Max 64 alphanumeric characters. Also special characters supported [+, =, ., @, -] Default: ProwlerEC2-Role ProwlerCron: Description: Enter cron schedule. Default, runs everyday at 1am. See https://crontab.guru/, for syntax help. Type: String Default: "0 1 * * *" LatestAmazonLinux2AmiId: Type: AWS::SSM::Parameter::Value Description: Latest AMI ID for Amazon Linux 2 (via AWS Publis SSM Parameters. See https://tinyurl.com/aws-public-ssm-parameters. Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-ebs ProwlerS3: Type: String Description: Enter S3 Bucket for Prowler Reports. prefix-awsaccount-awsregion AllowedPattern: ^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$ ConstraintDescription: Max 63 characters. Can't start or end with dash. Can use numbers and lowercase letters. Default: prowler-123456789012-us-east-1 ProwlerS3Account: Type: String Description: Enter AWS Account Number where Prowler S3 Bucket resides. AllowedPattern: ^\d{12}$ ConstraintDescription: An AWS Account Number must be a 12 digit numeric string. Default: 123456789012 AwsOrgId: Type: String Description: Enter AWS Organizations ID AllowedPattern: ^o-[a-z0-9]{10,32}$ ConstraintDescription: The Org Id must be a 12 character string starting with o- and followed by 10 lower case alphanumeric characters. Default: o-abcde12345 CrossAccountRole: Type: String Description: Enter CrossAccount Role Prowler will be using to assess AWS Accounts in the AWS Organization. (ProwlerCrossAccountRole) AllowedPattern: ^[\w+=,.@-]{1,64}$ ConstraintDescription: Max 64 alphanumeric characters. Also special characters [+, =, ., @, -] Default: ProwlerXA-Role Resources: ProwlerEc2: Type: AWS::EC2::Instance CreationPolicy: ResourceSignal: Timeout: PT5M Properties: KeyName: !Ref KeyPair ImageId: !Ref LatestAmazonLinux2AmiId IamInstanceProfile: !Ref ProwlerInstanceProfile InstanceType: !Ref InstanceType SubnetId: !Ref SubnetId SecurityGroupIds: - !Ref ProwlerSecurityGroup BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: Encrypted: true KmsKeyId: alias/aws/ebs VolumeType: standard DeleteOnTermination: true VolumeSize: 8 Tags: - Key: Name Value: !Ref ProwlerEc2Name - Key: App Value: Prowler UserData: Fn::Base64: !Sub | #!/bin/bash yum update -y aws-cfn-bootstrap /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource ProwlerEc2 --configsets onfirstboot --region ${AWS::Region} yum -y update /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ProwlerEc2 --region ${AWS::Region} Metadata: AWS::CloudFormation::Authentication: S3AccessCreds: type: S3 buckets: - !Ref ProwlerS3 roleName: Ref: ProwlerEc2Role AWS::CloudFormation::Init: configSets: onfirstboot: - build-number - configure-cfn - prowler-prereqs - prowler-reports - prowler-schedule onupdate: - build-number - prowler-prereqs - prowler-reports - prowler-schedule build-number: commands: show-build-number: command: !Sub | echo "BUILDNUMBER: ${BuildNumber}" configure-cfn: files: /etc/cfn/hooks.d/cfn-auto-reloader.conf: content: !Sub | [cfn-auto-reloader-hook] triggers=post.update path=Resources.ProwlerEc2.Metadata.AWS::CloudFormation::Init action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource ProwlerEc2 --configsets onupdate --region ${AWS::Region} runas=root mode: "000400" owner: root group: root /etc/cfn/cfn-hup.conf: content: !Sub | [main] stack=${AWS::StackId} region=${AWS::Region} verbose=true interval=5 mode: "000400" owner: root group: root services: sysvinit: cfn-hup: enabled: true ensureRunning: true files: - /etc/cfn/cfn-hup.conf - /etc/cfn/hooks.d/cfn-auto-reloader.conf prowler-prereqs: files: /home/ec2-user/.awsvariables: content: !Sub | export S3=s3://${ProwlerS3} export S3ACCOUNT=${ProwlerS3Account} export ROLE=${CrossAccountRole} mode: "000600" owner: ec2-user group: ec2-user commands: 01-install-prowler-prereqs-yum: command: | yum install python-pip git jq -y 02-install-prowler-prereqs-pip: command: | sudo -u ec2-user pip install --user boto3 awscli detect-secrets prowler-reports: files: /home/ec2-user/run-prowler-reports.sh: source: !Sub https://${ProwlerS3}.s3.${AWS::Region}.amazonaws.com/run-prowler-reports.sh mode: "000700" owner: ec2-user group: ec2-user prowler-schedule: files: /home/ec2-user/mycron-prowler: content: !Sub | ${ProwlerCron} bash -lc ./run-prowler-reports.sh > mycron-prowler.log mode: "000600" owner: ec2-user group: ec2-user commands: 01-create-prowler-cron-job: command: | sudo -u ec2-user crontab /home/ec2-user/mycron-prowler ProwlerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: Prowler-EC2-RemoteAdministration GroupDescription: Allow Remote Administration Tags: - Key: App Value: Prowler VpcId: !Ref VpcId SecurityGroupEgress: - Description: Allow HTTP Outbound IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 - Description: Allow HTTPS Outbound IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 0.0.0.0/0 Metadata: cfn_nag: rules_to_suppress: - id: W5 reason: "Using http/https to Internet for updates." - id: W28 reason: "Using a defined Security Group Name." ProwlerInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Roles: - !Ref ProwlerEc2Role ProwlerEc2Role: Type: AWS::IAM::Role Properties: Description: Prowler EC2 Instance Role RoleName: !Ref Ec2Role Tags: - Key: App Value: Prowler AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole Policies: - PolicyName: SSM-Agent PolicyDocument: Version: 2012-10-17 Statement: - Sid: AllowSsmAgent Effect: Allow Resource: "*" Action: - ssm:UpdateInstanceInformation - ssm:ListInstanceAssociations - ssm:UpdateInstanceAssociationStatus - ssm:PutConfigurePackageResult - ssm:GetManifest - ssm:PutComplianceItems - ec2messages:AcknowledgeMessage - ec2messages:DeleteMessage - ec2messages:FailMessage - ec2messages:GetEndpoint - ec2messages:GetMessages - ec2messages:SendReply - PolicyName: SSM-Inventory PolicyDocument: Version: 2012-10-17 Statement: - Sid: AllowPutInventory Effect: Allow Resource: "*" Action: - ssm:PutInventory - Sid: AllowGatherInventory Effect: Allow Resource: !Sub arn:${AWS::Partition}:ssm:${AWS::Region}::document/AWS-GatherSoftwareInventory Action: - ssm:GetDocument - ssm:DescribeDocument - PolicyName: SSM-SessionManager PolicyDocument: Version: 2012-10-17 Statement: - Sid: AllowSessionManager Effect: Allow Resource: "*" Action: - ssmmessages:CreateControlChannel - ssmmessages:CreateDataChannel - ssmmessages:OpenControlChannel - ssmmessages:OpenDataChannel - PolicyName: Prowler-S3-Reports PolicyDocument: Version: 2012-10-17 Statement: - Sid: AllowGetPutListObject Effect: Allow Resource: - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3} - !Sub arn:${AWS::Partition}:s3:::${ProwlerS3}/* Action: - s3:GetObject - s3:PutObject - s3:ListBucket - s3:PutObjectAcl - PolicyName: Prowler-CrossAccount-AssumeRole PolicyDocument: Version: 2012-10-17 Statement: - Sid: AllowStsAssumeRole Effect: Allow Resource: !Sub arn:${AWS::Partition}:iam::*:role/${CrossAccountRole} Action: sts:AssumeRole Condition: StringEquals: aws:PrincipalOrgId: !Ref AwsOrgId Metadata: cfn_nag: rules_to_suppress: - id: W28 reason: "Using a defined Role Name." - id: W11 reason: "Needed for SSM features." Outputs: ProwlerEc2Account: Description: AWS Account Number where Prowler EC2 Instance resides. Value: !Ref AWS::AccountId ProwlerEc2Role: Description: Instance Role given to the Prowler EC2 Instance (needed to grant sts:AssumeRole rights). Value: !Ref ProwlerEc2Role ProwlerS3: Description: S3 Bucket for Prowler Reports Value: !Ref ProwlerS3