├── .nojekyll ├── docs ├── generated │ ├── regions │ │ └── index.adoc │ ├── services │ │ ├── index.adoc │ │ └── metadata.adoc │ └── parameters │ │ └── index.adoc ├── partner_editable │ ├── licenses.adoc │ ├── pre-reqs.adoc │ ├── deployment_options.adoc │ ├── service_limits.adoc │ ├── regions.adoc │ ├── specialized_knowledge.adoc │ ├── overview_target_and_usage.adoc │ ├── additional_info.adoc │ ├── product_description.adoc │ ├── architecture.adoc │ ├── _settings.adoc │ ├── faq_troubleshooting.adoc │ └── deploy_steps.adoc └── images │ ├── image1.png │ ├── image2.png │ ├── image3.png │ ├── architecture_diagram.png │ ├── aws-quickstart-graphic.png │ └── HITRUST CSF Quick Start architecture diagram.pptx ├── .gitignore ├── assets ├── VPC_Architecture.pptx ├── landing │ ├── images │ │ ├── aws.png │ │ ├── block.png │ │ ├── compliance.png │ │ ├── external-link.png │ │ └── quick-start-architecture.png │ └── landing.html └── HITRUST-CSF-Security-Controls-Mapping.xlsx ├── functions ├── packages │ └── ACMCert │ │ └── lambda.zip └── source │ └── ACMCert │ ├── cfnresponse.py │ └── lambda_function.py ├── scripts └── scripts_userdata.sh ├── .gitmodules ├── NOTICE.txt ├── ci ├── taskcat.yml └── params.json ├── .taskcat.yml ├── README.md ├── templates ├── database.template ├── vpc-networking.template ├── ssl-acm.template ├── clean-bucket.template ├── elb.template ├── copy-lambdas.template ├── logging.template ├── main.template ├── application.template └── config-rules.template └── LICENSE.txt /.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/generated/regions/index.adoc: -------------------------------------------------------------------------------- 1 | // placeholder 2 | -------------------------------------------------------------------------------- /docs/generated/services/index.adoc: -------------------------------------------------------------------------------- 1 | // placeholder 2 | -------------------------------------------------------------------------------- /docs/generated/parameters/index.adoc: -------------------------------------------------------------------------------- 1 | // placeholder 2 | -------------------------------------------------------------------------------- /docs/generated/services/metadata.adoc: -------------------------------------------------------------------------------- 1 | // placeholder 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | local-params.json 2 | .vscode* 3 | taskcat_output* -------------------------------------------------------------------------------- /docs/partner_editable/licenses.adoc: -------------------------------------------------------------------------------- 1 | There are no additional license requirements to use this Quick Start. -------------------------------------------------------------------------------- /docs/images/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-hitrust-csf/HEAD/docs/images/image1.png -------------------------------------------------------------------------------- /docs/images/image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-hitrust-csf/HEAD/docs/images/image2.png -------------------------------------------------------------------------------- /docs/images/image3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-hitrust-csf/HEAD/docs/images/image3.png -------------------------------------------------------------------------------- /assets/VPC_Architecture.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-hitrust-csf/HEAD/assets/VPC_Architecture.pptx -------------------------------------------------------------------------------- /assets/landing/images/aws.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-hitrust-csf/HEAD/assets/landing/images/aws.png -------------------------------------------------------------------------------- /assets/landing/images/block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-hitrust-csf/HEAD/assets/landing/images/block.png -------------------------------------------------------------------------------- /assets/landing/images/compliance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-hitrust-csf/HEAD/assets/landing/images/compliance.png -------------------------------------------------------------------------------- /docs/images/architecture_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-hitrust-csf/HEAD/docs/images/architecture_diagram.png -------------------------------------------------------------------------------- /functions/packages/ACMCert/lambda.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-hitrust-csf/HEAD/functions/packages/ACMCert/lambda.zip -------------------------------------------------------------------------------- /assets/landing/images/external-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-hitrust-csf/HEAD/assets/landing/images/external-link.png -------------------------------------------------------------------------------- /docs/images/aws-quickstart-graphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-hitrust-csf/HEAD/docs/images/aws-quickstart-graphic.png -------------------------------------------------------------------------------- /assets/HITRUST-CSF-Security-Controls-Mapping.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-hitrust-csf/HEAD/assets/HITRUST-CSF-Security-Controls-Mapping.xlsx -------------------------------------------------------------------------------- /assets/landing/images/quick-start-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-hitrust-csf/HEAD/assets/landing/images/quick-start-architecture.png -------------------------------------------------------------------------------- /docs/images/HITRUST CSF Quick Start architecture diagram.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-hitrust-csf/HEAD/docs/images/HITRUST CSF Quick Start architecture diagram.pptx -------------------------------------------------------------------------------- /scripts/scripts_userdata.sh: -------------------------------------------------------------------------------- 1 | #UserData and or scripts should be stored here, but only for source code revision purposes and CloudFormation templates should always refer to 'quickstart-reference' S3 bucket 2 | -------------------------------------------------------------------------------- /docs/partner_editable/pre-reqs.adoc: -------------------------------------------------------------------------------- 1 | // If no preparation is required, remove this content. 2 | 3 | ==== Prepare your AWS account 4 | 5 | To deploy this Quick Start, you must have your own domain name, and it must be managed by Amazon Route 53. 6 | 7 | -------------------------------------------------------------------------------- /docs/partner_editable/deployment_options.adoc: -------------------------------------------------------------------------------- 1 | // Edit this placeholder text to accurately describe your architecture. 2 | 3 | This Quick Start provides the following deployment option: 4 | 5 | * *Deploy {partner-product-short-name} into new VPCs*. This builds a new AWS environment consisting of the VPCs, subnets, NAT gateways, security groups, bastion hosts, and other infrastructure components. It then deploys an example WordPress site into a new production VPC. 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "submodules/quickstart-aws-vpc"] 2 | path = submodules/quickstart-aws-vpc 3 | url = https://github.com/aws-quickstart/quickstart-aws-vpc.git 4 | branch = main 5 | [submodule "submodules/quickstart-linux-bastion"] 6 | path = submodules/quickstart-linux-bastion 7 | url = https://github.com/aws-quickstart/quickstart-linux-bastion.git 8 | branch = main 9 | [submodule "docs/boilerplate"] 10 | path = docs/boilerplate 11 | url = https://github.com/aws-quickstart/quickstart-documentation-base-common.git 12 | branch = main 13 | -------------------------------------------------------------------------------- /docs/partner_editable/service_limits.adoc: -------------------------------------------------------------------------------- 1 | // Replace the in each row to specify the number of resources used in this deployment. Remove the rows for resources that aren’t used. 2 | |=== 3 | |Resource |This deployment uses 4 | 5 | // Space needed to maintain table headers 6 | |VPCs |2 7 | |Elastic IP addresses |5 8 | |Security groups |6 9 | |AWS Identity and Access Management (IAM) roles |8 10 | |Auto Scaling groups |2 11 | |Application Load Balancers |1 12 | |Amazon S3 buckets |2 13 | |t3.small instances |3–5 14 | |EC2 key pairs |2 15 | |=== 16 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2016-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | 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 4 | 5 | http://aws.amazon.com/apache2.0/ 6 | 7 | or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 8 | -------------------------------------------------------------------------------- /ci/taskcat.yml: -------------------------------------------------------------------------------- 1 | global: 2 | marketplace-ami: false 3 | package-lambda: true 4 | owner: quickstart-eng@amazon.com 5 | qsname: quickstart-hitrust-csf 6 | regions: 7 | - us-west-2 8 | - ap-northeast-1 9 | - ap-northeast-2 10 | - ap-south-1 11 | - ap-southeast-1 12 | - ap-southeast-2 13 | - ca-central-1 14 | # - eu-central-1 15 | - eu-west-1 16 | # - sa-east-1 17 | - us-east-1 18 | - us-east-2 19 | - us-west-1 20 | reporting: true 21 | tests: 22 | quickstart-hitrust-csf: 23 | parameter_input: params.json 24 | template_file: main.template 25 | -------------------------------------------------------------------------------- /docs/partner_editable/regions.adoc: -------------------------------------------------------------------------------- 1 | // KEEP THIS FILE BLANK normally. By default, this file's content is excluded from the generated guide. The content about supported Regions now comes from the boilerplate (non-partner-editable) file regions_default.adoc. 2 | 3 | // If your Quick Start requires custom info (e.g., the product is limited to a specific Region and that will not change), uncomment the custom_supported_regions attribute in the _settings.adoc file. Add the custom info here. It then appears in the generated guide instead of the boilerplate. 4 | 5 | // Do not list all the supported Regions or provide any other info that will go out of date when new Regions are released or when services add support for more Regions. 6 | -------------------------------------------------------------------------------- /docs/partner_editable/specialized_knowledge.adoc: -------------------------------------------------------------------------------- 1 | // Replace the content in <> 2 | // For example: “familiarity with basic concepts in networking, database operations, and data encryption” or “familiarity with .” 3 | // Include links if helpful. 4 | // You don't need to list AWS services or point to general info about AWS; the boilerplate already covers this. 5 | 6 | This deployment requires a moderate level of familiarity with AWS services. If you’re new to AWS, see https://aws.amazon.com/getting-started/[Getting Started with AWS^] and https://aws.amazon.com/training/[Training and Certification^]. 7 | 8 | For information about HITRUST on AWS, see https://aws.amazon.com/compliance/hitrust/[HITRUST CSF^]. 9 | 10 | -------------------------------------------------------------------------------- /.taskcat.yml: -------------------------------------------------------------------------------- 1 | project: 2 | name: quickstart-hitrust-csf 3 | owner: quickstart-eng@amazon.com 4 | regions: 5 | - us-west-2 6 | - ap-northeast-1 7 | - ap-northeast-2 8 | - ap-south-1 9 | - ap-southeast-1 10 | - ap-southeast-2 11 | - ca-central-1 12 | # - eu-central-1 13 | - eu-west-1 14 | # - sa-east-1 15 | - us-east-1 16 | - us-east-2 17 | - us-west-1 18 | s3_bucket: '' 19 | tests: 20 | quickstart-hitrust-csf: 21 | parameters: 22 | AWSHostedZoneID: Z3AUDQLV95DIKK 23 | AWSPublicFQDN: $[taskcat_random-string]-hitrust.t.quickstart.awspartner.com 24 | AvailabilityZones: $[taskcat_genaz_2] 25 | BastionAccessCidr: 0.0.0.0/0 26 | BastionKeyPairName: $[taskcat_getkeypair] 27 | KeyPairName: $[taskcat_getkeypair] 28 | SSLCertificateARN: '' 29 | DBPassword: $[taskcat_genpass_8] 30 | QSS3BucketName: $[taskcat_autobucket] 31 | QSS3BucketRegion: $[taskcat_current_region] 32 | SourceCidr: 0.0.0.0/0 33 | s3_bucket: '' 34 | template: templates/main.template -------------------------------------------------------------------------------- /ci/params.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ParameterKey": "AWSHostedZoneID", 4 | "ParameterValue": "Z3AUDQLV95DIKK" 5 | }, 6 | { 7 | "ParameterKey": "AWSPublicFQDN", 8 | "ParameterValue": "$[taskcat_random-string]-hitrust.t.quickstart.awspartner.com" 9 | }, 10 | { 11 | "ParameterKey": "AvailabilityZones", 12 | "ParameterValue": "$[taskcat_genaz_2]" 13 | }, 14 | { 15 | "ParameterKey": "BastionAccessCidr", 16 | "ParameterValue": "0.0.0.0/0" 17 | }, 18 | { 19 | "ParameterKey": "BastionKeyPairName", 20 | "ParameterValue": "cikey" 21 | }, 22 | { 23 | "ParameterKey": "KeyPairName", 24 | "ParameterValue": "cikey" 25 | }, 26 | { 27 | "ParameterKey": "SSLCertificateARN", 28 | "ParameterValue": "" 29 | }, 30 | { 31 | "ParameterKey": "DBPassword", 32 | "ParameterValue": "$[taskcat_genpass_8]" 33 | }, 34 | { 35 | "ParameterKey": "QSS3BucketName", 36 | "ParameterValue": "$[taskcat_autobucket]" 37 | }, 38 | { 39 | "ParameterKey": "SourceCidr", 40 | "ParameterValue": "0.0.0.0/0" 41 | } 42 | ] 43 | -------------------------------------------------------------------------------- /docs/partner_editable/overview_target_and_usage.adoc: -------------------------------------------------------------------------------- 1 | // Replace the content in <> 2 | // Identify your target audience and explain how/why they would use this Quick Start. 3 | //Avoid borrowing text from third-party websites (copying text from AWS service documentation is fine). Also, avoid marketing-speak, focusing instead on the technical aspect. 4 | 5 | === AWS compliance architectures 6 | 7 | AWS compliance solutions help streamline, automate, and implement secure baselines in AWS, from initial design to operational security readiness. They incorporate the expertise of AWS solutions architects and security and compliance personnel to help you build an automated architecture. 8 | 9 | This Quick Start includes AWS CloudFormation templates to automate building a baseline architecture that fits within your organization’s larger Health Information Trust Alliance Common Security Framework (HITRUST-CSF) program. It also includes https://go.hitrustalliance.net/SR-Custom-Matrix-AWS[HITRUST CSF security controls mapping^], which maps HITRUST controls to architecture decisions, features, and the baseline configuration. 10 | 11 | This deployment is for health IT infrastructure architects, administrators, compliance professionals, and DevOps professionals who want to implement or extend HITRUST workloads to the AWS Cloud. 12 | -------------------------------------------------------------------------------- /docs/partner_editable/additional_info.adoc: -------------------------------------------------------------------------------- 1 | // Add steps as necessary for accessing the software, post-configuration, and testing. Don’t include full usage instructions for your software, but add links to your product documentation for that information. 2 | //Should any sections not be applicable, remove them 3 | 4 | == Test the deployment 5 | 6 | . Navigate to the landing page. In the *Outputs* tab shown in the previous figure, choose *LandingPageURL*. The following webpage appears: 7 | 8 | [#test1] 9 | .Confirmation on webpage after successful launch 10 | image::../images/image1.png[test1] 11 | 12 | [start=2] 13 | . Confirm that WordPress is installed correctly. Note the application's URL, and navigate to `ApplicationURL/wordpress/`. If you want to set up the site, enter the requisite information. 14 | . Navigate to the AWS Config console, where you can see the configuration status. Note that AWS Config monitors all resources in the AWS Region you deploy in, not just what's from this deployment. For example, an Amazon Elastic Block Store (Amazon EBS) volume may not be encrypted elsewhere. 15 | 16 | [#test2] 17 | .Resource monitoring 18 | image::../images/image2.png[test2] 19 | 20 | [start=4] 21 | 4. Disable read access for your Amazon S3 buckets. 22 | 23 | [#test3] 24 | .S3 buckets with public read access disabled 25 | image::../images/image3.png[test3] 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Deprecation Notice 3 | 4 | :x: This repository is subject to deprecation in Q4 2024. For more details, [please review this announcement](https://github.com/aws-ia/.announcements/issues/1). 5 | 6 | 7 | ## quickstart-hitrust-csf 8 | 9 | ## HITRUST on AWS 10 | 11 | This Quick Start deploys a model environment on the Amazon Web Services (AWS) Cloud that can help organizations with workloads that fall within the scope of the Health Information Trust Alliance Common Security Framework (HITRUST-CSF). Its architecture maps to certain technical requirements imposed by HITRUST controls. 12 | 13 | The Quick Start includes AWS CloudFormation templates to automate building a baseline architecture that fits within your organization’s larger HITRUST program. It also includes a [security controls reference](https://fwd.aws/bzzrk), which maps HITRUST controls to architecture decisions, features, and configuration of the baseline. 14 | 15 | This Quick Start is for health IT infrastructure architects, administrators, compliance professionals, and DevOps professionals who plan to implement or extend HITRUST workloads to the AWS Cloud. It's part of a set of AWS compliance offerings, which provide security-focused architecture solutions to help Managed Service Providers (MSPs), cloud provisioning teams, developers, integrators, and information security teams follow strict security, compliance, and risk management controls. 16 | 17 | ![Quick Start architecture for HITRUST](https://d1.awsstatic.com/partner-network/QuickStart/datasheets/hitrust-on-aws-architecture.666c124df171be2d74f18e30f3f2ed21a749b645.png) 18 | 19 | For architectural details, best practices, step-by-step instructions, and customization options, see the [deployment guide](https://fwd.aws/BqEkm). 20 | 21 | To post feedback, submit feature ideas, or report bugs, use the **Issues** section of this GitHub repo. 22 | 23 | If you'd like to submit code for this Quick Start, please review the [AWS Quick Start Contributor's Guide](https://aws-quickstart.github.io/). 24 | -------------------------------------------------------------------------------- /functions/source/ACMCert/cfnresponse.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. 2 | # This file is licensed to you under the AWS Customer Agreement (the "License"). 3 | # You may not use this file except in compliance with the License. 4 | # A copy of the License is located at http://aws.amazon.com/agreement/ . 5 | # This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. 6 | # See the License for the specific language governing permissions and limitations under the License. 7 | 8 | from botocore.vendored import requests 9 | import json 10 | 11 | SUCCESS = "SUCCESS" 12 | FAILED = "FAILED" 13 | 14 | def send(event, context, responseStatus, responseData, physicalResourceId, reason=None): 15 | responseUrl = event['ResponseURL'] 16 | 17 | print (responseUrl) 18 | 19 | responseBody = {} 20 | responseBody['Status'] = responseStatus 21 | if reason: 22 | responseBody['Reason'] = reason 23 | else: 24 | responseBody['Reason'] = 'See the details in CloudWatch Log Stream: ' + context.log_stream_name 25 | responseBody['PhysicalResourceId'] = physicalResourceId or context.log_stream_name 26 | responseBody['StackId'] = event['StackId'] 27 | responseBody['RequestId'] = event['RequestId'] 28 | responseBody['LogicalResourceId'] = event['LogicalResourceId'] 29 | responseBody['Data'] = responseData 30 | 31 | json_responseBody = json.dumps(responseBody) 32 | 33 | print ("Response body:\n" + json_responseBody) 34 | 35 | headers = { 36 | 'content-type' : '', 37 | 'content-length' : str(len(json_responseBody)) 38 | } 39 | 40 | try: 41 | response = requests.put(responseUrl, 42 | data=json_responseBody, 43 | headers=headers) 44 | print ("Status code: " + response.reason) 45 | except Exception as e: 46 | print ("send(..) failed executing requests.put(..): " + str(e)) 47 | -------------------------------------------------------------------------------- /docs/partner_editable/product_description.adoc: -------------------------------------------------------------------------------- 1 | // Replace the content in <> 2 | // Briefly describe the software. Use consistent and clear branding. 3 | // Include the benefits of using the software on AWS, and provide details on usage scenarios. 4 | 5 | HITRUST CSF (Health Information Trust Alliance Common Security Framework) is a security framework that incorporates security requirements into existing frameworks that originate from global entities, such as the following: 6 | 7 | * General Data Protection Regulation (GDPR) 8 | * International Organization for Standardization (ISO) 9 | * Federal Financial Institutions Examination Council (FFIEC) 10 | * Health Insurance Portability and Accountability Act (HIPAA) 11 | * Health Information Technology for Economic and Clinical Health Act (HITECH) 12 | * Projects of Common Interest (PCI) 13 | * Control Objectives for Information and Related Technologies (COBIT) 14 | * National Institute of Standards and Technology (NIST) 15 | * Fair Trade Commission (FTC) 16 | * Centers for Medicare and Medicaid Services (CMS) 17 | 18 | HITRUST developed the CSF Assurance Program, which comprises the requirements, methods, and tools that let organizations incrementally manage their compliance requirements. It also allows business partnerships and vendors to assess and report against multiple sets of requirements. 19 | 20 | Security and Compliance is a shared responsibility between AWS and its customers. This shared responsibility model can help lessen the customer’s operational burden as AWS operates, manages, and controls the OS components and virtualization layer down to the physical security of the facilities in which the service operates. AWS customers can design and implement an AWS environment and use AWS services in a manner that satisfies HITRUST CSF requirements. Customers can also use certain controls established under the HITRUST CSF validated assessment of AWS services. 21 | 22 | AWS services have been assessed under the HITRUST CSF Assurance Program by an approved HITRUST CSF assessor to meet HITRUST CSF v9.1 certification criteria. The HITRUST CSF certification of AWS is valid for two years and can be accessed at https://console.aws.amazon.com/artifact/[https://console.aws.amazon.com/artifact/^]. For more information, see https://aws.amazon.com/compliance/resources/[Compliance Resources^]. 23 | -------------------------------------------------------------------------------- /docs/partner_editable/architecture.adoc: -------------------------------------------------------------------------------- 1 | :xrefstyle: short 2 | 3 | Deploying this Quick Start with default parameters builds the following {partner-product-short-name} environment in the AWS Cloud. 4 | 5 | // Replace this example diagram with your own. Follow our wiki guidelines: https://w.amazon.com/bin/view/AWS_Quick_Starts/Process_for_PSAs/#HPrepareyourarchitecturediagram. Upload your source PowerPoint file to the GitHub {deployment name}/docs/images/ directory in this repo. 6 | 7 | [#architecture1] 8 | .Quick Start architecture for {partner-product-short-name} on AWS 9 | image::../images/architecture_diagram.png[Architecture] 10 | 11 | As shown in <>, the Quick Start sets up the following: 12 | 13 | * A highly available architecture that spans two Availability Zones. 14 | * A management VPC and production VPC configured with public and private subnets, according to AWS best practices, to provide you with your own virtual network on AWS. The management and production VPCs have VPC peering enabled. 15 | * In the public subnets: 16 | ** Managed network address translation (NAT) gateways to allow outbound internet access for resources in the private subnets. 17 | ** In the management VPC, 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 the private subnets. 18 | * Security groups for Amazon EC2 instances and load balancers, used in the sample application stack. The security groups limit access to only necessary services and disallow unencrypted traffic (for example, HTTP port 80). 19 | * An Amazon Simple Storage Service (Amazon S3) bucket for encrypted log content. 20 | * In the private subnets in the production VPC: 21 | ** An encrypted Multi-AZ Amazon Relational Database Service (Amazon RDS) MySQL database and a standby instance in a second private subnet. 22 | ** A three-tier Linux web application in an Auto Scaling group and an Application Load Balancer, which can be modified or bootstrapped with customer applications, such as WordPress. 23 | * A Secure Sockets Layer (SSL) certificate, managed by AWS Certificate Manager (ACM), on the load balancer to encrypt all traffic between the internet and load balancer. Separate self-signed certificates are generated on the Amazon EC2 instances to encrypt traffic between the load balancer and application instances. 24 | * AWS Config rules to monitor the deployment configuration. If you haven’t created a configuration recorder and delivery channel, this deployment creates them. 25 | * An Amazon Route 53 record set that maps the fully qualified domain name (FQDN) to the load balancer Domain Name System (DNS). 26 | * Logging, monitoring, and alerts using AWS CloudTrail, Amazon CloudWatch, and AWS Config rules. 27 | -------------------------------------------------------------------------------- /docs/partner_editable/_settings.adoc: -------------------------------------------------------------------------------- 1 | // Change the following attributes. 2 | :quickstart-project-name: quickstart-hitrust-csf 3 | :partner-product-name: HITRUST 4 | // For the following attribute, if you have no short name, enter the same name as partner-product-name. 5 | :partner-product-short-name: HITRUST 6 | // If there's no partner, comment partner-company-name. 7 | // :partner-company-name: Example Company Name, Ltd. 8 | :doc-month: January 9 | :doc-year: 2022 10 | // Uncomment the following "contributor" attributes as appropriate. If the partner agrees to include names, enter contributor names for every line we use. If partner doesn't want to include names, delete all placeholder names and keep only "{partner-company-name}" and "AWS Integration & Automation team." 11 | //:partner-contributors: Shuai Ye, Michael McConnell, and John Smith, {partner-company-name} 12 | //:other-contributors: Akua Mansa, Trek10 13 | //:aws-contributors: Janine Singh, AWS IoT Partner team 14 | :quickstart-contributors: AWS Healthcare and AWS Infrastructure & Automation teams 15 | // For deployment_time, use minutes if deployment takes an hour or less, 16 | // for example, 30 minutes or 60 minutes. 17 | // Use hours for deployment times greater than 60 minutes (rounded to a quarter hour), 18 | // for example, 1.25 hours, 2 hours, 2.5 hours. 19 | :deployment_time: 30 minutes 20 | :default_deployment_region: us-east-1 21 | :no_parameters: 22 | // Uncomment the following attribute only if your Quick Start requires custom info (e.g., the product is limited to a specific Region and that will not change). When this attribute is uncommented, any content in the partner-editable/regions.adoc file appears in the "Supported AWS Regions" section of the generated guide instead of the boilerplate info in regions_default.adoc. 23 | // :custom_supported_regions: 24 | // Uncomment the following two attributes if you are using an AWS Marketplace listing. 25 | // Additional content will be generated automatically based on these attributes. 26 | // :marketplace_subscription: 27 | // :marketplace_listing_url: https://example.com/ 28 | // Uncomment the following attribute to add a statement about AWS and our stance on compliance-related Quick Starts. 29 | // :compliance-statement: Deploying this Quick Start does not guarantee an organization’s compliance with any laws, certifications, policies, or other regulations. 30 | // Uncomment the following attribute if you are deploying a CDK Quick Start. Comment out :parameters_as_appendix: also. 31 | // :cdk_qs: 32 | // Uncomment the following attribute if you are deploying a Terraform Quick Start. Comment out :parameters_as_appendix: also. 33 | // :terraform_qs: 34 | // Uncomment the following two attributes if you are deploying a Terraform Quick Start. Comment out :parameters_as_appendix: also. 35 | // :no_parameters: 36 | // :git_repo_url: https://example.com/ 37 | // Uncomment the following attribute if you are deploying AWS Control Tower. 38 | // :control_tower 39 | -------------------------------------------------------------------------------- /docs/partner_editable/faq_troubleshooting.adoc: -------------------------------------------------------------------------------- 1 | // Add any tips or answers to anticipated questions. 2 | 3 | == FAQ 4 | 5 | *Q.* I encountered a *CREATE_FAILED* error when I launched the Quick Start. 6 | 7 | *A.* If AWS CloudFormation fails to create the stack, relaunch the template with *Rollback on failure* set to *Disabled*. This setting is under *Advanced* in the AWS CloudFormation console on the *Configure stack options* page. With this setting, the stack’s state is retained, and the instance continues running so that you can troubleshoot the issue. (For Windows, review the log files in `%ProgramFiles%\Amazon\EC2ConfigService` and `C:\cfn\log`.) 8 | // Customize this answer if needed. For example, if you’re deploying on Linux instances, either provide the location for log files on Linux or omit the final sentence. If the Quick Start has no EC2 instances, revise accordingly (something like "and the assets keep running"). 9 | 10 | WARNING: When you set *Rollback on failure* to *Disabled*, you continue to incur AWS charges for the stack. Delete the stack when you finish troubleshooting. 11 | 12 | For more information, see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/troubleshooting.html[Troubleshooting AWS CloudFormation^]. 13 | 14 | //Use these three apostrophes above each new question to create a dividing line. This helps people skim for the questions relevant to them, especially as the number and length of Qs & As increases. 15 | ''' 16 | *Q.* I encountered a size-limitation error when I deployed the AWS CloudFormation templates. 17 | 18 | *A.* Launch the Quick Start templates from the links in this guide or from another S3 bucket. If you deploy the templates from a local copy on your computer or from a location other than an S3 bucket, you might encounter template-size limitations. For more information, see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html[AWS CloudFormation quotas^]. 19 | 20 | *Q.* The DNS validation for the SSL certificate times out. 21 | 22 | *A.* You might encounter this issue if your DNS provider is not Amazon Route 53. In this case, you must add DNS records to your DNS registrar for the routing to work. For more information, see the following pages: 23 | 24 | * https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-validate-dns.html[Use DNS to Validate Domain Ownership^] 25 | * https://docs.aws.amazon.com/acm/latest/userguide/how-domain-validation-works.html[How Domain Validation Works^] 26 | 27 | *Q.* The DNS validation appears to have completed successfully, but the Quick Start errors out during the ACM certificate DNS step. 28 | 29 | *A.* If you run into this issue: 30 | 31 | * If you already have a wildcard ACM certificate in the AWS Region where you deployed the Quick Start, you can skip the ACM certificate DNS step by supplying the ARN of your preexisting certificate in the `SSLCertificateARN` parameter. You can also import certificates in the ACM console and use the ARN of your uploaded certificate. 32 | * Alternatively, ensure that DNS validation is working by provisioning certificates outside the Quick Start by following the instructions in the links provided for the previous bullet point. Use the certificate you created in the `SSLCertificateARN` parameter to skip the ACM certificate DNS step. 33 | -------------------------------------------------------------------------------- /docs/partner_editable/deploy_steps.adoc: -------------------------------------------------------------------------------- 1 | === Confirm your AWS account configuration 2 | 3 | . Sign in to your AWS account at https://aws.amazon.com with an IAM user role that has the necessary permissions. For more information, see link:#_planning_the_deployment[Planning the deployment], earlier in this guide. 4 | . Ensure that your AWS account is configured correctly, as discussed in the link:#_technical_requirements[Technical requirements] section. 5 | 6 | // Optional based on Marketplace listing. Not to be edited 7 | ifdef::marketplace_subscription[] 8 | === Subscribe to the {partner-product-short-name} AMI 9 | 10 | This Quick Start requires a subscription to the AMI for {partner-product-short-name} in AWS Marketplace. 11 | 12 | . Sign in to your AWS account. 13 | . Open the page for the {marketplace_listing_url}[{partner-product-short-name} AMI in AWS Marketplace^], and then choose *Continue to Subscribe*. 14 | . Review the terms and conditions for software usage, and then choose *Accept Terms*. A confirmation page loads, and an email confirmation is sent to the account owner. 15 | . When the subscription process completes, exit AWS Marketplace without further action. Do not provision the software from AWS Marketplace—the Quick Start deploys the AMI for you. 16 | endif::marketplace_subscription[] 17 | // \Not to be edited 18 | 19 | === Launch the Quick Start 20 | // Adapt the following warning to your Quick Start. 21 | WARNING: If you deploy {partner-product-short-name} into an existing VPC, ensure that your VPC has two private subnets in different Availability Zones for the workload instances and that the subnets are not shared. This Quick Start does not support https://docs.aws.amazon.com/vpc/latest/userguide/vpc-sharing.html[shared subnets^]. The subnets require https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html[NAT gateways^] in their route tables to allow instances to download packages and software without exposing the instances to the internet. Also ensure that the domain name in the DHCP options is configured, as explained in http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_DHCP_Options.html[DHCP options sets^]. Provide your VPC settings when you launch the Quick Start. 22 | 23 | Each deployment takes about {deployment_time} to complete. 24 | 25 | . Sign in to your AWS account, and choose one of the following options to launch the AWS CloudFormation template. For help with choosing an option, see link:#_deployment_options[Deployment options], earlier in this guide. 26 | + 27 | [cols="3,1"] 28 | |=== 29 | ^|https://fwd.aws/n4MQ9?[Deploy {partner-product-short-name} into new VPCs on AWS^] 30 | ^|https://github.com/aws-quickstart/quickstart-hitrust-csf[View templates^] 31 | |=== 32 | + 33 | . Check the AWS Region that's displayed in the upper-right corner of the navigation bar, and change it if necessary. This Region is where you build the infrastructure. The template launches in the {default_deployment_region} Region by default. For more information, see link:#_supported_aws_regions[Supported AWS Regions], earlier in this guide. 34 | . On the *Create stack* page, keep the default setting for the template URL, then choose *Next*. 35 | . On the *Specify stack details* page, change the stack name if needed. Review the parameters for the template. Provide values for any 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*. 36 | -------------------------------------------------------------------------------- /templates/database.template: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Description: Creates Database for WordPress instance (qs-1sflpub48) 3 | Metadata: 4 | cfn-lint: 5 | config: 6 | ignore_checks: 7 | - E9101 8 | - W9002 9 | - W9003 10 | - W9006 11 | Parameters: 12 | DBInstanceClass: 13 | Type: String 14 | Description: Database instance class 15 | DBMasterUser: 16 | Type: String 17 | Description: Master User for Database 18 | DBName: 19 | Type: String 20 | Description: Database Name 21 | Default: wordpressdb 22 | DBPassword: 23 | Type: String 24 | NoEcho: true 25 | Description: Database password 26 | DBPrivateSubnets: 27 | Type: List 28 | Description: Private subnets for database 29 | QSTagKey: 30 | Type: String 31 | Description: Tag key to identify resources from this Quick Start 32 | QSTagValue: 33 | Type: String 34 | Description: Tag value to identify resources from this Quick Start 35 | SecurityGroupAppInstance: 36 | Description: App Instance Security Group 37 | Type: AWS::EC2::SecurityGroup::Id 38 | VpcId: 39 | Type: AWS::EC2::VPC::Id 40 | Description: Amazon VPC ID 41 | 42 | Mappings: 43 | RDS: 44 | SecurityGroup: 45 | Name: sg-database-access 46 | Defaults: 47 | Engine: MySQL 48 | BackupRetention: 35 49 | Storage: 50 | Type: gp2 51 | AllocatedStorage: 50 52 | 53 | Resources: 54 | # Configure MySQL Database 55 | DBSecurityGroup: 56 | Type: AWS::EC2::SecurityGroup 57 | Properties: 58 | GroupDescription: Port 3306 database for access 59 | VpcId: !Ref VpcId 60 | SecurityGroupIngress: 61 | - IpProtocol: tcp 62 | FromPort: 3306 63 | ToPort: 3306 64 | SourceSecurityGroupId: !Ref SecurityGroupAppInstance 65 | Tags: 66 | - Key: Name 67 | Value: !FindInMap [ RDS, SecurityGroup, Name ] 68 | - Key: !Ref QSTagKey 69 | Value: !Ref QSTagValue 70 | DBSubnetGroup: 71 | Type: AWS::RDS::DBSubnetGroup 72 | Properties: 73 | DBSubnetGroupDescription: MySQL RDS Subnet Group 74 | SubnetIds: !Ref DBPrivateSubnets 75 | Tags: 76 | - Key: !Ref QSTagKey 77 | Value: !Ref QSTagValue 78 | DBMySQL: 79 | Type: AWS::RDS::DBInstance 80 | Properties: 81 | AllocatedStorage: !FindInMap [ RDS, Storage, AllocatedStorage] 82 | BackupRetentionPeriod: !FindInMap [ RDS, Defaults, BackupRetention ] 83 | CopyTagsToSnapshot: true 84 | DBInstanceClass: !Ref DBInstanceClass 85 | DBName: !Ref DBName 86 | DBSubnetGroupName: !Ref DBSubnetGroup 87 | Engine: !FindInMap [ RDS, Defaults, Engine] 88 | MultiAZ: true 89 | PubliclyAccessible: false 90 | StorageEncrypted: true 91 | StorageType: !FindInMap [ RDS, Storage, Type ] 92 | MasterUsername: !Ref DBMasterUser 93 | MasterUserPassword: !Ref DBPassword 94 | Tags: 95 | - Key: !Ref QSTagKey 96 | Value: !Ref QSTagValue 97 | VPCSecurityGroups: 98 | - !Ref DBSecurityGroup 99 | 100 | Outputs: 101 | DBName: 102 | Description: Database instance name 103 | Value: !Ref DBName 104 | EndpointAddress: 105 | Description: Database endpoint address 106 | Value: !GetAtt DBMySQL.Endpoint.Address 107 | Port: 108 | Description: Database port 109 | Value: !GetAtt DBMySQL.Endpoint.Port 110 | 111 | -------------------------------------------------------------------------------- /templates/vpc-networking.template: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Description: Set up VPC peering between VPCs (qs-1sflpub5h) 3 | Metadata: 4 | cfn-lint: 5 | config: 6 | ignore_checks: 7 | - W9002 8 | - W9003 9 | - W9006 10 | Parameters: 11 | ProductionVpcId: 12 | Type: AWS::EC2::VPC::Id 13 | Description: VPC ID for web app 14 | ProductionVpcCidr: 15 | Type: String 16 | Description: VPC CIDR for web app VPC 17 | 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]))$" 18 | ConstraintDescription: Must be a valid IP CIDR range of the form x.x.x.x/x. 19 | ProductionRouteTablePrivateA: 20 | Type: String 21 | Description: Route Table Private Subnet A - web app VPC 22 | ProductionRouteTablePrivateB: 23 | Type: String 24 | Description: Route Table Private Subnet B - web app VPC 25 | ProductionRouteTablePublic: 26 | Type: String 27 | Description: Route Table Public Subnet - web app VPC 28 | ManagementVpcId: 29 | Type: AWS::EC2::VPC::Id 30 | Description: Management VPC ID 31 | ManagementVpcCidr: 32 | Type: String 33 | Description: Management VPC CIDR 34 | 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]))$" 35 | ConstraintDescription: Must be a valid IP CIDR range of the form x.x.x.x/x. 36 | ManagementRouteTablePrivateA: 37 | Type: String 38 | Description: Route Table Private Subnet A - management VPC 39 | ManagementRouteTablePrivateB: 40 | Type: String 41 | Description: Route Table Private Subnet B - management VPC 42 | ManagementRouteTablePublic: 43 | Type: String 44 | Description: Route Table Public Subnet - management VPC 45 | QSTagKey: 46 | Type: String 47 | Description: Tag key to identify resources from this Quick Start. 48 | QSTagValue: 49 | Type: String 50 | Description: Tag value to identify resources from this Quick Start. 51 | 52 | Mappings: 53 | VpcPeering: 54 | Tags: 55 | Name: vpc-peering-hitrust-csf-quickstart 56 | 57 | Resources: 58 | VpcPeeringConnection: 59 | Type: AWS::EC2::VPCPeeringConnection 60 | Properties: 61 | PeerVpcId: !Ref ProductionVpcId 62 | VpcId: !Ref ManagementVpcId 63 | Tags: 64 | - Key: Name 65 | Value: !FindInMap [ VpcPeering, Tags, Name ] 66 | - Key: !Ref QSTagKey 67 | Value: !Ref QSTagValue 68 | RouteManagementProductionPrivateA: 69 | Type: AWS::EC2::Route 70 | Properties: 71 | RouteTableId: !Ref ManagementRouteTablePrivateA 72 | VpcPeeringConnectionId: !Ref VpcPeeringConnection 73 | DestinationCidrBlock: !Ref ProductionVpcCidr 74 | RouteManagementProductionPrivateB: 75 | Type: AWS::EC2::Route 76 | Properties: 77 | RouteTableId: !Ref ManagementRouteTablePrivateB 78 | VpcPeeringConnectionId: !Ref VpcPeeringConnection 79 | DestinationCidrBlock: !Ref ProductionVpcCidr 80 | RouteManagementProductionPublic: 81 | Type: AWS::EC2::Route 82 | Properties: 83 | RouteTableId: !Ref ManagementRouteTablePublic 84 | VpcPeeringConnectionId: !Ref VpcPeeringConnection 85 | DestinationCidrBlock: !Ref ProductionVpcCidr 86 | RouteProductionManagementPrivateA: 87 | Type: AWS::EC2::Route 88 | Properties: 89 | RouteTableId: !Ref ProductionRouteTablePrivateA 90 | VpcPeeringConnectionId: !Ref VpcPeeringConnection 91 | DestinationCidrBlock: !Ref ManagementVpcCidr 92 | RouteProductionManagementPrivateB: 93 | Type: AWS::EC2::Route 94 | Properties: 95 | RouteTableId: !Ref ProductionRouteTablePrivateB 96 | VpcPeeringConnectionId: !Ref VpcPeeringConnection 97 | DestinationCidrBlock: !Ref ManagementVpcCidr 98 | RouteProductionManagementPublic: 99 | Type: AWS::EC2::Route 100 | Properties: 101 | RouteTableId: !Ref ProductionRouteTablePublic 102 | VpcPeeringConnectionId: !Ref VpcPeeringConnection 103 | DestinationCidrBlock: !Ref ManagementVpcCidr 104 | 105 | Outputs: 106 | VpcPeeringConnectionId: 107 | Value: !Ref VpcPeeringConnection 108 | Description: VPC Peering Connection between management and production VPCs 109 | -------------------------------------------------------------------------------- /templates/ssl-acm.template: -------------------------------------------------------------------------------- 1 | 2 | AWSTemplateFormatVersion: '2010-09-09' 3 | Description: Creates R53 records and ACM Certificate. ACM certificate is for all subdomains for the specified domain name. (qs-1sflpub56) 4 | Metadata: 5 | cfn-lint: 6 | config: 7 | ignore_checks: 8 | - W9002 9 | - W9003 10 | - W9006 11 | Parameters: 12 | DomainName: 13 | Type: String 14 | Description: Domain Name configured for the cluster. 15 | HostedZoneID: 16 | Type: AWS::Route53::HostedZone::Id 17 | Description: Route 53 Hosted Zone ID to use. If left blank, Route 53 18 | will not be configured and DNS must be setup manually. If you specify this, 19 | you must also specify DomainName 20 | # MaxLength: 32 21 | LogBucket: 22 | Type: String 23 | Description: Bucket to write logs to 24 | QSS3BucketName: 25 | AllowedPattern: "^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$" 26 | ConstraintDescription: Quick Start bucket name can include numbers, lowercase 27 | letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen 28 | (-). 29 | Default: aws-quickstart 30 | Description: S3 bucket name for the Quick Start assets. This string can include 31 | numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start 32 | or end with a hyphen (-). 33 | Type: String 34 | QSS3BucketRegion: 35 | Default: 'us-east-1' 36 | Description: 'The AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted. When using your own bucket, you must specify this value.' 37 | Type: String 38 | QSS3KeyPrefix: 39 | AllowedPattern: "^[0-9a-zA-Z-/]*$" 40 | ConstraintDescription: Quick Start key prefix can include numbers, lowercase letters, 41 | uppercase letters, hyphens (-), and forward slash (/). 42 | Default: '' 43 | Description: S3 key prefix for the Quick Start assets. Quick Start key prefix 44 | can include numbers, lowercase letters, uppercase letters, hyphens (-), and 45 | forward slash (/). 46 | Type: String 47 | QSTagKey: 48 | Type: String 49 | Description: Tag key to identify resources from this Quick Start 50 | QSTagValue: 51 | Type: String 52 | Description: Tag value to identify resources from this Quick Start 53 | 54 | Conditions: 55 | UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-quickstart'] 56 | 57 | Resources: 58 | CopyLambdaStack: 59 | Type: AWS::CloudFormation::Stack 60 | Properties: 61 | TemplateURL: 62 | !Sub 63 | - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}templates/copy-lambdas.template 64 | - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] 65 | S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 66 | Parameters: 67 | LogBucket: !Ref LogBucket 68 | QSS3BucketName: !Ref QSS3BucketName 69 | QSS3BucketRegion: !Ref QSS3BucketRegion 70 | QSS3KeyPrefix: !Ref QSS3KeyPrefix 71 | QSTagKey: !Ref QSTagKey 72 | QSTagValue: !Ref QSTagValue 73 | Tags: 74 | - Key: !Ref QSTagKey 75 | Value: !Ref QSTagValue 76 | ACMCertificateRole: 77 | Type: AWS::IAM::Role 78 | Properties: 79 | AssumeRolePolicyDocument: 80 | Version: '2012-10-17' 81 | Statement: 82 | - Effect: Allow 83 | Principal: 84 | Service: lambda.amazonaws.com 85 | Action: sts:AssumeRole 86 | ManagedPolicyArns: 87 | - !Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole 88 | Path: "/" 89 | Policies: 90 | - PolicyName: lambda-acm 91 | PolicyDocument: 92 | Version: '2012-10-17' 93 | Statement: 94 | - Effect: Allow 95 | Action: 96 | - acm:RequestCertificate 97 | - acm:DescribeCertificate 98 | - acm:DeleteCertificate 99 | Resource: 100 | - "*" 101 | - Effect: Allow 102 | Action: 103 | - lambda:InvokeFunction 104 | Resource: 105 | - "*" 106 | - Effect: Allow 107 | Action: 108 | - route53:ChangeResourceRecordSets 109 | Resource: 110 | - !Sub arn:${AWS::Partition}:route53:::hostedzone/${HostedZoneID} 111 | - Effect: Allow 112 | Action: 113 | - logs:FilterLogEvents 114 | Resource: 115 | - "*" 116 | ACMCertificateLambda: 117 | Type: AWS::Lambda::Function 118 | Properties: 119 | Description: Creates and verifies an ACM certificate using DNS validation and Route53 120 | Handler: lambda_function.handler 121 | Runtime: python3.7 122 | Role: !GetAtt ACMCertificateRole.Arn 123 | Timeout: 600 124 | Tags: 125 | - Key: !Ref QSTagKey 126 | Value: !Ref QSTagValue 127 | Code: 128 | S3Bucket: !GetAtt CopyLambdaStack.Outputs.LambdaZipsBucket 129 | S3Key: !Sub "${QSS3KeyPrefix}functions/packages/ACMCert/lambda.zip" 130 | ACMCertificateDNS: 131 | Type: AWS::CloudFormation::CustomResource 132 | Properties: 133 | ServiceToken: !GetAtt ACMCertificateLambda.Arn 134 | HostedZoneId: !Ref HostedZoneID 135 | HostNames: 136 | - !Ref DomainName 137 | - !Sub '*.${DomainName}' 138 | 139 | Outputs: 140 | ACMCertificate: 141 | Description: ARN of the ACM-Generated SSL Certificate 142 | Value: !GetAtt ACMCertificateDNS.Arn 143 | LambdaZipsBucket: 144 | Value: !GetAtt CopyLambdaStack.Outputs.LambdaZipsBucket 145 | Description: Bucket of Lambda Artifacts 146 | -------------------------------------------------------------------------------- /templates/clean-bucket.template: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Description: This CloudFormation Template removes non-versioned files from an S3 Bucket. (qs-1sflpub3n) 3 | Metadata: 4 | cfn-lint: 5 | config: 6 | ignore_checks: 7 | - W9002 8 | - W9003 9 | - W9006 10 | Parameters: 11 | Bucket: 12 | Description: The Bucket required for the custom resource to function. 13 | Type: String 14 | QSTagKey: 15 | Type: String 16 | Description: Tag key to identify resources from this Quick Start. 17 | QSTagValue: 18 | Type: String 19 | Description: Tag value to identify resources from this Quick Start. 20 | Resources: 21 | CleanupRole: 22 | Type: AWS::IAM::Role 23 | Properties: 24 | Path: / 25 | AssumeRolePolicyDocument: 26 | Version: 2012-10-17 27 | Statement: 28 | - Effect: Allow 29 | Principal: 30 | Service: lambda.amazonaws.com 31 | Action: sts:AssumeRole 32 | Policies: 33 | - PolicyName: DeletePolicy 34 | PolicyDocument: 35 | Version: 2012-10-17 36 | Statement: 37 | - Sid: CleanupBucket 38 | Effect: Allow 39 | Action: 40 | - s3:PutObject 41 | - s3:DeleteObject 42 | - s3:GetObject 43 | - s3:ListBucket 44 | - s3:ListBucketVersions 45 | - s3:DeleteObjectVersion 46 | - s3:GetObjectVersion 47 | - s3:GetBucketVersioning 48 | Resource: 49 | - !Sub arn:${AWS::Partition}:s3:::${Bucket} 50 | - !Sub arn:${AWS::Partition}:s3:::${Bucket}/* 51 | CleanUpS3Bucket: 52 | Properties: 53 | DestBucket: !Ref Bucket 54 | ServiceToken: !GetAtt CleanUpS3BucketFunction.Arn 55 | Type: AWS::CloudFormation::CustomResource 56 | CleanUpS3BucketFunction: 57 | Type: AWS::Lambda::Function 58 | Properties: 59 | Tags: 60 | - Key: !Ref QSTagKey 61 | Value: !Ref QSTagValue 62 | Code: 63 | ZipFile: | 64 | import json 65 | import logging 66 | import threading 67 | import boto3 68 | import cfnresponse 69 | client = boto3.client('s3') 70 | def delete_NonVersionedobjects(bucket): 71 | print("Collecting data from" + bucket) 72 | paginator = client.get_paginator('list_objects_v2') 73 | result = paginator.paginate(Bucket=bucket) 74 | objects = [] 75 | for page in result: 76 | try: 77 | for k in page['Contents']: 78 | objects.append({'Key': k['Key']}) 79 | print("deleting objects") 80 | client.delete_objects(Bucket=bucket, Delete={'Objects': objects}) 81 | objects = [] 82 | except: 83 | pass 84 | print("bucket is already empty") 85 | def delete_versionedobjects(bucket): 86 | print("Collecting data from" + bucket) 87 | paginator = client.get_paginator('list_object_versions') 88 | result = paginator.paginate(Bucket=bucket) 89 | objects = [] 90 | for page in result: 91 | try: 92 | for k in page['Versions']: 93 | objects.append({'Key':k['Key'],'VersionId': k['VersionId']}) 94 | try: 95 | for k in page['DeleteMarkers']: 96 | version = k['VersionId'] 97 | key = k['Key'] 98 | objects.append({'Key': key,'VersionId': version}) 99 | except: 100 | pass 101 | print("deleting objects") 102 | client.delete_objects(Bucket=bucket, Delete={'Objects': objects}) 103 | except: 104 | pass 105 | print("bucket already empty") 106 | def timeout(event, context): 107 | logging.error('Execution is about to time out, sending failure response to CloudFormation') 108 | cfnresponse.send(event, context, cfnresponse.FAILED, {}, None) 109 | def handler(event, context): 110 | # make sure we send a failure to CloudFormation if the function is going to timeout 111 | timer = threading.Timer((context.get_remaining_time_in_millis() / 1000.00) - 0.5, timeout, args=[event, context]) 112 | timer.start() 113 | print('Received event: %s' % json.dumps(event)) 114 | status = cfnresponse.SUCCESS 115 | try: 116 | dest_bucket = event['ResourceProperties']['DestBucket'] 117 | if event['RequestType'] == 'Delete': 118 | CheckifVersioned = client.get_bucket_versioning(Bucket=dest_bucket) 119 | print (CheckifVersioned) 120 | if 'Status' in CheckifVersioned: 121 | print (CheckifVersioned['Status']) 122 | print ("This is a versioned bucket") 123 | delete_versionedobjects(dest_bucket) 124 | else: 125 | print ("This is not a versioned bucket") 126 | delete_NonVersionedobjects(dest_bucket) 127 | else: 128 | print("Nothing to do") 129 | except Exception as e: 130 | logging.error('Exception: %s' % e, exc_info=True) 131 | status = cfnresponse.FAILED 132 | finally: 133 | timer.cancel() 134 | cfnresponse.send(event, context, status, {}, None) 135 | Description: Empty the S3 Bucket 136 | Handler: index.handler 137 | Role: !GetAtt CleanupRole.Arn 138 | Runtime: python3.7 139 | Timeout: 600 # 10 min timeout 140 | -------------------------------------------------------------------------------- /assets/landing/landing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 145 | 146 | 147 | 148 | 149 |
150 | 151 |
152 | 153 |
Congratulations!
154 |
155 | 156 |
157 |
You have successfully launched the Quick Start reference architecture for {compliance-body} workloads on AWS!
158 |
159 | 160 |
161 | 162 |
163 |
What Now?
164 |
165 |
166 |
167 |
168 |
169 | This Quick Start installs a sample WordPress application that you can configure. Of course, this is just an example to get you started. You can use this architecture as a reference when creating an application architecture that supports your regulatory requirements. 170 |
171 |
172 |
173 |
174 |
175 | View the Security Control Matrix that maps the specific AWS security controls to the compliance requirements. 176 |
177 |
178 |
179 |
180 |
181 | If you need further assistance, please contact AWS Professional Services. Additionally, many AWS Partner Network (APN) Healthcare Competency Partners can help you tailor your application to the HITRUST Common Security Framework. 182 |
183 |
184 |
185 |
186 |
187 |
188 | 189 |
190 | 191 |
192 |
Network Architecture
193 |
194 |
Architecture
195 |
196 | The Quick Start created an architecture in your account similar to this diagram. The architecture is: 197 |
    198 |
  • Is Secure
  • 199 | The system includes security controls that restrict access to resources and helps meet compliance requirements specified in the Security Control Matrix 200 |
  • Is Elastic
  • 201 | The instances reside in Auto Scaling Groups that allow them to shrink or grow based on demand 202 |
  • Is Fault-tolerant
  • 203 | The system is deployed over two Availability Zones to increase availability 204 |
