AWSTemplateFormatVersion: '2010-09-09' Description: >- This template deploys two Windows Server Failover Clustering (WSFC) and SQL Server AlwaysOn Availability Group nodes. This template is intended to be installed into an existing VPC that was built using the sample reference architecture outlined in "Implementing Active Directory Domain Services in the AWS Cloud" **WARNING** This template creates Amazon EC2 Windows instance and related resources. You will be billed for the AWS resources used if you create a stack from this template. (qs-1qup6rahc) Metadata: cfn-lint: config: ignore_checks: - E0002 - E9101 - W9004 - W9006 AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Network configuration Parameters: - ThirdAZ - VPCID - PrivateSubnet1ID - PrivateSubnet2ID - PrivateSubnet3ID - Label: default: Amazon EC2 configuration Parameters: - KeyPairName - Label: default: Active Directory configuration Parameters: - ADScenarioType - DomainDNSName - DomainNetBIOSName - DomainAdminUser - DomainAdminPassword - DomainMemberSGID - ADServer1PrivateIP - ADServer2PrivateIP - Label: default: SQL Server configuration Parameters: - SQLServerVersion - SQLServiceAccount - SQLServiceAccountPassword - SQLLicenseProvided - AvailabiltyGroupName - Volume1Size - Volume1Type - Volume1Iops - Volume2Size - Volume2Type - Volume2Iops - Volume3Size - Volume3Type - Volume3Iops - SQL2016Media - SQL2017Media - SQL2019Media - Label: default: Failover cluster configuration Parameters: - ClusterName - WitnessType - WSFCFileServerInstanceType - WSFCFileServerNetBIOSName - WSFCFileServerPrivateIP - WSFCNode1InstanceType - WSFCNode1NetBIOSName - WSFCNode1PrivateIP1 - WSFCNode1PrivateIP2 - WSFCNode1PrivateIP3 - WSFCNode2InstanceType - WSFCNode2NetBIOSName - WSFCNode2PrivateIP1 - WSFCNode2PrivateIP2 - WSFCNode2PrivateIP3 - WSFCNode3InstanceType - WSFCNode3NetBIOSName - WSFCNode3PrivateIP1 - WSFCNode3PrivateIP2 - WSFCNode3PrivateIP3 - Label: default: Application Insights configuration Parameters: - EnableAppInsights - ApplicationName - ResourceGroupName - Label: default: AWS Systems Manager AMI configuration Parameters: - WS2019FULLBASE - WS2019FULLSQL2019ENT - WS2019FULLSQL2017ENT - WS2019FULLSQL2016ENT - Label: default: AWS Quick Start configuration Parameters: - QSS3BucketName - QSS3BucketRegion - QSS3KeyPrefix ParameterLabels: ApplicationName: default: Application Insights application name EnableAppInsights: default: Enable Application Insights ResourceGroupName: default: Resource Group name ADScenarioType: default: AD scenario type ADServer1PrivateIP: default: Domain controller 1 IP address ADServer2PrivateIP: default: Domain controller 2 IP address AvailabiltyGroupName: default: Availability group name ClusterName: default: Cluster NetBIOS name WitnessType: default: File share witness type DomainAdminPassword: default: Domain admin password DomainAdminUser: default: Domain admin user name DomainDNSName: default: Domain DNS name DomainMemberSGID: default: Security group ID for AD domain members DomainNetBIOSName: default: Domain NetBIOS name KeyPairName: default: Key pair name PrivateSubnet1ID: default: Private subnet 1 ID PrivateSubnet2ID: default: Private subnet 2 ID PrivateSubnet3ID: default: Private subnet 3 ID QSS3BucketName: default: Quick Start S3 bucket name QSS3BucketRegion: default: Quick Start S3 bucket Region QSS3KeyPrefix: default: Quick Start S3 key prefix SQL2016Media: default: SQL Server 2016 installation media location SQL2017Media: default: SQL Server 2017 installation media location SQL2019Media: default: SQL Server 2019 installation media location SQLLicenseProvided: default: Amazon-provided SQL Server license SQLServerVersion: default: SQL Server version SQLServiceAccount: default: Service account name SQLServiceAccountPassword: default: Service account password ThirdAZ: default: Third Availability Zone Volume1Iops: default: Data volume IOPS Volume1Size: default: Data volume size Volume1Type: default: Data volume type Volume2Iops: default: Logs volume IOPS Volume2Size: default: Logs volume size Volume2Type: default: Logs volume type Volume3Iops: default: TempDB volume IOPS Volume3Size: default: TempDB volume size Volume3Type: default: TempDB volume type VPCID: default: VPC ID WSFCFileServerInstanceType: default: File server instance type WSFCFileServerNetBIOSName: default: File server NetBIOS name WSFCFileServerPrivateIP: default: File server private IP address WSFCNode1InstanceType: default: Instance type for cluster node 1 WSFCNode1NetBIOSName: default: Cluster node 1 NetBIOS name WSFCNode1PrivateIP1: default: Cluster node 1 private IP address 1 WSFCNode1PrivateIP2: default: Cluster node 1 private IP address 2 WSFCNode1PrivateIP3: default: Cluster node 1 private IP address 3 WSFCNode2InstanceType: default: Instance type for cluster node 2 WSFCNode2NetBIOSName: default: Cluster node 2 NetBIOS name WSFCNode2PrivateIP1: default: Cluster node 2 private IP address 1 WSFCNode2PrivateIP2: default: Cluster node 2 private IP address 2 WSFCNode2PrivateIP3: default: Cluster node 2 private IP address 3 WSFCNode3InstanceType: default: Instance type for cluster node 3 WSFCNode3NetBIOSName: default: Cluster node 3 NetBIOS name WSFCNode3PrivateIP1: default: Cluster node 3 private IP address 1 WSFCNode3PrivateIP2: default: Cluster node 3 private IP address 2 WSFCNode3PrivateIP3: default: Cluster node 3 private IP address 3 WS2019FULLBASE: default: Windows Server 2019 full base AMI WS2019FULLSQL2019ENT: default: Windows Server 2019 full locale English with SQL Enterprise 2019 AMI WS2019FULLSQL2017ENT: default: Windows Server 2019 Locale English with SQL Enterprise 2017 AMI WS2019FULLSQL2016ENT: default: Windows Server 2019 Locale English with SQL Enterprise 2016 AMI Parameters: ApplicationName: Default: MSSQL Description: Application Insights application name Type: String EnableAppInsights: Type: String Default: 'false' Description: Select whether to enable Application Insights AllowedValues: - 'true' - 'false' ResourceGroupName: Type: String Default: SQL Description: Application Resource Group name ADScenarioType: AllowedValues: - "AWS Directory Service for Microsoft AD (Enterprise Edition)" - "Microsoft AD on Amazon EC2" Default: AWS Directory Service for Microsoft AD (Enterprise Edition) Description: 'Select the type of AD DS deployment to use: AWS Directory Service for Microsoft AD or managing your own Amazon EC2 AD instances.' Type: String ADServer1PrivateIP: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ Default: 10.0.0.10 Description: The IP address of the first Domain Controller. For AWS Managed AD, use the first DNS address. Type: String ADServer2PrivateIP: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ Default: 10.0.32.10 Description: The IP address of the first Domain Controller. For AWS Managed AD, use the second DNS address. Type: String ClusterName: AllowedPattern: '[a-zA-Z0-9\-]+' Default: "WSFCCluster1" Description: "NetBIOS name of the cluster (up to 15 characters)." MaxLength: '15' MinLength: '1' Type: "String" AvailabiltyGroupName: AllowedPattern: '[a-zA-Z0-9\-]+' Default: "SQLAG1" Description: "NetBIOS name of the availability group (up to 15 characters)." MaxLength: '15' MinLength: '1' Type: "String" DomainAdminPassword: AllowedPattern: (?=^.{6,255}$)((?=.*\d)(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[^A-Za-z0-9])(?=.*[a-z])|(?=.*[^A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[A-Z])(?=.*[^A-Za-z0-9]))^.* Description: Password for the domain admin user. Must be at least 8 characters, containing letters, numbers, and symbols. MaxLength: '32' MinLength: '8' NoEcho: 'true' Type: String DomainAdminUser: AllowedPattern: '[a-zA-Z0-9]*' Default: Admin Description: User name for the account that will be used as domain administrator. This is separate from the default "Administrator" account. MaxLength: '25' MinLength: '5' Type: String DomainDNSName: AllowedPattern: '[a-zA-Z0-9\-]+\..+' Default: example.com Description: Fully qualified domain name (FQDN). MaxLength: '255' MinLength: '2' Type: String DomainMemberSGID: Description: ID of the domain member security group (e.g., sg-7f16e910). Type: AWS::EC2::SecurityGroup::Id DomainNetBIOSName: AllowedPattern: '[a-zA-Z0-9\-]+' Default: EXAMPLE Description: NetBIOS name of the domain (up to 15 characters) for users of earlier versions of Windows. MaxLength: '15' MinLength: '1' Type: String KeyPairName: Description: Public/private key pair, which allows you to securely connect to your instance after it launches. Type: AWS::EC2::KeyPair::KeyName PrivateSubnet1ID: Description: ID of the private subnet 1 in Availability Zone 1 (e.g., subnet-a0246dcd). Type: AWS::EC2::Subnet::Id PrivateSubnet2ID: Description: ID of the private subnet 2 in Availability Zone 2 (e.g., subnet-a0246dcd). Type: AWS::EC2::Subnet::Id PrivateSubnet3ID: Default: '' Description: (Optional) ID of the private subnet 3 in Availability Zone 3 (e.g., subnet-a0246dcd). Type: String QSS3BucketName: AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$ ConstraintDescription: The 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: Name of the S3 bucket for your copy of the Quick Start assets. Keep the default name unless you are customizing the template. Changing the name updates code references to point to a new Quick Start location. This name can include numbers, lowercase letters, uppercase letters, and hyphens, but do not start or end with a hyphen (-). See https://aws-quickstart.github.io/option1.html. Type: String QSS3BucketRegion: Default: us-east-1 Description: 'AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted. Keep the default Region unless you are customizing the template. Changing this Region updates code references to point to a new Quick Start location. When using your own bucket, specify the Region. See https://aws-quickstart.github.io/option1.html.' Type: String QSS3KeyPrefix: AllowedPattern: ^[0-9a-zA-Z-/]*$ ConstraintDescription: The Quick Start S3 key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slashes (/). The prefix should end with a forward slash (/). Default: quickstart-microsoft-sql/ Description: S3 key prefix that is used to simulate a directory for your copy of the Quick Start assets. Keep the default prefix unless you are customizing the template. Changing this prefix updates code references to point to a new Quick Start location. This prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slashes (/). End with a forward slash. See https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html and https://aws-quickstart.github.io/option1.html. Type: String SQL2016Media: Default: https://download.microsoft.com/download/9/0/7/907AD35F-9F9C-43A5-9789-52470555DB90/ENU/SQLServer2016SP1-FullSlipstream-x64-ENU.iso Description: SQL Server 2016 installation media location Type: String SQL2017Media: Default: https://download.microsoft.com/download/E/F/2/EF23C21D-7860-4F05-88CE-39AA114B014B/SQLServer2017-x64-ENU.iso Description: SQL Server 2017 installation media location Type: String SQL2019Media: Default: https://go.microsoft.com/fwlink/?linkid=866664 Description: SQL Server 2019 installation media location Type: String SQLLicenseProvided: AllowedValues: - 'yes' - 'no' Default: 'yes' Description: License SQL Server from AWS Marketplace. Type: String SQLServerVersion: AllowedValues: - '2016' - '2017' - '2019' Default: '2019' Description: Version of SQL Server to install on failover cluster nodes. Type: String SQLServiceAccount: AllowedPattern: '[a-zA-Z0-9]*' Default: sqlsa Description: User name for the SQL Server service account. This account is a domain user. MaxLength: '25' MinLength: '5' Type: String SQLServiceAccountPassword: AllowedPattern: (?=^.{6,255}$)((?=.*\d)(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[^A-Za-z0-9])(?=.*[a-z])|(?=.*[^A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[A-Z])(?=.*[^A-Za-z0-9]))^.* Description: Password for the SQL Server service account. Must be at least 8 characters, containing letters, numbers and symbols. MaxLength: '32' MinLength: '8' NoEcho: 'true' Type: String ThirdAZ: AllowedValues: - 'no' - witness - full Default: 'no' Description: Choose full or witness to enable a deployment with three Availability Zones. The third zone can be used for the witness, or it can be a full SQL cluster node. Type: String Volume1Iops: Default: '1000' Description: IOPS for the SQL Server data drive (only used when volume type is io1). MaxValue: '20000' MinValue: '100' Type: Number Volume1Size: Default: '500' Description: Volume size for the SQL Server data drive, in GiB. MaxValue: '16000' MinValue: '100' Type: Number Volume1Type: AllowedValues: - gp2 - io1 Default: gp2 Description: Volume type for the SQL Server data drive. Type: String Volume2Iops: Default: '1000' Description: IOPS for the SQL Server logs drive (only used when volume type is io1). MaxValue: '20000' MinValue: '100' Type: Number Volume2Size: Default: '500' Description: Volume size for the SQL Server logs drive, in GiB. MaxValue: '16000' MinValue: '100' Type: Number Volume2Type: AllowedValues: - gp2 - io1 Default: gp2 Description: Volume type for the SQL Server logs drive. Type: String Volume3Iops: Default: '1000' Description: IOPS for the SQL Server TempDB drive (only used when volume type is io1). MaxValue: '20000' MinValue: '100' Type: Number Volume3Size: Default: '500' Description: Volume size for the SQL Server TempDB drive, in GiB. MaxValue: '16000' MinValue: '100' Type: Number Volume3Type: AllowedValues: - gp2 - io1 Default: gp2 Description: Volume type for the SQL Server TempDB drive. Type: String VPCID: Description: ID of the VPC (e.g., vpc-0343606e). Type: AWS::EC2::VPC::Id WitnessType: AllowedValues: - FSx - Windows file share Default: Windows file share Description: Type of file share to use for failover cluster witness. Type: String WSFCFileServerInstanceType: AllowedValues: - t2.small - t2.medium - t2.large - t3.small - t3.medium - t3.large - m4.large - m5.large Default: m5.large Description: Amazon EC2 instance type for a file server used to share install media, witness, and replication folders. Type: String WSFCFileServerNetBIOSName: AllowedPattern: '[a-zA-Z0-9\-]+' Default: WSFCFileServer Description: NetBIOS name of the WSFC file server (up to 15 characters). MaxLength: '15' MinLength: '1' Type: String WSFCFileServerPrivateIP: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ Default: 10.0.0.200 Description: Primary private IP for the file server located in Availability Zone 1. Type: String WSFCNode1InstanceType: AllowedValues: - r4.xlarge - r4.2xlarge - r4.4xlarge - r4.8xlarge - r5.large - r5.xlarge - r5.2xlarge - r5.4xlarge - r5.12xlarge ConstraintDescription: Only Amazon EBS-optimized instance types are allowed. Default: r5.2xlarge Description: Amazon EC2 instance type for the first WSFC node. Type: String WSFCNode1NetBIOSName: AllowedPattern: '[a-zA-Z0-9\-]+' Default: WSFCNode1 Description: NetBIOS name of the first WSFC node (up to 15 characters). MaxLength: '15' MinLength: '1' Type: String WSFCNode1PrivateIP1: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ Default: 10.0.0.100 Description: Primary private IP for the first WSFC node located in Availability Zone 1. Type: String WSFCNode1PrivateIP2: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ Default: 10.0.0.101 Description: Secondary private IP for WSFC cluster on first WSFC node. Type: String WSFCNode1PrivateIP3: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ Default: 10.0.0.102 Description: Third private IP for availability group listener on first WSFC node. Type: String WSFCNode2InstanceType: AllowedValues: - r4.xlarge - r4.2xlarge - r4.4xlarge - r4.8xlarge - r5.large - r5.xlarge - r5.2xlarge - r5.4xlarge - r5.12xlarge ConstraintDescription: Only Amazon EBS-optimized instance types are allowed. Default: r5.2xlarge Description: Amazon EC2 instance type for the second WSFC node. Type: String WSFCNode2NetBIOSName: AllowedPattern: '[a-zA-Z0-9\-]+' Default: WSFCNode2 Description: NetBIOS name of the second WSFC node (up to 15 characters). MaxLength: '15' MinLength: '1' Type: String WSFCNode2PrivateIP1: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ Default: 10.0.32.100 Description: Primary private IP for the second WSFC node located in Availability Zone 2. Type: String WSFCNode2PrivateIP2: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ Default: 10.0.32.101 Description: Secondary private IP for WSFC cluster on second WSFC node. Type: String WSFCNode2PrivateIP3: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ Default: 10.0.32.102 Description: Third private IP for availability group listener on second WSFC node. Type: String WSFCNode3InstanceType: AllowedValues: - r4.xlarge - r4.2xlarge - r4.4xlarge - r4.8xlarge - r5.large - r5.xlarge - r5.2xlarge - r5.4xlarge - r5.12xlarge ConstraintDescription: Only Amazon EBS-optimized instance types are allowed. Default: r5.2xlarge Description: Amazon EC2 instance type for the third WSFC node. Type: String WSFCNode3NetBIOSName: AllowedPattern: '[a-zA-Z0-9\-]+' Default: WSFCNode3 Description: NetBIOS name of the third WSFC node (up to 15 characters). MaxLength: '15' MinLength: '1' Type: String WSFCNode3PrivateIP1: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ Default: 10.0.64.100 Description: Primary private IP for the optional third WSFC node located in Availability Zone 3. Type: String WSFCNode3PrivateIP2: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ Default: 10.0.64.101 Description: Secondary private IP for WSFC cluster on optional third WSFC node. Type: String WSFCNode3PrivateIP3: AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ Default: 10.0.64.102 Description: Third private IP for availability group listener on optional third WSFC node. Type: String WS2019FULLBASE: Type: 'AWS::SSM::Parameter::Value' Default: '/aws/service/ami-windows-latest/Windows_Server-2019-English-Full-Base' WS2019FULLSQL2019ENT: Type: 'AWS::SSM::Parameter::Value' Default: '/aws/service/ami-windows-latest/Windows_Server-2019-English-Full-SQL_2019_Enterprise' WS2019FULLSQL2017ENT: Type: 'AWS::SSM::Parameter::Value' Default: '/aws/service/ami-windows-latest/Windows_Server-2019-English-Full-SQL_2017_Enterprise' WS2019FULLSQL2016ENT: Type: 'AWS::SSM::Parameter::Value' Default: '/aws/service/ami-windows-latest/Windows_Server-2019-English-Full-SQL_2016_SP3_Enterprise' Conditions: EnableAppInsightForSQLHA: !Equals [!Ref 'EnableAppInsights', 'true'] UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-quickstart'] UseAWSDirectoryServiceEE: !Equals - !Ref 'ADScenarioType' - AWS Directory Service for Microsoft AD (Enterprise Edition) ThirdAzIsWitness: !Equals - !Ref 'ThirdAZ' - witness ThirdAzIsFullNode: !Equals - !Ref 'ThirdAZ' - full IsTwoNode: !Not - !Equals - !Ref 'ThirdAZ' - full Vol1IsIo1: !Equals - !Ref 'Volume1Type' - io1 Vol2IsIo1: !Equals - !Ref 'Volume2Type' - io1 Vol3IsIo1: !Equals - !Ref 'Volume2Type' - io1 SQLBakedInAMI: !Equals - !Ref 'SQLLicenseProvided' - 'yes' SQLVersion2016: !Equals - !Ref 'SQLServerVersion' - '2016' SQLVersion2017: !Equals - !Ref 'SQLServerVersion' - '2017' FSxWitness: !Equals - !Ref 'WitnessType' - 'FSx' Rules: SubnetsInVPC: Assertions: - Assert: !EachMemberIn - !ValueOfAll - AWS::EC2::Subnet::Id - VpcId - !RefAll 'AWS::EC2::VPC::Id' AssertDescription: All subnets must in the VPC Resources: ADAdminSecrets: Type: AWS::SecretsManager::Secret Properties: Name: !Sub 'AWSQuickStart/${AWS::StackName}/ADAdminSecrets' Description: MS SQL Credentials for Quick Start SecretString: !Sub '{ "username" : "${DomainAdminUser}", "password" : "${DomainAdminPassword}" }' SQLSecrets: Type: AWS::SecretsManager::Secret Properties: Name: !Sub 'AWSQuickStart/${AWS::StackName}/SQLSecrets' Description: MS SQL Credentials for Quick Start SecretString: !Sub '{ "username" : "${SQLServiceAccount}", "password" : "${SQLServiceAccountPassword}" }' AWSQuickstartMSSQLRole: Type: AWS::IAM::Role Metadata: cfn-lint: config: ignore_checks: - EIAMPolicyActionWildcard Properties: Policies: - PolicyDocument: Version: '2012-10-17' Statement: - Action: - s3:GetObject - s3:ListBucket Resource: - !Sub ['arn:${AWS::Partition}:s3:::${S3Bucket}/${QSS3KeyPrefix}*', S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName]] - !Sub ['arn:${AWS::Partition}:s3:::${S3Bucket}', S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName]] Effect: Allow PolicyName: aws-quick-start-s3-policy - PolicyDocument: Version: '2012-10-17' Statement: - Action: - s3:GetObject Resource: - !Sub 'arn:${AWS::Partition}:s3:::aws-ssm-${AWS::Region}/*' - !Sub 'arn:${AWS::Partition}:s3:::aws-windows-downloads-${AWS::Region}/*' - !Sub 'arn:${AWS::Partition}:s3:::amazon-ssm-${AWS::Region}/*' - !Sub 'arn:${AWS::Partition}:s3:::amazon-ssm-packages-${AWS::Region}/*' - !Sub 'arn:${AWS::Partition}:s3:::${AWS::Region}-birdwatcher-prod/*' - !Sub 'arn:${AWS::Partition}:s3:::patch-baseline-snapshot-${AWS::Region}/*' Effect: Allow PolicyName: ssm-custom-s3-policy - PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - cloudformation:SignalResource Resource: !Sub 'arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*' - Effect: Allow Action: - ec2:DescribeInstances - ec2:DescribeInstanceStatus - ssm:* Resource: '*' PolicyName: MSSQL-SSM-AutomationExecution Path: / AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Principal: Service: - ec2.amazonaws.com - ssm.amazonaws.com Effect: Allow Version: '2012-10-17' AWSQuickstartMSSQL: Type: AWS::SSM::Document Properties: DocumentType: Automation Content: schemaVersion: "0.3" description: Deploy MSSQL with SSM Automation # Role that is utilized to perform the steps within the Automation Document. assumeRole: "{{AutomationAssumeRole}}" # Gathering parameters needed to configure DCs in the Quick Start parameters: SQLServerVersion: default: "2017" description: "Version of SQL Server to install on Failover Cluster Nodes" type: "String" SQLLicenseProvided: default: "yes" description: "License SQL Server from AWS Marketplace" type: "String" SQL2016Media: description: "SQL Server 2016 installation media location" type: "String" SQL2017Media: description: "SQL Server 2017 installation media location" type: "String" SQL2019Media: description: "SQL Server 2019 installation media location" type: "String" URLSuffix: description: "AWS URL suffix" type: "String" WSFCNode1NetBIOSName: default: "WSFCNode1" description: "NetBIOS name of the first WSFC Node (up to 15 characters)" type: "String" WSFCNode1PrivateIP2: default: "10.0.0.101" description: "Secondary private IP for WSFC cluster on first WSFC Node" type: "String" WSFCNode1PrivateIP3: default: "10.0.0.102" description: "Third private IP for Availability Group Listener on first WSFC Node" type: "String" WSFCNode2NetBIOSName: default: "WSFCNode2" description: "NetBIOS name of the second WSFC Node (up to 15 characters)" type: "String" WSFCNode2PrivateIP2: default: "10.0.32.101" description: "Secondary private IP for WSFC cluster on first WSFC Node" type: "String" WSFCNode2PrivateIP3: default: "10.0.32.102" description: "Third private IP for Availability Group Listener on first WSFC Node" type: "String" FSXFileSystemID: default: "" description: "ID of the FSX File System to be used as a cluster witness" type: "String" WSFCNode3NetBIOSName: default: "WSFCNode3" description: "NetBIOS name of the third WSFC Node (up to 15 characters)" type: "String" WSFCNode3PrivateIP2: default: "10.0.64.101" description: "Fixed private IP for the first Active Directory server located in Availability Zone 1" type: "String" WSFCNode3PrivateIP3: default: "10.0.64.102" description: "Third private IP for Availability Group Listener on first WSFC Node" type: "String" WSFCFileServerNetBIOSName: default: "WSFCFileServer" description: "NetBIOS name of the WSFCFileServer (up to 15 characters)" type: "String" ClusterName: default: "WSFCCluster1" description: "NetBIOS name of the Cluster (up to 15 characters)" type: "String" AvailabiltyGroupName: default: "SQLAG1" description: "NetBIOS name of the Availablity Group (up to 15 characters)" type: "String" ThirdAZ: default: "no" description: "Enable a 3 AZ deployment, the 3rd AZ can either be used just for the witness, or can be a full SQL cluster node." type: "String" DomainDNSName: default: "example.com" description: "Fully qualified domain name (FQDN) of the forest root domain e.g. example.com" type: "String" DomainNetBIOSName: default: "example" description: "NetBIOS name of the domain (up to 15 characters) for users of earlier versions of Windows e.g. EXAMPLE" type: "String" ManagedAD: default: "No" description: "Active Directory being Managed by AWS" type: "String" AdminSecrets: description: "AWS Secrets Parameter Name that has Password and User name for a domain administrator." type: "String" SQLSecrets: description: "AWS Secrets Parameter Name that has Password and User namer for the SQL Service Account." type: "String" QSS3BucketName: default: "aws-quickstart" description: "S3 bucket name for the Quick Start assets. Quick Start bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-)." type: "String" QSS3KeyPrefix: default: "quickstart-microsoft-sql/" 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" StackName: default: "" description: "Stack Name Input for cfn resource signal" type: "String" AutomationAssumeRole: default: "" description: "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf." type: "String" WitnessType: default: "Windoes file share" description: "Failover cluster witness type" type: "String" mainSteps: - name: "wsfcNode1InstanceId" action: aws:executeAwsApi onFailure: "step:signalfailure" inputs: Service: ec2 Api: DescribeInstances Filters: - Name: "tag:Name" Values: ["{{WSFCNode1NetBIOSName}}"] - Name: "tag:aws:cloudformation:stack-name" Values: ["{{StackName}}"] - Name: "instance-state-name" Values: [ "running" ] outputs: - Name: InstanceId Selector: "$.Reservations[0].Instances[0].InstanceId" Type: String - name: "wsfcNode2InstanceId" action: aws:executeAwsApi onFailure: "step:signalfailure" inputs: Service: ec2 Api: DescribeInstances Filters: - Name: "tag:Name" Values: ["{{WSFCNode2NetBIOSName}}"] - Name: "tag:aws:cloudformation:stack-name" Values: ["{{StackName}}"] - Name: "instance-state-name" Values: [ "running" ] outputs: - Name: InstanceId Selector: "$.Reservations[0].Instances[0].InstanceId" Type: String - name: InstanceIdBranch action: aws:branch inputs: Choices: - Or: - Variable: "{{ThirdAZ}}" StringEquals: "no" - Variable: "{{ThirdAZ}}" StringEquals: "witness" NextStep: FSxBranch - Variable: "{{ThirdAZ}}" StringEquals: "full" NextStep: wsfcNode3InstanceId - name: "wsfcNode3InstanceId" action: aws:executeAwsApi onFailure: "step:signalfailure" inputs: Service: ec2 Api: DescribeInstances Filters: - Name: "tag:Name" Values: ["{{WSFCNode3NetBIOSName}}"] - Name: "tag:aws:cloudformation:stack-name" Values: ["{{StackName}}"] - Name: "instance-state-name" Values: [ "running" ] outputs: - Name: InstanceId Selector: "$.Reservations[0].Instances[0].InstanceId" Type: "String" - name: "wsfcnInstanceIds" action: aws:executeAwsApi onFailure: "step:signalfailure" inputs: Service: ec2 Api: DescribeInstances Filters: - Name: "tag:Name" Values: [ "{{WSFCNode1NetBIOSName}}","{{WSFCNode2NetBIOSName}}", "{{WSFCNode3NetBIOSName}}"] - Name: "tag:aws:cloudformation:stack-name" Values: ["{{StackName}}"] - Name: "instance-state-name" Values: [ "running" ] outputs: - Name: InstanceIds Selector: "$.Reservations..Instances..InstanceId" Type: "StringList" - name: "wsfcnInstallDscModules" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcnInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/install-sql-modules.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./install-sql-modules.ps1" - name: "wsfcnInitializeDisk" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: AWS-RunRemoteScript InstanceIds: - "{{wsfcnInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/Initialize-GPT.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./Initialize-GPT.ps1" - name: "wsfcnLCMConfig" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcnInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/LCM-Config.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./LCM-Config.ps1" - name: "wsfcnDomainJoin" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcnInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/DomainJoin.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./DomainJoin.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -DomainDNSName {{DomainDNSName}} -AdminSecret {{AdminSecrets}}" - name: "wsfcnDomainConfig" action: aws:runCommand onFailure: "step:signalfailure" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{wsfcnInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: - | function DscStatusCheck () { $LCMState = (Get-DscLocalConfigurationManager).LCMState if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') { 'returning 3010, should continue after reboot' exit 3010 } else { 'Completed' } } Start-DscConfiguration 'C:\AWSQuickstart\DomainJoin' -Wait -Verbose -Force DscStatusCheck - name: "Node1nMof" action: "aws:runCommand" onFailure: "step:signalfailure" #nextStep: inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/Node1Config.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./Node1Config.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -DomainDNSName {{DomainDNSName}} -WSFCNode1PrivateIP2 {{WSFCNode1PrivateIP2}} -ClusterName {{ClusterName}} -AdminSecret {{AdminSecrets}} -SQLSecret {{SQLSecrets}}" - name: "Node1nConfig" action: aws:runCommand onFailure: "step:signalfailure" nextStep: "Node2Mof" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: - | function DscStatusCheck () { $LCMState = (Get-DscLocalConfigurationManager).LCMState if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') { 'returning 3010, should continue after reboot' exit 3010 } else { 'Completed' } } Start-DscConfiguration 'C:\AWSQuickstart\WSFCNode1Config' -Wait -Verbose -Force DscStatusCheck - name: FSxBranch action: aws:branch inputs: Choices: - Variable: "{{WitnessType}}" StringEquals: "Windows file share" NextStep: wsfcFileServerInstanceId - Variable: "{{WitnessType}}" StringEquals: "FSx" NextStep: wsfcfInstanceIds - name: "wsfcFileServerInstanceId" action: aws:executeAwsApi onFailure: "step:signalfailure" inputs: Service: ec2 Api: DescribeInstances Filters: - Name: "tag:Name" Values: ["{{WSFCFileServerNetBIOSName}}"] - Name: "tag:aws:cloudformation:stack-name" Values: ["{{StackName}}"] - Name: "instance-state-name" Values: [ "running" ] outputs: - Name: InstanceId Selector: "$.Reservations[0].Instances[0].InstanceId" Type: "String" - name: "wsfcwInstanceIds" action: aws:executeAwsApi onFailure: "step:signalfailure" inputs: Service: ec2 Api: DescribeInstances Filters: - Name: "tag:Name" Values: [ "{{WSFCNode1NetBIOSName}}","{{WSFCNode2NetBIOSName}}", "{{WSFCFileServerNetBIOSName}}"] - Name: "tag:aws:cloudformation:stack-name" Values: ["{{StackName}}"] - Name: "instance-state-name" Values: [ "running" ] outputs: - Name: InstanceIds Selector: "$.Reservations..Instances..InstanceId" Type: "StringList" - name: "wsfcwInstallDscModules" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcwInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/install-sql-modules.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./install-sql-modules.ps1" - name: "wsfcnodewInitializeDisk" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: AWS-RunRemoteScript InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" - "{{wsfcNode2InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/Initialize-GPT.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./Initialize-GPT.ps1" - name: "wsfcwLCMConfig" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcwInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/LCM-Config.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./LCM-Config.ps1" - name: "wsfcwDomainJoin" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcwInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/DomainJoin.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./DomainJoin.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -DomainDNSName {{DomainDNSName}} -AdminSecret {{AdminSecrets}}" - name: "wsfcwDomainConfig" action: aws:runCommand onFailure: "step:signalfailure" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{wsfcwInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: - | function DscStatusCheck () { $LCMState = (Get-DscLocalConfigurationManager).LCMState if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') { 'returning 3010, should continue after reboot' exit 3010 } else { 'Completed' } } Start-DscConfiguration 'C:\AWSQuickstart\DomainJoin' -Wait -Verbose -Force DscStatusCheck - name: "wsfcFileServerConfig" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcFileServerInstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/WSFCFileShare.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./WSFCFileShare.ps1" - name: "Node1wMof" action: "aws:runCommand" onFailure: "step:signalfailure" nextStep: Node1wConfig inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/Node1Config.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./Node1Config.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -DomainDNSName {{DomainDNSName}} -WSFCNode1PrivateIP2 {{WSFCNode1PrivateIP2}} -ClusterName {{ClusterName}} -AdminSecret {{AdminSecrets}} -SQLSecret {{SQLSecrets}} -FileServerNetBIOSName {{WSFCFileServerNetBIOSName}}" - name: "wsfcfInstanceIds" action: aws:executeAwsApi onFailure: "step:signalfailure" inputs: Service: ec2 Api: DescribeInstances Filters: - Name: "tag:Name" Values: [ "{{WSFCNode1NetBIOSName}}","{{WSFCNode2NetBIOSName}}"] - Name: "tag:aws:cloudformation:stack-name" Values: ["{{StackName}}"] - Name: "instance-state-name" Values: [ "running" ] outputs: - Name: InstanceIds Selector: "$.Reservations..Instances..InstanceId" Type: "StringList" - name: "wsfcfInstallDscModules" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcfInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/install-sql-modules.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./install-sql-modules.ps1" - name: "wsfcnodefInitializeDisk" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: AWS-RunRemoteScript InstanceIds: - "{{wsfcfInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/Initialize-GPT.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./Initialize-GPT.ps1" - name: "wsfcfLCMConfig" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcfInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/LCM-Config.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./LCM-Config.ps1" - name: "wsfcfDomainJoin" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcfInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/DomainJoin.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./DomainJoin.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -DomainDNSName {{DomainDNSName}} -AdminSecret {{AdminSecrets}}" - name: "wsfcfDomainConfig" action: aws:runCommand onFailure: "step:signalfailure" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{wsfcfInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: - | function DscStatusCheck () { $LCMState = (Get-DscLocalConfigurationManager).LCMState if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') { 'returning 3010, should continue after reboot' exit 3010 } else { 'Completed' } } Start-DscConfiguration 'C:\AWSQuickstart\DomainJoin' -Wait -Verbose -Force DscStatusCheck - name: "Node1fMof" action: "aws:runCommand" onFailure: "step:signalfailure" nextStep: Node1wConfig inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/Node1Config.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./Node1Config.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -DomainDNSName {{DomainDNSName}} -WSFCNode1PrivateIP2 {{WSFCNode1PrivateIP2}} -ClusterName {{ClusterName}} -AdminSecret {{AdminSecrets}} -SQLSecret {{SQLSecrets}} -FSXFileSystemID {{FSXFileSystemID}}" - name: "Node1wConfig" action: aws:runCommand onFailure: "step:signalfailure" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: - | function DscStatusCheck () { $LCMState = (Get-DscLocalConfigurationManager).LCMState if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') { 'returning 3010, should continue after reboot' exit 3010 } else { 'Completed' } } Start-DscConfiguration 'C:\AWSQuickstart\WSFCNode1Config' -Wait -Verbose -Force DscStatusCheck - name: "Node2Mof" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcNode2InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/AdditionalNodeConfig.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./AdditionalNodeConfig.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -WSFCNodePrivateIP2 {{WSFCNode2PrivateIP2}} -ClusterName {{ClusterName}} -AdminSecret {{AdminSecrets}} -SQLSecret {{SQLSecrets}}" - name: "Node2Config" action: aws:runCommand onFailure: "step:signalfailure" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{wsfcNode2InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: - | function DscStatusCheck () { $LCMState = (Get-DscLocalConfigurationManager).LCMState if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') { 'returning 3010, should continue after reboot' exit 3010 } else { 'Completed' } } Start-DscConfiguration 'C:\AWSQuickstart\AdditionalWSFCNode' -Wait -Verbose -Force DscStatusCheck - name: 3AZFullBranch action: aws:branch inputs: Choices: - Or: - Variable: "{{ThirdAZ}}" StringEquals: "no" - Variable: "{{ThirdAZ}}" StringEquals: "witness" NextStep: SqlInstallBranch - Variable: "{{ThirdAZ}}" StringEquals: "full" NextStep: Node3Mof - name: "Node3Mof" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcNode3InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/AdditionalNodeConfig.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./AdditionalNodeConfig.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -WSFCNodePrivateIP2 {{WSFCNode3PrivateIP2}} -ClusterName {{ClusterName}} -AdminSecret {{AdminSecrets}} -SQLSecret {{SQLSecrets}}" - name: "Node3Config" action: aws:runCommand onFailure: "step:signalfailure" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{wsfcNode3InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: - | function DscStatusCheck () { $LCMState = (Get-DscLocalConfigurationManager).LCMState if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') { 'returning 3010, should continue after reboot' exit 3010 } else { 'Completed' } } Start-DscConfiguration 'C:\AWSQuickstart\AdditionalWSFCNode' -Wait -Verbose -Force DscStatusCheck - name: SqlInstallBranch action: aws:branch inputs: Choices: - Variable: "{{SQLLicenseProvided}}" StringEquals: "no" NextStep: NodesSqlInstallBranch - Variable: "{{SQLLicenseProvided}}" StringEquals: "yes" NextStep: NodesReconfigureSQLBranch - name: NodesSqlInstallBranch action: aws:branch inputs: Choices: - Or: - Variable: "{{ThirdAZ}}" StringEquals: "no" - Variable: "{{ThirdAZ}}" StringEquals: "witness" NextStep: 2NodeDownloadSQL - Variable: "{{ThirdAZ}}" StringEquals: "full" NextStep: 3NodeDownloadSQL - name: "2NodeDownloadSQL" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" - "{{wsfcNode2InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/DownloadSQLEE.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./DownloadSQLEE.ps1 -SQLServerVersion {{SQLServerVersion}} -SQL2016Media {{SQL2016Media}} -SQL2017Media {{SQL2017Media}} -SQL2019Media {{SQL2019Media}}" - name: "2NodeSQLInstallMOF" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" - "{{wsfcNode2InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/Install-SQLEE.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./Install-SQLEE.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -DomainDNSName {{DomainDNSName}} -AdminSecret {{AdminSecrets}} -SQLServerVersion {{SQLServerVersion}} -SQLSecret {{SQLSecrets}}" - name: "2NodeSQLInstall" action: aws:runCommand onFailure: "step:signalfailure" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" - "{{wsfcNode2InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: - | function DscStatusCheck () { $LCMState = (Get-DscLocalConfigurationManager).LCMState if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') { 'returning 3010, should continue after reboot' exit 3010 } else { 'Completed' } } Start-DscConfiguration 'C:\AWSQuickstart\SQLInstall' -Wait -Verbose -Force DscStatusCheck - name: "2NodeInstallSSMS" action: aws:runCommand onFailure: "step:signalfailure" nextStep: "CreateAGBranch" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" - "{{wsfcNode2InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: - | $ssms = "C:\SQLMedia\SSMS-Setup-ENU.exe" $ssmsargs = "/quiet /norestart" Start-Process $ssms $ssmsargs -Wait -ErrorAction Stop - name: "3NodeDownloadSQL" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcnInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/DownloadSQLEE.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./DownloadSQLEE.ps1 -SQLServerVersion {{SQLServerVersion}} -SQL2016Media {{SQL2016Media}} -SQL2017Media {{SQL2017Media}} -SQL2019Media {{SQL2019Media}}" - name: "3NodeSQLInstallMOF" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcnInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/Install-SQLEE.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./Install-SQLEE.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -DomainDNSName {{DomainDNSName}} -AdminSecret {{AdminSecrets}} -SQLServerVersion {{SQLServerVersion}} -SQLSecret {{SQLSecrets}}" - name: "3NodeSQLInstall" action: aws:runCommand onFailure: "step:signalfailure" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{wsfcnInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: - | function DscStatusCheck () { $LCMState = (Get-DscLocalConfigurationManager).LCMState if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') { 'returning 3010, should continue after reboot' exit 3010 } else { 'Completed' } } Start-DscConfiguration 'C:\AWSQuickstart\SQLInstall' -Wait -Verbose -Force DscStatusCheck - name: "3NodeInstallSSMS" action: aws:runCommand onFailure: "step:signalfailure" nextStep: "CreateAGBranch" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{wsfcnInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: - | $ssms = "C:\sqlinstall\SSMS-Setup-ENU.exe" $ssmsargs = "/quiet /norestart" Start-Process $ssms $ssmsargs -Wait -ErrorAction Stop - name: NodesReconfigureSQLBranch action: aws:branch inputs: Choices: - Or: - Variable: "{{ThirdAZ}}" StringEquals: "no" - Variable: "{{ThirdAZ}}" StringEquals: "witness" NextStep: 2NodeReconfigureSQL - Variable: "{{ThirdAZ}}" StringEquals: "full" NextStep: 3NodeReconfigureSQL - name: "2NodeReconfigureSQL" action: "aws:runCommand" onFailure: "step:signalfailure" nextStep: "CreateAGBranch" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" - "{{wsfcNode2InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/Reconfigure-SQL-DSC.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./Reconfigure-SQL-DSC.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -AdminSecret {{AdminSecrets}} -SQLSecret {{SQLSecrets}}" - name: "3NodeReconfigureSQL" action: "aws:runCommand" onFailure: "step:signalfailure" nextStep: "CreateAGBranch" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcnInstanceIds.InstanceIds}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/Reconfigure-SQL-DSC.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./Reconfigure-SQL-DSC.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -AdminSecret {{AdminSecrets}} -SQLSecret {{SQLSecrets}}" - name: CreateAGBranch action: aws:branch inputs: Choices: - And: - Not: Variable: "{{ManagedAD}}" StringEquals: "yes" - Not: Variable: "{{ThirdAZ}}" StringEquals: "full" NextStep: 2NodeNoMadPrimaryCreateAG - And: - Variable: "{{ManagedAD}}" StringEquals: "yes" - Not: Variable: "{{ThirdAZ}}" StringEquals: "full" NextStep: 2NodeMadPrimaryCreateAG - And: - Not: Variable: "{{ManagedAD}}" StringEquals: "yes" - Variable: "{{ThirdAZ}}" StringEquals: "full" NextStep: 3NodeNoMadPrimaryCreateAG - And: - Variable: "{{ManagedAD}}" StringEquals: "yes" - Variable: "{{ThirdAZ}}" StringEquals: "full" NextStep: 3NodeMadPrimaryCreateAG - name: "2NodeNoMadPrimaryCreateAG" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/CreateAGNode1.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./CreateAGNode1.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -DomainDNSName {{DomainDNSName}} -AdminSecret {{AdminSecrets}} -SQLSecret {{SQLSecrets}} -ClusterName {{ClusterName}} -AvailabiltyGroupName {{AvailabiltyGroupName}} -WSFCNode1NetBIOSName {{WSFCNode1NetBIOSName}} -WSFCNode2NetBIOSName {{WSFCNode2NetBIOSName}} -AGListener1PrivateIP1 {{WSFCNode1PrivateIP3}} -AGListener1PrivateIP2 {{WSFCNode2PrivateIP3}}" - name: "2NodeNoMadPrimaryCreateAGConfig" action: aws:runCommand onFailure: "step:signalfailure" nextStep: "AdditionalCreateAGBranch" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: - | function DscStatusCheck () { $LCMState = (Get-DscLocalConfigurationManager).LCMState if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') { 'returning 3010, should continue after reboot' exit 3010 } else { 'Completed' } } Start-DscConfiguration 'C:\AWSQuickstart\AddAG' -Wait -Verbose -Force DscStatusCheck - name: "2NodeMadPrimaryCreateAG" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/CreateAGNode1.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./CreateAGNode1.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -DomainDNSName {{DomainDNSName}} -AdminSecret {{AdminSecrets}} -SQLSecret {{SQLSecrets}} -ClusterName {{ClusterName}} -AvailabiltyGroupName {{AvailabiltyGroupName}} -WSFCNode1NetBIOSName {{WSFCNode1NetBIOSName}} -WSFCNode2NetBIOSName {{WSFCNode2NetBIOSName}} -AGListener1PrivateIP1 {{WSFCNode1PrivateIP3}} -AGListener1PrivateIP2 {{WSFCNode2PrivateIP3}} -ManagedAD 'Yes'" - name: "2NodeMadPrimaryCreateAGConfig" action: aws:runCommand onFailure: "step:signalfailure" nextStep: "AdditionalCreateAGBranch" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: - | function DscStatusCheck () { $LCMState = (Get-DscLocalConfigurationManager).LCMState if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') { 'returning 3010, should continue after reboot' exit 3010 } else { 'Completed' } } Start-DscConfiguration 'C:\AWSQuickstart\AddAG' -Wait -Verbose -Force DscStatusCheck - name: "3NodeNoMadPrimaryCreateAG" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/CreateAGNode1.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./CreateAGNode1.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -DomainDNSName {{DomainDNSName}} -AdminSecret {{AdminSecrets}} -SQLSecret {{SQLSecrets}} -ClusterName {{ClusterName}} -AvailabiltyGroupName {{AvailabiltyGroupName}} -WSFCNode1NetBIOSName {{WSFCNode1NetBIOSName}} -WSFCNode2NetBIOSName {{WSFCNode2NetBIOSName}} -AGListener1PrivateIP1 {{WSFCNode1PrivateIP3}} -AGListener1PrivateIP2 {{WSFCNode2PrivateIP3}} -WSFCNode3NetBIOSName {{WSFCNode3NetBIOSName}} -AGListener1PrivateIP3 {{WSFCNode3PrivateIP3}}" - name: "3NodeNoMadPrimaryCreateAGConfig" action: aws:runCommand onFailure: "step:signalfailure" nextStep: "AdditionalCreateAGBranch" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: - | function DscStatusCheck () { $LCMState = (Get-DscLocalConfigurationManager).LCMState if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') { 'returning 3010, should continue after reboot' exit 3010 } else { 'Completed' } } Start-DscConfiguration 'C:\AWSQuickstart\AddAG' -Wait -Verbose -Force DscStatusCheck - name: "3NodeMadPrimaryCreateAG" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/CreateAGNode1.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./CreateAGNode1.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -DomainDNSName {{DomainDNSName}} -AdminSecret {{AdminSecrets}} -SQLSecret {{SQLSecrets}} -ClusterName {{ClusterName}} -AvailabiltyGroupName {{AvailabiltyGroupName}} -WSFCNode1NetBIOSName {{WSFCNode1NetBIOSName}} -WSFCNode2NetBIOSName {{WSFCNode2NetBIOSName}} -AGListener1PrivateIP1 {{WSFCNode1PrivateIP3}} -AGListener1PrivateIP2 {{WSFCNode2PrivateIP3}} -WSFCNode3NetBIOSName {{WSFCNode3NetBIOSName}} -AGListener1PrivateIP3 {{WSFCNode3PrivateIP3}} -ManagedAD 'Yes'" - name: "3NodeMadPrimaryCreateAGConfig" action: aws:runCommand onFailure: "step:signalfailure" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{wsfcNode1InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: - | function DscStatusCheck () { $LCMState = (Get-DscLocalConfigurationManager).LCMState if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') { 'returning 3010, should continue after reboot' exit 3010 } else { 'Completed' } } Start-DscConfiguration 'C:\AWSQuickstart\AddAG' -Wait -Verbose -Force DscStatusCheck - name: AdditionalCreateAGBranch action: aws:branch inputs: Choices: - Or: - Variable: "{{ThirdAZ}}" StringEquals: "no" - Variable: "{{ThirdAZ}}" StringEquals: "witness" NextStep: 2NodeAdditionalCreateAG - Variable: "{{ThirdAZ}}" StringEquals: "full" NextStep: 3NodeAdditionalCreateAG - name: "2NodeAdditionalCreateAG" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcNode2InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/AdditionalNodeCreateAG.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./AdditionalNodeCreateAG.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -AdminSecret {{AdminSecrets}} -SQLSecret {{SQLSecrets}} -ClusterName {{ClusterName}} -AvailabiltyGroupName {{AvailabiltyGroupName}} -PrimaryNetBIOSName {{WSFCNode1NetBIOSName}}" - name: "2NodeAdditionalCreateAGConfig" action: aws:runCommand onFailure: "step:signalfailure" nextStep: "CFNSignalEnd" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{wsfcNode2InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: - | function DscStatusCheck () { $LCMState = (Get-DscLocalConfigurationManager).LCMState if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') { 'returning 3010, should continue after reboot' exit 3010 } else { 'Completed' } } Start-DscConfiguration 'C:\AWSQuickstart\AddAG' -Wait -Verbose -Force DscStatusCheck - name: "3NodeAdditionalCreateAG" action: "aws:runCommand" onFailure: "step:signalfailure" inputs: DocumentName: "AWS-RunRemoteScript" InstanceIds: - "{{wsfcNode2InstanceId.InstanceId}}" - "{{wsfcNode3InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: sourceType: "S3" sourceInfo: !Sub - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/AdditionalNodeCreateAG.ps1"}' - S3Bucket: !If - UsingDefaultBucket - !Sub '${QSS3BucketName}-${AWS::Region}' - !Ref QSS3BucketName S3Region: !If - UsingDefaultBucket - !Ref AWS::Region - !Ref QSS3BucketRegion commandLine: "./AdditionalNodeCreateAG.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -AdminSecret {{AdminSecrets}} -SQLSecret {{SQLSecrets}} -ClusterName {{ClusterName}} -AvailabiltyGroupName {{AvailabiltyGroupName}} -PrimaryNetBIOSName {{WSFCNode1NetBIOSName}}" - name: "3NodeAdditionalCreateAGConfig" action: aws:runCommand onFailure: "step:signalfailure" inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{wsfcNode2InstanceId.InstanceId}}" - "{{wsfcNode3InstanceId.InstanceId}}" CloudWatchOutputConfig: CloudWatchOutputEnabled: "true" CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' Parameters: commands: - | function DscStatusCheck () { $LCMState = (Get-DscLocalConfigurationManager).LCMState if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') { 'returning 3010, should continue after reboot' exit 3010 } else { 'Completed' } } Start-DscConfiguration 'C:\AWSQuickstart\AddAG' -Wait -Verbose -Force DscStatusCheck # Determines if CFN Needs to be Signaled or if Work flow should just end - name: CFNSignalEnd action: aws:branch inputs: Choices: - NextStep: signalsuccess Not: Variable: "{{StackName}}" StringEquals: "" - NextStep: sleepend Variable: "{{StackName}}" StringEquals: "" # If all steps complete successfully signals CFN of Success - name: "signalsuccess" action: "aws:executeAwsApi" isEnd: True inputs: Service: cloudformation Api: SignalResource LogicalResourceId: "SSMWaitCondition" StackName: "{{StackName}}" Status: SUCCESS UniqueId: "{{wsfcNode2InstanceId.InstanceId}}" # If CFN Signl Not Needed this sleep ends work flow - name: "sleepend" action: "aws:sleep" isEnd: True inputs: Duration: PT1S # If any steps fails signals CFN of Failure - name: "signalfailure" action: "aws:executeAwsApi" inputs: Service: cloudformation Api: SignalResource LogicalResourceId: "SSMWaitCondition" StackName: "{{StackName}}" Status: FAILURE UniqueId: "{{wsfcNode2InstanceId.InstanceId}}" MSSQLSSMPassRolePolicy: Type: AWS::IAM::Policy Properties: PolicyName: MSSQL-SSM-PassRole PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - iam:PassRole Resource: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${AWSQuickstartMSSQLRole}' Roles: - !Ref 'AWSQuickstartMSSQLRole' WSFCRole: Type: AWS::IAM::Role Metadata: cfn-lint: config: ignore_checks: - EIAMPolicyActionWildcard - EIAMPolicyWildcardResource Properties: Policies: - PolicyDocument: Version: '2012-10-17' Statement: - Action: - s3:GetObject - s3:GetBucketLocation - s3:ListBucket Resource: - !Sub ['arn:${AWS::Partition}:s3:::${S3Bucket}/${QSS3KeyPrefix}*', S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName]] - !Sub ['arn:${AWS::Partition}:s3:::${S3Bucket}', S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName]] Effect: Allow PolicyName: aws-quick-start-s3-policy - PolicyDocument: Version: '2012-10-17' Statement: - Action: - s3:GetObject Resource: - !Sub 'arn:${AWS::Partition}:s3:::aws-ssm-${AWS::Region}/*' - !Sub 'arn:${AWS::Partition}:s3:::aws-windows-downloads-${AWS::Region}/*' - !Sub 'arn:${AWS::Partition}:s3:::amazon-ssm-${AWS::Region}/*' - !Sub 'arn:${AWS::Partition}:s3:::amazon-ssm-packages-${AWS::Region}/*' - !Sub 'arn:${AWS::Partition}:s3:::${AWS::Region}-birdwatcher-prod/*' - !Sub 'arn:${AWS::Partition}:s3:::patch-baseline-snapshot-${AWS::Region}/*' Effect: Allow PolicyName: ssm-custom-s3-policy - PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - secretsmanager:GetSecretValue - secretsmanager:DescribeSecret Resource: - !Ref 'SQLSecrets' - !Ref 'ADAdminSecrets' - Effect: Allow Action: - ssm:StartAutomationExecution Resource: '*' PolicyName: QS-MSSQL-SSM - PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: fsx:* Resource: '*' PolicyName: QS-MSSQL-FSX - PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - iam:PassRole Resource: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${AWSQuickstartMSSQLRole}' PolicyName: QS-MSSQL-SSM-PassRole Path: / ManagedPolicyArns: - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore' - !Sub 'arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy' - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonEC2ReadOnlyAccess' AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Principal: Service: - ec2.amazonaws.com - ssm.amazonaws.com Effect: Allow Version: '2012-10-17' WSFCProfile: Type: AWS::IAM::InstanceProfile Properties: Roles: - !Ref 'WSFCRole' Path: / WSFCFileServer: Type: AWS::EC2::Instance Condition: IsTwoNode Properties: ImageId: !Ref WS2019FULLBASE IamInstanceProfile: !Ref 'WSFCProfile' InstanceType: !Ref 'WSFCFileServerInstanceType' NetworkInterfaces: - DeleteOnTermination: true DeviceIndex: '0' SubnetId: !If - ThirdAzIsWitness - !Ref 'PrivateSubnet3ID' - !Ref 'PrivateSubnet1ID' PrivateIpAddresses: - Primary: true PrivateIpAddress: !Ref 'WSFCFileServerPrivateIP' GroupSet: - !Ref 'DomainMemberSGID' - !Ref 'WSFCSecurityGroup' - !Ref 'WSFCClientSecurityGroup' Tags: - Key: Name Value: !Ref 'WSFCFileServerNetBIOSName' BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: VolumeSize: 100 VolumeType: gp2 Encrypted: true KeyName: !Ref 'KeyPairName' WSFCNode1: Type: AWS::EC2::Instance Properties: ImageId: !If - SQLBakedInAMI - !If - 'SQLVersion2016' - !Ref WS2019FULLSQL2016ENT - !If - 'SQLVersion2017' - !Ref WS2019FULLSQL2017ENT - !Ref WS2019FULLSQL2019ENT - !Ref WS2019FULLBASE IamInstanceProfile: !Ref 'WSFCProfile' InstanceType: !Ref 'WSFCNode1InstanceType' EbsOptimized: true NetworkInterfaces: - DeleteOnTermination: true DeviceIndex: '0' SubnetId: !Ref 'PrivateSubnet1ID' PrivateIpAddresses: - Primary: true PrivateIpAddress: !Ref 'WSFCNode1PrivateIP1' - Primary: false PrivateIpAddress: !Ref 'WSFCNode1PrivateIP2' - Primary: false PrivateIpAddress: !Ref 'WSFCNode1PrivateIP3' GroupSet: - !Ref 'DomainMemberSGID' - !Ref 'WSFCSecurityGroup' - !Ref 'WSFCClientSecurityGroup' Tags: - Key: Name Value: !Ref 'WSFCNode1NetBIOSName' BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: VolumeSize: 100 VolumeType: gp2 Encrypted: true - DeviceName: /dev/xvdca VirtualName: ephemeral0 KeyName: !Ref 'KeyPairName' WSFCNode3: Type: AWS::EC2::Instance Condition: ThirdAzIsFullNode Properties: ImageId: !If - SQLBakedInAMI - !If - 'SQLVersion2016' - !Ref WS2019FULLSQL2016ENT - !If - 'SQLVersion2017' - !Ref WS2019FULLSQL2017ENT - !Ref WS2019FULLSQL2019ENT - !Ref WS2019FULLBASE IamInstanceProfile: !Ref 'WSFCProfile' InstanceType: !Ref 'WSFCNode3InstanceType' EbsOptimized: true NetworkInterfaces: - DeleteOnTermination: true DeviceIndex: '0' SubnetId: !Ref 'PrivateSubnet3ID' PrivateIpAddresses: - Primary: true PrivateIpAddress: !Ref 'WSFCNode3PrivateIP1' - Primary: false PrivateIpAddress: !Ref 'WSFCNode3PrivateIP2' - Primary: false PrivateIpAddress: !Ref 'WSFCNode3PrivateIP3' GroupSet: - !Ref 'DomainMemberSGID' - !Ref 'WSFCSecurityGroup' - !Ref 'WSFCClientSecurityGroup' Tags: - Key: Name Value: !Ref 'WSFCNode3NetBIOSName' BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: VolumeSize: 100 VolumeType: gp2 Encrypted: true - DeviceName: /dev/xvdca VirtualName: ephemeral0 KeyName: !Ref 'KeyPairName' WSFCNode2: Type: AWS::EC2::Instance Properties: ImageId: !If - SQLBakedInAMI - !If - 'SQLVersion2016' - !Ref WS2019FULLSQL2016ENT - !If - 'SQLVersion2017' - !Ref WS2019FULLSQL2017ENT - !Ref WS2019FULLSQL2019ENT - !Ref WS2019FULLBASE IamInstanceProfile: !Ref 'WSFCProfile' InstanceType: !Ref 'WSFCNode2InstanceType' EbsOptimized: true NetworkInterfaces: - DeleteOnTermination: true DeviceIndex: '0' SubnetId: !Ref 'PrivateSubnet2ID' PrivateIpAddresses: - Primary: true PrivateIpAddress: !Ref 'WSFCNode2PrivateIP1' - Primary: false PrivateIpAddress: !Ref 'WSFCNode2PrivateIP2' - Primary: false PrivateIpAddress: !Ref 'WSFCNode2PrivateIP3' GroupSet: - !Ref 'DomainMemberSGID' - !Ref 'WSFCSecurityGroup' - !Ref 'WSFCClientSecurityGroup' Tags: - Key: Name Value: !Ref 'WSFCNode2NetBIOSName' BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: VolumeSize: 100 VolumeType: gp2 Encrypted: true - DeviceName: /dev/xvdca VirtualName: ephemeral0 KeyName: !Ref 'KeyPairName' UserData: !Base64 Fn::Join: - '' - - "\n" - 'Start-SSMAutomationExecution -DocumentName ' - !Sub '"${AWSQuickstartMSSQL}"' - ' -Parameter @{"SQLServerVersion"=' - !Sub '"${SQLServerVersion}"' - ';"SQLLicenseProvided"=' - !Sub '"${SQLLicenseProvided}"' - ';"WSFCNode1NetBIOSName"=' - !Sub '"${WSFCNode1NetBIOSName}"' - ';"WSFCNode1PrivateIP2"=' - !Sub '"${WSFCNode1PrivateIP2}"' - ';"WSFCNode1PrivateIP3"=' - !Sub '"${WSFCNode1PrivateIP3}"' - ';"WSFCNode2NetBIOSName"=' - !Sub '"${WSFCNode2NetBIOSName}"' - ';"WSFCNode2PrivateIP2"=' - !Sub '"${WSFCNode2PrivateIP2}"' - ';"WSFCNode2PrivateIP3"=' - !Sub '"${WSFCNode2PrivateIP3}"' - ';"WSFCNode3NetBIOSName"=' - !Sub '"${WSFCNode3NetBIOSName}"' - ';"WSFCNode3PrivateIP2"=' - !Sub '"${WSFCNode3PrivateIP2}"' - ';"WSFCNode3PrivateIP3"=' - !Sub '"${WSFCNode3PrivateIP3}"' - ';"WSFCFileServerNetBIOSName"=' - !Sub '"${WSFCFileServerNetBIOSName}"' - ';"FSXFileSystemID"=' - !If - FSxWitness - !Sub '"${FSXFileSystem}"' - '"none"' - ';"ClusterName"=' - !Sub '"${ClusterName}"' - ';"AvailabiltyGroupName"=' - !Sub '"${AvailabiltyGroupName}"' - ';"ThirdAZ"=' - !Sub '"${ThirdAZ}"' - ';"DomainDNSName"=' - !Sub '"${DomainDNSName}"' - ';"DomainNetBIOSName"=' - !Sub '"${DomainNetBIOSName}"' - ';"ManagedAD"="' - !If - UseAWSDirectoryServiceEE - "yes" - "no" - '";"AdminSecrets"=' - !Sub '"${ADAdminSecrets}"' - ';"SQLSecrets"=' - !Sub '"${SQLSecrets}"' - ';"QSS3BucketName"=' - !Sub '"${QSS3BucketName}"' - ';"QSS3KeyPrefix"=' - !Sub '"${QSS3KeyPrefix}"' - ';"SQL2016Media"=' - !Sub '"${SQL2016Media}"' - ';"SQL2017Media"=' - !Sub '"${SQL2017Media}"' - ';"SQL2019Media"=' - !Sub '"${SQL2019Media}"' - ';"StackName"=' - !Sub '"${AWS::StackName}"' - ';"WitnessType"=' - !Sub '"${WitnessType}"' - ';"URLSuffix"=' - !Sub '"${AWS::URLSuffix}"' - ';"AutomationAssumeRole"=' - !Sub '"arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${AWSQuickstartMSSQLRole}"' - '}' - "\n" - "\n" WSFCNode1Volume1: Type: AWS::EC2::Volume Properties: Size: !Ref 'Volume1Size' VolumeType: !Ref 'Volume1Type' Encrypted: true AvailabilityZone: !GetAtt 'WSFCNode1.AvailabilityZone' Iops: !If - Vol1IsIo1 - !Ref 'Volume1Iops' - !Ref 'AWS::NoValue' WSFCNode1Volume2: Type: AWS::EC2::Volume Properties: Size: !Ref 'Volume2Size' VolumeType: !Ref 'Volume2Type' Encrypted: true AvailabilityZone: !GetAtt 'WSFCNode1.AvailabilityZone' Iops: !If - Vol2IsIo1 - !Ref 'Volume2Iops' - !Ref 'AWS::NoValue' WSFCNode1Volume3: Type: AWS::EC2::Volume Properties: Size: !Ref 'Volume3Size' VolumeType: !Ref 'Volume3Type' Encrypted: true AvailabilityZone: !GetAtt 'WSFCNode1.AvailabilityZone' Iops: !If - Vol3IsIo1 - !Ref 'Volume3Iops' - !Ref 'AWS::NoValue' WSFCNode2Volume1: Type: AWS::EC2::Volume Properties: Size: !Ref 'Volume1Size' VolumeType: !Ref 'Volume1Type' Encrypted: true AvailabilityZone: !GetAtt 'WSFCNode2.AvailabilityZone' Iops: !If - Vol1IsIo1 - !Ref 'Volume1Iops' - !Ref 'AWS::NoValue' WSFCNode2Volume2: Type: AWS::EC2::Volume Properties: Size: !Ref 'Volume2Size' VolumeType: !Ref 'Volume2Type' Encrypted: true AvailabilityZone: !GetAtt 'WSFCNode2.AvailabilityZone' Iops: !If - Vol2IsIo1 - !Ref 'Volume2Iops' - !Ref 'AWS::NoValue' WSFCNode2Volume3: Type: AWS::EC2::Volume Properties: Size: !Ref 'Volume3Size' VolumeType: !Ref 'Volume3Type' Encrypted: true AvailabilityZone: !GetAtt 'WSFCNode2.AvailabilityZone' Iops: !If - Vol3IsIo1 - !Ref 'Volume3Iops' - !Ref 'AWS::NoValue' WSFCNode3Volume1: Type: AWS::EC2::Volume Condition: ThirdAzIsFullNode Properties: Size: !Ref 'Volume1Size' VolumeType: !Ref 'Volume1Type' Encrypted: true AvailabilityZone: !GetAtt 'WSFCNode3.AvailabilityZone' Iops: !If - Vol1IsIo1 - !Ref 'Volume1Iops' - !Ref 'AWS::NoValue' WSFCNode3Volume2: Type: AWS::EC2::Volume Condition: ThirdAzIsFullNode Properties: Size: !Ref 'Volume2Size' VolumeType: !Ref 'Volume2Type' Encrypted: true AvailabilityZone: !GetAtt 'WSFCNode3.AvailabilityZone' Iops: !If - Vol2IsIo1 - !Ref 'Volume2Iops' - !Ref 'AWS::NoValue' WSFCNode3Volume3: Type: AWS::EC2::Volume Condition: ThirdAzIsFullNode Properties: Size: !Ref 'Volume3Size' VolumeType: !Ref 'Volume3Type' Encrypted: true AvailabilityZone: !GetAtt 'WSFCNode3.AvailabilityZone' Iops: !If - Vol3IsIo1 - !Ref 'Volume3Iops' - !Ref 'AWS::NoValue' WSFCNode1Volume1Attachment: Type: AWS::EC2::VolumeAttachment Properties: Device: /dev/xvdb InstanceId: !Ref 'WSFCNode1' VolumeId: !Ref 'WSFCNode1Volume1' WSFCNode1Volume2Attachment: Type: AWS::EC2::VolumeAttachment Properties: Device: /dev/xvdc InstanceId: !Ref 'WSFCNode1' VolumeId: !Ref 'WSFCNode1Volume2' WSFCNode1Volume3Attachment: Type: AWS::EC2::VolumeAttachment Properties: Device: /dev/xvdd InstanceId: !Ref 'WSFCNode1' VolumeId: !Ref 'WSFCNode1Volume3' WSFCNode2Volume1Attachment: Type: AWS::EC2::VolumeAttachment Properties: Device: /dev/xvdb InstanceId: !Ref 'WSFCNode2' VolumeId: !Ref 'WSFCNode2Volume1' WSFCNode2Volume2Attachment: Type: AWS::EC2::VolumeAttachment Properties: Device: /dev/xvdc InstanceId: !Ref 'WSFCNode2' VolumeId: !Ref 'WSFCNode2Volume2' WSFCNode2Volume3Attachment: Type: AWS::EC2::VolumeAttachment Properties: Device: /dev/xvdd InstanceId: !Ref 'WSFCNode2' VolumeId: !Ref 'WSFCNode2Volume3' WSFCNode3Volume1Attachment: Type: AWS::EC2::VolumeAttachment Condition: ThirdAzIsFullNode Properties: Device: /dev/xvdb InstanceId: !Ref 'WSFCNode3' VolumeId: !Ref 'WSFCNode3Volume1' WSFCNode3Volume2Attachment: Type: AWS::EC2::VolumeAttachment Condition: ThirdAzIsFullNode Properties: Device: /dev/xvdc InstanceId: !Ref 'WSFCNode3' VolumeId: !Ref 'WSFCNode3Volume2' WSFCNode3Volume3Attachment: Type: AWS::EC2::VolumeAttachment Condition: ThirdAzIsFullNode Properties: Device: /dev/xvdd InstanceId: !Ref 'WSFCNode3' VolumeId: !Ref 'WSFCNode3Volume3' SSMWaitHandle: Type: AWS::CloudFormation::WaitConditionHandle SSMWaitCondition: Type: AWS::CloudFormation::WaitCondition CreationPolicy: ResourceSignal: Timeout: PT180M Count: 1 DependsOn: "WSFCNode2" Properties: Handle: Ref: "SSMWaitHandle" Timeout: "9000" Count: 1 WSFCSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable the WSFC and SQL AlwaysOn Availability Group communications VpcId: !Ref 'VPCID' WSFCSecurityGroupIngressIcmp: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref 'WSFCSecurityGroup' SourceSecurityGroupId: !Ref 'WSFCSecurityGroup' IpProtocol: icmp FromPort: -1 ToPort: -1 WSFCSecurityGroupIngressTcp135: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref 'WSFCSecurityGroup' SourceSecurityGroupId: !Ref 'WSFCSecurityGroup' IpProtocol: tcp FromPort: 135 ToPort: 135 WSFCSecurityGroupIngressTcp137: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref 'WSFCSecurityGroup' SourceSecurityGroupId: !Ref 'WSFCSecurityGroup' IpProtocol: tcp FromPort: 137 ToPort: 137 WSFCSecurityGroupIngressTcp445: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref 'WSFCSecurityGroup' SourceSecurityGroupId: !Ref 'WSFCSecurityGroup' IpProtocol: tcp FromPort: 445 ToPort: 445 WSFCSecurityGroupIngressTcp1433: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref 'WSFCSecurityGroup' SourceSecurityGroupId: !Ref 'WSFCSecurityGroup' IpProtocol: tcp FromPort: 1433 ToPort: 1434 WSFCSecurityGroupIngressTcp3343: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref 'WSFCSecurityGroup' SourceSecurityGroupId: !Ref 'WSFCSecurityGroup' IpProtocol: tcp FromPort: 3343 ToPort: 3343 WSFCSecurityGroupIngressTcp5022: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref 'WSFCSecurityGroup' SourceSecurityGroupId: !Ref 'WSFCSecurityGroup' IpProtocol: tcp FromPort: 5022 ToPort: 5022 WSFCSecurityGroupIngressTcp5985: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref 'WSFCSecurityGroup' SourceSecurityGroupId: !Ref 'WSFCSecurityGroup' IpProtocol: tcp FromPort: 5985 ToPort: 5985 WSFCSecurityGroupIngressUdp137: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref 'WSFCSecurityGroup' SourceSecurityGroupId: !Ref 'WSFCSecurityGroup' IpProtocol: udp FromPort: 137 ToPort: 137 WSFCSecurityGroupIngressUdp3343: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref 'WSFCSecurityGroup' SourceSecurityGroupId: !Ref 'WSFCSecurityGroup' IpProtocol: udp FromPort: 3343 ToPort: 3343 WSFCSecurityGroupIngressUdp1434: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref 'WSFCSecurityGroup' SourceSecurityGroupId: !Ref 'WSFCSecurityGroup' IpProtocol: udp FromPort: 1434 ToPort: 1434 WSFCSecurityGroupIngressUdpHighPorts: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref 'WSFCSecurityGroup' SourceSecurityGroupId: !Ref 'WSFCSecurityGroup' IpProtocol: udp FromPort: 49152 ToPort: 65535 WSFCSecurityGroupIngressTcpHighPorts: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref 'WSFCSecurityGroup' SourceSecurityGroupId: !Ref 'WSFCSecurityGroup' IpProtocol: tcp FromPort: 49152 ToPort: 65535 SQLServerAccessSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: VpcId: !Ref 'VPCID' GroupDescription: Allows access to SQL Servers WSFCClientSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: SQL Client access ports VpcId: !Ref 'VPCID' SecurityGroupIngress: - IpProtocol: tcp FromPort: 1433 ToPort: 1433 SourceSecurityGroupId: !Ref 'SQLServerAccessSecurityGroup' FSxSG: Type: AWS::EC2::SecurityGroup Condition: FSxWitness Properties: GroupDescription: FSx share VpcId: !Ref VPCID SecurityGroupIngress: - IpProtocol: tcp FromPort: 5985 ToPort: 5985 SourceSecurityGroupId: !Ref DomainMemberSGID - IpProtocol: tcp FromPort: 53 ToPort: 53 SourceSecurityGroupId: !Ref DomainMemberSGID - IpProtocol: tcp FromPort: 53 ToPort: 53 SourceSecurityGroupId: !Ref DomainMemberSGID - IpProtocol: udp FromPort: 53 ToPort: 53 SourceSecurityGroupId: !Ref DomainMemberSGID - IpProtocol: tcp FromPort: 49152 ToPort: 65535 SourceSecurityGroupId: !Ref DomainMemberSGID - IpProtocol: udp FromPort: 49152 ToPort: 65535 SourceSecurityGroupId: !Ref DomainMemberSGID - IpProtocol: tcp FromPort: 88 ToPort: 88 SourceSecurityGroupId: !Ref DomainMemberSGID - IpProtocol: udp FromPort: 88 ToPort: 88 SourceSecurityGroupId: !Ref DomainMemberSGID - IpProtocol: tcp FromPort: 445 ToPort: 445 SourceSecurityGroupId: !Ref DomainMemberSGID - IpProtocol: udp FromPort: 445 ToPort: 445 SourceSecurityGroupId: !Ref DomainMemberSGID - IpProtocol: tcp FromPort: 389 ToPort: 389 SourceSecurityGroupId: !Ref DomainMemberSGID - IpProtocol: udp FromPort: 389 ToPort: 389 SourceSecurityGroupId: !Ref DomainMemberSGID - IpProtocol: tcp FromPort: 636 ToPort: 636 SourceSecurityGroupId: !Ref DomainMemberSGID FSXFileSystem: Type: 'AWS::FSx::FileSystem' Condition: FSxWitness Properties: FileSystemType: WINDOWS StorageCapacity: 32 StorageType: SSD SubnetIds: - !Ref PrivateSubnet1ID - !Ref PrivateSubnet2ID SecurityGroupIds: - !Ref FSxSG Tags: - Key: Name Value: windows WindowsConfiguration: ThroughputCapacity: 8 WeeklyMaintenanceStartTime: '4:16:30' DailyAutomaticBackupStartTime: '01:00' AutomaticBackupRetentionDays: 30 CopyTagsToBackups: false DeploymentType: 'MULTI_AZ_1' PreferredSubnetId: !Ref PrivateSubnet1ID SelfManagedActiveDirectoryConfiguration: DnsIps: - !Ref ADServer1PrivateIP - !Ref ADServer2PrivateIP DomainName: !Ref DomainDNSName UserName: !Ref DomainAdminUser Password: !Ref DomainAdminPassword AppInsightsForSQLHA: Condition: EnableAppInsightForSQLHA Type: AWS::ApplicationInsights::Application Properties: ResourceGroupName: !Ref 'ResourceGroupName' AutoConfigurationEnabled: false CustomComponents: - ComponentName: !Sub 'SQLHAClusterInstances-${ApplicationName}' ResourceList: - !Sub 'arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:instance/${WSFCNode1}' - !Sub 'arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:instance/${WSFCNode2}' - !If - ThirdAzIsFullNode - !Sub 'arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:instance/${WSFCNode3}' - !Ref 'AWS::NoValue' ComponentMonitoringSettings: - ComponentName: !Sub 'SQLHAClusterInstances-${ApplicationName}' Tier: SQL_SERVER_ALWAYSON_AVAILABILITY_GROUP ComponentConfigurationMode: DEFAULT_WITH_OVERWRITE DefaultOverwriteComponentConfiguration: SubComponentTypeConfigurations: - SubComponentType: AWS::EC2::Instance SubComponentConfigurationDetails: Logs: - LogGroupName: !Sub 'SQL_SERVER-${ResourceGroupName}' LogType: SQL_SERVER LogPath: C:\Program Files\Microsoft SQL Server\MSSQL**.MSSQLSERVER\MSSQL\Log\ERRORLOG Outputs: WSFCNode1NetBIOSName: Value: !Ref 'WSFCNode1NetBIOSName' Description: NetBIOS name of the 1st WSFC Node WSFCNode2NetBIOSName: Value: !Ref 'WSFCNode2NetBIOSName' Description: NetBIOS name of the 2nd WSFC Node WSFCNode3NetBIOSName: Condition: ThirdAzIsFullNode Value: !Ref 'WSFCNode3NetBIOSName' Description: NetBIOS name of the 3rd WSFC Node SQLServerAccessSecurityGroupID: Value: !Ref 'SQLServerAccessSecurityGroup' Description: Add instances that require access to SQL to this Security Group