Terraform, Kuberntes e AWS EKS

di Andrea Zani, in terraform,

Ecco altre mie annotazioni personali. Dopo il post precedente dove ho solo introdotto Terraform ora provo ad alzare l'asticella. Obbiettivo che voglio ottenere in questo post: avviare le tre istanze di MongoDb e le due web application in Kubernetes con Terraform ma all'interno di un cluster Kubernetes in AWS (EKS) anch'esso creato con Terraform.

Questa volta ho bisogno di questi tool:

  • Il tool terraform (versione >=1.0)
  • aws cli (versione >=2.0)
  • Account attivo in AWS

Dal post precedente si dovrebbe già avere installato il tool terraform nella macchina locale. aws cli lo si può scaricare da qui. Naturalmente è necessario avere un account attivo in AWS e configurato perché possa essere usato con il comando aws da terminale. Per completare questo passo è sufficiente lanciare il comando:

aws configure

E inserire i codici dell'utente creato dalla console web di AWS. Alla fine, per verificare che tutto funzioni, da terminale lanciare il comando:

$ aws sts get-caller-identity
{
    "UserId": "AEDAXXXXXXXXXXXXX228Z",
    "Account": "xxxxxxxxxxxx",
    "Arn": "arn:aws:iam::xxxxxxxxxxxx:user/xxxxxxxxxx"
}

Che dovrebbe dare in risultato simile all'esempio sopra. Premessa prima di continuare: anche se si sta utilizzando un account di prova in AWS che dà molti servizi gratuiti il primo anno, EKS non è tra questi.

Si comincia... EKS

Innanzitutto il codice utilizzato in Terraform nel post precedente per la creazione delle risorse rimarrà quasi immutato. Le uniche differenze saranno nella modalità di autenticazione di Kubernetes e Helm per accedere al cluster in AWS. Completamente nuovo è il codice in Terraform per la creazione delle risorse in AWS di un cluster per Kubernetes. Utilizzando AWS, inoltre, non utilizzerò il tipo NodePort per i service di Kubernetes per le web application, ma il Load Balancer. EKS è il servizio che mette a disposizione AWS per la creazione di un cluster Kubernetes nel Cloud. Permette la creazione di macchine virtuale (EC2) da utilizzare nel cluster per l'esecuzione dei Pod e offre anche la possibilità di usare la modalità Fargate per poterli farli girare in un ambiante Serverless. Inoltre è in grado di collegare in modo autonomo la creazione dei Load Balancer di Kubernetes con l'omonimo servizio si AWS, così come i dischi EBS che saranno creati e gestiti da AWS come Persistent Volume all'interno del cluster, con la possibilità anche di selezionare la tipologia di servizio per esigenze prestazionali o di capacità. Il suo utilizzo da console web all'inizio può appare problematico, perché necessita della creazione manuale di due Role per il suo utilizzo (Cluster Role e Node Role). Infine, si deve configurare il numero e il tipo di macchine da avviare e collegare al cluster in un Node Group. Per esigenze particolari si possono avviare più Node Group con ognuna la sua tipologia di macchine.

Personalmente ho sempre trovato l'uso del comando da terminale eksctl più comodo per la creazione (e distruzione) dei cluster Kubernetes in AWS/EKS - questione di gusti. Ora utilizzerà una nuova via: Terraform.

Come nello scorso post cercherà di mantenere una struttura più pulita possibile con il nome dei file consigliati.

versions.tf

Come spiegato nello scorso post, qui inserisco i provider che utilizzerò:

terraform {
  required_providers {
    helm = {
      source  = "hashicorp/helm"
      version = ">= 2.0.0"
      }

    kubernetes = { 
      source  = "hashicorp/kubernetes" 
      version = ">= 2.0.0" 
    } 

    random = { 
      source = "hashicorp/random" 
      version = "3.0.1" 
    }

    aws = {
      source = "hashicorp/aws"
      version = ">= 4.0.0"
    }
  }
}

Confrontato con lo stesso file precedente ho solo aggiunto il provider per AWS.

variables.tf

