infracloud/invista/nexus/OCI-DEV-NEXUS.md

431 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# OCI — Ambiente DEV Nexus (cmp-dev-nexus)
> **Atualizado:** 2026-03-03 (sessão 2) | **Responsável:** Tiago Ribeiro
> **Foco:** Compartment `cmp-dev-nexus` — o que existe, o que o Terraform sobe e o que se comunica com o quê.
---
## 1. Compartments
```
invistacloud (root)
└── cmp-top-invista
└── cmp-dev-inv (OCID: aaaaaaaa76x3...) ← compartment pai — rede, observabilidade
└── cmp-dev-nexus (OCID: aaaaaaaahycc62za...) ← clusters, API gateways, buckets MFE
```
| Compartment | OCID | Papel |
|---|---|---|
| `cmp-dev-inv` | `ocid1.compartment.oc1..aaaaaaaa76x3nykkjwvctpr6px34dysu3pbg7p62h2r65fegt7fvbrioll3a` | Rede (`vcn-oke`), Observabilidade, Bastion |
| `cmp-dev-nexus` | `ocid1.compartment.oc1..aaaaaaaahycc62za6ikthlhauvarvbdixc7xpjjmcrame3cirhu2kz74ddma` | Clusters OKE, API Gateways, Buckets MFE, tfstate |
> **Regra no Terraform:**
> - `local.compartment_id` → `cmp-dev-inv` (variável `existing_compartment_id` no `terraform.ci.tfvars`)
> - `local.cluster_compartment_id` → `cmp-dev-nexus` (derivado de `cluster_compartment_id_map`)
---
## 2. O que existe em cmp-dev-nexus
### 2.1 Clusters OKE — Terraform
| Cluster | OCID (sufixo) | K8s | Node Pool | Nodes |
|---|---|---|---|---|
| `cls-dev-nexus` | `…cobrewkvc3a` | v1.34.1 | `np-dev-1` | 3× VM.Standard.E4.Flex (2 OCPU / 16 GB) |
| `cls-dev-barramento` | `…cifn2eknv6q` | v1.34.1 | `np-dev-2` | 3× VM.Standard.E4.Flex |
| `cls-dev-observabilidade` | `…crszb62robq` | v1.34.1 | `np-dev-3` | 3× VM.Standard.E4.Flex |
Workers usam subnets em **`cmp-dev-inv` / `vcn-oke`** (`sbn-workers-1/2/3`). O compartment dos workers é `cmp-dev-nexus`.
### 2.2 Load Balancers OKE — OKE-managed
Criados automaticamente pelo Kubernetes (Services do tipo `LoadBalancer`). Ficam nas subnets `sbn-lb-1/2` da `vcn-oke`.
| IP | Visibilidade | Cluster |
|---|---|---|
| `10.110.133.131` | Privado | cls-dev-barramento |
| `10.110.135.3` | Privado | cls-dev-nexus |
| `10.110.129.64` | Privado | cls-dev-observabilidade |
| `10.110.143.54` | Privado | cls-dev-nexus |
| `137.131.236.202` | **Público** | cls-dev-nexus |
### 2.3 API Gateways PUBLIC por MFE — Terraform
Criados pelo módulo `modules/api_gateway_mfe`. **Um gateway por MFE** (OCI restrição: só um deployment com `path_prefix="/"` por gateway). Servem arquivos estáticos dos MFEs via Object Storage.
| Nome | Compartment | Subnet | Hostname OCI |
|---|---|---|---|
| `api-gw-mfe-shell-dev` | `cmp-dev-nexus` | `sbn-lb-1` | `la6rwvy7aelvtkhemy4mvicprq.apigateway.sa-saopaulo-1.oci.customer-oci.com` |
| `api-gw-mfe-auth-dev` | `cmp-dev-nexus` | `sbn-lb-1` | `imz5toeub62yeomplgnq3yrwgm.apigateway.sa-saopaulo-1.oci.customer-oci.com` |
| `api-gw-mfe-user-dev` | `cmp-dev-nexus` | `sbn-lb-1` | `gstnru35evwvofk7ptgsfa6uwa.apigateway.sa-saopaulo-1.oci.customer-oci.com` |
| `api-gw-mfe-person-dev` | `cmp-dev-nexus` | `sbn-lb-1` | `e3yyi4vpyp7awgkac7d46hmfsq.apigateway.sa-saopaulo-1.oci.customer-oci.com` |
| `api-gw-mfe-poc-dev` | `cmp-dev-nexus` | `sbn-lb-1` | `in3a3xf2c5ndft6nrzsvla7z6u.apigateway.sa-saopaulo-1.oci.customer-oci.com` |
| `api-gw-mfe-formalization-dev` | `cmp-dev-nexus` | `sbn-lb-1` | `ptv42bse7jtql4txgrr7p4sqyq.apigateway.sa-saopaulo-1.oci.customer-oci.com` |
Cada gateway tem **1 deployment** com `path_prefix="/"` contendo:
- `GET /``mfe-{name}-dev/o/index.html`
- `GET /{path*}``mfe-{name}-dev/o/{path}`
> Módulo TF: `tf_oci_clusters/modules/api_gateway_mfe`
> Após `terraform apply`, hostnmaes disponíveis no output `mfe_gateway_hostnames` da pipeline.
> Cada MFE tem CNAME no Cloudflare apontando para seu hostname único.
### 2.4 API Gateway PRIVATE — Manual (`api-gateway-nexus-dev`)
Serve o backend dos MSes. **Não gerenciado pelo Terraform.**
| Campo | Valor |
|---|---|
| Nome | `api-gateway-nexus-dev` |
| Tipo | **PRIVATE** |
| Compartment | `cmp-dev-nexus` |
| Subnet | `SBNT-DEV` (10.6.0.0/24) — **VCN-DEV** (manual) |
| Hostname | `dnqe6ufrommkqxtfp7k2ehrbmu.apigateway.sa-saopaulo-1.oci.customer-oci.com` |
| Gerenciado por | **Manual** (OCI Console) |
**11 deployments ativos:**
| Path | Serviço |
|---|---|
| `/api/auth` | ms-auth |
| `/api/user` | ms-user |
| `/api/user-external` | ms-user (externo) |
| `/api/user-internal` | ms-user (interno) |
| `/api/person` | ms-person |
| `/api/sso` | ms-auth-sso |
| `/api/role` | ms-role |
| `/api/poc` | ms-poc |
| `/api/cache` | ms-cache |
| `/api/commercial-manager` | ms-commercial-manager |
| `/mfe-shell` | mfe-shell (legado — substituído pelo `api-gateway-dev`) |
### 2.5 Object Storage — Buckets MFE — Terraform
Criados pelo módulo `api_gateway_mfe` junto com o gateway. Namespace: `grbb7qzeuoag`.
| Bucket | Acesso | Conteúdo |
|---|---|---|
| `mfe-auth-dev` | ObjectReadWithoutList | Build Angular do mfe-auth |
| `mfe-user-dev` | ObjectReadWithoutList | Build Angular do mfe-user |
| `mfe-person-dev` | ObjectReadWithoutList | Build Angular do mfe-person |
| `mfe-poc-dev` | ObjectReadWithoutList | Build Angular do mfe-poc |
| `mfe-shell-dev` | ObjectReadWithoutList | Build Angular do mfe-shell |
| `mfe-formalization-dev` | ObjectReadWithoutList | Build Angular do mfe-formalization |
> O conteúdo dos buckets é publicado pelas **pipelines dos repos de MFE** (branch `devops`), usando o template `azure-pipelines-templates/mfe/deploy-mfe-oci.yaml`.
### 2.6 Object Storage — Buckets Manuais
| Bucket | Uso |
|---|---|
| `tfstate-gqysee` | Terraform remote state |
| `tfstate-inidhr` | Terraform remote state |
| `tfstate-terraform` | Terraform remote state principal |
| `invista-inventcloud-bucket3` | Uso geral |
---
## 3. Rede — O que cmp-dev-nexus usa
Os recursos de rede ficam em **`cmp-dev-inv`** mas os clusters e gateways de `cmp-dev-nexus` se comunicam através delas.
### 3.1 VCN `vcn-oke` — Terraform (em `cmp-dev-inv`)
| Subnet | CIDR | Tipo | Usado por |
|---|---|---|---|
| `sbn-workers-1` | `10.110.0.0/20` | Pública | Worker nodes (cls-dev-nexus) |
| `sbn-workers-2` | `10.110.16.0/20` | Pública | Worker nodes (cls-dev-barramento) |
| `sbn-workers-3` | `10.110.32.0/20` | Pública | Worker nodes (cls-dev-observabilidade) |
| `sbn-lb-1` | `10.110.128.0/20` | Pública | OKE LBs + **api-gateway-dev** (PUBLIC) |
| `sbn-lb-2` | `10.110.144.0/20` | Pública | OKE LBs |
| `sbn-api-gateway` | `10.110.192.0/20` | **Privada** | Disponível — não usada atualmente |
**Gateways de rede (vcn-oke):**
| Recurso | Tipo | Função |
|---|---|---|
| `igw-oke` | Internet Gateway | Saída para internet (workers públicos, LBs, api-gateway) |
| `nat-oke` | NAT Gateway | Saída privada (sbn-api-gateway) |
| `sgw-oke` | Service Gateway | Acesso a OCI Object Storage (sem internet) |
| DRG | Dynamic Routing | Interconexão `vcn-oke``VCN-DEV` |
### 3.2 VCN `VCN-DEV` — Manual (em `cmp-dev-inv`)
| Campo | Valor |
|---|---|
| CIDR | `10.6.0.0/16` |
| Subnet relevante | `SBNT-DEV``10.6.0.0/24` (usada pelo `api-gateway-nexus-dev`) |
| Conectividade | DRG ↔ `vcn-oke` (roteamento para OKE workers) |
---
## 4. Diagrama de Comunicação
```
INTERNET
▼ [Cloudflare CNAME proxied]
api-gw-mfe-{name}-dev (PUBLIC · sbn-lb-1 · vcn-oke · cmp-dev-nexus) [um por MFE]
├── GET /index.html → Object Storage (mfe-{name}-dev bucket · cmp-dev-nexus)
└── GET /{path*} → Object Storage (arquivo estático do MFE)
INTERNET
▼ [Cloudflare → LB Test_Crivo_Dev (VCN-Shared)]
│ DRG: VCN-Shared → VCN-DEV
api-gateway-nexus-dev (PRIVATE · SBNT-DEV · VCN-DEV · cmp-dev-nexus)
│ DRG: VCN-DEV → vcn-oke
OKE Load Balancers (sbn-lb-1/2 · vcn-oke · cmp-dev-nexus)
K8s Services / Ingress (cls-dev-nexus)
Pods (sbn-workers-1/2/3 · vcn-oke · cmp-dev-nexus)
```
---
## 5. Terraform vs Manual vs OKE-managed
| Recurso | Gerenciado por | Onde criar/alterar |
|---|---|---|
| VCN `vcn-oke` + subnets + gateways + route tables | **Terraform** | `tf_oci_clusters/modules/network` |
| 3× OKE clusters + node pools (9 workers) | **Terraform** | `tf_oci_clusters/modules/oke_cluster` |
| 6× `api-gw-mfe-*-dev` (PUBLIC, um por MFE) + 6 deployments | **Terraform** | `tf_oci_clusters/modules/api_gateway_mfe` |
| 6× buckets `mfe-*-dev` (Object Storage) | **Terraform** | `tf_oci_clusters/modules/api_gateway_mfe` |
| ArgoCD (Helm v7.3.0) nos 3 clusters | **Terraform** | `tf_oci_clusters/environments/dev/argocd.tf` |
| Kubeconfigs `~/.kube/config-dev-{1,2,3}` | **Terraform** (local-exec) | Gerados no `terraform apply` |
| Observabilidade (alarms CPU, log group, dashboard) | **Terraform** | `tf_oci_clusters/modules/observability` |
| `api-gateway-nexus-dev` (PRIVATE) + 11 deployments | **Manual** (OCI Console) | Console OCI |
| `VCN-DEV` (10.6.0.0/16) + SBNT-DEV | **Manual** | Console OCI |
| DRG-Invista-Shared + attachments | **Manual** | Console OCI |
| LB `Test_Crivo_Dev` (10.8.4.127, VCN-Shared) | **Manual** | Console OCI |
| DNS Cloudflare (`*.invista.com.br`) | **Manual** | Cloudflare Dashboard |
| Conteúdo dos buckets MFE (builds Angular) | **Pipeline CI** | Repos `mfe-*` branch `devops` |
| 5× OKE Load Balancers | **OKE-managed** | K8s YAML (Services) |
| Buckets `tfstate-*` | **Manual** | Console OCI (criados uma vez) |
---
## 6. Terraform — Como executar
### 6.1 Repositório
| Campo | Valor |
|---|---|
| Azure DevOps | CN-Squad / Invista FIDC - Nexus |
| Repo | `tf_oci_clusters` |
| Pipeline | `terraform-tf_oci_clusters` (ID 51) |
| Variable Group | `oci-terraform` (ID 34) |
| Backend (remote state) | Object Storage OCI — bucket `tfstate-terraform` em `cmp-dev-nexus` |
### 6.2 Estrutura de arquivos
```
tf_oci_clusters/
├── environments/dev/
│ ├── main.tf # Clusters OKE, rede, bastion, observabilidade
│ ├── api_gateway_mfe.tf # API Gateway PUBLIC + deployments + buckets MFE
│ ├── argocd.tf # ArgoCD Helm install + kubeconfig gerado
│ ├── backend.tf # Remote state (S3-compat Object Storage)
│ ├── providers.tf # OCI provider (região, tenancy, user, key)
│ ├── variables.tf # Declaração de todas as variáveis
│ └── terraform.ci.tfvars # Valores do ambiente DEV (usado pela pipeline)
├── modules/
│ ├── network/ # VCN, subnets, IGW, NAT, SGW, route tables, security lists
│ ├── oke_cluster/ # OKE cluster + node pool
│ ├── api_gateway_mfe/ # API Gateway PUBLIC + buckets Object Storage + deployments
│ ├── observability/ # OCI Monitoring alarms, Log Group, dashboards
│ ├── compartment/ # Criação de compartment
│ ├── iam_domain/ # IAM Domain (OIDC/IDCS)
│ └── iam_service_accounts/ # IAM service accounts
└── argocd/
├── values.yaml # Helm values do ArgoCD
└── application-dev.yaml # ArgoCD Application GitOps manifest
```
### 6.3 terraform.ci.tfvars DEV — Campos obrigatórios e o que significam
```hcl
# ── Ambiente ────────────────────────────────────────────────
env_name = "dev" # Sufixo em todos os recursos (ex: api-gateway-dev)
# ── Compartments (preencher com OCIDs do Console OCI) ───────
create_compartment = false # cmp-dev-inv já existe — não recriar
create_cluster_compartment = false # cmp-dev-nexus já existe — não recriar
existing_compartment_id = "ocid1.compartment.oc1..aaaaaaaa76x3..." # cmp-dev-inv
cluster_compartment_id_map = {
"1" = "ocid1.compartment.oc1..aaaaaaaahycc62za..." # cmp-dev-nexus
"2" = "ocid1.compartment.oc1..aaaaaaaahycc62za..."
"3" = "ocid1.compartment.oc1..aaaaaaaahycc62za..."
}
# ── Rede ────────────────────────────────────────────────────
vcn_cidr = "10.110.0.0/16" # CIDR da vcn-oke
# ── Clusters OKE ────────────────────────────────────────────
kubernetes_version = "v1.34.1"
node_shape = "VM.Standard.E4.Flex"
ocpus = 2
memory_in_gbs = 16
node_pool_size_up = 3 # Workers ativos
node_pool_size_down = 0 # Workers no scale-down (desliga todos)
scale_mode = "up" # "up" = produtivo, "down" = econômico
# ── Bastion (debug/acesso SSH) ───────────────────────────────
enable_bastion = true
admin_cidr = "SEU_IP_PUBLICO/32" # IP liberado para SSH no bastion
ssh_public_key_path = "../../config/ssh/ci_ssh_key.pub" # arquivo em config/ssh/
# ── API Gateway MFE ─────────────────────────────────────────
enable_api_gateway_mfe = true
mfe_object_namespace = "grbb7qzeuoag" # namespace fixo do tenancy
# ── Observabilidade ─────────────────────────────────────────
observability_enable_dashboards = true
observability_dashboard_import_file = "../../dashboards/oke-observability-import.json"
```
### 6.4 Variáveis sensíveis — Variable Group `oci-terraform` (Azure DevOps)
Estas variáveis **não ficam no tfvars** — são injetadas pela pipeline via Variable Group:
| Variável | Descrição | De onde pegar |
|---|---|---|
| `TF_VAR_tenancy_ocid` | OCID do tenancy | Console OCI → Perfil → Tenancy |
| `TF_VAR_user_ocid` | OCID do usuário de serviço | Console OCI → IAM → Users |
| `TF_VAR_fingerprint` | Fingerprint da chave API | Console OCI → IAM → Users → API Keys |
| `TF_VAR_private_key_path` | Caminho da chave privada no agent | Arquivo no Azure DevOps agent |
| `TF_VAR_oci_region` | Região OCI | `sa-saopaulo-1` |
| `TF_VAR_compartment_parent_ocid` | OCID do compartment pai (cmp-top-invista) | Console OCI → IAM → Compartments |
| `OCI_CLI_KEY_FILE` | Caminho da chave para OCI CLI (kubeconfig) | Mesmo arquivo da chave privada |
### 6.5 Fluxo da pipeline
```
Push para main (tf_oci_clusters)
└─► Stage: Bootstrap
terraform init -backend-config=...
terraform validate
Stage: DEV — Plan
terraform plan -var-file=terraform.ci.tfvars -out=plan.tfplan
Stage: DEV — Approval ← aprovação manual no Azure DevOps
Stage: DEV — Apply
terraform apply plan.tfplan
```
### 6.6 O que o terraform apply cria no cmp-dev-nexus
```
6 API Gateways PUBLIC (um por MFE, sbn-lb-1, vcn-oke, cmp-dev-nexus):
api-gw-mfe-shell-dev → deploy-mfe-shell-dev
api-gw-mfe-auth-dev → deploy-mfe-auth-dev
api-gw-mfe-user-dev → deploy-mfe-user-dev
api-gw-mfe-person-dev → deploy-mfe-person-dev
api-gw-mfe-poc-dev → deploy-mfe-poc-dev
api-gw-mfe-formalization-dev → deploy-mfe-formalization-dev
6 buckets Object Storage:
mfe-auth-dev / mfe-user-dev / mfe-person-dev
mfe-poc-dev / mfe-shell-dev / mfe-formalization-dev
(namespace: grbb7qzeuoag · acesso: ObjectReadWithoutList)
3 clusters OKE (em cmp-dev-nexus, usando vcn-oke de cmp-dev-inv):
cls-dev-nexus → np-dev-1 (3× VM.Standard.E4.Flex)
cls-dev-barramento → np-dev-2 (3× VM.Standard.E4.Flex)
cls-dev-observabilidade → np-dev-3 (3× VM.Standard.E4.Flex)
ArgoCD v7.3.0 em cada cluster (Helm, namespace argocd)
Kubeconfigs: ~/.kube/config-dev-{1,2,3}
```
---
## 7. URLs de acesso
### 7.1 API Gateways PUBLIC — MFEs (um por MFE)
Hostnames gerados pelo OCI após o `terraform apply`. Ver output `mfe_gateway_hostnames` na pipeline.
> Cada MFE tem seu **próprio gateway** (OCI restringe a 1 deployment com `path_prefix="/"` por gateway).
> Cada DNS (Cloudflare CNAME) aponta para o hostname exclusivo do gateway do seu MFE.
| MFE | DNS (Cloudflare proxied CNAME) | Status |
|---|---|---|
| Shell | `mfe-shell-dev-oci.invista.com.br` | ✅ CNAME configurado |
| Auth | `mfe-auth-dev-oci.invista.com.br` | ✅ CNAME configurado |
| User | `mfe-user-dev-oci.invista.com.br` | ✅ CNAME configurado |
| Person | `mfe-person-dev-oci.invista.com.br` | ✅ CNAME configurado |
| PoC | `mfe-poc-dev-oci.invista.com.br` | ✅ CNAME configurado |
| Formalization | `mfe-formalization-dev-oci.invista.com.br` | ✅ CNAME configurado |
> Retornam 404 até que a pipeline de cada MFE (branch `devops`) publique o build no bucket.
> Se o gateway for recriado pelo Terraform, os hostnames OCI mudam → atualizar os CNAMEs.
### 7.2 API Gateway PRIVATE (`api-gateway-nexus-dev`) — Backend MS
Acessado via Cloudflare → LB Test_Crivo_Dev → DRG → VCN-DEV → gateway.
| Path | Serviço | URL interna |
|---|---|---|
| `/api/auth` | ms-auth | `*.invista.com.br/api/auth` |
| `/api/user` | ms-user | `*.invista.com.br/api/user` |
| `/api/person` | ms-person | `*.invista.com.br/api/person` |
| `/api/sso` | ms-auth-sso | `*.invista.com.br/api/sso` |
| `/api/role` | ms-role | `*.invista.com.br/api/role` |
| `/api/poc` | ms-poc | `*.invista.com.br/api/poc` |
| `/api/cache` | ms-cache | `*.invista.com.br/api/cache` |
| `/api/commercial-manager` | ms-commercial-manager | `*.invista.com.br/api/commercial-manager` |
| `/api/user-external` | ms-user-external | `*.invista.com.br/api/user-external` |
| `/api/user-internal` | ms-user-internal | `*.invista.com.br/api/user-internal` |
### 7.3 ArgoCD (somente via VPN/Bastion)
| Cluster | URL |
|---|---|
| `cls-dev-nexus` | `https://argocd.dev-01.interno.invista.com.br` |
| `cls-dev-barramento` | `https://argocd.dev-02.interno.invista.com.br` |
| `cls-dev-observabilidade` | `https://argocd.dev-03.interno.invista.com.br` |
### 7.4 OKE público direto
| Recurso | Endereço |
|---|---|
| OKE LB público (cls-dev-nexus) | `137.131.236.202` |
---
## 8. O que é manual e precisa ser preenchido manualmente
| Item | Onde preencher | Observação |
|---|---|---|
| `admin_cidr` no tfvars | Seu IP público `/32` | Mudar quando IP mudar |
| Variable Group `oci-terraform` no Azure DevOps | DevOps → Pipelines → Library | OCIDs, fingerprint, chave privada |
| DNS Cloudflare dos MFEs | Cloudflare Dashboard | CNAME → hostname do `api-gateway-dev` (output do pipeline) |
| `api-gateway-nexus-dev` deployments | OCI Console | Adicionar/remover MSes manualmente |
| Conteúdo dos buckets MFE | Pipeline MFE (branch `devops`) | Automático após push no repo do MFE |
| Kubeconfig local | `terraform apply` (automático) ou `oci ce cluster create-kubeconfig ...` | Precisa de OCI CLI configurado |
---
## 9. Referências — OCIDs e Endpoints
| Recurso | OCID / Valor |
|---|---|
| Tenancy | `ocid1.tenancy.oc1..aaaaaaaasks3yliaqansozrmkfwyi3z2bwlpx6yrd7mxnjukcgxazlqtxurq` |
| Compartment `cmp-dev-inv` | `ocid1.compartment.oc1..aaaaaaaa76x3nykkjwvctpr6px34dysu3pbg7p62h2r65fegt7fvbrioll3a` |
| Compartment `cmp-dev-nexus` | `ocid1.compartment.oc1..aaaaaaaahycc62za6ikthlhauvarvbdixc7xpjjmcrame3cirhu2kz74ddma` |
| VCN `vcn-oke` | `ocid1.vcn.oc1.sa-saopaulo-1.amaaaaaasks3yliapqrmikfzagpgqohuzjqik3hx63w7r2uajiqv5krvxkda` |
| VCN `VCN-DEV` | `ocid1.vcn.oc1.sa-saopaulo-1.amaaaaaasks3yliatoq6uvqqak3kax775ksd2jastvgsbiki7mgj6jzue6dq` |
| API Gateway `api-gateway-nexus-dev` | `ocid1.apigateway.oc1.sa-saopaulo-1.amaaaaaasks3yliabohvp4fqav5pansi57thnunkyons52idvpfjppyzugeq` |
| API Gateways MFE (6×) | `api-gw-mfe-{shell,auth,user,person,poc,formalization}-dev` — ver seção 2.3 |
| Object Storage Namespace | `grbb7qzeuoag` |
| Região | `sa-saopaulo-1` |
| Pipeline Terraform | ID 51 — `terraform-tf_oci_clusters` |
| Variable Group | ID 34 — `oci-terraform` |