AWSTemplateFormatVersion: '2010-09-09' Description: 'AWS Quickstart: CloudFormation stack (nested) for creating an EC2 instance with Tableau Server installed (Amazon Linux 2). (qs-1puphiilb)' Metadata: cfn-lint: config: ignore_checks: - E9101 - W9002 - W9003 - W9006 AWS::CloudFormation::Interface: ParameterGroups: - Label: default: AWS Environment and Machine Configuration Parameters: - PublicSubnetId - InstanceType - LatestAmiId - KeyPairName - IamInstanceProfile - SecurityGroup - S3BucketName - TableauServerHostname - TableauServerTargetGroupArn - TsmTargetGroupArn - TableauServerLogGroup - Label: default: Secrets Parameters: - TableauServerAdminUser - TableauServerAdminPassword - TsmUsername - TsmPassword - LicenseKey - Label: default: Registration Parameters: - AcceptEULA - RegFirstName - RegLastName - RegEmail - RegCompany - RegTitle - RegDepartment - RegIndustry - RegPhone - RegCity - RegState - RegZip - RegCountry - RegCompanyEmployees - RegOptIn ParameterLabels: PublicSubnetId: default: Target Public Subnet InstanceType: default: EC2 instance type for Tableau Server LatestAmiId: default: Use the latest version of Amazon Linux 2 (don't change this) KeyPairName: default: Key Pair Name IamInstanceProfile: default: IAM Role/Profile for the EC2 instance SecurityGroup: default: Security Group for EC2 instance TableauServerHostname: default: FQDN or IP address for Tableau Server TableauServerTargetGroupArn: default: Load Balancer (Tableau Server) Target Group ARN TsmTargetGroupArn: default: Load Balancer (Tableau TSM) Target Group ARN S3BucketName: default: S3 bucket for Tableau Server files TableauServerAdminPassword: default: Tableau Server administrator password TableauServerAdminUser: default: Tableau Server administrator username TsmUsername: default: Tableau Services Manager (TSM) administrator username TsmPassword: default: Tableau Services Manager (TSM) administrator password LicenseKey: default: Tableau Activation Key AcceptEULA: default: Accept Tableau End User License Agreement TableauServerLogGroup: default: Cloudwatch log group name for Tableau Server logs RegCity: default: City RegCompany: default: Company RegCountry: default: Country RegDepartment: default: Department RegEmail: default: Email Address RegFirstName: default: First Name RegIndustry: default: Industry RegLastName: default: Last Name RegPhone: default: Phone RegState: default: State RegTitle: default: Title RegZip: default: Zip/Postal Code RegCompanyEmployees: default: 5 RegOptIn: default: true Parameters: AcceptEULA: AllowedPattern: 'yes' AllowedValues: - 'yes' - 'no' Description: 'View the EULA at the Link: https://www.tableau.com/eula' Type: String InstanceType: AllowedValues: - m6i.4xlarge - m6i.8xlarge - m6i.12xlarge - m5.4xlarge - m5.8xlarge - m5.12xlarge ConstraintDescription: must be a valid EC2 instance type. Default: m5.8xlarge Description: Amazon EC2 instance type Type: String KeyPairName: ConstraintDescription: must be the name of an existing EC2 KeyPair. Description: Name of an existing EC2 KeyPair to enable SSH access to the instances Type: AWS::EC2::KeyPair::KeyName S3BucketName: Description: Name of the S3 bucket for Tableau Server Type: String SecurityGroup: Description: Security Group for Tableau Server Type: AWS::EC2::SecurityGroup::Id TableauIAMRole: Description: Tableau server IAM role name Type: String IamInstanceProfile: Description: Tableau server IAM Instance Profile name Type: String PublicSubnetId: Description: The public subnet to use Type: AWS::EC2::Subnet::Id TableauServerHostname: Description: Fully Qualified Domain Name or IP address users should use to access Tableau Server Type: String TableauServerTargetGroupArn: Description: ARN for Tableau Server's load balancer target group Type: String TsmTargetGroupArn: Description: ARN for Tableau TSM's load balancer target group Type: String LatestAmiId: Description: Latest Amazon Linux 2 AMI ID from SSM Parameter Store. Type: 'AWS::SSM::Parameter::Value' Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2' TableauServerAdminPassword: Description: The password of the initial administrator for Tableau Server MinLength: '1' NoEcho: 'true' Type: String TableauServerAdminUser: Description: The name of the initial administrator for Tableau Server MinLength: '1' Type: String LicenseKey: Description: License Key (leave blank for trial) Type: String TsmUsername: AllowedPattern: ^(?!(tableau|tsmagent|admin|root)$)[A-Za-z0-9]+$ Description: Tableau Services Manager (TSM) administrator username (cannot be 'tableau' or 'tsmagent' or 'admin' or 'root') MaxLength: '30' Type: String TsmPassword: Description: Tableau Services Manager (TSM) administrator password MinLength: '6' NoEcho: 'true' Type: String TableauServerLogGroup: Description: Name of the Cloudwatch log group for Tableau Server Type: String RegCity: Description: City Type: String RegCompany: Description: Company Type: String RegCountry: Description: Country Type: String RegDepartment: Description: Department Type: String RegEmail: Description: Email MinLength: '1' Type: String RegFirstName: Description: First Name MinLength: '1' Type: String RegIndustry: Description: Industry Type: String RegLastName: Description: Last Name MinLength: '1' Type: String RegPhone: Description: Phone Type: String RegState: Description: State Type: String RegTitle: Description: Title Type: String RegZip: Description: ZIP/Postal Code Type: String RegCompanyEmployees: Description: Company Size (# of employees) Type: String RegOptIn: Description: Opt into marketing communications (true/false) Type: String QSS3BucketName: AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$ ConstraintDescription: Quick Start bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-). Default: aws-quickstart Description: S3 bucket name for the Quick Start assets. This string can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-). Type: String QSS3KeyPrefix: AllowedPattern: ^[0-9a-zA-Z-/]*$ ConstraintDescription: Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/). Default: quickstart-tableau-server/ Description: S3 key prefix for the Quick Start assets. Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/). Type: String QSS3BucketRegion: Default: 'us-east-1' Description: "The AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted. When using your own bucket, you must specify this value." Type: String Mappings: DefaultConfiguration: Aws: InstanceName: Tableau-Server-Linux S3BackupPrefix: backups/ InstallationConfig: TableauServerInstaller: https://www.tableau.com/downloads/server/rpm AutomatedInstaller: https://raw.githubusercontent.com/tableau/server-install-script-samples/master/linux/automated-installer/automated-installer PostgreSqlJdbc: https://downloads.tableau.com/drivers/linux/postgresql/postgresql-42.2.14.jar InstallationTimeout: PT1H45M MachineConfiguration: SystemVolumeSize: 100 AmazonLinux: AmazonLinux2 Name: Tableau Server Quickstart Conditions: UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-quickstart'] UsingElasticIp: !Equals - '' - !Ref TableauServerTargetGroupArn IsTrial: !Equals - '' - !Ref 'LicenseKey' Resources: TableauServerEC2: Type: AWS::EC2::Instance Metadata: AWS::CloudFormation::Authentication: S3AccessCreds: type: S3 roleName: !Ref TableauIAMRole buckets: - !If [ UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName ] - !Ref S3BucketName AWS::CloudFormation::Init: configSets: setup: - step1 - step2 - step3 - step4 - step5 - step6 - step7 step1: # Create files and local user needed for Tableau installation packages: rpm: epel-release: https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm yum: awscli: [] commands: a: command: hostnamectl set-hostname $(hostnamectl --static) && echo "Setting static hostname" b: command: source /tmp/secrets.properties && echo "sourcing the secrets to env variables" c: command: !Sub useradd ${TsmUsername} && echo "Creating TSM user" d: command: !Sub echo ${TsmPassword} | passwd ${TsmUsername} --stdin && echo "Setting password for TSM user" e: cwd: /tmp command: pip3 install -r requirements.txt files: /tmp/secrets.properties: mode: '640' content: !Sub | tsm_admin_user='${TsmUsername}' tsm_admin_pass='${TsmPassword}' tableau_server_admin_user='${TableauServerAdminUser}' tableau_server_admin_pass='${TableauServerAdminPassword}' /tmp/registration.json: content: first_name: !Ref 'RegFirstName' last_name: !Ref 'RegLastName' email: !Ref 'RegEmail' company: !Sub '${RegCompany};AWSQuickStart-Linux' title: !Ref 'RegTitle' department: !Ref 'RegDepartment' industry: !Ref 'RegIndustry' phone: !Ref 'RegPhone' city: !Ref 'RegCity' state: !Ref 'RegState' zip: !Ref 'RegZip' country: !Ref 'RegCountry' eula: !Ref 'AcceptEULA' company_employees: !Ref 'RegCompanyEmployees' opt_in: !Ref 'RegOptIn' /tmp/config.json: content: configEntities: gatewaySettings: _type: gatewaySettingsType port: 80 publicHost: !Ref TableauServerHostname publicPort: !If [UsingElasticIp, '80', '443'] identityStore: _type: identityStoreType type: local /tmp/tableau-server.rpm: source: !FindInMap - DefaultConfiguration - InstallationConfig - TableauServerInstaller /tmp/automated-installer: mode: '550' source: !FindInMap - DefaultConfiguration - InstallationConfig - AutomatedInstaller /tmp/update-load-balancer.py: mode: 550 source: !Sub - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}scripts/update-load-balancer.py - S3Region: !If [ UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion ] S3Bucket: !If [ UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName ] authentication: S3AccessCreds /tmp/backup-restore-s3.py: mode: 550 source: !Sub - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}scripts/backup-restore-s3.py - S3Region: !If [ UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion ] S3Bucket: !If [ UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName ] authentication: S3AccessCreds /tmp/requirements.txt: source: !Sub - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}scripts/requirements.txt - S3Region: !If [ UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion ] S3Bucket: !If [ UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName ] authentication: S3AccessCreds /opt/tableau/tableau_driver/jdbc/postgresql-42.2.22.jar: mode: '550' source: !FindInMap - DefaultConfiguration - InstallationConfig - PostgreSqlJdbc step2: # Some companies have strict tagging policies in AWS, modify the tagname and tagvalue attributes to apply tags to the EC2 instance + its root volume files: /tmp/tag-instance.py: mode: 550 source: !Sub - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}scripts/tag-instance.py - S3Region: !If [ UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion ] S3Bucket: !If [ UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName ] authentication: S3AccessCreds commands: a: cwd: /tmp command: !Join - ' ' - - 'python3' - tag-instance.py - --tagname - Name - --tagvalue - !FindInMap ["DefaultConfiguration", "Aws", "InstanceName"] - --region - !Ref AWS::Region b: cwd: /tmp command: !Sub python3 tag-instance.py --tagname Description --tagvalue TableauQuickstartResource --region ${AWS::Region} c: cwd: /tmp command: !Sub python3 tag-instance.py --tagname Creator --tagvalue tbinns@tableau.com --region ${AWS::Region} d: cwd: /tmp command: !Sub python3 tag-instance.py --tagname Group --tagvalue tbinns@tableau.com --region ${AWS::Region} e: cwd: /tmp command: !Sub python3 tag-instance.py --tagname DeptCode --tagvalue 429 --region ${AWS::Region} f: cwd: /tmp command: !Sub python3 tag-instance.py --tagname Environment --tagvalue Development --region ${AWS::Region} g: cwd: /tmp command: !Sub python3 tag-instance.py --tagname Application --tagvalue AwsQuickstart --region ${AWS::Region} step3: # Setup CloudWatch logging files: /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json: content: !Sub | { "logs": { "logs_collected": { "files": { "collect_list": [ { "file_path": "/var/opt/tableau/tableau_server/data/tabsvc/logs/tabadmincontroller/tabadmincontroller_node*.log", "log_group_name": "${TableauServerLogGroup}", "log_stream_name": "tsm.log", "timestamp_format": "%H: %M: %S%y%b%-d" }, { "file_path": "/var/opt/tableau/tableau_server/data/tabsvc/logs/httpd/access.*.log", "log_group_name": "${TableauServerLogGroup}", "log_stream_name": "apache-access.log", "timestamp_format": "%H: %M: %S%y%b%-d" }, { "file_path": "/var/opt/tableau/tableau_server/data/tabsvc/logs/httpd/error.log", "log_group_name": "${TableauServerLogGroup}", "log_stream_name": "apache-error.log", "timestamp_format": "%H: %M: %S%y%b%-d" } ] } } }, "metrics": { "append_dimensions": { "InstanceId": "${!aws:InstanceId}", "InstanceType": "${!aws:InstanceType}" }, "metrics_collected": { "mem": { "measurement": [ "mem_used_percent" ] }, "swap": { "measurement": [ "swap_used_percent" ] } } } } commands: a: command: rpm -Uvh https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm b: command: /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a stop c: command: /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json -s step4: # Install Tableau Server and restore from backups living in S3 (if available) commands: a: command: Fn::Sub: - /tmp/automated-installer -a ${TsmUsername} -f /tmp/config.json -r /tmp/registration.json -s /tmp/secrets.properties ${LicenseString} -v --accepteula --force /tmp/tableau-server.rpm && echo "Installing Tableau Server" - LicenseString: Fn::If: - IsTrial - '' - Fn::Sub: '-k ${LicenseKey}' b: cwd: /tmp command: !Join [' ', ['python3', 'backup-restore-s3.py', '--command', 'restore', '--region', !Ref AWS::Region, '--s3bucket', !Ref S3BucketName, '--s3prefix', !FindInMap ["DefaultConfiguration", "Aws", "S3BackupPrefix"] ] ] step5: # Make Tableau Server and TSM Web accessible via Load Balancers commands: a: cwd: /tmp command: !Join [' ', ['python3', 'update-load-balancer.py', '--target_group_arn', !Ref TableauServerTargetGroupArn, '--region', !Ref AWS::Region, '--stop_instances', "true" ] ] b: cwd: /tmp command: !Join [' ', ['python3', 'update-load-balancer.py', '--target_group_arn', !Ref TsmTargetGroupArn, '--region', !Ref AWS::Region, '--stop_instances', "false" ] ] step6: # Clean up the environment commands: a: command: rm -f /tmp/secrets.properties && echo "Cleanup secrets.properties" b: command: rm -f /tmp/config.json && echo "Cleanup config.json" c: command: rm -f /tmp/tableau-server.rpm && echo "Cleanup tableau-server.rpm" d: command: rm -f /tmp/automated-installer && echo "Cleanup automated-installer" e: command: rm -f /tmp/registration.json && echo "Cleanup registration.json" step7: # Schedule automated Tableau server backups to S3 files: /tmp/backup.sh: mode: 550 content: !Join [' ', ['python3', '/tmp/backup-restore-s3.py', '--command', 'backup', '--region', !Ref AWS::Region, '--s3bucket', !Ref S3BucketName, '--s3prefix', !FindInMap ["DefaultConfiguration", "Aws", "S3BackupPrefix"] ] ] /etc/cron.d/tableau-server-backup: mode: 644 content: | # Backup Tableau Server to S3, every sunday at 11pm SHELL=/bin/bash PATH=/sbin:/bin:/user/sbin:/usr/bin MAILTO=root 0 23 * * 0 root /tmp/backup.sh CreationPolicy: ResourceSignal: Timeout: !FindInMap ["DefaultConfiguration", "InstallationConfig", "InstallationTimeout"] Properties: SecurityGroupIds: - !Ref 'SecurityGroup' SubnetId: !Ref 'PublicSubnetId' InstanceType: !Ref 'InstanceType' IamInstanceProfile: !Ref 'IamInstanceProfile' ImageId: !Ref LatestAmiId BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeType: gp2 VolumeSize: !FindInMap - DefaultConfiguration - MachineConfiguration - SystemVolumeSize EbsOptimized: true KeyName: !Ref 'KeyPairName' Tags: - Key: Name Value: !FindInMap ["DefaultConfiguration", "Aws", "InstanceName"] UserData: Fn::Base64: !Sub | #!/bin/bash -xe # Install/upgrade OS and dependencies yum -y upgrade yum install -y aws-cfn-bootstrap # Run ConfigSet to install Tableau Server /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource TableauServerEC2 --configsets setup --region ${AWS::Region} # All done, tell cloudformation we are ready to go! /opt/aws/bin/cfn-signal --exit-code 0 --resource TableauServerEC2 --region ${AWS::Region} --stack ${AWS::StackName} Outputs: InstanceID: Description: EC2 InstanceID of the instance running Tableau Server Value: !Ref 'TableauServerEC2'