In questo file ho aggiunto parecchie variabili per personalizzare la creazione di tutte le risorse. Oltre al nome del namespace per Kubernetes, ho inserito anche il nome della nuova VPC (vpc_name) e la regione (aws_region) in cui sarà creato il tutto in AWS. Inoltre per il mio scopo creerò due Node Group. Nel primo creerà tre macchine nel quale avvierò le istanze di MongoDB, e nel secondo userò una sola macchina virtuale nella quale avvierò le due web application. Per specificarne il tipo e il numero:

variable "aws_region" {
  description = "Aws region"
  type        = string
  default     = "eu-south-1"
}

...

variable "vpc_name" {
  description = "VPC name"
  type        = string
  default     = "AZ-vpc"
}

variable "cluster_name" {
  description = "K8s cluster name"
  type        = string
  default     = "K8sDemo"
}

variable "cluster_version" {
  description = "Cluster version"
  type        = string
  default     = "1.21"
}

variable "worker_group_mongodb_instance_type" {
  description = "Worker group mongodb instance type"
  type        = string
  default     = "t3.small"
}

variable "worker_group_mongodb_desidered_size" {
  description = "Worker group mongodb desidered size"
  type        = number
  default     = 3
}

variable "worker_group_webapp_instance_type" {
  description = "Worker group webapp instance type"
  type        = string
  default     = "t3.small"
}

variable "worker_group_webapp_desidered_size" {
  description = "Worker group webapp desidered size"
  type        = number
  default     = 1
}

providers.tf

provider "aws" {
  region = var.aws_region
}

data "aws_availability_zones" "available" {}

In questo caso configuro AWS comunicando la region che userà (eu-south-1, Milano) e con data chiedo a Terraform di poter richiedere le informazioni da AWS. Questo è necessario quando si devono gestire o fare riferimento a risorse già presenti. Per esempio, nel prossimo script (di uso solo didattico) per Terraform non creerò nulla ma richiederò solo informazioni sulla VPC in AWS nella region eu-south-1:

provider "aws" {
  region = "eu-south-1"
}

data "aws_availability_zones" "available" {}
data "aws_vpc" "selected" {}

data "aws_subnets" "example" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.selected.id]
  }
}

data "aws_subnet" "example" {
  for_each = toset(data.aws_subnets.example.ids)
  id       = each.value
}

####################

output "aws-vailable-zones" {
  description = "AWS Zones"
  value       = data.aws_availability_zones.available
}

output "vpc-default" {
  description = "AWS Zones"
  value       = data.aws_vpc.selected
}

output "subnet_cidr_blocks" {
  value = [for s in data.aws_subnet.example : s.id]
}

Nelle sezioni data ho inserito le richieste delle informazioni che voglio da AWS: VPC, zone, subnet. L'output dopo il comando terraform apply sarà:

aws-vailable-zones = {
  "all_availability_zones" = tobool(null)
  "exclude_names" = toset(null) /* of string */
  "exclude_zone_ids" = toset(null) /* of string */
  "filter" = toset(null) /* of object */
  "group_names" = toset([
    "eu-south-1",
  ])
  "id" = "eu-south-1"
  "names" = tolist([
    "eu-south-1a",
    "eu-south-1b",
    "eu-south-1c",
  ])
  "state" = tostring(null)
  "zone_ids" = tolist([
    "eus1-az1",
    "eus1-az2",
    "eus1-az3",
  ])
}
subnet_cidr_blocks = [
  "subnet-4e668127",
  "subnet-7216170a",
  "subnet-ee99b9a4",
]
vpc-default = {
  "arn" = "arn:aws:ec2:eu-south-1:############:vpc/vpc-876582ee"
  "cidr_block" = "172.31.0.0/16"
  "cidr_block_associations" = tolist([
    {
      "association_id" = "vpc-cidr-assoc-421afd2b"
      "cidr_block" = "172.31.0.0/16"
      "state" = "associated"
    },
  ])
  "default" = true
  "dhcp_options_id" = "dopt-8f6780e6"
  "enable_dns_hostnames" = true
  "enable_dns_support" = true
  "filter" = toset(null) /* of object */
  "id" = "vpc-876582ee"
  "instance_tenancy" = "default"
  "ipv6_association_id" = ""
  "ipv6_cidr_block" = ""
  "main_route_table_id" = "rtb-791cfb10"
  "owner_id" = "############"
  "state" = tostring(null)
  "tags" = tomap({})
}

E potrò utilizzare questi dati ovunque mi servano, come nel prossimo file.

1-vpc.tf

