diff --git a/.gitignore b/.gitignore index 80044bd6a..a0817bd7d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,27 @@ bazel-* compile_commands.json .gitconfig + +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* +*.private + +# Crash log files +crash.log + +# Terraform lock files +.terraform.lock.hcl + +# Ignore any .tfvars files +*.tfvars + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json diff --git a/cloud-service-provider/aws/ec2-vm/terraform/README.md b/cloud-service-provider/aws/ec2-vm/terraform/README.md new file mode 100644 index 000000000..cb32536b4 --- /dev/null +++ b/cloud-service-provider/aws/ec2-vm/terraform/README.md @@ -0,0 +1,111 @@ +## 🤖 Terraform Example for AWS EC2 VM using the default VPC + +This example uses the following Terraform module: + +[Terraform Module](https://registry.terraform.io/modules/intel/aws-vm/intel/latest) + +**For additional customization, refer to the module documentation under "Inputs".** + +**The Module supports non-default VPCs and much more than what is shown in this example.** + +## Overview + +This example creates an AWS EC2 in the default VPC. The default region is can be changed in variables.tf. + +This example also creates: + +- Public IP +- EC2 key pair +- The private key is created in the local system where terraform apply is done +- It also creates a new security groups for network access + +## Prerequisites + +1. **Install AWS CLI**: Follow the instructions to install the AWS CLI from the [official documentation](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html). + +2. **Configure AWS CLI**: Run the following command to configure your AWS credentials and default region. +3. **Install Terraform**: Follow the instructions to install Terraform from the [official documentation](https://learn.hashicorp.com/tutorials/terraform/install-cli). +4. Have your preferred Git client installed. If you don't have one, you can download it from [Git](https://git-scm.com/downloads). + +## Configure AWS CLI + +```bash +aws configure +``` + +You will be prompted to enter your AWS Access Key ID, Secret Access Key, default region name, and output format. + +## Modify the example to suit your needs + +This example is can be customized to your needs by modifying the following files: + +```bash +main.tf +variables.tf +``` + +For additional customization, refer to the module documentation under **"Inputs"** [Terraform Module](https://registry.terraform.io/modules/intel/aws-vm/intel/latest) +. + +The module supports much more than what is shown in this example. + +## Usage + +In variables.tf, replace the below with you own IPV4 CIDR range before running the example. + +Use to get your IP address. + +```hcl + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = "A.B.C.D/32" +``` + +**Depending on your use case, you might also need to allow additional. +ports.** + +**Modify variable.tf replicating the existing format to add additional ports.** + +## Run Terraform + +```bash +git clone https://github.com/opea-project/GenAIInfra.git +cd GenAIInfra/cloud-service-provider/aws/ec2-vm/terraform + +# Modify the main.tf and variables.tf file to suit your needs (see above) + +terraform init +terraform plan +terraform apply +``` + +## SSH + +At this point, the EC2 instance should be up and running. You can SSH into the instance using the private key created in the local system. + +```bash +chmod 600 tfkey.private +ssh -i tfkey.private ubuntu@***VM_PUBLIC_IP*** + +# If in a proxy environment, use the following command +ssh -i tfkey.private -x your-proxy.com.com:PORT ubuntu@***VM_PUBLIC_IP*** +``` + +## OPEA + +You can now deploy OPEA components using OPEA instructions. + +[OPEA GenAI Examples](https://github.com/opea-project/GenAIExamples) + +## Destroy + +To destroy the resources created by this example, run the following command: + +```bash +terraform destroy +``` + +## Considerations + +The AWS region where this example is run should have a default VPC. diff --git a/cloud-service-provider/aws/ec2-vm/terraform/main.tf b/cloud-service-provider/aws/ec2-vm/terraform/main.tf new file mode 100644 index 000000000..5a397a1d9 --- /dev/null +++ b/cloud-service-provider/aws/ec2-vm/terraform/main.tf @@ -0,0 +1,83 @@ +# Provision EC2 Instance on AWS in default vpc. It is configured to create the EC2 in +# US-East-1 region. The region is provided in variables.tf in this example folder. + +# This example also create an EC2 key pair. Associate the public key with the EC2 instance. +# Creates the private key in the local system where terraform apply is done. +# Create a new security group to open up the SSH port 22 to a specific IP CIDR block +# To ssh: +# chmod 600 tfkey.private +# ssh -i tfkey.private ubuntu@ + +data "aws_ami" "ubuntu-linux-2204" { + most_recent = true + owners = ["099720109477"] # Canonical + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] + } + filter { + name = "virtualization-type" + values = ["hvm"] + } +} + +resource "random_id" "rid" { + byte_length = 5 +} + +# RSA key of size 4096 bits +resource "tls_private_key" "rsa" { + algorithm = "RSA" + rsa_bits = 4096 +} + +resource "aws_key_pair" "TF_key" { + key_name = "TF_key-${random_id.rid.dec}" + public_key = tls_private_key.rsa.public_key_openssh +} + +resource "local_file" "TF_private_key" { + content = tls_private_key.rsa.private_key_pem + filename = "tfkey.private" +} +resource "aws_security_group" "ssh_security_group" { + description = "security group to configure ports for ssh" + name_prefix = "ssh_security_group" +} + +# Modify the `ingress_rules` variable in the variables.tf file to allow the required ports for your CIDR ranges +resource "aws_security_group_rule" "ingress_rules" { + count = length(var.ingress_rules) + type = "ingress" + security_group_id = aws_security_group.ssh_security_group.id + from_port = var.ingress_rules[count.index].from_port + to_port = var.ingress_rules[count.index].to_port + protocol = var.ingress_rules[count.index].protocol + cidr_blocks = [var.ingress_rules[count.index].cidr_blocks] +} + +resource "aws_network_interface_sg_attachment" "sg_attachment" { + count = length(module.ec2-vm) + security_group_id = aws_security_group.ssh_security_group.id + network_interface_id = module.ec2-vm[count.index].primary_network_interface_id +} + +# Modify the `vm_count` variable in the variables.tf file to create the required number of EC2 instances +module "ec2-vm" { + count = var.vm_count + source = "intel/aws-vm/intel" + version = "1.3.3" + key_name = aws_key_pair.TF_key.key_name + instance_type = var.instance_type # Modify the instance type as required for your AI needs + availability_zone = var.availability_zone + ami = data.aws_ami.ubuntu-linux-2204.id + + # Size of VM disk in GB + root_block_device = [{ + volume_size = var.volume_size + }] + + tags = { + Name = "opea-vm-${random_id.rid.dec}" + } +} \ No newline at end of file diff --git a/cloud-service-provider/aws/ec2-vm/terraform/outputs.tf b/cloud-service-provider/aws/ec2-vm/terraform/outputs.tf new file mode 100644 index 000000000..24448ce71 --- /dev/null +++ b/cloud-service-provider/aws/ec2-vm/terraform/outputs.tf @@ -0,0 +1,113 @@ +output "id" { + description = "The ID of the instance" + value = try(module.ec2-vm.*.id, module.ec2-vm.*.id, "") +} + +output "arn" { + description = "The ARN of the instance" + value = try(module.ec2-vm.*.arn, "") +} + +output "capacity_reservation_specification" { + description = "Capacity reservation specification of the instance" + value = try(module.ec2-vm.*.capacity_reservation_specification, "") +} + +output "instance_state" { + description = "The state of the instance. One of: `pending`, `running`, `shutting-down`, `terminated`, `stopping`, `stopped`" + value = try(module.ec2-vm.*.instance_state, "") +} + +output "outpost_arn" { + description = "The ARN of the Outpost the instance is assigned to" + value = try(module.ec2-vm.*.outpost_arn, "") +} + +output "password_data" { + description = "Base-64 encoded encrypted password data for the instance. Useful for getting the administrator password for instances running Microsoft Windows. This attribute is only exported if `get_password_data` is true" + value = try(module.ec2-vm.*.password_data, "") +} + +output "primary_network_interface_id" { + description = "The ID of the instance's primary network interface" + value = try(module.ec2-vm.*.primary_network_interface_id, "") +} + +output "private_dns" { + description = "The private DNS name assigned to the instance. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC" + value = try(module.ec2-vm.*.private_dns, "") +} + +output "public_dns" { + description = "The public DNS name assigned to the instance. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC" + value = try(module.ec2-vm.*.public_dns, "") +} + +output "public_ip" { + description = "The public IP address assigned to the instance, if applicable. NOTE: If you are using an aws_eip with your instance, you should refer to the EIP's address directly and not use `public_ip` as this field will change after the EIP is attached" + value = try(module.ec2-vm.*.public_ip, "") +} + +output "private_ip" { + description = "The private IP address assigned to the instance." + value = try(module.ec2-vm.*.private_ip, "") +} + +output "ipv6_addresses" { + description = "The IPv6 address assigned to the instance, if applicable." + value = try(module.ec2-vm.*.ipv6_addresses, []) +} + +output "tags_all" { + description = "A map of tags assigned to the resource, including those inherited from the provider default_tags configuration block" + value = try(module.ec2-vm.*.tags_all, {}) +} + +output "spot_bid_status" { + description = "The current bid status of the Spot Instance Request" + value = try(module.ec2-vm.*.spot_bid_status, "") +} + +output "spot_request_state" { + description = "The current request state of the Spot Instance Request" + value = try(module.ec2-vm.*.spot_request_state, "") +} + +output "spot_instance_id" { + description = "The Instance ID (if any) that is currently fulfilling the Spot Instance request" + value = try(module.ec2-vm.*.spot_instance_id, "") +} + +################################################################################ +# IAM Role / Instance Profile +################################################################################ + +output "iam_role_name" { + description = "The name of the IAM role" + value = try(module.ec2-vm.*.aws_iam_role.name, null) +} + +output "iam_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = try(module.ec2-vm.*.aws_iam_role.arn, null) +} + +output "iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = try(module.ec2-vm.*.aws_iam_role.unique_id, null) +} + +output "iam_instance_profile_arn" { + description = "ARN assigned by AWS to the instance profile" + value = try(module.ec2-vm.*.aws_iam_instance_profile.arn, null) +} + +output "iam_instance_profile_id" { + description = "Instance profile's ID" + value = try(module.ec2-vm.*.aws_iam_instance_profile.id, null) +} + +output "iam_instance_profile_unique" { + description = "Stable and unique string identifying the IAM instance profile" + value = try(module.ec2-vm.*.aws_iam_instance_profile.unique_id, null) +} \ No newline at end of file diff --git a/cloud-service-provider/aws/ec2-vm/terraform/providers.tf b/cloud-service-provider/aws/ec2-vm/terraform/providers.tf new file mode 100644 index 000000000..260a2e35e --- /dev/null +++ b/cloud-service-provider/aws/ec2-vm/terraform/providers.tf @@ -0,0 +1,4 @@ +provider "aws" { + # Environment Variables used for Authentication + region = var.region +} \ No newline at end of file diff --git a/cloud-service-provider/aws/ec2-vm/terraform/variables.tf b/cloud-service-provider/aws/ec2-vm/terraform/variables.tf new file mode 100644 index 000000000..5373bd3cc --- /dev/null +++ b/cloud-service-provider/aws/ec2-vm/terraform/variables.tf @@ -0,0 +1,166 @@ +variable "region" { + description = "Target AWS region to deploy EC2 in." + type = string + default = "us-east-1" +} + +variable "availability_zone" { + description = "Target AWS availability zone to deploy EC2 in." + type = string + default = "us-east-1d" +} + +variable "instance_type" { + description = "EC2 instance type." + type = string + default = "c7i.16xlarge" +} + +variable "volume_size" { + description = "Size of VM disk in GB." + type = number + default = 600 +} + +################################################################################################## +### ### +### PLEASE CHANGE THE IP CIDR BLOCK on TO ALLOW SSH FROM YOUR OWN ALLOWED IP ADDRESS FOR SSH ### +### Use https://whatismyipaddress.com/ to get your IP address ### +################################################################################################## + +# Variable to add ingress rules to the security group. Replace the default values with the required ports and CIDR ranges. +variable "ingress_rules" { + type = list(object({ + from_port = number + to_port = number + protocol = string + cidr_blocks = string + })) + default = [ + { + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = "A.B.C.D/32" # Replace with your IP CIDR block Use https://whatismyipaddress.com/ to get your IP address + + }, + { + from_port = 6379 + to_port = 6379 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + + }, + { + from_port = 8001 + to_port = 8001 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + }, + { + from_port = 6006 + to_port = 6006 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + }, + { + from_port = 6007 + to_port = 6007 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + }, + { + from_port = 6000 + to_port = 6000 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + }, + { + from_port = 7000 + to_port = 7000 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + }, + { + from_port = 8808 + to_port = 8808 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + }, + { + from_port = 8000 + to_port = 8000 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + }, + { + from_port = 9009 + to_port = 9009 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + }, + { + from_port = 9000 + to_port = 9000 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + }, + { + from_port = 8888 + to_port = 8888 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + }, + { + from_port = 5173 + to_port = 5173 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + }, + { + from_port = 5174 + to_port = 5174 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + }, + { + from_port = 8399 + to_port = 8399 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + }, + { + from_port = 9399 + to_port = 9399 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + }, + + + { + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + }, + { + from_port = 8028 + to_port = 8028 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + }, + { + from_port = 7778 + to_port = 7778 + protocol = "tcp" + cidr_blocks = "0.0.0.0/0" + } + ] +} + +# Variable for how many VMs to build +variable "vm_count" { + description = "Number of VMs to build." + type = number + default = 1 +} \ No newline at end of file diff --git a/cloud-service-provider/aws/ec2-vm/terraform/versions.tf b/cloud-service-provider/aws/ec2-vm/terraform/versions.tf new file mode 100644 index 000000000..00e217417 --- /dev/null +++ b/cloud-service-provider/aws/ec2-vm/terraform/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">=1.3.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.31" + } + } +} \ No newline at end of file