diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 01623f8..9cc75e2 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -2,11 +2,41 @@ # Change Log All notable changes to this project will be documented in this file. +## [6.0.0] - 2024-11-29 + +### Fixed +- Bootstraping EC2 IP evaluation +- route53-handler script does its work again +- fixed install script step +- fixed links for scalelite downloads when bootstrapping +- Greenlight user creation and bootup fixed + +### Changed +- pin platform for docker push/pull to linux/amd64 to ensure scripts are functional for arm based deployment systems +- upgrade of the Aurora PostgrSQL Engine Version to 16.4 +- upgrade of the (default) BBB version to 2.7.X +- removed the validation step of the Amazon Cloudformation templates to speed up setup. +- switch to valkey 7.2 +- reorganization and optimizing the templates for better maintainability and readability +- setup.sh and destroy.sh are now shell agnostic and improved +- stripped down new to create vpc to the minimum needed to remove complexity for the sample code +- rewriten handler scripts for better logging, debugging, maintainability +- removal of single instance install for cleaner repo +- removal of EnvironmentType Switch/Variables. +- removal of dev/stage/prod switch, we assume this a test/tinker environment. It ensures cleaner, simple templates +- make the version settings for bbb-scalable more dynamic and depend on the set application version. +- added scalelite managament script to delete instances from the LB and debug if needed directly from the console + + +### Upgrade Notes: +- Upgrading the Amazon Aurora Postgres Engine version and/or the switch to Aurora Serverless v2 via Amazon Cloudformation is not supported. Please refer to the official [upgrade documentation](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_UpgradeDBInstance.PostgreSQL.html) and [migrate to Aurora Serverless v2](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.upgrade.html) if you have a already running environment using an older engine version and/or Aurora Serverless v1 to proceed with the upgrade before using the Cloudformation deployment. +- This is an overall rewrite/remake of a lot of things, please do a new deployment rather than than an inplace upgrade. If you desperately want to update inplace: Do proper backups first! ## [5.0.1] - 2023-08-02 ### Fixed: - typo at setup.sh + ## [5.0.0] - 2023-06-15 ### Upgrade Notes: - Upgrading the Amazon Aurora Postgres Engine version and/or the switch to Aurora Serverless v2 via Amazon Cloudformation is not supported. Please refer to the official [upgrade documentation](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_UpgradeDBInstance.PostgreSQL.html) and [migrate to Aurora Serverless v2](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.upgrade.html) if you have a already running environment using an older engine version and/or Aurora Serverless v1 to proceed with the upgrade before using the Cloudformation deployment. diff --git a/README.md b/README.md index 5def496..7cf60d4 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ These parameters you have to pass to the [setup script](./setup.sh) | -h | the hosted zone ID the DNS records to be added | | -s | the Cloudformation stack name you want to use | | -d | the FQDN for (aligned to the hosted zone ) | - +| -l | the log level choose DEBUG, INFO, WARN, ERROR | **Deployment parameters:** @@ -84,18 +84,17 @@ The deployment parameters are placed into the bbb-on-aws-param.json or to be set | Parameter Name | Default Value | Description | Comment | | ---- | ---- | ---- | ---- | -| BBBApplicationVersion | focal-260 | Big Blue Button Version to be deployed | Refer to the Big Blue Button [documentation](https://docs.bigbluebutton.org/) to check for supported versions. | +| BBBApplicationVersion | focal-270 | Big Blue Button Version to be deployed | Refer to the Big Blue Button [documentation](https://docs.bigbluebutton.org/) to check for supported versions. | | BBBApplicationInstanceAMIParameter | /aws/service/canonical/ubuntu/server/20.04/stable/current/amd64/hvm/ebs-gp2/ami-id | Big Blue Button Application Instance AMI Parameter to be resolved | Refer to the Big Blue Button [documentation](https://docs.bigbluebutton.org/) to check for supported versions of Ubuntu for the application version you set using "BBBApplicationVersion" parameter. | -| BBBECSInstanceType| t3a.large| Instance size of the ECS Cluster worker nodes or "fargate" for serverless deployment | EC2 instance sizes should be aligned with the size VCPU and Memory limits of the to be deployed tasks. setting this parameter to fargate will cause a Serverless Setup using AWS Fargate | +| BBBECSInstanceType| fargate | Instance size of the ECS Cluster worker nodes or "fargate" for serverless deployment | EC2 instance sizes should be aligned with the size VCPU and Memory limits of the to be deployed tasks. setting this parameter to fargate will cause a Serverless Setup using AWS Fargate | | BBBApplicationInstanceType| t3a.xlarge| Instance size of the Big Blue Button Application node(s) | please refer to the Big Blue Button [Documentation](https://docs.bigbluebutton.org/2.2/install.html#minimum-server-requirements) for rightsizing | | BBBApplicationDataVolumeSize | 20 | the size of the application data volume used for recording buffer | | BBBApplicationRootVolumeSize | 20 | the size of the application root volume | -| BBBDBInstanceType| db.t3.medium| Instance size of the Aurora Database Instance or "serverless" for serverless deployment | Heavily related to usage, collect metrics and test. -| BBBCACHEDBInstanceType| cache.t3.micro| Instance size of the Redis security token and call ID handling | Depends on usage. +| BBBDBInstanceType | db.t3.medium| Instance size of the Aurora Database Instance or "serverless" for serverless deployment | Heavily related to usage, collect metrics and test. +| BBBCACHEDBInstanceType | cache.t3.micro| Instance size of the Amazon ElastiCache for security token and call ID handling | Depends on usage. | BBBVPC| 10.1.0.0/16 | The Cidr block or ID for the VPC created during the deployment | we deploy an own VPC for the deployment containing public and private subnets as well nas internet and nat gateways. If an ID is passed over (vpc-*) the deployment will use the existing custom VPC and it's subnets. be sure to add the subnet ids into the parameters as well! -| BBBPrivateApplicationSubnets| 10.1.5.0/24,10.1.6.0/24,10.1.7.0/24 | The cidr blocks or IDs of subnets within the VPC for the non-public components of the application deployment | count have to be = BBBNumberOfAZs -| BBBPrivateDBSubnets| 10.1.9.0/24,10.1.10.0/24,10.1.11.0/24| The cidr blocks or IDs of subnets within the VPC for the database backend. | count have to be = BBBNumberOfAZs -| BBBPublicApplicationSubnets| 10.1.15.0/24,10.1.16.0/24,10.1.17.0/24| The cidr blocks or IDs of subnets within the VPC for the direct public accessible application components | count have to be = BBBNumberOfAZs +| BBBApplicationSubnets | 10.1.5.0/24,10.1.6.0/24,10.1.7.0/24 | The cidr blocks or IDs of subnets within the VPC for the components of the application deployment | count have to be = BBBNumberOfAZs only to set for existing VPCs +| BBBDatastoreSubnets | 10.1.9.0/24,10.1.10.0/24,10.1.11.0/24| The cidr blocks or IDs of subnets within the VPC for the database backend. | count have to be = BBBNumberOfAZs only set for existing VPCS | BBBNumberOfAZs | 3 | Number of AZs to be utilized by the deployment | valid value 1,2 or 3 | BBBECSMaxInstances| 10| The maximum amount of instances the ECS cluster should scale out to | set a reasonable maximum to prevent cost explosion on unexpected usage | BBBECSMinInstances| 1| The minimum amount of worker instances at the ECS cluster| @@ -103,18 +102,16 @@ The deployment parameters are placed into the bbb-on-aws-param.json or to be set | BBBApplicationMaxInstances| 1| The maximum amount of Big Blue Button Application servers | Set depending on the awaited load and planned instance size. | | BBBApplicationMinInstances| 1| The minimum amount of Big Blue Button Application servers | As EC2 Autoscaling is currently not aware of ongoing video conferences, i recommend set min=max=desired and not use dynamic here (planned scale out/in) | | BBBApplicationDesiredInstances| 1| The desired amount of Big Blue Button Application servers | As EC2 Autoscaling is currently not aware of ongoing video conferences, i recommend set min=max=desired and not use dynamic here (planned scale out/in) | -| BBBDBEngineVersion| 10.7| Set the Postgres version to be used at the Amazon Aurora setup | please refer to the Amazon Aurora [documentation](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.Updates.20180305.html) for supported versions -| BBBEnvironmentStage| dev | can be set to "dev","stage" or "prod" | currently stage or prod does change the Amazon Aurora Setup to a Multi-AZ Setup and adds a 2nd Nat-Gateway to the deployment. +| BBBDBEngineVersion| 16.4| Set the Postgres version to be used at the Amazon Aurora setup | please refer to the Amazon Aurora [documentation](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.Updates.20180305.html) for supported versions | BBBServerlessAuroraMinCapacity | The minimum capacity for the Amazon Aurora Serverless Cluster. | Value has to be >= 2 | BBBServerlessAuroraMaxCapacity | The maximum capacity for the Amazon Aurora Serverless Cluster. -| BBBEnvironmentName| bbbonaws| the name of the environment -| BBBEnvironmentType| scalable| can be either "scalable" or "single" | scalable for full scalable deployments. Single does leave out the ECS cluster, scalelite inner-application load balancer and Databases and installs Big Blue Button and Greenlight on a single EC2 instance and a turn server instance. -| BBBgreenlightImage| bigbluebutton/greenlight:v2| greenlight container image to be used -| BBBScaleliteApiImage| blindsidenetwks/scalelite:v1-api| scalelite api container image to be used -| BBBScaleliteNginxImage| blindsidenetwks/scalelite:v1-nginx| scalelite nginx container image to be used -| BBBScalelitePollerImage| blindsidenetwks/scalelite:v1-poller| scalelite poller container image to be used -| BBBScaleliteImporterImage| blindsidenetwks/scalelite:v1-recording-importer| scalelite recording importer container image to be used -| BBBCacheAZMode| cross-az| Deploy the Redis cluster cross-az or single-az | only cross-az supported atm +| BBBEnvironmentName | bbbonaws| the name of the environment +| BBBgreenlightImage | bigbluebutton/greenlight:v3.4.1| greenlight container image to be used +| BBBScaleliteApiImage | blindsidenetwks/scalelite:v1.6-api| scalelite api container image to be used +| BBBScaleliteNginxImage | blindsidenetwks/scalelite:v1.6-nginx| scalelite nginx container image to be used +| BBBScalelitePollerImage | blindsidenetwks/scalelite:v1.6-poller| scalelite poller container image to be used +| BBBScaleliteImporterImage | blindsidenetwks/scalelite:v1.6-recording-importer| scalelite recording importer container image to be used +| BBBCacheAZMode| cross-az | Deploy the Amazon Elasticcache cluster cross-az or single-az | only cross-az supported atm | BBBGreenlightMemory| 1024 | memory limit of the Greenlight task | BBBGreenlightCPU| 512| vCPU limit of the Greenlight task | BBBScaleliteMemory | 2048 | Memory limit for the Scalelite tasks | setting per task for all inheritated containers @@ -123,7 +120,7 @@ The deployment parameters are placed into the bbb-on-aws-param.json or to be set | BBBSESValidated| false | controls if a pre validated SES domain is used | set to true if you setup the SES domain outside of this deployment | BBBACMCertArn | - | existing SSL/TLS Certificate ARN for HTTPS | add your Certificate ARN here. e.g. if you imported your own Cert into ACM. | BBBFrontendType | Greenlight | choose "Greenlight" for deploying a scalable Greenlight Frontend and "External" to only get the Scalelite API endpoint to be able to connect an externally managed LMS" - +| BBBUsePublicApplicationIP | ENABLED | Automatic Public IPs for ECS Tasks ENABLED/DISABLED - enabled for default deployment incl VPC creation # Deployment ## Automatic @@ -223,9 +220,9 @@ The Deployment consists of 2 main templates and 13 nested templates. *The deployment of Amazon Aurora is needed to provide a database for Greenlight and Scalelite where the video conference schedules, user data and recording information are persistent* --- -- Deploy Amazon Elasticache (Redis): [bbb-on-aws-cachedb.template.yaml](./templates/bbb-on-aws-cachedb.template.yaml) +- Deploy Amazon Elasticache: [bbb-on-aws-cachedb.template.yaml](./templates/bbb-on-aws-cachedb.template.yaml) - *This template deploys an Amazon Elasticache (redis) cluster where security token and conference IDs are located for the call handling via Scalelite* + *This template deploys an Amazon Elasticache cluster where security token and conference IDs are located for the call handling via Scalelite* --- - Fire up the ECS Cluster: [bbb-on-aws-ecs.template.yaml](./templates/bbb-on-aws-ecs.template.yaml) diff --git a/bbb-on-aws-param.json b/bbb-on-aws-param.json index abf0dae..317abff 100755 --- a/bbb-on-aws-param.json +++ b/bbb-on-aws-param.json @@ -1,6 +1,6 @@ { "Parameters" : { - "BBBApplicationVersion": "focal-260", + "BBBApplicationVersion": "focal-270", "BBBApplicationInstanceAMIParameter": "/aws/service/canonical/ubuntu/server/20.04/stable/current/amd64/hvm/ebs-gp2/ami-id", "BBBECSInstanceType": "fargate", "BBBApplicationInstanceType": "t3a.medium", @@ -9,32 +9,29 @@ "BBBDBInstanceType": "db.serverless", "BBBServerlessAuroraMinCapacity": 0.5, "BBBServerlessAuroraMaxCapacity": 2, - "BBBCACHEDBInstanceType": "cache.t3.micro", - "BBBVPCs": "10.1.0.0/16", - "BBBPrivateApplicationSubnets": "10.1.5.0/24,10.1.6.0/24,10.1.7.0/24", - "BBBPrivateDBSubnets": "10.1.9.0/24,10.1.10.0/24,10.1.11.0/24", - "BBBPublicApplicationSubnets": "10.1.15.0/24,10.1.16.0/24,10.1.17.0/24", - "BBBNumberOfAZs": 3, + "BBBCACHEDBInstanceType": "cache.t4g.micro", + "BBBVPCDEF": "10.1.0.0/16", + "BBBDatastoreSubnets": "10.1.5.0/24,10.1.6.0/24,10.1.7.0/24", + "BBBApplicationSubnets": "10.1.8.0/24,10.1.9.0/24,10.1.10.0/24", "BBBECSMaxInstances": 3, "BBBECSMinInstances": 1, "BBBECSDesiredInstances": 1, "BBBApplicationMaxInstances": 1, "BBBApplicationMinInstances": 1, "BBBApplicationDesiredInstances": 1, - "BBBDBEngineVersion": "14.3", - "BBBEnvironmentStage": "dev", + "BBBDBEngineVersion": "16.4", "BBBEnvironmentName": "bbbonaws", - "BBBEnvironmentType": "scalable", - "BBBgreenlightImageTag": "v3", - "BBBScaleliteApiImageTag": "v1.4-api", - "BBBScaleliteNginxImageTag": "v1.4-nginx", - "BBBScalelitePollerImageTag": "v1.4-poller", - "BBBScaleliteImporterImageTag": "v1.4-recording-importer", + "BBBgreenlightImageTag": "v3.4.1", + "BBBScaleliteApiImageTag": "v1.6-api", + "BBBScaleliteNginxImageTag": "v1.6-nginx", + "BBBScalelitePollerImageTag": "v1.6-poller", + "BBBScaleliteImporterImageTag": "v1.6-recording-importer", "BBBCacheAZMode": "cross-az", "BBBGreenlightMemory": 1024, "BBBGreenlightCPU": 512, "BBBScaleliteMemory": 2048, "BBBScaleliteCPU": 1024, - "BBBFrontendType": "Greenlight" + "BBBFrontendType": "Greenlight", + "BBBUsePublicApplicationIP": "ENABLED" } } \ No newline at end of file diff --git a/bbb-on-aws-root.template.yaml b/bbb-on-aws-root.template.yaml index dd7a358..1a77082 100644 --- a/bbb-on-aws-root.template.yaml +++ b/bbb-on-aws-root.template.yaml @@ -9,36 +9,26 @@ Description: > Authors: David Surey Parameters: - BBBVPCs: + BBBVPCDEF: Description: Please enter the IP range (CIDR notation) for the BBB VPC Type: String Default: 10.1.0.0/16 - BBBNumberOfAZs: - Description: Amount of Availability Zones to utilize - Type: Number - AllowedValues: - - 1 - - 2 - - 3 - Default: 3 - BBBPrivateApplicationSubnets: + BBBApplicationSubnets: Description: Comma separated list of the private subnets for instances Type: CommaDelimitedList Default: "10.1.5.0/24,10.1.6.0/24,10.1.7.0/24" - BBBPrivateDBSubnets: + BBBDatastoreSubnets: Description: Comma separated list of the private subnets for Ddtabases Type: CommaDelimitedList - Default: "10.1.9.0/24,10.1.10.0/24,10.1.11.0/24" - BBBPublicApplicationSubnets: - Description: Comma separated list of the appserver's subnets - Type: CommaDelimitedList - Default: "10.1.15.0/24,10.1.16.0/24,10.1.17.0/24" + Default: "10.1.8.0/24,10.1.9.0/24,10.1.10.0/24" BBBApplicationVersion: Description: Version of the Big Blue Button Application Type: String - Default: focal-260 + Default: focal-270 AllowedValues: - focal-260 + - focal-270 + - focal-300 BBBApplicationInstanceAMIParameter: Description: Ubuntu Version to be deployed for Application Instances Default: "/aws/service/canonical/ubuntu/server/20.04/stable/current/amd64/hvm/ebs-gp2/ami-id" @@ -78,13 +68,15 @@ Parameters: Type: String Default: 14.3 BBBECSInstanceType: - Description: Instance type for ECS Cluster worker nodes + Description: Compute type for ECS Cluster (FARGATE or EC2 instance type, x86_64 only) Type: String Default: fargate + AllowedPattern: ^(fargate|(t2|t3|t3a|m4|m5|m5a|m6i|c4|c5|c5a|c6i|r4|r5|r5a|r6i)\.(medium|large|xlarge|2xlarge|4xlarge|8xlarge|12xlarge|16xlarge|24xlarge))$ BBBApplicationInstanceType: - Description: Instance type for the appserver + Description: Instance type for BBB Application Server (x86_64 only) Type: String - Default: t3a.xlarge + Default: c5.2xlarge + AllowedPattern: ^(t3|t3a|m5|m5a|m5n|m5zn|m6i|m6a|m6in|c5|c5a|c5n|c6i|c6a|c6in|r5|r5a|r5b|r5n|r6i|r6a|r6in)\.(medium|large|xlarge|2xlarge|4xlarge|8xlarge|12xlarge|16xlarge|24xlarge)$ BBBApplicationDataVolumeSize: Description: Size of the application instance data volume Type: Number @@ -94,9 +86,10 @@ Parameters: Type: Number Default: 20 BBBDBInstanceType: - Description: DB RDS instance type + Description: DB instance type for Aurora PostgreSQL Type: String Default: db.serverless + AllowedPattern: ^(db\.(serverless|(t3|t4g|r5|r6g|r6i|r7g)\.(medium|large|xlarge|2xlarge|4xlarge|8xlarge|12xlarge|16xlarge|24xlarge)))$ BBBServerlessAuroraMinCapacity: Description: The minimum capacity for the Amazon Aurora Serverless Cluster. Type: Number @@ -106,24 +99,10 @@ Parameters: Type: Number Default: 4 BBBCACHEDBInstanceType: - Description: Instance type for Amazon ElastiCache (Redis) - Type: String - Default: cache.t3.medium - BBBEnvironmentStage: + Description: Instance type for Amazon ElastiCache Type: String - Description: Select the appropriate environment - AllowedValues: - - stage - - prod - - dev - Default: dev - BBBEnvironmentType: - Type: String - Description: Scalable or single-instance Setup - AllowedValues: - - scalable - - single - Default: scalable + Default: cache.t4g.micro + AllowedPattern: ^(cache\.(t3|t4g|m6g|m7g|r6g|r7g)\.(micro|small|medium|large|xlarge|2xlarge|4xlarge|8xlarge|12xlarge|16xlarge))$ BBBEnvironmentName: Description: An environment name that is prefixed to resource names Type: String @@ -227,12 +206,20 @@ Parameters: AllowedValues: - Greenlight - External + BBBNumberOfAZs: + Type: Number + Default: 2 + AllowedValues: [1, 2, 3] + Description: Number of Availability Zones to use + BBBUsePublicApplicationIP: + Type: String + AllowedValues: + - ENABLED + - DISABLED + Default: ENABLED + Conditions: - BBBProdEnvironment: !Equals [!Ref BBBEnvironmentStage, prod] - BBBStageEnvironment: !Not [!Equals [!Ref BBBEnvironmentStage, prod]] - BBBScalableEnvironment: !Equals [!Ref BBBEnvironmentType, scalable] - BBBSingleEnvironment: !Equals [!Ref BBBEnvironmentType, single] - BBBExistingVPC: !And [ !Not [ !Equals [ !Ref BBBVPCs, "" ]], !Equals [ !Select [ 0, !Split [ vpc-, !Ref BBBVPCs ]], "" ]] + BBBExistingVPC: !And [ !Not [ !Equals [ !Ref BBBVPCDEF, "" ]], !Equals [ !Select [ 0, !Split [ vpc-, !Ref BBBVPCDEF ]], "" ]] BBBNewVPC: !Not [ Condition: BBBExistingVPC ] BBBSESNotValidated: !Equals [false, !Ref BBBSESValidated] BBBGreenlight: !Equals [!Ref BBBFrontendType, Greenlight] @@ -334,8 +321,6 @@ Resources: Fn::Sub: BBBSESProviderStack-${BBBEnvironmentName} - Key: Environment Value: !Ref BBBEnvironmentName - - Key: Stage - Value: !Ref BBBEnvironmentStage BBBNetworkStack: Type: AWS::CloudFormation::Stack @@ -348,34 +333,15 @@ Resources: - Fn::ImportValue: !Sub "${BBBStackBucketStack}-BBBStackBucket" - "/bbb-on-aws-network.template.yaml" Parameters: - BBBVPCs: - Ref: BBBVPCs - BBBNumberOfAZs: - Ref: BBBNumberOfAZs - BBBPrivateApplicationSubnets: - Fn::Join: - - "," - - Ref: BBBPrivateApplicationSubnets - BBBPrivateDBSubnets: - Fn::Join: - - "," - - Ref: BBBPrivateDBSubnets - BBBPublicApplicationSubnets: - Fn::Join: - - "," - - Ref: BBBPublicApplicationSubnets - BBBEnvironmentStage: - Ref: BBBEnvironmentStage - BBBEnvironmentType: - Ref: BBBEnvironmentType + BBBVPCDEF: + Ref: BBBVPCDEF + BBBNumberOfAZs: !Ref BBBNumberOfAZs Tags: - Key: Name Value: Fn::Sub: BBBNetworkStack-${BBBEnvironmentName} - Key: Environment Value: !Ref BBBEnvironmentName - - Key: Stage - Value: !Ref BBBEnvironmentStage BBBSecurityGroupStack: Type: AWS::CloudFormation::Stack @@ -387,15 +353,13 @@ Resources: - Fn::ImportValue: !Sub "${BBBStackBucketStack}-BBBStackBucket" - "/bbb-on-aws-securitygroups.template.yaml" Parameters: - BBBVPCs: + BBBVPC: Fn::If: - BBBNewVPC - Fn::GetAtt: - BBBNetworkStack - - Outputs.BBBVPCs - - Ref: BBBVPCs - BBBEnvironmentType: - Ref: BBBEnvironmentType + - Outputs.BBBVPC + - Ref: BBBVPCDEF BBBECSInstanceType: Ref: BBBECSInstanceType BBBFrontendType: @@ -406,12 +370,9 @@ Resources: Fn::Sub: BBBSecurityGroupStack-${BBBEnvironmentName} - Key: Environment Value: !Ref BBBEnvironmentName - - Key: Stage - Value: !Ref BBBEnvironmentStage BBBStorageStack: Type: AWS::CloudFormation::Stack - Condition: BBBScalableEnvironment Properties: TemplateURL: Fn::Join: @@ -420,17 +381,16 @@ Resources: - Fn::ImportValue: !Sub "${BBBStackBucketStack}-BBBStackBucket" - "/bbb-on-aws-storage.template.yaml" Parameters: - BBBNumberOfAZs: - Ref: BBBNumberOfAZs - BBBPrivateApplicationSubnets: + BBBNumberOfAZs: !Ref BBBNumberOfAZs + BBBDatastoreSubnets: Fn::If: - BBBNewVPC - Fn::GetAtt: - BBBNetworkStack - - Outputs.BBBPrivateApplicationSubnets + - Outputs.BBBDatastoreSubnets - Fn::Join: - "," - - Ref: BBBPrivateApplicationSubnets + - Ref: BBBDatastoreSubnets BBBSharedStorageSecurityGroup: Fn::GetAtt: - BBBSecurityGroupStack @@ -441,12 +401,9 @@ Resources: Fn::Sub: BBBStorageStack-${BBBEnvironmentName} - Key: Environment Value: !Ref BBBEnvironmentName - - Key: Stage - Value: !Ref BBBEnvironmentStage BBBDatabaseStack: Type: AWS::CloudFormation::Stack - Condition: BBBScalableEnvironment Properties: TemplateURL: Fn::Join: @@ -463,35 +420,30 @@ Resources: Ref: BBBServerlessAuroraMinCapacity BBBServerlessAuroraMaxCapacity: Ref: BBBServerlessAuroraMaxCapacity - BBBPrivateDBSubnets: + BBBDatastoreSubnets: Fn::If: - BBBNewVPC - Fn::GetAtt: - BBBNetworkStack - - Outputs.BBBPrivateDBSubnets + - Outputs.BBBDatastoreSubnets - Fn::Join: - "," - - Ref: BBBPrivateDBSubnets + - Ref: BBBDatastoreSubnets BBBDBEngineVersion: Ref: BBBDBEngineVersion BBBDBSecurityGroup: Fn::GetAtt: - BBBSecurityGroupStack - Outputs.BBBDBSecurityGroup - BBBEnvironmentStage: - Ref: BBBEnvironmentStage Tags: - Key: Name Value: Fn::Sub: BBBDatabaseStack-${BBBEnvironmentName} - Key: Environment Value: !Ref BBBEnvironmentName - - Key: Stage - Value: !Ref BBBEnvironmentStage BBBCacheDBStack: Type: AWS::CloudFormation::Stack - Condition: BBBScalableEnvironment Properties: TemplateURL: Fn::Join: @@ -504,15 +456,15 @@ Resources: Ref: BBBNotificationTopic BBBCACHEDBInstanceType: Ref: BBBCACHEDBInstanceType - BBBPrivateDBSubnets: + BBBDatastoreSubnets: Fn::If: - BBBNewVPC - Fn::GetAtt: - BBBNetworkStack - - Outputs.BBBPrivateDBSubnets + - Outputs.BBBDatastoreSubnets - Fn::Join: - "," - - Ref: BBBPrivateDBSubnets + - Ref: BBBDatastoreSubnets BBBCACHEDBSecurityGroup: Fn::GetAtt: - BBBSecurityGroupStack @@ -523,12 +475,9 @@ Resources: Fn::Sub: BBBCACHEDDBStack-${BBBEnvironmentName} - Key: Environment Value: !Ref BBBEnvironmentName - - Key: Stage - Value: !Ref BBBEnvironmentStage BBBECSStack: Type: AWS::CloudFormation::Stack - Condition: BBBScalableEnvironment Properties: TemplateURL: Fn::Join: @@ -539,15 +488,15 @@ Resources: Parameters: BBBNotificationTopic: Ref: BBBNotificationTopic - BBBPrivateApplicationSubnets: + BBBApplicationSubnets: Fn::If: - BBBNewVPC - Fn::GetAtt: - BBBNetworkStack - - Outputs.BBBPrivateApplicationSubnets + - Outputs.BBBApplicationSubnets - Fn::Join: - "," - - Ref: BBBPrivateApplicationSubnets + - Ref: BBBApplicationSubnets BBBECSInstanceType: Ref: BBBECSInstanceType BBBECSTaskSecurityGroup: @@ -566,19 +515,16 @@ Resources: Fn::Sub: BBBECSStack-${BBBEnvironmentName} - Key: Environment Value: !Ref BBBEnvironmentName - - Key: Stage - Value: !Ref BBBEnvironmentStage BBBAppStack: Type: AWS::CloudFormation::Stack - Condition: BBBScalableEnvironment Properties: TemplateURL: Fn::Join: - "" - - "https://s3.amazonaws.com/" - Fn::ImportValue: !Sub "${BBBStackBucketStack}-BBBStackBucket" - - "/bbb-on-aws-bbbappscalable.template.yaml" + - "/bbb-on-aws-application.template.yaml" Parameters: BBBApplicationVersion: Ref: BBBApplicationVersion @@ -610,24 +556,15 @@ Resources: Fn::GetAtt: - BBBSecurityGroupStack - Outputs.BBBECSTaskSecurityGroup - BBBPublicApplicationSubnets: + BBBApplicationSubnets: Fn::If: - BBBNewVPC - Fn::GetAtt: - BBBNetworkStack - - Outputs.BBBPublicApplicationSubnets + - Outputs.BBBApplicationSubnets - Fn::Join: - "," - - Ref: BBBPublicApplicationSubnets - BBBPrivateApplicationSubnets: - Fn::If: - - BBBNewVPC - - Fn::GetAtt: - - BBBNetworkStack - - Outputs.BBBPrivateApplicationSubnets - - Fn::Join: - - "," - - Ref: BBBPrivateApplicationSubnets + - Ref: BBBApplicationSubnets BBBApplicationInstanceType: Ref: BBBApplicationInstanceType BBBApplicationDataVolumeSize: @@ -668,83 +605,9 @@ Resources: Fn::Sub: BBBApplicationStack-${BBBEnvironmentName} - Key: Environment Value: !Ref BBBEnvironmentName - - Key: Stage - Value: !Ref BBBEnvironmentStage - - BBBAppStackSingle: - Type: AWS::CloudFormation::Stack - Condition: BBBSingleEnvironment - Properties: - TemplateURL: - Fn::Join: - - "" - - - "https://s3.amazonaws.com/" - - Fn::ImportValue: !Sub "${BBBStackBucketStack}-BBBStackBucket" - - "/bbb-on-aws-bbbappsingle.template.yaml" - Parameters: - BBBApplicationVersion: - Ref: BBBApplicationVersion - BBBStackBucketStack: - Fn::ImportValue: !Sub "${BBBStackBucketStack}-BBBStackBucket" - BBBOperatorEMail: - Ref: BBBOperatorEMail - BBBNotificationTopic: - Ref: BBBNotificationTopic - BBBSystemLogsGroupArn: - Fn::GetAtt: - - BBBSystemLogsGroup - - Arn - BBBSystemLogsGroup: - Ref: BBBSystemLogsGroup - BBBApplicationLogsGroupArn: - Fn::GetAtt: - - BBBApplicationLogsGroup - - Arn - BBBApplicationLogsGroup: - Ref: BBBApplicationLogsGroup - BBBApplicationSecurityGroup: - Fn::GetAtt: - - BBBSecurityGroupStack - - Outputs.BBBApplicationSecurityGroup - BBBPublicApplicationSubnets: - Fn::If: - - BBBNewVPC - - Fn::GetAtt: - - BBBNetworkStack - - Outputs.BBBPublicApplicationSubnets - - Fn::Join: - - "," - - Ref: BBBPublicApplicationSubnets - BBBApplicationInstanceAMI: - Ref: BBBApplicationInstanceAMIParameter - BBBApplicationInstanceType: - Ref: BBBApplicationInstanceType - BBBApplicationDataVolumeSize: - Ref: BBBApplicationDataVolumeSize - BBBApplicationRootVolumeSize: - Ref: BBBApplicationRootVolumeSize - BBBApplicationMaxInstances: - Ref: BBBApplicationMaxInstances - BBBApplicationMinInstances: - Ref: BBBApplicationMinInstances - BBBApplicationDesiredInstances: - Ref: BBBApplicationDesiredInstances - BBBHostedZone: - Ref: BBBHostedZone - BBBDomainName: - Ref: BBBDomainName - Tags: - - Key: Name - Value: - Fn::Sub: BBBApplicationStackSingle-${BBBEnvironmentName} - - Key: Environment - Value: !Ref BBBEnvironmentName - - Key: Stage - Value: !Ref BBBEnvironmentStage BBBFontendAppsStack: Type: AWS::CloudFormation::Stack - Condition: BBBScalableEnvironment Properties: TemplateURL: Fn::Join: @@ -753,13 +616,13 @@ Resources: - Fn::ImportValue: !Sub "${BBBStackBucketStack}-BBBStackBucket" - "/bbb-on-aws-frontendapps.template.yaml" Parameters: - BBBVPCs: + BBBVPC: Fn::If: - BBBNewVPC - Fn::GetAtt: - BBBNetworkStack - - Outputs.BBBVPCs - - Ref: BBBVPCs + - Outputs.BBBVPC + - Ref: BBBVPCDEF BBBRDSDBConnectionSecret: Fn::GetAtt: - BBBDatabaseStack @@ -770,15 +633,17 @@ Resources: Fn::GetAtt: - BBBECSStack - Outputs.BBBECSCluster - BBBPublicApplicationSubnets: + BBBApplicationSubnets: Fn::If: - BBBNewVPC - Fn::GetAtt: - BBBNetworkStack - - Outputs.BBBPublicApplicationSubnets + - Outputs.BBBApplicationSubnets - Fn::Join: - "," - - Ref: BBBPublicApplicationSubnets + - Ref: BBBApplicationSubnets + BBBUsePublicApplicationIP: + Ref: BBBUsePublicApplicationIP BBBFrontendELBSecurityGroup: Fn::If: - BBBGreenlight @@ -874,15 +739,6 @@ Resources: Ref: BBBOperatorEMail BBBECSInstanceType: Ref: BBBECSInstanceType - BBBPrivateApplicationSubnets: - Fn::If: - - BBBNewVPC - - Fn::GetAtt: - - BBBNetworkStack - - Outputs.BBBPrivateApplicationSubnets - - Fn::Join: - - "," - - Ref: BBBPrivateApplicationSubnets BBBECSTaskSecurityGroup: Fn::GetAtt: - BBBSecurityGroupStack @@ -897,8 +753,6 @@ Resources: Fn::Sub: BBBFrontendApplicationStack-${BBBEnvironmentName} - Key: Environment Value: !Ref BBBEnvironmentName - - Key: Stage - Value: !Ref BBBEnvironmentStage Outputs: BBBNotificationTopic: diff --git a/destroy.sh b/destroy.sh index f2467d5..659c21d 100755 --- a/destroy.sh +++ b/destroy.sh @@ -1,106 +1,304 @@ -#!/bin/bash -# This is a simple bash script for the BBB Application Infrastructure deployment. -# It basically glues together the parts running in loose coupeling during the deployment and helps to speed things up which -# otherwise would have to be noted down and put into the command line. -# This can be migrated into real orchestration / automation toolsets if needed (e.g. Ansible, Puppet or Terraform) +#!/bin/sh +# BBB Application Infrastructure destruction script +# Author: David Surey - suredavi@amazon.de +# Disclaimer: NOT FOR PRODUCTION USE - Only for demo and testing purposes -# created by David Surey - suredavi@amazon.de -# Disclaimber: NOT FOR PRODUCTION USE - Only for demo and testing purposes +# Color codes for output formatting +RED=$(printf '\033[0;31m') +GREEN=$(printf '\033[0;32m') +YELLOW=$(printf '\033[1;33m') +BLUE=$(printf '\033[0;34m') +NC=$(printf '\033[0m') -ERROR_COUNT=0; +# Logging configuration +LOG_LEVEL=${LOG_LEVEL:-"INFO"} # Default to INFO if not set +LOG_FILE="/tmp/bbb-destroy-$(date +%Y%m%d-%H%M%S).log" -if [[ $# -lt 2 ]] ; then - echo 'arguments missing, please the aws profile string (-p) and the deployment Stack Name (-s)' +# Get numeric value for log level +get_log_level_value() { + case $1 in + "DEBUG") echo 0 ;; + "INFO") echo 1 ;; + "WARN") echo 2 ;; + "ERROR") echo 3 ;; + *) echo 1 ;; # Default to INFO + esac +} + +# Logging function +log() { + level=$1 + shift + message=$* + timestamp=$(date '+%Y-%m-%d %H:%M:%S') + + # Check if level meets minimum level + current_level=$(get_log_level_value "$level") + minimum_level=$(get_log_level_value "$LOG_LEVEL") + + if [ "$current_level" -lt "$minimum_level" ]; then + return 2 + fi + + # Color selection based on level + case $level in + "DEBUG") color=$BLUE ;; + "INFO") color=$GREEN ;; + "WARN") color=$YELLOW ;; + "ERROR") color=$RED ;; + *) color=$NC ;; + esac + + # Output to console with color + printf "%s [%s%s%s] %s\n" "$timestamp" "$color" "$level" "$NC" "$message" + + # Output to log file without color + printf "%s [%s] %s\n" "$timestamp" "$level" "$message" >> "$LOG_FILE" + + # Exit on ERROR level messages + if [ "$level" = "ERROR" ]; then + exit 1 + fi +} + +# Function to monitor CloudFormation stack deletion events in real-time +monitor_stack_deletion() { + local stack_name=$1 + local last_event_time + last_event_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + + log "DEBUG" "Starting deletion monitoring for stack: $stack_name" + + while true; do + if ! aws cloudformation describe-stacks --stack-name "$stack_name" --profile "$BBBPROFILE" >/dev/null 2>&1; then + log "INFO" "Stack $stack_name has been deleted successfully" + return 0 + fi + + events=$(aws cloudformation describe-stack-events \ + --profile "$BBBPROFILE" \ + --stack-name "$stack_name" \ + --query 'StackEvents[?contains(ResourceStatus, `IN_PROGRESS`) || contains(ResourceStatus, `COMPLETE`) || contains(ResourceStatus, `FAILED`)]') + + echo "$events" | jq -r --arg timestamp "$last_event_time" '.[] | select(.Timestamp > $timestamp) | "\(.Timestamp) [\(.LogicalResourceId)] \(.ResourceStatus) - \(.ResourceStatusReason // "No reason provided")"' | while read -r line; do + log "DEBUG" "$line" + done + + # Get stack status + stack_status=$(aws cloudformation describe-stacks \ + --profile "$BBBPROFILE" \ + --stack-name "$stack_name" \ + --query 'Stacks[0].StackStatus' \ + --output text 2>/dev/null) + + if [[ $stack_status =~ .*FAILED$ ]]; then + log "ERROR" "Stack deletion failed with status: $stack_status" + return 1 + fi + + last_event_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + sleep 5 + done +} + +# Function to delete a CloudFormation stack with monitoring +delete_stack() { + local stack_name=$1 + + if aws cloudformation describe-stacks --stack-name "$stack_name" --profile "$BBBPROFILE" > /dev/null 2>&1; then + log "DEBUG" "Deleting stack: $stack_name" + if aws cloudformation delete-stack --stack-name "$stack_name" --profile "$BBBPROFILE"; then + monitor_stack_deletion "$stack_name" + return $? + else + log "ERROR" "Failed to initiate stack deletion for $stack_name" + return 1 + fi + else + log "WARN" "Stack not found: $stack_name" + return 0 + fi +} + +# Input validation function +validate_input() { + param_name=$1 + param_value=$2 + + if [ -z "$param_value" ]; then + log "ERROR" "Parameter ${param_name} is required but not provided" + fi +} + +# Usage information +usage() { + echo "Usage: $0 -p -s " + echo "Options:" + echo " -p : AWS Profile" + echo " -s : Stack Name" + echo " -l : Log Level (DEBUG|INFO|WARN|ERROR) - default: INFO" exit 1 -fi +} + +# Initialize variables +BBBPROFILE="" +BBBSTACK="" -while getopts ":p:s:" opt; do - case $opt in - p) BBBPROFILE="$OPTARG" - ;; - s) BBBSTACK="$OPTARG" - ;; - \?) echo "Invalid option -$OPTARG" >&2 - ;; - esac +# Parse command line arguments +while getopts "p:s:l:" opt; do + case $opt in + p) BBBPROFILE="$OPTARG" ;; + s) BBBSTACK="$OPTARG" ;; + l) + case $OPTARG in + DEBUG|INFO|WARN|ERROR) LOG_LEVEL="$OPTARG" ;; + *) log "ERROR" "Invalid log level: $OPTARG. Valid levels are: DEBUG INFO WARN ERROR" ;; + esac + ;; + *) usage ;; + esac done -if ! [ -x "$(command -v aws)" ]; then - echo 'Error: aws cli is not installed.' >&2 - exit 1 -fi +# Validate all required parameters +log "DEBUG" "Validating input parameters..." +validate_input "AWS Profile" "$BBBPROFILE" +validate_input "Stack Name" "$BBBSTACK" -echo "using AWS Profile $BBBPROFILE" -echo "##################################################" - -# Empty the ECR Repository -BBBECRStack="${BBBSTACK}-registry" -ENVIRONMENTTYPE=$(jq -r ".Parameters.BBBEnvironmentType" bbb-on-aws-param.json) -if [ "$ENVIRONMENTTYPE" == 'scalable' ] -then - echo "Deleting all (cached) images from ECR repository" - echo "##################################################" - GREENLIGHTREGISTRY=$(aws ecr describe-repositories --profile=$BBBPROFILE --query 'repositories[?contains(repositoryName, `greenlight`)].repositoryName' --output text) - SCALELITEREGISTRY=$(aws ecr describe-repositories --profile=$BBBPROFILE --query 'repositories[?contains(repositoryName, `scalelite`)].repositoryName' --output text) - IMAGESGREENLIGHT=$(aws --profile $BBBPROFILE ecr describe-images --repository-name $GREENLIGHTREGISTRY --output json | jq '.[]' | jq '.[]' | jq "select (.imagePushedAt > 0)" | jq -r '.imageDigest') - for IMAGE in ${IMAGESGREENLIGHT[*]}; do - echo "Deleting $IMAGE" - aws ecr --profile $BBBPROFILE batch-delete-image --repository-name $GREENLIGHTREGISTRY --image-ids imageDigest=$IMAGE - done - IMAGESSCALELITE=$(aws --profile $BBBPROFILE ecr describe-images --repository-name $SCALELITEREGISTRY --output json | jq '.[]' | jq '.[]' | jq "select (.imagePushedAt > 0)" | jq -r '.imageDigest') - for IMAGE in ${IMAGESSCALELITE[*]}; do - echo "Deleting $IMAGE" - aws ecr --profile $BBBPROFILE batch-delete-image --repository-name $SCALELITEREGISTRY --image-ids imageDigest=$IMAGE - done +# Check for required tools +log "DEBUG" "Checking required tools..." +if ! command -v aws > /dev/null 2>&1; then + log "ERROR" "aws CLI is not installed" fi -# Destroy the BBB infrastructure. -echo "Delete the BBB Environment" -echo "##################################################" -aws cloudformation delete-stack --profile=$BBBPROFILE --stack-name $BBBSTACK +# Main execution starts here +log "INFO" "Starting BBB destruction with AWS Profile: $BBBPROFILE" +log "INFO" "##################################################" + +# Function to delete all versions of objects in an S3 bucket +cleanup_s3_bucket() { + local bucket_name=$1 + log "INFO" "Cleaning up S3 bucket: $bucket_name" + + # Check if bucket exists + if ! aws s3api head-bucket --bucket "$bucket_name" --profile "$BBBPROFILE" 2>/dev/null; then + log "WARN" "Bucket $bucket_name not found or no access" + return 0 + fi + + log "INFO" "Removing all versions from bucket: $bucket_name" -aws cloudformation wait stack-delete-complete --profile=$BBBPROFILE --stack-name $BBBSTACK + # Delete all versions (including delete markers) + while true; do + # Get versions and delete markers without pager + versions=$(aws s3api list-object-versions \ + --bucket "$bucket_name" \ + --profile "$BBBPROFILE" \ + --output json \ + --no-cli-pager \ + --max-items 1000) + + # Extract version IDs and keys + objects=$(echo "$versions" | jq -r '.Versions[]? | {Key:.Key,VersionId:.VersionId} | select(.VersionId != null)') + markers=$(echo "$versions" | jq -r '.DeleteMarkers[]? | {Key:.Key,VersionId:.VersionId} | select(.VersionId != null)') -echo "##################################################" -echo "Deletion finished" + # If no more versions or markers, break + if [ -z "$objects" ] && [ -z "$markers" ]; then + break + fi -# Destroy Bucket and ECR -echo "deleting the Prerequisites stacks" -echo "##################################################" + # Prepare delete payload + delete_payload=$(jq -n '{Objects: [inputs]}' <<<"$objects"$'\n'"$markers") + # Delete batch of objects without pager + if [ -n "$delete_payload" ]; then + aws s3api delete-objects \ + --bucket "$bucket_name" \ + --delete "$delete_payload" \ + --profile "$BBBPROFILE" \ + --no-cli-pager \ + --output json || true + fi + done + + # Final cleanup of any remaining objects without pager + aws s3 rm "s3://${bucket_name}" --recursive --profile "$BBBPROFILE" --no-cli-pager + + log "INFO" "Successfully cleaned up bucket: $bucket_name" + return 0 +} + +# Get source bucket name and clean it up +log "INFO" "Getting source bucket name" BBBPREPSTACK="${BBBSTACK}-Sources" -SOURCE=$(aws cloudformation describe-stack-resources --profile $BBBPROFILE --stack-name $BBBPREPSTACK --query "StackResources[?ResourceType=='AWS::S3::Bucket'].PhysicalResourceId" --output text) - -echo "##################################################" -echo "Truncate the S3 Bucket" -echo "##################################################" - -aws s3api --profile=$BBBPROFILE list-object-versions \ - --bucket $SOURCE \ - --query "Versions[].Key" \ - --output json | jq 'unique' | jq -r '.[]' | while read key; do - echo "deleting versions of $key" - aws s3api --profile=$BBBPROFILE list-object-versions \ - --bucket $SOURCE \ - --prefix $key \ - --query "Versions[].VersionId" \ - --output json | jq 'unique' | jq -r '.[]' | while read version; do - echo "deleting $version" - aws s3api --profile=$BBBPROFILE delete-object \ - --bucket $SOURCE \ - --key $key \ - --version-id $version - done -done - -aws cloudformation delete-stack --stack-name $BBBECRSTACK --profile=$BBBPROFILE -aws cloudformation wait stack-delete-complete --profile=$BBBPROFILE --stack-name $BBBECRStack - -aws cloudformation delete-stack --stack-name $BBBPREPSTACK --profile=$BBBPROFILE -aws cloudformation wait stack-delete-complete --profile=$BBBPROFILE --stack-name $BBBPREPSTACK - -echo "##################################################" -echo "Deletion done" - - -exit 0 +SOURCE=$(aws cloudformation describe-stack-resources \ + --profile "$BBBPROFILE" \ + --stack-name "$BBBPREPSTACK" \ + --query "StackResources[?ResourceType=='AWS::S3::Bucket'].PhysicalResourceId" \ + --output text) + +if [ -n "$SOURCE" ]; then + log "INFO" "Found source bucket: $SOURCE" + # Clean up S3 bucket before deleting the stack + if ! cleanup_s3_bucket "$SOURCE"; then + log "WARN" "Failed to clean up S3 bucket, continuing with stack deletion" + fi +else + log "WARN" "Source bucket not found, continuing with deletion" +fi + +# Function to delete ECR repository and its images +cleanup_ecr_repository() { + local repository_name=$1 + log "INFO" "Attempting to delete ECR repository: $repository_name" + + # Force delete the repository and all images + aws ecr delete-repository \ + --repository-name "$repository_name" \ + --force \ + --profile "$BBBPROFILE" >/dev/null 2>&1 || { + log "DEBUG" "Repository $repository_name does not exist or already deleted" + } + + return 0 +} + +# Get ECR repository names and delete them +log "INFO" "Getting ECR repository names" +BBBECRSTACK="${BBBSTACK}-registry" +ECR_REPOS=$(aws cloudformation describe-stack-resources \ + --profile "$BBBPROFILE" \ + --stack-name "$BBBECRSTACK" \ + --query "StackResources[?ResourceType=='AWS::ECR::Repository'].PhysicalResourceId" \ + --output text) + +if [ -n "$ECR_REPOS" ]; then + log "INFO" "Found ECR repositories" + + # Process each repository name + for repo in bigbluebutton/greenlight blindsidenetwks/scalelite; do + if [ -n "$repo" ]; then + log "INFO" "Processing repository: '$repo'" + if ! cleanup_ecr_repository "$repo"; then + log "WARN" "Failed to delete ECR repository: '$repo'" + fi + fi + done +else + log "WARN" "No ECR repositories found, continuing with deletion" +fi + +# Delete main stack +log "INFO" "Deleting main BBB stack" +delete_stack "$BBBSTACK" + +# Delete ECR Repository stack +log "INFO" "Deleting ECR Repository stack" +delete_stack "$BBBECRSTACK" + +# Delete source bucket stack +log "INFO" "Deleting source bucket stack" +delete_stack "$BBBPREPSTACK" + +log "INFO" "Destruction completed successfully" +log "INFO" "Log file available at: $LOG_FILE" +exit 0 diff --git a/scalelite_mgmt.sh b/scalelite_mgmt.sh new file mode 100755 index 0000000..bb25d0c --- /dev/null +++ b/scalelite_mgmt.sh @@ -0,0 +1,153 @@ +#!/bin/bash +set -e + +# Basic logging +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" +} + +# Function to get Scalelite task +get_scalelite_task() { + local region=$1 + local cluster=$2 + local profile=$3 + # First, find the service containing 'BBBScaleliteService' in its name + local service_arn=$(aws ecs list-services \ + --profile "$PROFILE" \ + --cluster "$CLUSTER" \ + --output text \ + --query "serviceArns[?contains(@, 'BBBScaleliteService')]") + + if [ -z "$service_arn" ]; then + echo "ERROR: Could not find Scalelite service in cluster $CLUSTER" >&2 + return 1 + fi + + # Get the tasks for the found service + local task_arns=$(aws ecs list-tasks \ + --profile "$PROFILE" \ + --cluster "$CLUSTER" \ + --service-name "$(basename "$service_arn")" \ + --output text \ + --query 'taskArns[*]') + + if [ -z "$task_arns" ]; then + echo "ERROR: No tasks found for Scalelite service" >&2 + return 1 + fi + + # Return the first task ARN + echo "$(basename "$task_arns")" +} + +# Function to execute interactive shell +debug_shell() { + local region=$1 + local cluster=$2 + local task=$3 + local profile=$4 + + log "Opening interactive shell..." + aws ecs execute-command \ + --region "$region" \ + --cluster "$cluster" \ + --profile "$profile" \ + --task "$task" \ + --container scalelite-api \ + --interactive \ + --command "/bin/sh" +} + +# Function to delete a specific server +delete_server() { + local region=$1 + local cluster=$2 + local task=$3 + local server_id=$4 + local profile=$5 + + if [ -z "$server_id" ]; then + log "Listing available servers..." + aws ecs execute-command \ + --region "$region" \ + --cluster "$cluster" \ + --task "$task" \ + --profile "$profile" \ + --container scalelite-api \ + --interactive \ + --command "/bin/sh -c 'bin/rake servers'" + + read -p "Enter server ID to delete (or press enter to cancel): " server_id + + if [ -z "$server_id" ]; then + log "Operation cancelled" + exit 0 + fi + fi + + log "Deleting server ID: $server_id" + aws ecs execute-command \ + --region "$region" \ + --cluster "$cluster" \ + --task "$task" \ + --profile "$profile" \ + --container scalelite-api \ + --interactive \ + --command "/bin/sh -c 'bin/rake servers:panic[$server_id] && bin/rake servers:remove[$server_id]'" +} + +# Function to prune all servers +prune_all() { + local region=$1 + local cluster=$2 + local task=$3 + local profile=$4 + + log "Pruning all servers..." + aws ecs execute-command \ + --region "$region" \ + --cluster "$cluster" \ + --task "$task" \ + --profile "$profile" \ + --container scalelite-api \ + --interactive \ + --command "/bin/sh -c 'for id in \$(bin/rake servers | grep \"id: \" | sed \"s/id: //\"); do bin/rake servers:panic[\$id] && bin/rake servers:remove[\$id]; done'" +} + +# Parse command line arguments +while getopts ":r:c:m:i:p:" opt; do + case $opt in + r) REGION="${OPTARG}" ;; + c) CLUSTER="${OPTARG}" ;; + m) METHOD="${OPTARG}" ;; + i) SERVER_ID="${OPTARG}" ;; + p) PROFILE="${OPTARG}" ;; + \?) echo "Invalid option -$OPTARG" >&2; exit 1 ;; + esac +done + +# Validate required parameters +if [ -z "$REGION" ] || [ -z "$CLUSTER" ]; then + echo "Usage: $0 -r -c -m [-i ]" + exit 1 +fi + +# Get Scalelite task +TASK=$(get_scalelite_task "$REGION" "$CLUSTER" "$PROFILE") + +# Execute requested method +case $METHOD in + "debug") + debug_shell "$REGION" "$CLUSTER" "$TASK" "$PROFILE" + ;; + "delete") + delete_server "$REGION" "$CLUSTER" "$TASK" "$SERVER_ID" "$PROFILE" + ;; + "prune") + prune_all "$REGION" "$CLUSTER" "$TASK" "$PROFILE" + ;; + *) + echo "Invalid method. Use 'debug', 'delete', or 'prune'" + exit 1 + ;; +esac diff --git a/scripts/bbb-appscalable-bootstrap.sh b/scripts/bbb-appscalable-bootstrap.sh deleted file mode 100644 index b2cfacb..0000000 --- a/scripts/bbb-appscalable-bootstrap.sh +++ /dev/null @@ -1,152 +0,0 @@ -#!/bin/bash - -while getopts ":a:b:c:e:g:h:i:j:k:l:m:n:o:" opt; do - case $opt in - a) BBBStackBucketStack="$OPTARG" - ;; - b) BBBSystemLogsGroup="$OPTARG" - ;; - c) BBBDomainName="$OPTARG" - ;; - e) BBBHostedZone="$OPTARG" - ;; - g) BBBOperatorEMail="$OPTARG" - ;; - h) BBBApplicationVersion="$OPTARG" - ;; - i) AWSRegion="$OPTARG" - ;; - j) BBBSharedStorageFS="$OPTARG" - ;; - k) BBBSharedStorageAPspool="$OPTARG" - ;; - l) BBBECSCluster="$OPTARG" - ;; - m) BBBECSInstanceType="$OPTARG" - ;; - n) BBBPrivateApplicationSubnets="$OPTARG" - ;; - o) BBBECSTaskSecurityGroup="$OPTARG" - ;; - \?) echo "Invalid option -$OPTARG" >&2 - ;; - esac -done - -wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb -dpkg -i -E ./amazon-cloudwatch-agent.deb - -# Adding cwagent user to all required groups -useradd mongodb -usermod -a -G adm cwagent -usermod -a -G mongodb cwagent -usermod -a -G mongodb mongodb -mkdir /var/log/mongodb -touch /var/log/mongodb/mongod.log -chown -R mongodb:mongodb /var/log/mongodb -chmod g+r /var/log/mongodb/mongod.log - -aws s3 cp s3://$BBBStackBucketStack/bbb-cwagent-config.json /tmp/bbb-cwagent-config.json -sed -i "s|SYSTEMLOGS_PLACEHOLDER|$BBBSystemLogsGroup|g" /tmp/bbb-cwagent-config.json -sed -i "s|APPLICATIONLOGS_PLACEHOLDER|$BBBSystemLogsGroup|g" /tmp/bbb-cwagent-config.json -sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/tmp/bbb-cwagent-config.json - -pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz - -cd /tmp -git clone https://github.com/aws/efs-utils -cd efs-utils -./build-deb.sh -apt -y install ./build/amazon-efs-utils*deb - -# Set instance Hostname -instance_ipv4=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4) -instance_random=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 6 | head -n 1) -instance_publichostname=vc-$instance_random -instance_fqdn=$instance_publichostname.$BBBDomainName - -# register in route53 -wget --tries=10 https://github.com/barnybug/cli53/releases/download/0.8.22/cli53-linux-amd64 -O /usr/local/bin/cli53 -sudo chmod +x /usr/local/bin/cli53 - -# create script for route53-handler -aws s3 cp s3://$BBBStackBucketStack/route53-handler.service /etc/systemd/system/route53-handler.service -aws s3 cp s3://$BBBStackBucketStack/route53-handler.sh /usr/local/bin/route53-handler.sh -chmod +x /usr/local/bin/route53-handler.sh - -sed -i "s/INSTANCE_PLACEHOLDER/$instance_publichostname/g" /etc/systemd/system/route53-handler.service -sed -i "s/ZONE_PLACEHOLDER/$BBBHostedZone/g" /etc/systemd/system/route53-handler.service - -systemctl daemon-reload -systemctl enable route53-handler -systemctl start route53-handler - -mkdir -p /mnt/bbb-recordings -echo "$BBBSharedStorageFS: /mnt/bbb-recordings efs defaults,_netdev,tls,iam,accesspoint=$BBBSharedStorageAPspool,rw 0 0" >> /etc/fstab - -mkdir -p /var/bigbluebutton - -if test -e "/dev/nvme1n1"; then - DEVICE=/dev/nvme1n1 - parted -s -a optimal -- $DEVICE mklabel gpt mkpart primary 1MiB -2048s - sleep 20s - mkfs.ext4 -F $DEVICE\p1 - UUID=$(blkid |grep "$DEVICE\p1" | awk '{print $2}' |sed 's/"//g') -else - DEVICE=/dev/sdf - parted -s -a optimal -- $DEVICE mklabel gpt mkpart primary 1MiB -2048s - sleep 20s - mkfs.ext4 -F $DEVICE\1 - UUID=$(blkid |grep "$DEVICE\1" | awk '{print $2}' |sed 's/"//g') -fi - -echo "$UUID /var/bigbluebutton ext4 defaults,nofail 0 2" >> /etc/fstab -mount -a - -x=1 -while [ $x -le 5 ] -do - until host $instance_fqdn | grep -m 1 "has address $instance_ipv4"; do sleep 5 ; done - x=$(( $x + 1 )) -done - -if [[ $BBBApplicationVersion == *"25"* ]]; then - wget -qO- https://ubuntu.bigbluebutton.org/bbb-install-2.5.sh | bash -s -- -v $BBBApplicationVersion -s $instance_fqdn -e $BBBOperatorEMail -j -elif [[ $BBBApplicationVersion == *"26"* ]]; then - wget -qO- https://ubuntu.bigbluebutton.org/bbb-install-2.6.sh | bash -s -- -v $BBBApplicationVersion -s $instance_fqdn -e $BBBOperatorEMail -j -elif [[ $BBBApplicationVersion == *"27"* ]]; then - wget -qO- https://ubuntu.bigbluebutton.org/bbb-install-2.7.sh | bash -s -- -v $BBBApplicationVersion -s $instance_fqdn -e $BBBOperatorEMail -j -fi - -groupadd -g 2000 scalelite-spool -usermod -a -G scalelite-spool bigbluebutton - -wget --tries=10 https://raw.githubusercontent.com/blindsidenetworks/scalelite/master/bigbluebutton/scalelite_post_publish.rb -O /usr/local/bigbluebutton/core/scripts/post_publish/scalelite_post_publish.rb -wget --tries=10 https://raw.githubusercontent.com/blindsidenetworks/scalelite/master/bigbluebutton/scalelite_batch_import.sh -O /usr/local/bigbluebutton/core/scripts/post_publish/scalelite_batch_import.sh -wget --tries=10 https://raw.githubusercontent.com/blindsidenetworks/scalelite/master/bigbluebutton/scalelite_prune_recordings.sh -O /usr/local/bigbluebutton/core/scripts/post_publish/scalelite_prune_recordings.sh - -chmod +x /usr/local/bigbluebutton/core/scripts/post_publish/scalelite_post_publish.rb /usr/local/bigbluebutton/core/scripts/post_publish/scalelite_prune_recordings.sh /usr/local/bigbluebutton/core/scripts/post_publish/scalelite_batch_import.sh -aws s3 cp s3://$BBBStackBucketStack/scalelite-config.yml /usr/local/bigbluebutton/core/scripts/scalelite.yml - -apt-get -y install ruby2.7-dev libsystemd-dev -gem install redis builder nokogiri loofah open4 absolute_time journald-logger - -# create script for scalelite-handler -aws s3 cp s3://$BBBStackBucketStack/scalelite-handler.service /etc/systemd/system/scalelite-handler.service -aws s3 cp s3://$BBBStackBucketStack/scalelite-handler.sh /usr/local/bin/scalelite-handler.sh -chmod +x /usr/local/bin/scalelite-handler.sh - -SERVER="$(bbb-conf --secret | head -2 | tail -1 | sed -r 's/.*URL: //g')api" -SECRET=$(bbb-conf --secret | head -3 | tail -1 | sed -r 's/.*Secret: //g') - -sed -i "s/SECRET_PLACEHOLDER/$SECRET/g" /etc/systemd/system/scalelite-handler.service -sed -i "s|SERVER_PLACEHOLDER|$SERVER|g" /etc/systemd/system/scalelite-handler.service -sed -i "s/AWSREGION_PLACEHOLDER/$AWSRegion/g" /etc/systemd/system/scalelite-handler.service -sed -i "s/ECSCLUSTER_PLACEHOLDER/$BBBECSCluster/g" /etc/systemd/system/scalelite-handler.service -sed -i "s/ECSMODE_PLACEHOLDER/$BBBECSInstanceType/g" /etc/systemd/system/scalelite-handler.service -sed -i "s/TASKSUBNETS_PLACEHOLDER/$BBBPrivateApplicationSubnets/g" /etc/systemd/system/scalelite-handler.service -sed -i "s/TASKSGS_PLACEHOLDER/$BBBECSTaskSecurityGroup/g" /etc/systemd/system/scalelite-handler.service - -systemctl daemon-reload -systemctl enable scalelite-handler -systemctl start scalelite-handler \ No newline at end of file diff --git a/scripts/bbb-appsingle-bootstrap.sh b/scripts/bbb-appsingle-bootstrap.sh deleted file mode 100644 index fba86a8..0000000 --- a/scripts/bbb-appsingle-bootstrap.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/bash - -while getopts ":a:b:c:e:g:h:i:" opt; do - case $opt in - a) BBBStackBucketStack="$OPTARG" - ;; - b) BBBSystemLogsGroup="$OPTARG" - ;; - c) BBBDomainName="$OPTARG" - ;; - e) BBBHostedZone="$OPTARG" - ;; - g) BBBOperatorEMail="$OPTARG" - ;; - h) BBBApplicationVersion="$OPTARG" - ;; - i) AWSRegion="$OPTARG" - ;; - \?) echo "Invalid option -$OPTARG" >&2 - ;; - esac -done - -wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb -dpkg -i -E ./amazon-cloudwatch-agent.deb - -# Adding cwagent user to all required groups -useradd mongodb -usermod -a -G adm cwagent -usermod -a -G mongodb cwagent -usermod -a -G mongodb mongodb -mkdir /var/log/mongodb -touch /var/log/mongodb/mongod.log -chown -R mongodb:mongodb /var/log/mongodb -chmod g+r /var/log/mongodb/mongod.log - -aws s3 cp s3://$BBBStackBucketStack/bbb-cwagent-config.json /tmp/bbb-cwagent-config.json -sed -i "s|SYSTEMLOGS_PLACEHOLDER|$BBBSystemLogsGroup|g" /tmp/bbb-cwagent-config.json -sed -i "s|APPLICATIONLOGS_PLACEHOLDER|$BBBSystemLogsGroup|g" /tmp/bbb-cwagent-config.json -sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/tmp/bbb-cwagent-config.json - -pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz - -# Set instance Hostname -instance_ipv4=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4) -instance_random=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 6 | head -n 1) -instance_publichostname=vc-$instance_random -instance_fqdn=$instance_publichostname.$BBBDomainName - -# register in route53 -wget --tries=10 https://github.com/barnybug/cli53/releases/download/0.8.22/cli53-linux-amd64 -O /usr/local/bin/cli53 -sudo chmod +x /usr/local/bin/cli53 - -# create script for route53-handler -aws s3 cp s3://$BBBStackBucketStack/route53-handler.service /etc/systemd/system/route53-handler.service -aws s3 cp s3://$BBBStackBucketStack/route53-handler.sh /usr/local/bin/route53-handler.sh -chmod +x /usr/local/bin/route53-handler.sh - -sed -i "s/INSTANCE_PLACEHOLDER/$instance_publichostname/g" /etc/systemd/system/route53-handler.service -sed -i "s/ZONE_PLACEHOLDER/$BBBHostedZone/g" /etc/systemd/system/route53-handler.service - -systemctl daemon-reload -systemctl enable route53-handler -systemctl start route53-handler - -mkdir -p /var/bigbluebutton - -if test -e "/dev/nvme1n1"; then - DEVICE=/dev/nvme1n1 - parted -s -a optimal -- $DEVICE mklabel gpt mkpart primary 1MiB -2048s - sleep 20s - mkfs.ext4 -F $DEVICE\p1 - UUID=$(blkid |grep "$DEVICE\p1" | awk '{print $2}' |sed 's/"//g') -else - DEVICE=/dev/sdf - parted -s -a optimal -- $DEVICE mklabel gpt mkpart primary 1MiB -2048s - sleep 20s - mkfs.ext4 -F $DEVICE\1 - UUID=$(blkid |grep "$DEVICE\1" | awk '{print $2}' |sed 's/"//g') -fi - -echo "$UUID /var/bigbluebutton ext4 defaults,nofail 0 2" >> /etc/fstab -mount -a - -x=1 -while [ $x -le 5 ] -do - until host $instance_fqdn | grep -m 1 "has address $instance_ipv4"; do sleep 5 ; done - x=$(( $x + 1 )) -done - -if [[ $BBBApplicationVersion == *"25"* ]]; then - wget -qO- https://ubuntu.bigbluebutton.org/bbb-install-2.5.sh | bash -s -- -v $BBBApplicationVersion -s $instance_fqdn -e $BBBOperatorEMail -j -elif [[ $BBBApplicationVersion == *"26"* ]]; then - wget -qO- https://ubuntu.bigbluebutton.org/bbb-install-2.6.sh | bash -s -- -v $BBBApplicationVersion -s $instance_fqdn -e $BBBOperatorEMail -j -elif [[ $BBBApplicationVersion == *"27"* ]]; then - wget -qO- https://ubuntu.bigbluebutton.org/bbb-install-2.7.sh | bash -s -- -v $BBBApplicationVersion -s $instance_fqdn -e $BBBOperatorEMail -j -fi \ No newline at end of file diff --git a/scripts/bbb-bootstrap.sh b/scripts/bbb-bootstrap.sh new file mode 100644 index 0000000..7be760f --- /dev/null +++ b/scripts/bbb-bootstrap.sh @@ -0,0 +1,663 @@ +#!/bin/bash +# BigBlueButton Application Bootstrap Script +# This script initializes and configures a BigBlueButton instance with scalable components + +# Exit on error +set -e + +# Log file location +LOG_FILE="/var/log/bbb-bootstrap.log" + +# Create log directory if it doesn't exist +if [ ! -d "/var/log" ]; then + mkdir -p "/var/log" +fi + +# Color codes for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Default log level +LOG_LEVEL="DEBUG" + +# Function for logging +log() { + local level=$1 + shift + local message=$@ + local timestamp=$(date '+%Y-%m-%d %H:%M:%S') + local log_message="${timestamp} [${level}] - $message" + + # Log level hierarchy: ERROR=0, WARN=1, INFO=2, DEBUG=3 + case $level in + "ERROR") level_num=0 ;; + "WARN") level_num=1 ;; + "INFO") level_num=2 ;; + "DEBUG") level_num=3 ;; + *) level_num=2 ;; # Default to INFO + esac + + # Determine current log level number + case $LOG_LEVEL in + "ERROR") current_level=0 ;; + "WARN") current_level=1 ;; + "INFO") current_level=2 ;; + "DEBUG") current_level=3 ;; + *) current_level=2 ;; # Default to INFO + esac + + # Only log if the message level is less than or equal to current log level + if [ $level_num -le $current_level ]; then + # Console output with colors + case $level in + "ERROR") + echo -e "${RED}[ERROR]${NC} ${timestamp} - $message" >&2 + ;; + "WARN") + echo -e "${YELLOW}[WARN]${NC} ${timestamp} - $message" + ;; + "INFO") + echo -e "${GREEN}[INFO]${NC} ${timestamp} - $message" + ;; + "DEBUG") + echo -e "${BLUE}[DEBUG]${NC} ${timestamp} - $message" + ;; + esac + + # File logging (without colors) + if ! echo "${log_message}" >> "${LOG_FILE}" 2>/dev/null; then + echo -e "${RED}[ERROR]${NC} ${timestamp} - Failed to write to log file: ${LOG_FILE}" >&2 + exit 1 + fi + fi +} + +# Function to rotate log file if it gets too large (>100MB) +rotate_log() { + local max_size=$((100 * 1024 * 1024)) # 100MB in bytes + if [ -f "$LOG_FILE" ]; then + local file_size=$(stat -f%z "$LOG_FILE" 2>/dev/null || stat -c%s "$LOG_FILE" 2>/dev/null) + if [ "$file_size" -gt "$max_size" ]; then + log "INFO" "Rotating log file (size: $file_size bytes)" + mv "$LOG_FILE" "${LOG_FILE}.$(date +%Y%m%d-%H%M%S)" + touch "$LOG_FILE" + fi + fi +} + +# Function to get EC2 metadata with IMDSv2 token +get_metadata() { + local metadata_path=$1 + local token + local max_attempts=3 + local attempt=1 + + while [ $attempt -le $max_attempts ]; do + log "DEBUG" "Requesting IMDSv2 token (attempt $attempt of $max_attempts)..." >&2 + token=$(curl -X PUT "http://169.254.169.254/latest/api/token" \ + -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" \ + -s -f 2>/dev/null) + + if [ $? -eq 0 ] && [ -n "$token" ]; then + log "DEBUG" "Token received successfully" >&2 + log "DEBUG" "Requesting metadata: $metadata_path" >&2 + + local result=$(curl -H "X-aws-ec2-metadata-token: $token" \ + -s -f "http://169.254.169.254/latest/meta-data/${metadata_path}" 2>/dev/null) + + if [ $? -eq 0 ] && [ -n "$result" ]; then + log "DEBUG" "Successfully retrieved metadata" >&2 + echo "$result" + return 0 + else + log "WARN" "Failed to get metadata, attempt $attempt" >&2 + fi + else + log "WARN" "Failed to get token, attempt $attempt" >&2 + fi + + attempt=$((attempt + 1)) + [ $attempt -le $max_attempts ] && sleep 5 + done + + log "ERROR" "Failed to get metadata after $max_attempts attempts" >&2 + return 1 +} + + +# Function to install CloudWatch agent +install_cloudwatch_agent() { + log "INFO" "Installing CloudWatch agent" + + if ! wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb; then + log "ERROR" "Failed to download CloudWatch agent" + return 1 + fi + + if ! dpkg -i -E ./amazon-cloudwatch-agent.deb; then + log "ERROR" "Failed to install CloudWatch agent" + return 1 + fi + + rm -f ./amazon-cloudwatch-agent.deb + + # Verify version + local version=$(/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a status | jq -r '.version') + log "INFO" "CloudWatch agent version ${version} installed successfully" +} + +# Function to configure CloudWatch users and permissions +configure_service_users() { + log "INFO" "Configuring Service users and permissions..." + + # Add MongoDB user and configure groups + useradd mongodb || log "WARN" "MongoDB user already exists" + usermod -a -G adm cwagent || log "ERROR" "Failed to add cwagent to adm group" + usermod -a -G mongodb cwagent || log "ERROR" "Failed to add cwagent to mongodb group" + usermod -a -G mongodb mongodb || log "ERROR" "Failed to add mongodb to mongodb group" + + # Setup MongoDB logging + mkdir -p /var/log/mongodb + touch /var/log/mongodb/mongod.log + chown -R mongodb:mongodb /var/log/mongodb + chmod g+r /var/log/mongodb/mongod.log + + log "INFO" "Service users and permissions configured" +} + +# Function to configure CloudWatch agent +configure_cloudwatch_agent() { + local bucket=$1 + local logs_group=$2 + + log "INFO" "Configuring CloudWatch agent..." + + if aws s3 cp "s3://${bucket}/bbb-cwagent-config.json" /tmp/bbb-cwagent-config.json; then + log "DEBUG" "CloudWatch config downloaded successfully" + + sed -i "s|SYSTEMLOGS_PLACEHOLDER|${logs_group}|g" /tmp/bbb-cwagent-config.json + sed -i "s|APPLICATIONLOGS_PLACEHOLDER|${logs_group}|g" /tmp/bbb-cwagent-config.json + + if sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/tmp/bbb-cwagent-config.json; then + log "INFO" "CloudWatch agent configured successfully" + else + log "ERROR" "Failed to configure CloudWatch agent" + return 1 + fi + else + log "ERROR" "Failed to download CloudWatch config from S3" + return 1 + fi +} + +# Function to install required packages +install_required_packages() { + log "INFO" "Installing required packages..." + + if pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz; then + log "DEBUG" "AWS CFN bootstrap installed successfully" + else + log "ERROR" "Failed to install AWS CFN bootstrap" + return 1 + fi +} +# Function to install and configure EFS utils +install_efs_utils() { + log "INFO" "Installing EFS utilities..." + + cd /tmp + if git clone https://github.com/aws/efs-utils; then + cd efs-utils + if ./build-deb.sh; then + if apt -y install ./build/amazon-efs-utils*deb; then + log "INFO" "EFS utils installed successfully" + cd /tmp && rm -rf /tmp/efs-utils + else + log "ERROR" "Failed to install EFS utils package" + return 1 + fi + else + log "ERROR" "Failed to build EFS utils" + return 1 + fi + else + log "ERROR" "Failed to clone EFS utils repository" + return 1 + fi +} + +# Function to configure instance hostname +configure_hostname() { + log "INFO" "Configuring instance hostname..." + + # Get instance IP using IMDSv2 + local instance_ipv4 + instance_ipv4=$(get_metadata "public-ipv4") + if [ $? -ne 0 ] || [ -z "$instance_ipv4" ]; then + log "ERROR" "Failed to get instance IP address" + return 1 + fi + log "DEBUG" "Retrieved instance IP: ${instance_ipv4}" + + # Generate random hostname suffix + local instance_random + instance_random=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 6 | head -n 1) + log "DEBUG" "Generated random suffix: ${instance_random}" + + # Set hostname variables + local instance_publichostname="vc-${instance_random}" + local instance_fqdn="${instance_publichostname}.${BBBDomainName}" + log "INFO" "Generated FQDN: ${instance_fqdn}" + + # Store instance information + echo "${instance_ipv4}" > /tmp/instance_ipv4 + echo "${instance_fqdn}" > /tmp/instance_fqdn + echo "${instance_publichostname}" > /tmp/instance_publichostname + + # Set system hostname + log "DEBUG" "Setting system hostname to: ${instance_fqdn}" + hostnamectl set-hostname "${instance_fqdn}" + + # Update hosts file + log "DEBUG" "Updating /etc/hosts with new hostname" + echo "${instance_ipv4} ${instance_fqdn} ${instance_publichostname}" >> /etc/hosts +} + +# Function to setup Route53 handler +setup_route53_handler() { + log "INFO" "Setting up Route53 handler..." + + # Install cli53 + log "DEBUG" "Installing cli53..." + if ! wget --tries=10 --timeout=20 https://github.com/barnybug/cli53/releases/download/0.8.22/cli53-linux-amd64 -O /usr/local/bin/cli53; then + log "ERROR" "Failed to download cli53" + return 1 + fi + chmod +x /usr/local/bin/cli53 + + # Setup Route53 handler service + log "DEBUG" "Setting up Route53 handler service..." + if ! aws s3 cp "s3://${BBBStackBucketStack}/route53-handler.service" /etc/systemd/system/route53-handler.service; then + log "ERROR" "Failed to download Route53 handler service file" + return 1 + fi + + if ! aws s3 cp "s3://${BBBStackBucketStack}/route53-handler.sh" /usr/local/bin/route53-handler.sh; then + log "ERROR" "Failed to download Route53 handler script" + return 1 + fi + chmod +x /usr/local/bin/route53-handler.sh + + # Configure Route53 handler + local instance_publichostname=$(cat /tmp/instance_publichostname) + sed -i "s/INSTANCE_PLACEHOLDER/${instance_publichostname}/g" /etc/systemd/system/route53-handler.service + sed -i "s/ZONE_PLACEHOLDER/${BBBHostedZone}/g" /etc/systemd/system/route53-handler.service + + # Enable and start Route53 handler service + systemctl daemon-reload + if ! systemctl enable route53-handler; then + log "ERROR" "Failed to enable Route53 handler service" + return 1 + fi + if ! systemctl start route53-handler; then + log "ERROR" "Failed to start Route53 handler service" + return 1 + fi + + log "INFO" "Route53 handler setup completed" +} + +# Function to setup storage +setup_storage() { + log "INFO" "Setting up storage..." + + # Setup shared storage + mkdir -p /mnt/bbb-recordings + local efs_mount_entry="${BBBSharedStorageFS}: /mnt/bbb-recordings efs defaults,_netdev,tls,iam,accesspoint=${BBBSharedStorageAPspool},rw 0 0" + log "DEBUG" "Adding EFS mount entry: ${efs_mount_entry}" + echo "${efs_mount_entry}" >> /etc/fstab + + # Setup BigBlueButton directory + mkdir -p /var/bigbluebutton + + # Setup device storage + local DEVICE + if test -e "/dev/nvme1n1"; then + DEVICE="/dev/nvme1n1" + log "DEBUG" "Using NVMe device: ${DEVICE}" + setup_nvme_storage "$DEVICE" + else + DEVICE="/dev/sdf" + log "DEBUG" "Using standard device: ${DEVICE}" + setup_standard_storage "$DEVICE" + fi + + # Mount all filesystems + log "DEBUG" "Mounting all filesystems..." + if ! mount -a; then + log "ERROR" "Failed to mount all filesystems" + return 1 + fi + + log "INFO" "Storage setup completed successfully" +} + +# Function to setup NVMe storage +setup_nvme_storage() { + local DEVICE=$1 + log "DEBUG" "Setting up NVMe storage on ${DEVICE}" + + if ! parted -s -a optimal -- "$DEVICE" mklabel gpt mkpart primary 1MiB -2048s; then + log "ERROR" "Failed to partition NVMe device" + return 1 + fi + + sleep 20s + if ! mkfs.ext4 -F "${DEVICE}p1"; then + log "ERROR" "Failed to create filesystem on NVMe device" + return 1 + fi + + local UUID=$(blkid | grep "${DEVICE}p1" | awk '{print $2}' | sed 's/"//g') + echo "$UUID /var/bigbluebutton ext4 defaults,nofail 0 2" >> /etc/fstab +} + +# Function to setup standard storage +setup_standard_storage() { + local DEVICE=$1 + log "DEBUG" "Setting up standard storage on ${DEVICE}" + + if ! parted -s -a optimal -- "$DEVICE" mklabel gpt mkpart primary 1MiB -2048s; then + log "ERROR" "Failed to partition standard device" + return 1 + fi + + sleep 20s + if ! mkfs.ext4 -F "${DEVICE}1"; then + log "ERROR" "Failed to create filesystem on standard device" + return 1 + fi + + local UUID=$(blkid | grep "${DEVICE}1" | awk '{print $2}' | sed 's/"//g') + echo "$UUID /var/bigbluebutton ext4 defaults,nofail 0 2" >> /etc/fstab +} + +# Function to wait for DNS propagation +wait_for_dns() { + local instance_fqdn=$(cat /tmp/instance_fqdn) + local instance_ipv4=$(cat /tmp/instance_ipv4) + log "INFO" "Waiting for DNS propagation for ${instance_fqdn}..." + + local max_attempts=30 + local attempt=1 + local wait_time=20 + + while [ $attempt -le $max_attempts ]; do + log "DEBUG" "DNS check attempt $attempt of $max_attempts" + + if [ "$(dig +short "$instance_fqdn")" = "$instance_ipv4" ]; then + log "INFO" "DNS propagation completed successfully" + return 0 + fi + + log "DEBUG" "Waiting ${wait_time} seconds for DNS propagation..." + sleep $wait_time + attempt=$((attempt + 1)) + done + + log "ERROR" "DNS propagation timed out after $((max_attempts * wait_time)) seconds" + return 1 +} + +# Function to install BigBlueButton +install_bbb() { + local instance_fqdn=$(cat /tmp/instance_fqdn) + log "INFO" "Installing BigBlueButton version ${BBBApplicationVersion}..." + + local formatted_version=$(echo "$BBBApplicationVersion" | sed 's/focal-//' | sed 's/\([0-9]\)\([0-9]\)\([0-9]\)/\1.\2/') + # Convert version number (e.g., 2.7.0) to URI format (v2.7.x-release) + local bbb_uri_version="v${formatted_version}.x-release" + + log "DEBUG" "Using BBB install script from branch: ${bbb_uri_version}" + log "DEBUG" "Using ${BBBApplicationVersion},${instance_fqdn},${BBBOperatorEmail}" + + # Install BBB using the correct version format + if ! sudo wget -qO- "https://raw.githubusercontent.com/bigbluebutton/bbb-install/${bbb_uri_version}/bbb-install.sh" | sudo bash -s -- -v "${BBBApplicationVersion}" -s "${instance_fqdn}" -e "${BBBOperatorEmail}" -j; then + log "ERROR" "Failed to install BBB" + return 1 + fi + + log "INFO" "BBB installation completed successfully" + + # Verify installation + if ! bbb-conf --check | tee /var/log/bbb-install-check.log; then + log "WARN" "BBB installation verification showed warnings, check /var/log/bbb-install-check.log" + fi +} + +# Function to setup Scalelite +setup_scalelite() { + log "INFO" "Setting up Scalelite..." + + # Create scalelite group + groupadd -g 2000 scalelite-spool || log "WARN" "Group scalelite-spool already exists" + usermod -a -G scalelite-spool bigbluebutton + + # Download Scalelite scripts + local SCRIPTS=( + "scalelite_post_publish.rb" + "scalelite_batch_import.sh" + ) + + for script in "${SCRIPTS[@]}"; do + log "DEBUG" "Downloading ${script}..." + if ! wget --tries=10 --timeout=20 "https://raw.githubusercontent.com/blindsidenetworks/scalelite/master/bigbluebutton/${script}" \ + -O "/usr/local/bigbluebutton/core/scripts/post_publish/${script}"; then + log "ERROR" "Failed to download ${script}" + return 1 + fi + done + + # Set permissions + chmod +x /usr/local/bigbluebutton/core/scripts/post_publish/*.{rb,sh} + + # Setup daily cron + if ! wget --tries=10 --timeout=20 "https://raw.githubusercontent.com/blindsidenetworks/scalelite/master/bigbluebutton/scalelite_prune_recordings" \ + -O "/etc/cron.daily/scalelite_prune_recordings"; then + log "ERROR" "Failed to download scalelite_prune_recordings" + return 1 + fi + chmod +x /etc/cron.daily/scalelite_prune_recordings + + # Download and configure Scalelite config + if ! aws s3 cp "s3://${BBBStackBucketStack}/scalelite-config.yml" /usr/local/bigbluebutton/core/scripts/scalelite.yml; then + log "ERROR" "Failed to download Scalelite config" + return 1 + fi + + # Install required packages + log "DEBUG" "Installing required packages..." + if ! apt-get -y install ruby-dev libsystemd-dev; then + log "ERROR" "Failed to install required system packages" + return 1 + fi + + if ! gem install redis builder nokogiri:1.15.7 loofah open4 absolute_time journald-logger; then + log "ERROR" "Failed to install required gems" + return 1 + fi + + setup_scalelite_handler +} + +# Function to setup Scalelite handler +setup_scalelite_handler() { + log "INFO" "Setting up Scalelite handler..." + + # Download handler files + if ! aws s3 cp "s3://${BBBStackBucketStack}/scalelite-handler.service" /etc/systemd/system/scalelite-handler.service; then + log "ERROR" "Failed to download Scalelite handler service file" + return 1 + fi + + if ! aws s3 cp "s3://${BBBStackBucketStack}/scalelite-handler.sh" /usr/local/bin/scalelite-handler.sh; then + log "ERROR" "Failed to download Scalelite handler script" + return 1 + fi + chmod +x /usr/local/bin/scalelite-handler.sh + + # Get BBB credentials + local SERVER + local SECRET + SERVER="$(bbb-conf --secret | head -2 | tail -1 | sed -r 's/.*URL: //g')api" + SECRET=$(bbb-conf --secret | head -3 | tail -1 | sed -r 's/.*Secret: //g') + + if [ -z "$SERVER" ] || [ -z "$SECRET" ]; then + log "ERROR" "Failed to get BBB credentials" + return 1 + fi + + # Configure handler service + log "DEBUG" "Configuring Scalelite handler service..." + sed -i "s/SECRET_PLACEHOLDER/${SECRET}/g" /etc/systemd/system/scalelite-handler.service + sed -i "s|SERVER_PLACEHOLDER|${SERVER}|g" /etc/systemd/system/scalelite-handler.service + sed -i "s/AWSREGION_PLACEHOLDER/${AWSRegion}/g" /etc/systemd/system/scalelite-handler.service + sed -i "s/ECSCLUSTER_PLACEHOLDER/${BBBECSCluster}/g" /etc/systemd/system/scalelite-handler.service + sed -i "s/ECSMODE_PLACEHOLDER/${BBBECSInstanceType}/g" /etc/systemd/system/scalelite-handler.service + sed -i "s/TASKSUBNETS_PLACEHOLDER/${BBBApplicationSubnet}/g" /etc/systemd/system/scalelite-handler.service + sed -i "s/TASKSGS_PLACEHOLDER/${BBBECSTaskSecurityGroup}/g" /etc/systemd/system/scalelite-handler.service + + # Enable and start handler service + systemctl daemon-reload + if ! systemctl enable scalelite-handler; then + log "ERROR" "Failed to enable Scalelite handler service" + return 1 + fi + if ! systemctl start scalelite-handler; then + log "ERROR" "Failed to start Scalelite handler service" + return 1 + fi + + log "INFO" "Scalelite handler setup completed successfully" +} + +# Parse command line arguments +while getopts ":a:b:c:e:g:h:i:j:k:l:m:n:o:d" opt; do + case $opt in + a) BBBStackBucketStack="${OPTARG}" + log "DEBUG" "Stack bucket parameter received: ${OPTARG}" + ;; + b) BBBSystemLogsGroup="${OPTARG}" + log "DEBUG" "System logs group parameter received: ${OPTARG}" + ;; + c) BBBDomainName="${OPTARG}" + log "DEBUG" "Domain name parameter received: ${OPTARG}" + ;; + e) BBBHostedZone="${OPTARG}" + log "DEBUG" "Hosted zone parameter received: ${OPTARG}" + ;; + g) BBBOperatorEmail="${OPTARG}" + log "DEBUG" "Operator email parameter received: ${OPTARG}" + ;; + h) BBBApplicationVersion="${OPTARG}" + log "DEBUG" "Application version parameter received: ${OPTARG}" + ;; + i) AWSRegion="${OPTARG}" + log "DEBUG" "AWS region parameter received: ${OPTARG}" + ;; + j) BBBSharedStorageFS="${OPTARG}" + log "DEBUG" "Shared storage FS parameter received: ${OPTARG}" + ;; + k) BBBSharedStorageAPspool="${OPTARG}" + log "DEBUG" "Shared storage AP spool parameter received: ${OPTARG}" + ;; + l) BBBECSCluster="${OPTARG}" + log "DEBUG" "ECS cluster parameter received: ${OPTARG}" + ;; + m) BBBECSInstanceType="${OPTARG}" + log "DEBUG" "ECS instance type parameter received: ${OPTARG}" + ;; + n) BBBApplicationSubnet="${OPTARG}" + log "DEBUG" "Application subnet parameter received: ${OPTARG}" + ;; + o) BBBECSTaskSecurityGroup="${OPTARG}" + log "DEBUG" "ECS task security group parameter received: ${OPTARG}" + ;; + d) LOG_LEVEL="DEBUG" + log "DEBUG" "Debug mode enabled" + ;; + \?) log "ERROR" "Invalid option -$OPTARG" + exit 1 + ;; + esac +done + +# Main execution flow +main() { + log "INFO" "Starting BBB bootstrap process..." + + log "DEBUG" "BBBOperatorEmail = ${BBBOperatorEmail}" + # Check required parameters + local required_params=( + "BBBStackBucketStack" + "BBBSystemLogsGroup" + "BBBDomainName" + "BBBHostedZone" + "BBBOperatorEmail" + "BBBApplicationVersion" + "AWSRegion" + "BBBSharedStorageFS" + "BBBSharedStorageAPspool" + "BBBECSCluster" + "BBBECSInstanceType" + "BBBApplicationSubnet" + "BBBECSTaskSecurityGroup" + ) + + for param in "${required_params[@]}"; do + if [ -z "${!param}" ]; then + log "ERROR" "Missing required parameter: $param" + return 1 + fi + done + + # Execute installation steps + local steps=( + "install_cloudwatch_agent" + "configure_service_users" + "configure_cloudwatch_agent" + "install_required_packages" + "install_efs_utils" + "configure_hostname" + "setup_route53_handler" + "setup_storage" + "wait_for_dns" + "install_bbb" + "setup_scalelite" + ) + + for step in "${steps[@]}"; do + log "INFO" "Executing step: $step" + if ! $step "$BBBStackBucketStack" "$BBBSystemLogsGroup"; then + log "ERROR" "Step failed: $step" + return 1 + fi + done + + log "INFO" "BBB bootstrap process completed successfully" + return 0 +} + +# Check log file permissions and rotate if needed +rotate_log + +# Execute main function +if ! main; then + log "ERROR" "Bootstrap process failed" + exit 1 +fi + +exit 0 \ No newline at end of file diff --git a/scripts/bbb-cwagent-config.json b/scripts/bbb-cwagent-config.json index fb136ca..9a9d5e6 100644 --- a/scripts/bbb-cwagent-config.json +++ b/scripts/bbb-cwagent-config.json @@ -1,121 +1,88 @@ { - "agent": { - "metrics_collection_interval": 60, - "run_as_user": "cwagent" + "agent": { + "metrics_collection_interval": 300, + "run_as_user": "cwagent" + }, + "logs": { + "logs_collected": { + "files": { + "collect_list": [ + { + "file_path": "/var/log/bbb-bootstrap.log", + "log_group_name": "SYSTEMLOGS_PLACEHOLDER", + "log_stream_name": "{instance_id}-bbb-bootstrap", + "timestamp_format": "%Y-%m-%d %H:%M:%S", + "retention_in_days": 7, + "multi_line_start_pattern": "^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}" + }, + { + "file_path": "/var/log/route53-handler.log", + "log_group_name": "SYSTEMLOGS_PLACEHOLDER", + "log_stream_name": "{instance_id}-route53-handler", + "timestamp_format": "%Y-%m-%d %H:%M:%S", + "retention_in_days": 7, + "multi_line_start_pattern": "^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}" + }, + { + "file_path": "/var/log/scalelite-handler.log", + "log_group_name": "SYSTEMLOGS_PLACEHOLDER", + "log_stream_name": "{instance_id}-scalelite-handler", + "timestamp_format": "%Y-%m-%d %H:%M:%S", + "retention_in_days": 7, + "multi_line_start_pattern": "^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}" + }, + { + "file_path": "/var/log/bbb-web.log", + "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", + "log_stream_name": "{instance_id}-bbb-web", + "timestamp_format": "%b %d %H:%M:%S", + "retention_in_days": 7 + }, + { + "file_path": "/var/log/nginx/error.log", + "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", + "log_stream_name": "{instance_id}-nginx-error", + "timestamp_format": "%Y/%m/%d %H:%M:%S", + "retention_in_days": 7 + }, + { + "file_path": "/var/log/syslog", + "log_group_name": "SYSTEMLOGS_PLACEHOLDER", + "log_stream_name": "{instance_id}-syslog", + "timestamp_format": "%b %d %H:%M:%S", + "retention_in_days": 7 + }, + { + "file_path": "/var/log/bbb-apps-akka/bbb-apps-akka.log", + "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", + "log_stream_name": "{instance_id}-bbb-apps-akka", + "timestamp_format": "%Y-%m-%d %H:%M:%S", + "retention_in_days": 7 + }, + { + "file_path": "/var/log/bbb-fsesl-akka/bbb-fsesl-akka.log", + "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", + "log_stream_name": "{instance_id}-bbb-fsesl-akka", + "timestamp_format": "%Y-%m-%d %H:%M:%S", + "retention_in_days": 7 + }, + { + "file_path": "/var/log/bbb-transcode/bbb-transcode.log", + "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", + "log_stream_name": "{instance_id}-bbb-transcode", + "timestamp_format": "%Y-%m-%d %H:%M:%S", + "retention_in_days": 7 + }, + { + "file_path": "/var/log/bbb-webrtc-sfu/bbb-webrtc-sfu.log", + "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", + "log_stream_name": "{instance_id}-bbb-webrtc-sfu", + "timestamp_format": "%Y-%m-%d %H:%M:%S", + "retention_in_days": 7 + } + ] + } }, - "logs": { - "logs_collected": { - "files": { - "collect_list": [ - { - "file_path": "/var/log/auth.log", - "log_group_name": "SYSTEMLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-auth.log", - "timestamp_format": "%b %d %H:%M:%S" - }, - { - "file_path": "/var/log/bbb-apps-akka/bbb-apps-akka.log", - "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-bbb-apps-akka.log", - "timestamp_format": "%b %d %H:%M:%S" - }, - { - "file_path": "/var/log/bbb-fsesl-akka/bbb-fsesl-akka.log", - "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-bbb-fsesl-akka.log", - "timestamp_format": "%b %d %H:%M:%S" - }, - { - "file_path": "/var/log/bbb-transcode-akka/bbb-transcode.log", - "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-bbb-transcode.log", - "timestamp_format": "%b %d %H:%M:%S" - }, - { - "file_path": "/var/log/bbb-webrtc-sfu/bbb-webrtc-sfu.log", - "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-bbb-webrtc-sfu.log", - "timestamp_format": "%b %d %H:%M:%S" - }, - { - "file_path": "/var/log/bigbluebutton/bbb-rap-worker.log", - "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-bbb-rap-worker.log", - "timestamp_format": "%b %d %H:%M:%S" - }, - { - "file_path": "/var/log/bigbluebutton/bbb-web.log", - "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-bbb-web.log", - "timestamp_format": "%b %d %H:%M:%S" - }, - { - "file_path": "/var/log/bigbluebutton/post_publish.log", - "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-post_publish.log", - "timestamp_format": "%b %d %H:%M:%S" - }, - { - "file_path": "/var/log/bigbluebutton/sanity.log", - "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-sanity.log", - "timestamp_format": "%b %d %H:%M:%S" - }, - { - "file_path": "/var/log/cloud-init.log", - "log_group_name": "SYSTEMLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-cloud-init", - "timestamp_format": "%Y-%m-%d %H:%M:%S,%f" - }, - { - "file_path": "/var/log/cloud-init-output.log", - "log_group_name": "SYSTEMLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-cloud-init-output" - }, - { - "file_path": "/var/log/kurento-media-server/*.log", - "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-kurento-media-server", - "timestamp_format": "%b %d %H:%M:%S" - }, - { - "file_path": "/var/log/letsencrypt/letsencrypt.log", - "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-letsencrypt.log", - "timestamp_format": "%b %d %H:%M:%S" - }, - { - "file_path": "/var/log/mongodb/mongod.log", - "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-mongod.log", - "timestamp_format": "%b %d %H:%M:%S" - }, - { - "file_path": "/var/log/nginx/*.log", - "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-nginx", - "timestamp_format": "%b %d %H:%M:%S" - }, - { - "file_path": "/var/log/red5/*.log", - "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-red5", - "timestamp_format": "%b %d %H:%M:%S" - }, - { - "file_path": "/var/log/redis/redis-server.log", - "log_group_name": "APPLICATIONLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-redis-server.log", - "timestamp_format": "%b %d %H:%M:%S" - }, - { - "file_path": "/var/log/syslog", - "log_group_name": "SYSTEMLOGS_PLACEHOLDER", - "log_stream_name": "{instance_id}-syslog", - "timestamp_format": "%b %d %H:%M:%S" - } - ] - } - } - } + "force_flush_interval": 30 + } } diff --git a/scripts/route53-handler.sh b/scripts/route53-handler.sh index e617e80..2d6280f 100644 --- a/scripts/route53-handler.sh +++ b/scripts/route53-handler.sh @@ -1,28 +1,205 @@ #!/bin/bash -# script handler route53 entries on startup / shutdown -# created by suredavi@amazon.de -# only for testing - -while getopts ":h:m:z:" opt; do - case $opt in - h) HOSTNAME="$OPTARG" - ;; - m) METHOD="$OPTARG" - ;; - z) ZONE="$OPTARG" - ;; - \?) echo "Invalid option -$OPTARG" >&2 - ;; - esac -done +# Script handler for route53 entries on startup / shutdown + +# Exit on error +set -e -if [[ $METHOD == "create" ]]; then - instance_ipv4=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4) - /usr/local/bin/cli53 rrcreate --replace $ZONE "$HOSTNAME 60 A $instance_ipv4" +# Log file location +LOG_FILE="/var/log/route53-handler.log" +# Create log directory if it doesn't exist +if [ ! -d "/var/log" ]; then + mkdir -p "/var/log" fi -if [[ $METHOD == "delete" ]]; then - /usr/local/bin/cli53 rrdelete $ZONE $HOSTNAME A +# Color codes for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Default log level +LOG_LEVEL="INFO" + +# Function for logging +log() { + local level=$1 + shift + local message=$@ + local timestamp=$(date '+%Y-%m-%d %H:%M:%S') + local log_message="${timestamp} [${level}] - $message" + + # Log level hierarchy: ERROR=0, WARN=1, INFO=2, DEBUG=3 + case $level in + "ERROR") level_num=0 ;; + "WARN") level_num=1 ;; + "INFO") level_num=2 ;; + "DEBUG") level_num=3 ;; + *) level_num=2 ;; # Default to INFO + esac + + # Determine current log level number + case $LOG_LEVEL in + "ERROR") current_level=0 ;; + "WARN") current_level=1 ;; + "INFO") current_level=2 ;; + "DEBUG") current_level=3 ;; + *) current_level=2 ;; # Default to INFO + esac + + # Only log if the message level is less than or equal to current log level + if [ $level_num -le $current_level ]; then + # Console output with colors + case $level in + "ERROR") + echo -e "${RED}[ERROR]${NC} ${timestamp} - $message" >&2 + ;; + "WARN") + echo -e "${YELLOW}[WARN]${NC} ${timestamp} - $message" + ;; + "INFO") + echo -e "${GREEN}[INFO]${NC} ${timestamp} - $message" + ;; + "DEBUG") + echo -e "${BLUE}[DEBUG]${NC} ${timestamp} - $message" + ;; + esac + + # File logging (without colors) + if ! echo "${log_message}" >> "${LOG_FILE}" 2>/dev/null; then + echo -e "${RED}[ERROR]${NC} ${timestamp} - Failed to write to log file: ${LOG_FILE}" >&2 + echo -e "${RED}[ERROR]${NC} ${timestamp} - Please check permissions or run with sudo" >&2 + exit 1 + fi + fi +} + +# Function to rotate log file if it gets too large (>100MB) +rotate_log() { + local max_size=$((100 * 1024 * 1024)) # 100MB in bytes + if [ -f "$LOG_FILE" ]; then + local file_size=$(stat -f%z "$LOG_FILE" 2>/dev/null || stat -c%s "$LOG_FILE" 2>/dev/null) + if [ "$file_size" -gt "$max_size" ]; then + log "INFO" "Rotating log file (size: $file_size bytes)" + mv "$LOG_FILE" "${LOG_FILE}.$(date +%Y%m%d-%H%M%S)" + touch "$LOG_FILE" + fi + fi +} + +# Function to get metadata +get_metadata() { + local metadata_path=$1 + local token + + log "DEBUG" "Requesting IMDSv2 token..." + token=$(curl -X PUT "http://169.254.169.254/latest/api/token" \ + -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" \ + -s -f 2>/dev/null) + + if [ $? -ne 0 ]; then + log "ERROR" "Failed to get IMDSv2 token" + return 1 + fi + + log "DEBUG" "Token received successfully" + log "DEBUG" "Requesting metadata: $metadata_path" + + local result=$(curl -H "X-aws-ec2-metadata-token: $token" \ + -s -f "http://169.254.169.254/latest/meta-data/${metadata_path}" 2>/dev/null) + + if [ $? -ne 0 ]; then + log "ERROR" "Failed to get metadata from path: $metadata_path" + return 1 + fi + + log "DEBUG" "Successfully retrieved metadata" + echo "$result" +} + +# Check log file permissions and rotate if needed +rotate_log + +# Log script start +log "INFO" "Starting Route53 handler script" +log "DEBUG" "Script started with parameters: $@" + +# Parse command line arguments +while getopts ":h:m:z:d" opt; do + case $opt in + h) HOSTNAME="${OPTARG}" + log "DEBUG" "Hostname parameter received: ${OPTARG}" + ;; + m) METHOD="${OPTARG}" + log "DEBUG" "Method parameter received: ${OPTARG}" + ;; + z) ZONE="${OPTARG}" + log "DEBUG" "Zone parameter received: ${OPTARG}" + ;; + d) LOG_LEVEL="DEBUG" + log "DEBUG" "Debug mode enabled" + ;; + \?) log "ERROR" "Invalid option -$OPTARG" + exit 1 + ;; + esac +done + +# Validate required parameters +if [ -z "$HOSTNAME" ] || [ -z "$METHOD" ] || [ -z "$ZONE" ]; then + log "ERROR" "Missing required parameters" + log "ERROR" "Usage: $0 -h -m -z [-d for debug]" + exit 1 fi +# Main logic +case $METHOD in + "create") + log "INFO" "Starting DNS record creation process" + log "DEBUG" "Parameters received:" + log "DEBUG" " Hostname: ${HOSTNAME}" + log "DEBUG" " Zone: ${ZONE}" + log "DEBUG" " Log Level: ${LOG_LEVEL}" + + # Get instance IP + log "DEBUG" "Attempting to get instance IP..." + instance_ipv4=$(get_metadata "public-ipv4") + if [ $? -ne 0 ] || [ -z "$instance_ipv4" ]; then + log "ERROR" "Failed to get instance IP address" + exit 1 + fi + + instance_ipv4=$(echo "$instance_ipv4" | tr -d '\n\r\t ') + log "INFO" "Retrieved IP Address: ${instance_ipv4}" + + # Create DNS record + log "INFO" "Creating Route53 record..." + log "DEBUG" "Executing cli53 command..." + if /usr/local/bin/cli53 rrcreate --replace "${ZONE}" "${HOSTNAME} 60 A ${instance_ipv4}"; then + log "INFO" "Successfully created DNS record" + else + log "ERROR" "Failed to create DNS record" + exit 1 + fi + ;; + + "delete") + log "INFO" "Starting DNS record deletion process" + log "DEBUG" "Attempting to delete record for ${HOSTNAME} in zone ${ZONE}" + + if /usr/local/bin/cli53 rrdelete "${ZONE}" "${HOSTNAME}" A; then + log "INFO" "Successfully deleted DNS record" + else + log "ERROR" "Failed to delete DNS record" + exit 1 + fi + ;; + + *) + log "ERROR" "Invalid method: ${METHOD}. Must be 'create' or 'delete'" + exit 1 + ;; +esac + +log "INFO" "Script completed successfully" exit 0 diff --git a/scripts/scalelite-handler.sh b/scripts/scalelite-handler.sh index ed9821c..4976721 100644 --- a/scripts/scalelite-handler.sh +++ b/scripts/scalelite-handler.sh @@ -1,41 +1,231 @@ #!/bin/bash # Script handles the add and remove for Application Server instances into scalelite -# only for testing purposes - -while getopts ":s:p:m:r:c:" opt; do - case $opt in - p) SECRET="$OPTARG" - ;; - s) SERVER="$OPTARG" - ;; - m) METHOD="$OPTARG" - ;; - r) REGION="$OPTARG" - ;; - c) ECS_CLUSTER="$OPTARG" - ;; - \?) echo "Invalid option -$OPTARG" >&2 - ;; - esac -done -OIFS=$IFS; -IFS=","; +# Exit on error +set -e + +# Log file location +LOG_FILE="/var/log/scalelite-handler.log" +# Create log directory if it doesn't exist +if [ ! -d "/var/log" ]; then + mkdir -p "/var/log" +fi + +# Color codes for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Default log level +LOG_LEVEL="INFO" + +# Function for logging +log() { + local level=$1 + shift + local message=$@ + local timestamp=$(date '+%Y-%m-%d %H:%M:%S') + local log_message="${timestamp} [${level}] - $message" + + # Log level hierarchy: ERROR=0, WARN=1, INFO=2, DEBUG=3 + case $level in + "ERROR") level_num=0 ;; + "WARN") level_num=1 ;; + "INFO") level_num=2 ;; + "DEBUG") level_num=3 ;; + *) level_num=2 ;; # Default to INFO + esac + + # Determine current log level number + case $LOG_LEVEL in + "ERROR") current_level=0 ;; + "WARN") current_level=1 ;; + "INFO") current_level=2 ;; + "DEBUG") current_level=3 ;; + *) current_level=2 ;; # Default to INFO + esac + + # Only log if the message level is less than or equal to current log level + if [ $level_num -le $current_level ]; then + # Console output with colors + case $level in + "ERROR") + echo -e "${RED}[ERROR]${NC} ${timestamp} - $message" >&2 + ;; + "WARN") + echo -e "${YELLOW}[WARN]${NC} ${timestamp} - $message" + ;; + "INFO") + echo -e "${GREEN}[INFO]${NC} ${timestamp} - $message" + ;; + "DEBUG") + echo -e "${BLUE}[DEBUG]${NC} ${timestamp} - $message" + ;; + esac + + # File logging (without colors) + if ! echo "${log_message}" >> "${LOG_FILE}" 2>/dev/null; then + echo -e "${RED}[ERROR]${NC} ${timestamp} - Failed to write to log file: ${LOG_FILE}" >&2 + echo -e "${RED}[ERROR]${NC} ${timestamp} - Please check permissions or run with sudo" >&2 + exit 1 + fi + fi +} + +# Function to rotate log file if it gets too large (>100MB) +rotate_log() { + local max_size=$((100 * 1024 * 1024)) # 100MB in bytes + if [ -f "$LOG_FILE" ]; then + local file_size=$(stat -f%z "$LOG_FILE" 2>/dev/null || stat -c%s "$LOG_FILE" 2>/dev/null) + if [ "$file_size" -gt "$max_size" ]; then + log "INFO" "Rotating log file (size: $file_size bytes)" + mv "$LOG_FILE" "${LOG_FILE}.$(date +%Y%m%d-%H%M%S)" + touch "$LOG_FILE" + fi + fi +} -# get the Scalelite Information -SCALELITE_SERVICE=$(aws ecs list-services --region $REGION --cluster $ECS_CLUSTER --query "serviceArns[?contains(@, 'BBBScaleliteService')]" --output text | xargs -n 1 basename) -SCALELITE_TASK=$(aws ecs list-tasks --region $REGION --cluster $ECS_CLUSTER --service $SCALELITE_SERVICE --output text | awk -F"/" '{print $NF}' | rev | awk -F"/" '{print $1}' | rev) +# Function to get Scalelite service information +get_scalelite_service() { + local region=$1 + local cluster=$2 + + log "DEBUG" "Getting Scalelite service from cluster: $cluster in region: $region" + local service=$(aws ecs list-services \ + --region "$region" \ + --cluster "$cluster" \ + --query "serviceArns[?contains(@, 'BBBScaleliteService')]" \ + --output text | xargs -n 1 basename) + + if [ -z "$service" ]; then + log "ERROR" "Failed to get Scalelite service" + return 1 + fi + + log "DEBUG" "Found Scalelite service: $service" + echo "$service" +} -# Decide if to add or to remove -if [[ $METHOD == "create" ]]; then - COMMAND_STRING="id=\$(bin/rake servers:add[$SERVER,$SECRET] | tail -n 1 | sed 's/id: //g'); bin/rake servers:enable[\$id];" +# Function to get Scalelite task +get_scalelite_task() { + local region=$1 + local cluster=$2 + local service=$3 + + log "DEBUG" "Getting Scalelite task for service: $service" + local task=$(aws ecs list-tasks \ + --region "$region" \ + --cluster "$cluster" \ + --service "$service" \ + --output text | awk -F"/" '{print $NF}' | rev | awk -F"/" '{print $1}' | rev) + + if [ -z "$task" ]; then + log "ERROR" "Failed to get Scalelite task" + return 1 + fi + + log "DEBUG" "Found Scalelite task: $task" + echo "$task" +} + +# Function to execute Scalelite command +execute_scalelite_command() { + local region=$1 + local cluster=$2 + local task=$3 + local command=$4 + + log "DEBUG" "Executing command in Scalelite container: $command" + aws ecs execute-command \ + --region "$region" \ + --cluster "$cluster" \ + --task "$task" \ + --container scalelite-api \ + --interactive \ + --command "/bin/sh -c \"$command\"" +} + +# Check log file permissions and rotate if needed +rotate_log + +# Log script start +log "INFO" "Starting Scalelite handler script" +log "DEBUG" "Script started with parameters: $@" + +# Parse command line arguments +while getopts ":s:p:m:r:c:d" opt; do + case $opt in + p) SECRET="${OPTARG}" + log "DEBUG" "Secret parameter received" + ;; + s) SERVER="${OPTARG}" + log "DEBUG" "Server parameter received" + ;; + m) METHOD="${OPTARG}" + log "DEBUG" "Method parameter received: ${OPTARG}" + ;; + r) REGION="${OPTARG}" + log "DEBUG" "Region parameter received: ${OPTARG}" + ;; + c) ECS_CLUSTER="${OPTARG}" + log "DEBUG" "ECS Cluster parameter received" + ;; + d) LOG_LEVEL="DEBUG" + log "DEBUG" "Debug mode enabled" + ;; + \?) log "ERROR" "Invalid option -$OPTARG" + exit 1 + ;; + esac +done + +# Validate required parameters +if [ -z "$SECRET" ] || [ -z "$SERVER" ] || [ -z "$METHOD" ] || [ -z "$REGION" ] || [ -z "$ECS_CLUSTER" ]; then + log "ERROR" "Missing required parameters" + log "ERROR" "Usage: $0 -s -p -m -r -c [-d for debug]" + exit 1 +fi + +# Get Scalelite service and task information +log "INFO" "Getting Scalelite service information" +SCALELITE_SERVICE=$(get_scalelite_service "$REGION" "$ECS_CLUSTER") +if [ $? -ne 0 ]; then + log "ERROR" "Failed to get Scalelite service information" + exit 1 fi -if [[ $METHOD == "delete" ]]; then - COMMAND_STRING="id=\$(bin/rake servers | grep -B 1 "$SERVER" | head -n 1 | sed 's/id: //g'); bin/rake servers:panic[\$id] && bin/rake servers:remove[\$id]" +SCALELITE_TASK=$(get_scalelite_task "$REGION" "$ECS_CLUSTER" "$SCALELITE_SERVICE") +if [ $? -ne 0 ]; then + log "ERROR" "Failed to get Scalelite task information" + exit 1 fi -# execute the needed command at the api container -aws ecs execute-command --region $REGION --cluster "$ECS_CLUSTER" --task $SCALELITE_TASK --container scalelite-api --interactive --command "/bin/sh -c \"$COMMAND_STRING\"" +# Prepare and execute command based on method +case $METHOD in + "create") + log "INFO" "Adding server to Scalelite: $SERVER" + COMMAND_STRING="id=\$(bin/rake servers:add[$SERVER,$SECRET] | tail -n 1 | sed 's/id: //g'); bin/rake servers:enable[\$id];" + ;; + "delete") + log "INFO" "Removing server from Scalelite: $SERVER" + COMMAND_STRING="id=\$(bin/rake servers | grep -B 1 \"$SERVER\" | head -n 1 | sed 's/id: //g'); bin/rake servers:panic[\$id] && bin/rake servers:remove[\$id]" + ;; + *) + log "ERROR" "Invalid method: $METHOD. Must be 'create' or 'delete'" + exit 1 + ;; +esac + +# Execute the command +log "DEBUG" "Executing Scalelite command" +if execute_scalelite_command "$REGION" "$ECS_CLUSTER" "$SCALELITE_TASK" "$COMMAND_STRING"; then + log "INFO" "Successfully executed $METHOD operation for server: $SERVER" +else + log "ERROR" "Failed to execute $METHOD operation for server: $SERVER" + exit 1 +fi -IFS=$OIFS; \ No newline at end of file +log "INFO" "Script completed successfully" +exit 0 diff --git a/setup.sh b/setup.sh index 77a37eb..e7b51e8 100755 --- a/setup.sh +++ b/setup.sh @@ -1,192 +1,419 @@ -#!/bin/bash -# This is a simple bash script for the BBB Application Infrastructure deployment. -# It basically glues together the parts running in loose coupeling during the deployment and helps to speed things up which -# otherwise would have to be noted down and put into the command line. -# This can be migrated into real orchestration / automation toolsets if needed (e.g. Ansible, Puppet or Terraform) +#!/bin/sh +# BBB Application Infrastructure deployment script +# Author: David Surey - suredavi@amazon.de +# Disclaimer: NOT FOR PRODUCTION USE - Only for demo and testing purposes -# created by David Surey - suredavi@amazon.de -# Disclaimber: NOT FOR PRODUCTION USE - Only for demo and testing purposes +# Color codes for output formatting +# Using printf to ensure proper escape sequence interpretation +RED=$(printf '\033[0;31m') +GREEN=$(printf '\033[0;32m') +YELLOW=$(printf '\033[1;33m') +BLUE=$(printf '\033[0;34m') +NC=$(printf '\033[0m') -ERROR_COUNT=0; +# Logging configuration +LOG_LEVEL=${LOG_LEVEL:-"INFO"} # Default to INFO if not set +LOG_FILE="/tmp/bbb-setup-$(date +%Y%m%d-%H%M%S).log" -if [[ $# -lt 5 ]] ; then - echo 'arguments missing, please provide at least email (-e), the aws profile string (-p), the domain name (-d), the deployment Stack Name (-s) and the hosted zone to be used (-h)' +# Get numeric value for log level +get_log_level_value() { + case $1 in + "DEBUG") echo 0 ;; + "INFO") echo 1 ;; + "WARN") echo 2 ;; + "ERROR") echo 3 ;; + *) echo 1 ;; # Default to INFO + esac +} + +# Logging function +log() { + level=$1 + shift + message=$* + timestamp=$(date '+%Y-%m-%d %H:%M:%S') + + # Check if level meets minimum level + current_level=$(get_log_level_value "$level") + minimum_level=$(get_log_level_value "$LOG_LEVEL") + + if [ "$current_level" -lt "$minimum_level" ]; then + return 2 + fi + + # Color selection based on level + case $level in + "DEBUG") color=$BLUE ;; + "INFO") color=$GREEN ;; + "WARN") color=$YELLOW ;; + "ERROR") color=$RED ;; + *) color=$NC ;; + esac + + # Output to console with color + printf "%s [%s%s%s] %s\n" "$timestamp" "$color" "$level" "$NC" "$message" + + # Output to log file without color + printf "%s [%s] %s\n" "$timestamp" "$level" "$message" >> "$LOG_FILE" + + # Exit on ERROR level messages + if [ "$level" = "ERROR" ]; then + exit 1 + fi +} + +debug_exec() { + if [ "$LOG_LEVEL" = "DEBUG" ]; then + "$@" 2>&1 | tee -a "$LOG_FILE" + else + "$@" >/dev/null 2>&1 + fi +} + +# Function to monitor CloudFormation stack events in real-time +monitor_stack() { + local stack_name=$1 + local last_event_time + last_event_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + + log "DEBUG" "Starting stack monitoring for: $stack_name" + + while true; do + events=$(aws cloudformation describe-stack-events \ + --profile "$BBBPROFILE" \ + --stack-name "$stack_name" \ + --query 'StackEvents[?contains(ResourceStatus, `IN_PROGRESS`) || contains(ResourceStatus, `COMPLETE`) || contains(ResourceStatus, `FAILED`)]') + + echo "$events" | jq -r --arg timestamp "$last_event_time" '.[] | select(.Timestamp > $timestamp) | "\(.Timestamp) [\(.LogicalResourceId)] \(.ResourceStatus) - \(.ResourceStatusReason // "No reason provided")"' | while read -r line; do + log "DEBUG" "$line" + done + + # Get stack status + stack_status=$(aws cloudformation describe-stacks \ + --profile "$BBBPROFILE" \ + --stack-name "$stack_name" \ + --query 'Stacks[0].StackStatus' \ + --output text) + + if [[ ! $stack_status =~ .*IN_PROGRESS$ ]]; then + if [[ $stack_status =~ .*FAILED$ || $stack_status =~ .*ROLLBACK.* ]]; then + log "ERROR" "Stack deployment failed with status: $stack_status" + return 1 + elif [[ $stack_status =~ .*COMPLETE$ ]]; then + log "INFO" "Stack deployment completed successfully with status: $stack_status" + return 0 + fi + fi + + last_event_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + sleep 5 + done +} + +# Modified deployment function +deploy_stack() { + local stack_name=$1 + local template=$2 + shift 2 + local params=("$@") + + log "INFO" "Starting deployment for stack: $stack_name" + log "DEBUG" "Using template: $template" + + if aws cloudformation deploy \ + --profile "$BBBPROFILE" \ + --stack-name "$stack_name" \ + "${params[@]}" \ + --template "$template" \ + --no-fail-on-empty-changeset; then + + monitor_stack "$stack_name" + return $? + else + log "ERROR" "Failed to initiate stack deployment for $stack_name" + return 1 + fi +} + +# Input validation function +validate_input() { + param_name=$1 + param_value=$2 + + if [ -z "$param_value" ]; then + log "ERROR" "Parameter ${param_name} is required but not provided" + fi +} + +# Usage information +usage() { + echo "Usage: $0 -p -e -h -s -d " + echo "Options:" + echo " -p : AWS Profile" + echo " -e : Operator Email" + echo " -h : Hosted Zone" + echo " -s : Stack Name" + echo " -d : Domain Name" + echo " -l : Log Level (DEBUG|INFO|WARN|ERROR) - default: INFO" exit 1 -fi +} + +# Initialize variables +BBBPROFILE="" +OPERATOREMAIL="" +HOSTEDZONE="" +BBBSTACK="" +DOMAIN="" -while getopts ":p:e:h:s:d:" opt; do - case $opt in - p) BBBPROFILE="$OPTARG" - ;; - e) OPERATOREMAIL="$OPTARG" - ;; - h) HOSTEDZONE="$OPTARG" - ;; - s) BBBSTACK="$OPTARG" - ;; - d) DOMAIN="$OPTARG" - ;; - \?) echo "Invalid option -$OPTARG" >&2 - ;; - esac +# Parse command line arguments +while getopts "p:e:h:s:d:l:" opt; do + case $opt in + p) BBBPROFILE="$OPTARG" ;; + e) OPERATOREMAIL="$OPTARG" ;; + h) HOSTEDZONE="$OPTARG" ;; + s) BBBSTACK="$OPTARG" ;; + d) DOMAIN="$OPTARG" ;; + l) + case $OPTARG in + DEBUG|INFO|WARN|ERROR) LOG_LEVEL="$OPTARG" ;; + *) log "ERROR" "Invalid log level: $OPTARG. Valid levels are: DEBUG INFO WARN ERROR" ;; + esac + ;; + *) log "ERROR" "Invalid option -$OPTARG" ;; + esac done -if ! [ -x "$(command -v aws)" ]; then - echo 'ERROR: aws cli is not installed.' >&2 - exit 1 +# Validate all required parameters +log "DEBUG" "Validating input parameters..." +validate_input "AWS Profile" "$BBBPROFILE" +validate_input "Operator Email" "$OPERATOREMAIL" +validate_input "Hosted Zone" "$HOSTEDZONE" +validate_input "Stack Name" "$BBBSTACK" +validate_input "Domain" "$DOMAIN" + +# Validate email format using grep +if ! echo "$OPERATOREMAIL" | grep -E "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$" > /dev/null; then + log "ERROR" "Invalid email format: $OPERATOREMAIL" fi -if ! docker ps -q 2>/dev/null; then - echo "ERROR: Docker is not running. Please start the docker runtime on your system and try again" - exit 1 +# Check for required tools +log "DEBUG" "Checking required tools..." +if ! debug_exec command -v aws; then + log "ERROR" "aws CLI is not installed" fi -echo "using AWS Profile $BBBPROFILE" -echo "##################################################" +if ! debug_exec docker ps; then + log "ERROR" "Docker is not running or not installed" +fi -echo "Validating AWS CloudFormation templates..." -echo "##################################################" -# Loop through the YAML templates in this repository -for TEMPLATE in $(find . -name 'bbb-on-aws-*.template.yaml'); do +if ! debug_exec command -v jq; then + log "ERROR" "jq is not installed" +fi - # Validate the template with CloudFormation - ERRORS=$(aws cloudformation validate-template --profile=$BBBPROFILE --template-body file://$TEMPLATE 2>&1 >/dev/null); - if [ "$?" -gt "0" ]; then - ((ERROR_COUNT++)); - echo "[fail] $TEMPLATE: $ERRORS"; - else - echo "[pass] $TEMPLATE"; - fi; - -done; +# Main execution starts here +log "INFO" "Starting BBB deployment with AWS Profile: $BBBPROFILE" +log "INFO" "##################################################" -# Error out if templates are not validate. -echo "$ERROR_COUNT template validation error(s)"; -if [ "$ERROR_COUNT" -gt 0 ]; - then exit 1; +# Deploy Prerequisites +log "INFO" "Deploying Prerequisites for BBB Environment" +BBBPREPSTACK="${BBBSTACK}-Sources" +if ! debug_exec deploy_stack "$BBBPREPSTACK" \ + "./templates/bbb-on-aws-buildbuckets.template.yaml"; then + log "ERROR" "Failed to deploy prerequisites stack" fi -echo "##################################################" -echo "Validating of AWS CloudFormation templates finished" -echo "##################################################" +# Get source bucket +SOURCE=$(aws cloudformation describe-stack-resources \ + --profile "$BBBPROFILE" \ + --stack-name "$BBBPREPSTACK" \ + --query "StackResources[?ResourceType=='AWS::S3::Bucket'].PhysicalResourceId" \ + --output text) -# Deploy the Needed Buckets for the later build -echo "deploy the Prerequisites of the BBB Environment and Application if needed" -echo "##################################################" -BBBPREPSTACK="${BBBSTACK}-Sources" -aws cloudformation deploy --stack-name $BBBPREPSTACK --profile=$BBBPROFILE --template ./templates/bbb-on-aws-buildbuckets.template.yaml -echo "##################################################" -echo "deployment done" - -SOURCE=$(aws cloudformation describe-stack-resources --profile $BBBPROFILE --stack-name $BBBPREPSTACK --query "StackResources[?ResourceType=='AWS::S3::Bucket'].PhysicalResourceId" --output text) - -# we will upload the needed CFN Templates to S3 containing the IaaC Code which deploys the actual infrastructure. -# This will error out if the source files are missing. -echo "##################################################" -echo "Copy Files to the S3 Bucket for further usage" -echo "##################################################" -if [ -e . ] -then - echo "##################################################" - echo "copy BBB code source file" - aws s3 sync --profile=$BBBPROFILE --exclude=".DS_Store" ./templates s3://$SOURCE - aws s3 sync --profile=$BBBPROFILE --exclude=".DS_Store" ./scripts s3://$SOURCE - echo "##################################################" +log "DEBUG" "Source bucket: $SOURCE" + +# Copy files to S3 +log "INFO" "Copying files to S3 bucket" +if [ -d "./templates" ] && [ -d "./scripts" ]; then + log "DEBUG" "Syncing templates and scripts to S3" + debug_exec aws s3 sync --profile="$BBBPROFILE" --exclude=".DS_Store" ./templates "s3://$SOURCE" + debug_exec aws s3 sync --profile="$BBBPROFILE" --exclude=".DS_Store" ./scripts "s3://$SOURCE" else - echo "BBB code source file missing" - echo "##################################################" + log "ERROR" "Required source directories (templates/ or scripts/) are missing" +fi + +# Deploy registry stack +deploy_registry_stack() { + log "INFO" "Deploying ECR registry stack..." + BBBECRSTACK="${BBBSTACK}-registry" + + if ! debug_exec deploy_stack "$BBBECRSTACK" \ + "./templates/bbb-on-aws-registry.template.yaml" \ + --capabilities CAPABILITY_IAM; then + log "ERROR" "Failed to deploy ECR registry stack" + return 1 + fi + + log "INFO" "ECR registry stack deployed successfully" + return 0 +} + +# Deploy the registry stack +log "INFO" "Deploying ECR registry stack" +if ! debug_exec deploy_registry_stack; then + log "ERROR" "Failed to deploy ECR registry stack" exit 1 fi -echo "##################################################" -echo "File Copy finished" - -ENVIRONMENTTYPE=$(jq -r ".Parameters.BBBEnvironmentType" bbb-on-aws-param.json) - -if [ "$ENVIRONMENTTYPE" == 'scalable' ] -then - BBBECRStack="${BBBSTACK}-registry" - aws cloudformation deploy --profile=$BBBPROFILE --stack-name $BBBECRStack \ - --parameter-overrides $PARAMETERS \ - $(jq -r '.Parameters | to_entries | map("\(.key)=\(.value)") | join(" ")' bbb-on-aws-param.json) \ - --template ./templates/bbb-on-aws-registry.template.yaml - - GREENLIGHTIMAGE=$(aws ecr describe-repositories --profile=$BBBPROFILE --query 'repositories[?contains(repositoryName, `greenlight`)].repositoryName' --output text) - SCALELITEIMAGE=$(aws ecr describe-repositories --profile=$BBBPROFILE --query 'repositories[?contains(repositoryName, `scalelite`)].repositoryName' --output text) - - SCALELITEREGISTRY=$(aws ecr describe-repositories --profile=$BBBPROFILE --query 'repositories[?contains(repositoryName, `scalelite`)].repositoryUri' --output text) - GREENLIGHTREGISTRY=$(aws ecr describe-repositories --profile=$BBBPROFILE --query 'repositories[?contains(repositoryName, `greenlight`)].repositoryUri' --output text) - - # we will mirror the needed images from dockerhub and push towards ECR - echo "##################################################" - echo "Mirror docker images to ECR for further usage" - echo "##################################################" - - SCALELITEIMAGETAGS=( BBBScaleliteNginxImageTag BBBScaleliteApiImageTag BBBScalelitePollerImageTag BBBScaleliteImporterImageTag ) - GREENLIGHTIMAGETAGS=( BBBgreenlightImageTag ) - - aws ecr get-login-password --profile=$BBBPROFILE | docker login --username AWS --password-stdin $SCALELITEREGISTRY - aws ecr get-login-password --profile=$BBBPROFILE | docker login --username AWS --password-stdin $GREENLIGHTREGISTRY - - for IMAGETAG in "${SCALELITEIMAGETAGS[@]}" - do - IMAGETAG=$(jq -r ".Parameters.$IMAGETAG" bbb-on-aws-param.json) - docker pull $SCALELITEIMAGE:$IMAGETAG - docker tag $SCALELITEIMAGE:$IMAGETAG $SCALELITEREGISTRY:$IMAGETAG - docker push $SCALELITEREGISTRY:$IMAGETAG - done - for IMAGETAG in "${GREENLIGHTIMAGETAGS[@]}" - do - IMAGETAG=$(jq -r ".Parameters.$IMAGETAG" bbb-on-aws-param.json) - docker pull $GREENLIGHTIMAGE:$IMAGETAG - docker tag $GREENLIGHTIMAGE:$IMAGETAG $GREENLIGHTREGISTRY:$IMAGETAG - docker push $GREENLIGHTREGISTRY:$IMAGETAG - done - - echo "##################################################" - echo "Registry Preperation finished" -fi - -# Setting the dynamic Parameters for the Deployment -PARAMETERS=" BBBOperatorEMail=$OPERATOREMAIL \ - BBBStackBucketStack=$BBBSTACK-Sources \ - BBBDomainName=$DOMAIN \ - BBBHostedZone=$HOSTEDZONE \ - BBBECRStack=$BBBSTACK-Registry" - -# Deploy the BBB infrastructure. -echo "Building the BBB Environment" -echo "##################################################" -aws cloudformation deploy --profile=$BBBPROFILE --stack-name $BBBSTACK \ + +# Get repository information and mirror images +log "INFO" "Getting repository information and mirroring images" +GREENLIGHTIMAGE=$(aws ecr describe-repositories --profile="$BBBPROFILE" --query 'repositories[?contains(repositoryName, `greenlight`)].repositoryName' --output text) +SCALELITEIMAGE=$(aws ecr describe-repositories --profile="$BBBPROFILE" --query 'repositories[?contains(repositoryName, `scalelite`)].repositoryName' --output text) + +SCALELITEREGISTRY=$(aws ecr describe-repositories --profile="$BBBPROFILE" --query 'repositories[?contains(repositoryName, `scalelite`)].repositoryUri' --output text) +GREENLIGHTREGISTRY=$(aws ecr describe-repositories --profile="$BBBPROFILE" --query 'repositories[?contains(repositoryName, `greenlight`)].repositoryUri' --output text) + +log "INFO" "##################################################" +log "INFO" "Mirror docker images to ECR for further usage" +log "INFO" "##################################################" + +SCALELITEIMAGETAGS=("BBBScaleliteNginxImageTag" "BBBScaleliteApiImageTag" "BBBScalelitePollerImageTag" "BBBScaleliteImporterImageTag") +GREENLIGHTIMAGETAGS=("BBBgreenlightImageTag") + +# Authenticate with ECR +if ! debug_exec aws ecr get-login-password --profile="$BBBPROFILE" | debug_exec docker login --username AWS --password-stdin "$SCALELITEREGISTRY"; then + log "ERROR" "Failed to authenticate with Scalelite ECR registry" +fi +if ! debug_exec aws ecr get-login-password --profile="$BBBPROFILE" | debug_exec docker login --username AWS --password-stdin "$GREENLIGHTREGISTRY"; then + log "ERROR" "Failed to authenticate with Greenlight ECR registry" +fi + +# Process Scalelite images +for IMAGETAG in "${SCALELITEIMAGETAGS[@]}" +do + TAGVALUE=$(jq -r ".Parameters.$IMAGETAG" bbb-on-aws-param.json) + log "DEBUG" "Processing Scalelite image with tag: $TAGVALUE" + debug_exec docker pull --platform linux/amd64 "$SCALELITEIMAGE:$TAGVALUE" + debug_exec docker tag "$SCALELITEIMAGE:$TAGVALUE" "$SCALELITEREGISTRY:$TAGVALUE" + debug_exec docker push "$SCALELITEREGISTRY:$TAGVALUE" +done + +for IMAGETAG in "${GREENLIGHTIMAGETAGS[@]}" +do + TAGVALUE=$(jq -r ".Parameters.$IMAGETAG" bbb-on-aws-param.json) + log "DEBUG" "Processing Greenlight image with tag: $TAGVALUE" + debug_exec docker pull --platform linux/amd64 "$GREENLIGHTIMAGE:$TAGVALUE" + debug_exec docker tag "$GREENLIGHTIMAGE:$TAGVALUE" "$GREENLIGHTREGISTRY:$TAGVALUE" + debug_exec docker push "$GREENLIGHTREGISTRY:$TAGVALUE" +done + +log "INFO" "##################################################" +log "INFO" "Registry Preparation finished" +log "INFO" "##################################################" + + +# Final deployment +log "INFO" "Deploying main BBB infrastructure" +if ! debug_exec deploy_stack "$BBBSTACK" \ + "./bbb-on-aws-root.template.yaml" \ --capabilities CAPABILITY_NAMED_IAM \ - --parameter-overrides $PARAMETERS \ - $(jq -r '.Parameters | to_entries | map("\(.key)=\(.value)") | join(" ")' bbb-on-aws-param.json) \ - --template ./bbb-on-aws-root.template.yaml + --parameter-overrides \ + "BBBOperatorEMail=$OPERATOREMAIL" \ + "BBBStackBucketStack=$BBBSTACK-Sources" \ + "BBBDomainName=$DOMAIN" \ + "BBBHostedZone=$HOSTEDZONE" \ + "BBBECRStack=$BBBSTACK-registry" \ + $(jq -r '.Parameters | to_entries | map("\(.key)=\(.value)") | join(" ")' bbb-on-aws-param.json); then + log "ERROR" "Main infrastructure deployment failed" +fi # Set the initial admin password for the environment -echo "Setting the intial admin password" -echo "##################################################" +log "INFO" "Setting the initial admin password" +log "INFO" "##################################################" + +# Get the secrets +log "DEBUG" "Retrieving administrator credentials from Secrets Manager" +ADMIN_SECRET=$(debug_exec aws secretsmanager list-secrets \ + --profile "$BBBPROFILE" \ + --filter Key="name",Values="BBBAdministratorlogin" \ + --query 'SecretList[0].Name' \ + --output text) + +if [ -z "$ADMIN_SECRET" ]; then + log "ERROR" "Failed to retrieve admin secret from Secrets Manager" +fi -#get the secrets -ADMIN_SECRET=$(aws secretsmanager list-secrets --profile $BBBPROFILE --filter Key="name",Values="BBBAdministratorlogin" --query 'SecretList[0].Name' --output text) -ADMIN_AUTH=$(aws secretsmanager get-secret-value --profile $BBBPROFILE --secret-id $ADMIN_SECRET) -ADMIN_PASSWORD=$(echo "$ADMIN_AUTH" | jq -r '.SecretString | fromjson | .password') -ADMIN_LOGIN=$(echo "$ADMIN_AUTH" | jq -r '.SecretString | fromjson | .username') +ADMIN_PASSWORD=$(debug_exec aws secretsmanager get-secret-value \ + --profile "$BBBPROFILE" \ + --secret-id "$ADMIN_SECRET" \ + --query 'SecretString' \ + --output text | jq -r '.password') -#get the cluster information -ECS_CLUSTERS=$(aws ecs --profile=$BBBPROFILE list-clusters) +ADMIN_LOGIN=$(debug_exec aws secretsmanager get-secret-value \ + --profile "$BBBPROFILE" \ + --secret-id "$ADMIN_SECRET" \ + --query 'SecretString' \ + --output text | jq -r '.username') + +if [ -z "$ADMIN_PASSWORD" ] || [ -z "$ADMIN_LOGIN" ]; then + log "ERROR" "Failed to retrieve admin credentials" + exit 1 +fi + +# Get the cluster information +log "DEBUG" "Retrieving ECS cluster information" +ECS_CLUSTERS=$(debug_exec aws ecs --profile="$BBBPROFILE" list-clusters) ECS_CLUSTER=$(echo "$ECS_CLUSTERS" | jq -r '.clusterArns[0] | split("/") | .[1]') -# get my greenlight service -GREENLIGHT_SERVICE=$(aws ecs list-services --profile $BBBPROFILE --cluster $ECS_CLUSTER --query "serviceArns[?contains(@, 'BBBgreenlightService')]" --output text | xargs -n 1 basename) -GREENLIGHT_TASK=$(aws ecs list-tasks --profile $BBBPROFILE --cluster $ECS_CLUSTER --service $GREENLIGHT_SERVICE --output text | awk -F"/" '{print $NF}' | rev | awk -F"/" '{print $1}' | rev) +if [ -z "$ECS_CLUSTER" ]; then + log "ERROR" "Failed to retrieve ECS cluster information" +fi + +# Get Greenlight service and task +log "DEBUG" "Retrieving Greenlight service information" +GREENLIGHT_SERVICE=$(debug_exec aws ecs list-services \ + --profile "$BBBPROFILE" \ + --cluster "$ECS_CLUSTER" \ + --query "serviceArns[?contains(@, 'BBBgreenlightService')]" \ + --output text | xargs -n 1 basename) + +if [ -z "$GREENLIGHT_SERVICE" ]; then + log "ERROR" "Failed to retrieve Greenlight service" +fi + +log "DEBUG" "Retrieving Greenlight task information" +GREENLIGHT_TASK=$(debug_exec aws ecs list-tasks \ + --profile "$BBBPROFILE" \ + --cluster "$ECS_CLUSTER" \ + --service "$GREENLIGHT_SERVICE" \ + --output text | awk -F"/" '{print $NF}' | rev | awk -F"/" '{print $1}' | rev) + +if [ -z "$GREENLIGHT_TASK" ]; then + log "ERROR" "Failed to retrieve Greenlight task" +fi -aws ecs execute-command --profile=$BBBPROFILE --cluster $ECS_CLUSTER \ - --task $GREENLIGHT_TASK \ +# Create admin user in Greenlight +log "INFO" "Creating admin user in Greenlight..." +output=$(debug_exec aws ecs execute-command \ + --profile="$BBBPROFILE" \ + --cluster "$ECS_CLUSTER" \ + --task "$GREENLIGHT_TASK" \ --container greenlight \ --interactive \ - --command "bundle exec rake admin:create["bbbadmin","${ADMIN_LOGIN}","${ADMIN_PASSWORD}"]" + --command "bundle exec rake admin:create[\"bbbadmin\",\"${ADMIN_LOGIN}\",\"${ADMIN_PASSWORD}\"]" 2>&1) + +if echo "$output" | grep -q "Email has already been taken"; then + log "DEBUG" "Admin user already exists, continuing..." +elif echo "$output" | grep -q "error\|Error\|ERROR" && ! echo "$output" | grep -q "Email has already been taken"; then + log "ERROR" "Failed to create admin user: $output" + exit 1 +else + log "INFO" "Admin user created successfully" +fi + -echo "##################################################" -echo "Deployment finished" +log "INFO" "Admin user creation process completed" +log "INFO" "##################################################" +log "INFO" "Deployment completed successfully" +log "INFO" "Log file available at: $LOG_FILE" exit 0 \ No newline at end of file diff --git a/templates/bbb-on-aws-bbbappscalable.template.yaml b/templates/bbb-on-aws-application.template.yaml similarity index 92% rename from templates/bbb-on-aws-bbbappscalable.template.yaml rename to templates/bbb-on-aws-application.template.yaml index 9363c0b..4e7c7ae 100644 --- a/templates/bbb-on-aws-bbbappscalable.template.yaml +++ b/templates/bbb-on-aws-application.template.yaml @@ -12,7 +12,7 @@ Parameters: BBBApplicationVersion: Description: Application Version for BBB Type: String - Default: focal-260 + Default: focal-270 BBBOperatorEMail: Description: E-Mail address to notify if there are any operational issues Type: String @@ -22,12 +22,9 @@ Parameters: BBBNotificationTopic: Description: Topic to be used for alarm notifications Type: String - BBBPublicApplicationSubnets: + BBBApplicationSubnets: Description: Comma separated list of the appserver's subnets Type: CommaDelimitedList - BBBPrivateApplicationSubnets: - Description: Comma separated list of the private EC2 instance subnets - Type: String BBBECSTaskSecurityGroup: Description: Security Group that should be assigned for the Task Instances Type: String @@ -35,9 +32,10 @@ Parameters: Description: Ubuntu AMI ID for Application Instances Type: String BBBApplicationInstanceType: - Description: Instance type for the appserver + Description: Instance type for BBB Application Server (x86_64 only) Type: String - Default: t3a.xlarge + Default: c5.2xlarge + AllowedPattern: ^(t3|t3a|m5|m5a|m5n|m5zn|m6i|m6a|m6in|c5|c5a|c5n|c6i|c6a|c6in|r5|r5a|r5b|r5n|r6i|r6a|r6in)\.(medium|large|xlarge|2xlarge|4xlarge|8xlarge|12xlarge|16xlarge|24xlarge)$ BBBApplicationDataVolumeSize: Description: Size of the application instance data volume Type: Number @@ -102,7 +100,7 @@ Resources: BBBApplicationAutoScaling: Type: AWS::AutoScaling::AutoScalingGroup Properties: - VPCZoneIdentifier: !Ref BBBPublicApplicationSubnets + VPCZoneIdentifier: !Ref BBBApplicationSubnets LaunchTemplate: LaunchTemplateId: !Ref BBBApplicationInstanceLaunchTemplate Version: !GetAtt BBBApplicationInstanceLaunchTemplate.LatestVersionNumber @@ -219,7 +217,8 @@ Resources: Groups: - !Ref BBBApplicationSecurityGroup UserData: - Fn::Base64: !Sub | + Fn::Base64: !Sub + - | #!/bin/bash -xe apt update -y @@ -233,22 +232,20 @@ Resources: while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 1; done - apt install -y git jq unzip python3-pip + apt install -y dnsutils net-tools git jq unzip python3-pip binutils rustc cargo pkg-config libssl-dev gettext cd /tmp pip3 install -U https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz - curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" - unzip awscliv2.zip - sudo ./aws/install + snap install aws-cli --classic curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb" -o "session-manager-plugin.deb" dpkg -i session-manager-plugin.deb while fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; do sleep 1; done - aws s3 cp s3://${BBBStackBucketStack}/bbb-appscalable-bootstrap.sh /tmp/bbb-bootstrap.sh + aws s3 cp s3://${BBBStackBucketStack}/bbb-bootstrap.sh /tmp/bbb-bootstrap.sh chmod +x /tmp/bbb-bootstrap.sh /tmp/bbb-bootstrap.sh -a ${BBBStackBucketStack} \ @@ -262,11 +259,11 @@ Resources: -k ${BBBSharedStorageAPspool} \ -l ${BBBECSCluster} \ -m ${BBBECSInstanceType} \ - -n "${BBBPrivateApplicationSubnets}" \ + -n "${BBBApplicationSubnetsString}" \ -o ${BBBECSTaskSecurityGroup} /usr/local/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource BBBApplicationAutoScaling --region ${AWS::Region} || true - + - BBBApplicationSubnetsString: !Join [",", !Ref BBBApplicationSubnets] Outputs: BBBApplicationEC2InstanceProfile: Description: Big Blue Button Application Instance Profile diff --git a/templates/bbb-on-aws-bbbappsingle.template.yaml b/templates/bbb-on-aws-bbbappsingle.template.yaml deleted file mode 100644 index 47fd0c7..0000000 --- a/templates/bbb-on-aws-bbbappsingle.template.yaml +++ /dev/null @@ -1,234 +0,0 @@ ---- -AWSTemplateFormatVersion: '2010-09-09' -Description: > - - This Cloudformation Template deploys the single server option of the BigBlueButton application server. - - Disclaimer: Not for production use. Demo and testing purposes only. - - Author: David Surey - -Parameters: - BBBApplicationVersion: - Description: Application Version for BBB - Type: String - Default: focal-260 - BBBOperatorEMail: - Description: E-Mail address to notify if there are any operational issues - Type: String - AllowedPattern: "([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)" - ConstraintDescription: Must be a valid email address. - Default: johndoe@example.com - BBBNotificationTopic: - Description: Topic to be used for alarm notifications - Type: String - BBBPublicApplicationSubnets: - Description: Comma separated list of the appserver's subnets - Type: CommaDelimitedList - BBBApplicationInstanceAMI: - Description: Ubuntu Version to be deployed for Application Instances - Default: focal-20.04 - Type: String - BBBApplicationInstanceType: - Description: Instance type for the appserver - Type: String - Default: t3a.xlarge - BBBApplicationDataVolumeSize: - Description: Size of the application instance data volume - Type: Number - Default: 20 - BBBApplicationRootVolumeSize: - Description: Size of the application instance data volume - Type: Number - Default: 20 - BBBApplicationMaxInstances: - Type: Number - Description: Maximum number of appserver instances - Default: 3 - BBBApplicationMinInstances: - Type: Number - Description: Minimum number of appserver instances - Default: 1 - BBBApplicationDesiredInstances: - Type: Number - Description: Desired number of appserver instances - Default: 1 - BBBApplicationSecurityGroup: - Description: Security Group that should be assigned for the appserver - Type: String - BBBHostedZone: - Description: Hosted zone in which the DNS entries for the app servers should be created - Type: String - BBBDomainName: - Description: Base domain name used for the instance - Type: String - BBBStackBucketStack: - Description: S3 Bucket Stack that contains scripts and sources - Type: String - BBBSystemLogsGroup: - Description: Log group to be used for the system logs - Type: String - BBBApplicationLogsGroup: - Description: Log group to be used for the Application logs - Type: String - BBBSystemLogsGroupArn: - Description: Log group to be used for the system logs - Type: String - BBBApplicationLogsGroupArn: - Description: Log group to be used for the Application logs - Type: String - -Resources: - BBBApplicationAutoScaling: - Type: AWS::AutoScaling::AutoScalingGroup - Properties: - VPCZoneIdentifier: !Ref BBBPublicApplicationSubnets - LaunchTemplate: - LaunchTemplateId: !Ref BBBApplicationInstanceLaunchTemplate - Version: !GetAtt BBBApplicationInstanceLaunchTemplate.LatestVersionNumber - TerminationPolicies: - - DEFAULT - MaxSize: !Ref BBBApplicationMaxInstances - MinSize: !Ref BBBApplicationMinInstances - DesiredCapacity: !Ref BBBApplicationDesiredInstances - NotificationConfiguration: - TopicARN: - Ref: BBBNotificationTopic - NotificationTypes: - - autoscaling:EC2_INSTANCE_LAUNCH - - autoscaling:EC2_INSTANCE_LAUNCH_ERROR - - autoscaling:EC2_INSTANCE_TERMINATE - - autoscaling:EC2_INSTANCE_TERMINATE_ERROR - CreationPolicy: - ResourceSignal: - Timeout: PT30M - UpdatePolicy: - AutoScalingReplacingUpdate: - WillReplace: true - - BBBApplicationEC2Role: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Statement: - - Effect: Allow - Principal: - Service: [ ec2.amazonaws.com ] - Action: [ "sts:AssumeRole" ] - Path: / - Policies: - - PolicyName: "bbbawsorchestrationaccess" - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: "Allow" - Action: - - route53:ChangeResourceRecordSets - - route53:GetHostedZone - - route53:ListResourceRecordSets - Resource: !Sub "arn:aws:route53:::hostedzone/${BBBHostedZone}" - - Effect: Allow - Action: - - s3:GetObject - Resource: - - !Sub "arn:aws:s3:::${BBBStackBucketStack}/*" - - Effect: Allow - Action: - - logs:PutLogEvents - - logs:CreateLogStream - - logs:DescribeLogStreams - - logs:CreateLogGroup - Resource: - - !Ref BBBSystemLogsGroupArn - - !Ref BBBApplicationLogsGroupArn - ManagedPolicyArns: - - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore - - BBBApplicationEC2InstanceProfile: - Type: AWS::IAM::InstanceProfile - Properties: - Path: / - Roles: - - !Ref BBBApplicationEC2Role - - BBBApplicationInstanceLaunchTemplate: - Type: AWS::EC2::LaunchTemplate - Properties: - LaunchTemplateData: - BlockDeviceMappings: - - DeviceName: /dev/sda1 - Ebs: - VolumeType: gp2 - VolumeSize: !Ref BBBApplicationRootVolumeSize - Encrypted: true - - DeviceName: /dev/sdf - Ebs: - VolumeSize: !Ref BBBApplicationDataVolumeSize - VolumeType: gp2 - Encrypted: true - IamInstanceProfile: - Arn: !GetAtt BBBApplicationEC2InstanceProfile.Arn - ImageId: !Ref BBBApplicationInstanceAMI - InstanceType: !Ref BBBApplicationInstanceType - NetworkInterfaces: - - AssociatePublicIpAddress: true - DeviceIndex: 0 - Groups: - - !Ref BBBApplicationSecurityGroup - UserData: - Fn::Base64: !Sub | - #!/bin/bash -xe - - apt update -y - - while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 1; done - - DEBIAN_FRONTEND='noninteractive' apt -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' full-upgrade - - apt autoremove -y - apt autoclean - - while fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 1; done - - apt install -y git jq unzip python3-pip - - cd /tmp - - pip3 install -U https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz - - curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" - unzip awscliv2.zip - sudo ./aws/install - - while fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; do sleep 1; done - - aws s3 cp s3://${BBBStackBucketStack}/bbb-appsingle-bootstrap.sh /tmp/bbb-bootstrap.sh - chmod +x /tmp/bbb-bootstrap.sh - - /tmp/bbb-bootstrap.sh -a ${BBBStackBucketStack} \ - -b ${BBBSystemLogsGroup} \ - -c ${BBBDomainName} \ - -e ${BBBHostedZone} \ - -g ${BBBOperatorEMail} \ - -h ${BBBApplicationVersion} \ - -i ${AWS::Region} - - /usr/local/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource BBBApplicationAutoScaling --region ${AWS::Region} || true - -Outputs: - BBBApplicationEC2InstanceProfile: - Description: Big Blue Button Application Instance Profile - Value: - Ref: BBBApplicationEC2InstanceProfile - BBBApplicationEC2Role: - Description: Big Blue Button Application Instance Role - Value: - Ref: BBBApplicationEC2Role - BBBApplicationAutoScaling: - Description: Big Blue Button Application Instance Autoscaling Group - Value: - Ref: BBBApplicationAutoScaling - BBBApplicationInstanceLaunchTemplate: - Description: Big Blue Button Application Instance Launch Template - Value: - Ref: BBBApplicationInstanceLaunchTemplate diff --git a/templates/bbb-on-aws-cachedb.template.yaml b/templates/bbb-on-aws-cachedb.template.yaml index 2779666..f4df519 100644 --- a/templates/bbb-on-aws-cachedb.template.yaml +++ b/templates/bbb-on-aws-cachedb.template.yaml @@ -1,155 +1,98 @@ ---- AWSTemplateFormatVersion: '2010-09-09' -Description: > - - This Cloudformation Template deploys the Redis Cluster (Amazon ElastiCache) used by the Scalelite load balancer. - - Disclaimer: Not for production use. Demo and testing purposes only. - - Author: David Surey +Description: 'Amazon ElastiCache template' Parameters: BBBNotificationTopic: Description: Topic to be used for alarm notifications Type: String + BBBCACHEDBInstanceType: - Description: Instance type for Amazon ElastiCache (Redis) + Description: Instance type for Amazon ElastiCache Type: String - Default: cache.t3.micro - BBBPrivateDBSubnets: - Description: Comma separated list of the private database subnets + Default: cache.t4g.micro + AllowedPattern: ^(cache\.(t3|t4g|m6g|m7g|r6g|r7g)\.(micro|small|medium|large|xlarge|2xlarge|4xlarge|8xlarge|12xlarge|16xlarge))$ + + BBBDatastoreSubnets: + Description: Comma separated list of the datastore subnets Type: CommaDelimitedList + BBBCACHEDBSecurityGroup: Description: Security Group that should be assigned for cache database Type: String + BBBCacheAZMode: - Description: Redis Cache AZ Mode + Description: ElastiCache Cache AZ Mode Type: String Default: single-az AllowedValues: - single-az - cross-az +Mappings: + CacheSettings: + ElastiCache: + Engine: valkey + Version: '7.2' + Port: 6379 + Family: valkey7 + +Conditions: + BBBIsCrossAZ: !Equals [!Ref BBBCacheAZMode, "cross-az"] + Resources: BBBCacheSubnetGroup: Type: AWS::ElastiCache::SubnetGroup Properties: Description: !Sub CacheSubnetGroup-${AWS::StackName} - SubnetIds: !Ref BBBPrivateDBSubnets + SubnetIds: !Ref BBBDatastoreSubnets BBBParametersGroup: Type: AWS::ElastiCache::ParameterGroup Properties: - CacheParameterGroupFamily: "redis7" - Description: "Modifications to support better performance" + CacheParameterGroupFamily: !FindInMap [CacheSettings, ElastiCache, Family] + Description: "ElastiCache parameter group with performance optimizations" Properties: tcp-keepalive: 60 timeout: 900 - BBBElasticacheRedis: - Type: AWS::ElastiCache::CacheCluster + BBBElasticache: + Type: AWS::ElastiCache::ReplicationGroup Properties: - NumCacheNodes: 1 - CacheNodeType: - Ref: BBBCACHEDBInstanceType - CacheParameterGroupName: - Ref: BBBParametersGroup + ReplicationGroupId: !Sub 'bbb-${AWS::Region}-${AWS::AccountId}' + ReplicationGroupDescription: !Sub ${AWS::StackName} Cache Cluster + NumCacheClusters: 1 + CacheNodeType: !Ref BBBCACHEDBInstanceType + CacheParameterGroupName: !Ref BBBParametersGroup CacheSubnetGroupName: !Ref BBBCacheSubnetGroup - AZMode: !Ref BBBCacheAZMode - Engine: "redis" - EngineVersion: "7.0" + Engine: !FindInMap [CacheSettings, ElastiCache, Engine] + EngineVersion: !FindInMap [CacheSettings, ElastiCache, Version] + Port: !FindInMap [CacheSettings, ElastiCache, Port] NotificationTopicArn: !Ref BBBNotificationTopic - VpcSecurityGroupIds: - - Ref: BBBCACHEDBSecurityGroup - - BBBDatabaseACPUUtilizationTooHighAlarm: - Type: 'AWS::CloudWatch::Alarm' - Properties: - AlarmActions: - - Ref: BBBNotificationTopic - AlarmDescription: 'Average Cache-Node CPU utilization over last 10 minutes too high.' - ComparisonOperator: GreaterThanThreshold - Dimensions: - - Name: CacheClusterId - Value: - Ref: BBBElasticacheRedis - - Name: CacheNodeId - Value: 0001 - EvaluationPeriods: 1 - MetricName: CPUUtilization - Namespace: 'AWS/ElastiCache' - OKActions: - - Ref: BBBNotificationTopic - Period: 600 - Statistic: Average - Threshold: 80 - - BBBCacheACPUCreditBalanceTooLowAlarm: - Type: 'AWS::CloudWatch::Alarm' - Properties: - AlarmActions: - - Ref: BBBNotificationTopic - AlarmDescription: 'Average Cache-Node CPU credit balance over last 10 minutes too low, expect a significant performance drop soon.' - ComparisonOperator: LessThanThreshold - Dimensions: - - Name: CacheClusterId - Value: - Ref: BBBElasticacheRedis - - Name: CacheNodeId - Value: 0001 - EvaluationPeriods: 1 - MetricName: CPUCreditBalance - Namespace: 'AWS/ElastiCache' - OKActions: - - Ref: BBBNotificationTopic - Period: 600 - Statistic: Average - Threshold: 20 - - BBBCacheAFreeableMemoryTooLowAlarm: - Type: 'AWS::CloudWatch::Alarm' - Properties: - AlarmActions: - - Ref: BBBNotificationTopic - AlarmDescription: 'Average Cache-Node freeable memory over last 10 minutes too low, performance may suffer.' - ComparisonOperator: LessThanThreshold - Dimensions: - - Name: CacheClusterId - Value: - Ref: BBBElasticacheRedis - - Name: CacheNodeId - Value: 0001 - EvaluationPeriods: 1 - MetricName: FreeableMemory - Namespace: 'AWS/ElastiCache' - OKActions: - - Ref: BBBNotificationTopic - Period: 600 - Statistic: Average - Threshold: 64000000 + SecurityGroupIds: [!Ref BBBCACHEDBSecurityGroup] + AutomaticFailoverEnabled: false + TransitEncryptionEnabled: false + MultiAZEnabled: !If + - BBBIsCrossAZ + - true + - false Outputs: BBBCacheDBAddress: Description: The Big Blue Button Cache Database Address - Value: - Fn::GetAtt: - - BBBElasticacheRedis - - RedisEndpoint.Address + Value: !GetAtt BBBElasticache.PrimaryEndPoint.Address + BBBCacheDBPort: Description: The Big Blue Button Cache Database Port - Value: - Fn::GetAtt: - - BBBElasticacheRedis - - RedisEndpoint.Port - BBBElasticacheRedis: - Description: The Big Blue Button Cache Redis Cluster - Value: - Ref: BBBElasticacheRedis - BBBParametersGroup: - Description: The Big Blue Button Cache Redis Parameter Group - Value: - Ref: BBBParametersGroup - BBBCacheSubnetGroup: - Description: The Big Blue Button Cache Redis Subnet Group - Value: - Ref: BBBCacheSubnetGroup + Value: !GetAtt BBBElasticache.PrimaryEndPoint.Port + + BBBElasticache: + Description: The Big Blue Button Cache ElastiCache Cluster + Value: !Ref BBBElasticache + + BBBParametersGroup: + Description: The Big Blue Button Cache ElastiCache Parameter Group + Value: !Ref BBBParametersGroup + + BBBCacheSubnetGroup: + Description: The Big Blue Button Cache ElastiCache Subnet Group + Value: !Ref BBBCacheSubnetGroup diff --git a/templates/bbb-on-aws-database.template.yaml b/templates/bbb-on-aws-database.template.yaml index 4cec549..598f6e9 100755 --- a/templates/bbb-on-aws-database.template.yaml +++ b/templates/bbb-on-aws-database.template.yaml @@ -1,11 +1,7 @@ ---- AWSTemplateFormatVersion: '2010-09-09' Description: > - This Cloudformation Template deploys the Database Cluster (Amazon Aurora) for the BigBlueButton application infrastructure. - Disclaimer: Not for production use. Demo and testing purposes only. - Author: David Surey Parameters: @@ -17,38 +13,73 @@ Parameters: Type: Number Default: 5432 BBBDBInstanceType: - Description: DB RDS instance type + Description: DB instance type for Aurora PostgreSQL Type: String Default: db.serverless + AllowedPattern: ^(db\.(serverless|(t3|t4g|r5|r6g|r6i|r7g)\.(medium|large|xlarge|2xlarge|4xlarge|8xlarge|12xlarge|16xlarge|24xlarge)))$ BBBServerlessAuroraMinCapacity: - Description: The minimum capacity for the Amazon Aurora Serverless Cluster. + Description: The minimum capacity for the Amazon Aurora Serverless Cluster Type: Number Default: 2 BBBServerlessAuroraMaxCapacity: Description: The maximum capacity for the Amazon Aurora Serverless Cluster Type: Number Default: 4 - BBBPrivateDBSubnets: + BBBDatastoreSubnets: Description: Comma separated list of the private database subnets Type: CommaDelimitedList BBBDBEngineVersion: Description: Database engine version for Aurora PostgreSQL Type: String - Default: 14.3 + Default: 16.4 + AllowedValues: + - 14.3 + - 14.4 + - 14.5 + - 14.6 + - 14.7 + - 14.8 + - 15.3 + - 15.4 + - 15.5 + - 16.1 + - 16.2 + - 16.3 + - 16.4 BBBDBSecurityGroup: Description: Security Group that should be assigned for the database Type: String - BBBEnvironmentStage: - Type: String - Description: Select the appropriate environment - AllowedValues: - - stage - - prod - - dev + +Mappings: + AuroraEngineMap: + "14.3": + Family: "aurora-postgresql14" + "14.4": + Family: "aurora-postgresql14" + "14.5": + Family: "aurora-postgresql14" + "14.6": + Family: "aurora-postgresql14" + "14.7": + Family: "aurora-postgresql14" + "14.8": + Family: "aurora-postgresql14" + "15.3": + Family: "aurora-postgresql15" + "15.4": + Family: "aurora-postgresql15" + "15.5": + Family: "aurora-postgresql15" + "16.1": + Family: "aurora-postgresql16" + "16.2": + Family: "aurora-postgresql16" + "16.3": + Family: "aurora-postgresql16" + "16.4": + Family: "aurora-postgresql16" Conditions: - BBBProdEnvironment: !Equals [ !Ref BBBEnvironmentStage, prod ] - BBBNonProdEnvironment: !Not [ Condition: BBBProdEnvironment ] BBBServerlessAurora: !Equals [ !Ref BBBDBInstanceType, db.serverless ] Resources: @@ -56,8 +87,7 @@ Resources: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: Subnet Group for RDS Deployment - SubnetIds: - Ref: BBBPrivateDBSubnets + SubnetIds: !Ref BBBDatastoreSubnets BBBRDSDBSecret: Type: AWS::SecretsManager::Secret @@ -67,7 +97,7 @@ Resources: SecretStringTemplate: '{"username": "BBBDBUsr"}' GenerateStringKey: 'password' PasswordLength: 16 - ExcludePunctuation: 'true' + ExcludePunctuation: true BBBRDSDBConnectionSecret: Type: AWS::SecretsManager::Secret @@ -75,16 +105,10 @@ Resources: Description: 'This is the BBB Database cluster url secret' SecretString: !Sub - '{"hostname": "${HOSTNAME}", "port": ${PORT}, "connectionString": "postgresql://${BBBDBUser}:${BBBDBPassword}@${HOSTNAME}:${PORT}"}' - - HOSTNAME: - Fn::GetAtt: - - BBBRDSCluster - - Endpoint.Address - PORT: - Fn::GetAtt: - - BBBRDSCluster - - Endpoint.Port - BBBDBUser: !Join [ '', [ '{{resolve:secretsmanager:', !Ref BBBRDSDBSecret, ':SecretString:username}}' ] ] - BBBDBPassword: !Join [ '', [ '{{resolve:secretsmanager:', !Ref BBBRDSDBSecret, ':SecretString:password}}' ] ] + - HOSTNAME: !GetAtt BBBRDSCluster.Endpoint.Address + PORT: !GetAtt BBBRDSCluster.Endpoint.Port + BBBDBUser: !Join ['', ['{{resolve:secretsmanager:', !Ref BBBRDSDBSecret, ':SecretString:username}}']] + BBBDBPassword: !Join ['', ['{{resolve:secretsmanager:', !Ref BBBRDSDBSecret, ':SecretString:password}}']] BBBDatabaseName: Type: AWS::SecretsManager::Secret @@ -96,154 +120,106 @@ Resources: PasswordLength: 16 ExcludePunctuation: true + BBBRDSDBParameterGroupPostgres: + Type: AWS::RDS::DBParameterGroup + Properties: + Description: Aurora PG Database Instance Parameter Group + Family: !FindInMap [AuroraEngineMap, !Ref BBBDBEngineVersion, Family] + + BBBRDSDBClusterParameterGroupPostgres: + Type: AWS::RDS::DBClusterParameterGroup + Properties: + Description: Aurora PG Database Cluster Parameter Group + Family: !FindInMap [AuroraEngineMap, !Ref BBBDBEngineVersion, Family] + Parameters: + timezone: Europe/Berlin + BBBRDSCluster: Type: AWS::RDS::DBCluster + DeletionPolicy: Delete Properties: - MasterUsername: !Join [ '', [ '{{resolve:secretsmanager:', !Ref BBBRDSDBSecret, ':SecretString:username}}' ] ] - MasterUserPassword: !Join [ '', [ '{{resolve:secretsmanager:', !Ref BBBRDSDBSecret, ':SecretString:password}}' ] ] - DatabaseName: !Join [ '', [ 'BBB', '{{resolve:secretsmanager:', !Ref BBBDatabaseName, ':SecretString:DBName}}' ] ] - Port: - Ref: BBBDBPort + MasterUsername: !Join ['', ['{{resolve:secretsmanager:', !Ref BBBRDSDBSecret, ':SecretString:username}}']] + MasterUserPassword: !Join ['', ['{{resolve:secretsmanager:', !Ref BBBRDSDBSecret, ':SecretString:password}}']] + DatabaseName: !Join ['', ['BBB', '{{resolve:secretsmanager:', !Ref BBBDatabaseName, ':SecretString:DBName}}']] + Port: !Ref BBBDBPort Engine: aurora-postgresql - EngineVersion: - Ref: BBBDBEngineVersion - StorageEncrypted: 'true' - DBSubnetGroupName: - Ref: BBBDBSubnetGroup - DBClusterParameterGroupName: - Ref: "BBBRDSDBClusterParameterGroupPostgres" - VpcSecurityGroupIds: - - Ref: BBBDBSecurityGroup + EngineVersion: !Ref BBBDBEngineVersion + StorageEncrypted: true + DBSubnetGroupName: !Ref BBBDBSubnetGroup + DBClusterParameterGroupName: !Ref BBBRDSDBClusterParameterGroupPostgres + VpcSecurityGroupIds: [!Ref BBBDBSecurityGroup] ServerlessV2ScalingConfiguration: !If - BBBServerlessAurora - MinCapacity: !Ref BBBServerlessAuroraMinCapacity MaxCapacity: !Ref BBBServerlessAuroraMaxCapacity - - !Ref "AWS::NoValue" + - !Ref AWS::NoValue BBBRDSDBInstance1: Type: AWS::RDS::DBInstance + DeletionPolicy: Delete Properties: - AllowMajorVersionUpgrade: 'false' - AutoMinorVersionUpgrade: 'true' - CopyTagsToSnapshot: 'true' - DBSubnetGroupName: - Ref: BBBDBSubnetGroup + AllowMajorVersionUpgrade: false + AutoMinorVersionUpgrade: true + CopyTagsToSnapshot: true + DBSubnetGroupName: !Ref BBBDBSubnetGroup Engine: aurora-postgresql - EngineVersion: - Ref: BBBDBEngineVersion - DBClusterIdentifier: - Ref: BBBRDSCluster - DBParameterGroupName: - Ref: BBBRDSDBParameterGroupPostgres - AvailabilityZone: - Fn::Select: - - '0' - - Fn::GetAZs: - Ref: AWS::Region - DBInstanceClass: - Ref: BBBDBInstanceType + EngineVersion: !Ref BBBDBEngineVersion + DBClusterIdentifier: !Ref BBBRDSCluster + DBParameterGroupName: !Ref BBBRDSDBParameterGroupPostgres + AvailabilityZone: !Select [0, !GetAZs ''] + DBInstanceClass: !Ref BBBDBInstanceType BBBRDSDBInstance2: Type: AWS::RDS::DBInstance - Condition: BBBProdEnvironment + DeletionPolicy: Delete Properties: - AllowMajorVersionUpgrade: 'false' - AutoMinorVersionUpgrade: 'true' - CopyTagsToSnapshot: 'true' - DBSubnetGroupName: - Ref: BBBDBSubnetGroup + AllowMajorVersionUpgrade: false + AutoMinorVersionUpgrade: true + CopyTagsToSnapshot: true + DBSubnetGroupName: !Ref BBBDBSubnetGroup Engine: aurora-postgresql - EngineVersion: - Ref: BBBDBEngineVersion - DBClusterIdentifier: - Ref: BBBRDSCluster - AvailabilityZone: - Fn::Select: - - '1' - - Fn::GetAZs: - Ref: AWS::Region - DBInstanceClass: - Ref: BBBDBInstanceType - - BBBRDSDBParameterGroupPostgres: - Type: AWS::RDS::DBParameterGroup - Properties: - Description: Aurora PG 14 Database Instance Parameter Group for Cloudformation Stack - Family: !If [ BBBServerlessAurora, aurora-postgresql14, aurora-postgresql14 ] - Parameters: - shared_preload_libraries: auto_explain,pg_stat_statements,pg_hint_plan,pgaudit - log_statement: "ddl" - log_connections: 1 - log_disconnections: 1 - log_lock_waits: 1 - log_min_duration_statement: 5000 - auto_explain.log_min_duration: 5000 - auto_explain.log_verbose: 1 - log_rotation_age: 1440 - log_rotation_size: 102400 - rds.log_retention_period: 10080 - random_page_cost: 1 - track_activity_query_size: 16384 - idle_in_transaction_session_timeout: 7200000 - statement_timeout: 7200000 - search_path: '"$user",public' - - BBBRDSDBClusterParameterGroupPostgres: - Type: AWS::RDS::DBClusterParameterGroup - Properties: - Description: CloudFormation Sample Aurora Cluster PG Parameter Group - Family: !If [ BBBServerlessAurora, aurora-postgresql14, aurora-postgresql14 ] - Parameters: - timezone: Europe/Berlin + EngineVersion: !Ref BBBDBEngineVersion + DBClusterIdentifier: !Ref BBBRDSCluster + DBParameterGroupName: !Ref BBBRDSDBParameterGroupPostgres + AvailabilityZone: !Select [1, !GetAZs ''] + DBInstanceClass: !Ref BBBDBInstanceType DBClusterEventSubscription: - Type: 'AWS::RDS::EventSubscription' + Type: AWS::RDS::EventSubscription Properties: EventCategories: - failover - failure - notification - SnsTopicArn: - Ref: BBBNotificationTopic - SourceIds: - - Ref: BBBRDSCluster - SourceType: 'db-cluster' + SnsTopicArn: !Ref BBBNotificationTopic + SourceIds: [!Ref BBBRDSCluster] + SourceType: db-cluster Outputs: - BBBDBName: - Value: - !Join [ '_', [ 'APPDB', !Ref AWS::StackName ] ] + BBBDBName: + Value: !Join ['_', ['APPDB', !Ref 'AWS::StackName']] BBBDB: Description: The Big Blue Button Database Created - Value: - Ref: BBBRDSCluster + Value: !Ref BBBRDSCluster BBBRDSDBConnectionSecret: Description: The Big Blue Button DB Connection Data - Value: - Ref: BBBRDSDBConnectionSecret + Value: !Ref BBBRDSDBConnectionSecret BBBRDSDBClusterParameterGroup: Description: The Big Blue Button DB Cluster Parameter Group - Value: - Ref: "BBBRDSDBClusterParameterGroupPostgres" + Value: !Ref BBBRDSDBClusterParameterGroupPostgres BBBDBSubnetGroup: Description: The Big Blue Button DB Subnet Group - Value: - Ref: BBBDBSubnetGroup + Value: !Ref BBBDBSubnetGroup BBBRDSCluster: Description: The Big Blue Button DB Cluster - Value: - Ref: BBBRDSCluster + Value: !Ref BBBRDSCluster BBBRDSDBInstance1: Description: The Big Blue Button DB Instance 1 - Value: - Ref: BBBRDSDBInstance1 + Value: !Ref BBBRDSDBInstance1 BBBRDSDBInstance2: - Condition: BBBProdEnvironment Description: The Big Blue Button DB Instance 2 - Value: - Ref: BBBRDSDBInstance2 + Value: !Ref BBBRDSDBInstance2 BBBRDSDBParameterGroup: Description: The Big Blue Button DB Parameter Group - Value: - Ref: "BBBRDSDBParameterGroupPostgres" + Value: !Ref BBBRDSDBParameterGroupPostgres diff --git a/templates/bbb-on-aws-ecs.template.yaml b/templates/bbb-on-aws-ecs.template.yaml index 2cfdabd..252c4b6 100644 --- a/templates/bbb-on-aws-ecs.template.yaml +++ b/templates/bbb-on-aws-ecs.template.yaml @@ -13,67 +13,10 @@ Parameters: Description: Topic to be used for alarm notifications Type: String BBBECSInstanceType: - Description: EC2 instance type for ECS Cluster worker nodes + Description: Compute type for ECS Cluster (FARGATE or EC2 instance type, x86_64 only) Type: String - Default: t3a.large - AllowedValues: - - fargate - - t3a.medium - - t3a.large - - t3a.xlarge - - t3a.2xlarge - - c5a.large - - c5a.xlarge - - c5a.2xlarge - - c5a.4xlarge - - c5a.8xlarge - - c5a.12xlarge - - c5a.16xlarge - - c5a.24xlarge - - m5a.large - - m5a.xlarge - - m5a.2xlarge - - m5a.4xlarge - - m5a.8xlarge - - m5a.12xlarge - - m5a.16xlarge - - m5a.24xlarge - - r5a.large - - r5a.xlarge - - r5a.2xlarge - - r5a.4xlarge - - r5a.8xlarge - - r5a.12xlarge - - r5a.16xlarge - - r5a.24xlarge - - t3.medium - - t3.large - - t3.xlarge - - t3.2xlarge - - c5.large - - c5.xlarge - - c5.2xlarge - - c5.4xlarge - - c5.8xlarge - - c5.12xlarge - - c5.16xlarge - - c5.24xlarge - - m5.large - - m5.xlarge - - m5.2xlarge - - m5.4xlarge - - m5.8xlarge - - m5.12xlarge - - m5.16xlarge - - m5.24xlarge - - r5.large - - r5.xlarge - - r5.2xlarge - - r5.4xlarge - - r5.8xlarge - - r5.12xlarge - - r5.16xlarge - - r5.24xlarge + Default: t3.large + AllowedPattern: ^(fargate|(t2|t3|t3a|m4|m5|m5a|m6i|c4|c5|c5a|c6i|r4|r5|r5a|r6i)\.(medium|large|xlarge|2xlarge|4xlarge|8xlarge|12xlarge|16xlarge|24xlarge))$ BBBECSMaxInstances: Type: Number Description: Maximum number of ECS Worker Instances @@ -86,7 +29,7 @@ Parameters: Type: Number Description: Desired number of ECS Worker Instances Default: 1 - BBBPrivateApplicationSubnets: + BBBApplicationSubnets: Description: Comma separated list of the private EC2 instance subnets Type: CommaDelimitedList BBBECSTaskSecurityGroup: @@ -131,7 +74,7 @@ Resources: Type: AWS::AutoScaling::AutoScalingGroup Condition: BBBECSEC2 Properties: - VPCZoneIdentifier: !Ref BBBPrivateApplicationSubnets + VPCZoneIdentifier: !Ref BBBApplicationSubnets LaunchConfigurationName: !Ref BBBECSInstanceLaunchConfiguration TerminationPolicies: - DEFAULT diff --git a/templates/bbb-on-aws-frontendapps.template.yaml b/templates/bbb-on-aws-frontendapps.template.yaml index c784a12..ee7e3b5 100644 --- a/templates/bbb-on-aws-frontendapps.template.yaml +++ b/templates/bbb-on-aws-frontendapps.template.yaml @@ -22,16 +22,13 @@ Parameters: BBBScaleliteELBSecurityGroup: Description: Security Group that should be assigned for the Scalelite ELB Type: String - BBBPublicApplicationSubnets: + BBBApplicationSubnets: Description: Comma separated list of the public subnets Type: CommaDelimitedList - BBBPrivateApplicationSubnets: - Description: Comma separated list of the private EC2 instance subnets - Type: CommaDelimitedList BBBNotificationTopic: Description: Topic to be used for alarm notifications Type: String - BBBVPCs: + BBBVPC: Description: Reference for the VPC Type: String BBBRDSDBConnectionSecret: @@ -168,6 +165,12 @@ Parameters: AllowedValues: - Greenlight - External + BBBUsePublicApplicationIP: + Type: String + AllowedValues: + - ENABLED + - DISABLED + Default: ENABLED Conditions: BBBSESRegionSet: !Not [ !Equals [ !Ref BBBSesRegion, "AWS::NoValue" ] ] @@ -454,7 +457,7 @@ Resources: LoadBalancerAttributes: - Key: idle_timeout.timeout_seconds Value: "30" - Subnets: !Ref BBBPublicApplicationSubnets + Subnets: !Ref BBBApplicationSubnets SecurityGroups: - !Ref BBBFrontendELBSecurityGroup @@ -481,11 +484,13 @@ Resources: HealthCheckProtocol: HTTP HealthCheckTimeoutSeconds: 60 HealthyThresholdCount: 3 + Matcher: + HttpCode: "200,301" Port: !If [ BBBECSFargate, 3000, 80 ] TargetType: !If [ BBBECSFargate, ip, instance ] Protocol: HTTP UnhealthyThresholdCount: 3 - VpcId: !Ref BBBVPCs + VpcId: !Ref BBBVPC BBBFrontendALBListenerHTTP2HTTPS: Type: AWS::ElasticLoadBalancingV2::Listener @@ -536,7 +541,7 @@ Resources: LoadBalancerAttributes: - Key: idle_timeout.timeout_seconds Value: "30" - Subnets: !Ref BBBPublicApplicationSubnets + Subnets: !Ref BBBApplicationSubnets SecurityGroups: - !Ref BBBScaleliteELBSecurityGroup @@ -577,7 +582,7 @@ Resources: TargetType: !If [ BBBECSFargate, ip, instance ] Protocol: HTTP UnhealthyThresholdCount: 2 - VpcId: !Ref BBBVPCs + VpcId: !Ref BBBVPC BBBgreenlightTaskdefinition: Type: AWS::ECS::TaskDefinition @@ -614,7 +619,7 @@ Resources: awslogs-stream-prefix: greenlight PortMappings: - ContainerPort: 3000 - Secrets: + Secrets: - Name: SECRET_KEY_BASE ValueFrom: !Sub "${BBBApplicationBaseSecret}:basekeyvalue::" - Name: BIGBLUEBUTTON_SECRET @@ -661,6 +666,8 @@ Resources: - "redis://${BBBCacheDBAddress}:${BBBCacheDBPort}" - BBBCacheDBAddress: !Ref BBBCacheDBAddress BBBCacheDBPort: !Ref BBBCacheDBPort + - Name: LOGLEVEL + Value: DEBUG BBBgreenlightService: Type: AWS::ECS::Service @@ -676,10 +683,10 @@ Resources: !If - BBBECSFargate - AwsvpcConfiguration: - AssignPublicIp: DISABLED + AssignPublicIp: !Ref BBBUsePublicApplicationIP SecurityGroups: - !Ref BBBECSTaskSecurityGroup - Subnets: !Ref BBBPrivateApplicationSubnets + Subnets: !Ref BBBApplicationSubnets - !Ref "AWS::NoValue" LoadBalancers: - ContainerName: greenlight @@ -979,10 +986,10 @@ Resources: !If - BBBECSFargate - AwsvpcConfiguration: - AssignPublicIp: DISABLED + AssignPublicIp: !Ref BBBUsePublicApplicationIP SecurityGroups: - !Ref BBBECSTaskSecurityGroup - Subnets: !Ref BBBPrivateApplicationSubnets + Subnets: !Ref BBBApplicationSubnets - !Ref "AWS::NoValue" LoadBalancers: - ContainerName: scalelite-nginx diff --git a/templates/bbb-on-aws-network.template.yaml b/templates/bbb-on-aws-network.template.yaml index e4d2405..1fe7aa6 100644 --- a/templates/bbb-on-aws-network.template.yaml +++ b/templates/bbb-on-aws-network.template.yaml @@ -1,78 +1,30 @@ ---- AWSTemplateFormatVersion: '2010-09-09' Description: > - This Cloudformation Template deploys the network for the application infrastructure. - Disclaimer: Not for production use. Demo and testing purposes only. - Author: David Surey - Parameters: - BBBVPCs: - Description: Please enter the IP range (CIDR notation) or ID for the BBB VPC + BBBVPCDEF: + Description: IP range (CIDR notation) for the VPC Type: String + Default: 10.1.0.0/16 + 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]))$ + BBBNumberOfAZs: Description: Amount of Availability Zones to utilize Type: Number - AllowedValues: - - 1 - - 2 - - 3 + AllowedValues: [1, 2, 3] Default: 3 - BBBPublicApplicationSubnets: - Description: Comma separated list of the appserver's subnets - Type: CommaDelimitedList - BBBPrivateApplicationSubnets: - Description: Comma separated list of the private subnets for instances - Type: CommaDelimitedList - BBBPrivateDBSubnets: - Description: Comma separated list of the private subnets for Ddtabases - Type: CommaDelimitedList - BBBEnvironmentType: - Description: 'Defines the environment type. Allowed values: scalable, single' - AllowedValues: - - scalable - - single - Type: String - BBBEnvironmentStage: - Type: String - Description: Select the appropriate environment - AllowedValues: - - stage - - prod - - dev Conditions: - BBBScalableEnvironment: !Equals [!Ref BBBEnvironmentType, scalable] - BBBScalableandProdEnvironment: !And [!Equals [!Ref BBBEnvironmentType, scalable], !Equals [!Ref BBBEnvironmentStage, prod]] - BBBScalableandNonProdEnvironment: !And [!Equals [!Ref BBBEnvironmentType, scalable], !Not [!Equals [!Ref BBBEnvironmentStage, prod]]] - BBBBuild2AZs: !Not [ !Equals [ !Ref BBBNumberOfAZs, 1 ]] - BBBBuild3AZs: !Equals [ !Ref BBBNumberOfAZs, 3 ] - BBBScalableBuild2AZs: !And - - Condition: BBBScalableEnvironment - - Condition: BBBBuild2AZs - BBBScalableBuild3AZs: !And - - Condition: BBBScalableEnvironment - - Condition: BBBBuild3AZs - BBBScalableandNonProdEnvironmentBuild2AZs: !And - - Condition: BBBScalableBuild2AZs - - !Not [Condition: BBBScalableandProdEnvironment] - BBBScalableandNonProdEnvironmentBuild3AZs: !And - - Condition: BBBScalableBuild3AZs - - !Not [Condition: BBBScalableandProdEnvironment] - BBBScalableandProdEnvironmentBuild2AZs: !And - - Condition: BBBScalableBuild2AZs - - Condition: BBBScalableandProdEnvironment - BBBScalableandProdEnvironmentBuild3AZs: !And - - Condition: BBBScalableBuild3AZs - - Condition: BBBScalableandProdEnvironment + BBBBuild2AZs: !Not [!Equals [!Ref BBBNumberOfAZs, 1]] + BBBBuild3AZs: !Equals [!Ref BBBNumberOfAZs, 3] Resources: BBBVPC: Type: AWS::EC2::VPC Properties: - CidrBlock: !Ref BBBVPCs + CidrBlock: !Ref BBBVPCDEF EnableDnsSupport: true EnableDnsHostnames: true @@ -85,343 +37,103 @@ Resources: InternetGatewayId: !Ref BBBIGW VpcId: !Ref BBBVPC - BBBPublicApplicationSubnet1: + # Dynamic Application Subnets + BBBApplicationSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref BBBVPC - AvailabilityZone: !Select [ 0, !GetAZs '' ] + AvailabilityZone: !Select [0, !GetAZs ''] MapPublicIpOnLaunch: true - CidrBlock: - Fn::Select: - - '0' - - Ref: BBBPublicApplicationSubnets + CidrBlock: !Select [0, !Cidr [!Ref BBBVPCDEF, 9, 8]] - BBBPublicApplicationSubnet2: + BBBApplicationSubnet2: Type: AWS::EC2::Subnet Condition: BBBBuild2AZs Properties: VpcId: !Ref BBBVPC - AvailabilityZone: !Select [ 1, !GetAZs '' ] + AvailabilityZone: !Select [1, !GetAZs ''] MapPublicIpOnLaunch: true - CidrBlock: - Fn::Select: - - '1' - - Ref: BBBPublicApplicationSubnets + CidrBlock: !Select [1, !Cidr [!Ref BBBVPCDEF, 9, 8]] - BBBPublicApplicationSubnet3: + BBBApplicationSubnet3: Type: AWS::EC2::Subnet Condition: BBBBuild3AZs Properties: VpcId: !Ref BBBVPC - AvailabilityZone: !Select [ 2, !GetAZs '' ] + AvailabilityZone: !Select [2, !GetAZs ''] MapPublicIpOnLaunch: true - CidrBlock: - Fn::Select: - - '2' - - Ref: BBBPublicApplicationSubnets - - BBBPrivateApplicationSubnet1: - Type: AWS::EC2::Subnet - Condition: BBBScalableEnvironment - Properties: - VpcId: !Ref BBBVPC - AvailabilityZone: !Select [ 0, !GetAZs '' ] - CidrBlock: - Fn::Select: - - '0' - - Ref: BBBPrivateApplicationSubnets - MapPublicIpOnLaunch: false - Tags: - - Key: Name - Value: !Sub ${AWS::Region} Private Subnet (AZ1) - - BBBPrivateApplicationSubnet2: - Type: AWS::EC2::Subnet - Condition: BBBScalableBuild2AZs - Properties: - VpcId: !Ref BBBVPC - AvailabilityZone: !Select [ 1, !GetAZs '' ] - CidrBlock: - Fn::Select: - - '1' - - Ref: BBBPrivateApplicationSubnets - MapPublicIpOnLaunch: false - Tags: - - Key: Name - Value: !Sub ${AWS::Region} Private Subnet (AZ2) + CidrBlock: !Select [2, !Cidr [!Ref BBBVPCDEF, 9, 8]] - BBBPrivateApplicationSubnet3: + # Dynamic Private Datastore Subnets + BBBDatastoreSubnet1: Type: AWS::EC2::Subnet - Condition: BBBScalableBuild3AZs Properties: VpcId: !Ref BBBVPC - AvailabilityZone: !Select [ 2, !GetAZs '' ] - CidrBlock: - Fn::Select: - - '2' - - Ref: BBBPrivateApplicationSubnets + AvailabilityZone: !Select [0, !GetAZs ''] MapPublicIpOnLaunch: false + CidrBlock: !Select [6, !Cidr [!Ref BBBVPCDEF, 9, 8]] - BBBPrivateDBSubnet1: + BBBDatastoreSubnet2: Type: AWS::EC2::Subnet - Condition: BBBScalableEnvironment - Properties: - VpcId: !Ref BBBVPC - AvailabilityZone: !Select [ 0, !GetAZs '' ] - CidrBlock: - Fn::Select: - - '0' - - Ref: BBBPrivateDBSubnets - MapPublicIpOnLaunch: false - - BBBPrivateDBSubnet2: - Type: AWS::EC2::Subnet - Condition: BBBScalableBuild2AZs + Condition: BBBBuild2AZs Properties: VpcId: !Ref BBBVPC - AvailabilityZone: !Select [ 1, !GetAZs '' ] - CidrBlock: - Fn::Select: - - '1' - - Ref: BBBPrivateDBSubnets + AvailabilityZone: !Select [1, !GetAZs ''] MapPublicIpOnLaunch: false + CidrBlock: !Select [7, !Cidr [!Ref BBBVPCDEF, 9, 8]] - BBBPrivateDBSubnet3: + BBBDatastoreSubnet3: Type: AWS::EC2::Subnet - Condition: BBBScalableBuild3AZs + Condition: BBBBuild3AZs Properties: VpcId: !Ref BBBVPC - AvailabilityZone: !Select [ 2, !GetAZs '' ] - CidrBlock: - Fn::Select: - - '2' - - Ref: BBBPrivateDBSubnets + AvailabilityZone: !Select [2, !GetAZs ''] MapPublicIpOnLaunch: false + CidrBlock: !Select [8, !Cidr [!Ref BBBVPCDEF, 9, 8]] - BBBNatGateway1EIP: - Type: AWS::EC2::EIP - Condition: BBBScalableEnvironment - DependsOn: BBBIGWAttachment - Properties: - Domain: vpc - - BBBNatGateway2EIP: - Type: AWS::EC2::EIP - Condition: BBBScalableandProdEnvironmentBuild2AZs - DependsOn: BBBIGWAttachment - Properties: - Domain: vpc - - BBBNatGateway1: - Type: AWS::EC2::NatGateway - Condition: BBBScalableEnvironment - Properties: - AllocationId: !GetAtt BBBNatGateway1EIP.AllocationId - SubnetId: !Ref BBBPublicApplicationSubnet1 - - BBBNatGateway2: - Type: AWS::EC2::NatGateway - Condition: BBBScalableandProdEnvironmentBuild2AZs - Properties: - AllocationId: !GetAtt BBBNatGateway2EIP.AllocationId - SubnetId: !Ref BBBPublicApplicationSubnet2 - - BBBPublicRouteTable: + # Route Tables + BBBRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref BBBVPC - BBBDefaultPublicRoute: + BBBDefaultRoute: Type: AWS::EC2::Route DependsOn: BBBIGWAttachment Properties: - RouteTableId: !Ref BBBPublicRouteTable + RouteTableId: !Ref BBBRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref BBBIGW - BBBPublicApplicationSubnet1RouteTableAssociation: + BBBApplicationSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: - RouteTableId: !Ref BBBPublicRouteTable - SubnetId: !Ref BBBPublicApplicationSubnet1 + RouteTableId: !Ref BBBRouteTable + SubnetId: !Ref BBBApplicationSubnet1 - BBBPublicApplicationSubnet2RouteTableAssociation: + BBBApplicationSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Condition: BBBBuild2AZs Properties: - RouteTableId: !Ref BBBPublicRouteTable - SubnetId: !Ref BBBPublicApplicationSubnet2 + RouteTableId: !Ref BBBRouteTable + SubnetId: !Ref BBBApplicationSubnet2 - BBBPublicApplicationSubnet3RouteTableAssociation: + BBBApplicationSubnet3RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Condition: BBBBuild3AZs Properties: - RouteTableId: !Ref BBBPublicRouteTable - SubnetId: !Ref BBBPublicApplicationSubnet3 - - BBBPrivateRouteTable1: - Type: AWS::EC2::RouteTable - Condition: BBBScalableEnvironment - Properties: - VpcId: !Ref BBBVPC - Tags: - - Key: Name - Value: !Sub ${AWS::Region} Private Routes (AZ1) - - BBBDefaultPrivateRoute1: - Type: AWS::EC2::Route - Condition: BBBScalableEnvironment - Properties: - RouteTableId: !Ref BBBPrivateRouteTable1 - DestinationCidrBlock: 0.0.0.0/0 - NatGatewayId: !Ref BBBNatGateway1 - - BBBPrivateApplicationSubnet1RouteTableAssociation: - Type: AWS::EC2::SubnetRouteTableAssociation - Condition: BBBScalableEnvironment - Properties: - RouteTableId: !Ref BBBPrivateRouteTable1 - SubnetId: !Ref BBBPrivateApplicationSubnet1 - - BBBPrivateRouteTable2: - Type: AWS::EC2::RouteTable - Condition: BBBScalableBuild2AZs - Properties: - VpcId: !Ref BBBVPC - - BBBDefaultPrivateRoute2Prod: - Type: AWS::EC2::Route - Condition: BBBScalableandProdEnvironmentBuild2AZs - Properties: - RouteTableId: !Ref BBBPrivateRouteTable2 - DestinationCidrBlock: 0.0.0.0/0 - NatGatewayId: !Ref BBBNatGateway2 - - BBBDefaultPrivateRoute2: - Type: AWS::EC2::Route - Condition: BBBScalableandNonProdEnvironmentBuild2AZs - Properties: - RouteTableId: !Ref BBBPrivateRouteTable2 - DestinationCidrBlock: 0.0.0.0/0 - NatGatewayId: !Ref BBBNatGateway1 - - BBBPrivateApplicationSubnet2RouteTableAssociation: - Type: AWS::EC2::SubnetRouteTableAssociation - Condition: BBBScalableBuild2AZs - Properties: - RouteTableId: !Ref BBBPrivateRouteTable2 - SubnetId: !Ref BBBPrivateApplicationSubnet2 - - BBBPrivateRouteTable3: - Type: AWS::EC2::RouteTable - Condition: BBBScalableBuild3AZs - Properties: - VpcId: !Ref BBBVPC - - BBBDefaultPrivateRoute3Prod: - Type: AWS::EC2::Route - Condition: BBBScalableandProdEnvironmentBuild3AZs - Properties: - RouteTableId: !Ref BBBPrivateRouteTable3 - DestinationCidrBlock: 0.0.0.0/0 - NatGatewayId: !Ref BBBNatGateway2 - - BBBDefaultPrivateRoute3: - Type: AWS::EC2::Route - Condition: BBBScalableandNonProdEnvironmentBuild3AZs - Properties: - RouteTableId: !Ref BBBPrivateRouteTable3 - DestinationCidrBlock: 0.0.0.0/0 - NatGatewayId: !Ref BBBNatGateway1 - - BBBPrivateApplicationSubnet3RouteTableAssociation: - Type: AWS::EC2::SubnetRouteTableAssociation - Condition: BBBScalableBuild3AZs - Properties: - RouteTableId: !Ref BBBPrivateRouteTable3 - SubnetId: !Ref BBBPrivateApplicationSubnet3 - - BBBPrivateDBSubnet1RouteTableAssociation: - Type: AWS::EC2::SubnetRouteTableAssociation - Condition: BBBScalableEnvironment - Properties: - RouteTableId: !Ref BBBPrivateRouteTable1 - SubnetId: !Ref BBBPrivateDBSubnet1 - - BBBPrivateDBRouteTable2: - Type: AWS::EC2::RouteTable - Condition: BBBScalableBuild2AZs - Properties: - VpcId: !Ref BBBVPC - - DefaultPrivateDBRoute2Prod: - Type: AWS::EC2::Route - Condition: BBBScalableandProdEnvironmentBuild2AZs - Properties: - RouteTableId: !Ref BBBPrivateDBRouteTable2 - DestinationCidrBlock: 0.0.0.0/0 - NatGatewayId: !Ref BBBNatGateway2 - - DefaultPrivateDBRoute2: - Type: AWS::EC2::Route - Condition: BBBScalableandNonProdEnvironmentBuild2AZs - Properties: - RouteTableId: !Ref BBBPrivateDBRouteTable2 - DestinationCidrBlock: 0.0.0.0/0 - NatGatewayId: !Ref BBBNatGateway1 - - BBBPrivateDBSubnet2RouteTableAssociation: - Type: AWS::EC2::SubnetRouteTableAssociation - Condition: BBBScalableBuild2AZs - Properties: - RouteTableId: !Ref BBBPrivateRouteTable2 - SubnetId: !Ref BBBPrivateDBSubnet2 - - BBBPrivateDBRouteTable3: - Type: AWS::EC2::RouteTable - Condition: BBBScalableBuild3AZs - Properties: - VpcId: !Ref BBBVPC - - BBBDefaultPrivateDBRoute3Prod: - Type: AWS::EC2::Route - Condition: BBBScalableandProdEnvironmentBuild3AZs - Properties: - RouteTableId: !Ref BBBPrivateDBRouteTable3 - DestinationCidrBlock: 0.0.0.0/0 - NatGatewayId: !Ref BBBNatGateway2 - - BBBDefaultPrivateDBRoute3: - Type: AWS::EC2::Route - Condition: BBBScalableandNonProdEnvironmentBuild3AZs - Properties: - RouteTableId: !Ref BBBPrivateDBRouteTable3 - DestinationCidrBlock: 0.0.0.0/0 - NatGatewayId: !Ref BBBNatGateway1 - - - BBBPrivateDBSubnet3RouteTableAssociation: - Type: AWS::EC2::SubnetRouteTableAssociation - Condition: BBBScalableBuild3AZs - Properties: - RouteTableId: !Ref BBBPrivateRouteTable3 - SubnetId: !Ref BBBPrivateDBSubnet3 + RouteTableId: !Ref BBBRouteTable + SubnetId: !Ref BBBApplicationSubnet3 Outputs: - BBBVPCs: - Description: A reference to the created VPC + BBBVPC: + Description: VPC ID Value: !Ref BBBVPC - BBBPublicApplicationSubnets: - Description: A list of the public Application subnets - Value: !Join [ ",", [ !Ref BBBPublicApplicationSubnet1, !If [ BBBBuild2AZs, !Ref BBBPublicApplicationSubnet2, !Ref "AWS::NoValue"], !If [ BBBBuild3AZs, !Ref BBBPublicApplicationSubnet3, !Ref "AWS::NoValue"]]] - - BBBPrivateApplicationSubnets: - Description: A list of the private Instance subnets - Condition: BBBScalableEnvironment - Value: !Join [ ",", [ !Ref BBBPrivateApplicationSubnet1, !If [ BBBBuild2AZs, !Ref BBBPrivateApplicationSubnet2, !Ref "AWS::NoValue"], !If [ BBBBuild3AZs, !Ref BBBPrivateApplicationSubnet3, !Ref "AWS::NoValue"]]] + BBBApplicationSubnets: + Description: A list of the application subnets + Value: !Join [ ",", [ !Ref BBBApplicationSubnet1, !If [ BBBBuild2AZs, !Ref BBBApplicationSubnet2, !Ref "AWS::NoValue"], !If [ BBBBuild3AZs, !Ref BBBApplicationSubnet3, !Ref "AWS::NoValue"]]] - BBBPrivateDBSubnets: - Description: A list of the private database subnets - Condition: BBBScalableEnvironment - Value: !Join [ ",", [ !Ref BBBPrivateDBSubnet1, !If [ BBBBuild2AZs, !Ref BBBPrivateDBSubnet2, !Ref "AWS::NoValue"], !If [ BBBBuild3AZs, !Ref BBBPrivateDBSubnet3, !Ref "AWS::NoValue"]]] \ No newline at end of file + BBBDatastoreSubnets: + Description: A list of the database subnets + Value: !Join [ ",", [ !Ref BBBDatastoreSubnet1, !If [ BBBBuild2AZs, !Ref BBBDatastoreSubnet2, !Ref "AWS::NoValue"], !If [ BBBBuild3AZs, !Ref BBBDatastoreSubnet3, !Ref "AWS::NoValue"]]] \ No newline at end of file diff --git a/templates/bbb-on-aws-securitygroups.template.yaml b/templates/bbb-on-aws-securitygroups.template.yaml index 28eacf2..c2ac81e 100644 --- a/templates/bbb-on-aws-securitygroups.template.yaml +++ b/templates/bbb-on-aws-securitygroups.template.yaml @@ -9,15 +9,9 @@ Description: > Author: David Surey Parameters: - BBBVPCs: + BBBVPC: Description: Reference for the VPC Type: String - BBBEnvironmentType: - Description: 'Defines the environment type. Allowed values: scalable, single' - AllowedValues: - - scalable - - single - Type: String BBBECSInstanceType: Description: Set the ECS Cluster Type to either EC2 based or Fargate based deployments Type: String @@ -29,23 +23,19 @@ Parameters: - External Conditions: - BBBScalableEnvironment: !Equals [!Ref BBBEnvironmentType, scalable] BBBECSFargate: !Equals [!Ref BBBECSInstanceType, fargate] BBBECSEC2: !Not [!Equals [!Ref BBBECSInstanceType, fargate]] BBBGreenlight: !Equals [!Ref BBBFrontendType, Greenlight] - BBBScalableGreenlight: !And [!Equals [!Ref BBBFrontendType, Greenlight], !Equals [!Ref BBBEnvironmentType, scalable]] Resources: BBBECSTaskSecurityGroup: Type: AWS::EC2::SecurityGroup - Condition: BBBScalableEnvironment Properties: GroupDescription: ECS Instance Security Group - VpcId: !Ref BBBVPCs + VpcId: !Ref BBBVPC BBBECSTaskSecurityGroupPorts: Type: AWS::EC2::SecurityGroupIngress - Condition: BBBScalableEnvironment Properties: GroupId: !Ref BBBECSTaskSecurityGroup IpProtocol: tcp @@ -55,14 +45,12 @@ Resources: BBBScaleliteELBSecurityGroup: Type: AWS::EC2::SecurityGroup - Condition: BBBScalableEnvironment Properties: GroupDescription: Scalelite Security Group - VpcId: !Ref BBBVPCs + VpcId: !Ref BBBVPC BBBScaleliteELBSecurityGroupPorts: Type: AWS::EC2::SecurityGroupIngress - Condition: BBBScalableEnvironment Properties: GroupId: !Ref BBBScaleliteELBSecurityGroup IpProtocol: tcp @@ -72,14 +60,12 @@ Resources: BBBFrontendELBSecurityGroup: Type: AWS::EC2::SecurityGroup - Condition: BBBScalableGreenlight Properties: GroupDescription: ALB Security Group - VpcId: !Ref BBBVPCs + VpcId: !Ref BBBVPC BBBECSSecurityGroupPublicports: Type: AWS::EC2::SecurityGroupIngress - Condition: BBBScalableGreenlight Properties: CidrIp: 0.0.0.0/0 IpProtocol: tcp @@ -89,7 +75,6 @@ Resources: BBBECSSecurityGroupPublicHTTP: Type: AWS::EC2::SecurityGroupIngress - Condition: BBBScalableGreenlight Properties: CidrIp: 0.0.0.0/0 IpProtocol: tcp @@ -99,7 +84,6 @@ Resources: BBBFrontendSecurityGroupALBports: Type: AWS::EC2::SecurityGroupIngress - Condition: BBBScalableGreenlight Properties: GroupId: !Ref BBBECSTaskSecurityGroup IpProtocol: tcp @@ -109,15 +93,13 @@ Resources: BBBDBSecurityGroup: Type: AWS::EC2::SecurityGroup - Condition: BBBScalableEnvironment Properties: VpcId: - Ref: BBBVPCs + Ref: BBBVPC GroupDescription: Security group for the Postgres DB BBBDBSecurityGroupPorts: Type: AWS::EC2::SecurityGroupIngress - Condition: BBBScalableEnvironment Properties: SourceSecurityGroupId: !Ref BBBECSTaskSecurityGroup IpProtocol: tcp @@ -127,15 +109,13 @@ Resources: BBBCACHEDBSecurityGroup: Type: AWS::EC2::SecurityGroup - Condition: BBBScalableEnvironment Properties: VpcId: - Ref: BBBVPCs + Ref: BBBVPC GroupDescription: Security group for the Redis Cache BBBCACHEDBSecurityGroupPorts: Type: AWS::EC2::SecurityGroupIngress - Condition: BBBScalableEnvironment Properties: SourceSecurityGroupId: !Ref BBBECSTaskSecurityGroup IpProtocol: tcp @@ -147,7 +127,7 @@ Resources: Type: AWS::EC2::SecurityGroup Properties: VpcId: - Ref: BBBVPCs + Ref: BBBVPC GroupDescription: Security group for the BigBlueButton Application Host BBBApplicationSecurityGroupWebSSLPort: @@ -206,15 +186,13 @@ Resources: BBBSharedStorageSecurityGroup: Type: AWS::EC2::SecurityGroup - Condition: BBBScalableEnvironment Properties: VpcId: - Ref: BBBVPCs + Ref: BBBVPC GroupDescription: Security group for the Shared Storage BBBSharedStorageSecurityGroupApplicationPort: Type: AWS::EC2::SecurityGroupIngress - Condition: BBBScalableEnvironment Properties: SourceSecurityGroupId: !Ref BBBApplicationSecurityGroup IpProtocol: tcp @@ -224,7 +202,6 @@ Resources: BBBSharedStorageSecurityGroupECSPort: Type: AWS::EC2::SecurityGroupIngress - Condition: BBBScalableEnvironment Properties: SourceSecurityGroupId: !Ref BBBECSTaskSecurityGroup IpProtocol: tcp @@ -234,29 +211,23 @@ Resources: Outputs: BBBECSTaskSecurityGroup: - Condition: BBBScalableEnvironment Description: A reference to the created Security Group for ECS Value: !Ref BBBECSTaskSecurityGroup BBBFrontendELBSecurityGroup: - Condition: BBBScalableGreenlight Description: A reference to the created Security Group for ELB Value: !Ref BBBFrontendELBSecurityGroup BBBScaleliteELBSecurityGroup: - Condition: BBBScalableEnvironment Description: A reference to the created Security Group for the Scalelite Load Balancer Value: !Ref BBBScaleliteELBSecurityGroup BBBDBSecurityGroup: - Condition: BBBScalableEnvironment Description: A reference to the created Security Group for the Database Value: !Ref BBBDBSecurityGroup BBBCACHEDBSecurityGroup: - Condition: BBBScalableEnvironment Description: A reference to the created Security Group for the Redis Cache Value: !Ref BBBCACHEDBSecurityGroup BBBApplicationSecurityGroup: Description: A reference to the created Security Group for the Public Ports of the Application Value: !Ref BBBApplicationSecurityGroup BBBSharedStorageSecurityGroup: - Condition: BBBScalableEnvironment Description: A reference to the created Security Group for the SharedStorage Value: !Ref BBBSharedStorageSecurityGroup diff --git a/templates/bbb-on-aws-storage.template.yaml b/templates/bbb-on-aws-storage.template.yaml index 14cf867..4e2d066 100644 --- a/templates/bbb-on-aws-storage.template.yaml +++ b/templates/bbb-on-aws-storage.template.yaml @@ -12,7 +12,7 @@ Parameters: BBBSharedStorageSecurityGroup: Type: String Description: Security Group that should be assigned to the shared storage - BBBPrivateApplicationSubnets: + BBBDatastoreSubnets: Type: CommaDelimitedList Description: Comma separated list of the private instance subnets BBBNumberOfAZs: @@ -60,7 +60,7 @@ Resources: Properties: FileSystemId: Ref: BBBSharedStorageFS - SubnetId: !Select [0, !Ref BBBPrivateApplicationSubnets] + SubnetId: !Select [0, !Ref BBBDatastoreSubnets] SecurityGroups: - !Ref BBBSharedStorageSecurityGroup @@ -70,7 +70,7 @@ Resources: Properties: FileSystemId: Ref: BBBSharedStorageFS - SubnetId: !Select [1, !Ref BBBPrivateApplicationSubnets] + SubnetId: !Select [1, !Ref BBBDatastoreSubnets] SecurityGroups: - !Ref BBBSharedStorageSecurityGroup @@ -80,7 +80,7 @@ Resources: Properties: FileSystemId: Ref: BBBSharedStorageFS - SubnetId: !Select [2, !Ref BBBPrivateApplicationSubnets] + SubnetId: !Select [2, !Ref BBBDatastoreSubnets] SecurityGroups: - !Ref BBBSharedStorageSecurityGroup diff --git a/validate.sh b/validate.sh deleted file mode 100755 index 3291b68..0000000 --- a/validate.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash -# This is a simple bash script for the BBB Application Infrastructure. -# It simply validates the Amazon Cloudformation templates. - -# created by David Surey - suredavi@amazon.de -# Disclaimber: NOT FOR PRODUCTION USE - Only for demo and testing purposes -ERROR_COUNT=0; - -if [[ $# -lt 1 ]] ; then - echo 'arguments missing, please provide a aws profile string (-p)' - exit 1 -fi - -while getopts ":p:e:s" opt; do - case $opt in - p) BBBPROFILE="$OPTARG" - ;; - \?) echo "Invalid option -$OPTARG" >&2 - ;; - esac -done - -echo "using AWS Profile $BBBPROFILE" -echo "##################################################" - -echo "Validating AWS CloudFormation templates..." -echo "##################################################" -# Loop through the YAML templates in this repository -for TEMPLATE in $(find . -name 'bbb-on-aws-*.template.yaml'); do - - # Validate the template with CloudFormation - ERRORS=$(aws cloudformation validate-template --profile=$BBBPROFILE --template-body file://$TEMPLATE 2>&1 >/dev/null); - if [ "$?" -gt "1" ]; then - ((ERROR_COUNT++)); - echo "[fail] $TEMPLATE: $ERRORS"; - else - echo "[pass] $TEMPLATE"; - fi; - -done; - -# Error out if templates are not validate. -echo "$ERROR_COUNT template validation error(s)"; -if [ "$ERROR_COUNT" -gt 1 ]; - then exit 1; -fi - -echo "##################################################" -echo "Validating of AWS CloudFormation templates finished" -echo "##################################################" - -exit 0 \ No newline at end of file