Con il codice seguente creo una nuova VPC:

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "3.2.0"

  name                 = var.vpc_name
  cidr                 = "10.0.0.0/16"
  azs                  = data.aws_availability_zones.available.names
  private_subnets      = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  public_subnets       = ["10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"]
  enable_nat_gateway   = true
  single_nat_gateway   = true
  enable_dns_hostnames = true

  tags = {
    "kubernetes.io/cluster/${var.cluster_name}" = "shared"
  }

  public_subnet_tags = {
    "kubernetes.io/cluster/${var.cluster_name}" = "shared"
    "kubernetes.io/role/elb"                      = "1"
  }

  private_subnet_tags = {
    "kubernetes.io/cluster/${var.cluster_name}" = "shared"
    "kubernetes.io/role/internal-elb"             = "1"
  }
}

In questa VPC ho definito il CIDR che dovrà gestire come le Subnet sottostanti (notare l'uso del data per l'inserimento delle zone disponibile nell'oggetto azs). Creo tre Subnet private e tre pubbliche e un Gateway in modo che dalle Subnet private, dove farò girare le mie macchine per Kubernetes, si potrà accedere a Internet. Nelle tre sezioni di TAGS inserisco delle property necessarie perché possano essere utilizzate da EKS.

2-security-groups.tf

Per i due Node Group che creerò nel mio cluster devo specificare i rispettivi Security Group. Non avendo esigenze particolari li creo senza inserire nessun permesso:

resource "aws_security_group" "worker_group_mgmt_one" {
  name_prefix = "worker_group_mgmt_one"
  vpc_id      = module.vpc.vpc_id
}

resource "aws_security_group" "worker_group_mgmt_two" {
  name_prefix = "worker_group_mgmt_two"
  vpc_id      = module.vpc.vpc_id
}

resource "aws_security_group" "all_worker_mgmt" {
  name_prefix = "all_worker_management"
  vpc_id      = module.vpc.vpc_id
}

3-eks.tf

All'interno di questo file vengono create le due Role necessarie per la creazione del cluster:

resource "aws_iam_role" "demo" {
  name = "eks-cluster-${var.cluster_name}"

  assume_role_policy = jsonencode({
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "Service": "eks.amazonaws.com"
        },
        "Action": "sts:AssumeRole"
      }
    ]
  })
}

resource "aws_iam_role" "nodes" {
  name = "eks-node-group-nodes-${var.cluster_name}"

  assume_role_policy = jsonencode({
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Principal = {
        Service = "ec2.amazonaws.com"
      }
    }]
    Version = "2012-10-17"
  })
}

Quindi queste Role vengono assegnate al cluster creato in questa sezione:

resource "aws_eks_cluster" "demo" {
  name     = var.cluster_name
  role_arn = aws_iam_role.demo.arn
  version = var.cluster_version

  vpc_config {
    subnet_ids = module.vpc.private_subnets
  }

  depends_on = [aws_iam_role_policy_attachment.demo-AmazonEKSClusterPolicy]
}

Un cluster senza macchine collegate è inutile. In EKS, come scritto sopra, è necessario creare almeno un Node Group. Nel mio esempio ne creo due, ecco solo la definizione per le macchine per MongoDb (la definizione per le due web application è simile):

resource "aws_eks_node_group" "mongodb-nodes" {
  cluster_name    = aws_eks_cluster.demo.name
  node_group_name = "mongodb-nodes"
  node_role_arn   = aws_iam_role.nodes.arn

  subnet_ids = module.vpc.private_subnets

  capacity_type  = "ON_DEMAND"
  instance_types = [var.worker_group_mongodb_instance_type]

  scaling_config {
    desired_size = var.worker_group_mongodb_desidered_size
    max_size     = var.worker_group_mongodb_desidered_size
    min_size     = var.worker_group_mongodb_desidered_size
  }

  update_config {
    max_unavailable = 1
  }

  labels = {
    role = "mongodb"
    name = "mongodb"
  }

  tags = {
    name = "mongodb1"
  }

depends_on = [
    aws_iam_role_policy_attachment.nodes-AmazonEKSWorkerNodePolicy,
    aws_iam_role_policy_attachment.nodes-AmazonEKS_CNI_Policy,
    aws_iam_role_policy_attachment.nodes-AmazonEC2ContainerRegistryReadOnly,
  ]
}

