feat: add invista nexus vendor infrastructure docs

This commit is contained in:
Tiago Ribeiro 2026-03-09 11:24:00 -03:00
parent ebd8d264b1
commit 849ccbe616
62 changed files with 4132 additions and 11 deletions

3
.gitignore vendored
View file

@ -2,3 +2,6 @@
credentials/*
!credentials/.gitignore
!credentials/README.md
# Terraform variable files can carry environment-specific secrets and IDs
*.tfvars

View file

@ -4,9 +4,9 @@ Documento de referencia rapida para acessos de infraestrutura.
## Ultima validacao
- Data: 2026-03-05
- Data: 2026-03-09
- Script: `python scripts/check-connections.py`
- Resultado: `20` verificacoes, `14` OK, `6` erros
- Resultado: `20` verificacoes, `13` OK, `7` erros
- Artefato: `scripts/connection-status.json`
## Resumo de acessos
@ -27,8 +27,8 @@ Documento de referencia rapida para acessos de infraestrutura.
| Cloudflare | inventcloud | OK | 3 zonas |
| MXRoute | api | OK | HTTP 200 |
| OCI | namespace | OK | Namespace `grbb7qzeuoag` |
| Kubernetes | cluster-info | ERRO | `kubectl cluster-info` sem retorno valido |
| Object Storage | civo | OK | Bucket acessivel |
| Kubernetes | cluster-info | OK | Funcional via desktop |
| Object Storage | civo | ERRO | Verificando via boto3 |
| Object Storage | euronodes | OK | Bucket acessivel |
## OCI
@ -37,15 +37,14 @@ Conexao OCI esta funcional, com namespace retornado:
- `grbb7qzeuoag`
Pendencia detectada na auditoria:
Auditoria de Seguranca:
- ✅ Permissoes dos arquivos `~/.oci/config` e `~/.oci/api_key.pem` corrigidas em 2026-03-09.
- Permissao de arquivos `C:\Users\TiagoRibeiro\.oci\config` e `C:\Users\TiagoRibeiro\.oci\api_key.pem` muito aberta.
- Correcao recomendada:
## Suporte Fornecedores (Vendor)
```powershell
oci setup repair-file-permissions --file C:\Users\TiagoRibeiro\.oci\config
oci setup repair-file-permissions --file C:\Users\TiagoRibeiro\.oci\api_key.pem
```
| Fornecedor | Projeto | Recurso | Localizacao |
|------------|---------|---------|-------------|
| Oracle | Invista Nexus | Fix Unauthorized | `inventcloud/invista/nexus/vendor/oracle/Fix-Unauthorized` |
## Vault de chaves (Civo)

View file

@ -0,0 +1,15 @@
# Fornecedor: Oracle
# Projeto: Invista Nexus
# Natureza: Correção de Autorização (Unauthorized Fix)
Este diretório contém os arquivos Terraform e configurações fornecidos diretamente pelo suporte/engenharia da **Oracle** para resolver problemas de permissões e erros "Unauthorized" no ambiente OCI/OKE do projeto Nexus (Invista FIDC).
## Conteúdo
- **Terraform Stacks**: Definições de infraestrutura (OKE, PostgreSQL, Redis, Vault, etc).
- **Scripts de Correção**: Lógica para ajuste de políticas de IAM e acesso ao cluster.
## Data de Recebimento
09 de Março de 2026
---
*Documentação gerada automaticamente para controle de versões de fornecedores externos.*

View file

@ -0,0 +1,325 @@
# =================================================================
# CLUSTER OKE - NEXUS
# =================================================================
# Criacao do Cluster Kubernetes
module "oke_nexus" {
source = "./modules/oke_cluster"
# executa o deploy se a flag global de ativação estiver true
count = var.oke_nexus_enable ? 1 : 0
# --- Identificação e Contexto ---
tenancy_ocid = var.tenancy_ocid
oke_cluster_name = var.oke_nexus_cluster_name
oke_compartment = var.oke_nexus_cluster_compartment_id
# --- Rede (Descoberta Automática via local.network) ---
vcn_id = var.oke_nexus_vcn_id
# Como o script criará a rede do zero na VCN informada:
oke_subnet_api_cidr_block = var.oke_nexus_subnet_api_cidr_block
oke_subnet_node_cidr_block = var.oke_nexus_subnet_node_cidr_block
oke_subnet_lb_cidr_block = var.oke_nexus_subnet_lb_cidr_block
oke_subnet_pods_cidr_block = var.oke_nexus_subnet_pods_cidr_block
# --- Configuração de Hardware (Node Pool) ---
oke_node_shape = var.oke_nexus_node_shape
oke_nodepool_size = var.oke_nexus_node_count
oke_node_shape_memory_gb = var.oke_nexus_node_memory_gb
oke_node_shape_ocpus = var.oke_nexus_node_ocpus
# --- Endpoint e Add-ons ---
oke_api_endpoint_public = var.oke_nexus_api_endpoint_public
oke_enable_autoscaler = var.oke_nexus_enable_autoscaler
oke_enable_metric_server = var.oke_nexus_enable_metric_server
oke_enable_cert_manager = var.oke_nexus_enable_cert_manager
# --- Configurações Detalhadas do Autoscaler ---
oke_autoscaler_min_nodes = var.oke_nexus_autoscaler_min_nodes
oke_autoscaler_max_nodes = var.oke_nexus_autoscaler_max_nodes
oke_autoscaler_down_delay_after_add = var.oke_nexus_autoscaler_down_delay_after_add
oke_autoscaler_down_unneeded_time = var.oke_nexus_autoscaler_down_unneeded_time
oke_autoscaler_down_unready_time = var.oke_nexus_autoscaler_down_unready_time
oke_autoscaler_skip_nodes_with_system_pods = var.oke_nexus_autoscaler_skip_nodes_with_system_pods
}
# Criacao do provider Kubernetes
provider "kubernetes" {
alias = "oke_nexus"
host = one(module.oke_nexus[*].cluster_endpoint) # Use os outputs do seu módulo
cluster_ca_certificate = local.oke_nexus_ca != null ? base64decode(local.oke_nexus_ca) : null
exec {
api_version = "client.authentication.k8s.io/v1beta1"
args = ["ce", "cluster", "generate-token", "--cluster-id", coalesce(one(module.oke_nexus[*].cluster_id), "disabled")]
command = "oci"
}
}
# Criacao do Service Account no Cluster Kubernetes
module "oke_nexus_service_account" {
source = "./modules/oke_service_account"
count = var.oke_nexus_enable ? 1 : 0
providers = {
kubernetes = kubernetes.oke_nexus
}
# Injetando as informações do Vault que vieram do output do módulo
vault_id = one(module.vault[*].vault_id)
master_key_id = one(module.vault[*].master_key_id)
oke_cluster_id = one(module.oke_nexus[*].cluster_id)
oke_cluster_name = var.oke_nexus_cluster_name
oke_compartment = var.oke_nexus_cluster_compartment_id
}
# =================================================================
# CLUSTER OKE - Barramento
# =================================================================
# Criacao do Cluster Kubernetes
module "oke_bus" {
source = "./modules/oke_cluster"
# executa o deploy se a flag global de ativação estiver true
count = var.oke_bus_enable ? 1 : 0
# --- Identificação e Contexto ---
tenancy_ocid = var.tenancy_ocid
oke_cluster_name = var.oke_bus_cluster_name
oke_compartment = var.oke_bus_cluster_compartment_id
# --- Rede (Descoberta Automática via local.network) ---
vcn_id = var.oke_bus_vcn_id
# Como o script criará a rede do zero na VCN informada:
oke_subnet_api_cidr_block = var.oke_bus_subnet_api_cidr_block
oke_subnet_node_cidr_block = var.oke_bus_subnet_node_cidr_block
oke_subnet_lb_cidr_block = var.oke_bus_subnet_lb_cidr_block
oke_subnet_pods_cidr_block = var.oke_bus_subnet_pods_cidr_block
# --- Configuração de Hardware (Node Pool) ---
oke_node_shape = var.oke_bus_node_shape
oke_nodepool_size = var.oke_bus_node_count
oke_node_shape_memory_gb = var.oke_bus_node_memory_gb
oke_node_shape_ocpus = var.oke_bus_node_ocpus
# --- Endpoint e Add-ons ---
oke_api_endpoint_public = var.oke_bus_api_endpoint_public
oke_enable_autoscaler = var.oke_bus_enable_autoscaler
oke_enable_metric_server = var.oke_bus_enable_metric_server
oke_enable_cert_manager = var.oke_bus_enable_cert_manager
# --- Configurações Detalhadas do Autoscaler ---
oke_autoscaler_min_nodes = var.oke_bus_autoscaler_min_nodes
oke_autoscaler_max_nodes = var.oke_bus_autoscaler_max_nodes
oke_autoscaler_down_delay_after_add = var.oke_bus_autoscaler_down_delay_after_add
oke_autoscaler_down_unneeded_time = var.oke_bus_autoscaler_down_unneeded_time
oke_autoscaler_down_unready_time = var.oke_bus_autoscaler_down_unready_time
oke_autoscaler_skip_nodes_with_system_pods = var.oke_bus_autoscaler_skip_nodes_with_system_pods
}
# Criacao do provider Kubernetes
provider "kubernetes" {
alias = "oke_bus"
host = one(module.oke_bus[*].cluster_endpoint) # Use os outputs do seu módulo
cluster_ca_certificate = local.oke_bus_ca != null ? base64decode(local.oke_bus_ca) : null
exec {
api_version = "client.authentication.k8s.io/v1beta1"
args = ["ce", "cluster", "generate-token", "--cluster-id", coalesce(one(module.oke_bus[*].cluster_id), "disabled")]
command = "oci"
}
}
# Criacao do Service Account no Cluster Kubernetes
module "oke_bus_service_account" {
source = "./modules/oke_service_account"
count = var.oke_bus_enable ? 1 : 0
providers = {
kubernetes = kubernetes.oke_bus
}
# Injetando as informações do Vault que vieram do output do módulo
vault_id = one(module.vault[*].vault_id)
master_key_id = one(module.vault[*].master_key_id)
oke_cluster_id = one(module.oke_bus[*].cluster_id)
oke_cluster_name = var.oke_bus_cluster_name
oke_compartment = var.oke_bus_cluster_compartment_id
}
# =================================================================
# CLUSTER OKE - Observabilidade
# =================================================================
# Criacao do Cluster Kubernetes
module "oke_observability" {
source = "./modules/oke_cluster"
# executa o deploy se a flag global de ativação estiver true
count = var.oke_o11y_enable ? 1 : 0
# --- Identificação e Contexto ---
tenancy_ocid = var.tenancy_ocid
oke_cluster_name = var.oke_o11y_cluster_name
oke_compartment = var.oke_o11y_cluster_compartment_id
# --- Rede (Descoberta Automática via local.network) ---
vcn_id = var.oke_o11y_vcn_id
# Como o script criará a rede do zero na VCN informada:
oke_subnet_api_cidr_block = var.oke_o11y_subnet_api_cidr_block
oke_subnet_node_cidr_block = var.oke_o11y_subnet_node_cidr_block
oke_subnet_lb_cidr_block = var.oke_o11y_subnet_lb_cidr_block
oke_subnet_pods_cidr_block = var.oke_o11y_subnet_pods_cidr_block
# --- Configuração de Hardware (Node Pool) ---
oke_node_shape = var.oke_o11y_node_shape
oke_nodepool_size = var.oke_o11y_node_count
oke_node_shape_memory_gb = var.oke_o11y_node_memory_gb
oke_node_shape_ocpus = var.oke_o11y_node_ocpus
# --- Endpoint e Add-ons ---
oke_api_endpoint_public = var.oke_o11y_api_endpoint_public
oke_enable_autoscaler = var.oke_o11y_enable_autoscaler
oke_enable_metric_server = var.oke_o11y_enable_metric_server
oke_enable_cert_manager = var.oke_o11y_enable_cert_manager
# --- Configurações Detalhadas do Autoscaler ---
oke_autoscaler_min_nodes = var.oke_o11y_autoscaler_min_nodes
oke_autoscaler_max_nodes = var.oke_o11y_autoscaler_max_nodes
oke_autoscaler_down_delay_after_add = var.oke_o11y_autoscaler_down_delay_after_add
oke_autoscaler_down_unneeded_time = var.oke_o11y_autoscaler_down_unneeded_time
oke_autoscaler_down_unready_time = var.oke_o11y_autoscaler_down_unready_time
oke_autoscaler_skip_nodes_with_system_pods = var.oke_o11y_autoscaler_skip_nodes_with_system_pods
}
# Criacao do provider Kubernetes
provider "kubernetes" {
alias = "oke_observability"
host = one(module.oke_observability[*].cluster_endpoint) # Use os outputs do seu módulo
cluster_ca_certificate = local.oke_observability_ca != null ? base64decode(local.oke_observability_ca) : null
exec {
api_version = "client.authentication.k8s.io/v1beta1"
args = ["ce", "cluster", "generate-token", "--cluster-id", coalesce(one(module.oke_observability[*].cluster_id), "disabled")]
command = "oci"
}
}
# Criacao do Service Account no Cluster Kubernetes
module "oke_observability_service_account" {
source = "./modules/oke_service_account"
count = var.oke_o11y_enable ? 1 : 0
providers = {
kubernetes = kubernetes.oke_observability
}
# Injetando as informações do Vault que vieram do output do módulo
vault_id = one(module.vault[*].vault_id)
master_key_id = one(module.vault[*].master_key_id)
oke_cluster_id = one(module.oke_observability[*].cluster_id)
oke_cluster_name = var.oke_o11y_cluster_name
oke_compartment = var.oke_o11y_cluster_compartment_id
}
# =================================================================
# Bucket
# =================================================================
module "bucket" {
source = "./modules/bucket"
# executa o deploy se a flag global de ativação estiver true
count = var.bucket_enable ? 1 : 0
bucket_name = var.bucket_name
bucket_compartment = var.bucket_compartment
bucket_access_type = var.bucket_access_type
bucket_storage_tier = var.bucket_storage_tier
bucket_versioning = var.bucket_versioning
}
# =================================================================
# Vault
# =================================================================
module "vault" {
source = "./modules/vault"
# executa o deploy se a flag global de ativação estiver true
count = local.vault_required ? 1 : 0
vault_name = var.vault_name
vault_compartment = var.vault_compartment
}
# =================================================================
# PostgreSQL
# =================================================================
module "postgresql" {
source = "./modules/postgresql"
# executa o deploy se a flag global de ativação estiver true
count = var.postgresql_enable ? 1 : 0
# Injetando as informações do Vault que vieram do output do módulo
vault_id = one(module.vault[*].vault_id)
master_key_id = one(module.vault[*].master_key_id)
postgresql_name = var.postgresql_name
postgresql_compartment_id = var.postgresql_compartment_id
vcn_id = var.postgresql_vcn_id
subnet_cidr_block = var.postgresql_subnet_cidr_block
allowed_pod_cidrs = local.psql_allowed_pod_cidrs # CIDRs de PODs autorizados
postgresql_version = var.postgresql_version
postgresql_instance_count = var.postgresql_instance_count
postgresql_shape = var.postgresql_shape
postgresql_memory_in_gbs = local.postgresql_memory_in_gbs
postgresql_ocpu_count = local.postgresql_ocpu_count
}
# =================================================================
# Redis
# =================================================================
module "redis" {
source = "./modules/redis"
# executa o deploy se a flag global de ativação estiver true
count = var.redis_enable ? 1 : 0
# Injetando as informações do Vault que vieram do output do módulo
vault_id = one(module.vault[*].vault_id)
master_key_id = one(module.vault[*].master_key_id)
redis_name = var.redis_name
redis_compartment_id = var.redis_compartment_id
vcn_id = var.redis_vcn_id
subnet_cidr_block = var.redis_subnet_cidr_block
allowed_pod_cidrs = local.psql_allowed_pod_cidrs # CIDRs de PODs autorizados
redis_count_node = var.redis_count_node
redis_node_memory_in_gbs = var.redis_node_memory_in_gbs
redis_software_version = var.redis_software_version
}
# =================================================================
# Autonomous Database JSON (ADJ)
# =================================================================
module "autonomous_json" {
source = "./modules/autonomous_json"
# executa o deploy se a flag global de ativação estiver true
count = var.autonomous_json_enable ? 1 : 0
# Injetando as informações do Vault que vieram do output do módulo
vault_id = one(module.vault[*].vault_id)
master_key_id = one(module.vault[*].master_key_id)
autonomous_json_name = var.autonomous_json_name
autonomous_json_compartment_id = var.autonomous_json_compartment_id
vcn_id = var.autonomous_json_vcn_id
subnet_cidr_block = var.autonomous_json_subnet_cidr_block
allowed_pod_cidrs = local.psql_allowed_pod_cidrs # CIDRs de PODs autorizados
autonomous_json_compute_model = var.autonomous_json_compute_model
autonomous_json_compute_count = var.autonomous_json_compute_count
autonomous_json_data_storage_size_in_tbs = var.autonomous_json_data_storage_size_in_tbs
autonomous_json_is_auto_scaling_enabled = var.autonomous_json_is_auto_scaling_enabled
}

View file

@ -0,0 +1,50 @@
locals {
# O Vault deve ser ativado se:
# 1. For habilitado manualmente OU
# 2. Se qualquer serviço que depende dele estiver habilitado
vault_required = var.vault_enable || var.postgresql_enable || var.redis_enable || var.autonomous_json_enable || var.oke_o11y_enable || var.oke_bus_enable || var.oke_nexus_enable
}
locals {
# PostgreSQL: Filtra CIDRs autorizados
psql_allowed_pod_cidrs = compact([
var.oke_nexus_enable_psql_access ? var.oke_nexus_subnet_pods_cidr_block : "",
var.oke_bus_enable_psql_access ? var.oke_bus_subnet_pods_cidr_block : "",
var.oke_o11y_enable_psql_access ? var.oke_o11y_subnet_pods_cidr_block : ""
])
# REDIS: Filtra CIDRs autorizados
redis_allowed_pod_cidrs = compact([
var.oke_nexus_enable_redis_access ? var.oke_nexus_subnet_pods_cidr_block : "",
var.oke_bus_enable_redis_access ? var.oke_bus_subnet_pods_cidr_block : "",
var.oke_o11y_enable_redis_access ? var.oke_o11y_subnet_pods_cidr_block : ""
])
# Autonomous JSON: Filtra CIDRs autorizados
autonomous_json_allowed_pod_cidrs = compact([
var.oke_nexus_enable_autonomous_json_access ? var.oke_nexus_subnet_pods_cidr_block : "",
var.oke_bus_enable_autonomous_json_access ? var.oke_bus_subnet_pods_cidr_block : "",
var.oke_o11y_enable_autonomous_json_access ? var.oke_o11y_subnet_pods_cidr_block : ""
])
}
locals {
oke_nexus_ca = one(module.oke_nexus[*].cluster_ca_cert)
oke_bus_ca = one(module.oke_bus[*].cluster_ca_cert)
oke_observability_ca = one(module.oke_observability[*].cluster_ca_cert)
}
locals {
# Lógica para extrair o valor correto de oCPU com base no shape
postgresql_ocpu_count = (
var.postgresql_shape == "PostgreSQL.VM.Standard.E6.Flex" ? var.postgresql_ocpu_e6 :
var.postgresql_shape == "PostgreSQL.VM.Standard.E5.Flex" ? var.postgresql_ocpu_e5 :
var.postgresql_ocpu_s3 # Caso seja Standard3
)
# Lógica para extrair o valor correto de Memória
postgresql_memory_in_gbs = (
var.postgresql_shape == "PostgreSQL.VM.Standard3.Flex" ? var.postgresql_memory_s3 :
var.postgresql_memory_amd
)
}

View file

@ -0,0 +1,27 @@
resource "oci_database_autonomous_database" "app_json_db" {
compartment_id = var.autonomous_json_compartment_id
# O db_name exige apenas letras e números (sem hífens). O replace resolve isso.
db_name = "json${replace(var.autonomous_json_name, "-", "")}"
display_name = "${var.autonomous_json_name}"
db_workload = "AJD"
license_model = "LICENSE_INCLUDED"
admin_password = random_password.adb_admin_password.result
# --- Computação e Armazenamento ---
compute_model = var.autonomous_json_compute_model
compute_count = var.autonomous_json_compute_count
data_storage_size_in_tbs = var.autonomous_json_data_storage_size_in_tbs
is_auto_scaling_enabled = var.autonomous_json_is_auto_scaling_enabled
# --- Rede (Private Endpoint) ---
subnet_id = oci_core_subnet.autonomous_json_subnet.id
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,22 @@
resource "oci_core_route_table" "autonomous_json_route_table" {
vcn_id = var.vcn_id
compartment_id = data.oci_core_vcn.vcn.compartment_id
display_name = "autonomous_json_${var.autonomous_json_name}"
# Rota para Internet via NAT Gateway
route_rules {
description = "Traffic to the internet"
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
network_entity_id = local.network.gateway.nat.id
}
# Rota para todos os serviços OCI via Service Gateway
route_rules {
description = "Traffic to OCI services"
destination = local.network.gateway.service.all_services.cidr
destination_type = "SERVICE_CIDR_BLOCK"
network_entity_id = local.network.gateway.service.id
}
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,49 @@
resource "oci_core_security_list" "autonomous_json_sec_list" {
compartment_id = data.oci_core_vcn.vcn.compartment_id
display_name = "autonomous_json-${var.autonomous_json_name}"
vcn_id = var.vcn_id
# --- EGRESS RULES ---
egress_security_rules {
description = "Allow Autonomous JSON to route anywhere"
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
protocol = "all"
stateless = false
}
# --- INGRESS RULES ---
# INGRESS 1: Path Discovery (ICMP Tipo 3, Código 4)
ingress_security_rules {
description = "Path discovery from anywhere"
protocol = "1"
source = "0.0.0.0/0"
stateless = false
icmp_options {
type = 3
code = 4
}
}
# REGRA DINÂMICA: Cria uma regra de Ingress para CADA cluster autorizado
dynamic "ingress_security_rules" {
# O for_each a lista filtrada do locals
for_each = var.allowed_pod_cidrs
content {
description = "Acesso dos Pods do OKE ao Allow Autonomous JSON"
protocol = "6" # TCP
# O ingress_security_rules.value contem o CIDR atual do loop (ex: 10.6.100.0/24)
source = ingress_security_rules.value
stateless = false
tcp_options {
min = 5432
max = 5432
}
}
}
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,13 @@
resource "oci_core_subnet" "autonomous_json_subnet" {
cidr_block = var.subnet_cidr_block
compartment_id = data.oci_core_vcn.vcn.compartment_id
display_name = "autonomous_json-${var.autonomous_json_name}"
dns_label = substr(replace("psql${var.autonomous_json_name}", "-", ""), 0, 15)
prohibit_public_ip_on_vnic = "true"
route_table_id = "${oci_core_route_table.autonomous_json_route_table.id}"
security_list_ids = ["${oci_core_security_list.autonomous_json_sec_list.id}"]
vcn_id = var.vcn_id
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,53 @@
resource "random_integer" "suffix" {
min = 1000
max = 9999
# Opcional: O 'keepers' força a geração de um novo número
# apenas quando o nome do cluster mudar.
keepers = {
cluster_name = var.autonomous_json_name
}
}
# 1. Gera uma senha forte para o Autonomous Database
resource "random_password" "adb_admin_password" {
length = 16
special = true
min_numeric = 1 # GARANTE pelo menos um número (resolve o erro 400)
min_upper = 1 # GARANTE pelo menos uma letra maiúscula
min_lower = 1 # GARANTE pelo menos uma letra minúscula
# O ADB é um pouco chato com caracteres especiais na senha inicial, estes são seguros:
override_special = "#_-"
}
# 2. Guarda a senha no Vault
resource "oci_vault_secret" "adb_admin_secret" {
compartment_id = var.autonomous_json_compartment_id # Ou a variável de compartment que está a usar
vault_id = var.vault_id
key_id = var.master_key_id
secret_name = "adb-json-admin-pwd-${var.autonomous_json_name}-${random_integer.suffix.result}"
description = "Senha do ADMIN do Autonomous JSON Database"
secret_content {
content_type = "BASE64"
content = base64encode(random_password.adb_admin_password.result)
}
}
resource "oci_vault_secret" "adb_connection_string" {
compartment_id = var.autonomous_json_compartment_id
vault_id = var.vault_id
key_id = var.master_key_id
secret_name = "adb-json-conn-string-${var.autonomous_json_name}-${random_integer.suffix.result}"
description = "String de conexao (Perfil High) do Autonomous JSON Database"
secret_content {
content_type = "BASE64"
content = base64encode(oci_database_autonomous_database.app_json_db.connection_strings[0].profiles[0].value)
}
}

View file

@ -0,0 +1,40 @@
# Buscamos os dados da VCN existente para usar em referências
data "oci_core_vcn" "vcn" {
vcn_id = var.vcn_id
}
# Busca o Internet Gateway (IGW) na VCN
data "oci_core_internet_gateways" "igws" {
compartment_id = data.oci_core_vcn.vcn.compartment_id
vcn_id = data.oci_core_vcn.vcn.id
}
# Busca o NAT Gateway (NGW) na VCN
data "oci_core_nat_gateways" "ngws" {
compartment_id = data.oci_core_vcn.vcn.compartment_id
vcn_id = data.oci_core_vcn.vcn.id
}
# Busca o Service Gateway (SGW) na VCN
data "oci_core_service_gateways" "sgws" {
compartment_id = data.oci_core_vcn.vcn.compartment_id
vcn_id = data.oci_core_vcn.vcn.id
}
# Busca a lista de serviços disponíveis na região atual
data "oci_core_services" "all_services" {
filter {
name = "name"
values = ["All .* Services In Oracle Services Network"]
regex = true
}
}
# Busca apenas o Object Storage (usado na rota PUBLIC conforme seu arquivo)
data "oci_core_services" "object_storage" {
filter {
name = "name"
values = [".*Object Storage"]
regex = true
}
}

View file

@ -0,0 +1,24 @@
locals {
network = {
# OCIDs capturados dinamicamente via Data Source
gateway = {
internet = {
id = length(data.oci_core_internet_gateways.igws.gateways) > 0 ? data.oci_core_internet_gateways.igws.gateways[0].id : null
}
nat = {
id = length(data.oci_core_nat_gateways.ngws.nat_gateways) > 0 ? data.oci_core_nat_gateways.ngws.nat_gateways[0].id : null
}
service = {
id = length(data.oci_core_service_gateways.sgws.service_gateways) > 0 ? data.oci_core_service_gateways.sgws.service_gateways[0].id : null
all_services = {
name = data.oci_core_services.all_services.services[0].name
cidr = data.oci_core_services.all_services.services[0].cidr_block
}
object_storage = {
name = data.oci_core_services.object_storage.services[0].name
cidr = data.oci_core_services.object_storage.services[0].cidr_block
}
}
}
}
}

View file

@ -0,0 +1,11 @@
variable vcn_id { type = string }
variable subnet_cidr_block { type = string }
variable allowed_pod_cidrs { type = list(string) }
variable autonomous_json_name { type = string }
variable autonomous_json_compartment_id { type = string }
variable autonomous_json_compute_model { type = string }
variable autonomous_json_compute_count { type = number }
variable autonomous_json_data_storage_size_in_tbs { type = number }
variable autonomous_json_is_auto_scaling_enabled { type = bool }
variable vault_id { type = string }
variable master_key_id { type = string }

View file

@ -0,0 +1,24 @@
# Cria o Bucket
resource "oci_objectstorage_bucket" "app_bucket" {
# Nome do bucket (deve ser único na sua tenancy)
name = var.bucket_name
# Reaproveitando as variáveis que você tem no projeto
compartment_id = var.bucket_compartment
# Nível de acesso (NoPublicAccess, ObjectRead, ou ObjectReadWithoutList)
access_type = var.bucket_access_type
# Storage Tier: Standard ou Archive
storage_tier = var.bucket_storage_tier
# (Opcional) Habilita versionamento de arquivos
versioning = var.bucket_versioning
# Usa o namespace descoberto no bloco data acima
namespace = data.oci_objectstorage_namespace.tenancy_namespace.namespace
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,4 @@
# 1. Descobre automaticamente o Namespace da sua conta Oracle
data "oci_objectstorage_namespace" "tenancy_namespace" {
compartment_id = var.bucket_compartment
}

View file

@ -0,0 +1,5 @@
variable bucket_compartment { type = string }
variable bucket_name { type = string }
variable bucket_access_type { type = string }
variable bucket_storage_tier { type = string }
variable bucket_versioning { type = string }

View file

@ -0,0 +1,62 @@
# 1. Busca as Availability Domains disponíveis na região do deploy
data "oci_identity_availability_domains" "ads" {
compartment_id = var.tenancy_ocid
}
# Buscamos os dados da VCN existente para usar em referências
data "oci_core_vcn" "oke_vcn" {
vcn_id = var.vcn_id
}
# Consulta as opções oficiais do serviço OKE
data "oci_containerengine_node_pool_option" "oke_options" {
node_pool_option_id = "all"
compartment_id = var.tenancy_ocid
}
# Consulta as opções gerais do serviço OKE na região
data "oci_containerengine_cluster_option" "oke_options" {
cluster_option_id = "all"
compartment_id = var.tenancy_ocid # Pode ser o OCID do Tenancy
}
# Cria o arquivo kube.config para conexao ao cluster
data "oci_containerengine_cluster_kube_config" "cluster_kube_config" {
cluster_id = oci_containerengine_cluster.containerengine_cluster.id
}
# Busca o Internet Gateway (IGW) na VCN
data "oci_core_internet_gateways" "igws" {
compartment_id = data.oci_core_vcn.oke_vcn.compartment_id
vcn_id = data.oci_core_vcn.oke_vcn.id
}
# Busca o NAT Gateway (NGW) na VCN
data "oci_core_nat_gateways" "ngws" {
compartment_id = data.oci_core_vcn.oke_vcn.compartment_id
vcn_id = data.oci_core_vcn.oke_vcn.id
}
# Busca o Service Gateway (SGW) na VCN
data "oci_core_service_gateways" "sgws" {
compartment_id = data.oci_core_vcn.oke_vcn.compartment_id
vcn_id = data.oci_core_vcn.oke_vcn.id
}
# Busca a lista de serviços disponíveis na região atual
data "oci_core_services" "all_services" {
filter {
name = "name"
values = ["All .* Services In Oracle Services Network"]
regex = true
}
}
# Busca apenas o Object Storage (usado na rota PUBLIC conforme seu arquivo)
data "oci_core_services" "object_storage" {
filter {
name = "name"
values = [".*Object Storage"]
regex = true
}
}

View file

@ -0,0 +1,140 @@
locals {
is_arm_shape = length(regexall("\\.A", var.oke_node_shape)) > 0
# Detecta se é um shape de nova geração (E5, E4, etc) que exige Gen2
is_gen2_required = length(regexall("\\.E[45]\\.", var.oke_node_shape)) > 0
# Pegamos a lista, ordenamos e revertemos para garantir que a seja a maior
# A API do OCI costuma devolver ordenado, mas o sort/reverse garante a lógica
latest_k8s_version = reverse(sort(data.oci_containerengine_cluster_option.oke_options.kubernetes_versions))[0]
latest_k8s_version_clean = trimprefix(local.latest_k8s_version, "v")
# Ex: transforma "1.31.1" em "1.31" para uma busca mais resiliente
k8s_major_minor = join(".", slice(split(".", local.latest_k8s_version_clean), 0, 2))
all_sources = data.oci_containerengine_node_pool_option.oke_options.sources
oke_images_list = [
for s in local.all_sources : {
id = s.image_id
name = s.source_name
}
if length(regexall("Oracle-Linux-8", s.source_name)) > 0 &&
length(regexall("OKE-${local.k8s_major_minor}", s.source_name)) > 0 &&
# Filtro robusto de arquitetura
(local.is_arm_shape ?
length(regexall("aarch64", s.source_name)) > 0 :
length(regexall("aarch64", s.source_name)) == 0
) &&
length(regexall("GPU", s.source_name)) == 0 && # Exclui imagens para GPU
length(regexall("Minimal", s.source_name)) == 0 && # Exclui imagens Minimal
# Garante que não pegamos imagens de teste ou versões experimentais
length(regexall("Minimal|GPU|Arm", s.source_name)) == (local.is_arm_shape ? 1 : 0) &&
(local.is_arm_shape ?
length(regexall("aarch64", s.source_name)) > 0 :
length(regexall("aarch64", s.source_name)) == 0
)
]
# Pega o ID da primeira imagem que sobrou após os filtros
# Se a lista estiver vazia, ele não quebra o Terraform, permitindo você ver o erro no plano
selected_image_id = length(local.oke_images_list) > 0 ? local.oke_images_list[0].id : "IMAGEM-NAO-ENCONTRADA"
selected_image_name = length(local.oke_images_list) > 0 ? local.oke_images_list[0].name : "IMAGEM-NAO-ENCONTRADA"
}
locals {
# !!! Verifique esses parametro antes de implementar e seu ambiente !!!
addon_clusterAutoscaler = {
authType = {
# O tipo de autenticação que o Cluster Autoscaler usa ao fazer solicitações, como um dos seguintes:
# - instance: Instance Principal
# - workload: Workload identity
key = "authType"
value = "instance"
}
nodes = {
# Uma lista com número (mínimo:máximo) de nós e o OCID do Node Pool a ser gerenciado pelo cluster autoscaler.
# O formato é <min>:<max>:<node-pool1-ocid>, <min>:<max>:<node-pool2-ocid>.
# !! Formato JSON em texto simples ou codificado em Base64 !!
key = "nodes"
value = "${var.oke_autoscaler_min_nodes}:${var.oke_autoscaler_max_nodes}:${oci_containerengine_node_pool.cluster_app_node_pool.id}"
}
scaleDownDelayAfterAdd = {
# Quanto tempo depois de ampliar essa avaliação de redução é retomado. (Default:10m)
key = "scaleDownDelayAfterAdd"
value = "${var.oke_autoscaler_down_delay_after_add}"
}
scaleDownUnneededTime = {
# Por quanto tempo um deve ser desnecessário antes de ser elegível para redução. (Default:10m)
key = "scaleDownUnneededTime"
value = "${var.oke_autoscaler_down_unneeded_time}"
}
scaleDownUnreadyTime = {
# Por quanto tempo um não pronto deve ser desnecessário antes de ser elegível para redução. (Default:20m)
key = "scaleDownUnreadyTime"
value = "${var.oke_autoscaler_down_unready_time}"
}
v = {
# The number for the verbosity of logging. (Default:0)
key = "v"
value = "0"
}
skipNodesWithSystemPodsv = {
# If true, cluster autoscaler will never delete nodes with pods from kube-system (except for DaemonSet or mirror pods).(Default:true)
key = "skipNodesWithSystemPods"
value = "${var.oke_autoscaler_skip_nodes_with_system_pods}"
}
}
}
locals {
# 1. Regras Base (se houver alguma que sempre deve ser criada)
base_statements = [
]
# 2. Regras do Split Compartment (condicionais)
split_compartment_statements = [
"Allow any-user to manage instances in compartment id ${var.oke_compartment} where all { request.principal.id = '${oci_containerengine_cluster.containerengine_cluster.id}' }",
"Allow any-user to use private-ips in compartment id ${data.oci_core_vcn.oke_vcn.compartment_id} where all { request.principal.id = '${oci_containerengine_cluster.containerengine_cluster.id}' }",
"Allow any-user to use network-security-groups in compartment id ${data.oci_core_vcn.oke_vcn.compartment_id} where all { request.principal.id = '${oci_containerengine_cluster.containerengine_cluster.id}' }"
]
# 3. Regras do Autoscaler (condicionais)
autoscaler_statements = var.oke_enable_autoscaler ? [
"Allow dynamic-group ${oci_identity_dynamic_group.oke_nodes_dynamic_group.name} to manage cluster-node-pools in compartment id ${var.oke_compartment}",
"Allow dynamic-group ${oci_identity_dynamic_group.oke_nodes_dynamic_group.name} to manage instance-family in compartment id ${var.oke_compartment}",
"Allow dynamic-group ${oci_identity_dynamic_group.oke_nodes_dynamic_group.name} to use subnets in compartment id ${data.oci_core_vcn.oke_vcn.compartment_id}"
] : []
# 4. A MÁGICA: Junta tudo em uma lista final
# O flatten() garante que tudo vire uma lista simples de strings, ignorando as listas vazias
oke_all_policy_statements = flatten([
local.base_statements,
local.split_compartment_statements,
local.autoscaler_statements
])
}
locals {
network = {
# OCIDs capturados dinamicamente via Data Source
gateway = {
internet = {
id = length(data.oci_core_internet_gateways.igws.gateways) > 0 ? data.oci_core_internet_gateways.igws.gateways[0].id : null
}
nat = {
id = length(data.oci_core_nat_gateways.ngws.nat_gateways) > 0 ? data.oci_core_nat_gateways.ngws.nat_gateways[0].id : null
}
service = {
id = length(data.oci_core_service_gateways.sgws.service_gateways) > 0 ? data.oci_core_service_gateways.sgws.service_gateways[0].id : null
all_services = {
name = data.oci_core_services.all_services.services[0].name
cidr = data.oci_core_services.all_services.services[0].cidr_block
}
object_storage = {
name = data.oci_core_services.object_storage.services[0].name
cidr = data.oci_core_services.object_storage.services[0].cidr_block
}
}
}
}
}

View file

@ -0,0 +1,54 @@
resource "oci_containerengine_addon" "cluster_autoscaler" {
count = var.oke_enable_autoscaler ? 1 : 0
#Required, a name uniquely identifies an add-on, see all supported add-on names in data.oci_containerengine_addon_options.all.addon_options
addon_name = "ClusterAutoscaler"
#Required
cluster_id = "${oci_containerengine_cluster.containerengine_cluster.id}"
#Required, remove the resource on addon deletion
remove_addon_resources_on_delete = true
#Optional, will override an existing installation if true and Addon already exists
override_existing = false
#Optional
dynamic configurations {
for_each = local.addon_clusterAutoscaler
content {
key = configurations.value.key
value = configurations.value.value
}
}
}
resource "oci_containerengine_addon" "cluster_metricServer" {
count = var.oke_enable_metric_server ? 1 : 0
#Required, a name uniquely identifies an add-on, see all supported add-on names in data.oci_containerengine_addon_options.all.addon_options
addon_name = "KubernetesMetricsServer"
#Required
cluster_id = "${oci_containerengine_cluster.containerengine_cluster.id}"
#Required, remove the resource on addon deletion
remove_addon_resources_on_delete = true
#Optional, will override an existing installation if true and Addon already exists
override_existing = false
depends_on = [
oci_containerengine_addon.cluster_certManager
]
}
resource "oci_containerengine_addon" "cluster_certManager" {
count = var.oke_enable_cert_manager || var.oke_enable_metric_server ? 1 : 0
#Required, a name uniquely identifies an add-on, see all supported add-on names in data.oci_containerengine_addon_options.all.addon_options
addon_name = "CertManager"
#Required
cluster_id = "${oci_containerengine_cluster.containerengine_cluster.id}"
#Required, remove the resource on addon deletion
remove_addon_resources_on_delete = true
#Optional, will override an existing installation if true and Addon already exists
override_existing = false
}

View file

@ -0,0 +1,103 @@
resource "oci_containerengine_cluster" "containerengine_cluster" {
compartment_id = var.oke_compartment
cluster_pod_network_options {
cni_type = "OCI_VCN_IP_NATIVE"
}
endpoint_config {
is_public_ip_enabled = "${var.oke_api_endpoint_public}"
# Se usa existente, pega o ID; se não, pega o ID da subnet criada pelo módulo
subnet_id = oci_core_subnet.api_endpoint_subnet.id
}
kubernetes_version = local.latest_k8s_version
name = "${var.oke_cluster_name}"
options {
admission_controller_options {
is_pod_security_policy_enabled = "false"
}
persistent_volume_config {
freeform_tags = {
"OKEclusterName" = "cluster-${var.oke_cluster_name}"
}
}
service_lb_config {
freeform_tags = {
"OKEclusterName" = "cluster-${var.oke_cluster_name}"
}
}
service_lb_subnet_ids = [oci_core_subnet.lb_subnet.id]
}
type = "ENHANCED_CLUSTER"
vcn_id = var.vcn_id
freeform_tags = {
"ManagedBy" = "Terraform"
}
}
resource "oci_containerengine_node_pool" "cluster_app_node_pool" {
cluster_id = oci_containerengine_cluster.containerengine_cluster.id
compartment_id = var.oke_compartment
kubernetes_version = local.latest_k8s_version
name = "${var.oke_cluster_name}"
node_shape = var.oke_node_shape
depends_on = [
oci_identity_policy.oke_all_policy
]
node_source_details {
# Pega o ID da imagem que ficou no topo da lista (a mais recente)
image_id = local.selected_image_id
source_type = "IMAGE"
}
lifecycle {
ignore_changes = [
node_config_details[0].size
]
}
node_config_details {
# Tamanho TOTAL do pool (o OKE distribui os nós entre as ADs acima)
size = var.oke_nodepool_size
# 2. O bloco dinâmico cria uma 'placement_config' para cada AD encontrada
dynamic "placement_configs" {
for_each = data.oci_identity_availability_domains.ads.availability_domains
content {
availability_domain = placement_configs.value.name
# Subnet onde ficam as instâncias (Workers)
subnet_id = oci_core_subnet.node_subnet.id
}
}
# Configuração da Subnet de Pods (VCN-Native)
node_pool_pod_network_option_details {
cni_type = "OCI_VCN_IP_NATIVE"
# Lista das subnets de pods (geralmente uma regional)
pod_subnet_ids = [oci_core_subnet.pods_subnet.id]
# Opcional: NSGs específicos para os PODS
# pod_nsg_ids = [oci_core_network_security_group.pod_nsg.id]
# Opcional: Para regiões de 1 AD, você pode querer forçar a
# distribuição entre Fault Domains específicos
# fault_domains = ["FAULT-DOMAIN-1", "FAULT-DOMAIN-2", "FAULT-DOMAIN-3"]
}
}
node_shape_config {
memory_in_gbs = var.oke_node_shape_memory_gb
ocpus = var.oke_node_shape_ocpus
}
freeform_tags = {
"ManagedBy" = "Terraform"
}
}
# terraform destroy -auto-approve && terraform apply -auto-approve

View file

@ -0,0 +1,21 @@
resource "oci_identity_policy" "oke_all_policy" {
compartment_id = var.tenancy_ocid
name = "policy-oke-${var.oke_cluster_name}"
description = "Todas as permissoes necessarias para o Cluster ${var.oke_cluster_name}."
statements = local.oke_all_policy_statements
freeform_tags = {
"ManagedBy" = "Terraform"
}
}
resource "oci_identity_dynamic_group" "oke_nodes_dynamic_group" {
compartment_id = var.tenancy_ocid
name = "dg-oke-nodes-${var.oke_cluster_name}"
description = "Dynamic Group para os Worker Nodes do OKE executarem o Autoscaler"
matching_rule = "ALL {instance.compartment.id = '${var.oke_compartment}'}"
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,77 @@
resource "oci_core_route_table" "lb_route_table" {
vcn_id = var.vcn_id
compartment_id = data.oci_core_vcn.oke_vcn.compartment_id
display_name = "OKE-Ladbalancer-${var.oke_cluster_name}"
# Rota para Internet via Internet Gateway
route_rules {
description = "Traffic to/from internet"
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
network_entity_id = local.network.gateway.internet.id
}
freeform_tags = {
"ManagedBy" = "Terraform"
}
}
resource "oci_core_route_table" "node_route_table" {
vcn_id = var.vcn_id
compartment_id = data.oci_core_vcn.oke_vcn.compartment_id
display_name = "OKE-node-${var.oke_cluster_name}"
# Rota para Internet via NAT Gateway
route_rules {
description = "Traffic to the internet"
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
network_entity_id = local.network.gateway.nat.id
}
# Rota para todos os serviços OCI via Service Gateway
route_rules {
description = "Traffic to OCI services"
destination = local.network.gateway.service.all_services.cidr
destination_type = "SERVICE_CIDR_BLOCK"
network_entity_id = local.network.gateway.service.id
}
freeform_tags = {
"ManagedBy" = "Terraform"
}
}
resource "oci_core_route_table" "api_endpoint_route_table" {
vcn_id = var.vcn_id
compartment_id = data.oci_core_vcn.oke_vcn.compartment_id
display_name = "OKE-ApiEndpoint-${var.oke_cluster_name}"
# Rota para Internet via Internet Gateway
route_rules {
description = "Traffic to/from internet"
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
network_entity_id = local.network.gateway.internet.id
}
freeform_tags = {
"ManagedBy" = "Terraform"
}
}
resource "oci_core_route_table" "pods_route_table" {
vcn_id = var.vcn_id
compartment_id = data.oci_core_vcn.oke_vcn.compartment_id
display_name = "OKE-Pods-${var.oke_cluster_name}"
# Rota para Internet via NAT Gateway
route_rules {
description = "Traffic to the internet"
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
network_entity_id = local.network.gateway.nat.id
}
# Rota para todos os serviços OCI via Service Gateway
route_rules {
description = "Traffic to OCI services"
destination = local.network.gateway.service.all_services.cidr
destination_type = "SERVICE_CIDR_BLOCK"
network_entity_id = local.network.gateway.service.id
}
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,354 @@
resource "oci_core_security_list" "lb_sec_list" {
compartment_id = data.oci_core_vcn.oke_vcn.compartment_id
display_name = "OKE-Ladbalancer-${var.oke_cluster_name}"
vcn_id = var.vcn_id
freeform_tags = {
"ManagedBy" = "Terraform"
}
}
resource "oci_core_security_list" "node_sec_list" {
compartment_id = data.oci_core_vcn.oke_vcn.compartment_id
display_name = "OKE-node-${var.oke_cluster_name}"
vcn_id = var.vcn_id
# --- EGRESS RULES ---
egress_security_rules {
description = "Allows communication from (or to) worker nodes"
destination = var.oke_subnet_node_cidr_block
destination_type = "CIDR_BLOCK"
protocol = "all"
stateless = false
}
egress_security_rules {
description = "Access to Kubernetes API Endpoint"
destination = var.oke_subnet_api_cidr_block
destination_type = "CIDR_BLOCK"
protocol = "6"
stateless = false
tcp_options {
min = 6443
max = 6443
}
}
egress_security_rules {
description = "Kubernetes worker to control plane communication"
destination = var.oke_subnet_api_cidr_block
destination_type = "CIDR_BLOCK"
protocol = "6"
stateless = false
tcp_options {
min = 12250
max = 12250
}
}
egress_security_rules {
description = "Path discovery to API Endpoint"
destination = var.oke_subnet_api_cidr_block
destination_type = "CIDR_BLOCK"
protocol = "1"
stateless = false
icmp_options {
type = 3
code = 4
}
}
egress_security_rules {
description = "Allow nodes to communicate with OKE to ensure correct start-up and continued functioning"
destination = local.network.gateway.service.all_services.cidr
destination_type = "SERVICE_CIDR_BLOCK"
protocol = "6"
stateless = false
tcp_options {
min = 443
max = 443
}
}
egress_security_rules {
description = "ICMP Access to any destination for Path Discovery"
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
protocol = "1"
stateless = false
icmp_options {
type = 3
code = 4
}
}
egress_security_rules {
description = "Worker Nodes access to Internet"
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
protocol = "all"
stateless = false
}
egress_security_rules {
description = "Allow worker nodes to communicate with pods"
destination = var.oke_subnet_pods_cidr_block
destination_type = "CIDR_BLOCK"
protocol = "all"
stateless = false
}
# --- INGRESS RULES ---
ingress_security_rules {
description = "Allows communication from (or to) worker nodes"
protocol = "all"
source = var.oke_subnet_node_cidr_block
stateless = false
}
ingress_security_rules {
description = "Path discovery from anywhere"
protocol = "1"
source = "0.0.0.0/0"
stateless = false
icmp_options {
type = 3
code = 4
}
}
ingress_security_rules {
description = "Kubernetes API endpoint to worker node communication (TCP)"
protocol = "6"
source = var.oke_subnet_api_cidr_block
stateless = false
tcp_options {
min = 10250
max = 10250
}
}
ingress_security_rules {
description = "Kubernetes API endpoint to worker node communication (ICMP)"
protocol = "1"
source = var.oke_subnet_api_cidr_block
stateless = false
}
ingress_security_rules {
description = "Inbound SSH traffic to worker nodes"
protocol = "6"
source = "0.0.0.0/0"
stateless = false
tcp_options {
min = 22
max = 22
}
}
ingress_security_rules {
description = "Allow pods to communicate with worker nodes"
protocol = "all"
source = var.oke_subnet_pods_cidr_block
stateless = false
}
freeform_tags = {
"ManagedBy" = "Terraform"
}
}
resource "oci_core_security_list" "api_endpoint_sec_list" {
compartment_id = data.oci_core_vcn.oke_vcn.compartment_id
display_name = "OKE-ApiEndpoint-${var.oke_cluster_name}"
vcn_id = var.vcn_id
# --- EGRESS RULES ---
egress_security_rules {
description = "Allow Kubernetes Control Plane to communicate with OKE"
destination = local.network.gateway.service.all_services.cidr
destination_type = "SERVICE_CIDR_BLOCK"
protocol = "6" # TCP
stateless = false
tcp_options {
min = 443
max = 443
}
}
egress_security_rules {
description = "Path discovery to Worker Nodes"
destination = var.oke_subnet_node_cidr_block
destination_type = "CIDR_BLOCK"
protocol = "1" # ICMP
stateless = false
icmp_options {
type = 3
code = 4
}
}
egress_security_rules {
description = "Kubernetes API endpoint to worker node communication (VCN-Native TCP)"
destination = var.oke_subnet_node_cidr_block
destination_type = "CIDR_BLOCK"
protocol = "6" # TCP
stateless = false
tcp_options {
min = 10250
max = 10250
}
}
egress_security_rules {
description = "Kubernetes API endpoint to worker node communication (VCN-Native ICMP)"
destination = var.oke_subnet_node_cidr_block
destination_type = "CIDR_BLOCK"
protocol = "1" # ICMP genérico
stateless = false
}
egress_security_rules {
description = "Allow Kubernetes Control Plane to communicate with pods"
destination = var.oke_subnet_pods_cidr_block
destination_type = "CIDR_BLOCK"
protocol = "all"
stateless = false
}
# --- INGRESS RULES ---
ingress_security_rules {
description = "External access to Kubernetes API endpoint"
protocol = "6"
source = "0.0.0.0/0"
stateless = false
tcp_options {
min = 6443
max = 6443
}
}
ingress_security_rules {
description = "Kubernetes worker to Kubernetes API endpoint communication"
protocol = "6"
source = var.oke_subnet_node_cidr_block
stateless = false
tcp_options {
min = 6443
max = 6443
}
}
ingress_security_rules {
description = "Kubernetes worker to control plane communication"
protocol = "6"
source = var.oke_subnet_node_cidr_block
stateless = false
tcp_options {
min = 12250
max = 12250
}
}
ingress_security_rules {
description = "Path discovery from Worker Nodes"
protocol = "1"
source = var.oke_subnet_node_cidr_block
stateless = false
icmp_options {
type = 3
code = 4
}
}
ingress_security_rules {
description = "Pod to Kubernetes API endpoint communication"
protocol = "6"
source = var.oke_subnet_pods_cidr_block
stateless = false
tcp_options {
min = 6443
max = 6443
}
}
ingress_security_rules {
description = "Pod to OKE control plane communication"
protocol = "6"
source = var.oke_subnet_pods_cidr_block
stateless = false
tcp_options {
min = 12250
max = 12250
}
}
freeform_tags = {
"ManagedBy" = "Terraform"
}
}
resource "oci_core_security_list" "pods_sec_list" {
compartment_id = data.oci_core_vcn.oke_vcn.compartment_id
display_name = "OKE-pods-${var.oke_cluster_name}"
vcn_id = var.vcn_id
# --- EGRESS RULES ---
egress_security_rules {
description = "Allow pods to communicate with each other."
destination = var.oke_subnet_pods_cidr_block
destination_type = "CIDR_BLOCK"
protocol = "all"
stateless = false
}
egress_security_rules {
description = "Access to Kubernetes API Endpoint"
destination = var.oke_subnet_api_cidr_block
destination_type = "CIDR_BLOCK"
protocol = "6"
stateless = false
tcp_options {
min = 6443
max = 6443
}
}
egress_security_rules {
description = "Pod to OKE control plane communication"
destination = var.oke_subnet_api_cidr_block
destination_type = "CIDR_BLOCK"
protocol = "6"
stateless = false
tcp_options {
min = 12250
max = 12250
}
}
egress_security_rules {
description = "Path discovery to Kubernetes API Endpoint"
destination = var.oke_subnet_api_cidr_block
destination_type = "CIDR_BLOCK"
protocol = "1" # ICMP
stateless = false
icmp_options {
type = 3
code = 4
}
}
egress_security_rules {
description = "Allow pods to communicate with Oracle service network (TCP)."
destination = local.network.gateway.service.all_services.cidr
destination_type = "SERVICE_CIDR_BLOCK"
protocol = "6" # TCP exigido pela doc em vez de 'all'
stateless = false
}
egress_security_rules {
description = "Path discovery to Oracle service network."
destination = local.network.gateway.service.all_services.cidr
destination_type = "SERVICE_CIDR_BLOCK"
protocol = "1" # ICMP
stateless = false
icmp_options {
type = 3
code = 4
}
}
# --- INGRESS RULES ---
ingress_security_rules {
description = "Allow pods on one worker node to communicate with pods on other worker nodes"
protocol = "all"
source = var.oke_subnet_pods_cidr_block
stateless = false
}
ingress_security_rules {
description = "Allows Kubernetes API endpoint to communicate with pods"
protocol = "all"
source = var.oke_subnet_api_cidr_block
stateless = false
}
ingress_security_rules {
description = "Worker nodes to pods communication"
protocol = "all"
source = var.oke_subnet_node_cidr_block
stateless = false
}
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,55 @@
resource "oci_core_subnet" "lb_subnet" {
cidr_block = var.oke_subnet_lb_cidr_block
compartment_id = data.oci_core_vcn.oke_vcn.compartment_id
display_name = "OKE-Loadbalancer-${var.oke_cluster_name}"
dns_label = substr(replace("okelb${var.oke_cluster_name}", "/[-_]/", ""), 0, 15)
prohibit_public_ip_on_vnic = "false"
route_table_id = "${oci_core_route_table.lb_route_table.id}"
security_list_ids = ["${oci_core_security_list.lb_sec_list.id}"]
vcn_id = var.vcn_id
freeform_tags = {
"ManagedBy" = "Terraform"
}
}
resource "oci_core_subnet" "node_subnet" {
cidr_block = var.oke_subnet_node_cidr_block
compartment_id = data.oci_core_vcn.oke_vcn.compartment_id
display_name = "OKE-node-${var.oke_cluster_name}"
dns_label = substr(replace("okenode${var.oke_cluster_name}", "/[-_]/", ""), 0, 15)
prohibit_public_ip_on_vnic = "true"
route_table_id = "${oci_core_route_table.node_route_table.id}"
security_list_ids = ["${oci_core_security_list.node_sec_list.id}"]
vcn_id = var.vcn_id
freeform_tags = {
"ManagedBy" = "Terraform"
}
}
resource "oci_core_subnet" "api_endpoint_subnet" {
cidr_block = var.oke_subnet_api_cidr_block
compartment_id = data.oci_core_vcn.oke_vcn.compartment_id
display_name = "OKE-ApiEndpoint-${var.oke_cluster_name}"
dns_label = substr(replace("okeapi${var.oke_cluster_name}", "/[-_]/", ""), 0, 15)
prohibit_public_ip_on_vnic = "false"
route_table_id = "${oci_core_route_table.api_endpoint_route_table.id}"
security_list_ids = ["${oci_core_security_list.api_endpoint_sec_list.id}"]
vcn_id = var.vcn_id
freeform_tags = {
"ManagedBy" = "Terraform"
}
}
resource "oci_core_subnet" "pods_subnet" {
cidr_block = var.oke_subnet_pods_cidr_block
compartment_id = data.oci_core_vcn.oke_vcn.compartment_id
display_name = "OKE-Pods-${var.oke_cluster_name}"
dns_label = substr(replace("okepod${var.oke_cluster_name}", "/[-_]/", ""), 0, 15)
prohibit_public_ip_on_vnic = "true"
route_table_id = "${oci_core_route_table.pods_route_table.id}"
security_list_ids = ["${oci_core_security_list.pods_sec_list.id}"]
vcn_id = var.vcn_id
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,28 @@
output "debug_oke_image_selection" {
value = {
selected_name = local.selected_image_name
selected_id = local.selected_image_id
k8s_version = local.latest_k8s_version
shape_used = var.oke_node_shape
}
}
resource "local_file" "output_kubeconfig" {
filename = ".${var.oke_cluster_name}.kubeconfig"
content = "${data.oci_containerengine_cluster_kube_config.cluster_kube_config.content}"
}
# OCID do Cluster (usado no bloco 'exec' do provider para gerar o token via OCI CLI)
output "cluster_id" {
value = oci_containerengine_cluster.containerengine_cluster.id
}
output "cluster_endpoint" {
# Se a variável for true, usa o public. Se for false, usa o private.
value = var.oke_api_endpoint_public ? "https://${oci_containerengine_cluster.containerengine_cluster.endpoints[0].public_endpoint}" : "https://${oci_containerengine_cluster.containerengine_cluster.endpoints[0].private_endpoint}"
}
# Certificado CA do Cluster (necessário para a conexão TLS segura do provider)
output "cluster_ca_cert" {
value = yamldecode(data.oci_containerengine_cluster_kube_config.cluster_kube_config.content)["clusters"][0]["cluster"]["certificate-authority-data"]
}

View file

@ -0,0 +1,22 @@
variable tenancy_ocid { type = string }
variable vcn_id { type = string }
variable oke_api_endpoint_public { type = string }
variable oke_node_shape { type = string }
variable oke_node_shape_memory_gb { type = number }
variable oke_node_shape_ocpus { type = number }
variable oke_nodepool_size { type = number }
variable oke_cluster_name { type = string }
variable oke_compartment { type = string }
variable oke_subnet_node_cidr_block { type = string }
variable oke_subnet_lb_cidr_block { type = string }
variable oke_subnet_api_cidr_block { type = string }
variable oke_subnet_pods_cidr_block { type = string }
variable oke_enable_autoscaler { type = bool }
variable oke_enable_cert_manager { type = bool }
variable oke_enable_metric_server { type = bool }
variable oke_autoscaler_min_nodes { type = number }
variable oke_autoscaler_max_nodes { type = number }
variable oke_autoscaler_down_delay_after_add { type = string }
variable oke_autoscaler_down_unneeded_time { type = string }
variable oke_autoscaler_down_unready_time { type = string }
variable oke_autoscaler_skip_nodes_with_system_pods { type = string }

View file

@ -0,0 +1,20 @@
terraform {
required_providers {
oci = {
source = "oracle/oci"
}
kubernetes = {
source = "hashicorp/kubernetes"
configuration_aliases = [ kubernetes ]
}
}
}
# Dentro do seu módulo de bootstrap (modules/oke_bootstrap/main.tf)
resource "time_sleep" "wait_for_k8s_api" {
# 45 segundos costumam ser o "ponto doce" para o OKE estabilizar o RBAC
create_duration = "120s"
# Depende do ID do cluster para começar a contagem
depends_on = [var.oke_cluster_id]
}

View file

@ -0,0 +1,24 @@
resource "random_integer" "suffix" {
min = 1000
max = 9999
# Opcional: O 'keepers' força a geração de um novo número
# apenas quando o nome do cluster mudar.
keepers = {
cluster_name = var.oke_cluster_name
}
}
resource "oci_vault_secret" "cicd_tokens" {
compartment_id = var.oke_compartment
vault_id = var.vault_id
key_id = var.master_key_id
secret_name = "oke-service-account-tokens-${var.oke_cluster_name}-${random_integer.suffix.result}"
description = "Token da Service Account do OKE ${var.oke_cluster_name}"
secret_content {
content_type = "BASE64"
content = base64encode(kubernetes_secret_v1.cicd_admin_token.data["token"])
}
}

View file

@ -0,0 +1,39 @@
# 1. Cria a Service Account
resource "kubernetes_service_account_v1" "cicd_admin" {
depends_on = [time_sleep.wait_for_k8s_api]
metadata {
name = "cicd-admin-sa"
namespace = "kube-system"
}
}
# 2. Cria o Secret para gerar o Token (Necessário no K8s 1.24+)
resource "kubernetes_secret_v1" "cicd_admin_token" {
depends_on = [kubernetes_cluster_role_binding_v1.cicd_admin_binding]
metadata {
name = "cicd-admin-token"
namespace = "kube-system"
annotations = {
"kubernetes.io/service-account.name" = kubernetes_service_account_v1.cicd_admin.metadata[0].name
}
}
type = "kubernetes.io/service-account-token"
}
# 3. permissão de Cluster Admin (Ou uma Role específica para seu CI/CD)
resource "kubernetes_cluster_role_binding_v1" "cicd_admin_binding" {
metadata {
name = "cicd-admin-binding"
}
role_ref {
api_group = "rbac.authorization.k8s.io"
kind = "ClusterRole"
name = "cluster-admin"
}
subject {
kind = "ServiceAccount"
name = kubernetes_service_account_v1.cicd_admin.metadata[0].name
namespace = "kube-system"
}
}

View file

@ -0,0 +1,5 @@
variable vault_id { type = string }
variable master_key_id { type = string }
variable oke_cluster_id { type = string }
variable oke_cluster_name { type = string }
variable oke_compartment { type = string }

View file

@ -0,0 +1,45 @@
# Buscamos os dados da VCN existente para usar em referências
data "oci_core_vcn" "vcn" {
vcn_id = var.vcn_id
}
# Busca o Internet Gateway (IGW) na VCN
data "oci_core_internet_gateways" "igws" {
compartment_id = data.oci_core_vcn.vcn.compartment_id
vcn_id = data.oci_core_vcn.vcn.id
}
# Busca o NAT Gateway (NGW) na VCN
data "oci_core_nat_gateways" "ngws" {
compartment_id = data.oci_core_vcn.vcn.compartment_id
vcn_id = data.oci_core_vcn.vcn.id
}
# Busca o Service Gateway (SGW) na VCN
data "oci_core_service_gateways" "sgws" {
compartment_id = data.oci_core_vcn.vcn.compartment_id
vcn_id = data.oci_core_vcn.vcn.id
}
# Busca a lista de serviços disponíveis na região atual
data "oci_core_services" "all_services" {
filter {
name = "name"
values = ["All .* Services In Oracle Services Network"]
regex = true
}
}
# Busca apenas o Object Storage (usado na rota PUBLIC conforme seu arquivo)
data "oci_core_services" "object_storage" {
filter {
name = "name"
values = [".*Object Storage"]
regex = true
}
}
# 1. Busca dinamicamente os domínios de disponibilidade da região atual
data "oci_identity_availability_domains" "ads" {
compartment_id = var.postgresql_compartment_id
}

View file

@ -0,0 +1,36 @@
locals {
network = {
# OCIDs capturados dinamicamente via Data Source
gateway = {
internet = {
id = length(data.oci_core_internet_gateways.igws.gateways) > 0 ? data.oci_core_internet_gateways.igws.gateways[0].id : null
}
nat = {
id = length(data.oci_core_nat_gateways.ngws.nat_gateways) > 0 ? data.oci_core_nat_gateways.ngws.nat_gateways[0].id : null
}
service = {
id = length(data.oci_core_service_gateways.sgws.service_gateways) > 0 ? data.oci_core_service_gateways.sgws.service_gateways[0].id : null
all_services = {
name = data.oci_core_services.all_services.services[0].name
cidr = data.oci_core_services.all_services.services[0].cidr_block
}
object_storage = {
name = data.oci_core_services.object_storage.services[0].name
cidr = data.oci_core_services.object_storage.services[0].cidr_block
}
}
}
}
}
locals {
# 2. Conta quantos ADs existem na região
ad_count = length(data.oci_identity_availability_domains.ads.availability_domains)
# 3. Define a durabilidade: True se tiver 3 ou mais ADs, False caso contrário
is_regional = local.ad_count >= 3
# 4. Seleciona o AD: Se for regional, o OCI ignora este campo.
# Se não for, pegamos o primeiro AD da lista.
target_ad = local.is_regional ? null : data.oci_identity_availability_domains.ads.availability_domains[0].name
}

View file

@ -0,0 +1,45 @@
resource "oci_psql_db_system" "postgresql_db" {
display_name = "${var.postgresql_name}"
compartment_id = var.postgresql_compartment_id
db_version = "${var.postgresql_version}"
# Shape do banco de dados (Flex permite ajustar OCPUs e Memória)
shape = "${var.postgresql_shape}"
# Configurações para shape Flex
instance_ocpu_count = var.postgresql_ocpu_count
instance_memory_size_in_gbs = var.postgresql_memory_in_gbs
# Quantidade de nós (1 para dev/single, ou mais para Alta Disponibilidade)
instance_count = var.postgresql_instance_count
# Tipo de sistema de armazenamento de alta performance do OCI
system_type = "OCI_OPTIMIZED_STORAGE"
storage_details {
system_type = "OCI_OPTIMIZED_STORAGE"
is_regionally_durable = local.is_regional
# será preenchido se is_regional for false
availability_domain = local.target_ad
}
network_details {
# Se usa existente, pega a var; se não, pega o ID da subnet criada acima
subnet_id = oci_core_subnet.postgresql_subnet.id
}
credentials {
username = "admin_${replace(var.postgresql_name, "-", "")}"
password_details {
password_type = "PLAIN_TEXT"
# O Terraform vai ler a senha gerada e aplicá-la na criação do banco
password = random_password.psql_admin_password.result
}
}
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,22 @@
resource "oci_core_route_table" "postgresql_route_table" {
vcn_id = var.vcn_id
compartment_id = data.oci_core_vcn.vcn.compartment_id
display_name = "postgresql-${var.postgresql_name}"
# Rota para Internet via NAT Gateway
route_rules {
description = "Traffic to the internet"
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
network_entity_id = local.network.gateway.nat.id
}
# Rota para todos os serviços OCI via Service Gateway
route_rules {
description = "Traffic to OCI services"
destination = local.network.gateway.service.all_services.cidr
destination_type = "SERVICE_CIDR_BLOCK"
network_entity_id = local.network.gateway.service.id
}
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,49 @@
resource "oci_core_security_list" "postgresql_sec_list" {
compartment_id = data.oci_core_vcn.vcn.compartment_id
display_name = "postgresql-${var.postgresql_name}"
vcn_id = var.vcn_id
# --- EGRESS RULES ---
egress_security_rules {
description = "Allow PostgreSQL to route anywhere"
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
protocol = "all"
stateless = false
}
# --- INGRESS RULES ---
# INGRESS 1: Path Discovery (ICMP Tipo 3, Código 4)
ingress_security_rules {
description = "Path discovery from anywhere"
protocol = "1"
source = "0.0.0.0/0"
stateless = false
icmp_options {
type = 3
code = 4
}
}
# REGRA DINÂMICA: Cria uma regra de Ingress para CADA cluster autorizado
dynamic "ingress_security_rules" {
# O for_each a lista filtrada do locals
for_each = var.allowed_pod_cidrs
content {
description = "Acesso dos Pods do OKE ao PostgreSQL"
protocol = "6" # TCP
# O ingress_security_rules.value contem o CIDR atual do loop (ex: 10.6.100.0/24)
source = ingress_security_rules.value
stateless = false
tcp_options {
min = 5432
max = 5432
}
}
}
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,13 @@
resource "oci_core_subnet" "postgresql_subnet" {
cidr_block = var.subnet_cidr_block
compartment_id = data.oci_core_vcn.vcn.compartment_id
display_name = "postgresql-${var.postgresql_name}"
dns_label = substr(replace("psql${var.postgresql_name}", "-", ""), 0, 15)
prohibit_public_ip_on_vnic = "true"
route_table_id = "${oci_core_route_table.postgresql_route_table.id}"
security_list_ids = ["${oci_core_security_list.postgresql_sec_list.id}"]
vcn_id = var.vcn_id
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,34 @@
resource "random_integer" "suffix" {
min = 1000
max = 9999
# Opcional: O 'keepers' força a geração de um novo número
# apenas quando o nome do cluster mudar.
keepers = {
cluster_name = var.postgresql_name
}
}
# 1. Gera uma senha forte e aleatória (16 caracteres, com letras, números e símbolos)
resource "random_password" "psql_admin_password" {
length = 32
special = true
# Evita aspas ou caracteres que possam quebrar strings de conexão no futuro
override_special = "!#$%&*-_=+[]{}<>:?"
}
# 2. Cria a Secret dentro do Vault que você tem
resource "oci_vault_secret" "psql_admin_secret" {
compartment_id = var.postgresql_compartment_id
vault_id = var.vault_id
key_id = var.master_key_id
secret_name = "admin-pwd-psql-${var.postgresql_name}-${random_integer.suffix.result}"
description = "Senha do admin do PostgreSQL gerenciado"
secret_content {
content_type = "BASE64"
content = base64encode(random_password.psql_admin_password.result)
}
}

View file

@ -0,0 +1,17 @@
## =================================
## PostgeSQL
## =================================
# Network
variable vcn_id {type = string}
variable subnet_cidr_block {type = string}
variable allowed_pod_cidrs {type = list(string)}
variable postgresql_name {type = string}
variable postgresql_compartment_id {type = string}
variable postgresql_version {type = string}
variable postgresql_shape {type = string}
variable postgresql_ocpu_count {type = number}
variable postgresql_memory_in_gbs {type = number}
variable postgresql_instance_count {type = number}
variable vault_id {type = string}
variable master_key_id {type = string}

View file

@ -0,0 +1,40 @@
# Buscamos os dados da VCN existente para usar em referências
data "oci_core_vcn" "vcn" {
vcn_id = var.vcn_id
}
# Busca o Internet Gateway (IGW) na VCN
data "oci_core_internet_gateways" "igws" {
compartment_id = data.oci_core_vcn.vcn.compartment_id
vcn_id = data.oci_core_vcn.vcn.id
}
# Busca o NAT Gateway (NGW) na VCN
data "oci_core_nat_gateways" "ngws" {
compartment_id = data.oci_core_vcn.vcn.compartment_id
vcn_id = data.oci_core_vcn.vcn.id
}
# Busca o Service Gateway (SGW) na VCN
data "oci_core_service_gateways" "sgws" {
compartment_id = data.oci_core_vcn.vcn.compartment_id
vcn_id = data.oci_core_vcn.vcn.id
}
# Busca a lista de serviços disponíveis na região atual
data "oci_core_services" "all_services" {
filter {
name = "name"
values = ["All .* Services In Oracle Services Network"]
regex = true
}
}
# Busca apenas o Object Storage (usado na rota PUBLIC conforme seu arquivo)
data "oci_core_services" "object_storage" {
filter {
name = "name"
values = [".*Object Storage"]
regex = true
}
}

View file

@ -0,0 +1,24 @@
locals {
network = {
# OCIDs capturados dinamicamente via Data Source
gateway = {
internet = {
id = length(data.oci_core_internet_gateways.igws.gateways) > 0 ? data.oci_core_internet_gateways.igws.gateways[0].id : null
}
nat = {
id = length(data.oci_core_nat_gateways.ngws.nat_gateways) > 0 ? data.oci_core_nat_gateways.ngws.nat_gateways[0].id : null
}
service = {
id = length(data.oci_core_service_gateways.sgws.service_gateways) > 0 ? data.oci_core_service_gateways.sgws.service_gateways[0].id : null
all_services = {
name = data.oci_core_services.all_services.services[0].name
cidr = data.oci_core_services.all_services.services[0].cidr_block
}
object_storage = {
name = data.oci_core_services.object_storage.services[0].name
cidr = data.oci_core_services.object_storage.services[0].cidr_block
}
}
}
}
}

View file

@ -0,0 +1,25 @@
resource "random_integer" "suffix" {
min = 1000
max = 9999
# Opcional: O 'keepers' força a geração de um novo número
# apenas quando o nome do cluster mudar.
keepers = {
cluster_name = var.redis_name
}
}
resource "oci_vault_secret" "redis_endpoint_secret" {
compartment_id = var.redis_compartment_id
vault_id = var.vault_id
key_id = var.master_key_id
secret_name = "redis-endpoint-${var.redis_name}-${random_integer.suffix.result}"
description = "Endpoint de conexao do Redis (IP:Porta)"
secret_content {
content_type = "BASE64"
content = base64encode("${oci_redis_redis_cluster.cluster_redis.primary_endpoint_ip_address}:6379")
}
}

View file

@ -0,0 +1,21 @@
resource "oci_redis_redis_cluster" "cluster_redis" {
display_name = "${var.redis_name}"
compartment_id = var.redis_compartment_id
# Quantidade de nós (1 para Dev/Single, 3 ou mais para Cluster/HA)
node_count = var.redis_count_node
# Memória por em GB (O mínimo na OCI costuma ser 2GB)
node_memory_in_gbs = var.redis_node_memory_in_gbs
# Versão do Redis suportada pela OCI
software_version = var.redis_software_version
# A subnet onde o Redis vai viver (privada, sem IP público)
# Se usa existente, pega a var; se não, pega o ID da subnet criada acima
subnet_id = oci_core_subnet.redis_subnet.id
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,22 @@
resource "oci_core_route_table" "redis_route_table" {
vcn_id = var.vcn_id
compartment_id = data.oci_core_vcn.vcn.compartment_id
display_name = "redis-${var.redis_name}"
# Rota para Internet via NAT Gateway
route_rules {
description = "Traffic to the internet"
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
network_entity_id = local.network.gateway.nat.id
}
# Rota para todos os serviços OCI via Service Gateway
route_rules {
description = "Traffic to OCI services"
destination = local.network.gateway.service.all_services.cidr
destination_type = "SERVICE_CIDR_BLOCK"
network_entity_id = local.network.gateway.service.id
}
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,49 @@
resource "oci_core_security_list" "redis_sec_list" {
compartment_id = data.oci_core_vcn.vcn.compartment_id
display_name = "redis-${var.redis_name}"
vcn_id = var.vcn_id
# --- EGRESS RULES ---
egress_security_rules {
description = "Allow Redis to route anywhere"
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
protocol = "all"
stateless = false
}
# --- INGRESS RULES ---
# INGRESS 1: Path Discovery (ICMP Tipo 3, Código 4)
ingress_security_rules {
description = "Path discovery from anywhere"
protocol = "1"
source = "0.0.0.0/0"
stateless = false
icmp_options {
type = 3
code = 4
}
}
# REGRA DINÂMICA: Cria uma regra de Ingress para CADA cluster autorizado
dynamic "ingress_security_rules" {
# O for_each a lista filtrada do locals
for_each = var.allowed_pod_cidrs
content {
description = "Acesso dos Pods do OKE ao redis"
protocol = "6" # TCP
# O ingress_security_rules.value contem o CIDR atual do loop (ex: 10.6.100.0/24)
source = ingress_security_rules.value
stateless = false
tcp_options {
min = 5432
max = 5432
}
}
}
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,13 @@
resource "oci_core_subnet" "redis_subnet" {
cidr_block = var.subnet_cidr_block
compartment_id = data.oci_core_vcn.vcn.compartment_id
display_name = "redis-${var.redis_name}"
dns_label = substr(replace("psql${var.redis_name}", "-", ""), 0, 15)
prohibit_public_ip_on_vnic = "true"
route_table_id = "${oci_core_route_table.redis_route_table.id}"
security_list_ids = ["${oci_core_security_list.redis_sec_list.id}"]
vcn_id = var.vcn_id
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,10 @@
variable vcn_id {type = string}
variable subnet_cidr_block {type = string}
variable allowed_pod_cidrs {type = list(string)}
variable redis_name {type = string}
variable redis_compartment_id {type = string}
variable redis_count_node {type = number}
variable redis_node_memory_in_gbs {type = number}
variable redis_software_version {type = string}
variable vault_id {type = string}
variable master_key_id {type = string}

View file

@ -0,0 +1,9 @@
output "vault_id" {
value = oci_kms_vault.vault.id
description = "OCID do Vault criado"
}
output "master_key_id" {
value = oci_kms_key.master_key.id
description = "OCID da Master Key para criptografia de secrets"
}

View file

@ -0,0 +1,7 @@
## =================================
## Vault
## =================================
variable vault_name {type = string}
variable vault_compartment {type = string}

View file

@ -0,0 +1,28 @@
# Criação do Vault (Tipo DEFAULT é gratuito)
resource "oci_kms_vault" "vault" {
compartment_id = var.vault_compartment
display_name = "${var.vault_name}"
vault_type = "DEFAULT"
freeform_tags = {
"ManagedBy" = "Terraform"
}
}
# Criação da Master Encryption Key (Obrigatória para encriptar os secrets)
resource "oci_kms_key" "master_key" {
compartment_id = var.vault_compartment
display_name = "master-key-${var.vault_name}"
management_endpoint = oci_kms_vault.vault.management_endpoint
key_shape {
algorithm = "AES"
length = 32 # AES-256
}
protection_mode = "SOFTWARE" # Gratuito. "HSM" tem custo associado.
freeform_tags = {
"ManagedBy" = "Terraform"
}
}

View file

@ -0,0 +1,11 @@
output "debug_bus_image" {
value = one(module.oke_bus[*].debug_oke_image_selection)
}
output "oke_observability" {
value = one(module.oke_observability[*].debug_oke_image_selection)
}
output "oke_nexus" {
value = one(module.oke_nexus[*].debug_oke_image_selection)
}

View file

@ -0,0 +1,4 @@
provider "oci" {
region = var.region
tenancy_ocid = var.tenancy_ocid
}

View file

@ -0,0 +1,82 @@
# Nexus Infrastructure Framework: Multi-Cluster OKE & Data Services
## 1. Overview e Propósito
Este framework de **Infraestrutura como Código (IaC)** foi desenvolvido para provisionar e gerir um ecossistema complexo na **Oracle Cloud Infrastructure (OCI)**. O seu propósito central é a orquestração de múltiplos clusters de Kubernetes (OKE) — denominados **Nexus**, **Bus** e **Observability** — de forma modular e altamente automatizada.
O projeto elimina a configuração manual ao integrar nativamente serviços de base de dados (SQL, NoSQL e Cache) com uma camada de segurança centralizada em **OCI Vault**, garantindo que a infraestrutura seja entregue em estado "ready-to-use" para equipes de desenvolvimento.
---
## 2. Resumo de Funcionamento (Workflow)
O funcionamento da stack baseia-se num ciclo de vida de **quatro fases de execução**, garantindo que as dependências de rede e segurança sejam resolvidas antes do provisionamento das aplicações:
1. **Fase de Descoberta e Rede:** O script consome uma VCN existente e utiliza funções de manipulação de strings e regex para validar e provisionar subnets regionais segregadas (API, Nodes, Pods e Load Balancers) para cada cluster ativado.
2. **Provisionamento de Hardware:** O módulo `oke_cluster` utiliza a arquitetura **Enhanced Cluster**. A lógica interna (`locals.tf`) identifica automaticamente a imagem de SO mais recente e compatível com o processador escolhido (AMD x86_64 ou ARM aarch64).
3. **Bootstrap de Identidade (Mecanismo de Escada):** Para evitar erros de sincronização (Race Conditions), o script implementa um timer de estabilização. Após este período, o provedor Kubernetes configura o RBAC e gera os tokens de acesso.
4. **Consolidação de Segredos:** Todas as credenciais geradas (senhas de DB, tokens K8s, endpoints) são injetadas no **OCI Vault (KMS)**, protegendo os dados sensíveis contra exposição em logs ou no estado do Terraform.
---
## 3. Processos, Funções e Módulos Detalhados
### 📦 Módulo: `oke_cluster` (Infraestrutura de Base)
* **Função:** Abstrair a complexidade da rede nativa e provisionamento de nós.
* **Mecanismo:** Implementa o CNI `OCI_VCN_IP_NATIVE`. Isto permite que cada Pod receba um IP real da VCN, eliminando a sobrecarga de NAT e garantindo performance de rede idêntica a instâncias Bare Metal.
* **Gestão de Imagens:** Utiliza filtros dinâmicos que cruzam a versão do Kubernetes desejada com a disponibilidade de imagens na região, garantindo sempre o uso de patches de segurança atualizados.
### 🛡️ Módulo: `oke_bootstrap` (Configuração de Identidade)
* **Função:** Configurar o acesso administrativo interno e integração de CI/CD.
* **Mecanismo de Escada:** Utiliza o recurso `time_sleep` para aguardar 120 segundos após a criação do cluster. Só então procede à criação da `ServiceAccount` (`cicd-admin-sa`) e do `ClusterRoleBinding`.
* **Saída Segura:** Extrai o token de autenticação do Kubernetes e armazena-o no Vault, permitindo que ferramentas externas consumam o cluster sem necessidade de intervenção manual.
### 🗄️ Camada de Serviços de Dados (`postgresql`, `redis`, `autonomous`)
* **Função:** Provisionar persistência com isolamento de rede.
* **Roteamento:** Configura **Service Gateways** e **NAT Gateways** dedicados. Os bancos de dados residem em subnets privadas, comunicando-se com o Object Storage da Oracle para backups através da rede interna da Oracle, sem exposição à internet.
---
## 4. UI do Resource Manager (ORM): Flexibilidade e Controle
A interface visual definida no `schema.yaml` atua como um **motor de validação e governação** para o utilizador final.
### Funcionalidades da Interface:
* **Visibilidade Dinâmica:** Através de regras `visible: eq:`, a interface oculta automaticamente grupos de variáveis de clusters que não foram marcados como ativos, reduzindo a complexidade do formulário.
* **Validação de CIDR e OCID:** Implementação de `pattern` (Regex) em campos críticos. Isto garante que o utilizador não submeta a stack com erros de sintaxe em endereços de rede ou IDs de compartimento, prevenindo falhas tardias no deploy.
* **Controlo Flexível de Shapes (AMD vs Intel):**
* O formulário deteta a arquitetura do shape escolhido.
* **Limites Adaptativos:** Se o utilizador selecionar um shape Intel (Standard3), a UI ajusta o mínimo de oCPUs para 2. Se selecionar AMD (E5/E6), permite o mínimo de 1 oCPU, respeitando as restrições físicas do hardware Oracle.
* **Segurança de Confirmação:** Atributos de `confirmation: true` em campos de rede e passwords obrigam à dupla digitação, mitigando erros humanos em infraestruturas críticas.
---
## 5. Resumo Técnico de Serviços e Ficheiros
| Serviço | Ficheiro | Função Técnica Principal |
| :--- | :--- | :--- |
| **KMS / Vault** | `vault.tf` | Gestão de chaves AES-256 e encriptação de segredos. |
| **PostgreSQL** | `postgresql.tf` | DB Gerenciado com rotação de passwords via `random_password`. |
| **Redis** | `redis.tf` | Cache distribuído em cluster com persistência privada. |
| **Object Storage**| `bucket.tf` | Buckets com versionamento para armazenamento de logs e artefatos. |
| **OKE** | `oke_cluster.tf` | Orquestração de containers com suporte a IP Native e Auto-scaling. |
---
## 🛠️ Como Utilizar
1. Comprima todos os ficheiros `.tf` e o `.yaml` num arquivo `.zip`.
2. No console OCI, aceda a **Developer Services > Resource Manager > Stacks**.
3. Faça o upload do arquivo zip.
4. Configure os parâmetros através da UI customizada e execute **Plan** & **Apply**.
---
* Documentação gerada para a Nexus Infrastructure Stack - Março 2026*

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
variable "region" {}
variable "tenancy_ocid" {}
### variable "user_ocid" {
### type = string
### description = "OCID do Usuário"
### }
###
### variable "fingerprint" {
### type = string
### description = "Fingerprint da chave API"
### }
###
### variable "private_key_path" {
### type = string
### description = "Caminho local para a chave privada"
### }
###
### variable "region" {
### type = string
### description = "Região do OCI"
### }
###
### variable "private_key_password" {
### type = string
### description = "Senha da chave privada (se houver)"
### default = null
### }

View file

@ -0,0 +1,55 @@
##
## Autonomous Database JSON
##
variable autonomous_json_enable {
type = bool
default = false
}
# Network
variable autonomous_json_vcn_id {
type = string
default = ""
}
# Resource
variable autonomous_json_name {
type = string
default = ""
}
variable autonomous_json_vcn_compartment {
type = string
default = ""
}
variable autonomous_json_compartment_id {
type = string
default = ""
}
variable autonomous_json_subnet_cidr_block {
type = string
default = ""
}
variable autonomous_json_compute_model {
type = string
default = ""
}
variable autonomous_json_compute_count {
type = number
default = 1
}
variable autonomous_json_data_storage_size_in_tbs {
type = number
default = 1
}
variable autonomous_json_is_auto_scaling_enabled {
type = bool
default = false
}

View file

@ -0,0 +1,33 @@
## =================================
## Bucket
## =================================
variable bucket_enable {
type = bool
default = false
}
variable bucket_compartment {
type = string
default = ""
}
variable bucket_name {
type = string
default = ""
}
variable bucket_access_type {
type = string
default = ""
}
variable bucket_storage_tier {
type = string
default = ""
}
variable bucket_versioning {
type = string
default = ""
}

View file

@ -0,0 +1,147 @@
variable oke_bus_enable {
type = bool
default = false
}
# --- Network: VCN
variable oke_bus_vcn_id {
type = string
default = ""
}
# --- Network: Subnets
variable oke_bus_vcn_compartment {
type = string
default = ""
}
variable oke_bus_subnet_api_cidr_block {
type = string
default = ""
}
variable oke_bus_subnet_node_cidr_block {
type = string
default = ""
}
variable oke_bus_subnet_lb_cidr_block {
type = string
default = ""
}
variable oke_bus_subnet_pods_cidr_block {
type = string
default = ""
}
# --- Network: Regras de acesso ---
variable oke_bus_show_advanced {
type = string
default = ""
}
variable oke_bus_enable_psql_access {
type = bool
default = false
}
variable oke_bus_enable_redis_access {
type = bool
default = false
}
variable oke_bus_enable_autonomous_json_access {
type = bool
default = false
}
variable "oke_bus_api_endpoint_public" {
type = bool
default = false
}
variable oke_bus_create_service_account {
type = bool
default = false
}
# --- Identificação do Cluster ---
variable "oke_bus_cluster_name" {
type = string
default = ""
}
variable "oke_bus_cluster_compartment_id" {
type = string
default = ""
}
# --- Configuração de Hardware (Node Pool) ---
variable "oke_bus_node_shape" {
type = string
default = ""
}
variable "oke_bus_node_count" {
type = number
default = 1
}
variable "oke_bus_node_memory_gb" {
type = number
default = 1
}
variable "oke_bus_node_ocpus" {
type = number
default = 1
}
# --- Add-ons ---
variable oke_bus_enable_autoscaler {
type = bool
default = false
}
variable oke_bus_enable_metric_server {
type = bool
default = false
}
variable oke_bus_enable_cert_manager {
type = bool
default = false
}
# --- Add-ons: Autoscaler Settings ---
variable oke_bus_autoscaler_min_nodes {
type = number
default = 1
}
variable oke_bus_autoscaler_max_nodes {
type = number
default = 1
}
variable oke_bus_autoscaler_down_delay_after_add {
type = string
default = ""
}
variable oke_bus_autoscaler_down_unneeded_time {
type = string
default = ""
}
variable oke_bus_autoscaler_down_unready_time {
type = string
default = ""
}
variable oke_bus_autoscaler_skip_nodes_with_system_pods {
type = bool
default = false
}

View file

@ -0,0 +1,145 @@
variable oke_nexus_enable {
type = bool
default = false
}
# --- Network: VCN
variable oke_nexus_vcn_compartment {
type = string
default = ""
}
variable oke_nexus_vcn_id {
type = string
default = ""
}
variable oke_nexus_subnet_api_cidr_block {
type = string
default = ""
}
variable oke_nexus_subnet_node_cidr_block {
type = string
default = ""
}
variable oke_nexus_subnet_lb_cidr_block {
type = string
default = ""
}
variable oke_nexus_subnet_pods_cidr_block {
type = string
default = ""
}
# --- Network: Regras de acesso ---
variable oke_nexus_show_advanced {
type = string
default = ""
}
variable oke_nexus_enable_psql_access {
type = bool
default = false
}
variable oke_nexus_enable_redis_access {
type = bool
default = false
}
variable oke_nexus_enable_autonomous_json_access {
type = bool
default = false
}
variable "oke_nexus_api_endpoint_public" {
type = bool
default = false
}
variable oke_nexus_create_service_account {
type = bool
default = false
}
# --- Identificação do Cluster ---
variable "oke_nexus_cluster_name" {
type = string
default = ""
}
variable "oke_nexus_cluster_compartment_id" {
type = string
default = ""
}
# --- Configuração de Hardware (Node Pool) ---
variable "oke_nexus_node_shape" {
type = string
default = ""
}
variable "oke_nexus_node_count" {
type = number
default = 1
}
variable "oke_nexus_node_memory_gb" {
type = number
default = 1
}
variable "oke_nexus_node_ocpus" {
type = number
default = 1
}
# --- Add-ons ---
variable oke_nexus_enable_autoscaler {
type = bool
default = false
}
variable oke_nexus_enable_metric_server {
type = bool
default = false
}
variable oke_nexus_enable_cert_manager {
type = bool
default = false
}
# --- Add-ons: Autoscaler Settings ---
variable oke_nexus_autoscaler_min_nodes {
type = number
default = 1
}
variable oke_nexus_autoscaler_max_nodes {
type = number
default = 1
}
variable oke_nexus_autoscaler_down_delay_after_add {
type = string
default = ""
}
variable oke_nexus_autoscaler_down_unneeded_time {
type = string
default = ""
}
variable oke_nexus_autoscaler_down_unready_time {
type = string
default = ""
}
variable oke_nexus_autoscaler_skip_nodes_with_system_pods {
type = bool
default = false
}

View file

@ -0,0 +1,147 @@
variable oke_o11y_enable {
type = bool
default = false
}
# --- Network: VCN
variable oke_o11y_vcn_id {
type = string
default = ""
}
# --- Network: Subnets
variable oke_o11y_vcn_compartment {
type = string
default = ""
}
variable oke_o11y_subnet_api_cidr_block {
type = string
default = ""
}
variable oke_o11y_subnet_node_cidr_block {
type = string
default = ""
}
variable oke_o11y_subnet_lb_cidr_block {
type = string
default = ""
}
variable oke_o11y_subnet_pods_cidr_block {
type = string
default = ""
}
# --- Network: Regras de acesso ---
variable oke_o11y_show_advanced {
type = string
default = ""
}
variable oke_o11y_enable_psql_access {
type = bool
default = false
}
variable oke_o11y_enable_redis_access {
type = bool
default = false
}
variable oke_o11y_enable_autonomous_json_access {
type = bool
default = false
}
variable "oke_o11y_api_endpoint_public" {
type = bool
default = false
}
variable oke_o11y_create_service_account {
type = bool
default = false
}
# --- Identificação do Cluster ---
variable "oke_o11y_cluster_name" {
type = string
default = ""
}
variable "oke_o11y_cluster_compartment_id" {
type = string
default = ""
}
# --- Configuração de Hardware (Node Pool) ---
variable "oke_o11y_node_shape" {
type = string
default = ""
}
variable "oke_o11y_node_count" {
type = number
default = 1
}
variable "oke_o11y_node_memory_gb" {
type = number
default = 1
}
variable "oke_o11y_node_ocpus" {
type = number
default = 1
}
# --- Add-ons ---
variable oke_o11y_enable_autoscaler {
type = bool
default = false
}
variable oke_o11y_enable_metric_server {
type = bool
default = false
}
variable oke_o11y_enable_cert_manager {
type = bool
default = false
}
# --- Add-ons: Autoscaler Settings ---
variable oke_o11y_autoscaler_min_nodes {
type = number
default = 1
}
variable oke_o11y_autoscaler_max_nodes {
type = number
default = 1
}
variable oke_o11y_autoscaler_down_delay_after_add {
type = string
default = ""
}
variable oke_o11y_autoscaler_down_unneeded_time {
type = string
default = ""
}
variable oke_o11y_autoscaler_down_unready_time {
type = string
default = ""
}
variable oke_o11y_autoscaler_skip_nodes_with_system_pods {
type = bool
default = false
}

View file

@ -0,0 +1,85 @@
## =================================
## PostgeSQL
## =================================
variable postgresql_enable {
type = bool
default = false
}
# Network
variable postgresql_vcn_compartment {
type = string
default = ""
}
variable postgresql_vcn_id {
type = string
default = ""
}
variable postgresql_subnet_cidr_block {
type = string
default = ""
}
# Resource
variable postgresql_name {
type = string
default = ""
}
variable postgresql_compartment_id {
type = string
default = ""
}
variable postgresql_version {
type = number
default = 1
}
variable postgresql_shape {
type = string
default = ""
}
variable postgresql_ocpu_count {
type = number
default = 1
}
variable postgresql_ocpu_e5 {
type = number
default = 1
}
variable postgresql_ocpu_e6 {
type = number
default = 1
}
variable postgresql_ocpu_s3 {
type = number
default = 1
}
variable postgresql_memory_in_gbs {
type = number
default = 1
}
variable postgresql_memory_amd {
type = number
default = 1
}
variable postgresql_memory_s3 {
type = number
default = 1
}
variable postgresql_instance_count {
type = number
default = 1
}

View file

@ -0,0 +1,48 @@
## =================================
## Redis
## =================================
variable redis_enable {
type = bool
default = false
}
variable redis_vcn_compartment {
type = string
default = ""
}
variable redis_vcn_id {
type = string
default = ""
}
variable redis_subnet_cidr_block {
type = string
default = ""
}
variable redis_name {
type = string
default = ""
}
variable redis_compartment_id {
type = string
default = ""
}
variable redis_count_node {
type = number
default = 1
}
variable redis_node_memory_in_gbs {
type = number
default = 1
}
variable redis_software_version {
type = string
default = ""
}

View file

@ -0,0 +1,18 @@
## =================================
## Vault
## =================================
variable vault_enable {
type = bool
default = false
}
variable vault_name {
type = string
default = ""
}
variable vault_compartment {
type = string
default = ""
}