├── demo-aws ├── .gitignore ├── output.tf ├── input.tf ├── providers.tf ├── aws │ ├── modules │ │ ├── langflow │ │ │ ├── terraform.tf │ │ │ ├── variables.tf │ │ │ └── main.tf │ │ ├── astra_db │ │ │ ├── terraform.tf │ │ │ ├── variables.tf │ │ │ └── main.tf │ │ ├── aws_infra │ │ │ ├── terraform.tf │ │ │ ├── outputs.tf │ │ │ ├── variables.tf │ │ │ └── main.tf │ │ ├── ecs_deployment │ │ │ ├── terraform.tf │ │ │ ├── variables.tf │ │ │ └── main.tf │ │ ├── chat_ui │ │ │ ├── outputs.tf │ │ │ ├── variables.tf │ │ │ ├── chat_ui_user_data.sh │ │ │ └── main.tf │ │ └── assistants │ │ │ ├── terraform.tf │ │ │ ├── variables.tf │ │ │ └── main.tf │ ├── terraform.tf │ ├── output.tf │ ├── preconditions.tf │ ├── .gitignore │ ├── main.tf │ ├── .terraform.lock.hcl │ ├── variables.tf │ └── README.md ├── terraform.tf ├── main.tf └── .terraform.lock.hcl ├── .DS_Store ├── imgs ├── aws-01.png ├── aws-02.png ├── aws-03.png ├── 01-login.png └── 02-token.png └── README.MD /demo-aws/.gitignore: -------------------------------------------------------------------------------- 1 | #Remove terraform 2 | .terraform 3 | 4 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/terraform-tryout/main/.DS_Store -------------------------------------------------------------------------------- /imgs/aws-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/terraform-tryout/main/imgs/aws-01.png -------------------------------------------------------------------------------- /imgs/aws-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/terraform-tryout/main/imgs/aws-02.png -------------------------------------------------------------------------------- /imgs/aws-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/terraform-tryout/main/imgs/aws-03.png -------------------------------------------------------------------------------- /imgs/01-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/terraform-tryout/main/imgs/01-login.png -------------------------------------------------------------------------------- /imgs/02-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/terraform-tryout/main/imgs/02-token.png -------------------------------------------------------------------------------- /demo-aws/output.tf: -------------------------------------------------------------------------------- 1 | output "datastax-ai-stack-aws" { 2 | value = module.datastax-ai-stack-aws 3 | sensitive = true 4 | } 5 | -------------------------------------------------------------------------------- /demo-aws/input.tf: -------------------------------------------------------------------------------- 1 | variable "astra_token" { 2 | type = string 3 | } 4 | 5 | variable "aws_profile" { 6 | type = string 7 | } 8 | -------------------------------------------------------------------------------- /demo-aws/providers.tf: -------------------------------------------------------------------------------- 1 | provider "astra" { 2 | token = var.astra_token 3 | } 4 | 5 | provider "aws" { 6 | region = "us-west-2" 7 | profile = var.aws_profile 8 | } 9 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/langflow/terraform.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "~> 5.47.0" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/astra_db/terraform.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | astra = { 4 | source = "datastax/astra" 5 | version = "~> 2.3.3" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/aws_infra/terraform.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "~> 5.47.0" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/ecs_deployment/terraform.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "~> 5.47.0" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/chat_ui/outputs.tf: -------------------------------------------------------------------------------- 1 | output "chat_ui_user_data_script" { 2 | description = "The user_data script to be used for initializing the ChatUI application on an existing VM." 3 | value = local.chat_ui_user_data_script 4 | } 5 | -------------------------------------------------------------------------------- /demo-aws/aws/terraform.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | astra = { 4 | source = "datastax/astra" 5 | version = "~> 2.3.3" 6 | } 7 | aws = { 8 | source = "hashicorp/aws" 9 | version = "~> 5.47.0" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /demo-aws/terraform.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | astra = { 4 | source = "datastax/astra" 5 | version = "~> 2.3.3" 6 | } 7 | aws = { 8 | source = "hashicorp/aws" 9 | version = "~> 5.47.0" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/assistants/terraform.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | astra = { 4 | source = "datastax/astra" 5 | version = "~> 2.3.3" 6 | } 7 | aws = { 8 | source = "hashicorp/aws" 9 | version = "~> 5.47.0" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /demo-aws/aws/output.tf: -------------------------------------------------------------------------------- 1 | output "vpc_id" { 2 | value = try(module.aws_infra.vpc_id, null) 3 | } 4 | 5 | output "alb_dns_name" { 6 | value = try(module.aws_infra.alb_dns_name, null) 7 | } 8 | 9 | output "db_ids" { 10 | value = zipmap(concat(module.assistants[*].db_id, values(module.vector_dbs)[*].db_id), concat(module.assistants[*].db_name, values(module.vector_dbs)[*].db_name)) 11 | } 12 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/astra_db/variables.tf: -------------------------------------------------------------------------------- 1 | variable "config" { 2 | type = object({ 3 | name = string 4 | regions = optional(set(string)) 5 | keyspaces = optional(list(string)) 6 | cloud_provider = optional(string) 7 | deletion_protection = optional(bool) 8 | }) 9 | } 10 | 11 | variable "cloud_provider" { 12 | type = string 13 | } 14 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/aws_infra/outputs.tf: -------------------------------------------------------------------------------- 1 | output "vpc_id" { 2 | value = local.vpc_id 3 | } 4 | 5 | output "alb_dns_name" { 6 | value = module.alb.dns_name 7 | } 8 | 9 | output "ecs_cluster_id" { 10 | value = module.ecs.cluster_id 11 | } 12 | 13 | output "target_groups" { 14 | value = module.alb.target_groups 15 | } 16 | 17 | output "security_groups" { 18 | value = [aws_security_group.ecs_cluster_sg.id] 19 | } 20 | 21 | output "private_subnets" { 22 | value = local.private_subnets 23 | } 24 | -------------------------------------------------------------------------------- /demo-aws/main.tf: -------------------------------------------------------------------------------- 1 | module "datastax-ai-stack-aws" { 2 | source = "./aws" 3 | 4 | domain_config = { 5 | auto_route53_setup = true 6 | hosted_zones = { 7 | default = { zone_name = "enterprise-ai-stack.com" } 8 | } 9 | } 10 | 11 | langflow = { 12 | domain = "langflow.enterprise-ai-stack.com" 13 | } 14 | 15 | assistants = { 16 | domain = "assistants.enterprise-ai-stack.com" 17 | db = { 18 | deletion_protection = false 19 | } 20 | } 21 | 22 | vector_dbs = [{ 23 | name = "terraform_demo" 24 | keyspaces = ["default_keyspace"] 25 | }] 26 | } -------------------------------------------------------------------------------- /demo-aws/aws/modules/langflow/variables.tf: -------------------------------------------------------------------------------- 1 | variable "config" { 2 | type = object({ 3 | version = optional(string) 4 | env = optional(map(string)) 5 | containers = optional(object({ 6 | cpu = optional(number) 7 | memory = optional(number) 8 | min_instances = optional(number) 9 | max_instances = optional(number) 10 | })) 11 | }) 12 | nullable = true 13 | } 14 | 15 | variable "infrastructure" { 16 | type = object({ 17 | cluster = string 18 | security_groups = set(string) 19 | subnets = set(string) 20 | cloud_provider = string 21 | }) 22 | } 23 | 24 | variable "target_group_arn" { 25 | type = string 26 | } 27 | 28 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/langflow/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | container_info = { 3 | name = "langflow" 4 | image = "langflowai/langflow:${coalesce(var.config.version, "latest")}" 5 | port = 7860 6 | health_path = "health" 7 | env = var.config.env 8 | } 9 | } 10 | 11 | output "container_info" { 12 | value = local.container_info 13 | } 14 | 15 | output "target_id" { 16 | value = module.ecs_deployment.target_id 17 | } 18 | 19 | module "ecs_deployment" { 20 | source = "../ecs_deployment" 21 | infrastructure = var.infrastructure 22 | config = var.config 23 | container_info = local.container_info 24 | target_group_arn = var.target_group_arn 25 | } 26 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/ecs_deployment/variables.tf: -------------------------------------------------------------------------------- 1 | variable "container_info" { 2 | type = object({ 3 | name = string 4 | image = string 5 | port = number 6 | env = map(string) 7 | entrypoint = optional(list(string)) 8 | health_path = string 9 | }) 10 | } 11 | 12 | variable "config" { 13 | type = object({ 14 | containers = optional(object({ 15 | cpu = optional(number) 16 | memory = optional(number) 17 | min_instances = optional(number) 18 | max_instances = optional(number) 19 | })) 20 | }) 21 | } 22 | 23 | variable "infrastructure" { 24 | type = object({ 25 | cluster = string 26 | security_groups = set(string) 27 | subnets = set(string) 28 | }) 29 | } 30 | 31 | variable "target_group_arn" { 32 | type = string 33 | } 34 | -------------------------------------------------------------------------------- /demo-aws/aws/preconditions.tf: -------------------------------------------------------------------------------- 1 | check "check_domain_config_unset_if_necessary" { 2 | assert { 3 | condition = local.components_used || var.domain_config == null 4 | error_message = "If no ECS-deployed components (langflow, assistants) are defined, domain_config won't do anything" 5 | } 6 | } 7 | 8 | locals { 9 | _domain_config_set = var.domain_config == null 10 | 11 | check_domain_config_set_if_necessary = ((local.components_used && local._domain_config_set) 12 | ? tobool("If any ECS-deployed components (langflow, assistants) are defined, domain_config is required to be set") 13 | : true 14 | ) 15 | } 16 | 17 | locals { 18 | components_used = length(local.components) > 0 19 | } 20 | 21 | locals { 22 | aws_infra_checks_pass = alltrue([ 23 | local.check_domain_config_set_if_necessary, 24 | local.components_used, 25 | ]) 26 | } 27 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/assistants/variables.tf: -------------------------------------------------------------------------------- 1 | variable "config" { 2 | type = object({ 3 | version = optional(string) 4 | env = optional(map(string)) 5 | db = optional(object({ 6 | regions = optional(set(string)) 7 | deletion_protection = optional(bool) 8 | cloud_provider = optional(string) 9 | })) 10 | containers = optional(object({ 11 | cpu = optional(number) 12 | memory = optional(number) 13 | min_instances = optional(number) 14 | max_instances = optional(number) 15 | })) 16 | }) 17 | nullable = true 18 | } 19 | 20 | variable "infrastructure" { 21 | type = object({ 22 | cluster = string 23 | security_groups = set(string) 24 | subnets = set(string) 25 | cloud_provider = string 26 | }) 27 | } 28 | 29 | variable "target_group_arn" { 30 | type = string 31 | } 32 | -------------------------------------------------------------------------------- /demo-aws/aws/.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | crash.*.log 11 | 12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 13 | # password, private keys, and other secrets. These should not be part of version 14 | # control as they are data points which are potentially sensitive and subject 15 | # to change depending on the environment. 16 | *.tfvars 17 | *.tfvars.json 18 | 19 | # Ignore override files as they are usually used to override resources locally and so 20 | # are not checked in 21 | override.tf 22 | override.tf.json 23 | *_override.tf 24 | *_override.tf.json 25 | 26 | # Include override files you do wish to add to version control using negated pattern 27 | # !example_override.tf 28 | 29 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 30 | # example: *tfplan* 31 | 32 | # Ignore CLI configuration files 33 | .terraformrc 34 | terraform.rc -------------------------------------------------------------------------------- /demo-aws/aws/modules/aws_infra/variables.tf: -------------------------------------------------------------------------------- 1 | variable "domain_config" { 2 | type = object({ 3 | auto_route53_setup = optional(bool) 4 | hosted_zones = optional(map(object({ 5 | zone_id = optional(string) 6 | zone_name = optional(string) 7 | }))) 8 | acm_cert_arn = optional(string) 9 | }) 10 | } 11 | 12 | variable "alb_config" { 13 | type = object({ 14 | vpc_id = string 15 | public_subnets = list(string) 16 | private_subnets = list(string) 17 | security_groups = list(string) 18 | }) 19 | nullable = true 20 | } 21 | 22 | variable "fargate_config" { 23 | type = object({ 24 | capacity_provider_weights = optional(object({ 25 | default_base = number 26 | default_weight = number 27 | spot_base = number 28 | spot_weight = number 29 | })) 30 | }) 31 | nullable = true 32 | } 33 | 34 | variable "components" { 35 | type = list(object({ 36 | name = string 37 | name_prefix = string 38 | port = number 39 | domain = optional(string) 40 | })) 41 | } 42 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/chat_ui/variables.tf: -------------------------------------------------------------------------------- 1 | variable "chat_ui" { 2 | type = object({ 3 | public_origin = string 4 | task_model = any 5 | models = any 6 | mongodb_url = string 7 | api_keys = object({ 8 | hf_token = optional(string) 9 | openai_api_key = optional(string) 10 | perplexityai_api_key = optional(string) 11 | cohere_api_key = optional(string) 12 | gemini_api_key = optional(string) 13 | }) 14 | vm_config = optional(object({ 15 | instance_type = string 16 | image_id = string 17 | subnet_id = optional(string) 18 | region_or_zone = optional(string) 19 | })) 20 | }) 21 | nullable = true 22 | } 23 | 24 | variable "cloud_provider" { 25 | type = object({ 26 | name = string 27 | ssh = optional(object({ 28 | aws_public_key_name = optional(string) 29 | gcp_user = optional(string) 30 | gcp_pub_key = optional(string) 31 | })) 32 | }) 33 | 34 | validation { 35 | condition = var.cloud_provider.name == "aws" || var.cloud_provider.name == "gcp" 36 | error_message = "provider.name must be either 'aws' or 'gcp'" 37 | } 38 | } 39 | 40 | variable "astra_token" { 41 | type = string 42 | } 43 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/astra_db/main.tf: -------------------------------------------------------------------------------- 1 | data "astra_available_regions" "this" { 2 | region_type = "vector" 3 | cloud_provider = var.cloud_provider 4 | only_enabled = true 5 | } 6 | 7 | locals { 8 | cloud_provider = coalesce(var.config.cloud_provider, var.cloud_provider) 9 | 10 | filtered_regions = [ 11 | for region in data.astra_available_regions.this.results : region.region 12 | if !region.reserved_for_qualified_users 13 | ] 14 | 15 | regions = coalesce(var.config.regions, [local.filtered_regions[0]]) 16 | 17 | keyspaces = coalesce(var.config.keyspaces, []) 18 | 19 | first_keyspace = length(local.keyspaces) > 0 ? local.keyspaces[0] : null 20 | rest_keyspaces = length(local.keyspaces) > 1 ? slice(local.keyspaces, 1, length(local.keyspaces)) : [] 21 | } 22 | 23 | resource "astra_database" "astra_vector_db" { 24 | cloud_provider = local.cloud_provider 25 | keyspace = local.first_keyspace 26 | name = var.config.name 27 | deletion_protection = var.config.deletion_protection 28 | regions = local.regions 29 | db_type = "vector" 30 | } 31 | 32 | resource "astra_keyspace" "astra_keyspaces" { 33 | for_each = toset(local.rest_keyspaces) 34 | database_id = astra_database.astra_vector_db.id 35 | name = each.key 36 | } 37 | 38 | output "db_id" { 39 | value = astra_database.astra_vector_db.id 40 | } 41 | 42 | output "db_name" { 43 | value = var.config.name 44 | } 45 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/assistants/main.tf: -------------------------------------------------------------------------------- 1 | module "assistants_api_db" { 2 | source = "../astra_db" 3 | cloud_provider = var.infrastructure.cloud_provider 4 | 5 | config = { 6 | name = "assistant_api_db" 7 | keyspaces = ["assistant_api"] 8 | regions = try(coalesce(var.config.db.regions), null) 9 | deletion_protection = try(coalesce(var.config.db.deletion_protection), null) 10 | cloud_provider = try(coalesce(var.config.db.cloud_provider), null) 11 | } 12 | } 13 | 14 | locals { 15 | container_info = { 16 | name = "astra-assistants" 17 | image = "datastax/astra-assistants:${coalesce(var.config.version, "latest")}" 18 | port = 8000 19 | entrypoint = ["poetry", "run", "uvicorn", "impl.main:app", "--host", "0.0.0.0", "--port", "8000"] 20 | health_path = "v1/health" 21 | env = var.config.env 22 | } 23 | } 24 | 25 | output "container_info" { 26 | value = local.container_info 27 | } 28 | 29 | output "target_id" { 30 | value = module.ecs_deployment.target_id 31 | } 32 | 33 | output "db_id" { 34 | value = module.assistants_api_db.db_id 35 | } 36 | 37 | output "db_name" { 38 | value = module.assistants_api_db.db_name 39 | } 40 | 41 | module "ecs_deployment" { 42 | source = "../ecs_deployment" 43 | infrastructure = var.infrastructure 44 | config = var.config 45 | container_info = local.container_info 46 | target_group_arn = var.target_group_arn 47 | } 48 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/chat_ui/chat_ui_user_data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo yum update -y 4 | yum install jq -y 5 | echo ${token} 6 | echo "export CLIENT_ID=$CLIENT_ID" >> /home/ec2-user/.bashrc 7 | export MONGODB_URL=${mongodb_url} 8 | echo "export MONGODB_URL=$MONGODB_URL" >> /home/ec2-user/.bashrc 9 | export HF_TOKEN=${hf_token} 10 | echo "export HF_TOKEN=$HF_TOKEN" >> /home/ec2-user/.bashrc 11 | export PUBLIC_ORIGIN=${public_origin} 12 | echo "export PUBLIC_ORIGIN=$PUBLIC_ORIGIN" >> /home/ec2-user/.bashrc 13 | export ENABLE_ASSISTANTS=true 14 | echo "export ENABLE_ASSISTANTS=$ENABLE_ASSISTANTS" >> /home/ec2-user/.bashrc 15 | export USE_LOCAL_WEBSEARCH=true 16 | echo "export USE_LOCAL_WEBSEARCH=$USE_LOCAL_WEBSEARCH" >> /home/ec2-user/.bashrc 17 | export OPENAI_API_KEY=${openai_api_key} 18 | echo "export OPENAI_API_KEY=$OPENAI_API_KEY" >> /home/ec2-user/.bashrc 19 | ASTRA_API_TOKEN=$(token) 20 | export ASTRA_API_TOKEN 21 | echo "export ASTRA_API_TOKEN=$ASTRA_API_TOKEN" >> /home/ec2-user/.bashrc 22 | export PERPLEXITYAI_API_KEY=${perplexityai_api_key} 23 | echo "export PERPLEXITYAI_API_KEY=$PERPLEXITYAI_API_KEY" >> /home/ec2-user/.bashrc 24 | export COHERE_API_KEY=${cohere_api_key} 25 | echo "export COHERE_API_KEY=$COHERE_API_KEY" >> /home/ec2-user/.bashrc 26 | export GEMINI_API_KEY=${gemini_api_key} 27 | echo "export GEMINI_API_KEY=$GEMINI_API_KEY" >> /home/ec2-user/.bashrc 28 | export TASK_MODEL=${task_model} 29 | echo "export TASK_MODEL=$TASK_MODEL" >> /home/ec2-user/.bashrc 30 | export MODELS=${models} 31 | echo "export MODELS=$MODELS" >> /home/ec2-user/.bashrc 32 | 33 | su ec2-user -c 'git clone https://@github.com/datastax/chat-ui.git' 34 | su ec2-user -c 'cd chat-ui' 35 | su ec2-user -c 'git switch assistants' 36 | su ec2-user -c 'npm install' 37 | su ec2-user -c 'npm run build' 38 | su ec2-user curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash 39 | su ec2-user source ~/.bashrc 40 | su ec2-user nvm install --lts 41 | su ec2-user -c 'npm install pm2 -g' 42 | 43 | su ec2-user pm2 start /app/build/index.js -i "$CPU_CORES" --no-daemon 44 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/chat_ui/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | chat_ui_user_data_script = templatefile("${path.module}/chat_ui_user_data.sh", { 3 | token = var.astra_token 4 | mongodb_url = var.chat_ui.mongodb_url 5 | hf_token = var.chat_ui.api_keys.hf_token 6 | openai_api_key = var.chat_ui.api_keys.openai_api_key 7 | perplexityai_api_key = var.chat_ui.api_keys.perplexityai_api_key 8 | cohere_api_key = var.chat_ui.api_keys.cohere_api_key 9 | gemini_api_key = var.chat_ui.api_keys.gemini_api_key 10 | public_origin = var.chat_ui.public_origin 11 | task_model = var.chat_ui.task_model 12 | models = var.chat_ui.models 13 | }) 14 | } 15 | 16 | resource "aws_instance" "chat_ui_vm" { 17 | count = var.cloud_provider.name == "aws" && var.chat_ui.vm_config != null ? 1 : 0 18 | 19 | instance_type = var.chat_ui.vm_config.instance_type 20 | ami = var.chat_ui.vm_config.image_id 21 | subnet_id = var.chat_ui.vm_config.subnet_id 22 | key_name = var.cloud_provider.ssh.aws_public_key_name 23 | availability_zone = var.chat_ui.vm_config.region_or_zone 24 | 25 | tags = { 26 | Name = "chat-ui-instance" 27 | } 28 | 29 | user_data = local.chat_ui_user_data_script 30 | } 31 | 32 | resource "google_compute_instance" "chat_ui_vm" { 33 | count = var.cloud_provider.name == "gcp" && var.chat_ui.vm_config != null ? 1 : 0 34 | 35 | name = "chat-ui-instance" 36 | machine_type = var.chat_ui.vm_config.instance_type 37 | zone = var.chat_ui.vm_config.region_or_zone 38 | 39 | boot_disk { 40 | initialize_params { 41 | image = var.chat_ui.vm_config.image_id 42 | } 43 | } 44 | 45 | network_interface { 46 | network = "default" 47 | subnetwork = var.chat_ui.vm_config.subnet_id 48 | } 49 | 50 | metadata = { 51 | ssh-keys = "${var.cloud_provider.ssh.gcp_user}:${var.cloud_provider.ssh.gcp_pub_key}" 52 | } 53 | 54 | lifecycle { 55 | prevent_destroy = false 56 | } 57 | 58 | metadata_startup_script = local.chat_ui_user_data_script 59 | } 60 | -------------------------------------------------------------------------------- /demo-aws/aws/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | create_assistants = var.assistants != null 3 | create_langflow = var.langflow != null 4 | 5 | infrastructure = { 6 | cluster = try(module.aws_infra[0].ecs_cluster_id, null) 7 | security_groups = try(module.aws_infra[0].security_groups, null) 8 | subnets = try(module.aws_infra[0].private_subnets, null) 9 | cloud_provider = "aws" 10 | } 11 | 12 | components = [ 13 | for each in [ 14 | (local.create_assistants ? { 15 | name = module.assistants[0].container_info.name 16 | port = module.assistants[0].container_info.port 17 | domain = var.assistants.domain 18 | name_prefix = "assist" 19 | } : null), 20 | (local.create_langflow ? { 21 | name = module.langflow[0].container_info.name 22 | port = module.langflow[0].container_info.port 23 | domain = var.langflow.domain 24 | name_prefix = "l-flow" 25 | } : null) 26 | ] : each if each != null 27 | ] 28 | } 29 | 30 | module "aws_infra" { 31 | source = "./modules/aws_infra" 32 | count = local.aws_infra_checks_pass ? 1 : 0 33 | 34 | alb_config = var.infrastructure 35 | domain_config = var.domain_config 36 | fargate_config = var.fargate_config 37 | components = local.components 38 | } 39 | 40 | module "assistants" { 41 | source = "./modules/assistants" 42 | count = local.create_assistants ? 1 : 0 43 | 44 | infrastructure = local.infrastructure 45 | target_group_arn = module.aws_infra[0].target_groups[module.assistants[0].container_info.name].arn 46 | config = var.assistants 47 | } 48 | 49 | module "langflow" { 50 | source = "./modules/langflow" 51 | count = local.create_langflow ? 1 : 0 52 | 53 | infrastructure = local.infrastructure 54 | target_group_arn = module.aws_infra[0].target_groups[module.langflow[0].container_info.name].arn 55 | config = var.langflow 56 | } 57 | 58 | module "vector_dbs" { 59 | source = "./modules/astra_db" 60 | 61 | for_each = { 62 | for db in var.vector_dbs : db.name => db 63 | } 64 | 65 | cloud_provider = local.infrastructure.cloud_provider 66 | config = each.value 67 | } 68 | -------------------------------------------------------------------------------- /demo-aws/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/datastax/astra" { 5 | version = "2.3.6" 6 | constraints = "~> 2.3.3" 7 | hashes = [ 8 | "h1:MeN1+a0qOEmQXPN6alH3Lt0td9NgwS4WhaJfCyilaWM=", 9 | "zh:0736bfc8621dc4f238a5f7ddb767f199f25144ec8db9d786f5bf081928b6a6f0", 10 | "zh:2957d0436b5311beec9e83192f686cfd0eacf177f9c75791abc465699b4ebf2d", 11 | "zh:296551af6fd6219faf2b5ddd60b479ff5f1d14678cbddfc0f4eb1ff4bf309903", 12 | "zh:55ab47f85bab4fbfc87ba3cf335ca89fd9ed3283612e7e5f6c7eabe33266f222", 13 | "zh:5f9fbe612524a447751001ba4e38fa367b49f6dca4b81890ed431113c2867dd9", 14 | "zh:6615bbda394b843a871bddab862c5eb82341b84342bdf7edeb8579065180524a", 15 | "zh:8d7da9deb36242f789b8c24628d9a5ebfbff44e1b15a8240de0d7cd5e42f2a86", 16 | "zh:9b2f164763329df83240c95904d735515b5d735113014c1246bdb0f2706ffa98", 17 | "zh:9bf21e0da743dfb5f59169f1ae43e539cf55224d8ad28c59bddd19c7f8c4e717", 18 | "zh:a4a3987afa51036fb3832aba4f3aa4925646f5b5ed399be5915ed913b9d84a94", 19 | "zh:e1ea8c5954d039c5cf8263c96df9659b99f7bb1340635682c8ba0e69aaa2542e", 20 | "zh:e58f6657cccdc2247ca234e4c3fdf8b2ca713d474b3b2e0ed2e0be78ee264088", 21 | "zh:e8cafc793cf9251fbcd1f397a936737c972603b8e5cf6ab830f9b3f29e8595aa", 22 | "zh:f6d6130e3328f66d084d642abc832d51bc1c5d5d976f9ffdcd74f377711735b7", 23 | ] 24 | } 25 | 26 | provider "registry.terraform.io/hashicorp/aws" { 27 | version = "5.47.0" 28 | constraints = ">= 4.66.1, >= 5.30.0, >= 5.46.0, ~> 5.47.0" 29 | hashes = [ 30 | "h1:T0tupfn2Ubj18Y7xmO0pFMvti1Qns2K6EGXenR6Hg30=", 31 | "zh:06037a14e47e8f82d0b3b326cd188566272b808b7970a9249a11db26d475b83d", 32 | "zh:116b7dd58ca964a1056249d2b6550f399b0a6bc9a7920b7ee134242114432c9f", 33 | "zh:1aa089c81459071c1d65ba7454f1122159e1fa1b5384e6e9ef85c8264f8a9ecb", 34 | "zh:2c1471acba40c4944aa88dda761093c0c969db6408bdc1a4fb62417788cd6bb6", 35 | "zh:3b950bea06ea4bf1ec359a97a4f1745b7efca7fc2da368843666020dd0ebc5d4", 36 | "zh:7191c5c2fce834d584153dcd5269ed3042437f224d341ad85df06b2247bd09b2", 37 | "zh:76d841b3f247f9bb3899dec3b4d871613a4ae8a83a581a827655d34b1bbee0ee", 38 | "zh:7c656ce252fafc2c915dad43a0a7da17dba975207d75841a02f3f2b92d51ec25", 39 | "zh:8ec97118cbdef64139c52b719e4e22443e67a1f37ea1597cd45b2e9b97332a35", 40 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 41 | "zh:a369deca7938236a7da59f7ad1fe18137f736764c9015ed10e88edb6e8505980", 42 | "zh:a743882fb099401eae0c86d9388a6faadbbc27b2ac9477aeef643e5de4eec3f9", 43 | "zh:d5f960f58aff06fc58e244fea6e665800384cacb8cd64a556f8e145b98650372", 44 | "zh:e31ffcfd560132ffbff2f574928ba392e663202a750750ed39a8950031b75623", 45 | "zh:ebd9061b92a772144564f35a63d5a08cb45e14a9d39294fda185f2e0de9c8e28", 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /demo-aws/aws/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/datastax/astra" { 5 | version = "2.3.3" 6 | constraints = "~> 2.3.3" 7 | hashes = [ 8 | "h1:eackAzFENohVgW6IY4nvEym6JWK7nXCYdB5xVvw5bTo=", 9 | "zh:001aa60f652b5054fcf577ffb08b590f544b5847a1ecc5f43a88b1eae2a4d6c1", 10 | "zh:0263858feca2dfdf0ee69fde4711091ff017ed6ae890b564d7667f3f8c28a243", 11 | "zh:089e33fcf5e15d0f207459bd3923cde766e4a544f46b134164a2abde8f26c5ce", 12 | "zh:0f6f84a61ee91d3deabbfd7a60eb3c4099b57cbfe1a21711385eb35e08970c78", 13 | "zh:27493cda6ebc21dafe701e414c4999ad0bda7aea4c06967c1eb4a0718af14c67", 14 | "zh:3f82ed06e976e3fdb326e78e9d3d39e763b7bac0ac2de35c761c54a7569f6240", 15 | "zh:46456bc64429ee034d5ea6c55c1cd2656a2535ced3e64a2157dcb106d10d317d", 16 | "zh:58fe618f86f5096f684e934f47c2be272985128c554e33204a688186d82002ac", 17 | "zh:5db7bb4e064facecf3a258375e874c9064e35368efd0639222dab8f1b49da345", 18 | "zh:7f5ac57b1534921ff8daf8ab51791e1aeff2d88e07cfdc0325f30cc49b03e73b", 19 | "zh:97b4fea28c1703913ccddbd5e954b64611eb8c26b286fddcdfbe496473012008", 20 | "zh:d5d8a56dd1c795ba7874fc47c284f0461dbde2d9c80a366ec6ba2699588f7c3a", 21 | "zh:f2b5320d3c61504ddfc4db868a8d2058a8a5dcfa9bc11dc96458aee0a4fc34f6", 22 | "zh:f74c2d3e436e344ebd65ef4e0e42a0617147723942b5c1f0725688cdef7f02e8", 23 | ] 24 | } 25 | 26 | provider "registry.terraform.io/hashicorp/aws" { 27 | version = "5.47.0" 28 | constraints = ">= 4.66.1, >= 5.30.0, >= 5.46.0, ~> 5.47.0" 29 | hashes = [ 30 | "h1:bZEm2TDCM7jmpNXK6QOWsT1YU8GiGGQaraUvwO887U8=", 31 | "zh:06037a14e47e8f82d0b3b326cd188566272b808b7970a9249a11db26d475b83d", 32 | "zh:116b7dd58ca964a1056249d2b6550f399b0a6bc9a7920b7ee134242114432c9f", 33 | "zh:1aa089c81459071c1d65ba7454f1122159e1fa1b5384e6e9ef85c8264f8a9ecb", 34 | "zh:2c1471acba40c4944aa88dda761093c0c969db6408bdc1a4fb62417788cd6bb6", 35 | "zh:3b950bea06ea4bf1ec359a97a4f1745b7efca7fc2da368843666020dd0ebc5d4", 36 | "zh:7191c5c2fce834d584153dcd5269ed3042437f224d341ad85df06b2247bd09b2", 37 | "zh:76d841b3f247f9bb3899dec3b4d871613a4ae8a83a581a827655d34b1bbee0ee", 38 | "zh:7c656ce252fafc2c915dad43a0a7da17dba975207d75841a02f3f2b92d51ec25", 39 | "zh:8ec97118cbdef64139c52b719e4e22443e67a1f37ea1597cd45b2e9b97332a35", 40 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 41 | "zh:a369deca7938236a7da59f7ad1fe18137f736764c9015ed10e88edb6e8505980", 42 | "zh:a743882fb099401eae0c86d9388a6faadbbc27b2ac9477aeef643e5de4eec3f9", 43 | "zh:d5f960f58aff06fc58e244fea6e665800384cacb8cd64a556f8e145b98650372", 44 | "zh:e31ffcfd560132ffbff2f574928ba392e663202a750750ed39a8950031b75623", 45 | "zh:ebd9061b92a772144564f35a63d5a08cb45e14a9d39294fda185f2e0de9c8e28", 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/ecs_deployment/main.tf: -------------------------------------------------------------------------------- 1 | output "target_id" { 2 | value = aws_ecs_service.this.id 3 | } 4 | 5 | data "aws_iam_policy_document" "ecs_assume_role_policy" { 6 | statement { 7 | actions = ["sts:AssumeRole"] 8 | 9 | principals { 10 | type = "Service" 11 | identifiers = ["ecs-tasks.amazonaws.com"] 12 | } 13 | } 14 | } 15 | 16 | data "aws_iam_policy_document" "execute_command_policy" { 17 | statement { 18 | actions = [ 19 | "ssmmessages:CreateControlChannel", 20 | "ssmmessages:CreateDataChannel", 21 | "ssmmessages:OpenControlChannel", 22 | "ssmmessages:OpenDataChannel" 23 | ] 24 | 25 | principals { 26 | type = "Service" 27 | identifiers = ["ecs-tasks.amazonaws.com"] 28 | } 29 | } 30 | } 31 | 32 | resource "aws_iam_role" "ecs_execution_role" { 33 | name_prefix = "ecs_execution_role" 34 | assume_role_policy = data.aws_iam_policy_document.ecs_assume_role_policy.json 35 | } 36 | 37 | resource "aws_iam_role_policy_attachment" "ssm_core" { 38 | role = aws_iam_role.ecs_execution_role.name 39 | policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" 40 | } 41 | 42 | resource "aws_ecs_task_definition" "this" { 43 | container_definitions = jsonencode([ 44 | { 45 | image = var.container_info.image, 46 | name = var.container_info.name, 47 | portMappings = [{ containerPort = var.container_info.port }], 48 | healthCheck = { 49 | command = [ 50 | "CMD-SHELL", "curl -f http://localhost:${var.container_info.port}/${var.container_info.health_path} || exit 1" 51 | ] 52 | startPeriod = 120 53 | } 54 | environment = concat([ 55 | { name = "ECS_ENABLE_CONTAINER_METADATA", value = "true" } 56 | ], [ 57 | for key, value in coalesce(var.container_info.env, {}) : { 58 | name = key 59 | value = value 60 | } 61 | ]) 62 | command = var.container_info.entrypoint 63 | } 64 | ]) 65 | 66 | family = "${var.container_info.name}-tasks" 67 | cpu = try(coalesce(var.config.containers.cpu), 1024) 68 | memory = try(coalesce(var.config.containers.memory), 2048) 69 | network_mode = "awsvpc" 70 | requires_compatibilities = ["FARGATE"] 71 | task_role_arn = aws_iam_role.ecs_execution_role.arn 72 | } 73 | 74 | resource "aws_ecs_service" "this" { 75 | cluster = var.infrastructure.cluster 76 | task_definition = aws_ecs_task_definition.this.arn 77 | name = var.container_info.name 78 | launch_type = "FARGATE" 79 | desired_count = try(var.config.containers.desired_count, 1) 80 | 81 | enable_execute_command = true 82 | 83 | load_balancer { 84 | container_name = var.container_info.name 85 | container_port = var.container_info.port 86 | target_group_arn = var.target_group_arn 87 | } 88 | 89 | network_configuration { 90 | security_groups = var.infrastructure.security_groups 91 | subnets = var.infrastructure.subnets 92 | } 93 | } 94 | 95 | resource "aws_appautoscaling_target" "this" { 96 | min_capacity = try(coalesce(var.config.min_instances), 1) 97 | max_capacity = try(coalesce(var.config.max_instances), 20) 98 | resource_id = "service/${var.infrastructure.cluster}/${aws_ecs_service.this.name}" 99 | scalable_dimension = "ecs:service:DesiredCount" 100 | service_namespace = "ecs" 101 | } 102 | 103 | resource "aws_appautoscaling_policy" "cpu_tracking" { 104 | name = "${aws_ecs_service.this.name}-cpu-tracking" 105 | policy_type = "TargetTrackingScaling" 106 | resource_id = aws_appautoscaling_target.this.resource_id 107 | scalable_dimension = aws_appautoscaling_target.this.scalable_dimension 108 | service_namespace = aws_appautoscaling_target.this.service_namespace 109 | 110 | target_tracking_scaling_policy_configuration { 111 | target_value = 80 112 | 113 | predefined_metric_specification { 114 | predefined_metric_type = "ECSServiceAverageCPUUtilization" 115 | } 116 | 117 | scale_out_cooldown = 60 118 | scale_in_cooldown = 60 119 | } 120 | } 121 | 122 | resource "aws_appautoscaling_policy" "mem_tracking" { 123 | name = "${aws_ecs_service.this.name}-mem-tracking" 124 | policy_type = "TargetTrackingScaling" 125 | resource_id = aws_appautoscaling_target.this.resource_id 126 | scalable_dimension = aws_appautoscaling_target.this.scalable_dimension 127 | service_namespace = aws_appautoscaling_target.this.service_namespace 128 | 129 | target_tracking_scaling_policy_configuration { 130 | target_value = 80 131 | 132 | predefined_metric_specification { 133 | predefined_metric_type = "ECSServiceAverageMemoryUtilization" 134 | } 135 | 136 | scale_out_cooldown = 60 137 | scale_in_cooldown = 60 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /demo-aws/aws/modules/aws_infra/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_availability_zones" "available" { 2 | state = "available" 3 | } 4 | 5 | locals { 6 | create_vpc = var.alb_config == null 7 | } 8 | 9 | module "vpc" { 10 | source = "terraform-aws-modules/vpc/aws" 11 | version = "~> 5.8.1" 12 | 13 | count = local.create_vpc ? 1 : 0 14 | 15 | name = "datastax-vpc" 16 | tags = { 17 | Project = "datastax" 18 | } 19 | 20 | azs = slice(data.aws_availability_zones.available.names, 0, 2) 21 | cidr = "10.0.0.0/16" 22 | create_igw = true 23 | enable_nat_gateway = true 24 | private_subnets = ["10.0.1.0/24", "10.0.2.0/24"] 25 | public_subnets = ["10.0.101.0/24", "10.0.102.0/24"] 26 | single_nat_gateway = true 27 | } 28 | 29 | locals { 30 | vpc_id = try(var.alb_config.vpc_id, module.vpc[0].vpc_id) 31 | public_subnets = try(var.alb_config.public_subnets, module.vpc[0].public_subnets) 32 | private_subnets = try(var.alb_config.private_subnets, module.vpc[0].private_subnets) 33 | security_groups = try(var.alb_config.security_groups, [module.vpc[0].default_security_group_id]) 34 | } 35 | 36 | locals { 37 | certificate_arn = try(aws_acm_certificate.service_cert[0].arn, var.domain_config.acm_cert_arn) 38 | } 39 | 40 | module "alb" { 41 | source = "terraform-aws-modules/alb/aws" 42 | version = "~> 9.9.0" 43 | 44 | depends_on = [aws_acm_certificate_validation.certificate_validation] 45 | 46 | enable_deletion_protection = false 47 | 48 | name = "datastax-alb" 49 | tags = { 50 | Project = "datastax" 51 | } 52 | 53 | vpc_id = local.vpc_id 54 | subnets = local.public_subnets 55 | security_groups = local.security_groups 56 | 57 | security_group_ingress_rules = { 58 | all_http = { 59 | from_port = 80 60 | to_port = 80 61 | protocol = "TCP" 62 | description = "Permit incoming HTTP requests from the internet" 63 | cidr_ipv4 = "0.0.0.0/0" 64 | } 65 | all_https = { 66 | from_port = 443 67 | to_port = 443 68 | protocol = "TCP" 69 | description = "Permit incoming HTTPS requests from the internet" 70 | cidr_ipv4 = "0.0.0.0/0" 71 | } 72 | } 73 | 74 | security_group_egress_rules = { 75 | all = { 76 | from_port = 0 77 | to_port = 65535 78 | protocol = "TCP" 79 | description = "Permit all outgoing requests to the internet" 80 | cidr_ipv4 = "0.0.0.0/0" 81 | } 82 | } 83 | 84 | listeners = { 85 | http = { 86 | port = 80 87 | protocol = "HTTP" 88 | redirect = { 89 | port = 443 90 | protocol = "HTTPS" 91 | status_code = "HTTP_301" 92 | } 93 | } 94 | https = { 95 | port = 443 96 | protocol = "HTTPS" 97 | certificate_arn = local.certificate_arn 98 | rules = { 99 | for config in var.components : config.name => { 100 | actions = [{ type = "forward", target_group_key = config.name }] 101 | conditions = [{ host_header = { values = [config.domain] } }] 102 | } 103 | } 104 | fixed_response = { 105 | content_type = "text/plain" 106 | status_code = "404" 107 | message_body = "Not Found" 108 | } 109 | } 110 | } 111 | 112 | target_groups = { 113 | for config in var.components : config.name => { 114 | name_prefix = config.name_prefix 115 | port = config.port 116 | protocol = "HTTP" 117 | target_type = "ip" 118 | create_attachment = false 119 | } 120 | } 121 | } 122 | 123 | module "ecs" { 124 | source = "terraform-aws-modules/ecs/aws" 125 | version = "~> 5.11.1" 126 | 127 | cluster_name = "datastax-ecs-cluster" 128 | 129 | fargate_capacity_providers = { 130 | FARGATE = { 131 | default_capacity_provider_strategy = { 132 | base = try(coalesce(var.fargate_config.capacity_provider_weights.default_base), 20) 133 | weight = try(coalesce(var.fargate_config.capacity_provider_weights.default_weight), 0) 134 | } 135 | } 136 | FARGATE_SPOT = { 137 | default_capacity_provider_strategy = { 138 | base = try(coalesce(var.fargate_config.capacity_provider_weights.spot_base), 0) 139 | weight = try(coalesce(var.fargate_config.capacity_provider_weights.default_weight), 80) 140 | } 141 | } 142 | } 143 | } 144 | 145 | resource "aws_security_group" "ecs_cluster_sg" { 146 | vpc_id = local.vpc_id 147 | 148 | name = "datastax-ecs-cluster-sg" 149 | 150 | ingress { 151 | from_port = 0 152 | to_port = 65535 153 | protocol = "tcp" 154 | security_groups = [module.alb.security_group_id] 155 | self = true 156 | } 157 | 158 | egress { 159 | from_port = 0 160 | to_port = 65535 161 | protocol = "tcp" 162 | cidr_blocks = ["0.0.0.0/0"] 163 | } 164 | } 165 | 166 | locals { 167 | domains = [for config in var.components : config.domain] 168 | hosted_zones = try(var.domain_config.hosted_zones, null) 169 | 170 | auto_route53_setup = try(var.domain_config.auto_route53_setup, null) == true 171 | auto_acm_cert = var.domain_config.acm_cert_arn == null 172 | 173 | hosted_zones_lut = { 174 | for idx, config in var.components : config.name => 175 | try(local.hosted_zones[config.name], local.hosted_zones["default"]) 176 | } 177 | } 178 | 179 | data "aws_route53_zone" "zones" { 180 | for_each = { 181 | for idx, config in var.components : idx => config 182 | if local.auto_route53_setup 183 | } 184 | 185 | zone_id = local.hosted_zones_lut[each.value.name]["zone_id"] 186 | name = local.hosted_zones_lut[each.value.name]["zone_name"] 187 | 188 | private_zone = local.hosted_zones_lut[each.value.name]["zone_name"] != null ? false : null 189 | } 190 | 191 | resource "aws_route53_record" "a_records" { 192 | for_each = { 193 | for idx, config in var.components : idx => config 194 | if local.auto_route53_setup 195 | } 196 | 197 | name = "${each.value.domain}." 198 | type = "A" 199 | zone_id = data.aws_route53_zone.zones[each.key].zone_id 200 | 201 | alias { 202 | name = "${module.alb.dns_name}." 203 | zone_id = module.alb.zone_id 204 | evaluate_target_health = true 205 | } 206 | } 207 | 208 | resource "aws_acm_certificate" "service_cert" { 209 | count = local.auto_acm_cert ? 1 : 0 210 | 211 | domain_name = local.domains[0] 212 | validation_method = "DNS" 213 | subject_alternative_names = toset(slice(local.domains, 1, length(local.domains))) 214 | 215 | lifecycle { 216 | create_before_destroy = true 217 | } 218 | 219 | tags = { 220 | Project = "datastax" 221 | Name = "datastax-service-cert" 222 | } 223 | } 224 | 225 | locals { 226 | dvos = local.auto_acm_cert ? tolist(aws_acm_certificate.service_cert[0].domain_validation_options) : [] 227 | } 228 | 229 | resource "aws_route53_record" "validation" { 230 | count = local.auto_acm_cert ? length(local.domains) : 0 231 | 232 | name = local.dvos[count.index]["resource_record_name"] 233 | type = local.dvos[count.index]["resource_record_type"] 234 | records = [local.dvos[count.index]["resource_record_value"]] 235 | 236 | zone_id = data.aws_route53_zone.zones[count.index].zone_id 237 | ttl = 60 238 | 239 | allow_overwrite = true 240 | } 241 | 242 | resource "aws_acm_certificate_validation" "certificate_validation" { 243 | count = local.auto_acm_cert ? 1 : 0 244 | certificate_arn = aws_acm_certificate.service_cert[0].arn 245 | validation_record_fqdns = aws_route53_record.validation[*].fqdn 246 | } 247 | -------------------------------------------------------------------------------- /demo-aws/aws/variables.tf: -------------------------------------------------------------------------------- 1 | variable "domain_config" { 2 | type = object({ 3 | auto_route53_setup = bool 4 | hosted_zones = optional(map(object({ 5 | zone_id = optional(string) 6 | zone_name = optional(string) 7 | }))) 8 | acm_cert_arn = optional(string) 9 | }) 10 | default = null 11 | 12 | validation { 13 | condition = try(!(var.domain_config.auto_route53_setup == true && length(var.domain_config.hosted_zones) == 0), true) 14 | error_message = "auto_route53_setup requires hosted_zones to be provided" 15 | } 16 | 17 | validation { 18 | condition = try(!(var.domain_config.auto_route53_setup == false && var.domain_config.acm_cert_arn == null), true) 19 | error_message = "must provide an acm_cert_arn if auto_route53_setup isn't true" 20 | } 21 | 22 | description = <
Otherwise, you must set each domain to the output `alb_dns_name` w/ an A record. | `bool` | 83 | | hosted_zones | A map of components (or a default value) to their hosted zones. The valid keys are {default, langflow, assistants}. For each, either `zone_id` or `zone_name` must be set. |
optional(map(object({
zone_id = optional(string)
zone_name = optional(string)
})))
| 84 | | acm_cert_arn | The ARN of the ACM certificate to use. Required if auto_route53_setup is `false`. If auto_route53_setup is `true`, you may choose to set this; otherwise, one is manually created. | `optional(boolean)` | 85 | 86 | ### `fargate_config` (optional) 87 | 88 | Options related to the deployment of the ECS on Fargate instances. 89 | 90 | | Field | Description | Type | 91 | | ------------------------- | ----------- | ---- | 92 | | capacity_provider_weights | The weights to assign to the capacity providers.
If not set, it's a 20/80 split between Fargate and Fargate Spot.|
optional(object({
default_base = number
default_weight = number
spot_base = number
spot_weight = number
}))
| 93 | 94 | ### `langflow` (optional) 95 | 96 | Options regarding the langflow deployment. If not set, langflow is not created. Must have a custom domain at hand to use this. 97 | 98 | | Field | Description | Type | 99 | | ---------- | ----------- | ---- | 100 | | version | The image version to use for the deployment; defaults to "latest". | `optional(string)` | 101 | | domain | The domain name to use for the service; used in the listener routing rules. | `string` | 102 | | env | Environment variables to set for the service. | `optional(map(string))` | 103 | | containers | Options for the ECS service.
- cpu: The amount of CPU to allocate to the service. Defaults to 1024.
- memory: The amount of memory to allocate to the service. Defaults to 2048 (Mi).
- min_instances: The minimum number of instances to run. Defaults to 1.
- max_instances: The maximum number of instances to run. Defaults to 100. |
optional(object({
cpu = optional(number)
memory = optional(number)
min_instances = optional(number)
max_instances = optional(number)
}))
| 104 | 105 | ### `assistants` (optional) 106 | 107 | Options regarding the astra-assistants-api deployment. If not set, assistants is not created. Must have a custom domain at hand to use this. 108 | 109 | | Field | Description | Type | 110 | | ---------- | ----------- | ---- | 111 | | version | The image version to use for the deployment; defaults to "latest". | `optional(string)` | 112 | | domain | The domain name to use for the service; used in the listener routing rules. | `string` | 113 | | env | Environment variables to set for the service. | `optional(map(string))` | 114 | | db | Options for the database Astra Assistants uses.
- regions: The regions to deploy the database to. Defaults to the first available region.
- deletion_protection: Whether to enable deletion protection on the database.
- cloud_provider: The cloud provider to use for the database. Defaults to "gcp". |
optional(object({
regions = optional(set(string))
deletion_protection = optional(bool)
cloud_provider = optional(string)
}))
| 115 | | containers | Options for the ECS service.
- cpu: The amount of CPU to allocate to the service. Defaults to 1024.
- memory: The amount of memory to allocate to the service. Defaults to 2048 (Mi).
- min_instances: The minimum number of instances to run. Defaults to 1.
- max_instances: The maximum number of instances to run. Defaults to 100. |
optional(object({
cpu = optional(number)
memory = optional(number)
min_instances = optional(number)
max_instances = optional(number)
}))
| 116 | 117 | ### `vector_dbs` optional 118 | 119 | A list of configuration for each vector-enabled DB you may want to create/deploy. No custom domain is required to use this. 120 | 121 | | Field | Description | Type | 122 | | -------------------- | ------------------------------------------------------------------------------ | ----------------------- | 123 | | name | The name of the database to create. | `string` | 124 | | regions | The regions to deploy the database to. Defaults to the first available region. | `optional(set(string))` | 125 | | keyspaces | The keyspaces to use for the database. The first keyspace will be used as the initial one for the database. Defaults to just "default_keyspace". | `optional(list(string))` | 126 | | cloud_provider | The cloud provider to use for the database. Defaults to "aws". | `optional(string)` | 127 | | deletion_protection | Whether to enable deletion protection on the database. | `optional(bool)` | 128 | 129 | ## Outputs 130 | 131 | ### `vpc_id` (`string`) 132 | 133 | The ID of the VPC used. If created, it's the new ID; if set, it regurgitates the set ID. 134 | 135 | ### `alb_dns_name` (`string`) 136 | 137 | The DNS name of the created ALB that the domains for langflow & assistants must be set to. 138 | 139 | ### `db_ids` (`map(string)`) 140 | 141 | A map of DB IDs => DB names for all of the dbs created (from the `assistants` module and the `vector_dbs` module), e.g: 142 | 143 | ```hcl 144 | "db_ids" = { 145 | "12345678-abcd-efgh-1234-abcd1234efgh" = "assistant_api_db" 146 | } 147 | ``` 148 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | 2 | ## Terraform Providers for RagStack AI 3 | 4 | ## Table of content 5 | 6 | 1. [Overview](#1-overview) 7 | 2. [Installation and Prerequisites](#2-installation-and-prerequisites) 8 | 3. [AWS Deployments]() 9 | 4. [GCP Deployments]() 10 | 5. [Azure Deployments]() 11 | 12 | ## 1. Overview 13 | 14 | Terraform module which helps you quickly deploy an opinionated AI/RAG stack to AWS, provided by Datastax. 15 | 16 | ## 2. Installation and Prerequisites 17 | 18 | ### 2.1 - Terraform Installation 19 | 20 | > Note: Check [Terraform Documentation](https://developer.hashicorp.com/terraform/install) 21 | 22 | 23 | - ✅ `2.1.a` **Update Homebrew**: Before installing any new package, it's good practice to update Homebrew: 24 | 25 | ```sh 26 | brew update 27 | ``` 28 | 29 | - ✅ `2.1.b` **Install Terraform**: To install Terraform using Homebrew, run the following command: 30 | 31 | ```sh 32 | brew tap hashicorp/tap 33 | brew install hashicorp/tap/terraform 34 | ``` 35 | 36 | - ✅ `2.1.c` **Verification**; After the installation completes, verify that Terraform is installed correctly by checking its version: 37 | 38 | ```sh 39 | terraform -v 40 | ``` 41 | 42 | ### 2.2 - Astra CLI Installation 43 | 44 | Astra CLI is a command-line interface (CLI) tool that enables users to interact with and manage the 45 | Astra database-as-a-service platform. It provides developers with a streamlined way to create, configure, and 46 | manipulate databases, making it easier to integrate Astra into their applications and workflows. 47 | 48 | Astra CLI goes beyond basic database management by facilitating the seamless integration of external tools like **dsbulk**, 49 | **cqlsh**, and **pulshar-shell** or by providing useful shortcuts like **dotenv** file generation. 50 | 51 | - ✅ `2.2.a` **Update Homebrew**: Before installing any new package, it's good practice to update Homebrew: 52 | 53 | ```sh 54 | brew update 55 | ``` 56 | 57 | - ✅ `2.2.b` **Install Astra CLI**: To install Terraform using Homebrew, run the following command: 58 | 59 | ```sh 60 | brew install datastax/astra-cli/astra-cli 61 | ``` 62 | 63 | - ✅ `2.2.c` Connect to [https://astra.datastax.com](https://astra.datastax.com) 64 | 65 | ![imgs/01-login.png](imgs/01-login.png) 66 | 67 | - ✅ `2.2.d` Navigate to token and generate a token with `Organization Administrator` permissions and copy the token starting by `AstraCS:...` 68 | 69 | ![imgs/02-token.png](imgs/02-token.png) 70 | 71 | > Note: 72 | > 73 | > `osx` is based on a linux kernel and can run shell scripts. Files are 74 | > installed in `/usr/local/Cellar/astra-cli/` and linked with a 75 | > symbolic link to `/usr/local/bin/astra`. The configuration of the CLI is 76 | > stored in `~/.astrarc` - **It is not lost on updates 77 | 78 | - ✅ `2.2.e` - Issue `setup` command and provide your token as an input. It must start by `AstraCS:...`. Make sure to have the `Organization Administrator` role to avoid any permission limitations later on. 79 | 80 | ```sh 81 | astra setup --token AstraCS... 82 | ``` 83 | 84 | - ✅ `2.2.f` - Display your local configuration list, validating setup is complete. 85 | 86 | ```bash 87 | astra config list 88 | ``` 89 | 90 | - ✅ `2.2.g` - Create a `vector database` with the CLI 91 | 92 | ```bash 93 | astra db create terraform_demo --vector --async 94 | ``` 95 | 96 | - ✅ `2.2.h` - You can see it pending with 97 | 98 | ```bash 99 | astra db list 100 | ``` 101 | 102 | ```console 103 | +---------------------+--------------------------------------+-----------+-------+---+-----------+ 104 | | Name | id | Regions | Cloud | V | Status | 105 | +---------------------+--------------------------------------+-----------+-------+---+-----------+ 106 | | terraform_demo | 2753308c-34bc-4bff-ab87-0ff3f2218fa3 | us-east1 | gcp | ■ | PENDING | 107 | +---------------------+--------------------------------------+-----------+-------+---+-----------+ 108 | ``` 109 | 110 | ### 2.d Get the sample project 111 | 112 | ``` 113 | git clone git@github.com:datastax/terraform-datastax-ai-stack.git 114 | ``` 115 | 116 | ## 3. AWS Deployment 117 | 118 | ![imgs/aws-03.png](imgs/aws-03.png) 119 | 120 | ### 3.1 AWS Credentials 121 | 122 | - ✅ `3.1.a` - Access the Idenity and Access Management console (IAM) in AWS 123 | 124 | - ✅ `3.1.b` - Create a an User with AWS and add the different permissions. 125 | 126 | - ✅ `3.1.c` - For this user create a pair of keys wih `access_key` and `secret_key` as follows: 127 | 128 | ![imgs/aws-01.png](imgs/aws-01.png) 129 | 130 | - ✅ `3.1.d` - Setup the access for an application outside 131 | 132 | ![imgs/aws-02.png](imgs/aws-02.png) 133 | 134 | ### 3.2 AWS CLI 135 | 136 | - ✅ `3.2.a` - Installing AWS CLI 137 | 138 | ```bash 139 | curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg" 140 | $ sudo installer -pkg AWSCLIV2.pkg -target / 141 | ``` 142 | 143 | - ✅ `3.2.b` - Setup AWS CLI (short term) 144 | 145 | ```bash 146 | touch ~/.aws/credentials 147 | vi ~/.aws/credentials 148 | ``` 149 | - Populate the configuration file with your credentials 150 | 151 | ```ini 152 | [default] 153 | aws_access_key_id = replace 154 | aws_secret_access_key = replace/K7MDENG/bPxRfiCYEXAMPLEKEY 155 | aws_session_token = replace 156 | ``` 157 | 158 | - ✅ `3.2.c` -Verify the cli is install 159 | 160 | ```bash 161 | aws --version 162 | ``` 163 | 164 | - Configure your profile 165 | 166 | ``` 167 | aws configure list-profiles 168 | aws configure list --profile default 169 | ``` 170 | 171 | ``` 172 | Name Value Type Location 173 | ---- ----- ---- -------- 174 | profile default manual --profile 175 | access_key ****************JR3H shared-credentials-file 176 | secret_key ****************c319 shared-credentials-file 177 | ``` 178 | 179 | ### 3.3 Init Terraform 180 | 181 | - ✅ `3.3.a` -Update `terraform.tf` as follows (filtering only AWS) 182 | 183 | ```typesafe 184 | terraform { 185 | required_providers { 186 | astra = { 187 | source = "datastax/astra" 188 | version = "~> 2.3.3" 189 | } 190 | aws = { 191 | source = "hashicorp/aws" 192 | version = "~> 5.47.0" 193 | } 194 | } 195 | } 196 | ``` 197 | 198 | 199 | - ✅ `3.3.b` - Update `input.tf` as follows (selecting AWS) 200 | 201 | ```console 202 | variable "astra_token" { 203 | type = string 204 | } 205 | 206 | variable "aws_profile" { 207 | type = string 208 | nullable = true 209 | default = null 210 | } 211 | ``` 212 | 213 | - ✅ `3.3.c` - Initialization 214 | 215 | ```bash 216 | cd demo-aws 217 | terraform init 218 | ``` 219 | 220 | Output 221 | ``` 222 | Initializing the backend... 223 | Initializing modules... 224 | - datastax-ai-stack-aws in aws 225 | - datastax-ai-stack-aws.assistants in aws/modules/assistants 226 | - datastax-ai-stack-aws.assistants.assistants_api_db in aws/modules/astra_db 227 | - datastax-ai-stack-aws.assistants.ecs_deployment in aws/modules/ecs_deployment 228 | - datastax-ai-stack-aws.aws_infra in aws/modules/aws_infra 229 | Downloading registry.terraform.io/terraform-aws-modules/alb/aws 9.9.0 for datastax-ai-stack-aws.aws_infra.alb... 230 | - datastax-ai-stack-aws.aws_infra.alb in .terraform/modules/datastax-ai-stack-aws.aws_infra.alb 231 | Downloading registry.terraform.io/terraform-aws-modules/ecs/aws 5.11.3 for datastax-ai-stack-aws.aws_infra.ecs... 232 | - datastax-ai-stack-aws.aws_infra.ecs in .terraform/modules/datastax-ai-stack-aws.aws_infra.ecs 233 | - datastax-ai-stack-aws.aws_infra.ecs.cluster in .terraform/modules/datastax-ai-stack-aws.aws_infra.ecs/modules/cluster 234 | - datastax-ai-stack-aws.aws_infra.ecs.service in .terraform/modules/datastax-ai-stack-aws.aws_infra.ecs/modules/service 235 | - datastax-ai-stack-aws.aws_infra.ecs.service.container_definition in .terraform/modules/datastax-ai-stack-aws.aws_infra.ecs/modules/container-definition 236 | Downloading registry.terraform.io/terraform-aws-modules/vpc/aws 5.8.1 for datastax-ai-stack-aws.aws_infra.vpc... 237 | - datastax-ai-stack-aws.aws_infra.vpc in .terraform/modules/datastax-ai-stack-aws.aws_infra.vpc 238 | - datastax-ai-stack-aws.langflow in aws/modules/langflow 239 | - datastax-ai-stack-aws.langflow.ecs_deployment in aws/modules/ecs_deployment 240 | - datastax-ai-stack-aws.vector_dbs in aws/modules/astra_db 241 | Initializing provider plugins... 242 | - Finding datastax/astra versions matching "~> 2.3.3"... 243 | - Finding hashicorp/aws versions matching ">= 4.66.1, >= 5.30.0, >= 5.46.0, ~> 5.47.0"... 244 | - Installing datastax/astra v2.3.6... 245 | - Installed datastax/astra v2.3.6 (signed by a HashiCorp partner, key ID 0ABDBFC051FFB5D5) 246 | - Installing hashicorp/aws v5.47.0... 247 | - Installed hashicorp/aws v5.47.0 (signed by HashiCorp) 248 | Partner and community providers are signed by their developers. 249 | If you'd like to know more about provider signing, you can read about it here: 250 | https://www.terraform.io/docs/cli/plugins/signing.html 251 | Terraform has created a lock file .terraform.lock.hcl to record the provider 252 | selections it made above. Include this file in your version control repository 253 | so that Terraform can guarantee to make the same selections by default when 254 | you run "terraform init" in the future. 255 | 256 | Terraform has been successfully initialized! 257 | 258 | You may now begin working with Terraform. Try running "terraform plan" to see 259 | any changes that are required for your infrastructure. All Terraform commands 260 | should now work. 261 | 262 | If you ever set or change modules or backend configuration for Terraform, 263 | rerun this command to reinitialize your working directory. If you forget, other 264 | commands will detect it and remind you to do so if necessary. 265 | ``` 266 | 267 | ```bash 268 | terraform plan 269 | ``` 270 | 271 | ``` 272 | var.astra_token 273 | Enter a value: AstraCS:XXXXXXXX 274 | 275 | var.aws_profile 276 | Enter a value: default 277 | ``` 278 | 279 | 280 | ```bash 281 | terraform apply 282 | ``` 283 | 284 | ## 4. GCP Deployment 285 | 286 | ## 5. AZURE Deployment 287 | 288 | --------------------------------------------------------------------------------