A parte l'assegnazione alla VPC creata, alla tipologia e numero di macchine da creare, ho inserito anche le Labels - role = "mongodb" - che utilizzerò per l'assegnazione dei Pod a queste macchine, come mostrerò in seguito.

Nel file ho inserito anche questa sezione:

data "aws_eks_cluster_auth" "demo" {
  name = var.cluster_name
}

Che sarà usato nel prossimo file.

4-kubernetes.tf

In questo file configuro i provider per Kubernetes e per Helm. Nel post precedente lo avevo fatto inserendo il path alla directory .kube, utilizzando AWS devo utilizzare:

provider "kubernetes" {
  host                   = aws_eks_cluster.demo.endpoint
  token                  = data.aws_eks_cluster_auth.demo.token
  cluster_ca_certificate = base64decode(aws_eks_cluster.demo.certificate_authority.0.data)
}

provider "helm" {
    kubernetes {
      host                   = aws_eks_cluster.demo.endpoint
      token                  = data.aws_eks_cluster_auth.demo.token
      cluster_ca_certificate = base64decode(aws_eks_cluster.demo.certificate_authority.0.data)
    }
}

Ed ecco l'uso di aws_eks_cluster_auth grazie al quale riesco ad avere le informazioni necessarie per gestire da Terraform la connessione a Kubernetes.

I file successivi sono gli stessi mostrati nel post precedente a parte piccole modifiche, come l'assegnazione dei Pod alle macchine volute. Nel caso di MongoDb per l'assegnazione dei Pod al Node Group con la Label role="mongodb" ho inserito questo codice:

  set {
    name = "nodeAffinityPreset.type"
    value = "hard"
  }
  
  set {
    name = "nodeAffinityPreset.key"
    value = "role"
  }

  set {
    name = "nodeAffinityPreset.values[0]"
    value = "mongodb"
  }

Come in quella documentazione, in Kubernetes ho a disposizione due metodi per l'Affinity ai Node (per la preferenza su quale node creare il Pod). In type, avendo inserito hard, l'Affinity è obbligatoria, mentre con Soft essa è solo una preferenza. Quest'ultimo metodo è comodo in caso il Node dove gira il Pod debba andare offline per qualsiasi ragione. Con la modalità hard il Pod non potrà mai essere ricreato perché l'unico Node con l'Affinity desiderata dal Pod non è disponibile, mentre con soft, non trovando il Node con l'Affinity desiderata sarà scelto un altro con le risorse libere necessarie. Nella configurazione sopra ho inserito l'Affinity per i Node che hanno una Label con la key role e come valore mongodb

Nel caso delle web application, le macchine nel Node Group avranno la Label role="webapp". Nel blocco Deployment ho aggiunto questo codice:

