diff --git a/.gitmodules b/.gitmodules index cdc7805..f0a5c20 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,15 @@ [submodule "docs/boilerplate"] path = docs/boilerplate url = https://github.com/aws-ia/aws-ia-documentation-base-common.git +[submodule "submodules/cfn-ps-aws-vpc"] + path = submodules/cfn-ps-aws-vpc + url = git@github.com:aws-ia/cfn-ps-aws-vpc.git + branch = main +[submodule "submodules/cfn-ps-microsoft-activedirectory"] + path = submodules/cfn-ps-microsoft-activedirectory + url = git@github.com:aws-ia/cfn-ps-microsoft-activedirectory.git + branch = main +[submodule "submodules/cfn-ps-microsoft-rdgateway"] + path = submodules/cfn-ps-microsoft-rdgateway + url = git@github.com:aws-ia/cfn-ps-microsoft-rdgateway.git + branch = main diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..fdd1b78 --- /dev/null +++ b/.metadata @@ -0,0 +1 @@ +language_type: cloudformation diff --git a/.project_automation/functional_tests/entrypoint.sh b/.project_automation/functional_tests/entrypoint.sh index d607291..11185c7 100755 --- a/.project_automation/functional_tests/entrypoint.sh +++ b/.project_automation/functional_tests/entrypoint.sh @@ -22,7 +22,7 @@ if echo "${DIFF_OUTPUT}" | grep "^diff --git a/docs/"; then asciidoctor --base-dir docs/ --backend=html5 -o ../index.html -w --doctype=book -a toc2 -a production_build docs/boilerplate/index_deployment_guide.adoc ## Create PR with index.html file CURRENT_BRANCH=$(git branch --show-current) - git checkout main + # git checkout main git checkout -b "${DOCS_BRANCH}" git add index.html git commit -m '(automated) rendered html deployment guide' @@ -30,7 +30,7 @@ if echo "${DIFF_OUTPUT}" | grep "^diff --git a/docs/"; then gh pr create --title 'Generated deployment guide' --body "_This is an automated PR with rendered html file for the deployment guide. Please review it before merge_" else printf '\nNo changes detected in the /docs files. \n' -fi +fi ##---------------------------------------------------- ## Download taskcat overrides from AWS Secrets Manager diff --git a/.project_automation/static_tests/Dockerfile b/.project_automation/static_tests/Dockerfile index 4f5b5b5..e8d7c32 100644 --- a/.project_automation/static_tests/Dockerfile +++ b/.project_automation/static_tests/Dockerfile @@ -1,4 +1,4 @@ FROM public.ecr.aws/codebuild/amazonlinux2-x86_64-standard:4.0 RUN git clone https://github.com/aws-quickstart/qs-cfn-lint-rules.git /tmp/qs-cfn-lint-rules -RUN cd /tmp/qs-cfn-lint-rules && pip install . -RUN cfn-lint -u \ No newline at end of file +RUN cd /tmp/qs-cfn-lint-rules && git checkout 8268db9df3407ccf5383def635bc22e0f99d6b39 && pip install . +#RUN cfn-lint -u \ No newline at end of file diff --git a/.project_automation/static_tests/entrypoint.sh b/.project_automation/static_tests/entrypoint.sh index 918207e..a96d0ce 100755 --- a/.project_automation/static_tests/entrypoint.sh +++ b/.project_automation/static_tests/entrypoint.sh @@ -6,4 +6,11 @@ PROJECT_PATH=${BASE_PATH}/project PROJECT_TYPE_PATH=${BASE_PATH}/projecttype cd $PROJECT_PATH -cfn-lint --non-zero-exit-code none -t templates/**/*.yaml -a /tmp/qs-cfn-lint-rules/qs_cfn_lint_rules/ \ No newline at end of file + +# Ignoring the following for migration +# All warnings, +# E1019 - Sub validation - false positive for conditionals, +# E2521 - required properties, E3002 - resource properties - false positive for newer resources than pinned CloudFormation resource spec +# E3005 - DependsOn - false positive for conditionals, +# E9101 - Inclusive language check - false positive for database resources +cfn-lint --ignore-checks W,E1019,E2521,E3002,E3005,E9101,E3030,E3031 -t templates/**/*.yaml -a /tmp/qs-cfn-lint-rules/qs_cfn_lint_rules/ diff --git a/.taskcat.yml b/.taskcat.yml index 911b592..00f52e8 100644 --- a/.taskcat.yml +++ b/.taskcat.yml @@ -1,33 +1,57 @@ project: - name: cfn-sample-template - owner: quickstart@amazon.com + name: cfn-ps-microsoft-sql-fci-fsx + owner: quickstart-eng@amazon.com package_lambda: false regions: - ap-northeast-1 - ap-northeast-2 + - ap-south-1 - ap-southeast-1 - ap-southeast-2 + - ca-central-1 - eu-central-1 - eu-west-1 + - eu-west-2 - sa-east-1 - us-east-1 + - us-east-2 - us-west-1 - us-west-2 + s3_bucket: '' tests: - sample: + mssql-ec2ad: parameters: - Param1: 'Inputs to Stack' - # Examples: of other taskcat dynamic input parameters for more into see http://taskcat.io - # - # AvailabilityZones: $[taskcat_genaz_3] - # ByteValue: 1 - # PasswordA: $[taskcat_genpass_8A] - # PasswordB: $[taskcat_genpass_32S] - # RandomNumber: $[taskcat_random-numbers] - # RandomString: $[taskcat_random-string] - # StackName: TestStack - # UUID: $[taskcat_genuuid] - # + ADScenarioType: 'Microsoft AD on Amazon EC2' + AvailabilityZones: $[taskcat_getaz_2] + DomainAdminPassword: $[taskcat_genpass_16] + EnableAppInsights: 'true' + KeyPairName: $[taskcat_getkeypair] + QSS3BucketName: $[taskcat_autobucket] + QSS3BucketRegion: $[taskcat_current_region] + NumberOfRDGWHosts: '0' + RDGWCIDR: 0.0.0.0/0 + SQLServiceAccountPassword: $[taskcat_genpass_16] + MSSQLMediaBucketName: override + MSSQLMediaPathKey: SQLServer2019-x64-ENU.iso + regions: + - us-east-1 + s3_bucket: '' + template: templates/mssqlfsx-main.template.yaml + mssql-mad: + parameters: + ADScenarioType: 'AWS Directory Service for Microsoft AD (Enterprise Edition)' + AvailabilityZones: $[taskcat_getaz_2] + DomainAdminPassword: $[taskcat_genpass_16] + EnableAppInsights: 'true' + KeyPairName: $[taskcat_getkeypair] + QSS3BucketName: $[taskcat_autobucket] + QSS3BucketRegion: $[taskcat_current_region] + NumberOfRDGWHosts: '0' + RDGWCIDR: 0.0.0.0/0 + SQLServiceAccountPassword: $[taskcat_genpass_16] + MSSQLMediaBucketName: override + MSSQLMediaPathKey: SQLServer2019-x64-ENU.iso regions: - us-east-2 - template: templates/another-workload.template.yaml + s3_bucket: '' + template: templates/mssqlfsx-main.template.yaml \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e1c6dc6 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +## SQL Server Failover Cluster Instance on AWS—Quick Start + +For architectural details, step-by-step instructions, and customization options, see the [deployment guide](https://fwd.aws/5XG6A?). + +To post feedback, submit feature ideas, or report bugs, use the **Issues** section of this GitHub repo. + +To submit code for this Quick Start, see the [AWS Quick Start Contributor's Kit](https://aws-quickstart.github.io/). diff --git a/docs/deployment_guide/images/aws-quickstart-graphic.png b/docs/deployment_guide/images/aws-quickstart-graphic.png new file mode 100644 index 0000000..8efde67 Binary files /dev/null and b/docs/deployment_guide/images/aws-quickstart-graphic.png differ diff --git a/docs/deployment_guide/images/cfn_outputs.png b/docs/deployment_guide/images/cfn_outputs.png new file mode 100644 index 0000000..cad7d4b Binary files /dev/null and b/docs/deployment_guide/images/cfn_outputs.png differ diff --git a/docs/deployment_guide/images/image1.png b/docs/deployment_guide/images/image1.png new file mode 100644 index 0000000..3e45bc3 Binary files /dev/null and b/docs/deployment_guide/images/image1.png differ diff --git a/docs/deployment_guide/images/image2.png b/docs/deployment_guide/images/image2.png new file mode 100644 index 0000000..ec6e3bc Binary files /dev/null and b/docs/deployment_guide/images/image2.png differ diff --git a/docs/deployment_guide/images/image3.png b/docs/deployment_guide/images/image3.png new file mode 100644 index 0000000..5dca2c1 Binary files /dev/null and b/docs/deployment_guide/images/image3.png differ diff --git a/docs/deployment_guide/images/image4.png b/docs/deployment_guide/images/image4.png new file mode 100644 index 0000000..4705d4e Binary files /dev/null and b/docs/deployment_guide/images/image4.png differ diff --git a/docs/deployment_guide/images/ms-sql-fci-fsx-architecture_diagram.png b/docs/deployment_guide/images/ms-sql-fci-fsx-architecture_diagram.png new file mode 100644 index 0000000..0af0c56 Binary files /dev/null and b/docs/deployment_guide/images/ms-sql-fci-fsx-architecture_diagram.png differ diff --git a/docs/deployment_guide/images/ms-sql-fci-fsx-architecture_diagram.pptx b/docs/deployment_guide/images/ms-sql-fci-fsx-architecture_diagram.pptx new file mode 100644 index 0000000..b8dcaa4 Binary files /dev/null and b/docs/deployment_guide/images/ms-sql-fci-fsx-architecture_diagram.pptx differ diff --git a/docs/deployment_guide/partner_editable/_settings.adoc b/docs/deployment_guide/partner_editable/_settings.adoc index 3771142..ccf7682 100644 --- a/docs/deployment_guide/partner_editable/_settings.adoc +++ b/docs/deployment_guide/partner_editable/_settings.adoc @@ -1,15 +1,15 @@ -:partner-solution-project-name: partner-solution-repo-name -:partner-solution-github-org: aws-quickstart -:partner-product-name: Full Product Name -:partner-product-short-name: Product Name -:partner-company-name: Example Company Name, Ltd. -:doc-month: January +:partner-solution-project-name: cfn-ps-microsoft-sql-fci-fsx +:partner-solution-github-org: aws-ia +:partner-product-name: SQL Server Failover Cluster Instance +:partner-product-short-name: SQL Server FCI +//:partner-company-name: Example Company Name, Ltd. +:doc-month: August :doc-year: 2023 -:partner-contributors: John Smith, {partner-company-name} +//:partner-contributors: John Smith, {partner-company-name} // :other-contributors: Akua Mansa, Trek10 -:aws-contributors: Janine Singh, AWS IoT Partner team -:aws-ia-contributors: Toni Jones, AWS Integration & Automation team -:deployment_time: 15 minutes +:aws-contributors: Sepehr Samiei and Garry Singh, AWS Microsoft Tech Specialist Solutions Architect team +:aws-ia-contributors: Dave May, AWS Integration & Automation team +:deployment_time: 2.25 hours :default_deployment_region: us-east-1 // :private_repo: diff --git a/docs/deployment_guide/partner_editable/architecture.adoc b/docs/deployment_guide/partner_editable/architecture.adoc index 55087cb..bf83d51 100644 --- a/docs/deployment_guide/partner_editable/architecture.adoc +++ b/docs/deployment_guide/partner_editable/architecture.adoc @@ -7,22 +7,46 @@ AWS Cloud. [#architecture1] .Partner Solution architecture for {partner-product-short-name} on AWS -image::../docs/deployment_guide/images/architecture_diagram.png[Architecture] +image::../docs/deployment_guide/images/ms-sql-fci-fsx-architecture_diagram.png[architecture1,90%] As shown in <>, this Partner Solution sets up the following: * A highly available architecture that spans two Availability Zones.* -* A virtual private cloud (VPC) configured with public and private subnets, according to AWS -best practices, to provide you with your own virtual network on AWS.* +* A VPC configured with public and private subnets, according to AWS best practices, to provide you with your own virtual network on AWS.* * In the public subnets: -** Managed network address translation (NAT) gateways to allow outbound -internet access for resources in the private subnets.* -** A Linux bastion host in an Auto Scaling group to allow inbound Secure -Shell (SSH) access to Amazon Elastic Compute Cloud (Amazon EC2) instances in public and private subnets.* +** Managed network address translation (NAT) gateways to allow outbound internet access for resources in the private subnets.* +** A Remote Desktop Gateway (RD Gateway) host in an Auto Scaling group to allow inbound Remote Desktop Protocol (RDP) access to EC2 instances in public and private subnets.* * In the private subnets: -** . -** . -// Add bullet points for any additional components that are included in the deployment. Ensure that the additional components are shown in the architecture diagram. End each bullet with a period. -* . +** Two EC2 instances running Microsoft Windows with SQL Server. These instances are installed as nodes in a WSFC cluster in an Always On FCI configuration across the Availability Zones. Each node contains an Amazon Elastic Block Store (Amazon EBS) root volume. +* An Amazon FSx file system, which the FCI nodes share. SQL Server is installed in this file system. This file system also stores all SQL database and log files, and it acts as the WSFC cluster's file-share witness.* +* AWS Directory Service with a managed directory. The Amazon FSx file system and the EC2 Windows instances that host this architecture's nodes are joined to the same Active Directory domain. +* AWS Secrets Manager keys to store credentials. +* An AWS Systems Manager automation document to automate the deployment. -[.small]#* The template that deploys this Partner Solution into an existing VPC skips the components marked by asterisks and prompts you for your existing VPC configuration.# +[.small]#*The template that deploys the Partner Solution into an existing VPC skips the components marked by asterisks and prompts you for your existing VPC configuration.# + +=== Comparison with SQL Server with Always On Replication === + +To better understand the architecture of the {partner-product-short-name} Partner Solution, it's helpful to compare with the https://fwd.aws/KEvrk?/[Partner Solution for SQL Server with Always On Replication^]. Both Partner Solutions are architected to ensure high availability. Both have EC2 instances clustered using WSFC. Both have database files stored in multiple Availability Zones. What's different is the way each Partner Solution accomplishes database high availability. + +The replication-based architecture requires a full installation of SQL Server (Standard or Enterprise edition) on each EC2 instance. Therefore, each EC2 instance requires a SQL Server license. + +*What makes the FCI-based architecture unique is that it requires only one SQL Server license.* With FCI, database-related files aren't replicated across the WSFC cluster, and SQL Server is not installed in the local file systems. Instead, this Partner Solution creates an Amazon FSx file system and installs SQL Server there. The EC2 instances (FCI nodes) share this file system, which also stores all the SQL database files and log files. In addition, this shared file system acts as the cluster's file-share witness. + +=== Advantages and disadvantages === +The architecture of each SQL Server Partner Solution has advantages and disadvantages. + +*SQL Server with Always On Replication Partner Solution:* + +* Advantages: +** The EBS volume type, capacity, and IOPS can be configured, allowing flexibility. +** It supports both AWS-provided licensing and Bring Your Own License models for Microsoft SQL. +* Disadvantage: +** Each WSFC node with SQL Server installed requires a SQL Server license, increasing cost. + +*{partner-product-short-name} Partner Solution:* + +* Advantage: It requires only one SQL Server license. +* Disadvantages: +** It relies on an Amazon FSx Multi-AZ file system, which is not supported in all AWS Regions. +** It requires the customer to provide a SQL Server .iso file and license since AWS-provided SQL licensing is not supported. diff --git a/docs/deployment_guide/partner_editable/deployment_options.adoc b/docs/deployment_guide/partner_editable/deployment_options.adoc index b667c14..f0ab976 100644 --- a/docs/deployment_guide/partner_editable/deployment_options.adoc +++ b/docs/deployment_guide/partner_editable/deployment_options.adoc @@ -2,7 +2,8 @@ This Partner Solution provides the following deployment options: -* https://qs_launch_permalink[Deploy {partner-product-short-name} into a new VPC^]. This option builds a new AWS environment that consists of the VPC, subnets, NAT gateways, security groups, bastion hosts, and other infrastructure components. It then deploys {partner-product-short-name} into this new VPC. -* https://qs_launch_permalink[Deploy {partner-product-short-name} into an existing VPC^]. This option provisions {partner-product-short-name} in your existing AWS infrastructure. +* https://fwd.aws/KmBKD?[Deploy {partner-product-short-name} into a new VPC^]. This option builds a new AWS environment that consists of the VPC, subnets, NAT gateways, security groups, bastion hosts, and other infrastructure components. It then deploys {partner-product-short-name} into this new VPC. +* https://fwd.aws/6JaVw?[Deploy into an existing VPC with AWS Managed Microsoft AD^]. This option provisions {partner-product-short-name} in your existing AWS infrastructure. +* https://fwd.aws/x5n57?[Deploy into an existing VPC with self-managed Active Directory^]. This option provisions {partner-product-short-name} in your existing AWS infrastructure. This Partner Solution provides separate templates for these options. It also lets you configure Classless Inter-Domain Routing (CIDR) blocks, instance types, and {partner-product-short-name} settings. diff --git a/docs/deployment_guide/partner_editable/licenses.adoc b/docs/deployment_guide/partner_editable/licenses.adoc index 9b10efe..b7225a5 100644 --- a/docs/deployment_guide/partner_editable/licenses.adoc +++ b/docs/deployment_guide/partner_editable/licenses.adoc @@ -1,3 +1,9 @@ // Include details about any licenses and how to sign up. Provide links as appropriate. -There is no cost to use this Partner Solution, but you will be billed for any AWS services or resources that this Partner Solution deploys. For more information, refer to the https://fwd.aws/rA69w?[AWS Partner Solution General Information Guide^]. \ No newline at end of file +There is no cost to use this Partner Solution, but you will be billed for any AWS services or resources that this Partner Solution deploys. For more information, refer to the https://fwd.aws/rA69w?[AWS Partner Solution General Information Guide^]. + +This Quick Start requires a license for Microsoft SQL Server 2019. You can obtain a trial license from the https://www.microsoft.com/en-us/evalcenter/evaluate-sql-server-2019[Microsoft Evaluation Center^]. + +Alternatively, if you're not using the software for a production environment, you can use the MSSQL Developer Edition. This edition provides the full capabilities of Enterprise Edition without requiring license costs. + +This Quick Start deploys MSSQL in Bring-Your-Own-License mode. It does not support deployment of MSSQL license-included Amazon Machine Images (AMIs). diff --git a/docs/deployment_guide/partner_editable/overview.adoc b/docs/deployment_guide/partner_editable/overview.adoc index f8197e0..bbc0533 100644 --- a/docs/deployment_guide/partner_editable/overview.adoc +++ b/docs/deployment_guide/partner_editable/overview.adoc @@ -1,10 +1,33 @@ -This guide covers the information you need to deploy the {partner-product-name} Partner Solution in the AWS Cloud. +This guide covers the information you need to deploy the {partner-product-name} Partner Solution in the AWS Cloud. -// Fill in the info in for use on the landing page only: -This Amazon Web Services (AWS) Partner Solution deploys []() in the AWS Cloud so that . This solution is for who want to so that . For more information, refer to the [ documentation](). +This Partner Solution is for IT infrastructure architects, database administrators, and DevOps professionals who plan to implement or extend Microsoft SQL Server (MSSQL) using SQL Server on AWS with Windows Server Failover Clustering (WSFC). Unlike the Partner Solution for https://aws.amazon.com/quickstart/architecture/sql/[SQL Server with Always On Replication^], this one deploys an https://docs.microsoft.com/en-us/sql/sql-server/failover-clusters/windows/always-on-failover-cluster-instances-sql-server?view=sql-server-ver15[Always On Failover Cluster Instance^] (FCI). It also deploys https://aws.amazon.com/fsx/windows/[Amazon FSx for Windows File Server^] as a network share to store the database files. -// Deploying this solution does not guarantee an organization’s compliance with any laws, certifications, policies, or other regulations. [Uncomment this statement only for solutions that relate to compliance. We'll add the corresponding reference part to the landing page and get legal approval before publishing.] +This guide does not provide general configuration and usage information for WSFC and MSSQL. For general guidance and best practices, consult the Microsoft product documentation and the https://d1.awsstatic.com/whitepapers/best-practices-for-deploying-microsoft-sql-server-on-aws.pdf[Best Practices for Deploying Microsoft SQL Server on AWS^] whitepaper. -// For advanced information about the product, troubleshooting, or additional functionality, refer to the https://{partner-solution-github-org}.github.io/{partner-solution-project-name}/operational/index.html[Operational Guide^]. +:xrefstyle: short -// For information about using this Partner Solution for migrations, refer to the https://{partner-solution-github-org}.github.io/{partner-solution-project-name}/migration/index.html[Migration Guide^]. \ No newline at end of file +The {partner-product-short-name} Partner Solution deploys a highly available environment that includes Windows Server and SQL Server running on Amazon Elastic Compute Cloud (EC2). It requires shared storage that is accessible by all nodes within the WSFC cluster. It supports SQL Server 2019 running on Windows Server 2019. + +This architecture uses a highly available Multi-AZ Amazon FSx file system as the network share used to store MSSQL database files. The Amazon FSx file system and EC2 Windows instances that host this architecture's nodes are joined to the same Active Directory domain. + +NOTE: The "instance" in "failover cluster instance" means something different from the "instance" in "EC2 instance." In this Partner Solution, a failover cluster instance, or FCI, has the appearance of an instance of SQL Server running on a single computer. A failover cluster instance provides failover from one EC2 instance (WSFC node) to another if the current EC2 instance goes down. For an illustration, see <>. + +Traditionally, FCIs have been difficult to deploy and manage. With its Multi-AZ file system option, Amazon FSx provides fully managed file storage. This storage enables the high availability and durability that's required to run business-critical Microsoft SQL Server database workloads without requiring licenses for each server. Amazon FSx automatically handles failover, simplifying shared storage to host your database deployments while reducing cost. + +The automation in this deployment uses AWS Systems Manager Automation, AWS CloudFormation, and Windows PowerShell Desired State Configuration (DSC) to deploy a multi-node SQL Always On FCI. Windows Server Failover Clustering is a prerequisite for deploying an Always On FCI. MSSQL uses WSFC to increase application availability. WSFC provides infrastructure features that complement the high availability and disaster recovery scenarios supported in the AWS Cloud. + +FCI, which was introduced with SQL Server 2008 as a high availability feature, continues to be available in all newer versions of MSSQL. When used on premises, SQL Server FCI is often used purely for high availability within a single data center. On AWS, you can use the {partner-product-short-name} Partner Solution to cover both high availability and disaster recovery requirements. + +Since FCIs require shared storage, traditionally they had to be deployed within a single data center. On AWS, the shared storage can span multiple Availability Zones, enabling WSFC clusters to span multiple Availability Zones. + +Implementing WSFC on AWS is similar to deploying it on premises as long as you meet these two requirements: + +* The cluster nodes are deployed inside a virtual private cloud (VPC). +* The cluster nodes are deployed in separate subnets to provide high availability across multiple Availability Zones. + +This Partner Solution meets these requirements. + +For more information: + +* https://docs.aws.amazon.com/fsx/latest/WindowsGuide/what-is.html[What Is Amazon FSx for Windows File Server?^] +* https://docs.aws.amazon.com/fsx/latest/WindowsGuide/sql-server.html[Using Amazon FSx for Windows File Server with Microsoft SQL Server^] diff --git a/docs/deployment_guide/partner_editable/post_deployment.adoc b/docs/deployment_guide/partner_editable/post_deployment.adoc index 7fab41c..ed6e4d9 100644 --- a/docs/deployment_guide/partner_editable/post_deployment.adoc +++ b/docs/deployment_guide/partner_editable/post_deployment.adoc @@ -1,3 +1,47 @@ // Include any postdeployment steps here, such as steps necessary to test that the deployment was successful. If there are no postdeployment steps, leave this file empty. == Postdeployment steps + +=== Run Windows updates + +In order to ensure the deployed servers' operating systems and installed applications have the latest Microsoft updates, run Windows Update on each server. + +. Create an RDP session from the Remote Desktop Gateway server to each deployed server. +. Choose the *Settings* application. +. Choose *Update & Security*. +. Choose *Check for updates*. +. Install any updates and reboot if necessary. + +=== Test the deployment +// If steps are required to test the deployment, add them here. If not, remove the heading + +. Open an RDP session to one of the two SQL servers. +. Open Windows *Administrative Tools*, and launch *Failover Cluster Manager*. ++ +[#postdeploy1] +.Failover Cluster Manager +[link=images/image1.png] +image::../docs/deployment_guide/images/image1.png[postdeploy1] ++ +. Choose *Nodes*, and ensure that both nodes are online. ++ +[#postdeploy2] +.Both nodes showing online +image::../docs/deployment_guide/images/image2.png[postdeploy2] ++ +. Select the failover cluster, and verify that both the cluster and the file-share witness are online. ++ +[#postdeploy3] +.Cluster overview showing that both the cluster and the file-share witness are online +image::../docs/deployment_guide/images/image3.png[postdeploy3] ++ +. Download https://docs.microsoft.com/en-us/sql/ssms/download-sql-server-management-studio-ssms?view=sql-server-ver15[SQL Server Management Studio (SSMS)^]. +. Install SQL Server Management Studio. The installation requires a reboot. +. After rebooting, log back in to the SQL server. +. Launch SQL Server Management Studio, and connect to the FCI. ++ +[#postdeploy4] +.Connecting to the cluster +image::../docs/deployment_guide/images/image4.png[50%] ++ +. If you are able to log in, the deployment was successful. diff --git a/docs/deployment_guide/partner_editable/pre_deployment.adoc b/docs/deployment_guide/partner_editable/pre_deployment.adoc index 5be88b7..76f39e9 100644 --- a/docs/deployment_guide/partner_editable/pre_deployment.adoc +++ b/docs/deployment_guide/partner_editable/pre_deployment.adoc @@ -1,3 +1,13 @@ //Include any predeployment steps here, such as signing up for a Marketplace AMI or making any changes to a partner account. If there are no predeployment steps, leave this file empty. == Predeployment steps + +The deployment of this Partner Solution requires a copy of the Microsoft SQL Server 2019 installation media as an .iso file. The typical file name is *SQLServer2019-x64-ENU.iso*. + +. Download the https://www.microsoft.com/en-us/evalcenter/evaluate-sql-server-2019[SQL Server 2019 evaluation edition^] from the Microsoft Evaluation Center. ++ +—or— ++ +Obtain the .iso file from the Microsoft Developer Network if you have an MSDN account with licenses for server software. + +. Upload the .iso file to an S3 bucket. diff --git a/docs/deployment_guide/partner_editable/troubleshooting.adoc b/docs/deployment_guide/partner_editable/troubleshooting.adoc index aaeba88..fc34823 100644 --- a/docs/deployment_guide/partner_editable/troubleshooting.adoc +++ b/docs/deployment_guide/partner_editable/troubleshooting.adoc @@ -2,5 +2,11 @@ For troubleshooting common Partner Solution issues, refer to the https://fwd.aws/rA69w?[AWS Partner Solution General Information Guide^] and https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/troubleshooting.html[Troubleshooting CloudFormation^]. -// == Resources -// Uncomment section and add links to any external resources that are specified by the partner. +If the deployment fails, follow these steps: + +. Open the https://console.aws.amazon.com/systems-manager/home?region=us-east-1[AWS Systems Manager console^]. +. Select your deployment Region. +. Choose *Automation* from the left-hand side, and locate the failed automation document. +. Navigate to the failed step. +. Expand *Output* to view the automation logs. +. Follow the link to Amazon CloudWatch Logs to view detailed automation logs. diff --git a/docs/generated/parameters/index.adoc b/docs/generated/parameters/index.adoc new file mode 100644 index 0000000..bd4ea65 --- /dev/null +++ b/docs/generated/parameters/index.adoc @@ -0,0 +1,9 @@ + +=== Launch into a new VPC +include::mssqlfci-main.template.adoc[] + +=== Launch with AWS managed directory service into an existing VPC +include::mssqlfsx-mad.template.adoc[] + +=== Launch with self-managed Active Directory into an existing VPC +include::mssqlfsx.template.adoc[] diff --git a/docs/generated/parameters/mssqlfci-main.template.adoc b/docs/generated/parameters/mssqlfci-main.template.adoc new file mode 100644 index 0000000..b7128e2 --- /dev/null +++ b/docs/generated/parameters/mssqlfci-main.template.adoc @@ -0,0 +1,76 @@ + +.Network configuration +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|Availability Zones +(`AvailabilityZones`)|`**__Requires input__**`|List of Availability Zones to use for the subnets in the VPC. Note: The logical order is preserved and 2 zones must be provided unless the Third Availability Zone parameter is specified, in which case 3 zones must be provided.|VPC CIDR block +(`VPCCIDR`)|`10.0.0.0/16`|CIDR block for the VPC.|Public subnet 1 CIDR +(`PublicSubnet1CIDR`)|`10.0.128.0/20`|CIDR block for the public subnet 2 located in Availability Zone 2.|Public subnet 2 CIDR +(`PublicSubnet2CIDR`)|`10.0.144.0/20`|CIDR block for the optional public subnet 3 located in Availability Zone 3.|Private subnet 1 CIDR +(`PrivateSubnet1CIDR`)|`10.0.0.0/19`|CIDR block for private subnet 1 located in Availability Zone 1.|Private subnet 2 CIDR +(`PrivateSubnet2CIDR`)|`10.0.32.0/19`|CIDR block for private subnet 2 located in Availability Zone 2. +|=== +.Remote Desktop Gateway configuration +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|Allowed RDGW external access CIDR +(`RDGWCIDR`)|`**__Requires input__**`|Allowed CIDR block for external access to the Remote Desktop Gateway instances.|Number of RDGW hosts +(`NumberOfRDGWHosts`)|`1`|Enter the number of Remote Desktop Gateway hosts to create.|RDGW instance type +(`RDGWInstanceType`)|`t2.large`|Amazon EC2 instance type for the Remote Desktop Gateway instances. +|=== +.Active Directory configuration +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|AD scenario type +(`ADScenarioType`)|`AWS Directory Service for Microsoft AD (Enterprise Edition)`|Select the type of AD DS deployment to use: AWS Directory Service for Microsoft AD or managing your own Amazon EC2 AD instances.|Domain DNS name +(`DomainDNSName`)|`example.com`|Fully qualified domain name (FQDN) of the forest root domain.|Domain NetBIOS name +(`DomainNetBIOSName`)|`example`|NetBIOS name of the domain (up to 15 characters) for users of earlier versions of Windows.|Domain admin user name +(`DomainAdminUser`)|`Admin`|User name for the account that will be added as domain administrator. This is separate from the default administrator account. Note: This user is used for Self-Managed AD Scenario, when using AWS Directory Service for Microsoft AD "Admin" is the default regardless of the value provided. provided.|Domain admin password +(`DomainAdminPassword`)|`**__Requires input__**`|Password for the domain admin user. Must be at least 8 characters containing letters, numbers, and symbols. +|=== +.Self-managed Active Directory configuration +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|Domain controller 1 instance type +(`ADServer1InstanceType`)|`m5.xlarge`|Amazon EC2 instance type for the first Active Directory instance.|Domain controller 1 NetBIOS name +(`ADServer1NetBIOSName`)|`DC1`|NetBIOS name of the first Active Directory server (up to 15 characters).|Domain controller 1 private IP address +(`ADServer1PrivateIP`)|`10.0.0.10`|Fixed private IP for the first Active Directory server located in Availability Zone 1.|Domain controller 2 instance type +(`ADServer2InstanceType`)|`m5.xlarge`|Amazon EC2 instance type for the second Active Directory instance.|Domain controller 2 NetBIOS name +(`ADServer2NetBIOSName`)|`DC2`|NetBIOS name of the second Active Directory server (up to 15 characters).|Domain controller 2 private IP address +(`ADServer2PrivateIP`)|`10.0.32.10`|Fixed private IP for the second Active Directory server located in Availability Zone 2. +|=== +.Amazon EC2 configuration +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|EC2 key pair name +(`KeyPairName`)|`**__Requires input__**`|Name of an existing EC2 key pair. All instances will launch with this key pair.|Server1 NetBIOS Name +(`SqlFSxServerNetBIOSName1`)|`SqlFSx1`|NetBIOS name of the SQL server (up to 15 characters)|Server2 NetBIOS Name +(`SqlFSxServerNetBIOSName2`)|`SqlFSx2`|NetBIOS name of the SQL server (up to 15 characters)|Windows version +(`WindowsVersion`)|`Windows_Server-2019-English-Full-Base*`|Select Windows version to run SQL Server.|Workload Servers EC2 instance type +(`WorkloadInstanceType`)|`r5.2xlarge`|Type of EC2 instance for the Workload instances +|=== +.Amazon FSx Windows File Share Configuration +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|File share volume size +(`FileShareVolumeSize`)|`32`|Capacity of the volume used to store SQL Server files. Minimum value is 32 and maximum 65536. (GB)|File share throughput capacity (MB/s) +(`FileShareThroughputCapacity`)|`8`|File share throughput capacity, in 2 to the n'th power increments, where 3 <= n <= 11, i.e. (between 2^3 and 2^11). +|=== +.Microsoft SQL Server FCI configurations +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|S3 bucket for SQL media +(`MSSQLMediaBucketName`)|`**__Requires input__**`|The S3 bucket name wherefrom MSSQL media can be downloaded. This string can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-).|S3 key for SQL media path +(`MSSQLMediaPathKey`)|`SQLServer2019-x64-ENU.iso`|The S3 bucket key wherefrom MSSQL media can be downloaded. This string can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/).|Windows Server failover cluster name +(`SqlFSxWSFCName`)|`WSFC1`|Windows Server failover cluster name|FCI name +(`SqlFSxFCIName`)|`SqlFCI`|FCI name|SQL administrator account(s) +(`SQLAdminAccounts`)|`example\Admin`|Comma-separated user names will be used as MSSQL DB Administrator. It could be AD domain admin, other local or domain users. +|=== +.AWS Quick Start Configuration +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|Quick Start S3 Bucket Name +(`QSS3BucketName`)|`aws-quickstart`|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 (-).|Quick Start S3 bucket region +(`QSS3BucketRegion`)|`us-east-1`|The AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted. When using your own bucket, you must specify this value.|Quick Start S3 Key Prefix +(`QSS3KeyPrefix`)|`quickstart-microsoft-sql-fci-fsx/`|S3 key prefix for the Quick Start assets. Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/). +|=== \ No newline at end of file diff --git a/docs/generated/parameters/mssqlfsx-mad.template.adoc b/docs/generated/parameters/mssqlfsx-mad.template.adoc new file mode 100644 index 0000000..41b79ba --- /dev/null +++ b/docs/generated/parameters/mssqlfsx-mad.template.adoc @@ -0,0 +1,57 @@ + +.Network Configuration +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|VPC ID +(`VPCID`)|`**__Requires input__**`|ID of your existing VPC for deployment|Private Subnet 1 ID +(`PrivateSubnet1ID`)|`**__Requires input__**`|ID of private subnet 1 in Availability Zone 1 for the Workload (e.g., subnet-a0246dcd)|Private Subnet 2 ID +(`PrivateSubnet2ID`)|`**__Requires input__**`|ID of private subnet 2 in Availability Zone 2 for the Workload (e.g., subnet-a0246dcd)|VPC CIDR +(`VPCcidr`)|`10.0.0.0/16`|CIDR block for the VPC. +|=== +.Microsoft Active Directory Configuration +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|Domain DNS Name +(`DomainDNSName`)|`example.com`|Fully qualified domain name (FQDN) e.g. example.com|Domain NetBIOS Name +(`DomainNetBIOSName`)|`EXAMPLE`|NetBIOS name of the domain (up to 15 characters) for users of earlier versions of Windows e.g. EXAMPLE|Domain Admin User Name +(`DomainAdminUser`)|`StackAdmin`|User name for the account that will be used as Domain Administrator. This is separate from the default "Administrator" account|Domain Admin Password +(`DomainAdminPassword`)|`**__Requires input__**`|Password for the domain admin user.|Security Group ID for AD Domain Members +(`DomainMemberSGID`)|`**__Requires input__**`|ID of the Domain Member Security Group (e.g., sg-9cb7d0e5)|**NO_LABEL** +(`ADDnsIpAddresses`)|`**__Requires input__**`|DNS IP addresses. Comma-separated.|The ID of the AWS Directory Service directory. +(`DirectoryId`)|`**__Requires input__**`|The ID of the AWS Directory Service directory. +|=== +.Amazon EC2 Configuration +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|Server1 NetBIOS Name +(`SqlFSxServerNetBIOSName1`)|`SqlFSx1`|NetBIOS name of the SQL server (up to 15 characters)|Server2 NetBIOS Name +(`SqlFSxServerNetBIOSName2`)|`SqlFSx2`|NetBIOS name of the SQL server (up to 15 characters)|SSH Key Name +(`KeyPairName`)|`**__Requires input__**`|Name of an existing EC2 key pair. All instances will launch with this key pair.|Windows version +(`WindowsVersion`)|`Windows_Server-2019-English-Full-Base*`|Select Windows version to run SQL Server.|Workload Servers Instance Type +(`WorkloadInstanceType`)|`r5.2xlarge`|Type of EC2 instance for the Workload instances +|=== +.Amazon FSx Windows File Share Configuration +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|File share volume size +(`FileShareVolumeSize`)|`32`|Capacity of the volume used to store SQL Server files. Minimum value is 32 and maximum 65536. (GB)|File share throughput capacity (MB/s) +(`FileShareThroughputCapacity`)|`8`|File share throughput capacity, in 2 to the n'th power increments, where 3 <= n <= 11, i.e. (between 2^3 and 2^11). +|=== +.Microsoft SQL Server FCI configurations +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|**NO_LABEL** +(`MSSQLMediaBucketName`)|`ss-experiments`|The S3 bucket name wherefrom MSSQL media can be downloaded. This string can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-).|**NO_LABEL** +(`MSSQLMediaPathKey`)|`quickstart-microsoft-SqlFSx/sql-installation-media/en_sql_server_2019_developer_x64_dvd_baea4195.iso`|The S3 bucket key wherefrom MSSQL media can be downloaded. This string can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/).|**NO_LABEL** +(`SqlFSxWSFCName`)|`WSFC1`|Windows Server failover cluster name|**NO_LABEL** +(`SqlFSxFCIName`)|`SqlFCI`|FCI name|**NO_LABEL** +(`SQLAdminAccounts`)|`contoso\Administrator`|Comma-separated user names will be used as MSSQL DB Administrator. It could be AD domain admin, other local or domain users. +|=== +.AWS Quick Start Configuration +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|Quick Start S3 Bucket Name +(`QSS3BucketName`)|`aws-quickstart`|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 (-).|Quick Start S3 bucket region +(`QSS3BucketRegion`)|`us-east-1`|The AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted. When using your own bucket, you must specify this value.|Quick Start S3 Key Prefix +(`QSS3KeyPrefix`)|`quickstart-microsoft-SqlFSx/`|S3 key prefix for the Quick Start assets. Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/). +|=== \ No newline at end of file diff --git a/docs/generated/parameters/mssqlfsx.template.adoc b/docs/generated/parameters/mssqlfsx.template.adoc new file mode 100644 index 0000000..80e1faf --- /dev/null +++ b/docs/generated/parameters/mssqlfsx.template.adoc @@ -0,0 +1,56 @@ + +.Network Configuration +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|VPC ID +(`VPCID`)|`**__Requires input__**`|ID of your existing VPC for deployment|10.0.0.0/8 +(`VPCcidr`)|`10.0.0.0/16`|CIDR block for the VPC.|Private Subnet 1 ID +(`PrivateSubnet1ID`)|`**__Requires input__**`|ID of private subnet 1 in Availability Zone 1 for the Workload (e.g., subnet-a0246dcd)|Private Subnet 2 ID +(`PrivateSubnet2ID`)|`**__Requires input__**`|ID of private subnet 2 in Availability Zone 2 for the Workload (e.g., subnet-a0246dcd) +|=== +.Microsoft Active Directory Configuration +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|Domain DNS Name +(`DomainDNSName`)|`example.com`|Fully qualified domain name (FQDN) e.g. example.com|Domain NetBIOS Name +(`DomainNetBIOSName`)|`EXAMPLE`|NetBIOS name of the domain (up to 15 characters) for users of earlier versions of Windows e.g. EXAMPLE|Domain Admin User Name +(`DomainAdminUser`)|`StackAdmin`|User name for the account that will be used as Domain Administrator. This is separate from the default "Administrator" account|Domain Admin Password +(`DomainAdminPassword`)|`**__Requires input__**`|Password for the domain admin user.|Security Group ID for AD Domain Members +(`DomainMemberSGID`)|`**__Requires input__**`|ID of the Domain Member Security Group (e.g., sg-9cb7d0e5)|**NO_LABEL** +(`ADDnsIpAddresses`)|`**__Requires input__**`|DNS IP addresses. Comma-separated. +|=== +.Amazon EC2 Configuration +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|Server1 NetBIOS Name +(`SqlFSxServerNetBIOSName1`)|`SqlFSx1`|NetBIOS name of the SQL server (up to 15 characters)|Server2 NetBIOS Name +(`SqlFSxServerNetBIOSName2`)|`SqlFSx2`|NetBIOS name of the SQL server (up to 15 characters)|SSH Key Name +(`KeyPairName`)|`**__Requires input__**`|Name of an existing EC2 key pair. All instances will launch with this key pair.|Windows version +(`WindowsVersion`)|`Windows_Server-2019-English-Full-Base*`|Select Windows version to run SQL Server.|Workload Servers Instance Type +(`WorkloadInstanceType`)|`r5.2xlarge`|Type of EC2 instance for the Workload instances +|=== +.Amazon FSx Windows File Share Configuration +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|File share volume size +(`FileShareVolumeSize`)|`32`|Capacity of the volume used to store SQL Server files. Minimum value is 32 and maximum 65536. (GB)|File share throughput capacity (MB/s) +(`FileShareThroughputCapacity`)|`8`|File share throughput capacity, in 2 to the n'th power increments, where 3 <= n <= 11, i.e. (between 2^3 and 2^11). +|=== +.Microsoft SQL Server FCI configurations +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|**NO_LABEL** +(`MSSQLMediaBucketName`)|`ss-experiments`|The S3 bucket name wherefrom MSSQL media can be downloaded. This string can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-).|**NO_LABEL** +(`MSSQLMediaPathKey`)|`quickstart-microsoft-SqlFSx/sql-installation-media/en_sql_server_2019_developer_x64_dvd_baea4195.iso`|The S3 bucket key wherefrom MSSQL media can be downloaded. This string can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/).|**NO_LABEL** +(`SqlFSxWSFCName`)|`WSFC1`|Windows Server failover cluster name|**NO_LABEL** +(`SqlFSxFCIName`)|`SqlFCI`|FCI name|**NO_LABEL** +(`SQLAdminAccounts`)|`contoso\Administrator`|Comma-separated user names will be used as MSSQL DB Administrator. It could be AD domain admin, other local or domain users. +|=== +.AWS Quick Start Configuration +[width="100%",cols="16%,11%,73%",options="header",] +|=== +|Parameter label (name) |Default value|Description|Quick Start S3 Bucket Name +(`QSS3BucketName`)|`aws-quickstart`|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 (-).|Quick Start S3 bucket region +(`QSS3BucketRegion`)|`us-east-1`|The AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted. When using your own bucket, you must specify this value.|Quick Start S3 Key Prefix +(`QSS3KeyPrefix`)|`quickstart-microsoft-SqlFSx/`|S3 key prefix for the Quick Start assets. Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/). +|=== \ No newline at end of file diff --git a/docs/generated/regions/index.adoc b/docs/generated/regions/index.adoc new file mode 100644 index 0000000..ff7bd09 --- /dev/null +++ b/docs/generated/regions/index.adoc @@ -0,0 +1 @@ +// placeholder diff --git a/docs/generated/services/index.adoc b/docs/generated/services/index.adoc new file mode 100644 index 0000000..ff7bd09 --- /dev/null +++ b/docs/generated/services/index.adoc @@ -0,0 +1 @@ +// placeholder diff --git a/docs/generated/services/metadata.adoc b/docs/generated/services/metadata.adoc new file mode 100644 index 0000000..ff7bd09 --- /dev/null +++ b/docs/generated/services/metadata.adoc @@ -0,0 +1 @@ +// placeholder diff --git a/index.html b/index.html new file mode 100644 index 0000000..2f2b743 --- /dev/null +++ b/index.html @@ -0,0 +1,1415 @@ + + + + + + + + + +SQL Server Failover Cluster Instance on AWS + + + + + +
+
+
+

