diff --git a/.github/workflows/terraform.yml b/.github/workflows/terraform.yml
index 4377b977..94c9f790 100644
--- a/.github/workflows/terraform.yml
+++ b/.github/workflows/terraform.yml
@@ -13,7 +13,7 @@ jobs:
strategy:
matrix:
- provider: ['rancher/aws', 'rancher/azure', 'rancher/do', 'rancher/gcp', 'rancher/hcloud', 'rancher/linode', 'rancher/scw', 'rancher/outscale', 'neuvector/aws']
+ provider: ['rancher/aws', 'rancher/azure', 'rancher/do', 'rancher/gcp', 'rancher/hcloud', 'rancher/linode', 'rancher/scw', 'rancher/outscale', 'neuvector/aws', 'opni/aws']
# Use the Bash shell regardless whether the GitHub Actions runner is ubuntu-latest, macos-latest, or windows-latest
defaults:
diff --git a/Makefile b/Makefile
index 6b05c893..e526a4dd 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
-PROVIDERS = rancher/rancher-common rancher/aws rancher/azure rancher/do rancher/gcp rancher/hcloud rancher/linode rancher/scw rancher/outscale neuvector/aws rancher/harvester
-CLOUD_PROVIDERS = rancher/aws rancher/azure rancher/do rancher/gcp rancher/hcloud rancher/linode rancher/scw rancher/outscale neuvector/aws rancher/harvester
+PROVIDERS = rancher/rancher-common rancher/aws rancher/azure rancher/do rancher/gcp rancher/hcloud rancher/linode rancher/scw rancher/outscale neuvector/aws rancher/harvester opni/aws
+CLOUD_PROVIDERS = rancher/aws rancher/azure rancher/do rancher/gcp rancher/hcloud rancher/linode rancher/scw rancher/outscale neuvector/aws rancher/harvester opni/aws
upgrade-targets = $(addprefix upgrade-, $(PROVIDERS))
docs-targets = $(addprefix docs-, $(PROVIDERS))
diff --git a/opni/aws/.terraform.lock.hcl b/opni/aws/.terraform.lock.hcl
new file mode 100644
index 00000000..6800cab5
--- /dev/null
+++ b/opni/aws/.terraform.lock.hcl
@@ -0,0 +1,127 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/hashicorp/aws" {
+ version = "4.17.0"
+ constraints = "4.17.0"
+ hashes = [
+ "h1:5NfNH5iQ3VGpMwjlSOkQyBYh8mvKH3QMp0UHB71eTqc=",
+ "h1:FtkENM8QDK6CLhBr4k97kx26G2CqHZk9q5sNhHcnYRc=",
+ "zh:2cc932fb0af13850de3c60a5318b695c82973489c140ca4f13218f69136c36e5",
+ "zh:4018884d66acfa8273f7100ef0334004ed8a3790ffc7621eaef65d1d9c3fab43",
+ "zh:6a7769e5c81e543f5deaaa8596e45f92244a61f5026c8c66d5bf55f2a7fd4801",
+ "zh:7956c1e17ec7647af3612cc98cbcd21d50b2d9f5e41c676b62ee214f5610c29f",
+ "zh:833d9d608dbffda7da565004ef592a8a364e96b5c13cacf873f5d32714e197ff",
+ "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
+ "zh:a55b8b72e47999d9c7aecaa009797ed7eb3f669a719d3f6127ee5e0f1b91ecc2",
+ "zh:a6f2377d71dfba9669f060e687498e589b490366026821bd83451ac9ef0cd9e8",
+ "zh:b006aa281097b3db27a62ea3c8cfaf4c4d979d57f4a6c180bd2da3d0ab4bd61a",
+ "zh:d6a6d29256fee6c3b35384719c84c19b13dcccc53bffce5f529023607d130d0b",
+ "zh:edc155e147883872e1227aa6a4ef3205fa9de421475d96c20a34a5eaff3df01f",
+ "zh:f25773dcc00dead0412e222cf3891ac6228dcb4d69da9bacfca305a0a2a1db56",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/helm" {
+ version = "2.5.1"
+ constraints = "2.5.1"
+ hashes = [
+ "h1:9yMFsXyHAo+mUuMKczNSw44HcZaf1JkMqgOUgJF1dXs=",
+ "h1:a9KwjqINdNy6IsEbkHUB1vwvYfy5OJ2VxFL9/NDFLoY=",
+ "zh:140b9748f0ad193a20d69e59d672f3c4eda8a56cede56a92f931bd3af020e2e9",
+ "zh:17ae319466ed6538ad49e011998bb86565fe0e97bc8b9ad7c8dda46a20f90669",
+ "zh:3a8bd723c21ba70e19f0395ed7096fc8e08bfc23366f1c3f06a9107eb37c572c",
+ "zh:3aae3b82adbe6dca52f1a1c8cf51575446e6b0f01f1b1f3b30de578c9af4a933",
+ "zh:3f65221f40148df57d2888e4f31ef3bf430b8c5af41de0db39a2b964e1826d7c",
+ "zh:650c74c4f46f5eb01df11d8392bdb7ebee3bba59ac0721000a6ad731ff0e61e2",
+ "zh:930fb8ab4cd6634472dfd6aa3123f109ef5b32cbe6ef7b4695fae6751353e83f",
+ "zh:ae57cd4b0be4b9ca252bc5d347bc925e35b0ed74d3dcdebf06c11362c1ac3436",
+ "zh:d15b1732a8602b6726eac22628b2f72f72d98b75b9c6aabceec9fd696fda696a",
+ "zh:d730ede1656bd193e2aea5302acec47c4905fe30b96f550196be4a0ed5f41936",
+ "zh:f010d4f9d8cd15936be4df12bf256cb2175ca1dedb728bd3a866c03d2ee7591f",
+ "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/local" {
+ version = "2.2.3"
+ constraints = "2.2.3"
+ hashes = [
+ "h1:FvRIEgCmAezgZUqb2F+PZ9WnSSnR5zbEM2ZI+GLmbMk=",
+ "h1:KmHz81iYgw9Xn2L3Carc2uAzvFZ1XsE7Js3qlVeC77k=",
+ "zh:04f0978bb3e052707b8e82e46780c371ac1c66b689b4a23bbc2f58865ab7d5c0",
+ "zh:6484f1b3e9e3771eb7cc8e8bab8b35f939a55d550b3f4fb2ab141a24269ee6aa",
+ "zh:78a56d59a013cb0f7eb1c92815d6eb5cf07f8b5f0ae20b96d049e73db915b238",
+ "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
+ "zh:8aa9950f4c4db37239bcb62e19910c49e47043f6c8587e5b0396619923657797",
+ "zh:996beea85f9084a725ff0e6473a4594deb5266727c5f56e9c1c7c62ded6addbb",
+ "zh:9a7ef7a21f48fabfd145b2e2a4240ca57517ad155017e86a30860d7c0c109de3",
+ "zh:a63e70ac052aa25120113bcddd50c1f3cfe61f681a93a50cea5595a4b2cc3e1c",
+ "zh:a6e8d46f94108e049ad85dbed60354236dc0b9b5ec8eabe01c4580280a43d3b8",
+ "zh:bb112ce7efbfcfa0e65ed97fa245ef348e0fd5bfa5a7e4ab2091a9bd469f0a9e",
+ "zh:d7bec0da5c094c6955efed100f3fe22fca8866859f87c025be1760feb174d6d9",
+ "zh:fb9f271b72094d07cef8154cd3d50e9aa818a0ea39130bc193132ad7b23076fd",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/tls" {
+ version = "3.4.0"
+ constraints = "3.4.0"
+ hashes = [
+ "h1:QpJxHEQt5369EnAZ10+8MnvJ0TktFA0oWbRe6lzvb+s=",
+ "h1:fSRc/OyRitbAST9vE+mEcmgJiDp+Jx8pGPbUUeYEQRc=",
+ "zh:2442a0df0cfb550b8eba9b2af39ac06f54b62447eb369ecc6b1c29f739b33bbb",
+ "zh:3ebb82cacb677a099de55f844f0d02886bc804b1a2b94441bc40fabcb64d2a38",
+ "zh:436125c2a7e66bc62a4a7c68bdca694f071d7aa894e8637dc83f4a68fe322546",
+ "zh:5f03db9f1d77e8274ff4750ae32d5c16c42b862b06bcb0683e4d733c8db922e4",
+ "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
+ "zh:8190142ae8a539ab34193b7e75da0fa04035d1dcd8af8be94df1eafeeffb44b6",
+ "zh:8cdc7cd9221e27c189e5beaf78462fce4c2edb081f415a1eafc6da2949de31e2",
+ "zh:a5de0f7f5d63c59ebf61d3c1d94040f410665ff0aa04f66674efe24b39a11f94",
+ "zh:a9fce48db3c140cc3e06f8a3c7ef4d36735e457e7660442d6d5dcd2b0781adc3",
+ "zh:beb92de584c790c7c7f047e45ccd22b6ee3263c7b5a91ae4d6882ae6e7700570",
+ "zh:f373f8cc52846fb513f44f468d885f722ca4dc22af9ff1942368cafd16b796b3",
+ "zh:f69627fd6e5a920b17ff423cdbad2715078ca6d13146dc67668795582ab43748",
+ ]
+}
+
+provider "registry.terraform.io/loafoe/ssh" {
+ version = "1.2.0"
+ constraints = "1.2.0"
+ hashes = [
+ "h1:Asn3o5hOS1CoNahhIDYLsFY9PmrDC+9EP5xF9Yw33Bc=",
+ "h1:L9AYfDrWk2nVMXTqk30z+iidgz0X3VIxCepQZX2ZSMk=",
+ "zh:0af31d1d2d4b6a71669a8c18654deb14fa32522ffdef4dfde62ac151f89f389b",
+ "zh:0d3b7ad68e886936d47626143c42ccb922a34aaab8b1e7d4f42ce077be307765",
+ "zh:228270701fcc507fb8b8a698451aa26b02c45a976b841e07c973556cf2406e53",
+ "zh:32de82d3fcd009a6a354c0468769f1442f01836a34f6b4578b4da054f5ecf102",
+ "zh:3f7fe1dad01cb05c29f1f6e6033891e66a21f2bc2a4fe1da6096b33f8889521a",
+ "zh:796115f0b374dece64ae47b7fa680d0ffce37a3d837de5c7145a41bf3b7c793f",
+ "zh:88c63a174435e2b9b6561187ae87cdeebaa9ffb86f61dd546fbd0e1c4beff6d6",
+ "zh:a73b8573a81be7ca021b4d65883f4058337d1a4d10a56792dc80cacbfea12031",
+ "zh:c2a45a6cfd301467139a8c1f533fa92163128d35998e7191b68a8fd103160e62",
+ "zh:ebef9e003bdb977fd51c4260cb350b62e1a93d746971d8a9ad46b43500066a3f",
+ "zh:ef8e621d7732b120e1af1dfa08f9b983d0d93de28f0e269afa7fd50a66436477",
+ ]
+}
+
+provider "registry.terraform.io/rancher/rancher2" {
+ version = "1.24.0"
+ constraints = "1.24.0"
+ hashes = [
+ "h1:XWrsT6bqmswE61stocRBu0CsuXPRHqs7xS30ktnM2Kg=",
+ "h1:o/PR3abotMQek+SPSp7SnwB5/nSagEw7CorPgNU1n0k=",
+ "zh:0278e7eca669b10082c7c0fd2037449e325d5f63db5881fd1a9b0c1cdf3be0bc",
+ "zh:19ef195b8af98deb2789533b05edf3870d49cdf82d6e07d197e9579bd77a0ffd",
+ "zh:3b875842e04b8205f018b5fbf481c0cfb69e2d1aae8b4b70323b60b1d03d2a7b",
+ "zh:6b7d4d6bb9c15fe6af369216afcf78020fdfbfbdebac7b158c8a1cde1278f38b",
+ "zh:a72d6438b7adfcc2327357bb4137ad65117e87db5ec463d2d9ed4a414d111a5b",
+ "zh:ad057167ddb5fc2126700328d41ba899db963d653e214c94d4f48b6f1e7b71b4",
+ "zh:b11dcb4adee3bd5a6a9118fe75709f3fb16b646eb022e21a8ea54d58ba3ebbcd",
+ "zh:b3516d8531e45cd9084fd12c00b94a0163b8d2cca7571ff68a8fe999a85232a5",
+ "zh:bc192ac3c7e98d5ad9532dd81ed29eb63c82fe8472dfc86b2807ff6182c95264",
+ "zh:cea331226092db9d6b7d45739293f2484c8811213636b07ca7c94a5d3592a925",
+ "zh:f26a9ebadbee87588166e519e1d74d14483a8188acc7b5c61809efb3c72f82c8",
+ "zh:f6705e74b669538280e9f1c9bce57a296497d7f17a7231dc9aaf95b89b3668a2",
+ ]
+}
diff --git a/opni/aws/README.md b/opni/aws/README.md
new file mode 100644
index 00000000..2f674597
--- /dev/null
+++ b/opni/aws/README.md
@@ -0,0 +1,106 @@
+# AWS Opni Quickstart
+
+This will create a single node RKE2 cluster running on an EC2 instance with SLES 15 and install Opni and Rancher into the cluster.
+The instance will have wide-open security groups and will be accessible over SSH using the SSH keys
+`id_rsa` and `id_rsa.pub` keys generated by terraform.
+
+It will create a second single node RKE2 cluster and join this then to Rancher and Opni.
+
+The opni dashboard login credentials are admin / ooPhe4oh
+
+Refer to [Requirements/Using Cloud Quickstarts](https://github.com/rancher/quickstart#requirements---cloud) to get started.
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.0.0 |
+| [aws](#requirement\_aws) | 4.17.0 |
+| [helm](#requirement\_helm) | 2.5.1 |
+| [local](#requirement\_local) | 2.2.3 |
+| [rancher2](#requirement\_rancher2) | 1.24.0 |
+| [ssh](#requirement\_ssh) | 1.2.0 |
+| [tls](#requirement\_tls) | 3.4.0 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | 4.17.0 |
+| [helm](#provider\_helm) | 2.5.1 |
+| [local](#provider\_local) | 2.2.3 |
+| [rancher2.bootstrap](#provider\_rancher2.bootstrap) | 1.24.0 |
+| [ssh](#provider\_ssh) | 1.2.0 |
+| [tls](#provider\_tls) | 3.4.0 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_elb.opni-lb](https://registry.terraform.io/providers/hashicorp/aws/4.17.0/docs/resources/elb) | resource |
+| [aws_instance.opni_server](https://registry.terraform.io/providers/hashicorp/aws/4.17.0/docs/resources/instance) | resource |
+| [aws_internet_gateway.opni_gateway](https://registry.terraform.io/providers/hashicorp/aws/4.17.0/docs/resources/internet_gateway) | resource |
+| [aws_key_pair.quickstart_key_pair](https://registry.terraform.io/providers/hashicorp/aws/4.17.0/docs/resources/key_pair) | resource |
+| [aws_route_table.opni_route_table](https://registry.terraform.io/providers/hashicorp/aws/4.17.0/docs/resources/route_table) | resource |
+| [aws_route_table_association.opni_route_table_association](https://registry.terraform.io/providers/hashicorp/aws/4.17.0/docs/resources/route_table_association) | resource |
+| [aws_security_group.opni_sg_allowall](https://registry.terraform.io/providers/hashicorp/aws/4.17.0/docs/resources/security_group) | resource |
+| [aws_subnet.opni_subnet](https://registry.terraform.io/providers/hashicorp/aws/4.17.0/docs/resources/subnet) | resource |
+| [aws_vpc.opni_vpc](https://registry.terraform.io/providers/hashicorp/aws/4.17.0/docs/resources/vpc) | resource |
+| [helm_release.cert_manager](https://registry.terraform.io/providers/hashicorp/helm/2.5.1/docs/resources/release) | resource |
+| [helm_release.longhorn](https://registry.terraform.io/providers/hashicorp/helm/2.5.1/docs/resources/release) | resource |
+| [helm_release.opni](https://registry.terraform.io/providers/hashicorp/helm/2.5.1/docs/resources/release) | resource |
+| [helm_release.opni-config](https://registry.terraform.io/providers/hashicorp/helm/2.5.1/docs/resources/release) | resource |
+| [helm_release.opni-crd](https://registry.terraform.io/providers/hashicorp/helm/2.5.1/docs/resources/release) | resource |
+| [helm_release.rancher_server](https://registry.terraform.io/providers/hashicorp/helm/2.5.1/docs/resources/release) | resource |
+| [local_file.kube_config_server_yaml](https://registry.terraform.io/providers/hashicorp/local/2.2.3/docs/resources/file) | resource |
+| [local_file.ssh_public_key_openssh](https://registry.terraform.io/providers/hashicorp/local/2.2.3/docs/resources/file) | resource |
+| [local_sensitive_file.ssh_private_key_pem](https://registry.terraform.io/providers/hashicorp/local/2.2.3/docs/resources/sensitive_file) | resource |
+| [rancher2_bootstrap.admin](https://registry.terraform.io/providers/rancher/rancher2/1.24.0/docs/resources/bootstrap) | resource |
+| [ssh_resource.check_installation](https://registry.terraform.io/providers/loafoe/ssh/1.2.0/docs/resources/resource) | resource |
+| [ssh_resource.install_rke2_0](https://registry.terraform.io/providers/loafoe/ssh/1.2.0/docs/resources/resource) | resource |
+| [ssh_resource.install_rke2_1](https://registry.terraform.io/providers/loafoe/ssh/1.2.0/docs/resources/resource) | resource |
+| [ssh_resource.install_rke2_2](https://registry.terraform.io/providers/loafoe/ssh/1.2.0/docs/resources/resource) | resource |
+| [ssh_resource.retrieve_config](https://registry.terraform.io/providers/loafoe/ssh/1.2.0/docs/resources/resource) | resource |
+| [ssh_resource.rke2_config_dir](https://registry.terraform.io/providers/loafoe/ssh/1.2.0/docs/resources/resource) | resource |
+| [ssh_resource.rke2_config_initial](https://registry.terraform.io/providers/loafoe/ssh/1.2.0/docs/resources/resource) | resource |
+| [ssh_resource.rke2_config_others](https://registry.terraform.io/providers/loafoe/ssh/1.2.0/docs/resources/resource) | resource |
+| [tls_private_key.global_key](https://registry.terraform.io/providers/hashicorp/tls/3.4.0/docs/resources/private_key) | resource |
+| [aws_ami.sles](https://registry.terraform.io/providers/hashicorp/aws/4.17.0/docs/data-sources/ami) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [aws\_access\_key](#input\_aws\_access\_key) | AWS access key used to create infrastructure | `string` | n/a | yes |
+| [aws\_secret\_key](#input\_aws\_secret\_key) | AWS secret key used to create AWS infrastructure | `string` | n/a | yes |
+| [rke2\_token](#input\_rke2\_token) | RKE2 token for joining the cluster | `string` | n/a | yes |
+| [aws\_region](#input\_aws\_region) | AWS region used for all resources | `string` | `"us-east-1"` | no |
+| [aws\_session\_token](#input\_aws\_session\_token) | AWS session token used to create AWS infrastructure | `string` | `""` | no |
+| [aws\_zone](#input\_aws\_zone) | AWS zone used for all resources | `string` | `"us-east-1b"` | no |
+| [cert\_manager\_version](#input\_cert\_manager\_version) | Version of cert-manager to install alongside Rancher and Opni (format: 0.0.0) | `string` | `"1.12.0"` | no |
+| [instance\_type](#input\_instance\_type) | Instance type used for all EC2 instances | `string` | `"t3a.2xlarge"` | no |
+| [kubernetes\_version](#input\_kubernetes\_version) | Kubernetes version to use | `string` | `"v1.25.11+rke2r1"` | no |
+| [longhorn\_version](#input\_longhorn\_version) | Longhorn version | `string` | `"1.5.1"` | no |
+| [opni\_cluster\_node\_count](#input\_opni\_cluster\_node\_count) | Amount of nodes in the Opni cluster | `number` | `3` | no |
+| [opni\_version](#input\_opni\_version) | Opni version | `string` | `"0.10.0"` | no |
+| [prefix](#input\_prefix) | Prefix added to names of all resources | `string` | `"opni-quickstart"` | no |
+| [rancher\_helm\_repository](#input\_rancher\_helm\_repository) | The helm repository, where the Rancher helm chart is installed from | `string` | `"https://releases.rancher.com/server-charts/latest"` | no |
+| [rancher\_server\_admin\_password](#input\_rancher\_server\_admin\_password) | Admin password to use for Rancher server bootstrap, min. 12 characters | `string` | `"adminadminadmin"` | no |
+| [rancher\_version](#input\_rancher\_version) | Rancher version | `string` | `"2.7.5"` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [grafana\_url](#output\_grafana\_url) | n/a |
+| [node\_ips](#output\_node\_ips) | n/a |
+| [opensearch\_url](#output\_opensearch\_url) | n/a |
+| [opni\_url](#output\_opni\_url) | n/a |
+| [opniadmin\_url](#output\_opniadmin\_url) | n/a |
+| [rancher\_url](#output\_rancher\_url) | n/a |
+
diff --git a/opni/aws/cert-manager.tf b/opni/aws/cert-manager.tf
new file mode 100644
index 00000000..17e3cdf6
--- /dev/null
+++ b/opni/aws/cert-manager.tf
@@ -0,0 +1,14 @@
+resource "helm_release" "cert_manager" {
+ depends_on = [local_file.kube_config_server_yaml]
+
+ name = "cert-manager"
+ chart = "https://charts.jetstack.io/charts/cert-manager-v${var.cert_manager_version}.tgz"
+ namespace = "cert-manager"
+ create_namespace = true
+ wait = true
+
+ set {
+ name = "installCRDs"
+ value = "true"
+ }
+}
diff --git a/opni/aws/data.tf b/opni/aws/data.tf
new file mode 100644
index 00000000..2ec860aa
--- /dev/null
+++ b/opni/aws/data.tf
@@ -0,0 +1,25 @@
+# Use latest SLES 15 SP3
+data "aws_ami" "sles" {
+ most_recent = true
+ owners = ["013907871322"] # SUSE
+
+ filter {
+ name = "name"
+ values = ["suse-sles-15-sp3*"]
+ }
+
+ filter {
+ name = "virtualization-type"
+ values = ["hvm"]
+ }
+
+ filter {
+ name = "architecture"
+ values = ["x86_64"]
+ }
+
+ filter {
+ name = "root-device-type"
+ values = ["ebs"]
+ }
+}
diff --git a/opni/aws/infra.tf b/opni/aws/infra.tf
new file mode 100644
index 00000000..12ccae3c
--- /dev/null
+++ b/opni/aws/infra.tf
@@ -0,0 +1,134 @@
+# AWS infrastructure resources
+
+resource "tls_private_key" "global_key" {
+ algorithm = "RSA"
+ rsa_bits = 2048
+}
+
+resource "local_sensitive_file" "ssh_private_key_pem" {
+ filename = "${path.module}/id_rsa"
+ content = tls_private_key.global_key.private_key_pem
+ file_permission = "0600"
+}
+
+resource "local_file" "ssh_public_key_openssh" {
+ filename = "${path.module}/id_rsa.pub"
+ content = tls_private_key.global_key.public_key_openssh
+}
+
+# Temporary key pair used for SSH accesss
+resource "aws_key_pair" "quickstart_key_pair" {
+ key_name_prefix = "${var.prefix}-opni-"
+ public_key = tls_private_key.global_key.public_key_openssh
+}
+
+resource "aws_vpc" "opni_vpc" {
+ cidr_block = "10.0.0.0/16"
+ enable_dns_hostnames = true
+ tags = {
+ Name = "${var.prefix}-opni-vpc"
+ }
+}
+
+resource "aws_internet_gateway" "opni_gateway" {
+ vpc_id = aws_vpc.opni_vpc.id
+
+ tags = {
+ Name = "${var.prefix}-opni-gateway"
+ }
+}
+
+resource "aws_subnet" "opni_subnet" {
+ vpc_id = aws_vpc.opni_vpc.id
+
+ cidr_block = "10.0.0.0/24"
+ availability_zone = var.aws_zone
+
+ tags = {
+ Name = "${var.prefix}-opni-subnet"
+ }
+}
+
+resource "aws_route_table" "opni_route_table" {
+ vpc_id = aws_vpc.opni_vpc.id
+
+ route {
+ cidr_block = "0.0.0.0/0"
+ gateway_id = aws_internet_gateway.opni_gateway.id
+ }
+
+ tags = {
+ Name = "${var.prefix}-opni-route-table"
+ }
+}
+
+resource "aws_route_table_association" "opni_route_table_association" {
+ subnet_id = aws_subnet.opni_subnet.id
+ route_table_id = aws_route_table.opni_route_table.id
+}
+
+
+# Security group to allow all traffic
+resource "aws_security_group" "opni_sg_allowall" {
+ name = "${var.prefix}-opni-allowall"
+ description = "opni quickstart - allow all traffic"
+ vpc_id = aws_vpc.opni_vpc.id
+
+ ingress {
+ from_port = "0"
+ to_port = "0"
+ protocol = "-1"
+ cidr_blocks = ["0.0.0.0/0"]
+ }
+
+ egress {
+ from_port = "0"
+ to_port = "0"
+ protocol = "-1"
+ cidr_blocks = ["0.0.0.0/0"]
+ }
+
+ tags = {
+ Creator = "opni-quickstart"
+ }
+}
+
+# AWS EC2 instance for creating a single node RKE cluster and installing the RKE2 cluster, Rancher and opni
+resource "aws_instance" "opni_server" {
+ count = var.opni_cluster_node_count
+
+ depends_on = [
+ aws_route_table_association.opni_route_table_association
+ ]
+ ami = data.aws_ami.sles.id
+ instance_type = var.instance_type
+
+ key_name = aws_key_pair.quickstart_key_pair.key_name
+ vpc_security_group_ids = [aws_security_group.opni_sg_allowall.id]
+ subnet_id = aws_subnet.opni_subnet.id
+ associate_public_ip_address = true
+
+ root_block_device {
+ volume_size = 200
+ }
+
+ provisioner "remote-exec" {
+ inline = [
+ "echo 'Waiting for cloud-init to complete...'",
+ "cloud-init status --wait > /dev/null",
+ "echo 'Completed cloud-init!'",
+ ]
+
+ connection {
+ type = "ssh"
+ host = self.public_ip
+ user = local.node_username
+ private_key = tls_private_key.global_key.private_key_pem
+ }
+ }
+
+ tags = {
+ Name = "${var.prefix}-opni-server"
+ Creator = "opni-quickstart"
+ }
+}
diff --git a/opni/aws/lb.tf b/opni/aws/lb.tf
new file mode 100644
index 00000000..b0d1b87e
--- /dev/null
+++ b/opni/aws/lb.tf
@@ -0,0 +1,41 @@
+resource "aws_elb" "opni-lb" {
+ name = "${var.prefix}-opni-lb"
+ subnets = [aws_subnet.opni_subnet.id]
+ security_groups = [aws_security_group.opni_sg_allowall.id]
+
+ health_check {
+ healthy_threshold = 2
+ unhealthy_threshold = 2
+ timeout = 3
+ target = "TCP:32000"
+ interval = 30
+ }
+
+ listener {
+ instance_port = 32090
+ instance_protocol = "tcp"
+ lb_port = 9090
+ lb_protocol = "tcp"
+ }
+
+ listener {
+ instance_port = 32000
+ instance_protocol = "tcp"
+ lb_port = 4000
+ lb_protocol = "tcp"
+ }
+
+ instances = [
+ aws_instance.opni_server[0].id,
+ aws_instance.opni_server[1].id,
+ aws_instance.opni_server[2].id
+ ]
+ cross_zone_load_balancing = true
+ idle_timeout = 400
+ connection_draining = true
+ connection_draining_timeout = 400
+
+ tags = {
+ Name = "${var.prefix}-opni-lb"
+ }
+}
diff --git a/opni/aws/longhorn.tf b/opni/aws/longhorn.tf
new file mode 100644
index 00000000..eaf81654
--- /dev/null
+++ b/opni/aws/longhorn.tf
@@ -0,0 +1,7 @@
+resource "helm_release" "longhorn" {
+ name = "longhorn"
+ chart = "https://github.com/longhorn/charts/releases/download/longhorn-${var.longhorn_version}/longhorn-${var.longhorn_version}.tgz"
+ namespace = "longhorn-system"
+ create_namespace = true
+ wait = true
+}
diff --git a/opni/aws/opni-config/Chart.yaml b/opni/aws/opni-config/Chart.yaml
new file mode 100644
index 00000000..d7769cc2
--- /dev/null
+++ b/opni/aws/opni-config/Chart.yaml
@@ -0,0 +1,5 @@
+apiVersion: v2
+name: opni-config
+type: application
+version: 0.1.0
+appVersion: 0.1.0
diff --git a/opni/aws/opni-config/templates/clusterissuer.yaml b/opni/aws/opni-config/templates/clusterissuer.yaml
new file mode 100644
index 00000000..cc23a476
--- /dev/null
+++ b/opni/aws/opni-config/templates/clusterissuer.yaml
@@ -0,0 +1,6 @@
+apiVersion: cert-manager.io/v1
+kind: ClusterIssuer
+metadata:
+ name: selfsigned-cluster-issuer
+spec:
+ selfSigned: {}
diff --git a/opni/aws/opni-config/templates/ingress-grafana.yaml b/opni/aws/opni-config/templates/ingress-grafana.yaml
new file mode 100644
index 00000000..f509dab1
--- /dev/null
+++ b/opni/aws/opni-config/templates/ingress-grafana.yaml
@@ -0,0 +1,23 @@
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: grafana
+ namespace: opni
+ annotations:
+ cert-manager.io/cluster-issuer: selfsigned-cluster-issuer
+spec:
+ tls:
+ - hosts:
+ - {{ .Values.grafanaHostname }}
+ secretName: grafana-tls-secret
+ rules:
+ - host: {{ .Values.grafanaHostname }}
+ http:
+ paths:
+ - backend:
+ service:
+ name: grafana-service
+ port:
+ number: 3000
+ path: /
+ pathType: Prefix
diff --git a/opni/aws/opni-config/templates/ingress-opensearch.yaml b/opni/aws/opni-config/templates/ingress-opensearch.yaml
new file mode 100644
index 00000000..3770a936
--- /dev/null
+++ b/opni/aws/opni-config/templates/ingress-opensearch.yaml
@@ -0,0 +1,24 @@
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: opensearch
+ namespace: opni
+ annotations:
+ cert-manager.io/cluster-issuer: selfsigned-cluster-issuer
+ nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
+spec:
+ tls:
+ - hosts:
+ - {{ .Values.opensearchHostname }}
+ secretName: opensearch-tls-secret
+ rules:
+ - host: {{ .Values.opensearchHostname }}
+ http:
+ paths:
+ - backend:
+ service:
+ name: opni-opensearch-svc-dashboards
+ port:
+ number: 5601
+ path: /
+ pathType: Prefix
diff --git a/opni/aws/opni-config/templates/ingress-opni-admin.yaml b/opni/aws/opni-config/templates/ingress-opni-admin.yaml
new file mode 100644
index 00000000..3248bfae
--- /dev/null
+++ b/opni/aws/opni-config/templates/ingress-opni-admin.yaml
@@ -0,0 +1,34 @@
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: opni-admin
+ namespace: opni
+ annotations:
+ cert-manager.io/cluster-issuer: selfsigned-cluster-issuer
+ nginx.ingress.kubernetes.io/auth-type: basic
+ nginx.ingress.kubernetes.io/auth-secret: opni-admin-http-auth
+spec:
+ tls:
+ - hosts:
+ - {{ .Values.opniAdminHostname }}
+ secretName: opni-admin-tls-secret
+ rules:
+ - host: {{ .Values.opniAdminHostname }}
+ http:
+ paths:
+ - backend:
+ service:
+ name: opni-admin-dashboard
+ port:
+ number: 12080
+ path: /
+ pathType: Prefix
+---
+apiVersion: v1
+kind: Secret
+metadata:
+ name: opni-admin-http-auth
+ namespace: opni
+stringData:
+ # admin / ooPhe4oh
+ auth: "admin:$apr1$qgGLG/b9$513sqc4ke.IVzBcesFjCX/"
\ No newline at end of file
diff --git a/opni/aws/opni-config/templates/navlink.yaml b/opni/aws/opni-config/templates/navlink.yaml
new file mode 100644
index 00000000..d7f0ddeb
--- /dev/null
+++ b/opni/aws/opni-config/templates/navlink.yaml
@@ -0,0 +1,29 @@
+apiVersion: ui.cattle.io/v1
+kind: NavLink
+metadata:
+ name: opni
+spec:
+ group: Opni
+ label: Opni Dashboard
+ target: _blank
+ toURL: https://{{ .Values.opniHostname }}
+---
+apiVersion: ui.cattle.io/v1
+kind: NavLink
+metadata:
+ name: grafana
+spec:
+ group: Opni
+ label: Grafana
+ target: _blank
+ toURL: https://{{ .Values.grafanaHostname }}
+---
+apiVersion: ui.cattle.io/v1
+kind: NavLink
+metadata:
+ name: opensearch
+spec:
+ group: Opni
+ label: OpenSearch
+ target: _blank
+ toURL: https://{{ .Values.opensearchHostname }}
diff --git a/opni/aws/opni-config/templates/opni-np-svc.yaml b/opni/aws/opni-config/templates/opni-np-svc.yaml
new file mode 100644
index 00000000..2c1c5b94
--- /dev/null
+++ b/opni/aws/opni-config/templates/opni-np-svc.yaml
@@ -0,0 +1,20 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: opni-np
+ namespace: opni
+spec:
+ ports:
+ - name: grpc
+ port: 9090
+ nodePort: 32090
+ protocol: TCP
+ targetPort: grpc
+ - name: noauth
+ port: 4000
+ nodePort: 32000
+ protocol: TCP
+ targetPort: noauth
+ selector:
+ app.kubernetes.io/name: opni-gateway
+ type: NodePort
diff --git a/opni/aws/opni-config/values.yaml b/opni/aws/opni-config/values.yaml
new file mode 100644
index 00000000..90c6da88
--- /dev/null
+++ b/opni/aws/opni-config/values.yaml
@@ -0,0 +1,3 @@
+opensearchHostname: ""
+grafanaHostname: ""
+opniAdminHostname: ""
\ No newline at end of file
diff --git a/opni/aws/opni-configs.tf b/opni/aws/opni-configs.tf
new file mode 100644
index 00000000..5751d20a
--- /dev/null
+++ b/opni/aws/opni-configs.tf
@@ -0,0 +1,17 @@
+resource "helm_release" "opni-config" {
+ depends_on = [helm_release.rancher_server, helm_release.cert_manager, helm_release.opni]
+ name = "opni-config"
+ chart = "./opni-config"
+ set {
+ name = "opensearchHostname"
+ value = local.opensearch_hostname
+ }
+ set {
+ name = "grafanaHostname"
+ value = local.grafana_hostname
+ }
+ set {
+ name = "opniAdminHostname"
+ value = local.opniadmin_hostname
+ }
+}
diff --git a/opni/aws/opni.tf b/opni/aws/opni.tf
new file mode 100644
index 00000000..e36613a8
--- /dev/null
+++ b/opni/aws/opni.tf
@@ -0,0 +1,71 @@
+resource "helm_release" "opni-crd" {
+ name = "opni-crd"
+ chart = "https://raw.githubusercontent.com/rancher/opni/charts-repo/assets/opni-crd/opni-crd-${var.opni_version}.tgz"
+ namespace = "opni"
+ create_namespace = true
+ wait = true
+}
+
+resource "helm_release" "opni" {
+ depends_on = [
+ helm_release.cert_manager,
+ helm_release.opni-crd,
+ helm_release.rancher_server,
+ helm_release.longhorn,
+ ]
+
+ name = "opni"
+ chart = "https://raw.githubusercontent.com/rancher/opni/charts-repo/assets/opni/opni-${var.opni_version}.tgz"
+ namespace = "opni"
+ create_namespace = true
+ wait = true
+
+ values = [
+ <