...
spec {
  affinity {
    node_affinity {
      required_during_scheduling_ignored_during_execution {
        node_selector_term {
          match_expressions {
            key = "role"
            operator = "In"
            values = ["webapp"]
          }
        }
      }
    }
  }
  volume {
  ...

Che equivale alla regola scritta sopra per il chart di Helm per MongoDb.

outputs.tf

Siccome ho demandato ad AWS la creazione dei Load Balancer, con questo file posso mostrare gli URL che dovrò utilizzare per accedere alle due web application:

output "mongodb-express-dns-name" {
  description = "MongoDb password"
  value       = "http://${kubernetes_service.mongoexpress-service.status[0].load_balancer[0].ingress[0].hostname}"
}

output "testdb-dns-name" {
  description = "Api rest port"
  value       = "http://${kubernetes_service.webapp-service.status[0].load_balancer[0].ingress[0].hostname}/api/info"
}

Avviare il tutto

Arrivato a questo punto è il momento di avviare la creazione di tutte queste risorse in AWS. In sequenza:

terraform init

Per il download dei provider necessari.

terraform plan

Per verificare che la sintassi sia corretta. E solo infine:

terraform apply -auto-approve

Questa volta la procedura sarà piuttosto lunga. Saranno necessari circa due minuti per la creazione della VPC, quindi dai cinque ai dieci minuti per la creazione del cluster EKS, e dai tre ai cinque minuti per la creazione dei due Node Group. Dopodiché inizierà la configurazione dei Pod e delle altre risorse necessarie all'interno di Kubernetes. E solo infine ecco l'output voluto:

Outputs:

mongodb-express-dns-name = "http://a56ec8e93f6c542ebbce34d149748ae2-93b4730aa8841fd1.elb.eu-south-1.amazonaws.com"
mongodb-password = <sensitive>
namespace = "test-blog-2"
testdb-dns-name = "http://a2a8417bd547347aa807c2af01b99b40-e8ef85a0505fb6f1.elb.eu-south-1.amazonaws.com/api/info"

Non rimane che provare questi url. Per mongo-express:

Per la web api:

Se si riceve errore quando si richiedono le due web application non si deve avere fretta. Il collegamento dal Load Balancer e le due web application in Kubernetes non è mai immediato e l'attesa è quasi sempre di qualche minuto.

Se volessi utilizzare Kubernetes in AWS da locale con il classico comando kubectl, dovrei usare questo comando da terminale:

aws eks --region eu-south-1 update-kubeconfig --name K8sDemo

Dove K8sDemo è il nome del cluster EKS creato in AWS (e inserito nelle variabili nei file per Terraform). Ultimo controllo che i Pod siano stati inseriti sulle giuste macchine:

$ kubectl get po -n test-blog-2 -o wide
NAME                      READY   STATUS    RESTARTS   AGE     IP           NODE
mongodb-easy-0            1/1     Running   0          22m     10.0.2.81    ip-10-0-2-234.eu-south-1.compute.internal
mongodb-easy-1            1/1     Running   0          22m     10.0.1.149   ip-10-0-1-233.eu-south-1.compute.internal
mongodb-easy-2            1/1     Running   0          21m     10.0.3.90    ip-10-0-3-143.eu-south-1.compute.internal
showdb-858c68d8b4-zld98   1/1     Running   0          9m21s   10.0.3.13    ip-10-0-3-251.eu-south-1.compute.internal
testdb-5454db7d9d-t6594   1/1     Running   0          9m21s   10.0.3.161   ip-10-0-3-251.eu-south-1.compute.internal

I tre Pod di MongoDb sono avviati sulle tre macchine dedicate, così come le due web application sono avviate entrambe su una sola macchina.

Controllato il buon funzionamento, distruggo il tutto con il comando:

terraform destroy -auto-approve

E in una decina di minuti tutto dovrebbe essere cancellato. In qualche caso può apparire questo errore:

Error: context deadline exceeded

Sembra che sia dovuto ad un timeout del provider di AWS durante la cancellazione, e il più delle volte basta rilanciare il comando qui sopra per completare la cancellazione. In altri casi, piuttosto rari, si deve intervenire manualmente cancellando le risorse direttamente dalla console web di AWS.

IAM Role ai Pod

Tra le feature del cluster di Kubernetes in AWS c'è la possibilità di assegnare delle IAM Role ai singoli Pod. Questo consente di poter accedere a risorse di AWS senza dover utilizzare direttamente delle credenziali che creerebbero poi il problema sulla sicurezza della loro gestione. Tale possibilità non la mostrerò in questo post perché ci sono ancora dei dettagli che non mi sono chiari - mancanza di tempo per approfondire la cosa, come scusa funziona sempre. Comunque maggiori info qui e nel link al video a fine post.

Usare altri Cloud

Se volessi usare un altro cloud le modifiche sono solo a livello di avvio e configurazione del cluster di Kubernetes. Nel mio caso, non essendoci la VPC così come in AWS, potrei cancellare completamente i file 1, 2 e 3, e modificare parzialmente il 4 con la configurazione di LKE (il cluster di Kubernetes in Linode) con queste modifiche:

terraform {
  required_providers {
    linode = {
      source  = "linode/linode"
    }
  }
}

# Configure the Linode Provider
provider "linode" {
  token = "################################################################"
}

resource "linode_lke_cluster" "mycluster" {
    label               = "my-cluster"
    k8s_version         = "1.23"
    region              = "eu-central"
    tags                = ["prod"]

    control_plane {
      high_availability = false
    }

    pool {
        type  = "g6-standard-1"
        count = 4
    }
}

resource "local_file" "config" {
    content_base64 = linode_lke_cluster.mycluster.kubeconfig
    filename = "${path.module}/config.txt"
}

provider "kubernetes" { 
  config_path = local_file.config.filename 
} 

resource "kubernetes_namespace" "test" { 
  metadata { 
    name = "my-test-123455"
  } 
}

Qui ho dovuto salvare il file di configurazione per l'accesso al cluster in un file locale (grazie alla resource di Terraform local_file) il cui percorso completo l'ho inserito come parametro in config_path (lo stesso file lo potrò utilizzare poi per accedere al cluster da locale).

Sicurezza

Come mostrato nel post precedente l'inserimento di password all'interno di Terraform è sempre problematico. Mi è stato suggerito di utilizzare i vari servizi di Secret dei vari cloud, in modo che è possibile creare manualmente un Secret contenente le credenziali poi da usare nella creazione di servizi che poi dovrebbero utilizzarle. Nel caso della mia demo la cosa non risolve il problema. Ipotizzando di avere un Secret in AWS dal nome example, è possibile leggerne il contenuto con questo codice in Terraform:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }

  }
}