SQL Server Failover Cluster Instance on AWS

+

Partner Solution Deployment Guide

+
+
+QS +
+
+
+

August 2023
+Sepehr Samiei and Garry Singh, AWS Microsoft Tech Specialist Solutions Architect team
+Dave May, AWS Integration & Automation team

+
+
+
+ +
+
+
+ + + + + +
+ + +Refer to the GitHub repository to view source files, report bugs, submit feature ideas, and post feedback about this Partner Solution. To comment on the documentation, refer to Feedback. +
+
+
+

This Partner Solution was created by Amazon Web Services (AWS). Partner Solutions are automated reference deployments that help people deploy popular technologies on AWS according to AWS best practices. If you’re unfamiliar with AWS Partner Solutions, refer to the AWS Partner Solution General Information Guide.

+
+
+
+
+

Overview

+
+
+

This guide covers the information you need to deploy the SQL Server Failover Cluster Instance Partner Solution in the AWS Cloud.

+
+
+

This Partner Solution is for IT infrastructure architects, database administrators, and DevOps professionals who plan to implement or extend Microsoft SQL Server (MSSQL) using SQL Server on AWS with Windows Server Failover Clustering (WSFC). Unlike the Partner Solution for SQL Server with Always On Replication, this one deploys an Always On Failover Cluster Instance (FCI). It also deploys Amazon FSx for Windows File Server as a network share to store the database files.

+
+
+

This guide does not provide general configuration and usage information for WSFC and MSSQL. For general guidance and best practices, consult the Microsoft product documentation and the Best Practices for Deploying Microsoft SQL Server on AWS whitepaper.

+
+
+

The SQL Server FCI Partner Solution deploys a highly available environment that includes Windows Server and SQL Server running on Amazon Elastic Compute Cloud (EC2). It requires shared storage that is accessible by all nodes within the WSFC cluster. It supports SQL Server 2019 running on Windows Server 2019.

+
+
+

This architecture uses a highly available Multi-AZ Amazon FSx file system as the network share used to store MSSQL database files. The Amazon FSx file system and EC2 Windows instances that host this architecture’s nodes are joined to the same Active Directory domain.

+
+
+ + + + + +
+ + +The "instance" in "failover cluster instance" means something different from the "instance" in "EC2 instance." In this Partner Solution, a failover cluster instance, or FCI, has the appearance of an instance of SQL Server running on a single computer. A failover cluster instance provides failover from one EC2 instance (WSFC node) to another if the current EC2 instance goes down. For an illustration, see Figure 1. +
+
+
+

Traditionally, FCIs have been difficult to deploy and manage. With its Multi-AZ file system option, Amazon FSx provides fully managed file storage. This storage enables the high availability and durability that’s required to run business-critical Microsoft SQL Server database workloads without requiring licenses for each server. Amazon FSx automatically handles failover, simplifying shared storage to host your database deployments while reducing cost.

+
+
+

The automation in this deployment uses AWS Systems Manager Automation, AWS CloudFormation, and Windows PowerShell Desired State Configuration (DSC) to deploy a multi-node SQL Always On FCI. Windows Server Failover Clustering is a prerequisite for deploying an Always On FCI. MSSQL uses WSFC to increase application availability. WSFC provides infrastructure features that complement the high availability and disaster recovery scenarios supported in the AWS Cloud.

+
+
+

FCI, which was introduced with SQL Server 2008 as a high availability feature, continues to be available in all newer versions of MSSQL. When used on premises, SQL Server FCI is often used purely for high availability within a single data center. On AWS, you can use the SQL Server FCI Partner Solution to cover both high availability and disaster recovery requirements.

+
+
+

Since FCIs require shared storage, traditionally they had to be deployed within a single data center. On AWS, the shared storage can span multiple Availability Zones, enabling WSFC clusters to span multiple Availability Zones.

+
+
+

Implementing WSFC on AWS is similar to deploying it on premises as long as you meet these two requirements:

+
+
+
    +
  • +

    The cluster nodes are deployed inside a virtual private cloud (VPC).

    +
  • +
  • +

    The cluster nodes are deployed in separate subnets to provide high availability across multiple Availability Zones.

    +
  • +
+
+
+

This Partner Solution meets these requirements.

+
+
+

For more information:

+
+ +
+
+
+

Costs and licenses

+
+
+

