--- AWSTemplateFormatVersion: 2010-09-09 Description: "Atlassian Bitbucket Data Center (qs-1qup6ra1t)" Metadata: QSLint: Exclusions: [E9101, EIAMPolicyWildcardResource, ERDSDBInstancePubliclyAccessible] QuickStartDocumentation: EntrypointName: "Launch into an existing VPC" AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Bitbucket setup Parameters: - BitbucketVersion - Label: default: Cluster nodes Parameters: - CloudWatchIntegration - ClusterNodeInstanceType - ClusterNodeMax - ClusterNodeMin - ClusterNodeVolumeSize - DeploymentAutomationRepository - DeploymentAutomationBranch - DeploymentAutomationPlaybook - DeploymentAutomationKeyName - DeploymentAutomationCustomParams - Label: default: File server Parameters: - FileServerInstanceType - HomeSize - HomeVolumeType - HomeIops - Label: default: Database Parameters: - DBEngine - DBEngineVersion - DBInstanceClass - DBIops - DBMasterUserPassword - DBMultiAZ - DBPassword - DBStorage - DBStorageType - Label: default: Bastion host utilization Parameters: - BastionHostRequired - KeyPairName - Label: default: Elasticsearch Parameters: - ElasticSearchPassword - ElasticsearchInstanceType - ElasticsearchNodeVolumeSize - Label: default: Networking Parameters: - CidrBlock - InternetFacingLoadBalancer - CustomDnsName - SSLCertificateARN - Label: default: Advanced (Optional) Parameters: - BitbucketProperties - JvmHeapOverride - JvmSupportOpts - CreateBucket - DBMaster - DBSnapshotId - ESBucketName - HomeVolumeSnapshotId - HomeDeleteOnTermination - BitbucketLicenseKey - BitbucketAdminPassword - BitbucketDatasetURL - Label: default: AWS Quick Start Configuration Parameters: - QSS3BucketName - QSS3KeyPrefix - ExportPrefix ParameterLabels: BitbucketProperties: default: Bitbucket properties BitbucketVersion: default: Version * BitbucketLicenseKey: default: License Key for Bitbucket (if you have one) BitbucketAdminPassword: default: Password for the administrator account BitbucketDatasetURL: default: HTTP/HTTPS URL to download the Bitbucket Dataset CidrBlock: default: Permitted IP range * ClusterNodeMax: default: Maximum number of cluster nodes ClusterNodeMin: default: Minimum number of cluster nodes ClusterNodeInstanceType: default: Bitbucket cluster node instance type ClusterNodeVolumeSize: default: Cluster node instance volume size CreateBucket: default: Create S3 bucket for Elasticsearch snapshots DBEngine: default: The database engine to deploy with DBEngineVersion: default: The database engine version to use DBInstanceClass: default: Database instance class DBIops: default: RDS Provisioned IOPS DBMasterUserPassword: default: Master password * DBMaster: default: Bitbucket primary database DBMultiAZ: default: Enable RDS Multi-AZ deployment DBPassword: default: Bitbucket database password * DBStorage: default: Database storage DBStorageType: default: Database storage type DBSnapshotId: default: Database snapshot ID to restore DeploymentAutomationRepository: default: Deployment Automation Git Repository URL DeploymentAutomationBranch: default: Deployment Automation Branch DeploymentAutomationPlaybook: default: Ansible playbook to invoke during instance initialization DeploymentAutomationCustomParams: default: Custom command-line parameters for Ansible DeploymentAutomationKeyName: default: SSH key name to use with the repository CloudWatchIntegration: default: Enable CloudWatch integration ElasticSearchPassword: default: Elasticsearch master user password * ElasticsearchInstanceType: default: Elasticsearch instance type ElasticsearchNodeVolumeSize: default: Elasticsearch disk space per node (GB) ESBucketName: default: Elasticsearch snapshot S3 bucket name ExportPrefix: default: ASI identifier FileServerInstanceType: default: File server instance type HomeDeleteOnTermination: default: Delete Home on termination HomeIops: default: Home directory IOPS HomeSize: default: Home directory size HomeVolumeSnapshotId: default: Home volume snapshot ID to restore HomeVolumeType: default: Home directory volume type InternetFacingLoadBalancer: default: Make instance internet facing JvmHeapOverride: default: JVM Heap Size Override JvmSupportOpts: default: Additional JVM options BastionHostRequired: default: Use Bastion host KeyPairName: default: SSH Key Pair Name CustomDnsName: default: Existing DNS name SSLCertificateARN: default: SSL Certificate ARN QSS3BucketName: default: Quick Start S3 Bucket Name QSS3KeyPrefix: default: Quick Start S3 Key Prefix Parameters: BitbucketProperties: Default: '' Description: A space-separated list of bitbucket properties in the form 'key1=value1 key2=value2 ...' Find documentation at https://confluence.atlassian.com/x/m5ZKLg. Type: String BitbucketVersion: Default: "7.17.1" AllowedPattern: '([^1234]\.\d+\.\d+(-?.*))' ConstraintDescription: 'Must be a valid Bitbucket version number. For example: 5.0.0 or higher' Description: 'Version of Bitbucket to install. Please use version 5.0.0 or higher. Find valid versions at http://go.atlassian.com/bbs-releases.' Type: String BitbucketLicenseKey: Default: '' Description: (Optional) Provide a license key for Bitbucket Data Center if you have one. NoEcho: true Type: String BitbucketAdminPassword: AllowedPattern: '((?=^.{0,255}$)((?=.*\\d)(?=.*[A-Z])(?=.*[a-z])|(?=.*\\d)(?=.*[^A-Za-z0-9])(?=.*[a-z])|(?=.*[^A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z])|(?=.*\\d)(?=.*[A-Z])(?=.*[^A-Za-z0-9]))^.*)|(^$)' ConstraintDescription: Must be at least 8 characters and include 1 uppercase, 1 lowercase, 1 number, 1 (non / @ " ') symbol Description: (Optional) Password for the Bitbucket administrator ('admin') account. MaxLength: 128 NoEcho: true Type: String BitbucketDatasetURL: Default: '' Description: (Optional) Provide the HTTP/HTTPS URL for the dataset to restore. Refer https://confluence.atlassian.com/bitbucketserver/importing-957497836.html. Type: String CidrBlock: AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})' ConstraintDescription: Must be a valid IP CIDR range of the form x.x.x.x/x. Description: CIDR block allowed to access the Atlassian product. This should be set to a trusted IP range; if you want to give public access use '0.0.0.0/0'. MinLength: 9 MaxLength: 18 Type: String ClusterNodeInstanceType: Default: c5.xlarge AllowedValues: - c4.large - c4.xlarge - c4.2xlarge - c4.4xlarge - c4.8xlarge - c5.large - c5.xlarge - c5.2xlarge - c5.4xlarge - c5.9xlarge - c5.18xlarge - c5d.large - c5d.xlarge - c5d.2xlarge - c5d.4xlarge - c5d.9xlarge - c5d.18xlarge - d2.xlarge - d2.2xlarge - d2.4xlarge - d2.8xlarge - h1.2xlarge - h1.4xlarge - h1.8xlarge - h1.16xlarge - i3.large - i3.xlarge - i3.2xlarge - i3.4xlarge - i3.8xlarge - i3.16xlarge - i3.metal - m4.large - m4.xlarge - m4.2xlarge - m4.4xlarge - m4.10xlarge - m4.16xlarge - m5.large - m5.xlarge - m5.2xlarge - m5.4xlarge - m5.12xlarge - m5.24xlarge - m5d.large - m5d.xlarge - m5d.2xlarge - m5d.4xlarge - m5d.12xlarge - m5d.24xlarge - r4.large - r4.xlarge - r4.2xlarge - r4.4xlarge - r4.8xlarge - r4.16xlarge - r5.large - r5.xlarge - r5.2xlarge - r5.4xlarge - r5.12xlarge - r5.24xlarge - r5d.large - r5d.xlarge - r5d.2xlarge - r5d.4xlarge - r5d.12xlarge - r5d.24xlarge - t2.medium - t2.large - t2.xlarge - t2.2xlarge - t3.medium - t3.large - t3.xlarge - t3.2xlarge - x1.16xlarge - x1.32xlarge - x1e.xlarge - x1e.2xlarge - x1e.4xlarge - x1e.8xlarge - x1e.16xlarge - x1e.32xlarge - z1d.large - z1d.xlarge - z1d.2xlarge - z1d.3xlarge - z1d.6xlarge - z1d.12xlarge ConstraintDescription: Must be an EC2 instance type from the selection list Description: 'Instance type for the cluster application nodes. See https://confluence.atlassian.com/x/GpdKLg for guidance.' Type: String ClusterNodeMax: Description: Maximum number of nodes in the cluster. Default: 2 Type: Number ClusterNodeMin: Default: 1 Description: Set to 1 for new deployment. Can be updated post launch. Type: Number ClusterNodeVolumeSize: Default: 50 Description: Size of Bitbucket web server cluster node volume in Gb. Type: Number CreateBucket: Default: "true" AllowedValues: ["true", "false"] ConstraintDescription: Must be 'true' or 'false'. Description: Set to true to create the S3 bucket within this stack, must be used in conjunction with ESBucketName. Type: String DeploymentAutomationRepository: Default: 'https://bitbucket.org/atlassian/dc-deployments-automation.git' Type: String Description: The deployment automation repository to use for per-node initialization. Leave this as default unless you have customizations. DeploymentAutomationBranch: Default: "master" Type: String Description: The deployment automation repository branch to pull from. DeploymentAutomationPlaybook: Default: "aws_bitbucket_dc_node.yml" Type: String Description: The Ansible playbook to invoke to initialize the application node on first start. DeploymentAutomationCustomParams: Default: "" Type: String Description: Additional command-line options for the `ansible-playbook` command. See https://bitbucket.org/atlassian/dc-deployments-automation/src/master/README.md for more information about overriding parameters. (Optional) DeploymentAutomationKeyName: Default: "" Type: String Description: Named Key Pair name to use with this repository. The key should be imported into the SSM parameter store. (Optional) CloudWatchIntegration: Default: "Metrics and Logs" Type: String Description: "Enables CloudWatch metrics with or without log gathering. If cost is an issue, you can disable this altogether." AllowedValues: ["Off", "Metrics Only", "Metrics and Logs"] ConstraintDescription: "Must be 'Off', 'Metrics Only', or 'Metrics and Logs'" DBEngine: Default: 'PostgreSQL' Description: 'Database Engine to use. PostgreSQL or Amazon Aurora PostgreSQL.' AllowedValues: - 'Amazon Aurora PostgreSQL' - 'PostgreSQL' ConstraintDescription: Must be 'Amazon Aurora PostgreSQL' or 'PostgreSQL'. Type: String DBEngineVersion: Default: 12 AllowedValues: - 12 - 11 - 10 Description: "The database engine version to use; we'll install a suitable minor version for your chosen engine. Make sure that the Bitbucket version you're installing supports the database engine selected." Type: String DBInstanceClass: Default: db.m4.large AllowedValues: - db.m4.large - db.m4.xlarge - db.m4.2xlarge - db.m4.4xlarge - db.m4.10xlarge - db.m4.16xlarge - db.r4.large - db.r4.xlarge - db.r4.2xlarge - db.r4.4xlarge - db.r4.8xlarge - db.r4.16xlarge - db.r5.large - db.r5.xlarge - db.r5.2xlarge - db.r5.4xlarge - db.r5.12xlarge - db.r5.24xlarge - db.t2.medium - db.t2.large - db.t2.xlarge - db.t2.2xlarge - db.t3.medium - db.t3.large - db.t3.xlarge - db.t3.2xlarge ConstraintDescription: Must be a valid RDS instance class from the list Description: RDS instance type (only r4 and r5 families are supported for Aurora). Type: String DBIops: Default: 1000 ConstraintDescription: Must be in the range 1000 - 30000. Description: 'Must be in the range of 1000 - 30000 and a multiple of 1000. This value is only used with Provisioned IOPS. Note: The ratio of IOPS per allocated-storage must be between 3.00 and 10.00. Not valid for Aurora.' MinValue: 1000 MaxValue: 30000 Type: Number DBMasterUserPassword: AllowedPattern: >- ^(?=^.{8,255}$)(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9])(?!.*[@/"']).*$ ConstraintDescription: >- Must be at least 8 characters and include 1 uppercase, 1 lowercase, 1 number, and 1 of the following symbols: ! # $ { * : [ = , ] - _ + % & Description: "Password for the master ('postgres') account. Must be at least 8 characters and include 1 uppercase, 1 lowercase, 1 number, and 1 of the following symbols: ! # $ { * : [ = , ] - _ + % &" NoEcho: True MaxLength: 128 MinLength: 8 Type: String DBMultiAZ: Description: Whether to provision a multi-AZ RDS instance. Default: "true" AllowedValues: - "true" - "false" ConstraintDescription: Must be 'true' or 'false'. Type: String DBPassword: AllowedPattern: '(?=^.{6,255}$)((?=.*\\d)(?=.*[A-Z])(?=.*[a-z])|(?=.*\\d)(?=.*[^A-Za-z0-9])(?=.*[a-z])|(?=.*[^A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z])|(?=.*\\d)(?=.*[A-Z])(?=.*[^A-Za-z0-9]))^.*' ConstraintDescription: 'Must be at least 8 characters and include 1 uppercase, 1 lowercase, 1 number, and 1 of the following symbols: ! # $ { * : [ = , ] - _ @ + % &' Description: "Database password used by BitBucket. Must be at least 8 characters and include 1 uppercase, 1 lowercase, 1 number, and 1 of the following symbols: ! # $ { * : [ = , ] - _ @ + % &" MaxLength: 128 NoEcho: true Type: String DBMaster: Default: '' ConstraintDescription: Must be a valid RDS ARN. Description: Database ARN of the RDS instance to replicate. Setting this parameter will bring up Bitbucket as a Disaster recovery standby, with an RDS read replica database. Not valid for Aurora. Type: String DBSnapshotId: Default: '' ConstraintDescription: Must be a valid RDS snapshot ID, or blank. Description: RDS snapshot ID of an existing backup to restore. Must be used in conjunction with HomeVolumeSnapshotId. Leave blank for a new instance. Not valid for Aurora. Type: String DBStorage: Default: 100 Description: Database allocated storage size, in gigabytes (GB). If you choose Provisioned IOPS, storage should be between 100 and 6144. Not used for Aurora. MinValue: 5 MaxValue: 6144 Type: Number DBStorageType: Default: General Purpose (SSD) AllowedValues: - General Purpose (SSD) - Provisioned IOPS ConstraintDescription: Must be 'General Purpose (SSD)' or 'Provisioned IOPS'. Description: Database storage type. Type: String ElasticSearchPassword: Description: "Password for the elasticsearch master user, the username will be 'bitbucket'. If no password is specified, Bitbucket will authenticate with the elasticsearch cluster using IAM request signing (not recommended)." Type: String NoEcho: True ElasticsearchInstanceType: Description: EC2 instance type for the Amazon Elasticsearch service to run on. Type: String Default: m4.xlarge.elasticsearch AllowedValues: - m4.large.elasticsearch - m4.xlarge.elasticsearch - m4.2xlarge.elasticsearch - r4.large.elasticsearch - r4.xlarge.elasticsearch - r4.2xlarge.elasticsearch - r4.4xlarge.elasticsearch - r4.8xlarge.elasticsearch - i2.xlarge.elasticsearch - i2.2xlarge.elasticsearch ConstraintDescription: Must be an Elasticsearch instance type in the M4, R4 or I2 family ElasticsearchNodeVolumeSize: Description: The EBS volume size (in GB) of each Elasticsearch node. Type: Number Default: 100 MinValue: 100 ESBucketName: Default: '' AllowedPattern: '[a-z0-9-]*' ConstraintDescription: Must contain only lowercase letters, numbers and hyphens (-). Description: Name of a new, or existing, S3 bucket configured for Elasticsearch snapshots. Type: String ExportPrefix: Default: 'ATL-' Description: Each Atlassian Standard Infrastructure (ASI) uses a unique identifier. If you have multiple ASIs within the same AWS region, use this field to specify where to deploy Bitbucket. Type: String FileServerInstanceType: Default: m4.xlarge AllowedValues: - c4.xlarge - c4.2xlarge - c4.4xlarge - c4.8xlarge - m4.xlarge - m4.2xlarge - m4.4xlarge - m4.10xlarge - m4.16xlarge - x1.32xlarge ConstraintDescription: Must be an EC2 instance type in the C4, M4, or X1 family, 'xlarge' or larger. Description: Instance type for the file server hosting the Bitbucket shared home directory. See https://confluence.atlassian.com/x/GpdKLg for guidance. Type: String HomeDeleteOnTermination: Default: "true" AllowedValues: ["true", "false"] ConstraintDescription: Must be 'true' or 'false'. Description: Delete Bitbucket home directory volume when the file server instance is terminated. You must back up your data before terminating your file server instance if this option is set to 'true'. Type: String HomeIops: Default: 1000 Description: 'Home directory IOPS (100 - 20000, only used with Provisioned IOPS). Note: The ratio of IOPS provisioned to the volume size requested can be a maximum of 50; for example, a volume with 5000 IOPS must be at least 100 GiB.' Type: Number MinValue: 100 MaxValue: 20000 ConstraintDescription: Must be in the range 100 - 20000. HomeSize: Default: 100 Description: Home directory storage size, in gigabytes (GB) (100 - 16384). ConstraintDescription: Must be in the range 100 - 16384. MinValue: 100 MaxValue: 16384 Type: Number HomeVolumeSnapshotId: Default: '' ConstraintDescription: Must be a valid EBS snapshot ID Description: EBS snapshot ID of an existing backup to restore as the home directory volume. Must be used in conjunction with DBSnapshotId. Leave blank for a new instance. Type: String HomeVolumeType: Default: Provisioned IOPS Description: Bitbucket home storage type. AllowedValues: [General Purpose (SSD), Provisioned IOPS] ConstraintDescription: Must be 'General Purpose (SSD)' or 'Provisioned IOPS'. Type: String InternetFacingLoadBalancer: Default: "true" AllowedValues: ["true", "false"] ConstraintDescription: Must be 'true' or 'false'. Description: Controls whether the load balancer should be visible to the internet (true) or only within the VPC (false). Type: String JvmHeapOverride: Default: '' Description: Override the default amount of memory to allocate to the JVM for your instance type - set size in meg or gig e.g. 1024m or 1g. Type: String JvmSupportOpts: Default: '' Description: Pass in any additional JVM options to tune the Bitbucket instance. Type: String BastionHostRequired: Default: "true" AllowedValues: - "true" - "false" Description: Whether to grant access to BitBucket EC2 instances through the ASI's Bastion host (if it exists). If 'true', remember to provide an EC2 Key Pair. If your ASI does not have a Bastion host, set this to 'false'. Type: String KeyPairName: ConstraintDescription: Must be the name of an existing EC2 Key Pair. Note the supplied value must not include the file extension. Description: Public/private EC2 Key Pairs (without file extension) to allow you to securely access the Bastion host. Type: String Default: '' CustomDnsName: Default: "" Description: 'Use custom existing DNS name for your Data Center instance. Please note: you must own the domain and configure it to point at the load balancer.' Type: String SSLCertificateARN: Default: '' Description: "Amazon Resource Name (ARN) of your SSL certificate. If you want to use your own certificate that you generated outside of Amazon, you need to first import it to AWS Certificate Manager. After a successful import, you'll receive the ARN. If you want to create a certificate with AWS Certificate Manager (ACM certificate), you will receive the ARN after it's successfully created." MinLength: 0 MaxLength: 90 Type: String QSS3BucketName: Default: 'aws-quickstart' AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$ ConstraintDescription: Quick Start bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-). Description: S3 bucket name for the Quick Start assets. Quick Start bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-). Type: String QSS3KeyPrefix: Default: 'quickstart-atlassian-bitbucket/' AllowedPattern: ^[0-9a-zA-Z-/]*$ ConstraintDescription: Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/). Description: S3 key prefix for the Quick Start assets. Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/). Type: String Conditions: IsURLProvided: !Not [!Equals [!Ref BitbucketDatasetURL, '']] IsLicenseKeyProvided: !Not [!Equals [!Ref BitbucketLicenseKey, '']] IsBBAdminPasswordProvided: !Not [!Equals [!Ref BitbucketAdminPassword, '']] DBProvisionedIops: !Equals [!Ref DBStorageType, Provisioned IOPS] EnableCloudWatch: !Not [!Equals [!Ref CloudWatchIntegration, 'Off']] EnableCloudWatchLogs: !Equals [!Ref CloudWatchIntegration, 'Metrics and Logs'] UseFineGrainedAccessControlES: !Not [!Equals [!Ref ElasticSearchPassword, '']] UseIAMSignedRequestsES: !Equals [!Ref ElasticSearchPassword, ''] RestoreFromEBSSnapshot: !Not [!Equals [!Ref HomeVolumeSnapshotId, '']] RestoreFromRDSSnapshot: !Not [!Equals [!Ref DBSnapshotId, '']] ESBucketNameSet: !Not [!Equals [!Ref ESBucketName, '']] StandbyMode: !Not [!Equals [!Ref DBMaster, '']] UseCustomDnsName: !Not [!Equals [!Ref CustomDnsName, '']] DoSSL: !Not [!Equals [!Ref SSLCertificateARN, '']] NotStandbyMode: !Equals [!Ref DBMaster, ''] KeyProvided: !Not [!Equals [!Ref KeyPairName, '']] IsHomeProvisionedIops: !Equals [!Ref HomeVolumeType, Provisioned IOPS] RestoreRDSOrStandby: !Or [Condition: RestoreFromRDSSnapshot, Condition: StandbyMode] CreateESBucket: !And - !Equals [!Ref CreateBucket, 'true'] - Condition: ESBucketNameSet SetDBMasterUserAndPassword: !And [!Not [!Equals [!Ref DBMasterUserPassword, '']], Condition: NotStandbyMode] UsePublicIp: !Equals [!Ref InternetFacingLoadBalancer, 'true'] GovCloudCondition: !Equals - !Ref 'AWS::Region' - us-gov-west-1 DBEngineAurora: !Equals [!Ref DBEngine, "Amazon Aurora PostgreSQL"] DBEnginePostgres: !Equals [!Ref DBEngine, "PostgreSQL"] UseBastionHost: !And - !Equals [!Ref BastionHostRequired, true] - !Condition KeyProvided Mappings: AWSRegionArch2AMI: ap-northeast-1: HVM64: ami-08d56ac42e2d4a08b ap-northeast-2: HVM64: ami-0eb7a369386789460 ap-south-1: HVM64: ami-0dafa01c8100180f8 ap-southeast-1: HVM64: ami-04fc979a55e14b094 ap-southeast-2: HVM64: ami-042c4533fa25c105a ca-central-1: HVM64: ami-040d8c460f4fc4a9f eu-central-1: HVM64: ami-00e232b942edaf8f9 eu-north-1: HVM64: ami-0e3f1570eb0a9bc7f eu-west-1: HVM64: ami-09d5dd12541e69077 eu-west-2: HVM64: ami-098a393b6fa6e700b eu-west-3: HVM64: ami-05cb6b584fc3c8ac8 sa-east-1: HVM64: ami-088911543b10876a4 us-east-1: HVM64: ami-038b3df3312ddf25d us-east-2: HVM64: ami-07b1d7739c91ed3fc us-west-1: HVM64: ami-0729cd65c1a99b0c9 us-west-2: HVM64: ami-090bc08d7ae1f3881 us-gov-west-1: HVM64: ami-0bbf3595bb2fb39ec us-gov-east-1: HVM64: ami-0cc17d57bec8c6017 Resources: AnsiblePinnedShaPutPolicy: Type: 'AWS::IAM::Policy' Properties: PolicyName: SSMParameterPutAccess PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - 'ssm:PutParameter' Resource: !Sub "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${AWS::StackName}/pinned-ansible-sha" Roles: - !Ref BitbucketFileServerRole - !Ref BitbucketClusterNodeRole BitbucketFileServerRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com - es.amazonaws.com Action: ['sts:AssumeRole'] ManagedPolicyArns: - !Sub "arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore" - !Sub "arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy" Path: / Policies: - PolicyName: BitbucketFileServerPolicy PolicyDocument: Version: 2012-10-17 Statement: - Action: - autoscaling:CreateOrUpdateTags - ec2:AttachVolume - ec2:CreateSnapshot - ec2:CreateTags - ec2:CreateVolume - ec2:DetachVolume - rds:AddTagsToResource - rds:CopyDBSnapshot - rds:CreateDBSnapshot - rds:DeleteDBSnapshot - rds:DescribeDBInstances - rds:DescribeDBSnapshots - rds:ModifyDBInstance - rds:PromoteReadReplica - rds:RestoreDBInstanceFromDBSnapshot Resource: - !Sub "arn:${AWS::Partition}:autoscaling:*:${AWS::AccountId}:autoScalingGroup:*:autoScalingGroupName/*" - !Sub "arn:${AWS::Partition}:ec2:*:${AWS::AccountId}:instance/*" - !Sub "arn:${AWS::Partition}:ec2:*:${AWS::AccountId}:volume/*" - !Sub "arn:${AWS::Partition}:rds:*:${AWS::AccountId}:snapshot:*" Effect: Allow - Action: - autoscaling:DescribeTags - ec2:DescribeInstances - ec2:DescribeSnapshots - ec2:DescribeTags - ec2:DescribeVolumes - es:AcceptInboundCrossClusterSearchConnection - es:CreateElasticsearchServiceRole - es:CreatePackage - es:DeleteElasticsearchServiceRole - es:DeleteInboundCrossClusterSearchConnection - es:DeleteOutboundCrossClusterSearchConnection - es:DeletePackage - es:DescribeElasticsearchInstanceTypeLimits - es:DescribeInboundCrossClusterSearchConnections - es:DescribeOutboundCrossClusterSearchConnections - es:DescribePackages - es:DescribeReservedElasticsearchInstanceOfferings - es:DescribeReservedElasticsearchInstances - es:GetPackageVersionHistory - es:ListDomainNames - es:ListDomainsForPackage - es:ListElasticsearchInstanceTypeDetails - es:ListElasticsearchInstanceTypes - es:ListElasticsearchVersions - es:PurchaseReservedElasticsearchInstanceOffering - es:RejectInboundCrossClusterSearchConnection - es:UpdatePackage Resource: "*" Effect: Allow - Action: iam:PassRole Resource: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/*" Effect: Allow - Action: - es:AcceptInboundConnection - es:AcceptInboundCrossClusterSearchConnection - es:AddTags - es:AssociatePackage - es:CancelElasticsearchServiceSoftwareUpdate - es:CreateDataPrepperPipeline - es:CreateDomain - es:CreateElasticsearchDomain - es:CreateElasticsearchServiceRole - es:CreateOutboundConnection - es:CreateOutboundCrossClusterSearchConnection - es:CreatePackage - es:CreateServiceRole - es:DeleteDataPrepperPipeline - es:DeleteDomain - es:DeleteElasticsearchDomain - es:DeleteElasticsearchServiceRole - es:DeleteInboundConnection - es:DeleteInboundCrossClusterSearchConnection - es:DeleteOutboundConnection - es:DeleteOutboundCrossClusterSearchConnection - es:DeletePackage - es:DescribeDataPrepperPipeline - es:DescribeDomain - es:DescribeDomainConfig - es:DescribeDomains - es:DescribeElasticsearchDomain - es:DescribeElasticsearchDomainConfig - es:DescribeElasticsearchDomains - es:DescribeElasticsearchInstanceTypeLimits - es:DescribeInboundConnections - es:DescribeInboundCrossClusterSearchConnections - es:DescribeInstanceTypeLimits - es:DescribeOutboundConnections - es:DescribeOutboundCrossClusterSearchConnections - es:DescribePackages - es:DescribeReservedElasticsearchInstanceOfferings - es:DescribeReservedElasticsearchInstances - es:DescribeReservedInstanceOfferings - es:DescribeReservedInstances - es:DissociatePackage - es:ESCrossClusterGet - es:ESHttpDelete - es:ESHttpGet - es:ESHttpHead - es:ESHttpPatch - es:ESHttpPost - es:ESHttpPut - es:GetCompatibleElasticsearchVersions - es:GetCompatibleVersions - es:GetPackageVersionHistory - es:GetUpgradeHistory - es:GetUpgradeStatus - es:IngestDataPrepperPipeline - es:ListDataPrepperPipelines - es:ListDomainNames - es:ListDomainsForPackage - es:ListElasticsearchInstanceTypeDetails - es:ListElasticsearchInstanceTypes - es:ListElasticsearchVersions - es:ListInstanceTypeDetails - es:ListInstanceTypes - es:ListPackagesForDomain - es:ListTags - es:ListVersions - es:PurchaseReservedElasticsearchInstanceOffering - es:PurchaseReservedInstanceOffering - es:RejectInboundConnection - es:RejectInboundCrossClusterSearchConnection - es:RemoveTags - es:StartElasticsearchServiceSoftwareUpdate - es:UpdateDataPrepperPipeline - es:UpdateDomainConfig - es:UpdateElasticsearchDomainConfig - es:UpdatePackage - es:UpgradeElasticsearchDomain Resource: - !Sub "arn:${AWS::Partition}:es:*:${AWS::AccountId}:pipeline/*" - !Sub "arn:${AWS::Partition}:es:*:${AWS::AccountId}:domain/*" Effect: Allow - !If - ESBucketNameSet - Effect: Allow Action: - 's3:DeleteObject' - 's3:GetObject' - 's3:ListBucket' - 's3:PutObject' Resource: - !Sub "arn:${AWS::Partition}:s3:::${ESBucketName}" - !Sub "arn:${AWS::Partition}:s3:::${ESBucketName}/*" - !Ref "AWS::NoValue" BitbucketClusterNodeRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: [ec2.amazonaws.com] Action: ['sts:AssumeRole'] ManagedPolicyArns: - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore' - !Sub "arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy" Path: / Policies: - PolicyName: BitbucketClusterNodePolicy PolicyDocument: Version: 2012-10-17 Statement: - Action: - 'autoscaling:DescribeTags' - 'ec2:DescribeInstances' - 'ec2:DescribeTags' Effect: Allow Resource: ['*'] - Action: - 'autoscaling:CreateOrUpdateTags' Effect: Allow Resource: - !Sub "arn:${AWS::Partition}:autoscaling:*:${AWS::AccountId}:autoScalingGroup:*:autoScalingGroupName/*" BitbucketFileServerInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: [!Ref BitbucketFileServerRole] BitbucketClusterNodeInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: [!Ref BitbucketClusterNodeRole] ClusterNodeGroup: Type: AWS::AutoScaling::AutoScalingGroup CreationPolicy: ResourceSignal: Count: !Ref ClusterNodeMin Timeout: PT15M Properties: DesiredCapacity: !If [StandbyMode, '0', !Ref ClusterNodeMin] LaunchConfigurationName: !Ref ClusterNodeLaunchConfig MinSize: !If [StandbyMode, '0', !Ref ClusterNodeMin] MaxSize: !Ref ClusterNodeMax LoadBalancerNames: [!Ref LoadBalancer] VPCZoneIdentifier: - !Select - 0 - !Split - ',' - Fn::ImportValue: !Sub '${ExportPrefix}PriNets' Tags: - Key: Name Value: !Sub "${AWS::StackName} Bitbucket Node" PropagateAtLaunch: true - Key: Cluster Value: !Ref AWS::StackName PropagateAtLaunch: true # NOTE: The leading COMMIT/TIMESTAMP are used to locate the position to update; see scripts/update-tags.py - Key: "atl:quickstart:commit-id" Value: "COMMIT: 1ab73837b650a79665ca974ceb37e4088eee21b2" PropagateAtLaunch: true - Key: "atl:quickstart:timestamp" Value: "TIMESTAMP: 2021-11-25T07:12:04Z" PropagateAtLaunch: true ClusterNodeLaunchConfig: Type: AWS::AutoScaling::LaunchConfiguration DependsOn: - AnsibleRepoPinSHA Metadata: AWS::CloudFormation::Init: config: files: /etc/atl: content: !Join - "\n" - - "ATL_PRODUCT=bitbucket" - "ATL_APP_DATA_MOUNT_ENABLED=false" - "" - !Sub ["ATL_PRODUCT_VERSION=${BitbucketVersion}", BitbucketVersion: !Ref BitbucketVersion] - !If [IsBBAdminPasswordProvided, !Sub ["ATL_BB_ADMIN_PASSWORD=${BBAdminPassword}", BBAdminPassword: !Ref BitbucketAdminPassword], !Ref "AWS::NoValue"] - !If [IsLicenseKeyProvided, !Sub ["ATL_BB_LICENSEKEY='${LicenseKey}'", LicenseKey: !Ref BitbucketLicenseKey], !Ref "AWS::NoValue"] - !If [IsURLProvided, !Sub ["ATL_DATASET_URL=${DatasetURL}", DatasetURL: !Ref BitbucketDatasetURL], !Ref "AWS::NoValue"] - !Sub ["ATL_BB_BASEURL=${BaseURL}", BaseURL: !If [UseCustomDnsName, !Sub ["${HTTP}://${CustomDNSName}", { HTTP: !If [DoSSL, https, http], CustomDNSName: !Ref CustomDnsName }], !Sub ["${HTTP}://${LoadBalancerDNSName}", { HTTP: !If [DoSSL, https, http], LoadBalancerDNSName: !GetAtt LoadBalancer.DNSName }]]] - !Sub ["ATL_PROXY_NAME=${AtlProxyName}", AtlProxyName: !If [UseCustomDnsName, !Ref CustomDnsName, !GetAtt LoadBalancer.DNSName]] - !Sub ["ATL_FILESERVER_IP=${FileServerPrivateIp}", FileServerPrivateIp: !GetAtt FileServer.PrivateIp] - !Sub ["ATL_DEPLOYMENT_REPOSITORY=${DeployRepository}", DeployRepository: !Ref "DeploymentAutomationRepository"] - !Sub ["ATL_DEPLOYMENT_REPOSITORY_BRANCH=${DeployRepositoryBranch}", DeployRepositoryBranch: !Ref "DeploymentAutomationBranch"] - !Sub ["ATL_DEPLOYMENT_REPOSITORY_PLAYBOOK=${DeployRepositoryPlaybook}", DeployRepositoryPlaybook: !Ref "DeploymentAutomationPlaybook"] - !Sub ["ATL_DEPLOYMENT_REPOSITORY_KEYNAME=${DeployRepositoryKeyName}", DeployRepositoryKeyName: !Ref "DeploymentAutomationKeyName"] - !Sub ["ATL_DEPLOYMENT_REPOSITORY_CUSTOM_PARAMS='${DeployRepositoryCustomParams}'", DeployRepositoryCustomParams: !Ref "DeploymentAutomationCustomParams"] - "" - !Sub ["ATL_JVM_HEAP=${JvmHeapOverride}", JvmHeapOverride: !Ref "JvmHeapOverride"] - !Sub ["ATL_JVM_OPTS='${JvmSupportOpts}'", JvmSupportOpts: !Ref "JvmSupportOpts"] - "" - !Sub ["ATL_AWS_REGION=${Region}", Region: !Ref "AWS::Region"] - !Sub ["ATL_AWS_STACK_NAME=${StackName}", StackName: !Ref "AWS::StackName"] - !Sub ["ATL_AWS_IAM_ROLE=${Role}", Role: !Ref BitbucketClusterNodeRole] - !Sub ["ATL_AWS_IAM_ROLE_ARN=${Role}", Role: !GetAtt BitbucketFileServerRole.Arn] - "" - "ATL_JDBC_DB_NAME=bitbucket" - "ATL_JDBC_DRIVER=org.postgresql.Driver" - "ATL_JDBC_USER=atlbitbucket" - !Sub ["ATL_JDBC_PASSWORD='${DBPassword}'", DBPassword: !Ref DBPassword] - !Sub ["ATL_DB_ROOT_PASSWORD='${DBMasterUserPassword}'", DBMasterUserPassword: !Ref DBMasterUserPassword] - !Sub ["ATL_DB_ENGINE=${DBEngine}", DBEngine: !If [DBEngineAurora, aurora_postgres, !If [DBEnginePostgres, rds_postgres, '']]] - !Sub ["ATL_DB_HOST=${DBEndpointAddress}", DBEndpointAddress: !If [DBEngineAurora, !GetAtt DBCluster.Outputs.RDSEndPointAddress, !GetAtt DB.Endpoint.Address]] - !Sub ["ATL_DB_PORT=${DBEndpointPort}", DBEndpointPort: !If [DBEngineAurora, !GetAtt DBCluster.Outputs.RDSEndPointPort, !GetAtt DB.Endpoint.Port]] - !Sub ["ATL_JDBC_URL=jdbc:postgresql://${DBEndpointAddress}:${DBEndpointPort}/bitbucket", { DBEndpointAddress: !If [DBEngineAurora, !GetAtt DBCluster.Outputs.RDSEndPointAddress, !GetAtt DB.Endpoint.Address], DBEndpointPort: !If [DBEngineAurora, !GetAtt DBCluster.Outputs.RDSEndPointPort, !GetAtt DB.Endpoint.Port]}] - "" - !Sub ["ATL_RDS_INSTANCE_ID=${DB}", DB: !If [ DBEngineAurora, !Ref "DBCluster", !Ref "DB"]] - !Sub ["ATL_RDS_INSTANCE_CLASS=${DBInstanceClass}", DBInstanceClass: !Ref "DBInstanceClass"] - !Sub ["ATL_RDS_MULTI_AZ=${DBMultiAZ}", DBMultiAZ: !Ref "DBMultiAZ"] - !Sub ["ATL_RDS_SUBNET_GROUP_NAME=${DBSubnetGroup}", DBSubnetGroup: !If [ DBEnginePostgres, !Ref "DBSubnetGroup", '']] - !Sub ["ATL_RDS_SECURITY_GROUP=${SecurityGroup}", SecurityGroup: !Ref "SecurityGroup"] - "" - !Sub ["ATL_NFS_DISK_VOLUME_TYPE=${VolType}", VolType: !If [IsHomeProvisionedIops, "io1", "gp2"]] - !Sub ["ATL_NFS_DISK_VOLUME_IOPS=${VolIOPs}", VolIOPs: !If [IsHomeProvisionedIops, !Ref "HomeIops", ""]] - "" - !Sub ["ATL_ELASTICSEARCH_HOST=${ESEndpoint}", ESEndpoint: !If [UseFineGrainedAccessControlES, !GetAtt ElasticsearchSecure.DomainEndpoint, !GetAtt Elasticsearch.DomainEndpoint]] - !Sub ["ATL_ELASTICSEARCH_PROTOCOL=${ESScheme}", ESScheme: !If [UseFineGrainedAccessControlES, "https", "http"]] - !If [UseFineGrainedAccessControlES, "ATL_ELASTICSEARCH_USERNAME=bitbucket", !Ref "AWS::NoValue"] - !If [UseFineGrainedAccessControlES, !Sub ["ATL_ELASTICSEARCH_PASSWORD=${ESPassword}", ESPassword: !Ref ElasticSearchPassword], !Ref "AWS::NoValue"] - "" - !Sub ["ATL_BITBUCKET_PROPERTIES=\"${BitbucketProperties}\"", BitbucketProperties: !Ref BitbucketProperties] - !If [DoSSL, "ATL_SSL_PROXY=true", !Ref "AWS::NoValue"] - !Sub ["ATL_AWS_ENABLE_CLOUDWATCH=${EnableCW}", EnableCW: !If [EnableCloudWatch, true, false]] - !Sub ["ATL_AWS_ENABLE_CLOUDWATCH_LOGS=${EnableCWLogs}", EnableCWLogs: !If [EnableCloudWatchLogs, true, false]] /opt/atlassian/bin/clone_deployment_repo: content: !Sub | #!/bin/bash key_location=/root/.ssh/deployment_repo_key key_name="${DeploymentAutomationKeyName}" bitbucket_version="${BitbucketVersion}" ssm_pin=/${AWS::StackName}/pinned-ansible-sha bitbucket_minor_version=$(echo "$bitbucket_version" | cut -d. -f-2) case "$bitbucket_minor_version" in 6.0) supported_git_version="2.17.2" ;; 6.1) supported_git_version="2.17.2" ;; 6.1) supported_git_version="2.17.2" ;; 6.2) supported_git_version="2.17.2" ;; 6.3) supported_git_version="2.17.2" ;; 6.4) supported_git_version="2.17.2" ;; 6.5) supported_git_version="2.17.2" ;; 6.6) supported_git_version="2.23.1" ;; 6.7) supported_git_version="2.23.4" ;; 6.8) supported_git_version="2.23.4" ;; 6.9) supported_git_version="2.23.4" ;; 6.10) supported_git_version="2.23.4" ;; 7.0) supported_git_version="2.23.4" ;; 7.1) supported_git_version="2.23.4" ;; 7.2) supported_git_version="2.23.4" ;; 7.3) supported_git_version="2.23.4" ;; 7.4) supported_git_version="2.23.4" ;; 7.5) supported_git_version="2.23.4" ;; 7.6) supported_git_version="2.23.4" ;; 7.7) supported_git_version="2.23.4" ;; 7.8) supported_git_version="2.23.4" ;; 7.9) supported_git_version="2.23.4" ;; 7.10) supported_git_version="2.23.4" ;; 7.11) supported_git_version="2.23.4" ;; 7.12) supported_git_version="2.23.4" ;; 7.13) supported_git_version="2.23.4" ;; 7.14) supported_git_version="2.32.0" ;; 7.15) supported_git_version="2.32.0" ;; 7.16) supported_git_version="2.32.0" ;; 7.17) supported_git_version="2.32.0" ;; 7.18) supported_git_version="2.32.0" ;; 7.19) supported_git_version="2.34.3" ;; 7.20) supported_git_version="2.34.3" ;; 7.21) supported_git_version="2.34.3" ;; 8.0) supported_git_version="2.34.3" ;; 8.1) supported_git_version="2.34.3" ;; 8.2) supported_git_version="2.34.3" ;; 8.3) supported_git_version="2.37.1" ;; *) supported_git_version="2.37.1" ;; esac yum install -y git-"$supported_git_version" awscli jq if [[ ! -z "$key_name" ]]; then # Ensure awscli is up to date key_val=$(aws --region=${AWS::Region} ssm get-parameters --names "$key_name" --with-decryption | jq --raw-output '.Parameters[0].Value') echo -e "$key_val" > $key_location chmod 600 $key_location export GIT_SSH_COMMAND="ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -i $key_location" else export GIT_SSH_COMMAND="ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no" fi ### Ansible repo pinning ### pinned_commit_id=$(aws --region=${AWS::Region} ssm get-parameters --names "$ssm_pin" | jq --raw-output '.Parameters[0].Value') git clone "${DeploymentAutomationRepository}" -b "${DeploymentAutomationBranch}" /opt/atlassian/dc-deployments-automation/ cd /opt/atlassian/dc-deployments-automation/ if [[ "$pinned_commit_id" == "latest" || -z "$pinned_commit_id" ]]; then head_id=$(git rev-parse HEAD) echo "SSM param [$ssm_pin] has been set to 'latest' - Using the HEAD SHA [$head_id] to build cluster [${AWS::StackName}]" echo "Updating SSM param [$ssm_pin] with current HEAD SHA: [$head_id]" aws --region=${AWS::Region} ssm put-parameter --name "$ssm_pin" --value "$head_id" --overwrite --type String else echo "Ansible repo has been pinned, checking out commit: [$pinned_commit_id]" git checkout -b "pinned-ansible-sha-$pinned_commit_id" "$pinned_commit_id" fi mode: "000750" owner: root group: root commands: 070_create_atl_dir: test: "test ! -d /opt/atlassian/" command: mkdir -p /opt/atlassian ignoreErrors: false 071_install_packages: command: yum install -y python-virtualenv ignoreErrors: true 072_clone_atl_scripts: test: "test ! -d /opt/atlassian/dc-deployments-automation/" command: /opt/atlassian/bin/clone_deployment_repo ignoreErrors: true 080_run_atl_init_node: command: !Sub | cd /opt/atlassian/dc-deployments-automation/ && ./bin/install-ansible && ./bin/ansible-with-atl-env inv/aws_node_local ${DeploymentAutomationPlaybook} /var/log/ansible-bootstrap.log ignoreErrors: true Properties: AssociatePublicIpAddress: false BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeSize: !Ref ClusterNodeVolumeSize KeyName: !If - KeyProvided - !Ref KeyPairName - Ref: AWS::NoValue IamInstanceProfile: !Ref BitbucketClusterNodeInstanceProfile ImageId: !FindInMap - AWSRegionArch2AMI - !Ref "AWS::Region" - HVM64 InstanceType: !Ref ClusterNodeInstanceType SecurityGroups: [!Ref SecurityGroup] UserData: Fn::Base64: !Join - "" - - "#!/bin/bash -xe\n" - "yum update -y aws-cfn-bootstrap amazon-ssm-agent\n" - !Sub "/opt/aws/bin/cfn-init -v --stack ${AWS::StackName}" - !Sub " --resource ClusterNodeLaunchConfig --region ${AWS::Region}\n" - !Sub "/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName}" - !Sub " --resource ClusterNodeGroup --region ${AWS::Region}\n" ClusterNodeScaleUpPolicy: Type: AWS::AutoScaling::ScalingPolicy Properties: AdjustmentType: ChangeInCapacity AutoScalingGroupName: !Ref ClusterNodeGroup Cooldown: '600' ScalingAdjustment: 1 ClusterNodeScaleDownPolicy: Type: AWS::AutoScaling::ScalingPolicy Properties: AdjustmentType: ChangeInCapacity AutoScalingGroupName: !Ref ClusterNodeGroup Cooldown: '600' ScalingAdjustment: -1 CPUAlarmHigh: Type: AWS::CloudWatch::Alarm Properties: AlarmDescription: Scale up if CPU > 60% for 5 minutes MetricName: CPUUtilization Namespace: AWS/EC2 Statistic: Average Period: 60 EvaluationPeriods: 5 Threshold: 60 AlarmActions: [!Ref ClusterNodeScaleUpPolicy] Dimensions: - Name: AutoScalingGroupName Value: !Ref ClusterNodeGroup ComparisonOperator: GreaterThanThreshold CPUAlarmLow: Type: AWS::CloudWatch::Alarm Properties: AlarmDescription: Scale down if CPU < 40% for 30 minutes MetricName: CPUUtilization Namespace: AWS/EC2 Statistic: Average Period: 60 EvaluationPeriods: 30 Threshold: 40 AlarmActions: [!Ref ClusterNodeScaleDownPolicy] Dimensions: - Name: AutoScalingGroupName Value: !Ref ClusterNodeGroup ComparisonOperator: LessThanThreshold Elasticsearch: Condition: UseIAMSignedRequestsES Type: AWS::Elasticsearch::Domain Properties: EBSOptions: EBSEnabled: true VolumeSize: !Ref ElasticsearchNodeVolumeSize VolumeType: gp2 ElasticsearchVersion: "7.10" ElasticsearchClusterConfig: InstanceType: !Ref ElasticsearchInstanceType AccessPolicies: Version: 2012-10-17 Statement: - Effect: "Allow" Principal: AWS: !GetAtt BitbucketClusterNodeRole.Arn Action: - es:AcceptInboundConnection - es:AcceptInboundCrossClusterSearchConnection - es:AddTags - es:AssociatePackage - es:CancelElasticsearchServiceSoftwareUpdate - es:CreateDataPrepperPipeline - es:CreateDomain - es:CreateElasticsearchDomain - es:CreateElasticsearchServiceRole - es:CreateOutboundConnection - es:CreateOutboundCrossClusterSearchConnection - es:CreatePackage - es:CreateServiceRole - es:DeleteDataPrepperPipeline - es:DeleteDomain - es:DeleteElasticsearchDomain - es:DeleteElasticsearchServiceRole - es:DeleteInboundConnection - es:DeleteInboundCrossClusterSearchConnection - es:DeleteOutboundConnection - es:DeleteOutboundCrossClusterSearchConnection - es:DeletePackage - es:DescribeDataPrepperPipeline - es:DescribeDomain - es:DescribeDomainConfig - es:DescribeDomains - es:DescribeElasticsearchDomain - es:DescribeElasticsearchDomainConfig - es:DescribeElasticsearchDomains - es:DescribeElasticsearchInstanceTypeLimits - es:DescribeInboundConnections - es:DescribeInboundCrossClusterSearchConnections - es:DescribeInstanceTypeLimits - es:DescribeOutboundConnections - es:DescribeOutboundCrossClusterSearchConnections - es:DescribePackages - es:DescribeReservedElasticsearchInstanceOfferings - es:DescribeReservedElasticsearchInstances - es:DescribeReservedInstanceOfferings - es:DescribeReservedInstances - es:DissociatePackage - es:ESCrossClusterGet - es:ESHttpDelete - es:ESHttpGet - es:ESHttpHead - es:ESHttpPatch - es:ESHttpPost - es:ESHttpPut - es:GetCompatibleElasticsearchVersions - es:GetCompatibleVersions - es:GetPackageVersionHistory - es:GetUpgradeHistory - es:GetUpgradeStatus - es:IngestDataPrepperPipeline - es:ListDataPrepperPipelines - es:ListDomainNames - es:ListDomainsForPackage - es:ListElasticsearchInstanceTypeDetails - es:ListElasticsearchInstanceTypes - es:ListElasticsearchVersions - es:ListInstanceTypeDetails - es:ListInstanceTypes - es:ListPackagesForDomain - es:ListTags - es:ListVersions - es:PurchaseReservedElasticsearchInstanceOffering - es:PurchaseReservedInstanceOffering - es:RejectInboundConnection - es:RejectInboundCrossClusterSearchConnection - es:RemoveTags - es:StartElasticsearchServiceSoftwareUpdate - es:UpdateDataPrepperPipeline - es:UpdateDomainConfig - es:UpdateElasticsearchDomainConfig - es:UpdatePackage - es:UpgradeElasticsearchDomain Resource: "*" Tags: - Key: Name Value: !Sub "${AWS::StackName} Bitbucket Elasticsearch cluster" - Key: Application Value: !Ref "AWS::StackId" ElasticsearchServiceRole: Condition: UseFineGrainedAccessControlES Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - "es.amazonaws.com" ElasticsearchSecure: Condition: UseFineGrainedAccessControlES DependsOn: - ElasticsearchServiceRole Type: AWS::Elasticsearch::Domain Properties: EBSOptions: EBSEnabled: true VolumeSize: !Ref ElasticsearchNodeVolumeSize VolumeType: gp2 ElasticsearchVersion: "7.10" ElasticsearchClusterConfig: InstanceType: !Ref ElasticsearchInstanceType AccessPolicies: Version: 2012-10-17 Statement: - Effect: "Allow" Principal: AWS: "*" Action: - es:ESHttpDelete - es:ESHttpGet - es:ESHttpHead - es:ESHttpPatch - es:ESHttpPost - es:ESHttpPut Resource: "*" AdvancedSecurityOptions: Enabled: true InternalUserDatabaseEnabled: true MasterUserOptions: MasterUserName: bitbucket MasterUserPassword: !Ref ElasticSearchPassword DomainEndpointOptions: EnforceHTTPS: true TLSSecurityPolicy: Policy-Min-TLS-1-2-2019-07 EncryptionAtRestOptions: Enabled: true KmsKeyId: !Ref ElasticsearchKMSKey NodeToNodeEncryptionOptions: Enabled: true VPCOptions: SubnetIds: - !Select - 0 - !Split - ',' - Fn::ImportValue: !Sub '${ExportPrefix}PriNets' SecurityGroupIds: - !Ref SecurityGroup Tags: - Key: Name Value: !Sub "${AWS::StackName} Bitbucket Elasticsearch cluster" - Key: Application Value: !Ref "AWS::StackId" ElasticsearchKMSKey: Condition: UseFineGrainedAccessControlES Type: AWS::KMS::Key Properties: Description: Encryption key for Elasticsearch cluster Enabled: true EnableKeyRotation: true KeyPolicy: Version: 2012-10-17 Id: !Sub "${AWS::StackName}" Statement: - Effect: Allow Principal: AWS: - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:root" Action: - kms:CancelKeyDeletion - kms:ConnectCustomKeyStore - kms:CreateAlias - kms:CreateCustomKeyStore - kms:CreateGrant - kms:CreateKey - kms:Decrypt - kms:DeleteAlias - kms:DeleteCustomKeyStore - kms:DeleteImportedKeyMaterial - kms:DescribeCustomKeyStores - kms:DescribeKey - kms:DisableKey - kms:DisableKeyRotation - kms:DisconnectCustomKeyStore - kms:EnableKey - kms:EnableKeyRotation - kms:Encrypt - kms:GenerateDataKey - kms:GenerateDataKeyPair - kms:GenerateDataKeyPairWithoutPlaintext - kms:GenerateDataKeyWithoutPlaintext - kms:GenerateRandom - kms:GetKeyPolicy - kms:GetKeyRotationStatus - kms:GetParametersForImport - kms:GetPublicKey - kms:ImportKeyMaterial - kms:ListAliases - kms:ListGrants - kms:ListKeyPolicies - kms:ListKeys - kms:ListResourceTags - kms:ListRetirableGrants - kms:PutKeyPolicy - kms:ReEncryptFrom - kms:ReEncryptTo - kms:ReplicateKey - kms:RetireGrant - kms:RevokeGrant - kms:ScheduleKeyDeletion - kms:Sign - kms:SynchronizeMultiRegionKey - kms:TagResource - kms:UntagResource - kms:UpdateAlias - kms:UpdateCustomKeyStore - kms:UpdateKeyDescription - kms:UpdatePrimaryRegion - kms:Verify Resource: '*' Tags: - Key: Name Value: !Sub ["${StackName} Elasticsearch Encryption Key", StackName: !Ref 'AWS::StackName'] ElasticsearchBucket: Type: AWS::S3::Bucket Condition: CreateESBucket Properties: BucketName: !Ref ESBucketName Tags: - Key: Cluster Value: !Ref "AWS::StackName" FileServer: Type: AWS::EC2::Instance DependsOn: - AnsibleRepoPinSHA Metadata: Comment: Set up NFS Server and initial bitbucket.properties AWS::CloudFormation::Init: config: files: /etc/atl: content: !Join - "\n" - - ATL_ENABLED_PRODUCTS= - ATL_ENABLED_SHARED_HOMES=Bitbucket - ATL_APP_NFS_SERVER=true - ATL_NFS_SERVER_DEVICE=/dev/xvdf # NOTE: For simplicity we should keep this as close to the BB node version above. - !Sub ["ATL_PRODUCT_VERSION=${BitbucketVersion}", BitbucketVersion: !Ref BitbucketVersion] - !Sub ["ATL_PROXY_NAME=${LoadBalancerDNSName}", LoadBalancerDNSName: !GetAtt LoadBalancer.DNSName] - "" - !Sub ["ATL_AWS_REGION=${Region}", Region: !Ref "AWS::Region"] - !Sub ["ATL_AWS_STACK_NAME=${StackName}", StackName: !Ref "AWS::StackName"] - !Sub ["ATL_AWS_IAM_ROLE=${Role}", Role: !Ref BitbucketFileServerRole] - !Sub ["ATL_AWS_IAM_ROLE_ARN=${Role}", Role: !GetAtt BitbucketFileServerRole.Arn] - "" - "ATL_JDBC_DB_NAME=bitbucket" - "ATL_JDBC_DRIVER=org.postgresql.Driver" - "ATL_JDBC_USER=atlbitbucket" - !Sub ["ATL_JDBC_PASSWORD='${DBPassword}'", DBPassword: !Ref DBPassword] - !Sub ["ATL_DB_ROOT_PASSWORD='${DBMasterUserPassword}'", DBMasterUserPassword: !Ref DBMasterUserPassword] - !Sub ["ATL_DB_HOST=${DBEndpointAddress}", DBEndpointAddress: !If [ DBEngineAurora, !GetAtt DBCluster.Outputs.RDSEndPointAddress, !GetAtt DB.Endpoint.Address]] - !Sub ["ATL_DB_PORT=${DBEndpointPort}", DBEndpointPort: !If [ DBEngineAurora, !GetAtt DBCluster.Outputs.RDSEndPointPort, !GetAtt DB.Endpoint.Port]] - !Sub ["ATL_JDBC_URL=jdbc:postgresql://${DBEndpointAddress}:${DBEndpointPort}/bitbucket", { DBEndpointAddress: !If [ DBEngineAurora, !GetAtt DBCluster.Outputs.RDSEndPointAddress, !GetAtt DB.Endpoint.Address], DBEndpointPort: !If [ DBEngineAurora, !GetAtt DBCluster.Outputs.RDSEndPointPort, !GetAtt DB.Endpoint.Port] }] - "" - !Sub ["ATL_RDS_INSTANCE_ID=${DB}", DB: !If [ DBEngineAurora, !Ref "DBCluster", !Ref "DB"]] - !Sub ["ATL_RDS_INSTANCE_CLASS=${DBInstanceClass}", DBInstanceClass: !Ref "DBInstanceClass"] - !Sub ["ATL_RDS_MULTI_AZ=${DBMultiAZ}", DBMultiAZ: !Ref "DBMultiAZ"] - !Sub ["ATL_RDS_SUBNET_GROUP_NAME=${DBSubnetGroup}", DBSubnetGroup: !If [ DBEnginePostgres, !Ref "DBSubnetGroup", '']] - !Sub ["ATL_RDS_SECURITY_GROUP=${SecurityGroup}", SecurityGroup: !Ref "SecurityGroup"] - "" - !Sub ["ATL_NFS_DISK_VOLUME_TYPE=${VolType}", VolType: !If [IsHomeProvisionedIops, "io1", "gp2"]] - !Sub ["ATL_NFS_DISK_VOLUME_IOPS=${VolIOPs}", VolIOPs: !If [IsHomeProvisionedIops, !Ref "HomeIops", ""]] - "" - !Sub ["ATL_ELASTICSEARCH_HOST=${ESEndpoint}", ESEndpoint: !If [UseFineGrainedAccessControlES, !GetAtt ElasticsearchSecure.DomainEndpoint, !GetAtt Elasticsearch.DomainEndpoint]] - !Sub ["ATL_ELASTICSEARCH_S3_BUCKET=${BucketName}", BucketName: !Ref ESBucketName] - !Sub ["ATL_BITBUCKET_PROPERTIES=\"${BitbucketProperties}\"", BitbucketProperties: !Ref BitbucketProperties] - !If [DoSSL, "ATL_SSL_PROXY=true", !Ref "AWS::NoValue"] - "" - !Sub ["ATL_DEPLOYMENT_REPOSITORY=${DeployRepository}", DeployRepository: !Ref "DeploymentAutomationRepository"] - !Sub ["ATL_DEPLOYMENT_REPOSITORY_BRANCH=${DeployRepositoryBranch}", DeployRepositoryBranch: !Ref "DeploymentAutomationBranch"] - !Sub ["ATL_DEPLOYMENT_REPOSITORY_PLAYBOOK=${DeployRepositoryPlaybook}", DeployRepositoryPlaybook: !Ref "DeploymentAutomationPlaybook"] - !Sub ["ATL_DEPLOYMENT_REPOSITORY_KEYNAME=${DeployRepositoryKeyName}", DeployRepositoryKeyName: !Ref "DeploymentAutomationKeyName"] - !Sub ["ATL_DEPLOYMENT_REPOSITORY_CUSTOM_PARAMS='${DeployRepositoryCustomParams}'", DeployRepositoryCustomParams: !Ref "DeploymentAutomationCustomParams"] - !Sub ["ATL_AWS_ENABLE_CLOUDWATCH=${EnableCW}", EnableCW: !If [EnableCloudWatch, true, false]] - !Sub ["ATL_AWS_ENABLE_CLOUDWATCH_LOGS=${EnableCWLogs}", EnableCWLogs: !If [EnableCloudWatchLogs, true, false]] /opt/atlassian/bin/clone_deployment_repo: content: !Sub | #!/bin/bash key_location=/root/.ssh/deployment_repo_key key_name="${DeploymentAutomationKeyName}" ssm_pin=/${AWS::StackName}/pinned-ansible-sha bitbucket_version="${BitbucketVersion}" bitbucket_minor_version=$(echo "$bitbucket_version" | cut -d. -f-2) case "$bitbucket_minor_version" in 6.0) supported_git_version="2.17.2" ;; 6.1) supported_git_version="2.17.2" ;; 6.1) supported_git_version="2.17.2" ;; 6.2) supported_git_version="2.17.2" ;; 6.3) supported_git_version="2.17.2" ;; 6.4) supported_git_version="2.17.2" ;; 6.5) supported_git_version="2.17.2" ;; 6.6) supported_git_version="2.23.1" ;; 6.7) supported_git_version="2.23.4" ;; 6.8) supported_git_version="2.23.4" ;; 6.9) supported_git_version="2.23.4" ;; 6.10) supported_git_version="2.23.4" ;; 7.0) supported_git_version="2.23.4" ;; 7.1) supported_git_version="2.23.4" ;; 7.2) supported_git_version="2.23.4" ;; 7.3) supported_git_version="2.23.4" ;; 7.4) supported_git_version="2.23.4" ;; 7.5) supported_git_version="2.23.4" ;; 7.6) supported_git_version="2.23.4" ;; 7.7) supported_git_version="2.23.4" ;; 7.8) supported_git_version="2.23.4" ;; 7.9) supported_git_version="2.23.4" ;; 7.10) supported_git_version="2.23.4" ;; 7.11) supported_git_version="2.23.4" ;; 7.12) supported_git_version="2.23.4" ;; 7.13) supported_git_version="2.23.4" ;; 7.14) supported_git_version="2.32.0" ;; 7.15) supported_git_version="2.32.0" ;; 7.16) supported_git_version="2.32.0" ;; 7.17) supported_git_version="2.32.0" ;; 7.18) supported_git_version="2.32.0" ;; 7.19) supported_git_version="2.34.3" ;; 7.20) supported_git_version="2.34.3" ;; 7.21) supported_git_version="2.34.3" ;; 8.0) supported_git_version="2.34.3" ;; 8.1) supported_git_version="2.34.3" ;; 8.2) supported_git_version="2.34.3" ;; 8.3) supported_git_version="2.37.1" ;; *) supported_git_version="2.37.1" ;; esac yum install -y git-"$supported_git_version" awscli jq if [[ ! -z "$key_name" ]]; then # Ensure awscli is up to date key_val=$(aws --region=${AWS::Region} ssm get-parameters --names "$key_name" --with-decryption | jq --raw-output '.Parameters[0].Value') echo -e "$key_val" > $key_location chmod 600 $key_location export GIT_SSH_COMMAND="ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -i $key_location" else export GIT_SSH_COMMAND="ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no" fi ### Ansible repo pinning ### pinned_commit_id=$(aws --region=${AWS::Region} ssm get-parameters --names "$ssm_pin" | jq --raw-output '.Parameters[0].Value') git clone "${DeploymentAutomationRepository}" -b "${DeploymentAutomationBranch}" /opt/atlassian/dc-deployments-automation/ cd /opt/atlassian/dc-deployments-automation/ if [[ "$pinned_commit_id" == "latest" || -z "$pinned_commit_id" ]]; then head_id=$(git rev-parse HEAD) echo "SSM param [$ssm_pin] has been set to 'latest' - Using the HEAD SHA [$head_id] to build cluster [${AWS::StackName}]" echo "Updating SSM param [$ssm_pin] with current HEAD SHA: [$head_id]" aws --region=${AWS::Region} ssm put-parameter --name "$ssm_pin" --value "$head_id" --overwrite --type String else echo "Ansible repo has been pinned, checking out commit: [$pinned_commit_id]" git checkout -b "pinned-ansible-sha-$pinned_commit_id" "$pinned_commit_id" fi mode: "000750" owner: root group: root commands: 070_create_atl_dir: test: "test ! -d /opt/atlassian/" command: mkdir -p /opt/atlassian ignoreErrors: false 071_install_packages: command: yum install -y python-virtualenv ignoreErrors: true 072_clone_atl_scripts: test: "test ! -d /opt/atlassian/dc-deployments-automation/" command: /opt/atlassian/bin/clone_deployment_repo ignoreErrors: true 080_run_atl_init_node: command: cd /opt/atlassian/dc-deployments-automation/ && ./bin/install-ansible && ./bin/ansible-with-atl-env inv/aws_node_local aws_bitbucket_nfs_node.yml /var/log/ansible-bootstrap.log ignoreErrors: true Properties: BlockDeviceMappings: - DeviceName: /dev/xvdf Ebs: DeleteOnTermination: !Ref HomeDeleteOnTermination Iops: !If [IsHomeProvisionedIops, !Ref HomeIops, !Ref "AWS::NoValue"] SnapshotId: !If [RestoreFromEBSSnapshot, !Ref HomeVolumeSnapshotId, !Ref "AWS::NoValue"] VolumeSize: !Ref HomeSize VolumeType: !If [IsHomeProvisionedIops, io1, gp2] IamInstanceProfile: !Ref BitbucketFileServerInstanceProfile EbsOptimized: true ImageId: !FindInMap - AWSRegionArch2AMI - !Ref "AWS::Region" - HVM64 InstanceType: !Ref FileServerInstanceType KeyName: !If - KeyProvided - !Ref KeyPairName - Ref: AWS::NoValue NetworkInterfaces: - GroupSet: [!Ref SecurityGroup] AssociatePublicIpAddress: false DeviceIndex: '0' DeleteOnTermination: true SubnetId: !Select - 0 - !Split - "," - Fn::ImportValue: !Sub "${ExportPrefix}PriNets" Tags: - Key: Name Value: !Sub "${AWS::StackName} Bitbucket NFS Server" - Key: Application Value: !Ref "AWS::StackId" UserData: Fn::Base64: !Join - "" - - "#!/bin/bash -xe\n" - "yum update -y aws-cfn-bootstrap amazon-ssm-agent\n" - !Sub ["/opt/aws/bin/cfn-init -v --stack ${StackName}", StackName: !Ref "AWS::StackName"] - !Sub [" --resource FileServer --region ${Region}\n", Region: !Ref "AWS::Region"] - !Sub ["/opt/aws/bin/cfn-signal -e $? --stack ${StackName}", StackName: !Ref "AWS::StackName"] - !Sub [" --resource FileServer --region ${Region}\n", Region: !Ref "AWS::Region"] ## We need a custom resource to generate a meaningful name. If the stack name ## is longer than 57 characters, using {StackName}-db would cause a deployment failure DbInstanceName: Condition: DBEnginePostgres Type: Custom::DbInstanceName Version: 1.0 Properties: ServiceToken: !GetAtt DBNameGenerator.Arn StackName: !Ref 'AWS::StackName' DBNameGenerator: Condition: DBEnginePostgres Type: "AWS::Lambda::Function" Properties: Handler: index.lambda_handler Role: !GetAtt DBNameGeneratorExecutionRole.Arn Runtime: python3.7 Timeout: 120 Code: ZipFile: | import cfnresponse def lambda_handler(event, context): stack_name = event['ResourceProperties']['StackName'] responseData = {} responseData['DBInstanceName'] = stack_name[:57] + "-db" cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData) DBNameGeneratorExecutionRole: Condition: DBEnginePostgres Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: "/" Policies: - PolicyName: root PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: !Sub "arn:${AWS::Partition}:logs:*:${AWS::AccountId}:log-group:/aws/lambda/*DBNameGenerator*" DB: Type: AWS::RDS::DBInstance Condition: DBEnginePostgres Properties: AllocatedStorage: !Ref DBStorage DBInstanceClass: !Ref DBInstanceClass DBInstanceIdentifier: !GetAtt DbInstanceName.DBInstanceName DBName: !If [RestoreRDSOrStandby, !Ref "AWS::NoValue", bitbucket] DBSnapshotIdentifier: !If [RestoreFromRDSSnapshot, !Ref DBSnapshotId, !Ref "AWS::NoValue"] DBSubnetGroupName: !Ref DBSubnetGroup Engine: postgres EngineVersion: !Ref DBEngineVersion Iops: !If [DBProvisionedIops, !Ref DBIops, !Ref "AWS::NoValue"] MasterUsername: !If ["SetDBMasterUserAndPassword", postgres, !Ref "AWS::NoValue"] MasterUserPassword: !If ["SetDBMasterUserAndPassword", !Ref DBMasterUserPassword, !Ref "AWS::NoValue"] MultiAZ: !If [StandbyMode, !Ref "AWS::NoValue", !Ref DBMultiAZ] SourceDBInstanceIdentifier: !If [StandbyMode, !Ref DBMaster, !Ref "AWS::NoValue"] StorageType: !If [DBProvisionedIops, io1, gp2] Tags: - Key: Name Value: !Sub "${AWS::StackName} Bitbucket PostgreSQL Database" VPCSecurityGroups: [!Ref SecurityGroup] DBSubnetGroup: Type: AWS::RDS::DBSubnetGroup Condition: DBEnginePostgres Properties: DBSubnetGroupDescription: DBSubnetGroup SubnetIds: !Split - "," - Fn::ImportValue: !Sub "${ExportPrefix}PriNets" DBCluster: Type: AWS::CloudFormation::Stack Condition: DBEngineAurora Properties: TemplateURL: !Sub - https://${QSS3BucketName}.${QSS3Region}.amazonaws.com/${QSS3KeyPrefix}submodules/quickstart-atlassian-services/templates/quickstart-database-for-atlassian-services.yaml - QSS3Region: !If ["GovCloudCondition", "s3-us-gov-west-1", "s3"] Parameters: DatabaseImplementation: !Ref DBEngine DBAllocatedStorage: !Ref DBStorage DBAutoMinorVersionUpgrade: "true" DBBackupRetentionPeriod: "1" DBEngineVersion: !Ref DBEngineVersion DBInstanceClass: !Ref DBInstanceClass DBIops: !Ref DBIops DBMasterUserPassword: !Ref DBMasterUserPassword DBMultiAZ: !Ref DBMultiAZ DBSecurityGroup: !Ref SecurityGroup DBStorageType: !Ref DBStorageType ExportPrefix: !Ref ExportPrefix QSS3BucketName: !Ref QSS3BucketName QSS3KeyPrefix: !Ref QSS3KeyPrefix LoadBalancer: Type: AWS::ElasticLoadBalancing::LoadBalancer Properties: AppCookieStickinessPolicy: - CookieName: BITBUCKETSESSIONID PolicyName: SessionStickiness ConnectionDrainingPolicy: Enabled: true Timeout: 300 ConnectionSettings: IdleTimeout: 3600 CrossZone: true Listeners: - LoadBalancerPort: '80' Protocol: HTTP InstancePort: !If [DoSSL, '7991', '7990'] InstanceProtocol: HTTP PolicyNames: [SessionStickiness] - !If - DoSSL - LoadBalancerPort: '443' Protocol: HTTPS InstancePort: '7990' InstanceProtocol: HTTP PolicyNames: - SessionStickiness SSLCertificateId: !Ref SSLCertificateARN - !Ref AWS::NoValue - LoadBalancerPort: '7999' Protocol: TCP InstancePort: '7999' InstanceProtocol: TCP HealthCheck: HealthyThreshold: '2' Interval: '60' Target: HTTP:7990/status Timeout: '29' UnhealthyThreshold: '2' Scheme: !If [UsePublicIp, 'internet-facing', 'internal'] SecurityGroups: [!Ref SecurityGroup] Subnets: !Split - "," - Fn::ImportValue: !Sub "${ExportPrefix}PubNets" Tags: - Key: Name Value: !Sub "${AWS::StackName} LoadBalancer" - Key: Cluster Value: !Ref AWS::StackName SecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Security group allowing SSH and HTTP/HTTPS access SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: !Ref CidrBlock - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: !Ref CidrBlock - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: !Ref CidrBlock - IpProtocol: tcp FromPort: 7999 ToPort: 7999 CidrIp: !Ref CidrBlock - !If - UseBastionHost - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: !Sub - "${BastionIp}/32" - BastionIp: Fn::ImportValue: !Sub '${ExportPrefix}BastionPrivIp' - Ref: AWS::NoValue - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: !Sub - "${NAT1IP}/32" - NAT1IP: Fn::ImportValue: !Sub '${ExportPrefix}NAT1EIP' - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: !Sub - "${NAT2IP}/32" - NAT2IP: Fn::ImportValue: !Sub '${ExportPrefix}NAT2EIP' - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: !Sub - "${NAT1IP}/32" - NAT1IP: Fn::ImportValue: !Sub '${ExportPrefix}NAT1EIP' - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: !Sub - "${NAT2IP}/32" - NAT2IP: Fn::ImportValue: !Sub '${ExportPrefix}NAT2EIP' Tags: - Key: Name Value: !Join [' ', [!Ref 'AWS::StackName', sg]] VpcId: Fn::ImportValue: !Sub "${ExportPrefix}VPCID" SecurityGroupIngress: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref SecurityGroup IpProtocol: "-1" FromPort: -1 ToPort: -1 SourceSecurityGroupId: !Ref SecurityGroup AnsibleRepoPinSHA: Type: AWS::SSM::Parameter Properties: Description: "The dc-deployments-automation commit SHA that all nodes in the cluster will use" Name: !Sub "/${AWS::StackName}/pinned-ansible-sha" Type: String AllowedPattern: '^(latest)|([0-9a-f]{5,40})$' Value: "latest" # Optional: Cloudwatch dashboard to be created when CloudWatch is enabled CloudWatchDashboard: Condition: EnableCloudWatch Type: AWS::CloudFormation::Stack Properties: TemplateURL: !Sub - https://${QSS3BucketName}.${QSS3Region}.amazonaws.com/${QSS3KeyPrefix}submodules/quickstart-atlassian-services/templates/quickstart-cloudwatch-dashboard.yaml - QSS3Region: !If ["GovCloudCondition", "s3-us-gov-west-1", "s3"] Parameters: ProductStackName: !Sub "${AWS::StackName}" ProductFamilyName: "bitbucket" AsgToMonitor: !Ref ClusterNodeGroup Outputs: ClusterNodeGroup: Description: The name of the auto scaling group of cluster nodes Value: !Ref ClusterNodeGroup DBEndpointAddress: Description: The Database Connection String Value: !If [DBEngineAurora, !GetAtt DBCluster.Outputs.RDSEndPointAddress, !GetAtt DB.Endpoint.Address] DBMaster: Description: The RDS ARN to use when creating a Data Center standby stack Value: !If [DBEngineAurora, '', !If [NotStandbyMode, !Sub ["arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:db:${DB}", {DB: !Ref "DB"}], !Ref "AWS::NoValue"]] ServiceURL: Description: The URL of the Bitbucket Data Center instance Value: !If - UseCustomDnsName - !Sub - "${HTTP}://${CustomDNSName}" - HTTP: !If [DoSSL, https, http] CustomDNSName: !Ref CustomDnsName - !Sub - "${HTTP}://${LoadBalancerDNSName}" - HTTP: !If [DoSSL, https, http] LoadBalancerDNSName: !GetAtt LoadBalancer.DNSName LoadBalancerURL: Description: The Load Balancer URL Value: !Sub - "${HTTP}://${LoadBalancerDNSName}" - HTTP: !If [DoSSL, 'https', 'http'] LoadBalancerDNSName: !GetAtt LoadBalancer.DNSName SGname: Description: The name of the SecurityGroup Value: !Ref SecurityGroup Export: { Name: !Join ['', [!Ref 'AWS::StackName', -SGname]] } CloudWatchDashboardURL: Description: CloudWatch monitoring dashboard URL Value: !GetAtt CloudWatchDashboard.Outputs.Dashboard Condition: EnableCloudWatch