# Configure the AWS Provider
provider "aws" {
  region = "eu-south-1"
}

data "aws_secretsmanager_secret" "byname" {
  name = "example"
}

data "aws_secretsmanager_secret_version" "secretversion" {
  secret_id = data.aws_secretsmanager_secret.byname.id
}

output "example1" {
  description = "Secret Metadata"
  value = data.aws_secretsmanager_secret.byname
}

output "example2" {
  description = "Secret Content"
  value = jsondecode(data.aws_secretsmanager_secret_version.secretversion.secret_string)
  sensitive = true
}

Il problema è che leggendo i file di stato creato da Terraform vedrei il contenuto in chiaro:

"instances": [
  {
    "schema_version": 0,
    "attributes": {
      "arn": "arn:aws:secretsmanager:eu-south-1:838080890745:secret:example-pcyzfF",
      "id": "arn:aws:secretsmanager:eu-south-1:838080890745:secret:example-pcyzfF|AWSCURRENT",
      "secret_binary": "",
      "secret_id": "arn:aws:secretsmanager:eu-south-1:838080890745:secret:example-pcyzfF",
      "secret_string": "{\n    \"test1\": \"1234\",\n    \"test2\": \"5678\"\n}",
      "version_id": "35fb8d94-b96c-4419-92ef-fd7e324e3470",
      "version_stage": "AWSCURRENT",
      "version_stages": [
        "AWSCURRENT"
      ]
    },
    "sensitive_attributes": []
  }
]

La soluzione è utilizzare servizi appositi per la gestione dello State, visto che lo State Storage su S3 permette anche la crittografazione del contenuto:

https://www.terraform.io/language/settings/backends/s3#encrypt

Critiche e apprezzamenti di Terraform con il Cloud

In passato ho avuto a che fare con chi vendeva Terraform come passepartout per tutti i Cloud. Come se costruiti dei file per la configurazione di risorse da utilizzare in AWS bastasse modificare il provider per poterli utilizzare con altri Cloud (Azure, Google, etc...). Ovviamente non così - e sarebbe da pazzi crederlo, almeno attualmente. E' sufficiente vedere il solo esempio sopra, dove ho voluto utilizzare gli stessi file di Terraform con due Cloud differenti, in cui ho dovuto stravolgere quasi tutti i file - a parte Kubernetes, anche se avrei dovuto rimuovere le Affinity. Questo sconvolgimento sarebbe stato altresì completo anche se avessi usato la controparte di Azure per la costruzione di una rete interna VNet. Lasciando perdere questa facile critica, di contro, è da apprezzare la possibilità di avere uno strumento unico per la creazione di risorse completamente differenti su piattaforme agli antipodi (esagerando). Senza strumenti come Terraform, per la creazione di risorse su AWS e Azure, avrei dovuto utilizzare il comando da terminale aws (lo stesso usato all'inizio del post - aws configure) e il comando az di Azure. Inoltre lo scambio di informazioni tra i due Cloud con quei comandi porterebbe ad una complicazione inutile che Terraform risolve facilmente. Ergo: Terraform promosso.

Fine

Ecco il repository con il codice. Qui un video illuminante su quanto esposto che mi ha aiutato a risolvere alcuni dubbi. Ci saranno altri post dedicati a Terraform? Perché no? Magari con qualcosa di più simpatico e utile - forse.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Nella stessa categoria
I più letti del mese