There is no cost to use this Partner Solution, but you will be billed for any AWS services or resources that this Partner Solution deploys. For more information, refer to the AWS Partner Solution General Information Guide.

+
+
+

This Quick Start requires a license for Microsoft SQL Server 2019. You can obtain a trial license from the Microsoft Evaluation Center.

+
+
+

Alternatively, if you’re not using the software for a production environment, you can use the MSSQL Developer Edition. This edition provides the full capabilities of Enterprise Edition without requiring license costs.

+
+
+

This Quick Start deploys MSSQL in Bring-Your-Own-License mode. It does not support deployment of MSSQL license-included Amazon Machine Images (AMIs).

+
+
+
+
+

Architecture

+
+
+

Deploying this Partner Solution with default parameters builds the following SQL Server FCI environment in the +AWS Cloud.

+
+
+
+architecture1 +
+
Figure 1. Partner Solution architecture for SQL Server FCI on AWS
+
+
+

As shown in Figure 1, this Partner Solution sets up the following:

+
+
+
    +
  • +

    A highly available architecture that spans two Availability Zones.*

    +
  • +
  • +

    A VPC configured with public and private subnets, according to AWS best practices, to provide you with your own virtual network on AWS.*

    +
  • +
  • +

    In the public subnets:

    +
    +
      +
    • +

      Managed network address translation (NAT) gateways to allow outbound internet access for resources in the private subnets.*

      +
    • +
    • +

      A Remote Desktop Gateway (RD Gateway) host in an Auto Scaling group to allow inbound Remote Desktop Protocol (RDP) access to EC2 instances in public and private subnets.*

      +
    • +
    +
    +
  • +
  • +

    In the private subnets:

    +
    +
      +
    • +

      Two EC2 instances running Microsoft Windows with SQL Server. These instances are installed as nodes in a WSFC cluster in an Always On FCI configuration across the Availability Zones. Each node contains an Amazon Elastic Block Store (Amazon EBS) root volume.

      +
    • +
    +
    +
  • +
  • +

    An Amazon FSx file system, which the FCI nodes share. SQL Server is installed in this file system. This file system also stores all SQL database and log files, and it acts as the WSFC cluster’s file-share witness.*

    +
  • +
  • +

    AWS Directory Service with a managed directory. The Amazon FSx file system and the EC2 Windows instances that host this architecture’s nodes are joined to the same Active Directory domain.

    +
  • +
  • +

    AWS Secrets Manager keys to store credentials.

    +
  • +
  • +

    An AWS Systems Manager automation document to automate the deployment.

    +
  • +
+
+
+

*The template that deploys the Partner Solution into an existing VPC skips the components marked by asterisks and prompts you for your existing VPC configuration.

+
+
+

Comparison with SQL Server with Always On Replication

+
+

To better understand the architecture of the SQL Server FCI Partner Solution, it’s helpful to compare with the Partner Solution for SQL Server with Always On Replication. Both Partner Solutions are architected to ensure high availability. Both have EC2 instances clustered using WSFC. Both have database files stored in multiple Availability Zones. What’s different is the way each Partner Solution accomplishes database high availability.

+
+
+

The replication-based architecture requires a full installation of SQL Server (Standard or Enterprise edition) on each EC2 instance. Therefore, each EC2 instance requires a SQL Server license.

+
+
+

What makes the FCI-based architecture unique is that it requires only one SQL Server license. With FCI, database-related files aren’t replicated across the WSFC cluster, and SQL Server is not installed in the local file systems. Instead, this Partner Solution creates an Amazon FSx file system and installs SQL Server there. The EC2 instances (FCI nodes) share this file system, which also stores all the SQL database files and log files. In addition, this shared file system acts as the cluster’s file-share witness.

+
+
+
+

Advantages and disadvantages

+
+

The architecture of each SQL Server Partner Solution has advantages and disadvantages.

+
+
+

SQL Server with Always On Replication Partner Solution:

+
+
+
    +
  • +

    Advantages:

    +
    +
      +
    • +

      The EBS volume type, capacity, and IOPS can be configured, allowing flexibility.

      +
    • +
    • +

      It supports both AWS-provided licensing and Bring Your Own License models for Microsoft SQL.

      +
    • +
    +
    +
  • +
  • +

    Disadvantage:

    +
    +
      +
    • +

      Each WSFC node with SQL Server installed requires a SQL Server license, increasing cost.

      +
    • +
    +
    +
  • +
+
+
+

SQL Server FCI Partner Solution:

+
+
+
    +
  • +

    Advantage: It requires only one SQL Server license.

    +
  • +
  • +

    Disadvantages:

    +
    +
      +
    • +

      It relies on an Amazon FSx Multi-AZ file system, which is not supported in all AWS Regions.

      +
    • +
    • +

      It requires the customer to provide a SQL Server .iso file and license since AWS-provided SQL licensing is not supported.

      +
    • +
    +
    +
  • +
+
+
+
+
+
+

Deployment options

+
+
+

This Partner Solution provides the following deployment options:

+
+
+ +
+
+

This Partner Solution provides separate templates for these options. It also lets you configure Classless Inter-Domain Routing (CIDR) blocks, instance types, and SQL Server FCI settings.

+
+
+
+
+

Predeployment steps

+
+
+

The deployment of this Partner Solution requires a copy of the Microsoft SQL Server 2019 installation media as an .iso file. The typical file name is SQLServer2019-x64-ENU.iso.

+
+
+
    +
  1. +

    Download the SQL Server 2019 evaluation edition from the Microsoft Evaluation Center.

    +
    +

    —or—

    +
    +
    +

    Obtain the .iso file from the Microsoft Developer Network if you have an MSDN account with licenses for server software.

    +
    +
  2. +
  3. +

    Upload the .iso file to an S3 bucket.

    +
  4. +
+
+
+
+
+

Deployment steps

+
+
+
    +
  1. +

    Sign in to your AWS account, and launch this Partner Solution, as described under Deployment options. The AWS CloudFormation console opens with a prepopulated template.

    +
  2. +
  3. +

    Choose the correct AWS Region, and then choose Next.

    +
  4. +
  5. +

    On the Create stack page, keep the default setting for the template URL, and then choose Next.

    +
  6. +
  7. +

    On the Specify stack details page, change the stack name if needed. Review the parameters for the template. Provide values for the parameters that require input. For all other parameters, review the default settings and customize them as necessary. When you finish reviewing and customizing the parameters, choose Next.

    +
    + + + + + +
    + + +Unless you’re customizing the Partner Solution templates or are instructed otherwise in this guide’s Predeployment section, don’t change the default settings for the following parameters: QSS3BucketName, QSS3BucketRegion, and QSS3KeyPrefix. Changing the values of these parameters will modify code references that point to the Amazon Simple Storage Service (Amazon S3) bucket name and key prefix. For more information, refer to the AWS Partner Solutions Contributor’s Guide. +
    +
    +
  8. +
  9. +

    On the Configure stack options page, you can specify tags (key-value pairs) for resources in your stack and set advanced options. When you finish, choose Next.

    +
  10. +
  11. +

    On the Review page, review and confirm the template settings. Under Capabilities, select all of the check boxes to acknowledge that the template creates AWS Identity and Access Management (IAM) resources that might require the ability to automatically expand macros.

    +
  12. +
  13. +

    Choose Create stack. The stack takes about 2.25 hours to deploy.

    +
  14. +
  15. +

    Monitor the stack’s status, and when the status is CREATE_COMPLETE, the SQL Server Failover Cluster Instance deployment is ready.

    +
  16. +
  17. +

    To view the created resources, choose the Outputs tab.

    +
  18. +
+
+
+
+
+

Postdeployment steps

+
+
+

Run Windows updates

+
+

In order to ensure the deployed servers' operating systems and installed applications have the latest Microsoft updates, run Windows Update on each server.

+
+
+
    +
  1. +

    Create an RDP session from the Remote Desktop Gateway server to each deployed server.

    +
  2. +
  3. +

    Choose the Settings application.

    +
  4. +
  5. +

    Choose Update & Security.

    +
  6. +
  7. +

    Choose Check for updates.

    +
  8. +
  9. +

    Install any updates and reboot if necessary.

    +
  10. +
+
+
+
+

Test the deployment

+
+
    +
  1. +

    Open an RDP session to one of the two SQL servers.

    +
  2. +
  3. +

    Open Windows Administrative Tools, and launch Failover Cluster Manager.

    +
    +
    +postdeploy1 +
    +
    Figure 2. Failover Cluster Manager
    +
    +
  4. +
  5. +

    Choose Nodes, and ensure that both nodes are online.

    +
    +
    +postdeploy2 +
    +
    Figure 3. Both nodes showing online
    +
    +
  6. +
  7. +

    Select the failover cluster, and verify that both the cluster and the file-share witness are online.

    +
    +
    +postdeploy3 +
    +
    Figure 4. Cluster overview showing that both the cluster and the file-share witness are online
    +
    +
  8. +
  9. +

    Download SQL Server Management Studio (SSMS).

    +
  10. +
  11. +

    Install SQL Server Management Studio. The installation requires a reboot.

    +
  12. +
  13. +

    After rebooting, log back in to the SQL server.

    +
  14. +
  15. +

    Launch SQL Server Management Studio, and connect to the FCI.

    +
    +
    +50% +
    +
    Figure 5. Connecting to the cluster
    +
    +
  16. +
  17. +

    If you are able to log in, the deployment was successful.

    +
  18. +
+
+
+
+
+
+

Troubleshooting

+
+
+

For troubleshooting common Partner Solution issues, refer to the AWS Partner Solution General Information Guide and Troubleshooting CloudFormation.

+
+
+

If the deployment fails, follow these steps:

+
+
+
    +
  1. +

    Open the AWS Systems Manager console.

    +
  2. +
  3. +

    Select your deployment Region.

    +
  4. +
  5. +

    Choose Automation from the left-hand side, and locate the failed automation document.

    +
  6. +
  7. +

    Navigate to the failed step.

    +
  8. +
  9. +

    Expand Output to view the automation logs.

    +
  10. +
  11. +

    Follow the link to Amazon CloudWatch Logs to view detailed automation logs.

    +
  12. +
+
+
+
+
+

Customer responsibility

+
+
+

After you deploy a Partner Solution, confirm that your resources and services are updated and configured—including any required patches—to meet your security and other needs. For more information, refer to the Shared Responsibility Model.

+
+
+
+
+

Feedback

+
+
+

To submit feature ideas and report bugs, use the Issues section of the GitHub repository for this Partner Solution. To submit code, refer to the Partner Solution Contributor’s Guide. To submit feedback on this deployment guide, use the following GitHub links:

+
+ +
+
+
+

Notices

+
+
+

This document is provided for informational purposes only. It represents current AWS product offerings and practices as of the date of issue of this document, which are subject to change without notice. Customers are responsible for making their own independent assessment of the information in this document and any use of AWS products or services, each of which is provided "as is" without warranty of any kind, whether expressed or implied. This document does not create any warranties, representations, contractual commitments, conditions, or assurances from AWS, its affiliates, suppliers, or licensors. The responsibilities and liabilities of AWS to its customers are controlled by AWS agreements, and this document is not part of, nor does it modify, any agreement between AWS and its customers.

+
+
+

The software included with this paper is licensed under the Apache License, version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at https://aws.amazon.com/apache2.0/ or in the accompanying "license" file. This code is distributed on an "as is" basis, without warranties or conditions of any kind, either expressed or implied. Refer to the License for specific language governing permissions and limitations.