205 |
206 |
207 |
208 | 209 | 210 | -------------------------------------------------------------------------------- /functions/source/ACMCert/lambda_function.py: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | import boto3 4 | import cfnresponse 5 | import time 6 | import re 7 | 8 | 9 | acm_client = boto3.client('acm') 10 | r53_client = boto3.client('route53') 11 | lambda_client = boto3.client('lambda') 12 | logs_client = boto3.client('logs') 13 | 14 | 15 | def handler(event, context): 16 | print('Received event: %s' % json.dumps(event)) 17 | status = cfnresponse.SUCCESS 18 | physical_resource_id = None 19 | data = {} 20 | reason = None 21 | try: 22 | if event['RequestType'] == 'Create': 23 | token = ''.join(ch for ch in str(event['StackId'] + event['LogicalResourceId']) if ch.isalnum()) 24 | token = token[len(token)-32:] 25 | if len(event['ResourceProperties']['HostNames']) > 1: 26 | arn = acm_client.request_certificate( 27 | ValidationMethod='DNS', 28 | DomainName=event['ResourceProperties']['HostNames'][0], 29 | SubjectAlternativeNames=event['ResourceProperties']['HostNames'][1:], 30 | IdempotencyToken=token 31 | )['CertificateArn'] 32 | else: 33 | arn = acm_client.request_certificate( 34 | ValidationMethod='DNS', 35 | DomainName=event['ResourceProperties']['HostNames'][0], 36 | IdempotencyToken=token 37 | )['CertificateArn'] 38 | physical_resource_id = arn 39 | logging.info("certificate arn: %s" % arn) 40 | rs = {} 41 | while True: 42 | try: 43 | for d in acm_client.describe_certificate(CertificateArn=arn)['Certificate']['DomainValidationOptions']: 44 | rs[d['ResourceRecord']['Name']] = d['ResourceRecord']['Value'] 45 | break 46 | except KeyError: 47 | if (context.get_remaining_time_in_millis() / 1000.00) > 20.0: 48 | print('waiting for ResourceRecord to be available') 49 | time.sleep(15) 50 | else: 51 | logging.error('timed out waiting for ResourceRecord') 52 | status = cfnresponse.FAILED 53 | time.sleep(15) 54 | rs = [{'Action': 'CREATE', 'ResourceRecordSet': {'Name': r, 'Type': 'CNAME', 'TTL': 600,'ResourceRecords': [{'Value': rs[r]}]}} for r in rs.keys()] 55 | try: 56 | r53_client.change_resource_record_sets(HostedZoneId=event['ResourceProperties']['HostedZoneId'], ChangeBatch={'Changes': rs}) 57 | except Exception as e: 58 | if not str(e).endswith('but it already exists'): 59 | raise 60 | while 'PENDING_VALIDATION' in [v['ValidationStatus'] for v in acm_client.describe_certificate(CertificateArn=arn)['Certificate']['DomainValidationOptions']]: 61 | print('waiting for validation to complete') 62 | if (context.get_remaining_time_in_millis() / 1000.00) > 20.0: 63 | time.sleep(15) 64 | else: 65 | logging.error('validation timed out') 66 | status = cfnresponse.FAILED 67 | for r in [v for v in acm_client.describe_certificate(CertificateArn=arn)['Certificate']['DomainValidationOptions']]: 68 | if r['ValidationStatus'] != 'SUCCESS': 69 | logging.debug(r) 70 | status = cfnresponse.FAILED 71 | reason = 'One or more domains failed to validate' 72 | logging.error(reason) 73 | data['Arn'] = arn 74 | elif event['RequestType'] == 'Update': 75 | reason = 'Exception: Stack updates are not supported' 76 | logging.error(reason) 77 | status = cfnresponse.FAILED 78 | physical_resource_id = event['PhysicalResourceId'] 79 | elif event['RequestType'] == 'Delete': 80 | physical_resource_id=event['PhysicalResourceId'] 81 | if not re.match(r'arn:[\w+=/,.@-]+:[\w+=/,.@-]+:[\w+=/,.@-]*:[0-9]+:[\w+=,.@-]+(/[\w+=,.@-]+)*', physical_resource_id): 82 | logging.info("PhysicalId is not an acm arn, assuming creation never happened and skipping delete") 83 | else: 84 | rs={} 85 | for d in acm_client.describe_certificate(CertificateArn=physical_resource_id)['Certificate']['DomainValidationOptions']: 86 | rs[d['ResourceRecord']['Name']] = d['ResourceRecord']['Value'] 87 | rs = [{'Action': 'DELETE', 'ResourceRecordSet': {'Name': r, 'Type': 'CNAME', 'TTL': 600,'ResourceRecords': [{'Value': rs[r]}]}} for r in rs.keys()] 88 | try: 89 | r53_client.change_resource_record_sets(HostedZoneId=event['ResourceProperties']['HostedZoneId'], ChangeBatch={'Changes': rs}) 90 | except r53_client.exceptions.InvalidChangeBatch as e: 91 | pass 92 | time.sleep(30) 93 | try: 94 | acm_client.delete_certificate(CertificateArn=physical_resource_id) 95 | except acm_client.exceptions.ResourceInUseException as e: 96 | time.sleep(60) 97 | acm_client.delete_certificate(CertificateArn=physical_resource_id) 98 | 99 | except Exception as e: 100 | logging.error('Exception: %s' % e, exc_info=True) 101 | reason = str(e) 102 | status = cfnresponse.FAILED 103 | finally: 104 | if event['RequestType'] == 'Delete': 105 | try: 106 | wait_message = 'waiting for events for request_id %s to propagate to cloudwatch...' % context.aws_request_id 107 | while not logs_client.filter_log_events( 108 | logGroupName=context.log_group_name, 109 | logStreamNames=[context.log_stream_name], 110 | filterPattern='"%s"' % wait_message 111 | )['events']: 112 | print(wait_message) 113 | time.sleep(5) 114 | except Exception as e: 115 | logging.error('Exception: %s' % e, exc_info=True) 116 | time.sleep(120) 117 | cfnresponse.send(event, context, status, data, physical_resource_id, reason) 118 | -------------------------------------------------------------------------------- /templates/elb.template: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Description: Configures an ELB and SSL Certificate (qs-1sflpub4e) 3 | Metadata: 4 | cfn-lint: 5 | config: 6 | ignore_checks: 7 | - W9002 8 | - W9003 9 | - W9006 10 | Parameters: 11 | AWSHostedZoneID: 12 | Description: DNS Zone ID to contain the cluster's DNS entry (blank = no DNS) 13 | Type: String 14 | AWSPublicFQDN: 15 | Description: Website will be reachable at this address (blank = no DNS) 16 | Type: String 17 | LogBucket: 18 | Description: Bucket to write logs to 19 | Type: String 20 | PublicSubnetIds: 21 | Type: List 22 | Description: Public subnets in your VPC 23 | QSS3BucketName: 24 | AllowedPattern: "^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$" 25 | ConstraintDescription: Quick Start bucket name can include numbers, lowercase 26 | letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen 27 | (-). 28 | Default: aws-quickstart 29 | Description: S3 bucket name for the Quick Start assets. This string can include 30 | numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start 31 | or end with a hyphen (-). 32 | Type: String 33 | QSS3BucketRegion: 34 | Default: 'us-east-1' 35 | Description: 'The AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted. When using your own bucket, you must specify this value.' 36 | Type: String 37 | QSS3KeyPrefix: 38 | AllowedPattern: "^[0-9a-zA-Z-/]*$" 39 | ConstraintDescription: Quick Start key prefix can include numbers, lowercase letters, 40 | uppercase letters, hyphens (-), and forward slash (/). 41 | Default: quickstart-hitrust-csf/ 42 | Description: S3 key prefix for the Quick Start assets. Quick Start key prefix 43 | can include numbers, lowercase letters, uppercase letters, hyphens (-), and 44 | forward slash (/). 45 | Type: String 46 | QSTagKey: 47 | Type: String 48 | Description: Tag key to identify resources from this Quick Start 49 | QSTagValue: 50 | Type: String 51 | Description: Tag value to identify resources from this Quick Start 52 | SourceCIDR: 53 | 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]))$" 54 | ConstraintDescription: Must be a valid IP CIDR range of the form x.x.x.x/x. 55 | Description: IP address/range to allow access from 56 | Type: String 57 | SSLCertificateARN: 58 | Type: String 59 | Default: "" 60 | Description: The Amazon Resource Name for the existing SSL cert you wish to use; empty for none 61 | VpcCidr: 62 | 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]))$" 63 | ConstraintDescription: Must be a valid IP CIDR range of the form x.x.x.x/x. 64 | Type: String 65 | Description: VPC CIDR 66 | VpcId: 67 | Type: AWS::EC2::VPC::Id 68 | Description: Amazon VPC ID 69 | 70 | Mappings: 71 | Defaults: 72 | Elb: 73 | HealthCheckGracePeriod: 300 74 | LogLocation: hitrust-qs-elb-logs 75 | 76 | Conditions: 77 | NoSSLCertificate: !Equals [ '', !Ref SSLCertificateARN ] 78 | UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-quickstart'] 79 | 80 | Resources: 81 | ConfigureSSLStack: 82 | Type: AWS::CloudFormation::Stack 83 | Condition: NoSSLCertificate 84 | Properties: 85 | TemplateURL: 86 | !Sub 87 | - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}templates/ssl-acm.template 88 | - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] 89 | S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 90 | Parameters: 91 | QSS3BucketName: !Ref QSS3BucketName 92 | QSS3BucketRegion: !Ref QSS3BucketRegion 93 | QSS3KeyPrefix: !Ref QSS3KeyPrefix 94 | QSTagKey: !Ref QSTagKey 95 | QSTagValue: !Ref QSTagValue 96 | DomainName: !Ref AWSPublicFQDN 97 | HostedZoneID: !Ref AWSHostedZoneID 98 | LogBucket: !Ref LogBucket 99 | Tags: 100 | - Key: QSTagKey 101 | Value: QSTagValue 102 | 103 | Elb: 104 | Type: AWS::ElasticLoadBalancingV2::LoadBalancer 105 | Properties: 106 | LoadBalancerAttributes: 107 | - Key: access_logs.s3.enabled 108 | Value: 'true' 109 | - Key: access_logs.s3.bucket 110 | Value: !Ref LogBucket 111 | - Key: access_logs.s3.prefix 112 | Value: !FindInMap [ Defaults, Elb, LogLocation ] 113 | Scheme: internet-facing 114 | SecurityGroups: 115 | - !Ref ElbSecurityGroup 116 | Subnets: !Ref PublicSubnetIds 117 | Tags: 118 | - Key: !Ref QSTagKey 119 | Value: !Ref QSTagValue 120 | Type: application 121 | ElbSecurityGroup: 122 | Type: AWS::EC2::SecurityGroup 123 | Properties: 124 | GroupDescription: ELB Security Group for HITRUST CSF Quick Start 125 | SecurityGroupEgress: 126 | - IpProtocol: tcp 127 | FromPort: 443 128 | ToPort: 443 129 | CidrIp: !Ref VpcCidr 130 | SecurityGroupIngress: 131 | - IpProtocol: tcp 132 | FromPort: 443 133 | ToPort: 443 134 | CidrIp: !Ref SourceCIDR 135 | Tags: 136 | - Key: !Ref QSTagKey 137 | Value: !Ref QSTagValue 138 | VpcId: !Ref VpcId 139 | ElbListenerHTTPS: 140 | Type: AWS::ElasticLoadBalancingV2::Listener 141 | Properties: 142 | Certificates: 143 | - CertificateArn: !If [ NoSSLCertificate, !GetAtt ConfigureSSLStack.Outputs.ACMCertificate, !Ref SSLCertificateARN ] 144 | DefaultActions: 145 | - Type: forward 146 | TargetGroupArn: !Ref ElbTargetGroupHTTPS 147 | LoadBalancerArn: !Ref Elb 148 | Port: 443 149 | Protocol: HTTPS 150 | ElbTargetGroupHTTPS: 151 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 152 | Properties: 153 | HealthCheckIntervalSeconds: 30 154 | HealthCheckTimeoutSeconds: 5 155 | HealthyThresholdCount: 2 156 | UnhealthyThresholdCount: 5 157 | HealthCheckPath: "/landing.html" 158 | Port: 443 159 | Protocol: HTTPS 160 | Tags: 161 | - Key: !Ref QSTagKey 162 | Value: !Ref QSTagValue 163 | VpcId: !Ref VpcId 164 | 165 | RecordAlias: 166 | Type: AWS::Route53::RecordSet 167 | Properties: 168 | AliasTarget: 169 | DNSName: !GetAtt Elb.DNSName 170 | HostedZoneId: !GetAtt Elb.CanonicalHostedZoneID 171 | HostedZoneId: !Ref AWSHostedZoneID 172 | Name: !Ref AWSPublicFQDN 173 | Type: A 174 | 175 | Outputs: 176 | ElbTargetGroupHTTPS: 177 | Value: !Ref ElbTargetGroupHTTPS 178 | Description: ELB Target Group 179 | ElbDns: 180 | Value: !GetAtt Elb.DNSName 181 | Description: ELB DNS Name 182 | ElbSecurityGroup: 183 | Value: !Ref ElbSecurityGroup 184 | Description: The ID of the security group for the load balancer 185 | RecordSet: 186 | Value: !Ref RecordAlias 187 | Description: Domain Name of the Record Set 188 | 189 | -------------------------------------------------------------------------------- /templates/copy-lambdas.template: -------------------------------------------------------------------------------- 1 | # This is modified from https://github.com/aws-quickstart/quickstart-bitnami-wordpress/blob/master/templates/copy-lambdas.template 2 | 3 | --- 4 | AWSTemplateFormatVersion: '2010-09-09' 5 | Description: This template creates an S3 bucket in the same region where the stack 6 | is launched and copy the Lambda functions code from original bucket to the new bucket. 7 | (qs-1op312ie1) 8 | Metadata: 9 | cfn-lint: 10 | config: 11 | ignore_checks: 12 | - W9002 13 | - W9003 14 | - W9006 15 | Parameters: 16 | LogBucket: 17 | Type: String 18 | Description: Log Bucket 19 | QSS3BucketName: 20 | AllowedPattern: "^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$" 21 | ConstraintDescription: Quick Start bucket name can include numbers, lowercase 22 | letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen 23 | (-). 24 | Default: aws-quickstart 25 | Description: S3 bucket name for the Quick Start assets. Quick Start bucket name 26 | can include numbers, lowercase letters, uppercase letters, and hyphens (-). 27 | It cannot start or end with a hyphen (-). 28 | Type: String 29 | QSS3BucketRegion: 30 | Default: 'us-east-1' 31 | Description: 'The AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted. When using your own bucket, you must specify this value.' 32 | Type: String 33 | QSS3KeyPrefix: 34 | AllowedPattern: "^[0-9a-zA-Z-/]*$" 35 | ConstraintDescription: Quick Start key prefix can include numbers, lowercase letters, 36 | uppercase letters, hyphens (-), and forward slash (/). 37 | Default: quickstart-codepipeline-bluegreen-deployment/ 38 | Description: S3 key prefix for the Quick Start assets. Quick Start key prefix 39 | can include numbers, lowercase letters, uppercase letters, hyphens (-), and 40 | forward slash (/). 41 | Type: String 42 | QSTagKey: 43 | Type: String 44 | Description: Tag key to identify resources from this Quick Start 45 | QSTagValue: 46 | Type: String 47 | Description: Tag value to identify resources from this Quick Start 48 | 49 | Mappings: 50 | Storage: 51 | LambdaZips: 52 | LogPrefix: lambda-zips/ 53 | 54 | Conditions: 55 | UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-quickstart'] 56 | 57 | Resources: 58 | CopyObjects: 59 | Type: AWS::CloudFormation::CustomResource 60 | Properties: 61 | ServiceToken: !GetAtt CopyObjectsFunction.Arn 62 | DestBucket: !Ref LambdaZipsBucket 63 | Objects: 64 | - functions/packages/ACMCert/lambda.zip 65 | SourceBucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 66 | Prefix: !Ref QSS3KeyPrefix 67 | CopyObjectsFunction: 68 | Type: AWS::Lambda::Function 69 | Properties: 70 | Code: 71 | ZipFile: | 72 | import json 73 | import logging 74 | import threading 75 | import boto3 76 | import cfnresponse 77 | 78 | def copy_objects(source_bucket, dest_bucket, prefix, objects): 79 | s3 = boto3.client('s3') 80 | for o in objects: 81 | key = prefix + o 82 | copy_source = { 83 | 'Bucket': source_bucket, 84 | 'Key': key 85 | } 86 | s3.copy_object(CopySource=copy_source, Bucket=dest_bucket, Key=key) 87 | 88 | def delete_objects(bucket): 89 | client = boto3.client('s3') 90 | print("Collecting data from" + bucket) 91 | paginator = client.get_paginator('list_object_versions') 92 | result = paginator.paginate(Bucket=bucket) 93 | objects = [] 94 | for page in result: 95 | try: 96 | for k in page['Versions']: 97 | objects.append({'Key':k['Key'],'VersionId': k['VersionId']}) 98 | try: 99 | for k in page['DeleteMarkers']: 100 | version = k['VersionId'] 101 | key = k['Key'] 102 | objects.append({'Key': key,'VersionId': version}) 103 | except: 104 | pass 105 | print("deleting objects") 106 | client.delete_objects(Bucket=bucket, Delete={'Objects': objects}) 107 | except: 108 | pass 109 | print("bucket already empty") 110 | 111 | def timeout(event, context): 112 | logging.error('Execution is about to time out, sending failure response to CloudFormation') 113 | cfnresponse.send(event, context, cfnresponse.FAILED, {}, None) 114 | 115 | def handler(event, context): 116 | # make sure we send a failure to CloudFormation if the function is going to timeout 117 | timer = threading.Timer((context.get_remaining_time_in_millis() / 1000.00) - 0.5, timeout, args=[event, context]) 118 | timer.start() 119 | 120 | print('Received event: %s' % json.dumps(event)) 121 | status = cfnresponse.SUCCESS 122 | try: 123 | source_bucket = event['ResourceProperties']['SourceBucket'] 124 | dest_bucket = event['ResourceProperties']['DestBucket'] 125 | prefix = event['ResourceProperties']['Prefix'] 126 | objects = event['ResourceProperties']['Objects'] 127 | if event['RequestType'] == 'Delete': 128 | delete_objects(dest_bucket) 129 | else: 130 | copy_objects(source_bucket, dest_bucket, prefix, objects) 131 | except Exception as e: 132 | logging.error('Exception: %s' % e, exc_info=True) 133 | status = cfnresponse.FAILED 134 | finally: 135 | timer.cancel() 136 | cfnresponse.send(event, context, status, {}, None) 137 | 138 | Description: Copies objects from a source S3 bucket to a destination S3 bucket 139 | Handler: index.handler 140 | Role: !GetAtt CopyObjectsRole.Arn 141 | Runtime: python3.7 142 | Tags: 143 | - Key: !Ref QSTagKey 144 | Value: !Ref QSTagValue 145 | Timeout: 600 146 | CopyObjectsRole: 147 | Properties: 148 | AssumeRolePolicyDocument: 149 | Statement: 150 | - Action: sts:AssumeRole 151 | Effect: Allow 152 | Principal: 153 | Service: lambda.amazonaws.com 154 | Version: '2012-10-17' 155 | ManagedPolicyArns: 156 | - !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole' 157 | Path: "/" 158 | Policies: 159 | - PolicyDocument: 160 | Statement: 161 | - Action: 162 | - s3:GetObject 163 | Effect: Allow 164 | Resource: !Sub 165 | - arn:${AWS::Partition}:s3:::${S3Bucket}/${QSS3KeyPrefix}* 166 | - S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 167 | - Action: 168 | - s3:PutObject 169 | - s3:DeleteObject 170 | - s3:GetObject 171 | - s3:ListBucket 172 | - s3:ListBucketVersions 173 | - s3:DeleteObjectVersion 174 | - s3:GetObjectVersion 175 | - s3:GetBucketVersioning 176 | Effect: Allow 177 | Resource: 178 | - !Sub arn:${AWS::Partition}:s3:::${LambdaZipsBucket}/${QSS3KeyPrefix}* 179 | - !Sub arn:${AWS::Partition}:s3:::${LambdaZipsBucket} 180 | Version: '2012-10-17' 181 | PolicyName: object-copier 182 | Type: AWS::IAM::Role 183 | LambdaZipsBucket: 184 | Type: AWS::S3::Bucket 185 | Properties: 186 | AccessControl: Private 187 | BucketEncryption: 188 | ServerSideEncryptionConfiguration: 189 | - ServerSideEncryptionByDefault: 190 | SSEAlgorithm: AES256 191 | LoggingConfiguration: 192 | DestinationBucketName: !Ref LogBucket 193 | LogFilePrefix: !FindInMap [ Storage, LambdaZips, LogPrefix ] 194 | Tags: 195 | - Key: !Ref QSTagKey 196 | Value: !Ref QSTagValue 197 | VersioningConfiguration: 198 | Status: Enabled 199 | LambdaZipsBucketPolicy: 200 | DependsOn: CleanupLambdaZipsBucket 201 | Type: AWS::S3::BucketPolicy 202 | Properties: 203 | Bucket: !Ref LambdaZipsBucket 204 | PolicyDocument: 205 | Version: 2012-10-17 206 | Id: LambdaZipsBucketPolicy 207 | Statement: 208 | - Sid: ForceSSL 209 | Effect: Deny 210 | Principal: "*" 211 | Action: s3:* 212 | Resource: !Sub arn:${AWS::Partition}:s3:::${LambdaZipsBucket}/* 213 | Condition: 214 | Bool: 215 | "aws:SecureTransport": "false" 216 | 217 | CleanupLambdaZipsBucket: 218 | Type: AWS::CloudFormation::Stack 219 | Properties: 220 | TemplateURL: 221 | !Sub 222 | - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}templates/clean-bucket.template 223 | - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] 224 | S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 225 | Parameters: 226 | Bucket: !Ref LambdaZipsBucket 227 | QSTagKey: !Ref QSTagKey 228 | QSTagValue: !Ref QSTagValue 229 | Tags: 230 | - Key: !Ref QSTagKey 231 | Value: !Ref QSTagValue 232 | 233 | Outputs: 234 | LambdaZipsBucket: 235 | Description: S3 Bucket for the Lambda Function Code 236 | Value: !Ref LambdaZipsBucket 237 | -------------------------------------------------------------------------------- /templates/logging.template: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: '2010-09-09' 3 | Description: CloudFormation Template for basic logging functionality. Sets up ELB logging and Config if 4 | the Config Recorder and Config Delivery Channel are not yet set up. (qs-1sflpub4u) 5 | Metadata: 6 | cfn-lint: 7 | config: 8 | ignore_checks: 9 | - W9002 10 | - W9003 11 | - W9006 12 | Parameters: 13 | ConfigRecorderName: 14 | Type: String 15 | Default: "" 16 | Description: Config Recorder. Empty string will create a new Config Recorder. 17 | ConfigDeliveryChannelName: 18 | Type: String 19 | Default: "" 20 | Description: Config Delivery Channel. Must have if Config Recorder specified. 21 | QSS3BucketName: 22 | AllowedPattern: "^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$" 23 | ConstraintDescription: Quick Start bucket name can include numbers, lowercase 24 | letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen 25 | (-). 26 | Default: aws-quickstart 27 | Description: S3 bucket name for the Quick Start assets. This string can include 28 | numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start 29 | or end with a hyphen (-). 30 | Type: String 31 | QSS3BucketRegion: 32 | Default: 'us-east-1' 33 | Description: 'The AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted. When using your own bucket, you must specify this value.' 34 | Type: String 35 | QSS3KeyPrefix: 36 | AllowedPattern: "^[0-9a-zA-Z-/]*$" 37 | ConstraintDescription: Quick Start key prefix can include numbers, lowercase letters, 38 | uppercase letters, hyphens (-), and forward slash (/). 39 | Default: quickstart-tableau-server-healthcare/ 40 | Description: S3 key prefix for the Quick Start assets. Quick Start key prefix 41 | can include numbers, lowercase letters, uppercase letters, hyphens (-), and 42 | forward slash (/). 43 | Type: String 44 | QSTagKey: 45 | Type: String 46 | Description: Tag key to identify resources from this Quick Start 47 | QSTagValue: 48 | Type: String 49 | Description: Tag value to identify resources from this Quick Start 50 | 51 | Mappings: 52 | CloudTrail: 53 | S3: 54 | LogPrefix: cloud-trail/hitrust 55 | ElbLogging: 56 | us-east-1: 57 | Principal: '127311923021' 58 | us-east-2: 59 | Principal: '033677994240' 60 | us-west-1: 61 | Principal: '027434742980' 62 | us-west-2: 63 | Principal: '797873946194' 64 | ca-central-1: 65 | Principal: '985666609251' 66 | eu-central-1: 67 | Principal: '054676820928' 68 | eu-west-1: 69 | Principal: '156460612806' 70 | eu-west-2: 71 | Principal: '652711504416' 72 | eu-west-3: 73 | Principal: '009996457667' 74 | eu-north-1: 75 | Principal: '897822967062' 76 | ap-east-1: 77 | Principal: '754344448648' 78 | ap-northeast-1: 79 | Principal: '582318560864' 80 | ap-northeast-2: 81 | Principal: '600734575887' 82 | ap-northeast-3: 83 | Principal: '383597477331' 84 | ap-southeast-1: 85 | Principal: '114774131450' 86 | ap-southeast-2: 87 | Principal: '783225319266' 88 | ap-south-1: 89 | Principal: '718504428378' 90 | me-south-1: 91 | Principal: '076674570225' 92 | sa-east-1: 93 | Principal: '507241528517' 94 | us-gov-west-1: 95 | Principal: '048591011584' 96 | us-gov-east-1: 97 | Principal: '190560391635' 98 | cn-north-1: 99 | Principal: '638102146993' 100 | cn-northwest-1: 101 | Principal: '037604701340' 102 | 103 | Conditions: 104 | CreateConfig: !And [ !Equals [ !Ref ConfigRecorderName, '' ], !Equals [ !Ref ConfigDeliveryChannelName, '' ] ] 105 | UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-quickstart'] 106 | 107 | Resources: 108 | LogBucket: 109 | Type: AWS::S3::Bucket 110 | Properties: 111 | AccessControl: LogDeliveryWrite 112 | BucketEncryption: 113 | ServerSideEncryptionConfiguration: 114 | - ServerSideEncryptionByDefault: 115 | SSEAlgorithm: AES256 116 | Tags: 117 | - Key: !Ref QSTagKey 118 | Value: !Ref QSTagValue 119 | VersioningConfiguration: 120 | Status: Enabled 121 | LogBucketPolicy: 122 | DependsOn: CleanupLogBucket 123 | Type: AWS::S3::BucketPolicy 124 | Properties: 125 | Bucket: !Ref LogBucket 126 | PolicyDocument: 127 | Version: 2012-10-17 128 | Id: LogBucketPolicy 129 | Statement: 130 | - Sid: DenyDeleteObject 131 | Effect: Deny 132 | Principal: "*" 133 | Action: "s3:DeleteObject" 134 | Resource: !Sub arn:${AWS::Partition}:s3:::${LogBucket}/* 135 | - Sid: DenyDeleteBucket 136 | Effect: Deny 137 | Principal: "*" 138 | Action: "s3:DeleteBucket" 139 | Resource: !Sub arn:${AWS::Partition}:s3:::${LogBucket} 140 | - Sid: ElbLogs 141 | Effect: Allow 142 | Principal: 143 | AWS: 144 | - !FindInMap [ ElbLogging, !Ref "AWS::Region" , Principal ] 145 | Action: "s3:PutObject" 146 | Resource: !Sub arn:${AWS::Partition}:s3:::${LogBucket}/* 147 | - Sid: AllowCloudTrailAclCheck 148 | Effect: Allow 149 | Principal: 150 | Service: cloudtrail.amazonaws.com 151 | Action: s3:GetBucketAcl 152 | Resource: !Sub arn:${AWS::Partition}:s3:::${LogBucket} 153 | - Sid: AllowCloudTrailWrite 154 | Effect: Allow 155 | Principal: 156 | Service: cloudtrail.amazonaws.com 157 | Action: s3:PutObject 158 | Resource: !Sub 159 | - arn:${AWS::Partition}:s3:::${LogBucket}/${LogPrefix}/AWSLogs/${AWS::AccountId}/* 160 | - { LogPrefix: !FindInMap [ CloudTrail, S3, LogPrefix ] } 161 | Condition: 162 | StringEquals: 163 | "s3:x-amz-acl": bucket-owner-full-control 164 | - Sid: ForceSSL 165 | Effect: Deny 166 | Principal: "*" 167 | Action: s3:* 168 | Resource: !Sub arn:${AWS::Partition}:s3:::${LogBucket}/* 169 | Condition: 170 | Bool: 171 | "aws:SecureTransport": "false" 172 | 173 | CloudTrailHitrustQS: 174 | Type: AWS::CloudTrail::Trail 175 | DependsOn: 176 | - LogBucketPolicy 177 | Properties: 178 | EnableLogFileValidation: true 179 | IncludeGlobalServiceEvents: true 180 | IsLogging: true 181 | S3BucketName: !Ref LogBucket 182 | S3KeyPrefix: !FindInMap [ CloudTrail, S3, LogPrefix ] 183 | Tags: 184 | - Key: !Ref QSTagKey 185 | Value: !Ref QSTagValue 186 | 187 | # Cleans up log bucket when stack is deleted/rolled back 188 | CleanupLogBucket: 189 | Type: AWS::CloudFormation::Stack 190 | Properties: 191 | TemplateURL: 192 | !Sub 193 | - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}templates/clean-bucket.template 194 | - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] 195 | S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 196 | Parameters: 197 | Bucket: !Ref LogBucket 198 | QSTagKey: !Ref QSTagKey 199 | QSTagValue: !Ref QSTagValue 200 | Tags: 201 | - Key: !Ref QSTagKey 202 | Value: !Ref QSTagValue 203 | 204 | # Instantiate Config 205 | ConfigRole: 206 | Type: AWS::IAM::Role 207 | Properties: 208 | AssumeRolePolicyDocument: 209 | Version: 2012-10-17 210 | Statement: 211 | - Effect: Allow 212 | Principal: 213 | Service: 214 | - "config.amazonaws.com" 215 | Action: 216 | - "sts:AssumeRole" 217 | ManagedPolicyArns: 218 | - !Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AWSConfigRulesExecutionRole 219 | - !Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AWSConfigRole 220 | ConfigRolePolicy: 221 | Type: AWS::IAM::Policy 222 | Properties: 223 | PolicyName: ConfigRolePolicy 224 | PolicyDocument: 225 | Version: 2012-10-17 226 | Statement: 227 | - Effect: Allow 228 | Action: 229 | - s3:PutObject 230 | Resource: !Sub arn:${AWS::Partition}:s3:::${LogBucket}/config* 231 | Condition: 232 | StringLike: 233 | s3:x-amz-acl: bucket-owner-full-control 234 | - Effect: Allow 235 | Action: 236 | - lambda:InvokeFunction 237 | Resource: "*" 238 | - Effect: Allow 239 | Action: 240 | - "config:Put*" 241 | Resource: "*" 242 | - Effect: Allow 243 | Action: 244 | - SNS:Publish 245 | Resource: "*" 246 | Roles: 247 | - !Ref ConfigRole 248 | ConfigSNS: 249 | Type: AWS::SNS::Topic 250 | Condition: CreateConfig 251 | ConfigSNSPolicy: 252 | Type: AWS::SNS::TopicPolicy 253 | Condition: CreateConfig 254 | Properties: 255 | PolicyDocument: 256 | Id: ConfigSNSPolicy 257 | Version: 2012-10-17 258 | Statement: 259 | - Effect: Allow 260 | Principal: 261 | AWS: [ !GetAtt ConfigRole.Arn ] 262 | Action: SNS:Publish 263 | Resource: !Ref ConfigSNS 264 | Topics: 265 | - !Ref ConfigSNS 266 | ConfigRecorder: 267 | Type: AWS::Config::ConfigurationRecorder 268 | Condition: CreateConfig 269 | Properties: 270 | RecordingGroup: 271 | AllSupported: true 272 | IncludeGlobalResourceTypes: true 273 | RoleARN: !GetAtt ConfigRole.Arn 274 | ConfigDeliveryChannel: 275 | Type: AWS::Config::DeliveryChannel 276 | Condition: CreateConfig 277 | Properties: 278 | ConfigSnapshotDeliveryProperties: 279 | DeliveryFrequency: One_Hour 280 | S3BucketName: !Ref LogBucket 281 | S3KeyPrefix: "config" 282 | SnsTopicARN: !Ref ConfigSNS 283 | 284 | ConfigRulesStack: 285 | Type: AWS::CloudFormation::Stack 286 | DependsOn: 287 | - ConfigRole 288 | - ConfigRolePolicy 289 | Properties: 290 | TemplateURL: 291 | !Sub 292 | - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}templates/config-rules.template 293 | - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] 294 | S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 295 | Parameters: 296 | LogBucket: !Ref LogBucket 297 | QSTagKey: !Ref QSTagKey 298 | QSTagValue: !Ref QSTagValue 299 | Tags: 300 | - Key: QSTagKey 301 | Value: QSTagValue 302 | 303 | Outputs: 304 | LogBucket: 305 | Value: !Ref LogBucket 306 | Description: Logging bucket for HITRUST CST Quick Start 307 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /templates/main.template: -------------------------------------------------------------------------------- 1 | # CloudFormation Organization 2 | # - master 3 | # - logging 4 | # - config rules 5 | # - vpc management 6 | # - vpc production 7 | # - vpc networking (peering) 8 | # - bastion 9 | # - application 10 | # - encrypted elb 11 | # - copy lambda 12 | # - configure ssl 13 | # - acm/route53 14 | # - database 15 | 16 | AWSTemplateFormatVersion: 2010-09-09 17 | Description: 'Main template for deploying example HITRUST CSF Quick Start. Creates VPC, 3-tier web app, and logging. (qs-1q77hsjug)' 18 | 19 | Metadata: 20 | cfn-lint: 21 | config: 22 | ignore_checks: 23 | - W9006 24 | - W9901 25 | QuickStartDocumentation: 26 | EntrypointName: "Parameters for deploying into new VPCs" 27 | AWS::CloudFormation::Interface: 28 | ParameterGroups: 29 | - Label: 30 | default: Network Configuration 31 | Parameters: 32 | - AvailabilityZones 33 | - BastionAccessCidr 34 | - SourceCidr 35 | - Label: 36 | default: Logging Configuration 37 | Parameters: 38 | - ConfigRecorder 39 | - ConfigDeliveryChannel 40 | - Label: 41 | default: Server DNS configuration 42 | Parameters: 43 | - AWSPublicFQDN 44 | - AWSHostedZoneID 45 | - SSLCertificateARN 46 | - Label: 47 | default: Security Configuration 48 | Parameters: 49 | - DBPassword 50 | - BastionKeyPairName 51 | - KeyPairName 52 | - Label: 53 | default: AWS Quick Start Configuration 54 | Parameters: 55 | - QSS3BucketName 56 | - QSS3BucketRegion 57 | - QSS3KeyPrefix 58 | - QSTagKey 59 | - QSTagValue 60 | ParameterLabels: 61 | AWSHostedZoneID: 62 | default: DNS Zone ID 63 | AWSPublicFQDN: 64 | default: Full DNS Name for Web App 65 | AvailabilityZones: 66 | default: Availability Zones 67 | BastionAccessCidr: 68 | default: CIDR to access bastion 69 | BastionKeyPairName: 70 | default: Bastion key pair 71 | ConfigRecorder: 72 | default: AWS Config Recorder Name 73 | ConfigDeliveryChannel: 74 | default: AWS Config Delivery Channel Name 75 | DBPassword: 76 | default: Database Password 77 | KeyPairName: 78 | default: EC2 Key Pair (App) 79 | QSS3BucketName: 80 | default: Quick Start S3 Bucket Name 81 | QSS3BucketRegion: 82 | default: Quick Start S3 bucket region 83 | QSS3KeyPrefix: 84 | default: Quick Start S3 Key Prefix 85 | QSTagKey: 86 | default: Quick Start Tag key 87 | QSTagValue: 88 | default: Quick Start Tag value 89 | SSLCertificateARN: 90 | default: SSL Certificate ARN (Requires matching DNS name) 91 | SourceCidr: 92 | default: Source CIDR for access 93 | 94 | Parameters: 95 | AWSHostedZoneID: 96 | Description: DNS Zone ID to contain the server's DNS entry. 97 | Type: AWS::Route53::HostedZone::Id 98 | AWSPublicFQDN: 99 | Description: Web app will be reachable at this address. 100 | Type: String 101 | AvailabilityZones: 102 | Description: 'List of Availability Zones to use for the subnets in the VPC. 103 | You must use two Availabilty Zones. The Quick Start preserves the logical order you specify.' 104 | Type: List 105 | BastionAccessCidr: 106 | 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]))$" 107 | ConstraintDescription: Must be a valid IP CIDR range of the form x.x.x.x/x. 108 | Description: IP address/range to allow access to bastion host from 109 | Type: String 110 | BastionKeyPairName: 111 | Type: AWS::EC2::KeyPair::KeyName 112 | Description: Name of an existing EC2 KeyPair to enable SSH access to Bastion Host 113 | ConstraintDescription: Must be the name of an existing EC2 Key Pair 114 | ConfigRecorder: 115 | Type: String 116 | Default: "" 117 | Description: Config Recorder in your AWS Region. Leaving empty will try to create a new recorder. 118 | ConfigDeliveryChannel: 119 | Type: String 120 | Default: "" 121 | Description: Config Delivery Channel in your AWS Region. Leaving empty will try to create a new recorder. Required if you specify Config Recorder 122 | DBPassword: 123 | Description: Mixed alphanumeric and must be between 8 and 28 characters and contain 124 | at least one capital letter 125 | NoEcho: true 126 | Type: String 127 | MinLength: 8 128 | MaxLength: 28 129 | AllowedPattern: '[a-zA-Z0-9!^*\-_+]*' 130 | ConstraintDescription: Can only contain alphanumeric characters or the following 131 | special characters !^*-_+, between 8 and 28 characters 132 | KeyPairName: 133 | Type: AWS::EC2::KeyPair::KeyName 134 | Description: Name of an existing EC2 KeyPair to enable SSH access to app servers 135 | ConstraintDescription: Must be the name of an existing EC2 Key Pair 136 | QSS3BucketName: 137 | AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-.]*[0-9a-zA-Z])*$ 138 | ConstraintDescription: Quick Start bucket name can include numbers, lowercase 139 | letters, uppercase letters, periods (.), and hyphens (-). It cannot start or 140 | end with a hyphen (-). 141 | Default: aws-quickstart 142 | Description: S3 bucket name for the Quick Start assets. Quick Start bucket name 143 | can include numbers, lowercase letters, uppercase letters, and hyphens (-). 144 | It cannot start or end with a hyphen (-). 145 | Type: String 146 | QSS3BucketRegion: 147 | Default: 'us-east-1' 148 | Description: 'The AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted. When using your own bucket, you must specify this value.' 149 | Type: String 150 | QSS3KeyPrefix: 151 | AllowedPattern: ^[0-9a-zA-Z-/]*$ 152 | ConstraintDescription: Quick Start key prefix can include numbers, lowercase letters, 153 | uppercase letters, hyphens (-), and forward slash (/). 154 | Default: quickstart-hitrust-csf/ 155 | Description: S3 key prefix for the Quick Start assets. Quick Start key prefix 156 | can include numbers, lowercase letters, uppercase letters, hyphens (-), and 157 | forward slash (/). 158 | Type: String 159 | QSTagKey: 160 | Type: String 161 | Description: Tag key to identify resources from this Quick Start 162 | Default: QuickStartID 163 | QSTagValue: 164 | Type: String 165 | Description: Tag value to identify resources from this Quick Start 166 | Default: quickstart-hitrust-csf 167 | SSLCertificateARN: 168 | Type: String 169 | Default: "" 170 | Description: The Amazon Resource Name for the existing SSL cert you wish to use; empty for none. If empty, will create as part of Quick Start 171 | SourceCidr: 172 | 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]))$" 173 | ConstraintDescription: Must be a valid IP CIDR range of the form x.x.x.x/x. 174 | Description: IP address/range to allow access to web app from 175 | Type: String 176 | 177 | Mappings: 178 | Networking: 179 | ProductionCIDR: 180 | Private1A: 10.100.0.0/20 181 | Private1B: 10.100.16.0/20 182 | Private2A: 10.100.32.0/20 183 | Private2B: 10.100.48.0/20 184 | Public1: 10.100.100.0/24 185 | Public2: 10.100.101.0/24 186 | VPC: 10.100.0.0/16 187 | ManagementCIDR: 188 | Private1A: 10.101.0.0/20 189 | Private1B: 10.101.16.0/20 190 | Private2A: 10.101.32.0/20 191 | Private2B: 10.101.48.0/20 192 | Public1: 10.101.100.0/24 193 | Public2: 10.101.101.0/24 194 | VPC: 10.101.0.0/16 195 | Bastion: 196 | InstanceType: t2.medium 197 | 198 | Conditions: 199 | UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-quickstart'] 200 | 201 | Resources: 202 | # Need Config Rules 203 | Logging: 204 | Type: AWS::CloudFormation::Stack 205 | Properties: 206 | TemplateURL: 207 | !Sub 208 | - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}templates/logging.template 209 | - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] 210 | S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 211 | Parameters: 212 | ConfigRecorderName: !Ref ConfigRecorder 213 | ConfigDeliveryChannelName: !Ref ConfigDeliveryChannel 214 | QSS3BucketName: !Ref QSS3BucketName 215 | QSS3BucketRegion: !Ref QSS3BucketRegion 216 | QSS3KeyPrefix: !Ref QSS3KeyPrefix 217 | QSTagKey: !Ref QSTagKey 218 | QSTagValue: !Sub ${QSTagValue}-${AWS::StackName} 219 | Tags: 220 | - Key: !Ref QSTagKey 221 | Value: !Sub ${QSTagValue}-${AWS::StackName} 222 | 223 | ProductionVpc: 224 | Type: AWS::CloudFormation::Stack 225 | Properties: 226 | TemplateURL: 227 | !Sub 228 | - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}submodules/quickstart-aws-vpc/templates/aws-vpc.template.yaml 229 | - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] 230 | S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 231 | Parameters: 232 | AvailabilityZones: !Join [ "," , !Ref AvailabilityZones ] 233 | CreateAdditionalPrivateSubnets: 'true' 234 | CreatePrivateSubnets: 'true' 235 | NumberOfAZs: '2' 236 | PrivateSubnet1ACIDR: !FindInMap [ Networking, ProductionCIDR, Private1A ] 237 | PrivateSubnet1BCIDR: !FindInMap [ Networking, ProductionCIDR, Private1B ] 238 | PrivateSubnet2ACIDR: !FindInMap [ Networking, ProductionCIDR, Private2A ] 239 | PrivateSubnet2BCIDR: !FindInMap [ Networking, ProductionCIDR, Private2B ] 240 | PublicSubnet1CIDR: !FindInMap [ Networking, ProductionCIDR, Public1 ] 241 | PublicSubnet2CIDR: !FindInMap [ Networking, ProductionCIDR, Public2 ] 242 | PublicSubnetTag1: !Sub ${QSTagKey}=${QSTagValue}-${AWS::StackName} 243 | PrivateSubnetATag1: !Sub ${QSTagKey}=${QSTagValue}-${AWS::StackName} 244 | PrivateSubnetBTag1: !Sub ${QSTagKey}=${QSTagValue}-${AWS::StackName} 245 | VPCCIDR: !FindInMap [ Networking, ProductionCIDR, VPC ] 246 | Tags: 247 | - Key: !Ref QSTagKey 248 | Value: !Sub ${QSTagValue}-${AWS::StackName} 249 | 250 | ManagementVpc: 251 | Type: AWS::CloudFormation::Stack 252 | Properties: 253 | TemplateURL: 254 | !Sub 255 | - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}submodules/quickstart-aws-vpc/templates/aws-vpc.template.yaml 256 | - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] 257 | S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 258 | Parameters: 259 | AvailabilityZones: !Join [ "," , !Ref AvailabilityZones ] 260 | CreateAdditionalPrivateSubnets: 'true' 261 | CreatePrivateSubnets: 'true' 262 | NumberOfAZs: '2' 263 | PrivateSubnet1ACIDR: !FindInMap [ Networking, ManagementCIDR, Private1A ] 264 | PrivateSubnet1BCIDR: !FindInMap [ Networking, ManagementCIDR, Private1B ] 265 | PrivateSubnet2ACIDR: !FindInMap [ Networking, ManagementCIDR, Private2A ] 266 | PrivateSubnet2BCIDR: !FindInMap [ Networking, ManagementCIDR, Private2B ] 267 | PublicSubnet1CIDR: !FindInMap [ Networking, ManagementCIDR, Public1 ] 268 | PublicSubnet2CIDR: !FindInMap [ Networking, ManagementCIDR, Public2 ] 269 | PublicSubnetTag1: !Sub ${QSTagKey}=${QSTagValue}-${AWS::StackName} 270 | PrivateSubnetATag1: !Sub ${QSTagKey}=${QSTagValue}-${AWS::StackName} 271 | PrivateSubnetBTag1: !Sub ${QSTagKey}=${QSTagValue}-${AWS::StackName} 272 | VPCCIDR: !FindInMap [ Networking, ManagementCIDR, VPC ] 273 | Tags: 274 | - Key: !Ref QSTagKey 275 | Value: !Sub ${QSTagValue}-${AWS::StackName} 276 | 277 | VpcNetworking: 278 | Type: AWS::CloudFormation::Stack 279 | Properties: 280 | TemplateURL: 281 | !Sub 282 | - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}templates/vpc-networking.template 283 | - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] 284 | S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 285 | Parameters: 286 | ProductionVpcId: !GetAtt ProductionVpc.Outputs.VPCID 287 | ProductionVpcCidr: !GetAtt ProductionVpc.Outputs.VPCCIDR 288 | ProductionRouteTablePrivateA: !GetAtt ProductionVpc.Outputs.PrivateSubnet1ARouteTable 289 | ProductionRouteTablePrivateB: !GetAtt ProductionVpc.Outputs.PrivateSubnet2ARouteTable 290 | ProductionRouteTablePublic: !GetAtt ProductionVpc.Outputs.PublicSubnetRouteTable 291 | ManagementVpcId: !GetAtt ManagementVpc.Outputs.VPCID 292 | ManagementVpcCidr: !GetAtt ManagementVpc.Outputs.VPCCIDR 293 | ManagementRouteTablePrivateA: !GetAtt ManagementVpc.Outputs.PrivateSubnet1ARouteTable 294 | ManagementRouteTablePrivateB: !GetAtt ManagementVpc.Outputs.PrivateSubnet2ARouteTable 295 | ManagementRouteTablePublic: !GetAtt ManagementVpc.Outputs.PublicSubnetRouteTable 296 | QSTagKey: !Ref QSTagKey 297 | QSTagValue: !Sub ${QSTagValue}-${AWS::StackName} 298 | Tags: 299 | - Key: !Ref QSTagKey 300 | Value: !Sub ${QSTagValue}-${AWS::StackName} 301 | 302 | Bastion: 303 | Type: AWS::CloudFormation::Stack 304 | DependsOn: 305 | - VpcNetworking 306 | Properties: 307 | TemplateURL: 308 | !Sub 309 | - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}submodules/quickstart-linux-bastion/templates/linux-bastion.template 310 | - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] 311 | S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 312 | Parameters: 313 | BastionInstanceType: !FindInMap [ Networking, Bastion, InstanceType ] 314 | EnableBanner: 'false' 315 | KeyPairName: !Ref BastionKeyPairName 316 | RemoteAccessCIDR: !Ref BastionAccessCidr 317 | PublicSubnet1ID: !GetAtt ManagementVpc.Outputs.PublicSubnet1ID 318 | PublicSubnet2ID: !GetAtt ManagementVpc.Outputs.PublicSubnet2ID 319 | QSS3BucketName: !Ref QSS3BucketName 320 | QSS3BucketRegion: !Ref QSS3BucketRegion 321 | QSS3KeyPrefix: !Sub ${QSS3KeyPrefix}submodules/quickstart-linux-bastion/ 322 | VPCID: !GetAtt ManagementVpc.Outputs.VPCID 323 | Tags: 324 | - Key: !Ref QSTagKey 325 | Value: !Sub ${QSTagValue}-${AWS::StackName} 326 | 327 | Application: 328 | Type: AWS::CloudFormation::Stack 329 | Properties: 330 | TemplateURL: 331 | !Sub 332 | - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}templates/application.template 333 | - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] 334 | S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 335 | Parameters: 336 | AWSHostedZoneID: !Ref AWSHostedZoneID 337 | AWSPublicFQDN: !Ref AWSPublicFQDN 338 | BastionSecurityGroup: !GetAtt Bastion.Outputs.BastionSecurityGroupID 339 | DBPassword: !Ref DBPassword 340 | KeyPairName: !Ref KeyPairName 341 | LogBucket: !GetAtt Logging.Outputs.LogBucket 342 | PublicSubnetIds: !Sub "${ProductionVpc.Outputs.PublicSubnet1ID},${ProductionVpc.Outputs.PublicSubnet2ID}" 343 | PrivateSubnetAppIds: !Sub "${ProductionVpc.Outputs.PrivateSubnet1AID},${ProductionVpc.Outputs.PrivateSubnet2AID}" 344 | PrivateSubnetDBIds: !Sub "${ProductionVpc.Outputs.PrivateSubnet1BID},${ProductionVpc.Outputs.PrivateSubnet2BID}" 345 | QSS3BucketName: !Ref QSS3BucketName 346 | QSS3BucketRegion: !Ref QSS3BucketRegion 347 | QSS3KeyPrefix: !Ref QSS3KeyPrefix 348 | QSTagKey: !Ref QSTagKey 349 | QSTagValue: !Sub ${QSTagValue}-${AWS::StackName} 350 | SourceCidr: !Ref SourceCidr 351 | SSLCertificateARN: !Ref SSLCertificateARN 352 | VpcCidr: !GetAtt ProductionVpc.Outputs.VPCCIDR 353 | VpcId: !GetAtt ProductionVpc.Outputs.VPCID 354 | Tags: 355 | - Key: !Ref QSTagKey 356 | Value: !Sub ${QSTagValue}-${AWS::StackName} 357 | 358 | Outputs: 359 | ManagementVpcId: 360 | Description: Management VPC ID 361 | Value: !GetAtt ManagementVpc.Outputs.VPCID 362 | ProductionVpcId: 363 | Description: Production VPC ID 364 | Value: !GetAtt ProductionVpc.Outputs.VPCID 365 | ApplicationURL: 366 | Description: Web App URL 367 | Value: !GetAtt Application.Outputs.ApplicationURL 368 | LandingPageURL: 369 | Description: Landing Page 370 | Value: !GetAtt Application.Outputs.LandingPageURL 371 | -------------------------------------------------------------------------------- /templates/application.template: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | Description: Provides nesting for required stacks to deploy a full sample web application 4 | with reverse proxy, ELBs, IAM, and other resources (for demonstration/POC/testing) (qs-1sflpub1k) 5 | Metadata: 6 | cfn-lint: 7 | config: 8 | ignore_checks: 9 | - W9006 10 | AWS::CloudFormation::Interface: 11 | ParameterGroups: 12 | - Label: 13 | default: Server DNS configuration 14 | Parameters: 15 | - AWSPublicFQDN 16 | - AWSHostedZoneID 17 | - SSLCertificateARN 18 | - Label: 19 | default: AWS Quick Start Configuration 20 | Parameters: 21 | - QSS3BucketName 22 | - QSS3BucketRegion 23 | - QSS3KeyPrefix 24 | - QSTagKey 25 | - QSTagValue 26 | - Label: 27 | default: Security Configuration 28 | Parameters: 29 | - DBPassword 30 | - KeyPairName 31 | - LogBucket 32 | - Label: 33 | default: Network Configuration 34 | Parameters: 35 | - BastionSecurityGroup 36 | - PublicSubnetIds 37 | - PrivateSubnetAppIds 38 | - PrivateSubnetDBIds 39 | - SourceCidr 40 | - VpcCidr 41 | - VpcId 42 | ParameterLabels: 43 | AWSHostedZoneID: 44 | default: DNS Hosted Zone ID 45 | AWSPublicFQDN: 46 | default: Full DNS Name for Web App 47 | BastionSecurityGroup: 48 | default: Bastion Host Security Group ID 49 | DBPassword: 50 | default: Database Password 51 | KeyPairName: 52 | default: EC2 Key Pair 53 | LogBucket: 54 | default: Log Bucket 55 | PublicSubnetIds: 56 | default: Public Subnet IDs 57 | PrivateSubnetAppIds: 58 | default: App Tier Private Subnet IDs 59 | PrivateSubnetDBIds: 60 | default: DB Tier Private Subnet IDs 61 | QSS3BucketName: 62 | default: Quick Start S3 Bucket Name 63 | QSS3BucketRegion: 64 | default: Quick Start S3 bucket region 65 | QSS3KeyPrefix: 66 | default: Quick Start S3 Key Prefix 67 | QSTagKey: 68 | default: Quick Start Tag key 69 | QSTagValue: 70 | default: Quick Start Tag value 71 | SSLCertificateARN: 72 | default: SSL Certificate ARN (Requires matching DNS name) 73 | SourceCidr: 74 | default: Source CIDR for Access 75 | VpcCidr: 76 | default: VPC CIDR Block 77 | VpcId: 78 | default: VPC ID 79 | Parameters: 80 | AWSHostedZoneID: 81 | Description: DNS Zone ID to contain the server's DNS entry. 82 | Type: String 83 | AWSPublicFQDN: 84 | Description: Web app will be reachable at this address. 85 | Type: String 86 | BastionSecurityGroup: 87 | Type: AWS::EC2::SecurityGroup::Id 88 | Description: EC2 Security Group for the bastion host. 89 | DBPassword: 90 | Description: Mixed alphanumeric and must be between 8 and 28 characters and contain 91 | at least one capital letter 92 | NoEcho: true 93 | Type: String 94 | MinLength: 8 95 | MaxLength: 28 96 | AllowedPattern: '[a-zA-Z0-9!^*\-_+]*' 97 | ConstraintDescription: Can only contain alphanumeric characters or the following 98 | special characters !^*-_+, between 8 and 28 characters 99 | KeyPairName: 100 | Type: AWS::EC2::KeyPair::KeyName 101 | Description: Name of an existing EC2 KeyPair to enable SSH access to app servers 102 | ConstraintDescription: Must be the name of an existing EC2 Key Pair 103 | LogBucket: 104 | Type: String 105 | Description: Bucket to write logs to 106 | PublicSubnetIds: 107 | Type: List 108 | Description: Public subnets in your VPC 109 | PrivateSubnetAppIds: 110 | Description: Private Subnet Ids to launch App Servers in 111 | Type: List 112 | PrivateSubnetDBIds: 113 | Description: Private Subnet Ids to launch DB Servers in 114 | Type: List 115 | QSS3BucketName: 116 | AllowedPattern: "^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$" 117 | ConstraintDescription: Quick Start bucket name can include numbers, lowercase 118 | letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen 119 | (-). 120 | Default: aws-quickstart 121 | Description: S3 bucket name for the Quick Start assets. This string can include 122 | numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start 123 | or end with a hyphen (-). 124 | Type: String 125 | QSS3BucketRegion: 126 | Default: 'us-east-1' 127 | Description: 'The AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted. When using your own bucket, you must specify this value.' 128 | Type: String 129 | QSS3KeyPrefix: 130 | AllowedPattern: "^[0-9a-zA-Z-/]*$" 131 | ConstraintDescription: Quick Start key prefix can include numbers, lowercase letters, 132 | uppercase letters, hyphens (-), and forward slash (/). 133 | Default: quickstart-hitrust-csf 134 | Description: S3 key prefix for the Quick Start assets. Quick Start key prefix 135 | can include numbers, lowercase letters, uppercase letters, hyphens (-), and 136 | forward slash (/). 137 | Type: String 138 | QSTagKey: 139 | Type: String 140 | Description: Tag key to identify resources from this Quick Start. 141 | QSTagValue: 142 | Type: String 143 | Description: Tag value to identify resources from this Quick Start. 144 | SourceCidr: 145 | 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]))$" 146 | ConstraintDescription: Must be a valid IP CIDR range of the form x.x.x.x/x. 147 | Description: IP address/range to allow access from 148 | Type: String 149 | SSLCertificateARN: 150 | Type: String 151 | Default: "" 152 | Description: The Amazon Resource Name for the existing SSL cert you wish to use; empty for none 153 | VpcCidr: 154 | 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]))$" 155 | ConstraintDescription: Must be a valid IP CIDR range of the form x.x.x.x/x. 156 | Type: String 157 | Description: VPC CIDR 158 | VpcId: 159 | Type: AWS::EC2::VPC::Id 160 | Description: Amazon VPC ID 161 | Mappings: 162 | # Storage: 163 | # Defaults: 164 | # ArchiveTier: GLACIER 165 | # WebContent: 166 | # LogPrefix: web-content/ 167 | Elb: 168 | Defaults: 169 | HealthCheckGracePeriod: 300 170 | Database: 171 | Defaults: 172 | User: HitrustExampleDbUser 173 | Name: HiTrustWordpressDB 174 | InstanceClass: 175 | GovCloud: db.t2.small 176 | NonGovCloud: db.t3.small 177 | Ec2: 178 | Defaults: 179 | InstanceSize: t3.small 180 | AWSAMIRegionMap: 181 | AMI: 182 | AMZNLINUX2HVM: amzn2-ami-hvm-2.0.20211103.0-x86_64-gp2 183 | # ap-east-1: 184 | # AMZNLINUX2HVM: ami-570c7726 185 | ap-northeast-1: 186 | AMZNLINUX2HVM: ami-0e60b6d05dc38ff11 187 | ap-northeast-2: 188 | AMZNLINUX2HVM: ami-0a5a6128e65676ebb 189 | ap-south-1: 190 | AMZNLINUX2HVM: ami-0f1fb91a596abf28d 191 | ap-southeast-1: 192 | AMZNLINUX2HVM: ami-024221a59c9020e72 193 | ap-southeast-2: 194 | AMZNLINUX2HVM: ami-043e0add5c8665836 195 | ca-central-1: 196 | AMZNLINUX2HVM: ami-0730e12069ab20c26 197 | eu-central-1: 198 | AMZNLINUX2HVM: ami-030e490c34394591b 199 | eu-north-1: 200 | AMZNLINUX2HVM: ami-08b0de3847e24ff84 201 | eu-west-1: 202 | AMZNLINUX2HVM: ami-09d4a659cdd8677be 203 | eu-west-2: 204 | AMZNLINUX2HVM: ami-0fc15d50d39e4503c 205 | eu-west-3: 206 | AMZNLINUX2HVM: ami-05f0a049e7aeb407c 207 | sa-east-1: 208 | AMZNLINUX2HVM: ami-03a343b9045171cec 209 | us-east-1: 210 | AMZNLINUX2HVM: ami-04ad2567c9e3d7893 211 | us-east-2: 212 | AMZNLINUX2HVM: ami-0dd0ccab7e2801812 213 | # us-gov-east-1: 214 | # AMZNLINUX2HVM: ami-a2d938d3 215 | # us-gov-west-1: 216 | # AMZNLINUX2HVM: ami-e9a9d388 217 | us-west-1: 218 | AMZNLINUX2HVM: ami-0074ef78ecb07948c 219 | us-west-2: 220 | AMZNLINUX2HVM: ami-00be885d550dcee43 221 | 222 | Conditions: 223 | GovCloudCondition: !Or [!Equals [!Ref "AWS::Region", 'us-gov-west-1'], !Equals [!Ref "AWS::Region", 'us-gov-east-1']] 224 | GovCloudEastCondition: !Equals [!Ref "AWS::Region", 'us-gov-east-1'] 225 | GovCloudWestCondition: !Equals [!Ref "AWS::Region", 'us-gov-west-1'] 226 | UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-quickstart'] 227 | 228 | Resources: 229 | # Create the Elb 230 | ElbStack: 231 | Type: AWS::CloudFormation::Stack 232 | Properties: 233 | TemplateURL: 234 | !Sub 235 | - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}templates/elb.template 236 | - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] 237 | S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 238 | Parameters: 239 | AWSHostedZoneID: !Ref AWSHostedZoneID 240 | AWSPublicFQDN: !Ref AWSPublicFQDN 241 | LogBucket: !Ref LogBucket 242 | PublicSubnetIds: !Join [',', !Ref PublicSubnetIds] 243 | QSS3BucketName: !Ref QSS3BucketName 244 | QSS3BucketRegion: !Ref QSS3BucketRegion 245 | QSS3KeyPrefix: !Ref QSS3KeyPrefix 246 | QSTagKey: !Ref QSTagKey 247 | QSTagValue: !Ref QSTagValue 248 | SourceCIDR: !Ref SourceCidr 249 | SSLCertificateARN: !Ref SSLCertificateARN 250 | VpcCidr: !Ref VpcCidr 251 | VpcId: !Ref VpcId 252 | Tags: 253 | - Key: !Ref QSTagKey 254 | Value: !Ref QSTagValue 255 | 256 | AppInstanceSecurityGroup: 257 | Type: AWS::EC2::SecurityGroup 258 | Properties: 259 | GroupDescription: Enable HTTPS from the load balancer only 260 | SecurityGroupIngress: 261 | - IpProtocol: tcp 262 | FromPort: 443 263 | ToPort: 443 264 | SourceSecurityGroupId: !GetAtt ElbStack.Outputs.ElbSecurityGroup 265 | - IpProtocol: tcp 266 | FromPort: 22 267 | ToPort: 22 268 | SourceSecurityGroupId: !Ref BastionSecurityGroup 269 | Tags: 270 | - Key: !Ref QSTagKey 271 | Value: !Ref QSTagValue 272 | VpcId: !Ref VpcId 273 | 274 | # Configure Database 275 | AppDatabase: 276 | Type: AWS::CloudFormation::Stack 277 | Properties: 278 | TemplateURL: 279 | !Sub 280 | - https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}templates/database.template 281 | - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] 282 | S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 283 | Parameters: 284 | DBInstanceClass: !If [GovCloudCondition, !FindInMap [Database, InstanceClass, GovCloud], !FindInMap [Database, InstanceClass, NonGovCloud]] 285 | DBMasterUser: !FindInMap [Database, Defaults, User] 286 | DBName: !FindInMap [Database, Defaults, Name] 287 | DBPassword: !Ref DBPassword 288 | DBPrivateSubnets: !Join [",", !Ref PrivateSubnetDBIds] 289 | QSTagKey: !Ref QSTagKey 290 | QSTagValue: !Ref QSTagValue 291 | SecurityGroupAppInstance: !Ref AppInstanceSecurityGroup 292 | VpcId: !Ref VpcId 293 | Tags: 294 | - Key: !Ref QSTagKey 295 | Value: !Ref QSTagValue 296 | 297 | AppInstanceRole: 298 | Type: AWS::IAM::Role 299 | Properties: 300 | AssumeRolePolicyDocument: 301 | Version: 2012-10-17 302 | Statement: 303 | - Effect: Allow 304 | Principal: 305 | Service: 306 | - ec2.amazonaws.com 307 | Action: 308 | - sts:AssumeRole 309 | Path: / 310 | Policies: 311 | - PolicyName: CreateTags 312 | PolicyDocument: 313 | Version: 2012-10-17 314 | Statement: 315 | - Effect: Allow 316 | Action: 317 | - ec2:Describe* 318 | - ec2:CreateTags 319 | Resource: "*" 320 | - PolicyName: GetObj 321 | PolicyDocument: 322 | Version: 2012-10-17 323 | Statement: 324 | - Effect: Allow 325 | Action: 326 | - s3:GetObject 327 | Resource: !Sub 328 | - arn:${AWS::Partition}:s3:::${S3Bucket}/${QSS3KeyPrefix}* 329 | - S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 330 | AppInstanceProfile: 331 | Type: AWS::IAM::InstanceProfile 332 | Properties: 333 | Roles: 334 | - !Ref AppInstanceRole 335 | # Launch configuration 336 | AppLaunchConfig: 337 | Type: AWS::AutoScaling::LaunchConfiguration 338 | DependsOn: 339 | - AppDatabase 340 | Metadata: 341 | AWS::CloudFormation::Init: 342 | configSets: 343 | wordpress_install: 344 | - install_cfn 345 | - install_wordpress 346 | install_cfn: 347 | files: 348 | /etc/cfn/cfn-hup.conf: 349 | content: !Sub | 350 | [main] 351 | stack=${AWS::StackId} 352 | region=${AWS::Region} 353 | mode: '000400' 354 | owner: root 355 | group: root 356 | /etc/cfn/hooks.d/cfn-auto-reloader.conf: 357 | content: !Sub | 358 | [cfn-auto-reloader-hook] 359 | triggers=post.update 360 | path=Resources.AppLaunchConfig.Metadata.AWS::CloudFormation::Init 361 | action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource AppLaunchConfig --configsets wordpress_install --region ${AWS::Region} 362 | mode: '000400' 363 | owner: root 364 | group: root 365 | services: 366 | sysvinit: 367 | cfn-hup: 368 | enabled: true 369 | ensureRunning: true 370 | files: 371 | - /etc/cfn/cfn-hup.conf 372 | - /etc/cfn/hooks.d/cfn-auto-reloader.conf 373 | install_wordpress: 374 | packages: 375 | yum: 376 | mariadb: [] 377 | httpd: [] 378 | sources: 379 | /var/www/html: https://wordpress.org/latest.tar.gz 380 | files: 381 | /tmp/httpd/ssl.conf: 382 | content: | 383 | Listen 443 384 | 385 | DocumentRoot /var/www/html 386 | LoadModule ssl_module /etc/httpd/modules/mod_ssl.so 387 | SSLEngine on 388 | SSLCertificateFile /etc/ssl/certs/common.crt 389 | SSLCertificateKeyFile /etc/ssl/private/common.key 390 | 391 | mode: '000755' 392 | owner: root 393 | group: root 394 | /var/www/html/wordpress/wp-config.php: 395 | content: !Sub 396 | - | 397 | > /home/ec2-user/tmp.log 457 | else 458 | echo "Creating /etc/ssl/private directory" >> /home/ec2-user/tmp.log 459 | mkdir /etc/ssl/private 460 | fi 461 | openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/common.key -out /etc/ssl/certs/common.crt -subj "/C=US/ST=Washington/L=Seattle/O=NonProductionTestCert/CN=NonProductionTestCert" 462 | 463 | /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource AppLaunchConfig --configsets wordpress_install --region ${AWS::Region} 464 | 465 | cp /tmp/httpd/ssl.conf /etc/httpd/conf.d/ssl.conf 466 | service httpd restart 467 | 468 | ###################################################################### 469 | # NOTE: UPDATE THESE VALUES ACCORDING TO THE COMPLIANCE BODY # 470 | ###################################################################### 471 | LANDING_PAGE="/var/www/html/landing.html" 472 | COMPLIANCE_BODY_LABEL="HITRUST" 473 | COMPLIANCE_SURVEY_LINK="hitrust" 474 | COMPLIANCE_MATRIX_FILENAME="HITRUST-CSF-Security-Controls-Mapping.xlsx" 475 | ###################################################################### 476 | 477 | # Download the landing page. 478 | aws s3 cp s3://${QSS3BucketName}/${QSS3KeyPrefix}assets/landing/landing.html $LANDING_PAGE 479 | 480 | # Replace relative image links with links to the production S3 source. 481 | sed -i 's|images|https://${QSS3BucketName}.${QSS3Region}.amazonaws.com/${QSS3KeyPrefix}assets/landing/images|g' $LANDING_PAGE 482 | # Inject the landing page branding label. 483 | sed -i "s|{compliance-body}|$COMPLIANCE_BODY_LABEL|g" $LANDING_PAGE 484 | # Inject the survey link parameter. 485 | sed -i "s|{compliance-body-survey-link}|$COMPLIANCE_SURVEY_LINK|g" $LANDING_PAGE 486 | # Inject the security control matrix file location. 487 | sed -i "s|{compliance-body-matrix}|https://${QSS3BucketName}.${QSS3Region}.amazonaws.com/${QSS3KeyPrefix}assets/$COMPLIANCE_MATRIX_FILENAME|g" $LANDING_PAGE 488 | /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource AppASG --region ${AWS::Region} 489 | 490 | - QSS3Region: !If [ GovCloudEastCondition, s3-us-gov-east-1, !If [ GovCloudWestCondition, s3-us-gov-west-1, s3 ] ] 491 | 492 | AppASG: 493 | Type: AWS::AutoScaling::AutoScalingGroup 494 | Properties: 495 | HealthCheckGracePeriod: !FindInMap [ Elb, Defaults, HealthCheckGracePeriod ] 496 | HealthCheckType: ELB 497 | MinSize: '2' 498 | MaxSize: '4' 499 | Tags: 500 | - Key: !Ref QSTagKey 501 | Value: !Ref QSTagValue 502 | PropagateAtLaunch: true 503 | LaunchConfigurationName: !Ref AppLaunchConfig 504 | TargetGroupARNs: 505 | - !GetAtt ElbStack.Outputs.ElbTargetGroupHTTPS 506 | VPCZoneIdentifier: !Ref PrivateSubnetAppIds 507 | CreationPolicy: 508 | ResourceSignal: 509 | Count: 1 510 | Timeout: PT15M 511 | 512 | AutoScalingUpApp: 513 | Type: AWS::AutoScaling::ScalingPolicy 514 | Properties: 515 | AdjustmentType: ChangeInCapacity 516 | AutoScalingGroupName: !Ref AppASG 517 | Cooldown: '1' 518 | ScalingAdjustment: 1 519 | AutoScalingDownApp: 520 | Type: AWS::AutoScaling::ScalingPolicy 521 | Properties: 522 | AdjustmentType: ChangeInCapacity 523 | AutoScalingGroupName: !Ref AppASG 524 | Cooldown: '1' 525 | ScalingAdjustment: -1 526 | CWAlarmHighCPUApp: 527 | Type: AWS::CloudWatch::Alarm 528 | Properties: 529 | EvaluationPeriods: 1 530 | Statistic: Average 531 | Threshold: 50 532 | AlarmDescription: Alarm if CPU too high or metric disappears indicating instance 533 | is down 534 | Period: 60 535 | AlarmActions: 536 | - !Ref AutoScalingDownApp 537 | Namespace: AWS/EC2 538 | Dimensions: 539 | - Name: AutoScalingGroupName 540 | Value: !Ref AppASG 541 | ComparisonOperator: GreaterThanThreshold 542 | MetricName: CPUUtilization 543 | CWAlarmLowCPUApp: 544 | Type: AWS::CloudWatch::Alarm 545 | Properties: 546 | EvaluationPeriods: 1 547 | Statistic: Average 548 | Threshold: 10 549 | AlarmDescription: Alarm if CPU too low, remove an app server 550 | Period: 60 551 | AlarmActions: 552 | - !Ref AutoScalingDownApp 553 | Namespace: AWS/EC2 554 | Dimensions: 555 | - Name: AutoScalingGroupName 556 | Value: !Ref AppASG 557 | ComparisonOperator: LessThanThreshold 558 | MetricName: CPUUtilization 559 | 560 | Outputs: 561 | LoadBalancerDNSName: 562 | Value: !GetAtt ElbStack.Outputs.ElbDns 563 | Description: DNS Name for the load balancer 564 | RecordSet: 565 | Value: !GetAtt ElbStack.Outputs.RecordSet 566 | Description: Domain Name of the Record Set 567 | ApplicationURL: 568 | Description: Public DNS name to reach cluster 569 | Value: !Sub https://${AWSPublicFQDN} 570 | LandingPageURL: 571 | Value: !Sub https://${AWSPublicFQDN}/landing.html 572 | Description: Landing Page 573 | -------------------------------------------------------------------------------- /templates/config-rules.template: -------------------------------------------------------------------------------- 1 | # Config Rules that are included. 2 | # N.B. These are examples and are not meant to be exhaustive. 3 | # Please consult your legal and compliance teams to determine 4 | # the configuration management scope you wish to capture. 5 | 6 | Description: Config rules that are included. (qs-1sflpub3u) 7 | Metadata: 8 | cfn-lint: 9 | config: 10 | ignore_checks: 11 | - W9002 12 | - W9003 13 | - W9006 14 | Parameters: 15 | LogBucket: 16 | Type: String 17 | Description: Log bucket 18 | QSTagKey: 19 | Type: String 20 | Description: Tag key to identify resources from this Quick Start 21 | QSTagValue: 22 | Type: String 23 | Description: Tag value to identify resources from this Quick Start 24 | 25 | Mappings: 26 | Config: 27 | Defaults: 28 | MaximumExecutionFrequency: One_Hour 29 | 30 | Resources: 31 | # IAM role for describing configurations in custom resources 32 | LambdaConfigRole: 33 | Type: AWS::IAM::Role 34 | Properties: 35 | AssumeRolePolicyDocument: 36 | Version: 2012-10-17 37 | Statement: 38 | - Effect: Allow 39 | Principal: 40 | Service: 41 | - "lambda.amazonaws.com" 42 | Action: 43 | - "sts:AssumeRole" 44 | ManagedPolicyArns: 45 | - !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSConfigRulesExecutionRole' 46 | - !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSConfigRole' 47 | Policies: 48 | - PolicyName: DescribeForConfig 49 | PolicyDocument: 50 | Version: 2012-10-17 51 | Statement: 52 | - Effect: Allow 53 | Action: 54 | - elasticloadbalancing:DescribeLoadBalancers 55 | - elasticloadbalancing:DescribeListeners 56 | - elasticloadbalancing:DescribeTags 57 | - ec2:DescribeSecurityGroups 58 | - logs:CreateLogGroup 59 | - logs:CreateLogStream 60 | - logs:PutLogEvents 61 | Resource: "*" 62 | 63 | # Config: Recorder enabled 64 | TriggerConfigRecorderEnabled: 65 | Type: "AWS::Lambda::Permission" 66 | Properties: 67 | Action: lambda:InvokeFunction 68 | FunctionName: !GetAtt LambdaConfigRecorderEnabled.Arn 69 | Principal: config.amazonaws.com 70 | RuleConfigRecorderEnabled: 71 | Type: AWS::Config::ConfigRule 72 | DependsOn: TriggerConfigRecorderEnabled 73 | Properties: 74 | Description: Checks if AWS Config recorder is turned on. N.B. This current rule does not check for S3 or SNS delivery mechanisms. 75 | Source: 76 | Owner: CUSTOM_LAMBDA 77 | SourceIdentifier: !GetAtt LambdaConfigRecorderEnabled.Arn 78 | SourceDetails: 79 | - EventSource: aws.config 80 | MaximumExecutionFrequency: !FindInMap [ Config, Defaults, MaximumExecutionFrequency ] 81 | MessageType: ScheduledNotification 82 | LambdaConfigRecorderEnabled: 83 | Type: AWS::Lambda::Function 84 | Properties: 85 | Description: Checks if Config Recorder is enabled 86 | Handler: index.handler 87 | Runtime: python3.7 88 | Role: !GetAtt LambdaConfigRole.Arn 89 | Timeout: 30 90 | Tags: 91 | - Key: !Ref QSTagKey 92 | Value: !Ref QSTagValue 93 | Code: 94 | ZipFile: | 95 | import boto3 96 | import json 97 | from datetime import datetime 98 | config = boto3.client('config') 99 | def evaluate_compliance(rule_parameters): 100 | # First check configuration recorder is created 101 | config_recorder_response = config.describe_configuration_recorder_status() 102 | if 'ConfigurationRecordersStatus' not in config_recorder_response or len(config_recorder_response['ConfigurationRecordersStatus']) < 1: 103 | return { 104 | 'compliance_type': 'NON_COMPLIANT', 105 | 'annotation': 'Cannot find config recorder status' 106 | } 107 | for config_recorder in config_recorder_response['ConfigurationRecordersStatus']: 108 | if not config_recorder['recording']: 109 | return { 110 | 'compliance_type': 'NON_COMPLIANT', 111 | 'annotation': 'Config recorder is not recording' 112 | } 113 | # Check that there are delivery channels and that they're mapping to the appropriate buckets 114 | delivery_channels_response = config.describe_delivery_channels() 115 | if 'DeliveryChannels' not in delivery_channels_response or len(delivery_channels_response['DeliveryChannels']) < 1: 116 | return { 117 | 'compliance_type': 'NON_COMPLIANT', 118 | 'annotation': 'No delivery channel for config recorder' 119 | } 120 | return { 121 | 'compliance_type': 'COMPLIANT', 122 | 'annotation': 'Config recorder enabled with appropriate delivery channel' 123 | } 124 | def handler(event, context): 125 | today = datetime.today() 126 | rule_parameters = json.loads(event['ruleParameters']) if 'ruleParameters' in event else {} 127 | evaluation = evaluate_compliance(rule_parameters) 128 | result_token = event['resultToken'] if 'resultToken' in event else 'No token found' 129 | config.put_evaluations( 130 | Evaluations=[ 131 | { 132 | 'ComplianceResourceType': 'AWS::::Account', 133 | 'ComplianceResourceId': event['accountId'], 134 | 'ComplianceType': evaluation['compliance_type'], 135 | 'Annotation': evaluation['annotation'], 136 | 'OrderingTimestamp': datetime(today.year, today.month, today.day, today.hour) 137 | } 138 | ], 139 | ResultToken=result_token 140 | ) 141 | 142 | # CloudTrail: Check Enabled 143 | CloudTrailEnabled: 144 | Type: AWS::Config::ConfigRule 145 | Properties: 146 | Description: Checks whether AWS CloudTrail is enabled in your AWS account 147 | MaximumExecutionFrequency: !FindInMap [ Config, Defaults, MaximumExecutionFrequency ] 148 | Source: 149 | Owner: AWS 150 | SourceIdentifier: CLOUD_TRAIL_ENABLED 151 | 152 | # CloudTrail: Log files validated 153 | CloudTrailLogFileValidation: 154 | Type: AWS::Config::ConfigRule 155 | Properties: 156 | Description: Checks whether AWS CloudTrail creates a signed digest file with logs. AWS recommends that the file validation must be enabled on all trails. The rule is noncompliant if the validation is not enabled. 157 | MaximumExecutionFrequency: !FindInMap [ Config, Defaults, MaximumExecutionFrequency ] 158 | Source: 159 | Owner: AWS 160 | SourceIdentifier: CLOUD_TRAIL_LOG_FILE_VALIDATION_ENABLED 161 | 162 | # EBS: Volumes encrypted 163 | EbsEncryption: 164 | Type: AWS::Config::ConfigRule 165 | Properties: 166 | Description: Checks whether EBS volumes that are in an attached state are encrypted. 167 | Scope: 168 | ComplianceResourceTypes: 169 | - AWS::EC2::Volume 170 | Source: 171 | Owner: AWS 172 | SourceIdentifier: ENCRYPTED_VOLUMES 173 | 174 | # ELB: Logging enabled to log bucket 175 | ElbLogging: 176 | Type: AWS::Config::ConfigRule 177 | Properties: 178 | Description: Checks whether the Application Load Balancers and the Classic Load Balancers have logging enabled. 179 | InputParameters: 180 | s3BucketNames: !Ref LogBucket 181 | Scope: 182 | ComplianceResourceTypes: 183 | - AWS::ElasticLoadBalancing::LoadBalancer 184 | - AWS::ElasticLoadBalancingV2::LoadBalancer 185 | Source: 186 | Owner: AWS 187 | SourceIdentifier: ELB_LOGGING_ENABLED 188 | 189 | # ELB: Encryption enabled (443) 190 | TriggerALBEncryption: 191 | Type: "AWS::Lambda::Permission" 192 | Properties: 193 | Action: lambda:InvokeFunction 194 | FunctionName: !GetAtt LambdaALBEncryption.Arn 195 | Principal: config.amazonaws.com 196 | ALBEncryption: 197 | Type: AWS::Config::ConfigRule 198 | DependsOn: TriggerALBEncryption 199 | Properties: 200 | Description: Checks to ensure that all application load balancers are only reachable via HTTPS endpoints and not HTTP as well as have a cert on the listener 201 | InputParameters: 202 | DesiredPort: 443 203 | DesiredProtocol: HTTPS 204 | TagKey: !Ref QSTagKey 205 | TagValue: !Ref QSTagValue 206 | Scope: 207 | ComplianceResourceTypes: 208 | - AWS::ElasticLoadBalancingV2::LoadBalancer 209 | Source: 210 | Owner: CUSTOM_LAMBDA 211 | SourceIdentifier: !GetAtt LambdaALBEncryption.Arn 212 | SourceDetails: 213 | - EventSource: aws.config 214 | MessageType: ConfigurationItemChangeNotification 215 | LambdaALBEncryption: 216 | Type: AWS::Lambda::Function 217 | Properties: 218 | Description: Checks if ALB only allows HTTPS 219 | Handler: index.handler 220 | Runtime: python3.6 221 | Role: !GetAtt LambdaConfigRole.Arn 222 | Timeout: 30 223 | Code: 224 | ZipFile: | 225 | from __future__ import print_function 226 | import json 227 | import boto3 228 | # Create boto3 clients 229 | config = boto3.client('config') 230 | elb = boto3.client('elbv2') 231 | def evaluate_compliance(configuration_item, rule_parameters): 232 | desired_port = rule_parameters['DesiredPort'] 233 | desired_protocol = rule_parameters['DesiredProtocol'] 234 | tag_key = rule_parameters['TagKey'] 235 | tag_value = rule_parameters['TagValue'] 236 | 237 | # First check if the load balancer is in scope (i.e. check tags) 238 | if tag_key not in configuration_item['tags'] or configuration_item['tags'][tag_key] != tag_value: 239 | return { 240 | 'compliance_type': 'NOT_APPLICABLE', 241 | 'annotation': 'Resource tags do not match desired tag. Out of scope' 242 | } 243 | 244 | load_balancer_arn = configuration_item['configuration']['loadBalancerArn'] 245 | 246 | # At this point we know in scope as the tag is in the ELB 247 | # Check for compliance - port and SSL certificate 248 | listeners_obj = elb.describe_listeners(LoadBalancerArn=load_balancer_arn) 249 | 250 | for listener in listeners_obj['Listeners']: 251 | if desired_protocol != listener['Protocol']: 252 | return { 253 | 'compliance_type': 'NON_COMPLIANT', 254 | 'annotation': 'Insecure %s protocol being used for the load balancer' % listener['Protocol'] 255 | } 256 | if int(desired_port) != listener['Port']: 257 | return { 258 | 'compliance_type': 'NON_COMPLIANT', 259 | 'annotation': '%s port being used for the load balancer rather than %s' % (listener['Port'], desired_port) 260 | } 261 | if len(listener['Certificates']) < 1: 262 | return { 263 | 'compliance_type': 'NON_COMPLIANT', 264 | 'annotation': 'Does not have a SSL Cert installed' 265 | } 266 | for cert in listener['Certificates']: 267 | if 'CertificateArn' not in cert: 268 | return { 269 | 'compliance_type': 'NON_COMPLIANT', 270 | 'annotation': 'Invalid SSL Cert installed - no ARN found' 271 | } 272 | return { 273 | 'compliance_type': 'COMPLIANT', 274 | 'annotation': 'Load balancer is secure' 275 | } 276 | def handler(event, context): 277 | invoking_event = json.loads(event['invokingEvent']) 278 | rule_parameters = json.loads(event['ruleParameters']) 279 | configuration_item = invoking_event['configurationItem'] 280 | # Check for compliance 281 | evaluation = evaluate_compliance(configuration_item, rule_parameters) 282 | result_token = event['resultToken'] if 'resultToken' in event else 'No token found' 283 | # Post result 284 | config.put_evaluations( 285 | Evaluations=[ 286 | { 287 | 'ComplianceResourceType': configuration_item['resourceType'], 288 | 'ComplianceResourceId': configuration_item['resourceId'], 289 | 'ComplianceType': evaluation['compliance_type'], 290 | 'Annotation': evaluation['annotation'], 291 | 'OrderingTimestamp': configuration_item['configurationItemCaptureTime'] 292 | }, 293 | ], 294 | ResultToken=result_token 295 | ) 296 | Tags: 297 | - Key: !Ref QSTagKey 298 | Value: !Ref QSTagValue 299 | 300 | # IAM: Disallow Admin in all policies 301 | IamAdminDisallow: 302 | Type: AWS::Config::ConfigRule 303 | Properties: 304 | Description: 'Checks whether the default version of AWS Identity and Access Management (IAM) policies do not have administrator access. If any statement has "Effect": "Allow" with "Action": "*" over "Resource": "*", the rule is NON_COMPLIANT.' 305 | Scope: 306 | ComplianceResourceTypes: 307 | - AWS::IAM::Policy 308 | Source: 309 | Owner: AWS 310 | SourceIdentifier: IAM_POLICY_NO_STATEMENTS_WITH_ADMIN_ACCESS 311 | 312 | # IAM: Require IAM Users to have MFA 313 | IamMfa: 314 | Type: AWS::Config::ConfigRule 315 | Properties: 316 | Description: Checks whether the AWS Identity and Access Management users have multi-factor authentication (MFA) enabled. 317 | MaximumExecutionFrequency: !FindInMap [ Config, Defaults, MaximumExecutionFrequency ] 318 | Source: 319 | Owner: AWS 320 | SourceIdentifier: IAM_USER_MFA_ENABLED 321 | 322 | # RDS: Backups enabled 323 | RdsBackup: 324 | Type: AWS::Config::ConfigRule 325 | Properties: 326 | Description: Checks whether RDS DB instances have backups enabled. 327 | Scope: 328 | ComplianceResourceTypes: 329 | - AWS::RDS::DBInstance 330 | Source: 331 | Owner: AWS 332 | SourceIdentifier: DB_INSTANCE_BACKUP_ENABLED 333 | 334 | # RDS: DB instances not public 335 | RdsNotPublic: 336 | Type: AWS::Config::ConfigRule 337 | Properties: 338 | Description: Checks whether the Amazon Relational Database Service (RDS) instances are not publicly accessible. The rule is non-compliant if the publiclyAccessible field is true in the instance configuration item. 339 | Scope: 340 | ComplianceResourceTypes: 341 | - AWS::RDS::DBInstance 342 | Source: 343 | Owner: AWS 344 | SourceIdentifier: RDS_INSTANCE_PUBLIC_ACCESS_CHECK 345 | 346 | # RDS: Multi-AZ enabled 347 | RdsMultiAZ: 348 | Type: AWS::Config::ConfigRule 349 | Properties: 350 | Description: Checks whether high availability is enabled for your RDS DB instances. 351 | Scope: 352 | ComplianceResourceTypes: 353 | - AWS::RDS::DBInstance 354 | Source: 355 | Owner: AWS 356 | SourceIdentifier: RDS_MULTI_AZ_SUPPORT 357 | 358 | # RDS: No public snapshots 359 | RdsSnapshotsPublic: 360 | Type: AWS::Config::ConfigRule 361 | Properties: 362 | Description: Checks if Amazon Relational Database Service (Amazon RDS) snapshots are public. The rule is non-compliant if any existing and new Amazon RDS snapshots are public. 363 | Scope: 364 | ComplianceResourceTypes: 365 | - AWS::RDS::DBSnapshot 366 | Source: 367 | Owner: AWS 368 | SourceIdentifier: RDS_SNAPSHOTS_PUBLIC_PROHIBITED 369 | 370 | # RDS: Encryption at-rest enabled 371 | RdsEncryptionAtRest: 372 | Type: AWS::Config::ConfigRule 373 | Properties: 374 | Description: Checks whether storage encryption is enabled for your RDS DB instances. 375 | Scope: 376 | ComplianceResourceTypes: 377 | - AWS::RDS::DBInstance 378 | Source: 379 | Owner: AWS 380 | SourceIdentifier: RDS_STORAGE_ENCRYPTED 381 | 382 | # S3: Bucket Logging enabled 383 | S3BucketLogging: 384 | Type: AWS::Config::ConfigRule 385 | Properties: 386 | Description: Checks whether logging is enabled for your S3 buckets. 387 | InputParameters: 388 | targetBucket: !Ref LogBucket 389 | Scope: 390 | ComplianceResourceTypes: 391 | - AWS::S3::Bucket 392 | Source: 393 | Owner: AWS 394 | SourceIdentifier: S3_BUCKET_LOGGING_ENABLED 395 | 396 | # S3: Bucket has SSE enabled 397 | S3BucketSSE: 398 | Type: AWS::Config::ConfigRule 399 | Properties: 400 | Description: Checks that your Amazon S3 bucket either has S3 default encryption enabled or that the S3 bucket policy explicitly denies put-object requests without server side encryption. 401 | Scope: 402 | ComplianceResourceTypes: 403 | - AWS::S3::Bucket 404 | Source: 405 | Owner: AWS 406 | SourceIdentifier: S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED 407 | 408 | # S3: Bucket enforces in-transit encryption 409 | S3BucketForceSSL: 410 | Type: AWS::Config::ConfigRule 411 | Properties: 412 | Description: Checks whether S3 buckets have policies that require requests to use Secure Socket Layer (SSL). 413 | Scope: 414 | ComplianceResourceTypes: 415 | - AWS::S3::Bucket 416 | Source: 417 | Owner: AWS 418 | SourceIdentifier: S3_BUCKET_SSL_REQUESTS_ONLY 419 | 420 | # S3: Disallow Public Read 421 | S3DisallowPublicRead: 422 | Type: AWS::Config::ConfigRule 423 | Properties: 424 | Description: Checks that your Amazon S3 buckets do not allow public read access. The rule checks the Block Public Access settings, the bucket policy, and the bucket access control list (ACL). 425 | MaximumExecutionFrequency: !FindInMap [ Config, Defaults, MaximumExecutionFrequency ] 426 | Scope: 427 | ComplianceResourceTypes: 428 | - AWS::S3::Bucket 429 | Source: 430 | Owner: AWS 431 | SourceIdentifier: S3_BUCKET_PUBLIC_READ_PROHIBITED 432 | 433 | # S3: Disallow Public Write 434 | S3DisallowPublicWrite: 435 | Type: AWS::Config::ConfigRule 436 | Properties: 437 | Description: Checks that your Amazon S3 buckets do not allow public write access. The rule checks the Block Public Access settings, the bucket policy, and the bucket access control list (ACL). 438 | MaximumExecutionFrequency: !FindInMap [ Config, Defaults, MaximumExecutionFrequency ] 439 | Scope: 440 | ComplianceResourceTypes: 441 | - AWS::S3::Bucket 442 | Source: 443 | Owner: AWS 444 | SourceIdentifier: S3_BUCKET_PUBLIC_READ_PROHIBITED 445 | 446 | # Security Groups: Block port 80 447 | TriggerSecurityGroupPortBlocked: 448 | Type: "AWS::Lambda::Permission" 449 | Properties: 450 | Action: lambda:InvokeFunction 451 | FunctionName: !GetAtt LambdaSecurityGroupPortBlocked.Arn 452 | Principal: config.amazonaws.com 453 | SecurityGroupPortBlocked: 454 | Type: AWS::Config::ConfigRule 455 | DependsOn: TriggerSecurityGroupPortBlocked 456 | Properties: 457 | Description: Checks to ensure security groups do not have a specific port open 458 | InputParameters: 459 | UndesiredPort: 80 460 | TagKey: !Ref QSTagKey 461 | TagValue: !Ref QSTagValue 462 | Scope: 463 | ComplianceResourceTypes: 464 | - AWS::EC2::SecurityGroup 465 | Source: 466 | Owner: CUSTOM_LAMBDA 467 | SourceIdentifier: !GetAtt LambdaSecurityGroupPortBlocked.Arn 468 | SourceDetails: 469 | - EventSource: aws.config 470 | MessageType: ConfigurationItemChangeNotification 471 | LambdaSecurityGroupPortBlocked: 472 | Type: AWS::Lambda::Function 473 | Properties: 474 | Description: Checks if Security Groups don't have a specific port open 475 | Handler: index.handler 476 | Runtime: python3.7 477 | Role: !GetAtt LambdaConfigRole.Arn 478 | Timeout: 30 479 | Code: 480 | ZipFile: | 481 | from __future__ import print_function 482 | import json 483 | import boto3 484 | config = boto3.client('config') 485 | ec2 = boto3.client('ec2') 486 | def evaluate_compliance(configuration_item, rule_parameters): 487 | undesired_port = int(rule_parameters['UndesiredPort']) 488 | tag_key = rule_parameters['TagKey'] 489 | tag_value = rule_parameters['TagValue'] 490 | configuration = configuration_item['configuration'] 491 | 492 | if tag_key not in configuration_item['tags'] or configuration_item['tags'][tag_key] != tag_value: 493 | return { 494 | 'compliance_type': 'NOT_APPLICABLE', 495 | 'annotation': 'Resource tags do not match desired tag. Out of scope' 496 | } 497 | 498 | for ip_permissions in configuration['ipPermissions']: 499 | if 'fromPort' not in ip_permissions and 'toPort' not in ip_permissions: 500 | return { 501 | 'compliance_type': 'NON_COMPLIANT', 502 | 'annotation': 'Security Group is open to all traffic so port %s is not blocked' % str(undesired_port) 503 | } 504 | if ip_permissions['fromPort'] <= undesired_port <= ip_permissions['toPort']: 505 | return { 506 | 'compliance_type': 'NON_COMPLIANT', 507 | 'annotation': 'Security Group port %s is not blocked' % str(undesired_port) 508 | } 509 | return { 510 | 'compliance_type': 'COMPLIANT', 511 | 'annotation': 'Port %s not open for ingress' % str(undesired_port) 512 | } 513 | def handler(event, context): 514 | invoking_event = json.loads(event['invokingEvent']) 515 | rule_parameters = json.loads(event['ruleParameters']) 516 | configuration_item = invoking_event['configurationItem'] 517 | evaluation = evaluate_compliance(configuration_item, rule_parameters) 518 | result_token = event['resultToken'] if 'resultToken' in event else 'No token found' 519 | config.put_evaluations( 520 | Evaluations=[ 521 | { 522 | 'ComplianceResourceType': configuration_item['resourceType'], 523 | 'ComplianceResourceId': configuration_item['resourceId'], 524 | 'ComplianceType': evaluation['compliance_type'], 525 | 'Annotation': evaluation['annotation'], 526 | 'OrderingTimestamp': configuration_item['configurationItemCaptureTime'] 527 | }, 528 | ], 529 | ResultToken=result_token 530 | ) 531 | Tags: 532 | - Key: !Ref QSTagKey 533 | Value: !Ref QSTagValue 534 | 535 | # Security Groups: Block unrestricted SSH 536 | RuleUnrestrictedSSH: 537 | Type: AWS::Config::ConfigRule 538 | Properties: 539 | Description: Checks whether security groups that are in use disallow unrestricted incoming SSH traffic. 540 | Scope: 541 | ComplianceResourceTypes: 542 | - AWS::EC2::SecurityGroup 543 | Source: 544 | Owner: AWS 545 | SourceIdentifier: INCOMING_SSH_DISABLED 546 | 547 | # Tagging enabled for Quick Start Resources 548 | RequiredTags: 549 | Type: AWS::Config::ConfigRule 550 | Properties: 551 | Description: Checks whether required tag are used. 552 | InputParameters: 553 | tag1Key: !Ref QSTagKey 554 | tag1Value: !Ref QSTagValue 555 | Scope: 556 | ComplianceResourceTypes: 557 | - AWS::EC2::Instance 558 | - AWS::ElasticLoadBalancingV2::LoadBalancer 559 | - AWS::RDS::DBInstance 560 | - AWS::RDS::DBSubnetGroup 561 | - AWS::S3::Bucket 562 | Source: 563 | Owner: AWS 564 | SourceIdentifier: REQUIRED_TAGS 565 | 566 | # VPC: Flow logs enabled 567 | VpcFlowLogs: 568 | Type: AWS::Config::ConfigRule 569 | Properties: 570 | Description: Checks whether Amazon Virtual Private Cloud flow logs are found and enabled for Amazon VPC. 571 | MaximumExecutionFrequency: !FindInMap [ Config, Defaults, MaximumExecutionFrequency ] 572 | Source: 573 | Owner: AWS 574 | SourceIdentifier: VPC_FLOW_LOGS_ENABLED 575 | 576 | --------------------------------------------------------------------------------