As you design your parameters, follow the guidelines outlined here.
Use parameters and setting defaults
Parameterize as much as possible, cover settings that you expect customers to customize. Some examples are CIDR blocks, FQDN names, host names, instance types, and storage volume sizes.
When setting defaults for instance types, make sure that the default is available in all or most of the AWS Regions. To check availability, refer to Amazon EC2 On-Demand Pricing.
Follow standards for naming, labeling, and grouping
For parameter names, labels, and groups, follow the guidelines in the Parameter naming standards section of this guide.
Use parameter types
Use AWS-specific parameter types as much as possible. This enables customers to pick values from a dropdown list. For details, refer to Parameters in the AWS CloudFormation documentation.
Use the DependsOn attribute
Use the DependsOn
attribute appropriately across resources to control the order of resource creation. Be mindful of the situation when a DependsOn attribute is required.
Add Partner Solution portability parameters
Include the standard parameters for the Partner Solution S3 bucket name, bucket Region, and key prefix.
Set the default value for the key prefix to quickstart-companyname-productname/, e.g., quickstart-microsoft-rdgateway/
.
...
QSS3BucketName:
AllowedPattern: ^[0-9a-z]+([0-9a-z-\.]*[0-9a-z])*$
ConstraintDescription:
The S3 bucket name can include numbers, lowercase letters,
and hyphens (-), but it cannot start or end with a hyphen.
Default: aws-quickstart
Description: >-
Name of the S3 bucket for your copy of the deployment assets. Keep the default
name unless you are customizing the template. Changing the name updates code
references to point to a new location.
MinLength: 3
MaxLength: 63
Type: String
QSS3BucketRegion:
Default: us-east-1
Description: >-
AWS Region where the S3 bucket (QSS3BucketName) is hosted. Keep
the default Region unless you are customizing the template. Changing the Region
updates code references to point to a new location. When using your own bucket,
specify the Region.
Type: String
QSS3KeyPrefix:
AllowedPattern: ^([0-9a-zA-Z!-_\.\*'\(\)/]+/)*$
ConstraintDescription:
The S3 key prefix can include numbers, lowercase letters, uppercase letters,
hyphens (-), underscores (_), periods (.), asterisks (*), single quotes ('),
open parenthesis ((), close parenthesis ()), and forward slashes (/). End the
prefix with a forward slash.
Default: quickstart-ibm-security-guardium-insights/
Description: >-
S3 key prefix that is used to simulate a folder for your copy of the
deployment assets. Keep the default prefix unless you are customizing
the template. Changing the prefix updates code references to point to
a new location.
Type: String
...
Use the AWS CloudFormation Fn::Sub function to join the Partner Solution S3 bucket name and key prefix values. Here’s an example using the short form (!Sub
):
TemplateURL: !Sub
- https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}templates/cribl-single-x86.workload.template.yaml
Use an IAM role with a policy that allows GetObject
to ${S3Bucket}/${QSS3KeyPrefix}*
. Example:
BastionHostRole:
Condition: CreateIAMRole
Type: AWS::IAM::Role
Properties:
Path: !If [RolePathProvided, !Ref RolePath, !Ref AWS::NoValue]
PermissionsBoundary:
!If [PermissionsBoundaryProvided, !Ref PermissionsBoundaryArn, !Ref AWS::NoValue]
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- !Sub ec2.${AWS::URLSuffix}
ManagedPolicyArns:
- !Sub arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore
- !Sub arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy
BastionHostPolicy:
Type: AWS::IAM::Policy
Condition: CreateIAMRole
Properties:
PolicyName: BastionPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: ListQSS3BucketObjects
Effect: Allow
Action: s3:ListBucket
Resource: !Sub
- arn:${AWS::Partition}:s3:::${S3Bucket}
- S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName]
- Sid: GetQSS3Objects
Effect: Allow
Action:
- s3:GetObject
- s3:GetObjectVersion
Resource: !Sub
- arn:${AWS::Partition}:s3:::${S3Bucket}/${QSS3KeyPrefix}*
- S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName]
- Sid: WriteToCloudWatchLogs
Effect: Allow
Action:
- logs:CreateLogStream
- logs:GetLogEvents
- logs:PutLogEvents
- logs:DescribeLogGroups
- logs:DescribeLogStreams
- logs:PutRetentionPolicy
- logs:PutMetricFilter
- logs:CreateLogGroup
Resource: !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:${BastionMainLogGroup}:*
- Sid: Global
Effect: Allow
Action:
- ec2:DescribeAddresses
Resource: '*'
- !If
- HasRemoteCIDR
- Sid: AssociateStackEips
Effect: Allow
Action:
- ec2:AssociateAddress
Resource: '*'
Condition:
StringEquals:
ec2:ResourceTag/aws:cloudformation:stack-id: !Ref AWS::StackId
- !Ref AWS::NoValue
Roles:
- !If [CreateIAMRole, !Ref BastionHostRole, !Ref AlternativeIAMRole]
Associate the instance with the instance profile from the IAM role. Here’s an example based on the previous code:
BastionLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Metadata:...
Properties:
LaunchTemplateData:
Placement:
Tenancy: !Ref BastionTenancy
KeyName: !If [UseKeyPair, !Ref KeyPairName, !Ref AWS::NoValue]
ImageId: !If
- UseOSImageOverride
- !Ref OSImageOverride
- !FindInMap [AWSAMIRegionMap, !Ref AWS::Region, !FindInMap [LinuxAMINameMap, !Ref BastionAMIOS, Code]]
InstanceType: !Ref BastionInstanceType
IamInstanceProfile:
Arn: !GetAtt BastionHostProfile.Arn
...
Use AWS CloudFormation authentication with the IAM role, and use cfn-init
to access resources. Example:
Metadata:
AWS::CloudFormation::Authentication:
S3AccessCreds:
type: S3
roleName: !If [CreateIAMRole, !Ref BastionHostRole, !Ref AlternativeIAMRole]
buckets:
- !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName]
AWS::CloudFormation::Init:
config:
files:
/tmp/auditd.rules:
mode: 000550
owner: root
group: root
content: |
-a exit,always -F arch=b64 -S execve
-a exit,always -F arch=b32 -S execve
/tmp/auditing_configure.sh:
source: !Sub
- https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}scripts/auditing_configure.sh
- S3Bucket: !If [UsingDefaultBucket, !Sub 'aws-quickstart-${AWS::Region}', !Ref QSS3BucketName]
S3Region: !If [UsingDefaultBucket, !Ref AWS::Region, !Ref QSS3BucketRegion]
mode: 000550
owner: root
group: root
authentication: S3AccessCreds
/tmp/bastion_bootstrap.sh:
source: !If
- UseAlternativeInitialization
- !Ref AlternativeInitializationScript
- !Sub
- https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}scripts/bastion_bootstrap.sh
- S3Bucket: !If [UsingDefaultBucket, !Sub 'aws-quickstart-${AWS::Region}', !Ref QSS3BucketName]
S3Region: !If [UsingDefaultBucket, !Ref AWS::Region, !Ref QSS3BucketRegion]
mode: 000550
owner: root
group: root
authentication: S3AccessCreds
commands:
a-add_auditd_rules:
cwd: /tmp/
env:
BASTION_OS: !FindInMap [LinuxAMINameMap, !Ref BastionAMIOS, OS]
command: ./auditing_configure.sh
b-bootstrap:
cwd: /tmp/
env:
REGION: !Sub ${AWS::Region}
URL_SUFFIX: !Sub ${AWS::URLSuffix}
BANNER_REGION: !If [UsingDefaultBucket, !Ref AWS::Region, !Ref QSS3BucketRegion]
command: !Sub
- ./bastion_bootstrap.sh --banner ${BannerUrl} --enable ${EnableBanner} --tcp-forwarding ${EnableTCPForwarding} --x11-forwarding ${EnableX11Forwarding}
- BannerUrl: !If
- DefaultBanner
- !Sub
- s3://${S3Bucket}/${QSS3KeyPrefix}scripts/banner_message.txt
- S3Bucket: !If [UsingDefaultBucket, !Sub 'aws-quickstart-${AWS::Region}', !Ref QSS3BucketName]
- !Ref BastionBanner
Properties:
LaunchTemplateData:
Placement:
Tenancy: !Ref BastionTenancy
KeyName: !If [UseKeyPair, !Ref KeyPairName, !Ref AWS::NoValue]
ImageId: !If
- UseOSImageOverride
- !Ref OSImageOverride
- !FindInMap [AWSAMIRegionMap, !Ref AWS::Region, !FindInMap [LinuxAMINameMap, !Ref BastionAMIOS, Code]]
InstanceType: !Ref BastionInstanceType
IamInstanceProfile:
Arn: !GetAtt BastionHostProfile.Arn
NetworkInterfaces:
- DeviceIndex: 0
AssociatePublicIpAddress: !If [HasRemoteCIDR, true, false]
Groups:
- !Ref BastionSecurityGroup
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeSize: !Ref RootVolumeSize
VolumeType: gp2
Encrypted: true
DeleteOnTermination: true
UserData:
Fn::Base64: !Sub
- |
#!/usr/bin/env bash
set -x
for e in $(echo "${EnvironmentVariables}" | tr ',' ' '); do
export $e
echo "$e" >> /root/.bashrc
done
export PATH=$PATH:/usr/local/bin
yum install -y git unzip wget curl || apt-get install -y git unzip wget curl || zypper -n install git unzip wget curl
#cfn signaling functions
cfn_fail() {
cfn-signal -e 1 --stack ${AWS::StackName} --region ${AWS::Region} --resource BastionAutoScalingGroup
exit 1
}
cfn_success() {
cfn-signal -e 0 --stack ${AWS::StackName} --region ${AWS::Region} --resource BastionAutoScalingGroup
exit 0
}
pushd /tmp
if [[ "a$(which aws)" == "a" ]]
then
echo "Installing AWS CLI..."
uname=$(uname -m)
wget -nv -O "./awscliv2.zip" "https://awscli.amazonaws.com/awscli-exe-linux-$uname.zip"
unzip -q ./awscliv2.zip
./aws/install
fi
until aws s3 cp --no-progress --region ${AWS::Region} "s3://${S3Bucket}/${QSS3KeyPrefix}scripts/cfn-tools.sh" .
do
echo "Retrying..."
done
source ./cfn-tools.sh
popd /tmp
qs_update-os || qs_err;
qs_bootstrap_pip || qs_err " pip bootstrap failed ";
qs_aws-cfn-bootstrap || qs_err " cfn bootstrap failed ";
EIP_LIST="${EIP1},${EIP2},${EIP3},${EIP4}"
CLOUDWATCHGROUP=${BastionMainLogGroup}
cfn-init -v --stack '${AWS::StackName}' --resource BastionLaunchTemplate --region ${AWS::Region} || cfn_fail
[ $(qs_status) == 0 ] && cfn_success || cfn_fail
- EIP1: !If [HasRemoteCIDR, !Ref EIP1, 'Null']
EIP2: !If [2BastionCondition, !Ref EIP2, 'Null']
EIP3: !If [3BastionCondition, !Ref EIP3, 'Null']
EIP4: !If [4BastionCondition, !Ref EIP4, 'Null']
S3Bucket: !If [UsingDefaultBucket, !Sub 'aws-quickstart-${AWS::Region}', !Ref QSS3BucketName]