+
+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/scripts/AdditionalNodeConfig.ps1 b/scripts/AdditionalNodeConfig.ps1 new file mode 100644 index 0000000..f292e82 --- /dev/null +++ b/scripts/AdditionalNodeConfig.ps1 @@ -0,0 +1,93 @@ +[CmdletBinding()] +param( + + [Parameter(Mandatory=$true)] + [string]$DomainNetBIOSName, + + [Parameter(Mandatory=$true)] + [string]$WSFCNodePrivateIP2, + + [Parameter(Mandatory=$true)] + [string]$ClusterName, + + [Parameter(Mandatory=$true)] + [string]$AdminSecret + +) + +# Getting the DSC Cert Encryption Thumbprint to Secure the MOF File +$DscCertThumbprint = (get-childitem -path cert:\LocalMachine\My | where { $_.subject -eq "CN=AWSQSDscEncryptCert" }).Thumbprint +# Getting Password from Secrets Manager for AD Admin User +$AdminUser = ConvertFrom-Json -InputObject (Get-SECSecretValue -SecretId $AdminSecret).SecretString +$ClusterAdminUser = $DomainNetBIOSName + '\' + $AdminUser.UserName +# Creating Credential Object for Administrator +$Credentials = (New-Object PSCredential($ClusterAdminUser,(ConvertTo-SecureString $AdminUser.Password -AsPlainText -Force))) + +$ConfigurationData = @{ + AllNodes = @( + @{ + NodeName="*" + CertificateFile = "C:\AWSQuickstart\publickeys\AWSQSDscPublicKey.cer" + Thumbprint = $DscCertThumbprint + PSDscAllowDomainUser = $true + }, + @{ + NodeName = 'localhost' + } + ) +} + +Configuration AdditionalWSFCNode { + param( + [PSCredential] $Credentials + ) + + Import-Module -Name xFailOverCluster + Import-Module -Name PSDscResources + + Import-DscResource -ModuleName xFailOverCluster + Import-DscResource -ModuleName PSDscResources + + Node 'localhost'{ + + + WindowsFeature AddFailoverFeature { + Ensure = 'Present' + Name = 'Failover-clustering' + } + + WindowsFeature AddRemoteServerAdministrationToolsClusteringFeature { + Ensure = 'Present' + Name = 'RSAT-Clustering-Mgmt' + DependsOn = '[WindowsFeature]AddFailoverFeature' + } + + WindowsFeature AddRemoteServerAdministrationToolsClusteringPowerShellFeature { + Ensure = 'Present' + Name = 'RSAT-Clustering-PowerShell' + DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringFeature' + } + + WindowsFeature AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature { + Ensure = 'Present' + Name = 'RSAT-Clustering-CmdInterface' + DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringPowerShellFeature' + } + + xWaitForCluster WaitForCluster { + Name = $ClusterName + RetryIntervalSec = 10 + RetryCount = 60 + DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature' + } + + xCluster JoinNodeToCluster { + Name = $ClusterName + StaticIPAddress = $WSFCNodePrivateIP2 + DomainAdministratorCredential = $Credentials + DependsOn = '[xWaitForCluster]WaitForCluster' + } + } +} + +AdditionalWSFCNode -OutputPath 'C:\AWSQuickstart\AdditionalWSFCNode' -ConfigurationData $ConfigurationData -Credentials $Credentials \ No newline at end of file diff --git a/scripts/Configure-MAD-Permissions.ps1 b/scripts/Configure-MAD-Permissions.ps1 new file mode 100644 index 0000000..80cd119 --- /dev/null +++ b/scripts/Configure-MAD-Permissions.ps1 @@ -0,0 +1,38 @@ +[CmdletBinding()] +param( + + [Parameter(Mandatory=$true)] + [string]$AdminSecret, + + [Parameter(Mandatory=$true)] + [string]$DomainNetBIOSName, + + [Parameter(Mandatory=$true)] + [string]$wsfcName +) + + +$HostName = hostname +$AdminUser = ConvertFrom-Json -InputObject (Get-SECSecretValue -SecretId $AdminSecret).SecretString +$ClusterAdminUser = $DomainNetBIOSName + '\' + $AdminUser.UserName +$Credentials = (New-Object PSCredential($ClusterAdminUser,(ConvertTo-SecureString $AdminUser.Password -AsPlainText -Force))) +$wsfcCN = $wsfcName +Invoke-Command -scriptblock { + $computer = get-adcomputer $Using:wsfcCN + $discard,$OU = $computer -split ',',2 + $acl = get-acl "ad:$OU" + $acl.access #to get access right of the OU + $sid = [System.Security.Principal.SecurityIdentifier] $computer.SID + $objectguid1 = new-object Guid bf967a86-0de6-11d0-a285-00aa003049e2 # is the rightsGuid for Create Computer Object class + $inheritedobjectguid = new-object Guid bf967aa5-0de6-11d0-a285-00aa003049e2 # is the schemaIDGuid for the OU + $identity = [System.Security.Principal.IdentityReference] $SID + $adRights = [System.DirectoryServices.ActiveDirectoryRights] "CreateChild" + $adRights2 = [System.DirectoryServices.ActiveDirectoryRights] "ReadProperty" + $type = [System.Security.AccessControl.AccessControlType] "Allow" + $inheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance] "All" + $ace1 = new-object System.DirectoryServices.ActiveDirectoryAccessRule $identity,$adRights,$type,$objectGuid1,$inheritanceType,$inheritedobjectguid + $ACE2 = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $identity,$adRights2,$type,$inheritanceType + $acl.AddAccessRule($ace1) + $acl.AddAccessRule($ACE2) + Set-acl -aclobject $acl "ad:$OU" +} -Credential $Credentials -ComputerName $HostName -Authentication credssp \ No newline at end of file diff --git a/scripts/DomainJoin.ps1 b/scripts/DomainJoin.ps1 new file mode 100644 index 0000000..a2878d9 --- /dev/null +++ b/scripts/DomainJoin.ps1 @@ -0,0 +1,66 @@ +[CmdletBinding()] +# Incoming Parameters for Script, CloudFormation\SSM Parameters being passed in +param( + [Parameter(Mandatory=$true)] + [string]$InstanceName, + + [Parameter(Mandatory=$true)] + [string]$DomainNetBIOSName, + + [Parameter(Mandatory=$true)] + [string]$DomainDNSName, + + [Parameter(Mandatory=$true)] + [string]$AdminSecret +) + +# Formatting AD Admin User to proper format for JoinDomain DSC Resources in this Script +$DomainAdmin = 'Domain\User' -replace 'Domain',$DomainNetBIOSName -replace 'User',$UserName +$Admin = ConvertFrom-Json -InputObject (Get-SECSecretValue -SecretId $AdminSecret).SecretString +$AdminUser = $DomainNetBIOSName + '\' + $Admin.UserName +# Creating Credential Object for Administrator +$Credentials = (New-Object PSCredential($AdminUser,(ConvertTo-SecureString $Admin.Password -AsPlainText -Force))) +# Getting the DSC Cert Encryption Thumbprint to Secure the MOF File +$DscCertThumbprint = (get-childitem -path cert:\LocalMachine\My | where { $_.subject -eq "CN=AWSQSDscEncryptCert" }).Thumbprint +# Getting the Name Tag of the Instance +# $NameTag = (Get-EC2Tag -Filter @{ Name="resource-id";Values=(Invoke-RestMethod -Method Get -Uri http://169.254.169.254/latest/meta-data/instance-id)}| Where-Object { $_.Key -eq "Name" }) +# $NewName = $NameTag.Value +$NewName = $InstanceName +$DNSName = $DomainDNSName +# Creating Configuration Data Block that has the Certificate Information for DSC Configuration Processing +$ConfigurationData = @{ + AllNodes = @( + @{ + NodeName="*" + CertificateFile = "C:\AWSQuickstart\publickeys\AWSQSDscPublicKey.cer" + Thumbprint = $DscCertThumbprint + PSDscAllowDomainUser = $true + }, + @{ + NodeName = 'localhost' + } + ) +} + +Configuration DomainJoin { + param( + [PSCredential] $Credentials + ) + + Import-Module -Name PSDesiredStateConfiguration + Import-Module -Name ComputerManagementDsc + + Import-DscResource -Module PSDesiredStateConfiguration + Import-DscResource -Module ComputerManagementDsc + + Node 'localhost' { + + Computer JoinDomain { + Name = $NewName + DomainName = $DNSName + Credential = $Credentials + } + } +} + +DomainJoin -OutputPath 'C:\AWSQuickstart\DomainJoin' -ConfigurationData $ConfigurationData -Credentials $Credentials \ No newline at end of file diff --git a/scripts/LCM-Config.ps1 b/scripts/LCM-Config.ps1 new file mode 100644 index 0000000..3bda006 --- /dev/null +++ b/scripts/LCM-Config.ps1 @@ -0,0 +1,21 @@ +# This block sets the LCM configuration to what we need for QS +[DSCLocalConfigurationManager()] +configuration LCMConfig +{ + Node 'localhost' { + Settings { + RefreshMode = 'Push' + ActionAfterReboot = 'StopConfiguration' + RebootNodeIfNeeded = $false + CertificateId = $DscCertThumbprint + } + } +} + +$DscCertThumbprint = [string](get-childitem -path cert:\LocalMachine\My | where { $_.subject -eq "CN=AWSQSDscEncryptCert" }).Thumbprint + +#Generates MOF File for LCM +LCMConfig -OutputPath 'C:\AWSQuickstart\LCMConfig' + +# Sets LCM Configuration to MOF generated in previous command +Set-DscLocalConfigurationManager -Path 'C:\AWSQuickstart\LCMConfig' diff --git a/scripts/Node1Config.ps1 b/scripts/Node1Config.ps1 new file mode 100644 index 0000000..3cd40e9 --- /dev/null +++ b/scripts/Node1Config.ps1 @@ -0,0 +1,117 @@ +[CmdletBinding()] +param( + + [Parameter(Mandatory=$true)] + [string]$DomainNetBIOSName, + + [Parameter(Mandatory=$true)] + [string]$DomainDnsName, + + [Parameter(Mandatory=$true)] + [string]$WSFCNode1PrivateIP2, + + [Parameter(Mandatory=$true)] + [string]$ClusterName, + + [Parameter(Mandatory=$true)] + [string]$AdminSecret, + + [Parameter(Mandatory=$false)] + [string]$FileServerNetBIOSName + +) + +# Getting the DSC Cert Encryption Thumbprint to Secure the MOF File +$DscCertThumbprint = (get-childitem -path cert:\LocalMachine\My | where { $_.subject -eq "CN=AWSQSDscEncryptCert" }).Thumbprint +# Getting Password from Secrets Manager for AD Admin User +$AdminUser = ConvertFrom-Json -InputObject (Get-SECSecretValue -SecretId $AdminSecret).SecretString +$ClusterAdminUser = $DomainNetBIOSName + '\' + $AdminUser.UserName +# Creating Credential Object for Administrator +$Credentials = (New-Object PSCredential($ClusterAdminUser,(ConvertTo-SecureString $AdminUser.Password -AsPlainText -Force))) + +if ($FileServerNetBIOSName) { + $ShareName = "\\" + $FileServerNetBIOSName + "\SqlWitnessShare" +} + +$ConfigurationData = @{ + AllNodes = @( + @{ + NodeName="*" + CertificateFile = "C:\AWSQuickstart\publickeys\AWSQSDscPublicKey.cer" + Thumbprint = $DscCertThumbprint + PSDscAllowDomainUser = $true + }, + @{ + NodeName = 'localhost' + } + ) +} + +Configuration WSFCNode1Config { + param( + [PSCredential] $Credentials + ) + + Import-Module -Name PSDscResources + Import-Module -Name xFailOverCluster + Import-Module -Name xActiveDirectory + + Import-DscResource -Module PSDscResources + Import-DscResource -ModuleName xFailOverCluster + Import-DscResource -ModuleName xActiveDirectory + + Node 'localhost' { + WindowsFeature RSAT-AD-PowerShell { + Name = 'RSAT-AD-PowerShell' + Ensure = 'Present' + } + + WindowsFeature AddFailoverFeature { + Ensure = 'Present' + Name = 'Failover-clustering' + DependsOn = '[WindowsFeature]RSAT-AD-PowerShell' + } + + WindowsFeature AddRemoteServerAdministrationToolsClusteringFeature { + Ensure = 'Present' + Name = 'RSAT-Clustering-Mgmt' + DependsOn = '[WindowsFeature]AddFailoverFeature' + } + + WindowsFeature AddRemoteServerAdministrationToolsClusteringPowerShellFeature { + Ensure = 'Present' + Name = 'RSAT-Clustering-PowerShell' + DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringFeature' + } + + WindowsFeature AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature { + Ensure = 'Present' + Name = 'RSAT-Clustering-CmdInterface' + DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringPowerShellFeature' + } + + xCluster CreateCluster { + Name = $ClusterName + StaticIPAddress = $WSFCNode1PrivateIP2 + DomainAdministratorCredential = $Credentials + DependsOn = '[WindowsFeature]AddRemoteServerAdministrationToolsClusteringCmdInterfaceFeature' + } + + if ($FileServerNetBIOSName) { + xClusterQuorum 'SetQuorumToNodeAndFileShareMajority' { + IsSingleInstance = 'Yes' + Type = 'NodeAndFileShareMajority' + Resource = $ShareName + DependsOn = '[xCluster]CreateCluster' + } + } else { + xClusterQuorum 'SetQuorumToNodeMajority' { + IsSingleInstance = 'Yes' + Type = 'NodeMajority' + DependsOn = '[xCluster]CreateCluster' + } + } + } +} + +WSFCNode1Config -OutputPath 'C:\AWSQuickstart\WSFCNode1Config' -ConfigurationData $ConfigurationData -Credentials $Credentials diff --git a/scripts/complete-fci.ps1 b/scripts/complete-fci.ps1 new file mode 100644 index 0000000..ae1690a --- /dev/null +++ b/scripts/complete-fci.ps1 @@ -0,0 +1,54 @@ + [CmdletBinding()] +param( + + [Parameter(Mandatory=$true)] + [string]$AdminSecret, + + [Parameter(Mandatory=$true)] + [string]$DomainNetBIOSName, + + [Parameter(Mandatory=$true)] + [string]$FileServerPath, + + [Parameter(Mandatory=$true)] + [string]$Node1FciIp, + + [Parameter(Mandatory=$true)] + [string]$Node1SubnetMask, + + [Parameter(Mandatory=$true)] + [string]$Node2FciIp, + + [Parameter(Mandatory=$true)] + [string]$Node2SubnetMask, + + [Parameter(Mandatory=$true)] + [string]$FCIName, + + [Parameter(Mandatory=$true)] + [string]$SQLAdminAccounts + +) + +# Creating Credential Object for Administrator +$AdminUser = ConvertFrom-Json -InputObject (Get-SECSecretValue -SecretId $AdminSecret).SecretString +$ClusterAdminUser = $DomainNetBIOSName + '\' + $AdminUser.UserName +$Credentials = (New-Object PSCredential($ClusterAdminUser,(ConvertTo-SecureString $AdminUser.Password -AsPlainText -Force))) + +#https://docs.microsoft.com/en-us/sql/database-engine/install-windows/install-sql-server-from-the-command-prompt?view=sql-server-ver15 + +$HostName = hostname + +#Need to run cluster validation first +Invoke-Command -scriptblock { Test-Cluster } -Credential $Credentials -ComputerName $HostName -Authentication credssp + +$mediaExtractPath = 'C:\SQLinstallmedia' +$sqlRootPath = "\\{0}\SqlShare\mssql" -f $FileServerPath +$sqlDataPath = "\\{0}\SqlShare\mssql\data" -f $FileServerPath +$sqlLogPath = "\\{0}\SqlShare\mssql\logs" -f $FileServerPath + + +$arguments = '/QUIET /ACTION=CompleteFailoverCluster /InstanceName=MSSQLSERVER /INDICATEPROGRESS=FALSE /FAILOVERCLUSTERNETWORKNAME={0} /FAILOVERCLUSTERIPADDRESSES="IPv4;{1};Cluster Network 1;{2}" "IPv4;{3};Cluster Network 2;{4}" /CONFIRMIPDEPENDENCYCHANGE=TRUE /FAILOVERCLUSTERGROUP="SQL Server (MSSQLSERVER)" /INSTALLSQLDATADIR="C:\Program Files\Microsoft SQL Server" /SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS" /SQLSYSADMINACCOUNTS={5} /INSTALLSQLDATADIR={6} /SQLUSERDBDIR={7} /SQLUSERDBLOGDIR={8}' -f $FCIName, $Node1FciIp, $Node1SubnetMask, $Node2FciIp, $Node2SubnetMask, $SQLAdminAccounts, $sqlRootPath, $sqlDataPath, $sqlLogPath +Invoke-Command -scriptblock { + Start-Process -FilePath C:\SQLinstallmedia\setup.exe -ArgumentList $Using:arguments -Wait -NoNewWindow +} -Credential $Credentials -ComputerName $HostName -Authentication credssp diff --git a/scripts/create-ca-share.ps1 b/scripts/create-ca-share.ps1 new file mode 100644 index 0000000..e957cd2 --- /dev/null +++ b/scripts/create-ca-share.ps1 @@ -0,0 +1,44 @@ +[CmdletBinding()] +param( + + [Parameter(Mandatory=$true)] + [string]$DomainNetBIOSName, + + [Parameter(Mandatory=$true)] + [string]$AdminSecret, + + [Parameter(Mandatory=$false)] + [string]$FSxRemoteAdminEndpoint + +) + + +$AdminUser = ConvertFrom-Json -InputObject (Get-SECSecretValue -SecretId $AdminSecret).SecretString +$ClusterAdminUser = $DomainNetBIOSName + '\' + $AdminUser.UserName +# Creating Credential Object for Administrator +$Credentials = (New-Object PSCredential($ClusterAdminUser,(ConvertTo-SecureString $AdminUser.Password -AsPlainText -Force))) + +#Configure CA SMB share on FSx +$shareName = "SqlShare" +Invoke-Command -ComputerName $FSxRemoteAdminEndpoint -ConfigurationName FSxRemoteAdmin -scriptblock { + New-FSxSmbShare -Name $Using:shareName -Path "D:\share\" -Description "CA share for MSSQL FCI" -ContinuouslyAvailable $True -Credential $Using:Credentials +} -Credential $Credentials + +Invoke-Command -ComputerName $FSxRemoteAdminEndpoint -ConfigurationName FSxRemoteAdmin -scriptblock { + Grant-FSxSmbShareAccess -Name $Using:shareName -AccountName $Using:ClusterAdminUser -AccessRight Full -force +} -Credential $Credentials + +#Configure Witness SMB share on FSx +$WitnessshareName = "SqlWitnessShare" +Invoke-Command -ComputerName $FSxRemoteAdminEndpoint -ConfigurationName FSxRemoteAdmin -scriptblock { + New-FSxSmbShare -Name $Using:WitnessshareName -Path "D:\share\" -Description "Witness share for MSSQL FCI" -ContinuouslyAvailable $True -Credential $Using:Credentials +} -Credential $Credentials + +Invoke-Command -ComputerName $FSxRemoteAdminEndpoint -ConfigurationName FSxRemoteAdmin -scriptblock { + Grant-FSxSmbShareAccess -Name $Using:WitnessshareName -AccountName Everyone -AccessRight Change -force +} -Credential $Credentials + + + + + diff --git a/scripts/enable-CredSSP.ps1 b/scripts/enable-CredSSP.ps1 new file mode 100644 index 0000000..35be13a --- /dev/null +++ b/scripts/enable-CredSSP.ps1 @@ -0,0 +1,13 @@ +[CmdletBinding()] +param( + + [Parameter(Mandatory=$true)] + [string]$DomainDNSName + +) + + +$HostName = hostname +$HostAddress = "{0}.{1}" -f $HostName, $DomainDNSName +Enable-WSManCredSSP -Role "Server" -Force +Enable-WSManCredSSP -Role "Client" -DelegateComputer $HostAddress -Force \ No newline at end of file diff --git a/scripts/install-dsc-modules.ps1 b/scripts/install-dsc-modules.ps1 new file mode 100644 index 0000000..5247e8f --- /dev/null +++ b/scripts/install-dsc-modules.ps1 @@ -0,0 +1,26 @@ +[CmdletBinding()] +param() + +"Setting up Powershell Gallery to Install DSC Modules" +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force +Set-PSRepository -Name PSGallery -InstallationPolicy Trusted + +"Installing the needed Powershell DSC modules for this Quick Start" +Install-Module -Name ComputerManagementDsc +Install-Module -Name "xFailOverCluster" +Install-Module -Name PSDscResources +Install-Module -Name xSmbShare +Install-Module -Name "xActiveDirectory" + +"Disabling Windows Firewall" +Get-NetFirewallProfile | Set-NetFirewallProfile -Enabled False + +"Creating Directory for DSC Public Cert" +New-Item -Path C:\AWSQuickstart\publickeys -ItemType directory + +"Setting up DSC Certificate to Encrypt Credentials in MOF File" +$cert = New-SelfSignedCertificate -Type DocumentEncryptionCertLegacyCsp -DnsName 'AWSQSDscEncryptCert' -HashAlgorithm SHA256 +# Exporting the public key certificate +$cert | Export-Certificate -FilePath "C:\AWSQuickstart\publickeys\AWSQSDscPublicKey.cer" -Force + diff --git a/scripts/prepare-fci.ps1 b/scripts/prepare-fci.ps1 new file mode 100644 index 0000000..49d14aa --- /dev/null +++ b/scripts/prepare-fci.ps1 @@ -0,0 +1,51 @@ +[CmdletBinding()] +param( + + [Parameter(Mandatory=$true)] + [string]$AdminSecret, + + [Parameter(Mandatory=$true)] + [string]$DomainNetBIOSName, + + [Parameter(Mandatory=$true)] + [string]$SqlUserSecret, + + [Parameter(Mandatory=$true)] + [string]$MSSQLMediaBucket, + + [Parameter(Mandatory=$true)] + [string]$MSSQLMediaKey + +) + +$HostName = hostname + +# Creating Credential Object for Administrator +$AdminUser = ConvertFrom-Json -InputObject (Get-SECSecretValue -SecretId $AdminSecret).SecretString +$ClusterAdminUser = $DomainNetBIOSName + '\' + $AdminUser.UserName +$Credentials = (New-Object PSCredential($ClusterAdminUser,(ConvertTo-SecureString $AdminUser.Password -AsPlainText -Force))) + +#Retrieving MSSQL service account +$SqlUser = ConvertFrom-Json -InputObject (Get-SECSecretValue -SecretId $SqlUserSecret).SecretString +$SqlUserName = $DomainNetBIOSName + '\' + $SqlUser.UserName +$SqlUserPassword = $SqlUser.Password + +#Acquiring MSSQL installation media from S3 +$mediaIsoPath = 'c:\mssql-setup-media\en_sql_server_2019_developer_x64_dvd_baea4195.iso' +$mediaExtractPath = 'C:\SQLinstallmedia' + +Copy-S3Object -BucketName $MSSQLMediaBucket -Key $MSSQLMediaKey -LocalFile $mediaIsoPath + +#Mounting and extracting installation media files +New-Item -Path $mediaExtractPath -ItemType Directory +$mountResult = Mount-DiskImage -ImagePath $mediaIsoPath -PassThru +$volumeInfo = $mountResult | Get-Volume +$driveInfo = Get-PSDrive -Name $volumeInfo.DriveLetter +Copy-Item -Path ( Join-Path -Path $driveInfo.Root -ChildPath '*' ) -Destination $mediaExtractPath -Recurse +Dismount-DiskImage -ImagePath $mediaIsoPath + +#Prepare FCI installation +$arguments = '/ACTION="PrepareFailoverCluster" /IAcceptSQLServerLicenseTerms="True" /IACCEPTROPENLICENSETERMS="False" /SUPPRESSPRIVACYSTATEMENTNOTICE="True" /ENU="True" /QUIET="True" /UpdateEnabled="False" /USEMICROSOFTUPDATE="False" /SUPPRESSPAIDEDITIONNOTICE="True" /UpdateSource="MU" /FEATURES=SQLENGINE,REPLICATION,FULLTEXT,DQ /HELP="False" /INDICATEPROGRESS="False" /X86="False" /INSTANCENAME="MSSQLSERVER" /INSTALLSHAREDDIR="C:\Program Files\Microsoft SQL Server" /INSTALLSHAREDWOWDIR="C:\Program Files (x86)\Microsoft SQL Server" /INSTANCEID="MSSQLSERVER" /INSTANCEDIR="C:\Program Files\Microsoft SQL Server" /AGTSVCACCOUNT="{0}" /AGTSVCPASSWORD="{1}" /FILESTREAMLEVEL="0" /SQLSVCACCOUNT="{0}" /SQLSVCPASSWORD="{1}" /SQLSVCINSTANTFILEINIT="False" /FTSVCACCOUNT="NT Service\MSSQLFDLauncher"' -f $SqlUserName, $SqlUserPassword +Invoke-Command -scriptblock { + Start-Process -FilePath C:\SQLinstallmedia\setup.exe -ArgumentList $Using:arguments -Wait -NoNewWindow +} -Credential $Credentials -ComputerName $HostName -Authentication credssp \ No newline at end of file diff --git a/scripts/sample_userdata.sh b/scripts/sample_userdata.sh deleted file mode 100644 index a84a81f..0000000 --- a/scripts/sample_userdata.sh +++ /dev/null @@ -1 +0,0 @@ -#UserData and or scripts should be stored here, but only for source code revision purposes cf templatess should refer to prod s3bucket allways diff --git a/submodules/cfn-ps-aws-vpc b/submodules/cfn-ps-aws-vpc new file mode 160000 index 0000000..5e14469 --- /dev/null +++ b/submodules/cfn-ps-aws-vpc @@ -0,0 +1 @@ +Subproject commit 5e1446917af2851e43b154297aa601403d535799 diff --git a/submodules/cfn-ps-microsoft-activedirectory b/submodules/cfn-ps-microsoft-activedirectory new file mode 160000 index 0000000..51a35fc --- /dev/null +++ b/submodules/cfn-ps-microsoft-activedirectory @@ -0,0 +1 @@ +Subproject commit 51a35fc03bec87d8937dd8cce8c3c2cf09fedee3 diff --git a/submodules/cfn-ps-microsoft-rdgateway b/submodules/cfn-ps-microsoft-rdgateway new file mode 160000 index 0000000..dbcd3a6 --- /dev/null +++ b/submodules/cfn-ps-microsoft-rdgateway @@ -0,0 +1 @@ +Subproject commit dbcd3a6ec33cfe2ac353b2ffe8d65622dad05228 diff --git a/templates/another-workload.template.yaml b/templates/another-workload.template.yaml deleted file mode 100644 index 6865197..0000000 --- a/templates/another-workload.template.yaml +++ /dev/null @@ -1,99 +0,0 @@ ---- -AWSTemplateFormatVersion: '2010-09-09' -Description: Sample Stack (qs-def3wdf) -Metadata: - cfn-lint: - config: - ignore_checks: - - W9002 - - W9003 -Parameters: - Param1: - Description: Param1 - Type: String -Resources: - LambdaExecutionRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: '2012-10-17' - Statement: - - Effect: Allow - Principal: - Service: - - lambda.amazonaws.com - Action: - - sts:AssumeRole - Path: "/" - Policies: - - PolicyName: lambda_policy - PolicyDocument: - Version: '2012-10-17' - Statement: - - Effect: Allow - Action: - - logs:CreateLogGroup - - logs:CreateLogStream - - logs:PutLogEvents - Resource: !Sub 'arn:${AWS::Partition}:logs:*:*:*' - GenID: - Type: AWS::Lambda::Function - Properties: - Code: - ZipFile: - Fn::Join: - - "\n" - - - import random - - import json - - import cfnresponse - - from cfnresponse import send, SUCCESS - - 'def handler(event, context):' - - " if event['RequestType'] == 'Delete':" - - " send(event, context, 'SUCCESS', {})" - - " return" - - " if event['RequestType'] == 'Create':" - - ' token= "%0x.%0x" % (random.SystemRandom().getrandbits(3*8), - random.SystemRandom().getrandbits(8*8))' - - " responseData = {}" - - " responseData['Data'] = token" - - " send(event, context, 'SUCCESS', responseData)" - - " return token" - Handler: index.handler - Runtime: python3.7 - Timeout: 5 - Role: - Fn::GetAtt: - - LambdaExecutionRole - - Arn - GetID: - Type: Custom::GenerateID - Version: '1.0' - Properties: - ServiceToken: - Fn::GetAtt: - - GenID - - Arn - ResponseURL: - Fn::Join: - - '' - - - http://ResponseURL - - Ref: AWS::StackId - - RequestId - StackId: - Ref: AWS::StackId - ResourceProperties: - RequestType: Create - RequestId: - Fn::Join: - - '' - - - Ref: AWS::StackId - - RequestId - LogicalResourceId: GenIDLogicalResourceId -Outputs: - ClusterID: - Value: - Fn::GetAtt: - - GetID - - Data - Param1Output: - Value: !Ref Param1 diff --git a/templates/functions/source/_lambda_source b/templates/functions/source/_lambda_source deleted file mode 100644 index d36a1e1..0000000 --- a/templates/functions/source/_lambda_source +++ /dev/null @@ -1,3 +0,0 @@ -# -# Lambda source files go here -# diff --git a/templates/mssqlfsx-mad.template.yaml b/templates/mssqlfsx-mad.template.yaml new file mode 100644 index 0000000..d08e58d --- /dev/null +++ b/templates/mssqlfsx-mad.template.yaml @@ -0,0 +1,1413 @@ +AWSTemplateFormatVersion: '2010-09-09' +Description: This workload template deploys a SqlFSx instance in an existing VPC. **IMPORTANT** This template creates EC2 instances and related resources. You will be billed for the AWS resources used if + you create a stack from this template. (qs-1rqmju6ap) +Metadata: + cfn-lint: + config: + ignore_checks: + - W9006 + - E9101 + - E3030 #Invalid runtime - false positive + - E3031 #False positive + QuickStartDocumentation: + EntrypointName: Parameters for deploying into an existing VPC with AWS Managed Microsoft AD + Order: '2' + AWS::CloudFormation::Interface: + ParameterGroups: + - Label: + default: Network configuration + Parameters: + - VPCID + - PrivateSubnet1ID + - PrivateSubnet2ID + - VPCcidr + - Label: + default: Microsoft Active Directory configuration + Parameters: + - DomainDNSName + - DomainNetBIOSName + - DomainAdminUser + - DomainAdminPassword + - DomainMemberSGID + - ADDnsIpAddresses + - DirectoryId + - Label: + default: Amazon EC2 configuration + Parameters: + - SqlFSxServerNetBIOSName1 + - SqlFSxServerNetBIOSName2 + - KeyPairName + - WindowsVersion + - WorkloadInstanceType + - Label: + default: Amazon FSx Windows file-share configuration + Parameters: + - FileShareVolumeSize + - FileShareThroughputCapacity + - Label: + default: Microsoft SQL Server FCI configurations + Parameters: + - MSSQLMediaBucketName + - MSSQLMediaPathKey + - SqlFSxWSFCName + - SqlFSxFCIName + - SQLAdminAccounts + - Label: + default: Application Insights configuration + Parameters: + - EnableAppInsights + - ApplicationName + - ResourceGroupName + - 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 + KeyPairName: + default: SSH key name + PrivateSubnet1ID: + default: Private subnet 1 ID + PrivateSubnet2ID: + default: Private subnet 2 ID + VPCcidr: + default: VPC CIDR + QSS3BucketName: + default: Quick Start S3 bucket name + QSS3BucketRegion: + default: Quick Start S3 bucket Region + QSS3KeyPrefix: + default: Quick Start S3 key prefix + VPCID: + default: VPC ID + WorkloadInstanceType: + default: Workload servers instance type + DomainAdminPassword: + default: Domain administrator password + DomainAdminUser: + default: Domain administrator user name + DomainDNSName: + default: Domain DNS name + DomainMemberSGID: + default: Security group ID for Active Directory domain members + DomainNetBIOSName: + default: Domain NetBIOS name + MSSQLMediaBucketName: + default: S3 bucket for SQL media + MSSQLMediaPathKey: + default: S3 key for SQL media path + SqlFSxWSFCName: + default: Windows Server Failover Cluster (WSFC) name + SqlFSxFCIName: + default: Failover cluster instance (FCI) name + SQLAdminAccounts: + default: SQL administrator account or accounts + ADDnsIpAddresses: + default: Active Directory DNS IP addresses + DirectoryId: + default: ID of the AWS Directory Service directory + SqlFSxServerNetBIOSName1: + default: Server1 NetBIOS name + SqlFSxServerNetBIOSName2: + default: Server2 NetBIOS name + WindowsVersion: + default: Windows version + FileShareVolumeSize: + default: File-share volume size + FileShareThroughputCapacity: + default: File-share throughput capacity +Parameters: + ApplicationName: + Default: MSSQLFCI + 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 + KeyPairName: + Description: Name of an existing EC2 key pair. All instances will launch with this key pair. + Type: AWS::EC2::KeyPair::KeyName + FileShareVolumeSize: + Default: 32 + Description: Capacity (GB) of the volume used to store SQL Server files. Minimum value is 32 and maximum 65536. + Type: Number + MinValue: 32 + MaxValue: 65536 + FileShareThroughputCapacity: + Default: 8 + Description: File-share throughput capacity (MB/s) in 2 to the nth power increments, where 3 ≤ n ≤ 11, i.e. between 2^3 and 2^11. + Type: Number + AllowedValues: + - 8 + - 16 + - 32 + - 64 + - 128 + - 256 + - 512 + - 1024 + - 2048 + PrivateSubnet1ID: + Description: ID of private subnet 1 in Availability Zone 1 for the workload (e.g., subnet-a0246dcd). + Type: AWS::EC2::Subnet::Id + PrivateSubnet2ID: + Description: ID of private subnet 2 in Availability Zone 2 for the workload (e.g., subnet-a0246dcd). + Type: AWS::EC2::Subnet::Id + 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-ia-us-east-1 + 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: cfn-ps-microsoft-sql-fci-fsx/ + 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 + VPCID: + Description: ID of your existing VPC for deployment. + Type: AWS::EC2::VPC::Id + SqlFSxServerNetBIOSName1: + AllowedPattern: '[a-zA-Z0-9\-]+' + Default: SqlFSx1 + Description: NetBIOS name of the primary instance. You can use up to 15 characters. + MaxLength: '15' + MinLength: '1' + Type: String + SqlFSxServerNetBIOSName2: + AllowedPattern: '[a-zA-Z0-9\-]+' + Default: SqlFSx2 + Description: NetBIOS name of the secondary instance. You can use up to 15 characters. + MaxLength: '15' + MinLength: '1' + Type: String + SqlFSxWSFCName: + AllowedPattern: '[a-zA-Z0-9\-]+' + Default: WSFC1 + Description: Name of the Windows Server Failover Cluster (WSFC). You can use up to 15 characters. + MaxLength: '15' + MinLength: '1' + Type: String + SqlFSxFCIName: + AllowedPattern: '[a-zA-Z0-9\-]+' + Default: SqlFCI + Description: Name of the failover cluster instance (FCI). You can use up to 15 characters. + MaxLength: '15' + MinLength: '1' + Type: String + DomainAdminPassword: + Description: Password for the domain administrator. + MaxLength: '32' + MinLength: '0' + NoEcho: 'true' + Type: String + DomainAdminUser: + AllowedPattern: '[a-zA-Z0-9]*' + Default: StackAdmin + 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 + ADDnsIpAddresses: + Description: DNS IP addresses for Active Directory. Separate these with commas. + MaxLength: '255' + MinLength: '2' + Type: String + DomainMemberSGID: + Description: ID of the domain member security group (e.g., sg-9cb7d0e5). + Type: String + DomainNetBIOSName: + AllowedPattern: '[a-zA-Z0-9\-]+' + Default: EXAMPLE + Description: NetBIOS name of the domain for users of earlier versions of Windows. You can use up to 15 characters. + MaxLength: '15' + MinLength: '1' + Type: String + DirectoryId: + Description: The ID of the AWS Directory Service directory. + MaxLength: '15' + MinLength: '1' + Type: String + WindowsVersion: + AllowedValues: + - Windows_Server-2019-English-Full-Base* + - Windows_Server-2016-English-Full-Base* + ConstraintDescription: Must be one of the supported Windows versions. + Default: Windows_Server-2019-English-Full-Base* + Description: Select Windows version to run SQL Server. + Type: String + WorkloadInstanceType: + AllowedValues: + - t3a.2xlarge + - t3.2xlarge + - r5a.xlarge + - r5a.2xlarge + - r5a.4xlarge + - r5a.8xlarge + - r5.large + - r5.xlarge + - r5.2xlarge + - r5.4xlarge + - r5.8xlarge + - r4.xlarge + - r4.2xlarge + - r4.4xlarge + - r4.8xlarge + - z1d.large + - z1d.xlarge + ConstraintDescription: Must contain a valid instance type. + Default: r5.2xlarge + Description: Type of EC2 instance for the workload instances. + Type: String + MSSQLMediaBucketName: + AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$ + ConstraintDescription: You can use lowercase letters, uppercase letters, and hyphens (-). Do not start or end with a hyphen (-). + Default: ss-experiments + Description: Name of the S3 bucket from which MSSQL media can be downloaded. You can use lowercase letters, uppercase letters, and hyphens (-). Do not start or end with a hyphen (-). + Type: String + MSSQLMediaPathKey: + Default: cfn-ps-microsoft-SqlFSx/sql-installation-media/en_sql_server_2019_developer_x64_dvd_baea4195.iso + Description: Key for the S3 bucket from which MSSQL media can be downloaded. You can use numbers, lowercase letters, uppercase letters, hyphens (-), and forward slashes (/). + Type: String + SQLAdminAccounts: + Default: contoso\Administrator + Description: Comma-separated user names will be used as MSSQL DB administrators. It could be Active Directory domain administrators or other local or domain users. + MaxLength: '25' + MinLength: '5' + Type: String + VPCcidr: + 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])(\/(1[6-9]|2[0-8]))$ + ConstraintDescription: The CIDR block parameter must be in the form x.x.x.x/16-28. + Default: 10.0.0.0/16 + Description: CIDR block for the VPC. + Type: String +Conditions: + UsingDefaultBucket: + Fn::Equals: + - Ref: QSS3BucketName + - aws-ia-us-east-1 + EnableAppInsightForSQLHA: + Fn::Equals: + - Ref: EnableAppInsights + - 'true' +Rules: + KeyPairsNotEmpty: + Assertions: + - Assert: + Fn::Not: + - Fn::EachMemberEquals: + - Fn::RefAll: AWS::EC2::KeyPair::KeyName + - '' + AssertDescription: All key pair parameters must not be empty + SubnetsInVPC: + Assertions: + - Assert: + Fn::EachMemberIn: + - Fn::ValueOfAll: + - AWS::EC2::Subnet::Id + - VpcId + - Fn::RefAll: AWS::EC2::VPC::Id + AssertDescription: All subnets must in the VPC +Resources: + ADAdminSecrets: + Type: AWS::SecretsManager::Secret + Properties: + Name: + Fn::Sub: AWSQuickStart/${AWS::StackName}/ADAdminSecrets + Description: Active Directory administrator credentials for Quick Start + SecretString: + Fn::Sub: '{ "username" : "${DomainAdminUser}", "password" : "${DomainAdminPassword}" }' + QuickStartLogs: + Type: AWS::Logs::LogGroup + Properties: + LogGroupName: + Fn::Sub: /aws/Quick_Start/${AWS::StackName} + RetentionInDays: 30 + WindowsBaseAMIInfo: + Type: Custom::WindowsBaseAMIInfo + Properties: + ServiceToken: + Fn::GetAtt: + - AMIInfoFunction + - Arn + Region: + Ref: AWS::Region + AMIName: + Ref: WindowsVersion + AMIInfoFunction: + Type: AWS::Lambda::Function + Properties: + Code: + ZipFile: + Fn::Join: + - '' + - - "/**\n" + - "* A Lambda function that looks up the latest AMI ID for a given region and architecture.\n" + - "**/\n" + - var aws = require("aws-sdk"); + - exports.handler = function(event, context) { + - ' console.log("REQUEST RECEIVED:\n" + JSON.stringify(event));' + - " // For Delete requests, immediately send a SUCCESS response.\n" + - ' if (event.RequestType == "Delete") {' + - ' sendResponse(event, context, "SUCCESS");' + - " return;\n" + - " }\n" + - ' var responseStatus = "FAILED";' + - " var responseData = {};\n" + - " var ec2 = new aws.EC2({region: event.ResourceProperties.Region});\n" + - " var describeImagesParams = {\n" + - ' Filters: [{ Name: "name", Values: [event.ResourceProperties.AMIName]}],' + - ' Owners: ["amazon"]' + - " };\n" + - " // Get AMI IDs with the specified name pattern and owner\n" + - " ec2.describeImages(describeImagesParams, function(err, describeImagesResult) {\n" + - " if (err) {\n" + - ' responseData = {Error: "DescribeImages call failed"};' + - ' console.log(responseData.Error + ":\n", err);' + - " }\n" + - " else {\n" + - " var images = describeImagesResult.Images;\n" + - " // Sort images by name in decscending order. The names contain the AMI version, formatted as YYYY.MM.Ver.\n" + - " images.sort(function(x, y) { return y.CreationDate.localeCompare(x.CreationDate); });\n" + - " for (var j = 0; j < images.length; j++) {\n" + - " if (isBeta(images[j].Name)) continue;\n" + - ' responseStatus = "SUCCESS";' + - ' responseData["Id"] = images[j].ImageId;' + - " break;\n" + - " }\n" + - " }\n" + - " sendResponse(event, context, responseStatus, responseData);\n" + - " });\n" + - "};\n" + - "\n" + - "// Check if the image is a beta or rc image. The Lambda function won't return any of those images.\n" + - function isBeta(imageName) { + - ' return imageName.toLowerCase().indexOf("beta") > -1 || imageName.toLowerCase().indexOf(".rc") > -1;' + - "}\n" + - "// Send response to the pre-signed S3 URL \n" + - function sendResponse(event, context, responseStatus, responseData) { + - " var responseBody = JSON.stringify({\n" + - " Status: responseStatus,\n" + - ' Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,' + - " PhysicalResourceId: context.logStreamName,\n" + - " StackId: event.StackId,\n" + - " RequestId: event.RequestId,\n" + - " LogicalResourceId: event.LogicalResourceId,\n" + - " Data: responseData\n" + - " });\n" + - ' console.log("RESPONSE BODY:\n", responseBody);' + - ' var https = require("https");' + - ' var url = require("url");' + - " var parsedUrl = url.parse(event.ResponseURL);\n" + - " var options = {\n" + - " hostname: parsedUrl.hostname,\n" + - " port: 443,\n" + - " path: parsedUrl.path,\n" + - ' method: "PUT",' + - " headers: {\n" + - ' "content-type": "",' + - ' "content-length": responseBody.length' + - " }\n" + - " };\n" + - ' console.log("SENDING RESPONSE...\n");' + - " var request = https.request(options, function(response) {\n" + - ' console.log("STATUS: " + response.statusCode);' + - ' console.log("HEADERS: " + JSON.stringify(response.headers));' + - " // Tell AWS Lambda that the function execution is done \n" + - " context.done();\n" + - " });\n" + - ' request.on("error", function(error) {' + - ' console.log("sendResponse Error:" + error);' + - " // Tell AWS Lambda that the function execution is done \n" + - " context.done();\n" + - " });\n" + - " // write data to request body\n" + - " request.write(responseBody);\n" + - " request.end();\n" + - '}' + Handler: index.handler + Role: + Fn::GetAtt: + - AMIInfoFunctionLambdaExecutionRole + - Arn + Runtime: nodejs18.x + Timeout: 30 + AMIInfoFunctionLambdaExecutionRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: + - lambda.amazonaws.com + Action: + - sts:AssumeRole + Path: / + Policies: + - PolicyName: root + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - logs:CreateLogGroup + - logs:CreateLogStream + - logs:PutLogEvents + Resource: + Fn::Sub: arn:${AWS::Partition}:logs:*:*:* + - Effect: Allow + Action: + - ec2:DescribeImages + Resource: '*' + AWSQuickstartSqlFSxRole: + Type: AWS::IAM::Role + Metadata: + cfn-lint: + config: + ignore_checks: + - EIAMPolicyWildcardResource + - EIAMPolicyActionWildcard + Properties: + Policies: + - PolicyDocument: + Version: '2012-10-17' + Statement: + - Action: + - s3:GetObject + - s3:ListBucket + Resource: + - Fn::Sub: + - arn:${AWS::Partition}:s3:::${S3Bucket}/${QSS3KeyPrefix}* + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + - Fn::Sub: + - arn:${AWS::Partition}:s3:::${S3Bucket} + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + Effect: Allow + PolicyName: aws-quick-start-s3-policy + - PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - cloudformation:SignalResource + Resource: + Fn::Sub: arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/* + - Effect: Allow + Action: + - ec2:DescribeInstances + - ec2:DescribeInstanceStatus + - ec2:StopInstances + - ec2:StartInstances + - ec2:DescribeNetworkInterfaces + - ec2:DescribeSubnets + - ssm:* + - fsx:Describe* + - fsx:ListTagsForResource + Resource: '*' + PolicyName: SqlFSx-SSM-AutomationExecution + Path: / + AssumeRolePolicyDocument: + Statement: + - Action: + - sts:AssumeRole + Principal: + Service: + - ec2.amazonaws.com + - ssm.amazonaws.com + Effect: Allow + Version: '2012-10-17' + SqlFSxAutomation: + Type: AWS::SSM::Document + Properties: + DocumentType: Automation + Content: + schemaVersion: '0.3' + description: Deploy SqlFSx with SSM Automation + assumeRole: '{{AutomationAssumeRole}}' + parameters: + DomainDNSName: + default: + Fn::Sub: ${DomainDNSName} + description: Fully qualified domain name (FQDN) of the forest root domain. + type: String + DomainNetBIOSName: + default: + Fn::Sub: ${DomainNetBIOSName} + description: NetBIOS name of the domain for users of earlier versions of Windows. You can use up to 15 characters. + type: String + DirectoryId: + default: + Fn::Sub: ${DirectoryId} + description: The ID of the AWS Directory Service directory (e.g., d-97672886b5). + type: String + ADDnsIpAddresses: + default: + Fn::Sub: ${ADDnsIpAddresses} + description: The IP addresses of the DNS servers in the directory. Required when DHCP is not configured. + type: String + MSSQLMediaBucketName: + default: + Fn::Sub: ${MSSQLMediaBucketName} + description: The S3 bucket from which MSSQL media can be downloaded. + type: String + MSSQLMediaPathKey: + default: + Fn::Sub: ${MSSQLMediaPathKey} + description: The key for the S3 bucket from which MSSQL media can be downloaded. + type: String + ClusterName: + default: + Fn::Sub: ${SqlFSxWSFCName} + description: NetBIOS name of the cluster (up to 15 characters). + type: String + SqlFSxFCIName: + default: + Fn::Sub: ${SqlFSxFCIName} + description: NetBIOS name of the failover cluster instance (up to 15 characters). + type: String + SQLAdminAccounts: + default: + Fn::Sub: ${SQLAdminAccounts} + description: MSSQL administrator accounts + type: String + AdminSecrets: + default: + Fn::Sub: ${ADAdminSecrets} + description: Domain administrator credentials + type: String + SqlFSxInstanceId: + default: '' + description: ID of the SqlFSx instance + type: String + StackName: + default: + Fn::Sub: ${AWS::StackName} + description: CloudFormation stack name + type: String + SqlFSxServerNetBIOSName1: + default: + Fn::Sub: ${SqlFSxServerNetBIOSName1} + description: CloudFormation stack name + type: String + AutomationAssumeRole: + default: '' + description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf. + type: String + WindowsVersion: + default: + Fn::Sub: ${WindowsVersion} + description: The AMI used to deploy SqlFSx instance. + type: String + URLSuffix: + description: AWS URL suffix + 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 + mainSteps: + - name: FindFSxVolDnsAddress + action: aws:executeScript + onFailure: step:signalfailure + inputs: + Runtime: PowerShell Core 6.0 + Script: "Install-Module AWS.Tools.FSx -Force\nImport-Module AWS.Tools.FSx\n \n$fsList = Get-FSXFileSystem; \n$fsList.forEach{\n $tags = $_.Tags; \n foreach ($tag in $tags)\n {\n if ($tag.key\ + \ -eq \"Name\" -And $tag.value -eq \"SqlFSxFileSystem\")\n {\n $dnsName = $_.DNSName;\n $psEndpoint = $_.WindowsConfiguration.RemoteAdministrationEndpoint\n break;\n \ + \ }\n }\n};\n$resultStr = \"{0};{1}\" -f $dnsName, $psEndpoint\nWrite-Host $dnsName\nreturn @{message=$resultStr}\n" + outputs: + - Name: FSxDnsAddress + Selector: $.Payload.message + Type: String + - name: GetAllClusterNodes + action: aws:executeAwsApi + outputs: + - Name: InstanceIds + Selector: $.Reservations..Instances..InstanceId + Type: StringList + inputs: + Service: ec2 + Api: DescribeInstances + Filters: + - Name: tag:FCIName + Values: + - Ref: SqlFSxFCIName + - Name: instance-state-name + Values: + - running + description: Get instances + - name: GetClusterNode1 + action: aws:executeAwsApi + outputs: + - Name: InstanceIds + Selector: $.Reservations..Instances..InstanceId + Type: StringList + inputs: + Service: ec2 + Api: DescribeInstances + Filters: + - Name: tag:Name + Values: + - Ref: SqlFSxServerNetBIOSName1 + - Name: instance-state-name + Values: + - running + description: Get instances + - name: GetClusterNode2 + action: aws:executeAwsApi + outputs: + - Name: InstanceIds + Selector: $.Reservations..Instances..InstanceId + Type: StringList + inputs: + Service: ec2 + Api: DescribeInstances + Filters: + - Name: tag:Name + Values: + - Ref: SqlFSxServerNetBIOSName2 + - Name: instance-state-name + Values: + - running + description: Get instances + - name: FindSecondaryIpAddresses + action: aws:executeScript + onFailure: step:signalfailure + inputs: + Runtime: PowerShell Core 6.0 + Script: "$inputPayload = $env:InputPayload | ConvertFrom-Json; \nInstall-Module AWS.Tools.EC2 -Force\nImport-Module AWS.Tools.EC2\n \n#Function to find Subnet\nfunction Get-SubnetMask($i)\n\ + {\n $instance = $i\n $subnetid = $instance.instances[0].NetworkInterfaces.SubnetId\n $subnet = get-ec2subnet -SubnetId $subnetid\n $cidr = $subnet.CidrBlock\n $cidr_mask = $cidr.split('/')[1]\n\ + \ $A = 0\n $A_Index = 8\n $B = 0\n $B_Index = 16\n $C = 0\n $C_Index = 24\n $D = 0\n $D_Index = 32\n for ($i = 1; $i -le $cidr_mask; $i++)\n {\n if ($i -le $A_Index)\n\ + \ {\n $A += ([Math]::Pow(2, 8 - $i))\n }\n elseif ($i -le $B_Index)\n {\n $B += ([Math]::Pow(2, 8 - $i + $A_Index))\n }\n elseif ($i\ + \ -le $C_Index)\n {\n $C += ([Math]::Pow(2, 8 - $i + $B_Index))\n }\n elseif ($i -le $D_Index)\n {\n $D += ([Math]::Pow(2, 8 - $i + $C_Index))\n\ + \ }\n }\n $subnet_mask = \"{0}.{1}.{2}.{3}\" -f $A, $B, $C, $D\n return $subnet_mask\n}\n\n$instanceIds = $inputPayload.parameter\n \n$resultStr = \"\"\n$subnetMaskStr = \"\"\ + \n\n#Finding secondary IP addresses of the primary instance \nforeach ($i in $instanceIds)\n{ \n $instance = get-ec2instance -InstanceId $i\n $tags = $instance.instances[0].Tags\n\ + \ foreach ($t in $tags)\n {\n if ($t.Key -eq \"FCIRole\" -And $t.Value -eq \"Primary\")\n {\n $IPs = $instance.instances[0].NetworkInterfaces.PrivateIpAddresses\n $ip1\ + \ = $IPs[1].PrivateIpAddress\n $ip2 = $IPs[2].PrivateIpAddress\n $resultStr = \"{0};{1}\" -f $ip1, $ip2\n \n $subnetMaskStr = Get-SubnetMask($instance)\n break;\n\ + \ } \n }\n if ([System.String]::IsNullOrEmpty($resultStr) -Eq $False)\n {\n break;\n }\n}\n\n#Finding secondary IP addresses of the secondary instance \nforeach\ + \ ($i in $instanceIds)\n{ \n $instance = get-ec2instance -InstanceId $i\n $tags = $instance.instances[0].Tags\n foreach ($t in $tags)\n {\n if ($t.Key -eq \"FCIRole\"\ + \ -And $t.Value -eq \"Secondary\")\n {\n $IPs = $instance.instances[0].NetworkInterfaces.PrivateIpAddresses \n $ip1 = $IPs[1].PrivateIpAddress\n $ip2 = $IPs[2].PrivateIpAddress\ + \ \n $resultStr = \"{0};{1};{2}\" -f $resultStr, $ip1, $ip2\n \n $subnetMask = Get-SubnetMask($instance)\n $subnetMaskStr = \"{0};{1}\" -f $subnetMaskStr,\ + \ $subnetMask\n break;\n } \n }\n}\n\n$resultStr = \"{0};{1}\" -f $resultStr, $subnetMaskStr\n\nWrite-Host $dnsName\nreturn @{message=$resultStr}\n" + InputPayload: + parameter: '{{GetAllClusterNodes.InstanceIds}}' + outputs: + - Name: SecondaryPrivateIpAddresses + Selector: $.Payload.message + Type: String + - name: UpdateSSMAgent + action: aws:runCommand + timeoutSeconds: 300 + onFailure: Continue + inputs: + DocumentName: AWS-UpdateSSMAgent + InstanceIds: '{{GetAllClusterNodes.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + - name: InitializeDisk + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunPowerShellScript + InstanceIds: '{{GetAllClusterNodes.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + commands: + - "C:\\ProgramData\\Amazon\\EC2-Windows\\Launch\\Scripts\\InitializeDisks.ps1\n" + - name: InstallDSCModules + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: '{{GetAllClusterNodes.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/install-dsc-modules.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./install-dsc-modules.ps1 + - name: wsfcnLCMConfig + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: '{{GetAllClusterNodes.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/LCM-Config.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./LCM-Config.ps1 + - name: JoinAD + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-JoinDirectoryServiceDomain + InstanceIds: '{{GetAllClusterNodes.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + directoryId: '{{DirectoryId}}' + directoryName: '{{DomainDNSName}}' + dnsIpAddresses: + Fn::Select: + - 0 + - Fn::Split: + - ',' + - Ref: ADDnsIpAddresses + - name: EnableCredSSP + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: '{{GetAllClusterNodes.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/enable-CredSSP.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./enable-CredSSP.ps1 -DomainDNSName '{{DomainDNSName}}' + - name: CreateCAShare + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: '{{GetClusterNode1.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/create-ca-share.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./create-ca-share.ps1 -DomainNetBIOSName '{{DomainNetBIOSName}}' -AdminSecret '{{AdminSecrets}}' -FSxRemoteAdminEndpoint '{{FindFSxVolDnsAddress.FSxDnsAddress}}'.split(';')[1] + - name: CreateNode1WsfcMOF + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: '{{GetClusterNode1.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/Node1Config.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./Node1Config.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -DomainDNSName {{DomainDNSName}} -WSFCNode1PrivateIP2 '{{FindSecondaryIpAddresses.SecondaryPrivateIpAddresses}}'.split(';')[0] + -ClusterName {{ClusterName}} -AdminSecret {{AdminSecrets}} -FileServerNetBIOSName '{{FindFSxVolDnsAddress.FSxDnsAddress}}'.split(';')[0] + - name: RunNode1WsfcMOF + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunPowerShellScript + InstanceIds: '{{GetClusterNode1.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + commands: + - "function DscStatusCheck () {\n $LCMState = (Get-DscLocalConfigurationManager).LCMState\n if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') {\n \ + \ 'returning 3010, should continue after reboot'\n exit 3010\n } else {\n 'Completed'\n }\n}\n\nStart-DscConfiguration 'C:\\AWSQuickstart\\WSFCNode1Config' -Wait -Verbose\ + \ -Force\n\nDscStatusCheck\n" + - name: CreateNode2WsfcMOF + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: + - '{{GetClusterNode2.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/AdditionalNodeConfig.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./AdditionalNodeConfig.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -WSFCNodePrivateIP2 '{{FindSecondaryIpAddresses.SecondaryPrivateIpAddresses}}'.split(';')[2] -ClusterName + {{ClusterName}} -AdminSecret {{AdminSecrets}} + - name: RunNode2WsfcMOF + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunPowerShellScript + InstanceIds: + - '{{GetClusterNode2.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + commands: + - "function DscStatusCheck () {\n $LCMState = (Get-DscLocalConfigurationManager).LCMState\n if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') {\n \ + \ 'returning 3010, should continue after reboot'\n exit 3010\n } else {\n 'Completed'\n }\n}\n\nStart-DscConfiguration 'C:\\AWSQuickstart\\AdditionalWSFCNode' -Wait\ + \ -Verbose -Force\n\nDscStatusCheck \n" + - name: ConfigurePermissionsOnMAD + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: '{{GetClusterNode1.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/Configure-MAD-Permissions.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./Configure-MAD-Permissions.ps1 -AdminSecret {{AdminSecrets}} -DomainNetBIOSName '{{DomainNetBIOSName}}' -wsfcName '{{ClusterName}}' + - name: PrepareFCI + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: '{{GetAllClusterNodes.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/prepare-fci.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./prepare-fci.ps1 -AdminSecret '{{AdminSecrets}}' -DomainNetBIOSName {{DomainNetBIOSName}} -SqlUserSecret {{AdminSecrets}} -MSSQLMediaBucket '{{MSSQLMediaBucketName}}' -MSSQLMediaKey + '{{MSSQLMediaPathKey}}' + - name: CompleteFCI + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: '{{GetClusterNode1.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/complete-fci.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./complete-fci.ps1 -AdminSecret '{{AdminSecrets}}' -DomainNetBIOSName {{DomainNetBIOSName}} -FileServerPath '{{FindFSxVolDnsAddress.FSxDnsAddress}}'.split(';')[0] -Node1FciIp + '{{FindSecondaryIpAddresses.SecondaryPrivateIpAddresses}}'.split(';')[1] -Node1SubnetMask '{{FindSecondaryIpAddresses.SecondaryPrivateIpAddresses}}'.split(';')[4] -Node2FciIp '{{FindSecondaryIpAddresses.SecondaryPrivateIpAddresses}}'.split(';')[3] + -Node2SubnetMask '{{FindSecondaryIpAddresses.SecondaryPrivateIpAddresses}}'.split(';')[5] -FCIName {{SqlFSxFCIName}} -SQLAdminAccounts '{{SQLAdminAccounts}}' + - name: CFNSignalEnd + action: aws:branch + inputs: + Choices: + - NextStep: signalsuccess + Not: + Variable: '{{StackName}}' + StringEquals: '' + - NextStep: sleepend + Variable: '{{StackName}}' + StringEquals: '' + - name: signalsuccess + action: aws:executeAwsApi + isEnd: true + inputs: + Service: cloudformation + Api: SignalResource + LogicalResourceId: SSMWaitCondition + StackName: '{{StackName}}' + Status: SUCCESS + UniqueId: '{{SqlFSxInstanceId}}' + - name: sleepend + action: aws:sleep + isEnd: true + inputs: + Duration: PT1S + - name: signalfailure + action: aws:executeAwsApi + inputs: + Service: cloudformation + Api: SignalResource + LogicalResourceId: SSMWaitCondition + StackName: '{{StackName}}' + Status: FAILURE + UniqueId: '{{SqlFSxInstanceId}}' + SqlFSxSSMPassRolePolicy: + Type: AWS::IAM::Policy + Properties: + PolicyName: SqlFsx-SSM-PassRole + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - iam:PassRole + Resource: + Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${AWSQuickstartSqlFSxRole} + Roles: + - Ref: AWSQuickstartSqlFSxRole + SqlFSxInstance1: + Type: AWS::EC2::Instance + DependsOn: FSxWindowsFileSystem + Properties: + ImageId: + Fn::GetAtt: + - WindowsBaseAMIInfo + - Id + IamInstanceProfile: + Ref: SqlFSxProfile + InstanceType: + Ref: WorkloadInstanceType + NetworkInterfaces: + - DeleteOnTermination: true + DeviceIndex: '0' + SubnetId: + Ref: PrivateSubnet1ID + SecondaryPrivateIpAddressCount: 2 + GroupSet: + - Ref: DomainMemberSGID + - Ref: WorkloadSecurityGroup + Tags: + - Key: Name + Value: + Ref: SqlFSxServerNetBIOSName1 + - Key: FCIName + Value: + Ref: SqlFSxFCIName + - Key: FCIRole + Value: Primary + - Key: ResourceGroup + Value: + Ref: ResourceGroupName + BlockDeviceMappings: + - DeviceName: /dev/sda1 + Ebs: + VolumeSize: 100 + VolumeType: gp2 + KeyName: + Ref: KeyPairName + UserData: + Fn::Base64: + Fn::Join: + - '' + - - "\n" + - "New-Item -Path C:\\ -Name log -ItemType Directory \n" + - "$transcriptPath = 'C:\\log\\user-data-transcript.txt' \n" + - "Start-Transcript $transcriptPath \n" + - Fn::Sub: Set-Content -Value "${SqlFSxServerNetBIOSName1}" -Path C:\log\hostname.txt + - "\n" + - Fn::Sub: Rename-Computer -NewName "${SqlFSxServerNetBIOSName1}" + - "\n" + - "$instanceId = (New-Object System.Net.WebClient).DownloadString('http://169.254.169.254/latest/meta-data/instance-id')\n" + - 'Start-SSMAutomationExecution -DocumentName ' + - Fn::Sub: '"${SqlFSxAutomation}"' + - ' -Parameter @{' + - '"SqlFSxInstanceId"=' + - $instanceId + - ;"URLSuffix"= + - Fn::Sub: '"${AWS::URLSuffix}"' + - ;"QSS3BucketName"= + - Fn::Sub: '"${QSS3BucketName}"' + - ;"QSS3KeyPrefix"= + - Fn::Sub: '"${QSS3KeyPrefix}"' + - ;"AutomationAssumeRole"= + - Fn::Sub: '"arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${AWSQuickstartSqlFSxRole}"' + - '}' + - "\n" + - "\n" + SqlFSxInstance2: + Type: AWS::EC2::Instance + Properties: + ImageId: + Fn::GetAtt: + - WindowsBaseAMIInfo + - Id + IamInstanceProfile: + Ref: SqlFSxProfile + InstanceType: + Ref: WorkloadInstanceType + NetworkInterfaces: + - DeleteOnTermination: true + DeviceIndex: '0' + SubnetId: + Ref: PrivateSubnet2ID + SecondaryPrivateIpAddressCount: 2 + GroupSet: + - Ref: DomainMemberSGID + - Ref: WorkloadSecurityGroup + Tags: + - Key: Name + Value: + Ref: SqlFSxServerNetBIOSName2 + - Key: FCIName + Value: + Ref: SqlFSxFCIName + - Key: FCIRole + Value: Secondary + - Key: ResourceGroup + Value: + Ref: ResourceGroupName + BlockDeviceMappings: + - DeviceName: /dev/sda1 + Ebs: + VolumeSize: 100 + VolumeType: gp2 + KeyName: + Ref: KeyPairName + UserData: + Fn::Base64: + Fn::Join: + - '' + - - "\n" + - "New-Item -Path C:\\ -Name log -ItemType Directory \n" + - Fn::Sub: Set-Content -Value "${SqlFSxServerNetBIOSName2}" -Path C:\log\hostname.txt + - "\n" + - Fn::Sub: Rename-Computer -NewName "${SqlFSxServerNetBIOSName2}" + - "\n" + - "\n" + SqlFSxRole: + Type: AWS::IAM::Role + Metadata: + cfn-lint: + config: + ignore_checks: + - EIAMPolicyActionWildcard + - EIAMPolicyWildcardResource + Properties: + Policies: + - PolicyDocument: + Version: '2012-10-17' + Statement: + - Action: + - s3:GetObject + Resource: + - Fn::Sub: + - arn:${AWS::Partition}:s3:::${S3Bucket}/${QSS3KeyPrefix}* + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + Effect: Allow + - Action: + - s3:* + Resource: '*' + Effect: Allow + PolicyName: aws-quick-start-s3-policy + - PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - secretsmanager:GetSecretValue + - secretsmanager:DescribeSecret + Resource: + - Ref: ADAdminSecrets + - Effect: Allow + Action: + - ssm:StartAutomationExecution + Resource: '*' + PolicyName: QS-SqlFSx-SSM + - PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - ds:* + Resource: + - '*' + PolicyName: QS-SqlFSx-DS + - PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - iam:PassRole + Resource: + Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${AWSQuickstartSqlFSxRole} + PolicyName: QS-SqlFSx-SSM-PassRole + ManagedPolicyArns: + - Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore + Path: / + AssumeRolePolicyDocument: + Statement: + - Action: + - sts:AssumeRole + Principal: + Service: + - ec2.amazonaws.com + Effect: Allow + Version: '2012-10-17' + SqlFSxProfile: + Type: AWS::IAM::InstanceProfile + Properties: + Roles: + - Ref: SqlFSxRole + Path: / + SSMWaitHandle: + Type: AWS::CloudFormation::WaitConditionHandle + SSMWaitCondition: + Type: AWS::CloudFormation::WaitCondition + CreationPolicy: + ResourceSignal: + Timeout: PT120M + Count: 1 + DependsOn: SqlFSxInstance1 + Properties: + Handle: + Ref: SSMWaitHandle + Timeout: '7200' + Count: 1 + WorkloadSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Allow access to the Workload instances + VpcId: + Ref: VPCID + SecurityGroupIngress: + - IpProtocol: '-1' + FromPort: -1 + ToPort: -1 + CidrIp: + Ref: VPCcidr + FSxWindowsFileSystem: + Type: AWS::FSx::FileSystem + Properties: + FileSystemType: WINDOWS + StorageCapacity: + Ref: FileShareVolumeSize + StorageType: SSD + SubnetIds: + - Ref: PrivateSubnet1ID + - Ref: PrivateSubnet2ID + SecurityGroupIds: + - Ref: WorkloadSecurityGroup + - Ref: DomainMemberSGID + Tags: + - Key: Name + Value: SqlFSxFileSystem + WindowsConfiguration: + ThroughputCapacity: + Ref: FileShareThroughputCapacity + WeeklyMaintenanceStartTime: '4:16:30' + DailyAutomaticBackupStartTime: 01:00 + AutomaticBackupRetentionDays: 30 + CopyTagsToBackups: false + DeploymentType: MULTI_AZ_1 + PreferredSubnetId: + Ref: PrivateSubnet1ID + ActiveDirectoryId: + Fn::Sub: ${DirectoryId} + ResourceGroup: + Type: AWS::ResourceGroups::Group + Properties: + Name: + Ref: ResourceGroupName + Description: A resource group for SQL servers + ResourceQuery: + Type: TAG_FILTERS_1_0 + Query: + ResourceTypeFilters: + - AWS::EC2::Instance + TagFilters: + - Key: ResourceGroup + Values: + - Ref: ResourceGroupName + AppInsightsForSQLHA: + Condition: EnableAppInsightForSQLHA + DependsOn: SSMWaitCondition + Type: AWS::ApplicationInsights::Application + Properties: + ResourceGroupName: + Ref: ResourceGroupName + AutoConfigurationEnabled: false + CustomComponents: + - ComponentName: + Fn::Sub: SQLHAClusterInstances-${ApplicationName} + ResourceList: + - Fn::Sub: arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:instance/${SqlFSxInstance1} + - Fn::Sub: arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:instance/${SqlFSxInstance2} + ComponentMonitoringSettings: + - ComponentName: + Fn::Sub: SQLHAClusterInstances-${ApplicationName} + Tier: SQL_SERVER_ALWAYSON_AVAILABILITY_GROUP + ComponentConfigurationMode: DEFAULT_WITH_OVERWRITE + DefaultOverwriteComponentConfiguration: + SubComponentTypeConfigurations: + - SubComponentType: AWS::EC2::Instance + SubComponentConfigurationDetails: + Logs: + - LogGroupName: + Fn::Sub: SQL_SERVER-${ResourceGroupName} + LogType: SQL_SERVER + LogPath: C:\Program Files\Microsoft SQL Server\MSSQL**.MSSQLSERVER\MSSQL\Log\ERRORLOG + Tags: + - Key: SourceTemplate + Value: AWSQuickStart +Outputs: + InstanceIP: + Description: Private IP address of SqlFSx instance. + Value: + Fn::GetAtt: + - SqlFSxInstance1 + - PrivateIp + Postdeployment: + Value: https://fwd.aws/5XG6A + Description: See the deployment guide for post-deployment steps. diff --git a/templates/mssqlfsx-main.template.yaml b/templates/mssqlfsx-main.template.yaml new file mode 100644 index 0000000..fc43d1f --- /dev/null +++ b/templates/mssqlfsx-main.template.yaml @@ -0,0 +1,608 @@ +--- +AWSTemplateFormatVersion: '2010-09-09' +Description: This workload template deploys a SQL FSx instance in a new VPC. **IMPORTANT** + This template creates EC2 instances and related resources. You will be billed for + the AWS resources used if you create a stack from this template. (qs-1rih7cd6r) +Metadata: + cfn-lint: + config: + ignore_checks: + - W9006 + QuickStartDocumentation: + EntrypointName: "Parameters for deploying into a new VPC" + Order: "1" + AWS::CloudFormation::Interface: + ParameterGroups: + - Label: + default: Network configuration + Parameters: + - AvailabilityZones + - VPCCIDR + - PublicSubnet1CIDR + - PublicSubnet2CIDR + - PrivateSubnet1CIDR + - PrivateSubnet2CIDR + - Label: + default: Remote Desktop Gateway configuration + Parameters: + - RDGWCIDR + - NumberOfRDGWHosts + - RDGWInstanceType + - Label: + default: Active Directory configuration + Parameters: + - ADScenarioType + - DomainDNSName + - DomainNetBIOSName + - DomainAdminUser + - DomainAdminPassword + - Label: + default: Self-managed Active Directory configuration + Parameters: + - ADServer1InstanceType + - ADServer1NetBIOSName + - ADServer1PrivateIP + - ADServer2InstanceType + - ADServer2NetBIOSName + - ADServer2PrivateIP + - Label: + default: Amazon EC2 configuration + Parameters: + - KeyPairName + - SqlFSxServerNetBIOSName1 + - SqlFSxServerNetBIOSName2 + - WindowsVersion + - WorkloadInstanceType + - Label: + default: Amazon FSx Windows file-share configuration + Parameters: + - FileShareVolumeSize + - FileShareThroughputCapacity + - Label: + default: Microsoft SQL Server FCI configurations + Parameters: + - MSSQLMediaBucketName + - MSSQLMediaPathKey + - SqlFSxWSFCName + - SqlFSxFCIName + - SQLAdminAccounts + - Label: + default: Application Insights configuration + Parameters: + - EnableAppInsights + - ApplicationName + - ResourceGroupName + - 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: Active Directory scenario type + ADServer1InstanceType: + default: Domain controller 1 instance type + ADServer1NetBIOSName: + default: Domain controller 1 NetBIOS name + ADServer1PrivateIP: + default: Domain controller 1 private IP address + ADServer2InstanceType: + default: Domain controller 2 instance type + ADServer2NetBIOSName: + default: Domain controller 2 NetBIOS name + ADServer2PrivateIP: + default: Domain controller 2 private IP address + AvailabilityZones: + default: Availability Zones + DomainAdminPassword: + default: Domain administrator password + DomainAdminUser: + default: Domain administrator user name + DomainDNSName: + default: Domain DNS name + DomainNetBIOSName: + default: Domain NetBIOS name + FileShareThroughputCapacity: + default: File-share throughput capacity + FileShareVolumeSize: + default: File-share volume size + KeyPairName: + default: EC2 key pair name + MSSQLMediaBucketName: + default: S3 bucket for SQL media + MSSQLMediaPathKey: + default: S3 key for SQL media path + NumberOfRDGWHosts: + default: Number of RD Gateway hosts + PrivateSubnet1CIDR: + default: Private subnet 1 CIDR + PrivateSubnet2CIDR: + default: Private subnet 2 CIDR + PublicSubnet1CIDR: + default: Public subnet 1 CIDR + PublicSubnet2CIDR: + default: Public subnet 2 CIDR + QSS3BucketName: + default: Quick Start S3 bucket name + QSS3BucketRegion: + default: Quick Start S3 bucket Region + QSS3KeyPrefix: + default: Quick Start S3 key prefix + RDGWInstanceType: + default: RD Gateway instance type + RDGWCIDR: + default: Allowed RD Gateway external access CIDR + SQLAdminAccounts: + default: SQL administrator account or accounts + SqlFSxFCIName: + default: Failover cluster instance (FCI) name + SqlFSxServerNetBIOSName1: + default: Server1 NetBIOS name + SqlFSxServerNetBIOSName2: + default: Server2 NetBIOS name + SqlFSxWSFCName: + default: Windows Server Failover Cluster (WSFC) name + VPCCIDR: + default: VPC CIDR block + WindowsVersion: + default: Windows version + WorkloadInstanceType: + default: Workload servers EC2 instance type +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: 'Type of AD DS deployment to use. If you want to manage your own Amazon EC2 Active Directory instances, choose "Microsoft AD on Amazon EC2."' + Type: String + ADServer1InstanceType: + AllowedValues: + - t2.large + - m4.large + - m4.xlarge + - m4.2xlarge + - m4.4xlarge + - m5.large + - m5.xlarge + - m5.2xlarge + - m5.4xlarge + Default: m5.xlarge + Description: Amazon EC2 instance type for the first Active Directory instance, located in Availability Zone 1. + Type: String + ADServer1NetBIOSName: + AllowedPattern: '[a-zA-Z0-9\-]+' + Default: DC1 + Description: NetBIOS name of the first Active Directory instance, located in Availability Zone 1. This name can have up to 15 characters. + MaxLength: '15' + MinLength: '1' + 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: Fixed private IP address for the first Active Directory instance, located in + Availability Zone 1. + Type: String + ADServer2InstanceType: + AllowedValues: + - t2.large + - m4.large + - m4.xlarge + - m4.2xlarge + - m4.4xlarge + - m5.large + - m5.xlarge + - m5.2xlarge + - m5.4xlarge + Default: m5.xlarge + Description: Amazon EC2 instance type for the second Active Directory instance, located in Availability Zone 2. + Type: String + ADServer2NetBIOSName: + AllowedPattern: '[a-zA-Z0-9\-]+' + Default: DC2 + Description: NetBIOS name of the second Active Directory instance, located in Availability Zone 2. This name can have up to 15 characters. + MaxLength: '15' + MinLength: '1' + 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: Fixed private IP address for the second Active Directory instance, located in + Availability Zone 2. + Type: String + AvailabilityZones: + Description: >- + Availability Zones to use for the subnets in the VPC. Two Availability Zones are used and the logical order is preserved. + Type: List + 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 administrator. 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 added as domain administrator. This is separate from the default "Administrator" account. You can specify this user name only for the self-managed Active Directory scenario. If you're using the AWS Directory Service for Microsoft AD, the default stays "Admin" regardless of any value provided. + MaxLength: '25' + MinLength: '5' + Type: String + DomainDNSName: + AllowedPattern: '[a-zA-Z0-9\-]+\..+' + Default: example.com + Description: Fully qualified domain name (FQDN) of the forest root domain. + MaxLength: '255' + MinLength: '2' + Type: String + DomainNetBIOSName: + AllowedPattern: '[a-zA-Z0-9\-]+' + Default: example + Description: NetBIOS name of the domain for users of earlier + versions of Windows. You can use up to 15 characters. + MaxLength: '15' + MinLength: '1' + Type: String + FileShareThroughputCapacity: + Default: 8 + Description: File-share throughput capacity (MB/s) in 2 to the nth power increments, where 3 ≤ n ≤ 11, i.e. between 2^3 and 2^11. + Type: Number + AllowedValues: + - 8 + - 16 + - 32 + - 64 + - 128 + - 256 + - 512 + - 1024 + - 2048 + FileShareVolumeSize: + Default: '32' + Description: Capacity (GB) of the volume used to store SQL Server files. Minimum value is 32 and maximum 65536. + Type: Number + MinValue: 32 + MaxValue: 65536 + KeyPairName: + Description: Name of an existing EC2 key pair. All instances will launch with + this key pair. + Type: AWS::EC2::KeyPair::KeyName + MSSQLMediaBucketName: + AllowedPattern: "^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$" + ConstraintDescription: You can use lowercase letters, uppercase letters, and hyphens (-). Do not start or end with a hyphen (-). + Description: Name of the S3 bucket from which MSSQL media can be downloaded. You can use lowercase letters, uppercase letters, and hyphens (-). Do not start or end with a hyphen (-). + Type: String + MSSQLMediaPathKey: + Default: SQLServer2019-x64-ENU.iso + Description: Key for the S3 bucket from which MSSQL media can be downloaded. You can use numbers, lowercase letters, uppercase letters, hyphens (-), and forward slashes (/). + Type: String + NumberOfRDGWHosts: + AllowedValues: + - '0' + - '1' + - '2' + - '3' + - '4' + Default: '1' + Description: Number of Remote Desktop Gateway hosts to create. + Type: String + PrivateSubnet1CIDR: + 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])(\/([0-9]|[1-2][0-9]|3[0-2]))$ + Default: 10.0.0.0/19 + Description: CIDR block for private subnet 1 located in Availability Zone 1. + Type: String + PrivateSubnet2CIDR: + 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])(\/([0-9]|[1-2][0-9]|3[0-2]))$ + Default: 10.0.32.0/19 + Description: CIDR block for private subnet 2 located in Availability Zone 2. + Type: String + PublicSubnet1CIDR: + Default: 10.0.128.0/20 + Description: CIDR block for public subnet 1 located in Availability Zone 1. + Type: String + PublicSubnet2CIDR: + Default: 10.0.144.0/20 + Description: CIDR block for public subnet 2 located in Availability Zone 2. + 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-ia-us-east-1 + 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: cfn-ps-microsoft-sql-fci-fsx/ + 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 + RDGWInstanceType: + AllowedValues: + - t2.small + - t2.medium + - t2.large + - m5.large + - m5.xlarge + - m5.2xlarge + - m5.4xlarge + Default: t2.large + Description: Amazon EC2 instance type for the Remote Desktop Gateway instances. + Type: String + RDGWCIDR: + 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])(\/([0-9]|[1-2][0-9]|3[0-2]))$ + ConstraintDescription: The CIDR block parameter must be in the form x.x.x.x/x. + Description: Allowed CIDR block for external access to the Remote Desktop Gateway instances. + Type: String + SQLAdminAccounts: + Default: "example\\Admin" + Description: Comma-separated user names are used as MSSQL DB administrators. It could be Active Directory domain administrators or other local or domain users. + MaxLength: '25' + MinLength: '5' + Type: String + SqlFSxFCIName: + AllowedPattern: "[a-zA-Z0-9\\-]+" + Default: SqlFCI + Description: Name of the failover cluster instance (FCI). You can use up to 15 characters. + MaxLength: '15' + MinLength: '1' + Type: String + SqlFSxServerNetBIOSName1: + AllowedPattern: "[a-zA-Z0-9\\-]+" + Default: SqlFSx1 + Description: NetBIOS name of the primary instance. You can use up to 15 characters. + MaxLength: '15' + MinLength: '1' + Type: String + SqlFSxServerNetBIOSName2: + AllowedPattern: "[a-zA-Z0-9\\-]+" + Default: SqlFSx2 + Description: NetBIOS name of the secondary instance. You can use up to 15 characters. + MaxLength: '15' + MinLength: '1' + Type: String + SqlFSxWSFCName: + AllowedPattern: "[a-zA-Z0-9\\-]+" + Default: WSFC1 + Description: Name of the Windows Server Failover Cluster (WSFC). You can use up to 15 characters. + MaxLength: '15' + MinLength: '1' + Type: String + VPCCIDR: + 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])(\/(1[6-9]|2[0-8]))$ + ConstraintDescription: The CIDR block parameter must be in the form x.x.x.x/16-28. + Default: 10.0.0.0/16 + Description: CIDR block for the VPC. + Type: String + WindowsVersion: + AllowedValues: + - Windows_Server-2019-English-Full-Base* + - Windows_Server-2016-English-Full-Base* + ConstraintDescription: Must be one of the supported Windows versions. + Default: Windows_Server-2019-English-Full-Base* + Description: Choose the Windows version to run SQL Server. + Type: String + WorkloadInstanceType: + AllowedValues: + - t3a.2xlarge + - t3.2xlarge + - r5a.xlarge + - r5a.2xlarge + - r5a.4xlarge + - r5a.8xlarge + - r5.large + - r5.xlarge + - r5.2xlarge + - r5.4xlarge + - r5.8xlarge + - r4.xlarge + - r4.2xlarge + - r4.4xlarge + - r4.8xlarge + - z1d.large + - z1d.xlarge + ConstraintDescription: Must contain a valid instance type. + Default: r5.2xlarge + Description: Type of EC2 instance for the workload instances. + Type: String +Conditions: + UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-ia-us-east-1'] + AWSManagedAD: !Equals [!Ref ADScenarioType, 'AWS Directory Service for Microsoft AD (Enterprise Edition)'] + IncludeRDGW: !Not + - !Equals + - !Ref NumberOfRDGWHosts + - '0' +Resources: + VPCStack: + Type: AWS::CloudFormation::Stack + Properties: + TemplateURL: !Sub + - 'https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}submodules/cfn-ps-aws-vpc/templates/aws-vpc.template.yaml' + - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] + S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] + Parameters: + AvailabilityZones: !Join + - ',' + - !Ref 'AvailabilityZones' + NumberOfAZs: '2' + PrivateSubnet1ACIDR: !Ref 'PrivateSubnet1CIDR' + PrivateSubnet2ACIDR: !Ref 'PrivateSubnet2CIDR' + PublicSubnet1CIDR: !Ref 'PublicSubnet1CIDR' + PublicSubnet2CIDR: !Ref 'PublicSubnet2CIDR' + VPCCIDR: !Ref 'VPCCIDR' + ADStack: + Type: AWS::CloudFormation::Stack + Properties: + TemplateURL: !Sub + - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}submodules/cfn-ps-microsoft-activedirectory/templates/${QSADTemplate} + - QSADTemplate: !If [AWSManagedAD, ad-3.template, ad-1.template] + S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] + S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] + Parameters: !If + - AWSManagedAD + - DomainAdminPassword: !Ref 'DomainAdminPassword' + DomainDNSName: !Ref 'DomainDNSName' + DomainNetBIOSName: !Ref 'DomainNetBIOSName' + KeyPairName: !Ref 'KeyPairName' + PrivateSubnet1ID: !GetAtt 'VPCStack.Outputs.PrivateSubnet1AID' + PrivateSubnet2ID: !GetAtt 'VPCStack.Outputs.PrivateSubnet2AID' + QSS3BucketName: !Ref 'QSS3BucketName' + QSS3BucketRegion: !Ref 'QSS3BucketRegion' + QSS3KeyPrefix: !Sub '${QSS3KeyPrefix}submodules/cfn-ps-microsoft-activedirectory/' + VPCCIDR: !Ref 'VPCCIDR' + VPCID: !GetAtt 'VPCStack.Outputs.VPCID' + - ADServer1InstanceType: !Ref 'ADServer1InstanceType' + ADServer1NetBIOSName: !Ref 'ADServer1NetBIOSName' + ADServer1PrivateIP: !Ref 'ADServer1PrivateIP' + ADServer2InstanceType: !Ref 'ADServer2InstanceType' + ADServer2NetBIOSName: !Ref 'ADServer2NetBIOSName' + ADServer2PrivateIP: !Ref 'ADServer2PrivateIP' + DomainAdminPassword: !Ref 'DomainAdminPassword' + DomainAdminUser: !Ref 'DomainAdminUser' + DomainDNSName: !Ref 'DomainDNSName' + DomainNetBIOSName: !Ref 'DomainNetBIOSName' + KeyPairName: !Ref 'KeyPairName' + PrivateSubnet1ID: !GetAtt 'VPCStack.Outputs.PrivateSubnet1AID' + PrivateSubnet2ID: !GetAtt 'VPCStack.Outputs.PrivateSubnet2AID' + QSS3BucketName: !Ref 'QSS3BucketName' + QSS3BucketRegion: !Ref 'QSS3BucketRegion' + QSS3KeyPrefix: !Sub '${QSS3KeyPrefix}submodules/cfn-ps-microsoft-activedirectory/' + VPCCIDR: !Ref 'VPCCIDR' + VPCID: !GetAtt 'VPCStack.Outputs.VPCID' + RDGWStack: + Condition: IncludeRDGW + Type: AWS::CloudFormation::Stack + Properties: + TemplateURL: !Sub + - 'https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}submodules/cfn-ps-microsoft-rdgateway/templates/rdgw-domain.template' + - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] + S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] + Parameters: + DomainAdminPassword: !Ref 'DomainAdminPassword' + DomainAdminUser: admin + DomainDNSName: !Ref 'DomainDNSName' + DomainMemberSGID: !GetAtt 'ADStack.Outputs.DomainMemberSGID' + DomainNetBIOSName: !Ref 'DomainNetBIOSName' + KeyPairName: !Ref 'KeyPairName' + NumberOfRDGWHosts: !Ref 'NumberOfRDGWHosts' + PublicSubnet1ID: !GetAtt 'VPCStack.Outputs.PublicSubnet1ID' + PublicSubnet2ID: !GetAtt 'VPCStack.Outputs.PublicSubnet2ID' + QSS3BucketName: !Ref 'QSS3BucketName' + QSS3BucketRegion: !Ref 'QSS3BucketRegion' + QSS3KeyPrefix: !Sub '${QSS3KeyPrefix}submodules/cfn-ps-microsoft-rdgateway/' + RDGWInstanceType: !Ref 'RDGWInstanceType' + RDGWCIDR: !Ref 'RDGWCIDR' + VPCID: !GetAtt 'VPCStack.Outputs.VPCID' + SQLStack: + Type: AWS::CloudFormation::Stack + Properties: + TemplateURL: !Sub + - 'https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}templates/${SQLTemplate}' + - SQLTemplate: !If [AWSManagedAD, mssqlfsx-mad.template.yaml, mssqlfsx.template.yaml] + S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] + S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] + Parameters: !If + - AWSManagedAD + - VPCID: !GetAtt 'VPCStack.Outputs.VPCID' + PrivateSubnet1ID: !GetAtt 'VPCStack.Outputs.PrivateSubnet1AID' + PrivateSubnet2ID: !GetAtt 'VPCStack.Outputs.PrivateSubnet2AID' + DirectoryId: !GetAtt 'ADStack.Outputs.DirectoryID' + DomainDNSName: !Ref 'DomainDNSName' + DomainMemberSGID: !GetAtt 'ADStack.Outputs.DomainMemberSGID' + VPCcidr: !Ref 'VPCCIDR' + DomainNetBIOSName: !Ref 'DomainNetBIOSName' + DomainAdminUser: admin + DomainAdminPassword: !Ref 'DomainAdminPassword' + ADDnsIpAddresses: !Join [',', [!GetAtt 'ADStack.Outputs.ADServer1PrivateIP', !GetAtt 'ADStack.Outputs.ADServer2PrivateIP']] + SqlFSxServerNetBIOSName1: !Ref 'SqlFSxServerNetBIOSName1' + SqlFSxServerNetBIOSName2: !Ref 'SqlFSxServerNetBIOSName2' + KeyPairName: !Ref 'KeyPairName' + WindowsVersion: !Ref 'WindowsVersion' + WorkloadInstanceType: !Ref 'WorkloadInstanceType' + FileShareVolumeSize: !Ref 'FileShareVolumeSize' + FileShareThroughputCapacity: !Ref 'FileShareThroughputCapacity' + MSSQLMediaBucketName: !Ref 'MSSQLMediaBucketName' + MSSQLMediaPathKey: !Ref 'MSSQLMediaPathKey' + SqlFSxWSFCName: !Ref 'SqlFSxWSFCName' + SqlFSxFCIName: !Ref 'SqlFSxFCIName' + SQLAdminAccounts: !Ref 'SQLAdminAccounts' + QSS3BucketName: !Ref 'QSS3BucketName' + QSS3BucketRegion: !Ref 'QSS3BucketRegion' + QSS3KeyPrefix: !Ref 'QSS3KeyPrefix' + ApplicationName: !Ref 'ApplicationName' + EnableAppInsights: !Ref 'EnableAppInsights' + ResourceGroupName: !Ref 'ResourceGroupName' + - VPCID: !GetAtt 'VPCStack.Outputs.VPCID' + PrivateSubnet1ID: !GetAtt 'VPCStack.Outputs.PrivateSubnet1AID' + PrivateSubnet2ID: !GetAtt 'VPCStack.Outputs.PrivateSubnet2AID' + DomainDNSName: !Ref 'DomainDNSName' + DomainMemberSGID: !GetAtt 'ADStack.Outputs.DomainMemberSGID' + VPCcidr: !Ref 'VPCCIDR' + DomainNetBIOSName: !Ref 'DomainNetBIOSName' + DomainAdminUser: admin + DomainAdminPassword: !Ref 'DomainAdminPassword' + ADDnsIpAddresses: !Join [',', [!Ref ADServer1PrivateIP, !Ref ADServer2PrivateIP]] + SqlFSxServerNetBIOSName1: !Ref 'SqlFSxServerNetBIOSName1' + SqlFSxServerNetBIOSName2: !Ref 'SqlFSxServerNetBIOSName2' + KeyPairName: !Ref 'KeyPairName' + WindowsVersion: !Ref 'WindowsVersion' + WorkloadInstanceType: !Ref 'WorkloadInstanceType' + FileShareVolumeSize: !Ref 'FileShareVolumeSize' + FileShareThroughputCapacity: !Ref 'FileShareThroughputCapacity' + MSSQLMediaBucketName: !Ref 'MSSQLMediaBucketName' + MSSQLMediaPathKey: !Ref 'MSSQLMediaPathKey' + SqlFSxWSFCName: !Ref 'SqlFSxWSFCName' + SqlFSxFCIName: !Ref 'SqlFSxFCIName' + SQLAdminAccounts: !Ref 'SQLAdminAccounts' + QSS3BucketName: !Ref 'QSS3BucketName' + QSS3BucketRegion: !Ref 'QSS3BucketRegion' + QSS3KeyPrefix: !Ref 'QSS3KeyPrefix' + ApplicationName: !Ref 'ApplicationName' + EnableAppInsights: !Ref 'EnableAppInsights' + ResourceGroupName: !Ref 'ResourceGroupName' diff --git a/templates/mssqlfsx.template.yaml b/templates/mssqlfsx.template.yaml new file mode 100644 index 0000000..dceebb4 --- /dev/null +++ b/templates/mssqlfsx.template.yaml @@ -0,0 +1,1394 @@ +AWSTemplateFormatVersion: '2010-09-09' +Description: This workload template deploys a SqlFSx instance in an existing VPC. **IMPORTANT** This template creates EC2 instances and related resources. You will be billed for the AWS resources used if + you create a stack from this template. (qs-1rqmju6dm) +Metadata: + cfn-lint: + config: + ignore_checks: + - W9006 + - E9101 + - E3030 #Invalid runtime - false positive + - E3031 #False positive + QuickStartDocumentation: + EntrypointName: Parameters for deploying into an existing VPC with self-managed Active Directory + Order: '3' + AWS::CloudFormation::Interface: + ParameterGroups: + - Label: + default: Network configuration + Parameters: + - VPCID + - VPCcidr + - PrivateSubnet1ID + - PrivateSubnet2ID + - Label: + default: Microsoft Active Directory configuration + Parameters: + - DomainDNSName + - DomainNetBIOSName + - DomainAdminUser + - DomainAdminPassword + - DomainMemberSGID + - ADDnsIpAddresses + - Label: + default: Amazon EC2 configuration + Parameters: + - SqlFSxServerNetBIOSName1 + - SqlFSxServerNetBIOSName2 + - KeyPairName + - WindowsVersion + - WorkloadInstanceType + - Label: + default: Amazon FSx Windows file-share configuration + Parameters: + - FileShareVolumeSize + - FileShareThroughputCapacity + - Label: + default: Microsoft SQL Server FCI configurations + Parameters: + - MSSQLMediaBucketName + - MSSQLMediaPathKey + - SqlFSxWSFCName + - SqlFSxFCIName + - SQLAdminAccounts + - Label: + default: Application Insights configuration + Parameters: + - EnableAppInsights + - ApplicationName + - ResourceGroupName + - 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 + KeyPairName: + default: SSH key name + PrivateSubnet1ID: + default: Private subnet 1 ID + PrivateSubnet2ID: + default: Private subnet 2 ID + QSS3BucketName: + default: Quick Start S3 bucket name + QSS3BucketRegion: + default: Quick Start S3 bucket Region + QSS3KeyPrefix: + default: Quick Start S3 key prefix + VPCID: + default: VPC ID + VPCcidr: + default: 10.0.0.0/8 + WorkloadInstanceType: + default: Workload servers instance type + DomainAdminPassword: + default: Domain administrator password + DomainAdminUser: + default: Domain administrator user name + DomainDNSName: + default: Domain DNS name + DomainMemberSGID: + default: Security group ID for Active Directory domain members + ADDnsIpAddresses: + default: Active Directory DNS IP addresses + MSSQLMediaBucketName: + default: S3 bucket for SQL media + MSSQLMediaPathKey: + default: S3 key for SQL media path + SqlFSxWSFCName: + default: Windows Server Failover Cluster (WSFC) name + SqlFSxFCIName: + default: Failover cluster instance (FCI) name + SQLAdminAccounts: + default: SQL administrator account or accounts + DomainNetBIOSName: + default: Domain NetBIOS name + SqlFSxServerNetBIOSName1: + default: Server1 NetBIOS name + SqlFSxServerNetBIOSName2: + default: Server2 NetBIOS name + WindowsVersion: + default: Windows version + FileShareVolumeSize: + default: File-share volume size + FileShareThroughputCapacity: + default: File-share throughput capacity +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 + KeyPairName: + Description: Name of an existing EC2 key pair. All instances will launch with this key pair. + Type: AWS::EC2::KeyPair::KeyName + FileShareVolumeSize: + Default: 32 + Description: Capacity (GB) of the volume used to store SQL Server files. Minimum value is 32 and maximum 65536. + Type: Number + MinValue: 32 + MaxValue: 65536 + FileShareThroughputCapacity: + Default: 8 + Description: File-share throughput capacity (MB/s) in 2 to the nth power increments, where 3 ≤ n ≤ 11, i.e. between 2^3 and 2^11. + Type: Number + AllowedValues: + - 8 + - 16 + - 32 + - 64 + - 128 + - 256 + - 512 + - 1024 + - 2048 + PrivateSubnet1ID: + Description: ID of private subnet 1 in Availability Zone 1 for the workload (e.g., subnet-a0246dcd). + Type: AWS::EC2::Subnet::Id + PrivateSubnet2ID: + Description: ID of private subnet 2 in Availability Zone 2 for the workload (e.g., subnet-a0246dcd). + Type: AWS::EC2::Subnet::Id + 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-ia-us-east-1 + 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: cfn-ps-microsoft-sql-fci-fsx/ + 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 + VPCID: + Description: ID of your existing VPC for deployment + Type: AWS::EC2::VPC::Id + VPCcidr: + 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])(\/(1[6-9]|2[0-8]))$ + ConstraintDescription: The CIDR block parameter must be in the form x.x.x.x/16-28. + Default: 10.0.0.0/16 + Description: CIDR block for the VPC. + Type: String + SqlFSxServerNetBIOSName1: + AllowedPattern: '[a-zA-Z0-9\-]+' + Default: SqlFSx1 + Description: NetBIOS name of the primary instance. You can use up to 15 characters. + MaxLength: '15' + MinLength: '1' + Type: String + SqlFSxServerNetBIOSName2: + AllowedPattern: '[a-zA-Z0-9\-]+' + Default: SqlFSx2 + Description: NetBIOS name of the secondary instance. You can use up to 15 characters. + MaxLength: '15' + MinLength: '1' + Type: String + SqlFSxWSFCName: + AllowedPattern: '[a-zA-Z0-9\-]+' + Default: WSFC1 + Description: Name of the Windows Server Failover Cluster (WSFC). You can use up to 15 characters. + MaxLength: '15' + MinLength: '1' + Type: String + SqlFSxFCIName: + AllowedPattern: '[a-zA-Z0-9\-]+' + Default: SqlFCI + Description: Name of the failover cluster instance (FCI). You can use up to 15 characters. + MaxLength: '15' + MinLength: '1' + Type: String + DomainAdminPassword: + Description: Password for the domain administrator. + MaxLength: '32' + MinLength: '0' + NoEcho: 'true' + Type: String + DomainAdminUser: + AllowedPattern: '[a-zA-Z0-9]*' + Default: StackAdmin + 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 + ADDnsIpAddresses: + Description: DNS IP addresses for Active Directory. Separate these with commas. + MaxLength: '255' + MinLength: '2' + Type: String + DomainMemberSGID: + Description: ID of the domain member security group (e.g., sg-9cb7d0e5). + Type: String + DomainNetBIOSName: + AllowedPattern: '[a-zA-Z0-9\-]+' + Default: EXAMPLE + Description: NetBIOS name of the domain for users of earlier versions of Windows. You can use up to 15 characters. + MaxLength: '15' + MinLength: '1' + Type: String + WindowsVersion: + AllowedValues: + - Windows_Server-2019-English-Full-Base* + - Windows_Server-2016-English-Full-Base* + ConstraintDescription: Must be one of the supported Windows versions. + Default: Windows_Server-2019-English-Full-Base* + Description: Select Windows version to run SQL Server. + Type: String + WorkloadInstanceType: + AllowedValues: + - t3a.2xlarge + - t3.2xlarge + - r5a.xlarge + - r5a.2xlarge + - r5a.4xlarge + - r5a.8xlarge + - r5.large + - r5.xlarge + - r5.2xlarge + - r5.4xlarge + - r5.8xlarge + - r4.xlarge + - r4.2xlarge + - r4.4xlarge + - r4.8xlarge + - z1d.large + - z1d.xlarge + ConstraintDescription: Must contain a valid instance type. + Default: r5.2xlarge + Description: Type of EC2 instance for the workload instances. + Type: String + MSSQLMediaBucketName: + AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$ + ConstraintDescription: You can use lowercase letters, uppercase letters, and hyphens (-). Do not start or end with a hyphen (-). + Default: ss-experiments + Description: Name of the S3 bucket from which MSSQL media can be downloaded. You can use lowercase letters, uppercase letters, and hyphens (-). Do not start or end with a hyphen (-). + Type: String + MSSQLMediaPathKey: + Default: cfn-ps-microsoft-SqlFSx/sql-installation-media/en_sql_server_2019_developer_x64_dvd_baea4195.iso + Description: Key for the S3 bucket from which MSSQL media can be downloaded. You can use numbers, lowercase letters, uppercase letters, hyphens (-), and forward slashes (/). + Type: String + SQLAdminAccounts: + Default: contoso\Administrator + Description: Comma-separated user names are used as MSSQL DB administrators. It could be Active Directory domain administrators or other local or domain users. + MaxLength: '25' + MinLength: '5' + Type: String +Conditions: + UsingDefaultBucket: + Fn::Equals: + - Ref: QSS3BucketName + - aws-ia-us-east-1 + EnableAppInsightForSQLHA: + Fn::Equals: + - Ref: EnableAppInsights + - 'true' +Rules: + KeyPairsNotEmpty: + Assertions: + - Assert: + Fn::Not: + - Fn::EachMemberEquals: + - Fn::RefAll: AWS::EC2::KeyPair::KeyName + - '' + AssertDescription: All key pair parameters must not be empty + SubnetsInVPC: + Assertions: + - Assert: + Fn::EachMemberIn: + - Fn::ValueOfAll: + - AWS::EC2::Subnet::Id + - VpcId + - Fn::RefAll: AWS::EC2::VPC::Id + AssertDescription: All subnets must in the VPC +Resources: + ADAdminSecrets: + Type: AWS::SecretsManager::Secret + Properties: + Name: + Fn::Sub: AWSQuickStart/${AWS::StackName}/ADAdminSecrets + Description: Active Directory administrator Credentials for Quick Start + SecretString: + Fn::Sub: '{ "username" : "${DomainAdminUser}", "password" : "${DomainAdminPassword}" }' + QuickStartLogs: + Type: AWS::Logs::LogGroup + Properties: + LogGroupName: + Fn::Sub: /aws/Quick_Start/${AWS::StackName} + RetentionInDays: 30 + WindowsBaseAMIInfo: + Type: Custom::WindowsBaseAMIInfo + Properties: + ServiceToken: + Fn::GetAtt: + - AMIInfoFunction + - Arn + Region: + Ref: AWS::Region + AMIName: + Ref: WindowsVersion + AMIInfoFunction: + Type: AWS::Lambda::Function + Properties: + Code: + ZipFile: + Fn::Join: + - '' + - - "/**\n" + - "* A Lambda function that looks up the latest AMI ID for a given region and architecture.\n" + - "**/\n" + - var aws = require("aws-sdk"); + - exports.handler = function(event, context) { + - ' console.log("REQUEST RECEIVED:\n" + JSON.stringify(event));' + - " // For Delete requests, immediately send a SUCCESS response.\n" + - ' if (event.RequestType == "Delete") {' + - ' sendResponse(event, context, "SUCCESS");' + - " return;\n" + - " }\n" + - ' var responseStatus = "FAILED";' + - " var responseData = {};\n" + - " var ec2 = new aws.EC2({region: event.ResourceProperties.Region});\n" + - " var describeImagesParams = {\n" + - ' Filters: [{ Name: "name", Values: [event.ResourceProperties.AMIName]}],' + - ' Owners: ["amazon"]' + - " };\n" + - " // Get AMI IDs with the specified name pattern and owner\n" + - " ec2.describeImages(describeImagesParams, function(err, describeImagesResult) {\n" + - " if (err) {\n" + - ' responseData = {Error: "DescribeImages call failed"};' + - ' console.log(responseData.Error + ":\n", err);' + - " }\n" + - " else {\n" + - " var images = describeImagesResult.Images;\n" + - " // Sort images by name in decscending order. The names contain the AMI version, formatted as YYYY.MM.Ver.\n" + - " images.sort(function(x, y) { return y.CreationDate.localeCompare(x.CreationDate); });\n" + - " for (var j = 0; j < images.length; j++) {\n" + - " if (isBeta(images[j].Name)) continue;\n" + - ' responseStatus = "SUCCESS";' + - ' responseData["Id"] = images[j].ImageId;' + - " break;\n" + - " }\n" + - " }\n" + - " sendResponse(event, context, responseStatus, responseData);\n" + - " });\n" + - "};\n" + - "\n" + - "// Check if the image is a beta or rc image. The Lambda function won't return any of those images.\n" + - function isBeta(imageName) { + - ' return imageName.toLowerCase().indexOf("beta") > -1 || imageName.toLowerCase().indexOf(".rc") > -1;' + - "}\n" + - "// Send response to the pre-signed S3 URL \n" + - function sendResponse(event, context, responseStatus, responseData) { + - " var responseBody = JSON.stringify({\n" + - " Status: responseStatus,\n" + - ' Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,' + - " PhysicalResourceId: context.logStreamName,\n" + - " StackId: event.StackId,\n" + - " RequestId: event.RequestId,\n" + - " LogicalResourceId: event.LogicalResourceId,\n" + - " Data: responseData\n" + - " });\n" + - ' console.log("RESPONSE BODY:\n", responseBody);' + - ' var https = require("https");' + - ' var url = require("url");' + - " var parsedUrl = url.parse(event.ResponseURL);\n" + - " var options = {\n" + - " hostname: parsedUrl.hostname,\n" + - " port: 443,\n" + - " path: parsedUrl.path,\n" + - ' method: "PUT",' + - " headers: {\n" + - ' "content-type": "",' + - ' "content-length": responseBody.length' + - " }\n" + - " };\n" + - ' console.log("SENDING RESPONSE...\n");' + - " var request = https.request(options, function(response) {\n" + - ' console.log("STATUS: " + response.statusCode);' + - ' console.log("HEADERS: " + JSON.stringify(response.headers));' + - " // Tell AWS Lambda that the function execution is done \n" + - " context.done();\n" + - " });\n" + - ' request.on("error", function(error) {' + - ' console.log("sendResponse Error:" + error);' + - " // Tell AWS Lambda that the function execution is done \n" + - " context.done();\n" + - " });\n" + - " // write data to request body\n" + - " request.write(responseBody);\n" + - " request.end();\n" + - '}' + Handler: index.handler + Role: + Fn::GetAtt: + - AMIInfoFunctionLambdaExecutionRole + - Arn + Runtime: nodejs18.x + Timeout: 30 + AMIInfoFunctionLambdaExecutionRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: + - lambda.amazonaws.com + Action: + - sts:AssumeRole + Path: / + Policies: + - PolicyName: root + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - logs:CreateLogGroup + - logs:CreateLogStream + - logs:PutLogEvents + Resource: + Fn::Sub: arn:${AWS::Partition}:logs:*:*:* + - Effect: Allow + Action: + - ec2:DescribeImages + Resource: '*' + AWSQuickstartSqlFSxRole: + Type: AWS::IAM::Role + Metadata: + cfn-lint: + config: + ignore_checks: + - EIAMPolicyWildcardResource + - EIAMPolicyActionWildcard + Properties: + Policies: + - PolicyDocument: + Version: '2012-10-17' + Statement: + - Action: + - s3:GetObject + - s3:ListBucket + Resource: + - Fn::Sub: + - arn:${AWS::Partition}:s3:::${S3Bucket}/${QSS3KeyPrefix}* + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + - Fn::Sub: + - arn:${AWS::Partition}:s3:::${S3Bucket} + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + Effect: Allow + PolicyName: aws-quick-start-s3-policy + - PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - cloudformation:SignalResource + Resource: + Fn::Sub: arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/* + - Effect: Allow + Action: + - ec2:DescribeInstances + - ec2:DescribeInstanceStatus + - ec2:StopInstances + - ec2:StartInstances + - ec2:DescribeNetworkInterfaces + - ec2:DescribeSubnets + - ssm:* + - fsx:Describe* + - fsx:ListTagsForResource + Resource: '*' + PolicyName: SqlFSx-SSM-AutomationExecution + Path: / + AssumeRolePolicyDocument: + Statement: + - Action: + - sts:AssumeRole + Principal: + Service: + - ec2.amazonaws.com + - ssm.amazonaws.com + Effect: Allow + Version: '2012-10-17' + SqlFSxAutomation: + Type: AWS::SSM::Document + Properties: + DocumentType: Automation + Content: + schemaVersion: '0.3' + description: Deploy SqlFSx with SSM Automation + assumeRole: '{{AutomationAssumeRole}}' + parameters: + DomainDNSName: + default: + Fn::Sub: ${DomainDNSName} + description: Fully qualified domain name (FQDN) of the forest root domain. + type: String + DomainNetBIOSName: + default: + Fn::Sub: ${DomainNetBIOSName} + description: NetBIOS name of the domain for users of earlier versions of Windows. You can use up to 15 characters. + type: String + MSSQLMediaBucketName: + default: + Fn::Sub: ${MSSQLMediaBucketName} + description: Name of the S3 bucket from which MSSQL media can be downloaded. You can use lowercase letters, uppercase letters, and hyphens (-). Do not start or end with a hyphen (-). + type: String + MSSQLMediaPathKey: + default: + Fn::Sub: ${MSSQLMediaPathKey} + description: Key for the S3 bucket from which MSSQL media can be downloaded. You can use numbers, lowercase letters, uppercase letters, hyphens (-), and forward slashes (/). + type: String + ClusterName: + default: + Fn::Sub: ${SqlFSxWSFCName} + description: NetBIOS name of the cluster (up to 15 characters). + type: String + SqlFSxFCIName: + default: + Fn::Sub: ${SqlFSxFCIName} + description: NetBIOS name of the failover cluster instance (up to 15 characters). + type: String + SQLAdminAccounts: + default: + Fn::Sub: ${SQLAdminAccounts} + description: MSSQL administrator accounts + type: String + AdminSecrets: + default: + Fn::Sub: ${ADAdminSecrets} + description: Domain admin credentials + type: String + SqlFSxInstanceId: + default: '' + description: ID of the SqlFSx instance + type: String + StackName: + default: + Fn::Sub: ${AWS::StackName} + description: CloudFormation stack name + type: String + SqlFSxServerNetBIOSName1: + default: + Fn::Sub: ${SqlFSxServerNetBIOSName1} + description: CloudFormation stack name + type: String + AutomationAssumeRole: + default: '' + description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf. + type: String + WindowsVersion: + default: + Fn::Sub: ${WindowsVersion} + description: The AMI used to deploy SqlFSx instance. + type: String + URLSuffix: + description: AWS URL suffix + 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 + mainSteps: + - name: FindFSxVolDnsAddress + action: aws:executeScript + onFailure: step:signalfailure + inputs: + Runtime: PowerShell Core 6.0 + Script: "Install-Module AWS.Tools.FSx -Force\nImport-Module AWS.Tools.FSx\n \n$fsList = Get-FSXFileSystem; \n$fsList.forEach{\n $tags = $_.Tags; \n foreach ($tag in $tags)\n {\n if ($tag.key\ + \ -eq \"Name\" -And $tag.value -eq \"SqlFSxFileSystem\")\n {\n $dnsName = $_.DNSName;\n $psEndpoint = $_.WindowsConfiguration.RemoteAdministrationEndpoint\n break;\n \ + \ }\n }\n};\n$resultStr = \"{0};{1}\" -f $dnsName, $psEndpoint\nWrite-Host $dnsName\nreturn @{message=$resultStr}\n" + outputs: + - Name: FSxDnsAddress + Selector: $.Payload.message + Type: String + - name: GetAllClusterNodes + action: aws:executeAwsApi + outputs: + - Name: InstanceIds + Selector: $.Reservations..Instances..InstanceId + Type: StringList + inputs: + Service: ec2 + Api: DescribeInstances + Filters: + - Name: tag:FCIName + Values: + - Ref: SqlFSxFCIName + - Name: instance-state-name + Values: + - running + description: Get instances + - name: GetClusterNode1 + action: aws:executeAwsApi + outputs: + - Name: InstanceIds + Selector: $.Reservations..Instances..InstanceId + Type: StringList + inputs: + Service: ec2 + Api: DescribeInstances + Filters: + - Name: tag:Name + Values: + - Ref: SqlFSxServerNetBIOSName1 + - Name: instance-state-name + Values: + - running + description: Get instances + - name: GetClusterNode2 + action: aws:executeAwsApi + outputs: + - Name: InstanceIds + Selector: $.Reservations..Instances..InstanceId + Type: StringList + inputs: + Service: ec2 + Api: DescribeInstances + Filters: + - Name: tag:Name + Values: + - Ref: SqlFSxServerNetBIOSName2 + - Name: instance-state-name + Values: + - running + description: Get instances + - name: FindSecondaryIpAddresses + action: aws:executeScript + onFailure: step:signalfailure + inputs: + Runtime: PowerShell Core 6.0 + Script: "$inputPayload = $env:InputPayload | ConvertFrom-Json; \nInstall-Module AWS.Tools.EC2 -Force\nImport-Module AWS.Tools.EC2\n \n#Function to find Subnet\nfunction Get-SubnetMask($i)\n\ + {\n $instance = $i\n $subnetid = $instance.instances[0].NetworkInterfaces.SubnetId\n $subnet = get-ec2subnet -SubnetId $subnetid\n $cidr = $subnet.CidrBlock\n $cidr_mask = $cidr.split('/')[1]\n\ + \ $A = 0\n $A_Index = 8\n $B = 0\n $B_Index = 16\n $C = 0\n $C_Index = 24\n $D = 0\n $D_Index = 32\n for ($i = 1; $i -le $cidr_mask; $i++)\n {\n if ($i -le $A_Index)\n\ + \ {\n $A += ([Math]::Pow(2, 8 - $i))\n }\n elseif ($i -le $B_Index)\n {\n $B += ([Math]::Pow(2, 8 - $i + $A_Index))\n }\n elseif ($i\ + \ -le $C_Index)\n {\n $C += ([Math]::Pow(2, 8 - $i + $B_Index))\n }\n elseif ($i -le $D_Index)\n {\n $D += ([Math]::Pow(2, 8 - $i + $C_Index))\n\ + \ }\n }\n $subnet_mask = \"{0}.{1}.{2}.{3}\" -f $A, $B, $C, $D\n return $subnet_mask\n}\n\n$instanceIds = $inputPayload.parameter\n \n$resultStr = \"\"\n$subnetMaskStr = \"\"\ + \n\n#Finding secondary IP addresses of the primary instance \nforeach ($i in $instanceIds)\n{ \n $instance = get-ec2instance -InstanceId $i\n $tags = $instance.instances[0].Tags\n\ + \ foreach ($t in $tags)\n {\n if ($t.Key -eq \"FCIRole\" -And $t.Value -eq \"Primary\")\n {\n $IPs = $instance.instances[0].NetworkInterfaces.PrivateIpAddresses\n $ip1\ + \ = $IPs[1].PrivateIpAddress\n $ip2 = $IPs[2].PrivateIpAddress\n $resultStr = \"{0};{1}\" -f $ip1, $ip2\n \n $subnetMaskStr = Get-SubnetMask($instance)\n break;\n\ + \ } \n }\n if ([System.String]::IsNullOrEmpty($resultStr) -Eq $False)\n {\n break;\n }\n}\n\n#Finding secondary IP addresses of the secondary instance \nforeach\ + \ ($i in $instanceIds)\n{ \n $instance = get-ec2instance -InstanceId $i\n $tags = $instance.instances[0].Tags\n foreach ($t in $tags)\n {\n if ($t.Key -eq \"FCIRole\"\ + \ -And $t.Value -eq \"Secondary\")\n {\n $IPs = $instance.instances[0].NetworkInterfaces.PrivateIpAddresses \n $ip1 = $IPs[1].PrivateIpAddress\n $ip2 = $IPs[2].PrivateIpAddress\ + \ \n $resultStr = \"{0};{1};{2}\" -f $resultStr, $ip1, $ip2\n \n $subnetMask = Get-SubnetMask($instance)\n $subnetMaskStr = \"{0};{1}\" -f $subnetMaskStr,\ + \ $subnetMask\n break;\n } \n }\n}\n\n$resultStr = \"{0};{1}\" -f $resultStr, $subnetMaskStr\n\nWrite-Host $dnsName\nreturn @{message=$resultStr}\n" + InputPayload: + parameter: '{{GetAllClusterNodes.InstanceIds}}' + outputs: + - Name: SecondaryPrivateIpAddresses + Selector: $.Payload.message + Type: String + - name: UpdateSSMAgent + action: aws:runCommand + timeoutSeconds: 300 + onFailure: Continue + inputs: + DocumentName: AWS-UpdateSSMAgent + InstanceIds: '{{GetAllClusterNodes.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + - name: InitializeDisk + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunPowerShellScript + InstanceIds: '{{GetAllClusterNodes.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + commands: + - "C:\\ProgramData\\Amazon\\EC2-Windows\\Launch\\Scripts\\InitializeDisks.ps1\n" + - name: InstallDSCModules + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: '{{GetAllClusterNodes.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/install-dsc-modules.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./install-dsc-modules.ps1 + - name: wsfcnLCMConfig + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: '{{GetAllClusterNodes.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/LCM-Config.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./LCM-Config.ps1 + - name: CreateDomainJoinMOF + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: '{{GetAllClusterNodes.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/DomainJoin.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./DomainJoin.ps1 -DomainNetBIOSName '{{DomainNetBIOSName}}' -DomainDNSName '{{DomainDNSName}}' -AdminSecret '{{AdminSecrets}}' -InstanceName $(Get-Content -Path C:\log\hostname.txt) + - name: RunDomainJoinMOF + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunPowerShellScript + InstanceIds: '{{GetAllClusterNodes.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + commands: + - "function DscStatusCheck () {\n $LCMState = (Get-DscLocalConfigurationManager).LCMState\n if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') {\n \ + \ 'returning 3010, should continue after reboot'\n exit 3010\n } else {\n 'Completed'\n }\n}\n\nStart-DscConfiguration 'C:\\AWSQuickstart\\DomainJoin' -Wait -Verbose\ + \ -Force\n\nDscStatusCheck\n" + - name: EnableCredSSP + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: '{{GetAllClusterNodes.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/enable-CredSSP.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./enable-CredSSP.ps1 -DomainDNSName '{{DomainDNSName}}' + - name: CreateCAShare + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: '{{GetClusterNode1.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/create-ca-share.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./create-ca-share.ps1 -DomainNetBIOSName '{{DomainNetBIOSName}}' -AdminSecret '{{AdminSecrets}}' -FSxRemoteAdminEndpoint '{{FindFSxVolDnsAddress.FSxDnsAddress}}'.split(';')[1] + - name: CreateNode1WsfcMOF + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: '{{GetClusterNode1.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/Node1Config.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./Node1Config.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -DomainDNSName {{DomainDNSName}} -WSFCNode1PrivateIP2 '{{FindSecondaryIpAddresses.SecondaryPrivateIpAddresses}}'.split(';')[0] + -ClusterName {{ClusterName}} -AdminSecret {{AdminSecrets}} -FileServerNetBIOSName '{{FindFSxVolDnsAddress.FSxDnsAddress}}'.split(';')[0] + - name: RunNode1WsfcMOF + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunPowerShellScript + InstanceIds: '{{GetClusterNode1.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + commands: + - "function DscStatusCheck () {\n $LCMState = (Get-DscLocalConfigurationManager).LCMState\n if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') {\n \ + \ 'returning 3010, should continue after reboot'\n exit 3010\n } else {\n 'Completed'\n }\n}\n\nStart-DscConfiguration 'C:\\AWSQuickstart\\WSFCNode1Config' -Wait -Verbose\ + \ -Force\n\nDscStatusCheck\n" + - name: CreateNode2WsfcMOF + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: + - '{{GetClusterNode2.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/AdditionalNodeConfig.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./AdditionalNodeConfig.ps1 -DomainNetBIOSName {{DomainNetBIOSName}} -WSFCNodePrivateIP2 '{{FindSecondaryIpAddresses.SecondaryPrivateIpAddresses}}'.split(';')[2] -ClusterName + {{ClusterName}} -AdminSecret {{AdminSecrets}} + - name: RunNode2WsfcMOF + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunPowerShellScript + InstanceIds: + - '{{GetClusterNode2.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + commands: + - "function DscStatusCheck () {\n $LCMState = (Get-DscLocalConfigurationManager).LCMState\n if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') {\n \ + \ 'returning 3010, should continue after reboot'\n exit 3010\n } else {\n 'Completed'\n }\n}\n\nStart-DscConfiguration 'C:\\AWSQuickstart\\AdditionalWSFCNode' -Wait\ + \ -Verbose -Force\n\nDscStatusCheck \n" + - name: PrepareFCI + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: '{{GetAllClusterNodes.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/prepare-fci.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./prepare-fci.ps1 -AdminSecret '{{AdminSecrets}}' -DomainNetBIOSName {{DomainNetBIOSName}} -SqlUserSecret {{AdminSecrets}} -MSSQLMediaBucket '{{MSSQLMediaBucketName}}' -MSSQLMediaKey + '{{MSSQLMediaPathKey}}' + - name: CompleteFCI + action: aws:runCommand + onFailure: step:signalfailure + inputs: + DocumentName: AWS-RunRemoteScript + InstanceIds: '{{GetClusterNode1.InstanceIds}}' + CloudWatchOutputConfig: + CloudWatchOutputEnabled: 'true' + CloudWatchLogGroupName: + Ref: QuickStartLogs + Parameters: + sourceType: S3 + sourceInfo: + Fn::Sub: + - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/complete-fci.ps1"}' + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + S3Region: + Fn::If: + - UsingDefaultBucket + - Ref: AWS::Region + - Ref: QSS3BucketRegion + commandLine: ./complete-fci.ps1 -AdminSecret '{{AdminSecrets}}' -DomainNetBIOSName {{DomainNetBIOSName}} -FileServerPath '{{FindFSxVolDnsAddress.FSxDnsAddress}}'.split(';')[0] -Node1FciIp + '{{FindSecondaryIpAddresses.SecondaryPrivateIpAddresses}}'.split(';')[1] -Node1SubnetMask '{{FindSecondaryIpAddresses.SecondaryPrivateIpAddresses}}'.split(';')[4] -Node2FciIp '{{FindSecondaryIpAddresses.SecondaryPrivateIpAddresses}}'.split(';')[3] + -Node2SubnetMask '{{FindSecondaryIpAddresses.SecondaryPrivateIpAddresses}}'.split(';')[5] -FCIName {{SqlFSxFCIName}} -SQLAdminAccounts '{{SQLAdminAccounts}}' + - name: CFNSignalEnd + action: aws:branch + inputs: + Choices: + - NextStep: signalsuccess + Not: + Variable: '{{StackName}}' + StringEquals: '' + - NextStep: sleepend + Variable: '{{StackName}}' + StringEquals: '' + - name: signalsuccess + action: aws:executeAwsApi + isEnd: true + inputs: + Service: cloudformation + Api: SignalResource + LogicalResourceId: SSMWaitCondition + StackName: '{{StackName}}' + Status: SUCCESS + UniqueId: '{{SqlFSxInstanceId}}' + - name: sleepend + action: aws:sleep + isEnd: true + inputs: + Duration: PT1S + - name: signalfailure + action: aws:executeAwsApi + inputs: + Service: cloudformation + Api: SignalResource + LogicalResourceId: SSMWaitCondition + StackName: '{{StackName}}' + Status: FAILURE + UniqueId: '{{SqlFSxInstanceId}}' + SqlFSxSSMPassRolePolicy: + Type: AWS::IAM::Policy + Properties: + PolicyName: SqlFsx-SSM-PassRole + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - iam:PassRole + Resource: + Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${AWSQuickstartSqlFSxRole} + Roles: + - Ref: AWSQuickstartSqlFSxRole + SqlFSxInstance1: + Type: AWS::EC2::Instance + DependsOn: FSxWindowsFileSystem + Properties: + ImageId: + Fn::GetAtt: + - WindowsBaseAMIInfo + - Id + IamInstanceProfile: + Ref: SqlFSxProfile + InstanceType: + Ref: WorkloadInstanceType + NetworkInterfaces: + - DeleteOnTermination: true + DeviceIndex: '0' + SubnetId: + Ref: PrivateSubnet1ID + SecondaryPrivateIpAddressCount: 2 + GroupSet: + - Ref: DomainMemberSGID + - Ref: WorkloadSecurityGroup + Tags: + - Key: Name + Value: + Ref: SqlFSxServerNetBIOSName1 + - Key: FCIName + Value: + Ref: SqlFSxFCIName + - Key: FCIRole + Value: Primary + - Key: ResourceGroup + Value: + Ref: ResourceGroupName + BlockDeviceMappings: + - DeviceName: /dev/sda1 + Ebs: + VolumeSize: 100 + VolumeType: gp2 + KeyName: + Ref: KeyPairName + UserData: + Fn::Base64: + Fn::Join: + - '' + - - "\n" + - "New-Item -Path C:\\ -Name log -ItemType Directory \n" + - "$transcriptPath = 'C:\\log\\user-data-transcript.txt' \n" + - "Start-Transcript $transcriptPath \n" + - Fn::Sub: Set-Content -Value "${SqlFSxServerNetBIOSName1}" -Path C:\log\hostname.txt + - "\n" + - Fn::Sub: Rename-Computer -NewName "${SqlFSxServerNetBIOSName1}" + - "\n" + - "$instanceId = (New-Object System.Net.WebClient).DownloadString('http://169.254.169.254/latest/meta-data/instance-id')\n" + - 'Start-SSMAutomationExecution -DocumentName ' + - Fn::Sub: '"${SqlFSxAutomation}"' + - ' -Parameter @{' + - '"SqlFSxInstanceId"=' + - $instanceId + - ;"URLSuffix"= + - Fn::Sub: '"${AWS::URLSuffix}"' + - ;"QSS3BucketName"= + - Fn::Sub: '"${QSS3BucketName}"' + - ;"QSS3KeyPrefix"= + - Fn::Sub: '"${QSS3KeyPrefix}"' + - ;"AutomationAssumeRole"= + - Fn::Sub: '"arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${AWSQuickstartSqlFSxRole}"' + - '}' + - "\n" + - "\n" + SqlFSxInstance2: + Type: AWS::EC2::Instance + Properties: + ImageId: + Fn::GetAtt: + - WindowsBaseAMIInfo + - Id + IamInstanceProfile: + Ref: SqlFSxProfile + InstanceType: + Ref: WorkloadInstanceType + NetworkInterfaces: + - DeleteOnTermination: true + DeviceIndex: '0' + SubnetId: + Ref: PrivateSubnet2ID + SecondaryPrivateIpAddressCount: 2 + GroupSet: + - Ref: DomainMemberSGID + - Ref: WorkloadSecurityGroup + Tags: + - Key: Name + Value: + Ref: SqlFSxServerNetBIOSName2 + - Key: FCIName + Value: + Ref: SqlFSxFCIName + - Key: FCIRole + Value: Secondary + - Key: ResourceGroup + Value: + Ref: ResourceGroupName + BlockDeviceMappings: + - DeviceName: /dev/sda1 + Ebs: + VolumeSize: 100 + VolumeType: gp2 + KeyName: + Ref: KeyPairName + UserData: + Fn::Base64: + Fn::Join: + - '' + - - "\n" + - "New-Item -Path C:\\ -Name log -ItemType Directory \n" + - Fn::Sub: Set-Content -Value "${SqlFSxServerNetBIOSName2}" -Path C:\log\hostname.txt + - "\n" + - Fn::Sub: Rename-Computer -NewName "${SqlFSxServerNetBIOSName2}" + - "\n" + - "\n" + SqlFSxRole: + Type: AWS::IAM::Role + Metadata: + cfn-lint: + config: + ignore_checks: + - EIAMPolicyWildcardResource + - EIAMPolicyActionWildcard + Properties: + Policies: + - PolicyDocument: + Version: '2012-10-17' + Statement: + - Action: + - s3:GetObject + Resource: + - Fn::Sub: + - arn:${AWS::Partition}:s3:::${S3Bucket}/${QSS3KeyPrefix}* + - S3Bucket: + Fn::If: + - UsingDefaultBucket + - Fn::Sub: ${QSS3BucketName}-${AWS::Region} + - Ref: QSS3BucketName + Effect: Allow + - Action: + - s3:* + Resource: '*' + Effect: Allow + PolicyName: aws-quick-start-s3-policy + - PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - secretsmanager:GetSecretValue + - secretsmanager:DescribeSecret + Resource: + - Ref: ADAdminSecrets + - Effect: Allow + Action: + - ssm:StartAutomationExecution + Resource: '*' + PolicyName: QS-SqlFSx-SSM + - PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - iam:PassRole + Resource: + Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${AWSQuickstartSqlFSxRole} + PolicyName: QS-SqlFSx-SSM-PassRole + ManagedPolicyArns: + - Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore + Path: / + AssumeRolePolicyDocument: + Statement: + - Action: + - sts:AssumeRole + Principal: + Service: + - ec2.amazonaws.com + Effect: Allow + Version: '2012-10-17' + SqlFSxProfile: + Type: AWS::IAM::InstanceProfile + Properties: + Roles: + - Ref: SqlFSxRole + Path: / + SSMWaitHandle: + Type: AWS::CloudFormation::WaitConditionHandle + SSMWaitCondition: + Type: AWS::CloudFormation::WaitCondition + CreationPolicy: + ResourceSignal: + Timeout: PT120M + Count: 1 + DependsOn: SqlFSxInstance1 + Properties: + Handle: + Ref: SSMWaitHandle + Timeout: '7200' + Count: 1 + WorkloadSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Allow access to the Workload instances + VpcId: + Ref: VPCID + SecurityGroupIngress: + - IpProtocol: '-1' + FromPort: -1 + ToPort: -1 + CidrIp: + Ref: VPCcidr + FSxWindowsFileSystem: + Type: AWS::FSx::FileSystem + Properties: + FileSystemType: WINDOWS + StorageCapacity: + Ref: FileShareVolumeSize + StorageType: SSD + SubnetIds: + - Ref: PrivateSubnet1ID + - Ref: PrivateSubnet2ID + SecurityGroupIds: + - Ref: WorkloadSecurityGroup + - Ref: DomainMemberSGID + Tags: + - Key: Name + Value: SqlFSxFileSystem + WindowsConfiguration: + ThroughputCapacity: + Ref: FileShareThroughputCapacity + WeeklyMaintenanceStartTime: '4:16:30' + DailyAutomaticBackupStartTime: 01:00 + AutomaticBackupRetentionDays: 30 + CopyTagsToBackups: false + DeploymentType: MULTI_AZ_1 + PreferredSubnetId: + Ref: PrivateSubnet1ID + SelfManagedActiveDirectoryConfiguration: + DnsIps: + - Fn::Select: + - 0 + - Fn::Split: + - ',' + - Ref: ADDnsIpAddresses + DomainName: + Ref: DomainDNSName + FileSystemAdministratorsGroup: Domain Admins + UserName: + Fn::Sub: ${DomainAdminUser} + Password: + Fn::Sub: ${DomainAdminPassword} + ResourceGroup: + Type: AWS::ResourceGroups::Group + Properties: + Name: + Ref: ResourceGroupName + Description: A resource group for SQL servers + ResourceQuery: + Type: TAG_FILTERS_1_0 + Query: + ResourceTypeFilters: + - AWS::EC2::Instance + TagFilters: + - Key: ResourceGroup + Values: + - Ref: ResourceGroupName + AppInsightsForSQLHA: + Condition: EnableAppInsightForSQLHA + DependsOn: SSMWaitCondition + Type: AWS::ApplicationInsights::Application + Properties: + ResourceGroupName: + Ref: ResourceGroupName + AutoConfigurationEnabled: false + CustomComponents: + - ComponentName: + Fn::Sub: SQLHAClusterInstances-${ApplicationName} + ResourceList: + - Fn::Sub: arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:instance/${SqlFSxInstance1} + - Fn::Sub: arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:instance/${SqlFSxInstance2} + ComponentMonitoringSettings: + - ComponentName: + Fn::Sub: SQLHAClusterInstances-${ApplicationName} + Tier: SQL_SERVER_ALWAYSON_AVAILABILITY_GROUP + ComponentConfigurationMode: DEFAULT_WITH_OVERWRITE + DefaultOverwriteComponentConfiguration: + SubComponentTypeConfigurations: + - SubComponentType: AWS::EC2::Instance + SubComponentConfigurationDetails: + Logs: + - LogGroupName: + Fn::Sub: SQL_SERVER-${ResourceGroupName} + LogType: SQL_SERVER + LogPath: C:\Program Files\Microsoft SQL Server\MSSQL**.MSSQLSERVER\MSSQL\Log\ERRORLOG + Tags: + - Key: SourceTemplate + Value: AWSQuickStart +Outputs: + InstanceIP: + Description: Private IP address of SqlFSx instance. + Value: + Fn::GetAtt: + - SqlFSxInstance1 + - PrivateIp + Postdeployment: + Value: https://fwd.aws/5XG6A + Description: See the deployment guide for post-deployment steps. diff --git a/templates/sample-workload.template.template b/templates/sample-workload.template.template deleted file mode 100644 index 989e996..0000000 --- a/templates/sample-workload.template.template +++ /dev/null @@ -1,97 +0,0 @@ ---- -AWSTemplateFormatVersion: '2010-09-09' -Description: Sample Stack -Parameters: - Param1: - Description: Param1 - Type: String -Resources: - LambdaExecutionRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: '2012-10-17' - Statement: - - Effect: Allow - Principal: - Service: - - lambda.amazonaws.com - Action: - - sts:AssumeRole - Path: "/" - Policies: - - PolicyName: lambda_policy - PolicyDocument: - Version: '2012-10-17' - Statement: - - Effect: Allow - Action: - - logs:CreateLogGroup - - logs:CreateLogStream - - logs:PutLogEvents - Resource: arn:aws:logs:*:*:* - - Effect: Allow - Action: - - cloudformation:DescribeStacks - Resource: "*" - GenID: - Type: AWS::Lambda::Function - Properties: - Code: - ZipFile: - Fn::Join: - - "\n" - - - import random - - import json - - import cfnresponse - - from cfnresponse import send, SUCCESS - - 'def handler(event, context):' - - " if event['RequestType'] == 'Delete':" - - " send(event, context, 'SUCCESS', {})" - - " return" - - " if event['RequestType'] == 'Create':" - - ' token= "%0x.%0x" % (random.SystemRandom().getrandbits(3*8), - random.SystemRandom().getrandbits(8*8))' - - " responseData = {}" - - " responseData['Data'] = token" - - " send(event, context, 'SUCCESS', responseData)" - - " return token" - Handler: index.handler - Runtime: python3.7 - Timeout: 5 - Role: - Fn::GetAtt: - - LambdaExecutionRole - - Arn - GetID: - Type: Custom::GenerateID - Version: '1.0' - Properties: - ServiceToken: - Fn::GetAtt: - - GenID - - Arn - ResponseURL: - Fn::Join: - - '' - - - http://ResponseURL - - Ref: AWS::StackId - - RequestId - StackId: - Ref: AWS::StackId - ResourceProperties: - RequestType: Create - RequestId: - Fn::Join: - - '' - - - Ref: AWS::StackId - - RequestId - LogicalResourceId: GenIDLogicalResourceId -Outputs: - ClusterID: - Value: - Fn::GetAtt: - - GetID - - Data - Param1Output: - Value: !Ref Param1 diff --git a/templates/scripts/sample_userdata.sh b/templates/scripts/sample_userdata.sh deleted file mode 100644 index a84a81f..0000000 --- a/templates/scripts/sample_userdata.sh +++ /dev/null @@ -1 +0,0 @@ -#UserData and or scripts should be stored here, but only for source code revision purposes cf templatess should refer to prod s3bucket allways