Cómo leer esta página. Diez pasos, un comando a la vez. Para cada uno: el comando, la salida exacta de la máquina del curso (Terraform 1.12.2), y qué hay que mirar. Escribe tú mismo cada comando. Los bloques «Para entender bien» son opcionales. Los comandos
terraform …son idénticos en todas partes; cuando un comando depende del sistema, se dan las dos versiones, una debajo de la otra. Si la carpeta del taller 1 ya no existe o sistate listno respondelocal_file.bonjour, rehaz primero el taller fundamental 1 (kit:https://github.com/hrhouma2/aiopsatlas-terraform-labo-fr).
El taller 1 te hizo crear un recurso y leer el state. Pero una infraestructura no se crea de una vez para siempre: se modifica, alguien la rompe por descuido, y un día se desmonta. Estos son los tres gestos de este taller. Primero vas a eliminar bonjour.txt a mano, como un colega que «limpia» una carpeta, y vas a ver a Terraform darse cuenta y luego recrearlo: es la deriva de la lección 01. Luego vas a cambiar una línea de main.tf y descubrir que, para el provider local, cambiar el contenido de un archivo no lo modifica: lo reemplaza, con un símbolo que debes saber reconocer antes de decir yes. Finalmente vas a destruir todo, y verificar que el registro está vacío y que el kit cuenta cero.
Siempre el arquitecto. Una deriva (drift) es un obrero que desplazó un muro sin decirlo: el edificio ya no corresponde al registro; el arquitecto se da cuenta cuando va a ver el lugar (Refreshing state...). Modificar en el lugar (~) es repintar un muro: el muro sigue siendo el mismo. Reemplazar (-/+) es demoler el muro y reconstruir uno en el mismo lugar: tiene un nuevo número en el registro. Destruir (-) es demoler sin reconstruir. El presupuesto siempre dice cuál de los tres prevé; tú lo lees antes de firmar.
| Símbolo en el plan | Frase en el plan | Qué hace Terraform | En este taller |
|---|---|---|---|
+ | will be created | crea un objeto nuevo | paso 3: el archivo eliminado a mano |
~ | will be updated in-place | cambia un atributo, el objeto permanece | nunca para content de un local_file |
-/+ | must be replaced | destruye el objeto, luego crea otro | paso 5: content cambia |
- | will be destroyed | elimina el objeto | paso 8: destroy |
La misma terminal que para el taller 1, en la carpeta travail/atelier-1 del kit lab-terraform. Y VS Code para modificar main.tf. Los comandos terraform … son idénticos en todas partes; los comandos para eliminar, leer o listar un archivo se dan en las dos versiones.
Windows (PowerShell), desde la raíz del kit:
cd travail\atelier-1Linux, macOS, WSL 2, Git Bash:
cd travail/atelier-1Luego:
terraform state listlocal_file.bonjourLo que pide el comando: «¿Qué gestionas en esta carpeta?»
Qué mirar: una línea, local_file.bonjour. Es el estado en el que te dejó el taller 1. Si lees No state file was found! o nada en absoluto, la carpeta no es la correcta o el recurso fue destruido: rehaz el taller 1 antes de continuar.
Windows (PowerShell):
Remove-Item .\bonjour.txt
Get-ChildItemLinux, macOS, WSL 2, Git Bash:
rm bonjour.txt
lsLo que piden los comandos: «Elimina bonjour.txt, luego muéstrame la carpeta.»
Qué mirar: bonjour.txt ya no está; main.tf y terraform.tfstate siguen ahí. Acabas de hacer lo que un colega hace en la consola AWS cuando elimina un bucket «que no servía para nada»: lo real cambió, pero ni el código ni el registro lo saben todavía.
terraform planlocal_file.bonjour: Refreshing state... [id=fd9aee5556589b4d797e6d49f8ec894e29e57713]
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# local_file.bonjour will be created
+ resource "local_file" "bonjour" {
+ content = "Bonjour Terraform"
+ content_base64sha256 = (known after apply)
…
+ filename = "./bonjour.txt"
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.Lo que pide el comando: «Compara el código, el registro y lo real, y dime qué falta.»
Qué mirar: Refreshing state... [id=fd9aee55…]: Terraform fue a ver el archivo que su registro menciona, y no lo encontró. Resultado: lo retira de su registro y el presupuesto dice will be created, Plan: 1 to add. El mismo plan que en el taller 1, paso 5, aunque no tocaste el código. Eso es una deriva: lo real se movió sin pasar por Terraform, y Terraform lo detectó en el momento del plan, no antes.
Refreshing state... del plan (y del apply) el que relee lo real. Un archivo eliminado hace tres días solo se descubre en el próximo plan.+ y no -/+? El objeto ya no existe en absoluto: no hay nada que destruir, solo que crear. -/+ (paso 5) es para un objeto que todavía existe pero que hay que rehacer.plan siguiente propone restablecerla como dice el código. El plan te lo muestra; a ti te toca decidir si es el código o la consola el que tenía razón.terraform applylocal_file.bonjour: Refreshing state... [id=fd9aee5556589b4d797e6d49f8ec894e29e57713]
…
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
local_file.bonjour: Creating...
local_file.bonjour: Creation complete after 0s [id=fd9aee5556589b4d797e6d49f8ec894e29e57713]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.Lo que pide el comando: «Recalcula el presupuesto, espera mi yes, luego hazlo.»
Qué mirar: bonjour.txt volvió, y su id es el mismo que antes (fd9aee55…): el identificador de un local_file es la huella de su contenido, y el contenido no cambió. Verifica con Get-Content .\bonjour.txt o cat bonjour.txt: Bonjour Terraform. Un script no habría sabido que había que reparar; Terraform lo supo porque tiene un registro y un código para comparar.
En VS Code, modifica la línea content de main.tf para obtener:
resource "local_file" "bonjour" {
filename = "${path.module}/bonjour.txt"
content = "Bonjour Terraform, deuxième version"
}Guarda, luego:
terraform planlocal_file.bonjour: Refreshing state... [id=fd9aee5556589b4d797e6d49f8ec894e29e57713]
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# local_file.bonjour must be replaced
-/+ resource "local_file" "bonjour" {
~ content = "Bonjour Terraform" -> "Bonjour Terraform, deuxième version" # forces replacement
~ content_base64sha256 = "SvXkP5Iqo1rJKHgTVJnjanxfsNVGhb+r52mzUqZJYyg=" -> (known after apply)
~ content_base64sha512 = "r3+PevYNJRFsHMz4H4U1DK+lg89nhO9SLJZMAqqtzPKiIv3ilrmbx5sYh2JCW9o5EF0NXanYmuxMvI2QN4Z1Ag==" -> (known after apply)
~ content_md5 = "ff3a967c227a58691a3d34a931d3eeb5" -> (known after apply)
~ content_sha1 = "fd9aee5556589b4d797e6d49f8ec894e29e57713" -> (known after apply)
~ content_sha256 = "4af5e43f922aa35ac92878135499e36a7c5fb0d54685bfabe769b352a6496328" -> (known after apply)
~ content_sha512 = "af7f8f7af60d25116c1cccf81f85350cafa583cf6784ef522c964c02aaadccf2a222fde296b99bc79b188762425bda39105d0d5da9d89aec4cbc8d9037867502" -> (known after apply)
~ id = "fd9aee5556589b4d797e6d49f8ec894e29e57713" -> (known after apply)
# (3 unchanged attributes hidden)
}
Plan: 1 to add, 0 to change, 1 to destroy.Lo que pide el comando: lo mismo que en el paso 3. Pero esta vez es el código el que cambió, no lo real.
Qué mirar, línea por línea. Es el plan más importante del módulo:
| Línea | Qué dice |
|---|---|
-/+ destroy and then create replacement | La leyenda: el símbolo usado es -/+, «destruir y luego crear un reemplazo». |
# local_file.bonjour must be replaced | No will be updated in-place: el recurso debe ser reemplazado. |
-/+ resource "local_file" "bonjour" { | El bloque entero lleva el -/+. |
~ content = "Bonjour Terraform" -> "Bonjour Terraform, deuxième version" # forces replacement | El ~ dice que el valor cambia, del antiguo (->) al nuevo. El comentario # forces replacement dice que es este atributo el que obliga a reemplazar. |
~ content_md5 = "ff3a967c…" -> (known after apply) | Las huellas cambiarán; Terraform todavía no conoce las nuevas. |
~ id = "fd9aee55…" -> (known after apply) | El identificador va a cambiar. Será otro objeto. |
# (3 unchanged attributes hidden) | filename, directory_permission, file_permission no se mueven; Terraform los oculta por legibilidad. |
Plan: 1 to add, 0 to change, 1 to destroy. | Una destrucción y una creación, cero modificación. |
Esta es tu primera respuesta-entregable: guarda esta salida.
La diferencia esencial entre
~y-/+. El~delante decontentdice qué cambia. El-/+delante deresourcedice cómo lo va a hacer Terraform: demoler, y luego reconstruir. Para un archivo de texto, no verás la diferencia. Para una base de datos,-/+significa que los datos desaparecen con el objeto antiguo. Es el provider quien decide, atributo por atributo; el providerlocaldecidió quecontentfuerza el reemplazo. Tú lees las tres pistas (must be replaced,# forces replacement,1 to destroy) antes de escribiryes.
local reemplaza en lugar de modificar? Porque no sabe «corregir» un archivo existente: sabe escribirlo o borrarlo. El id de un local_file es además la huella SHA-1 de su contenido; cambiar el contenido cambia el identificador, y un objeto cuyo identificador cambia es, para Terraform, otro objeto. En la máquina del curso, file_permission también fuerza el reemplazo. Otros providers actúan de otra manera: cambiar una etiqueta (tag) en un recurso AWS es un ~, cambiar su nombre suele ser un -/+.apply en producción, es lo que hay que leer.terraform apply -replace=local_file.bonjour reemplaza el recurso incluso sin cambio de código. Útil cuando un objeto está corrupto. Lo verás en el proyecto 08.terraform applylocal_file.bonjour: Refreshing state... [id=fd9aee5556589b4d797e6d49f8ec894e29e57713]
…
Plan: 1 to add, 0 to change, 1 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
local_file.bonjour: Destroying... [id=fd9aee5556589b4d797e6d49f8ec894e29e57713]
local_file.bonjour: Destruction complete after 0s
local_file.bonjour: Creating...
local_file.bonjour: Creation complete after 0s [id=bd2b5f8ccbb2cbc2b9bea872211549635a76b092]
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.Lo que pide el comando: lo mismo que en el paso 4.
Qué mirar: cuatro líneas de acción en lugar de dos, en el orden del símbolo -/+: Destroying... luego Destruction complete (el antiguo, fd9aee55…), luego Creating... luego Creation complete (el nuevo, bd2b5f8c…). Y la última línea: Apply complete! Resources: 1 added, 0 changed, 1 destroyed. No 1 changed: nada se modificó, un objeto fue destruido y otro creado.
Windows (PowerShell):
Get-Content .\bonjour.txtLinux, macOS, WSL 2, Git Bash:
cat bonjour.txtBonjour Terraform, deuxième versionLuego:
terraform state show local_file.bonjour# local_file.bonjour:
resource "local_file" "bonjour" {
content = "Bonjour Terraform, deuxième version"
content_base64sha256 = "hg9L75JxH+5SDk+ZOADZinGONkirydetS0TFjN3uZ3o="
content_base64sha512 = "EjT8mRVQU2JQ+Em/rnbRKYfe7E2UqIWw8yQMMtaatimH5XCKg4ZomW4PI5yO5jhujdSrU04vX17HE2kaqEVSkQ=="
content_md5 = "9a1a1355b48e28ad727200e450b851e1"
content_sha1 = "bd2b5f8ccbb2cbc2b9bea872211549635a76b092"
content_sha256 = "860f4bef92711fee520e4f993800d98a718e3648abc9d7ad4b44c58cddee677a"
content_sha512 = "1234fc991550536250f849bfae76d12987deec4d94a885b0f3240c32d69ab62987e5708a838668996e0f239c8ee6386e8dd4ab534e2f5f5ec713691aa8455291"
directory_permission = "0777"
file_permission = "0777"
filename = "./bonjour.txt"
id = "bd2b5f8ccbb2cbc2b9bea872211549635a76b092"
}Lo que piden los comandos: «Muéstrame el archivo real, luego lo que dice el registro sobre él.»
Qué mirar: el archivo contiene la segunda versión. En el registro, content es el nuevo valor, todas las huellas cambiaron, y el id es bd2b5f8c…, el mostrado por Creation complete en el paso 6. Compara con el state show del taller 1, paso 9: misma dirección local_file.bonjour, pero otro objeto. El código, el registro y lo real vuelven a estar de acuerdo: un terraform plan ahora diría No changes.
terraform destroylocal_file.bonjour: Refreshing state... [id=bd2b5f8ccbb2cbc2b9bea872211549635a76b092]
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
# local_file.bonjour will be destroyed
- resource "local_file" "bonjour" {
- content = "Bonjour Terraform, deuxième version" -> null
- content_base64sha256 = "hg9L75JxH+5SDk+ZOADZinGONkirydetS0TFjN3uZ3o=" -> null
…
- filename = "./bonjour.txt" -> null
- id = "bd2b5f8ccbb2cbc2b9bea872211549635a76b092" -> null
}
Plan: 0 to add, 0 to change, 1 to destroy.
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
local_file.bonjour: Destroying... [id=bd2b5f8ccbb2cbc2b9bea872211549635a76b092]
local_file.bonjour: Destruction complete after 0s
Destroy complete! Resources: 1 destroyed.Lo que pide el comando: «Destruye todo lo que gestionas aquí; muéstrame el presupuesto y espera mi yes.»
Qué mirar: el tercer símbolo, - solo, y cada atributo que pasa a -> null («ya nada»). La pregunta es más grave que en el apply: Do you really want to destroy all resources? y There is no undo. Escribe yes. La frase que hay que esperar: Destroy complete! Resources: 1 destroyed. El archivo desapareció de la carpeta.
terraform state listLuego, para ver qué responde Terraform cuando se le pide un recurso que ya no gestiona:
terraform state show local_file.bonjourNo instance found for the given address!
This command requires that the address references one specific instance.
To view the available instances, use "terraform state list". Please modify
the address to reference a specific instance.Lo que piden los comandos: «Lista lo que gestionas», luego «muéstrame local_file.bonjour».
Qué mirar: state list no responde nada: ni una línea, ni un error, un registro vacío. Es distinto del No state file was found! del taller 1 antes del apply: aquí el archivo terraform.tfstate existe, pero su lista de recursos está vacía. Y state show responde No instance found for the given address!: la dirección es correcta, pero ya nada corresponde a ella, y Terraform te remite a state list. Esta es tu segunda respuesta-entregable: la salida vacía de terraform state list.
Windows (PowerShell):
Get-ChildItem -ForceLinux, macOS, WSL 2, Git Bash:
ls -latotal 17
drwxr-xr-x 1 rehou 197609 0 Sep 15 14:57 .
drwxr-xr-x 1 rehou 197609 0 Sep 15 14:57 ..
drwxr-xr-x 1 rehou 197609 0 Sep 15 14:57 .terraform
-rw-r--r-- 1 rehou 197609 1228 Sep 15 14:57 .terraform.lock.hcl
-rw-r--r-- 1 rehou 197609 130 Sep 15 14:57 main.tf
-rw-r--r-- 1 rehou 197609 181 Sep 15 14:57 terraform.tfstate
-rw-r--r-- 1 rehou 197609 1653 Sep 15 14:57 terraform.tfstate.backup(Listado ls -la de la máquina del curso; Get-ChildItem -Force muestra los mismos cinco nombres: .terraform, .terraform.lock.hcl, main.tf, terraform.tfstate, terraform.tfstate.backup.)
Lo que pide el comando: «Muéstrame toda la carpeta, incluidos los archivos ocultos.»
Qué mirar: bonjour.txt ya no está. main.tf está intacto: destruir los recursos nunca toca el código. terraform.tfstate todavía está ahí pero minúsculo (181 bytes), y apareció un terraform.tfstate.backup: la copia del registro de antes del destroy, que Terraform guarda por precaución. .terraform/ y .terraform.lock.hcl también permanecen: si relanzas apply, no hace falta rehacer init.
Abre el state para verlo vacío, sin modificarlo (Get-Content .\terraform.tfstate o cat terraform.tfstate):
{
"version": 4,
"terraform_version": "1.12.2",
"serial": 6,
"lineage": "69fb219e-a88c-a97f-5aed-a33e7c34ff69",
"outputs": {},
"resources": [],
"check_results": null
}"resources": []: nada. El "serial" cuenta las escrituras en el state (seis aquí: dos apply en el taller 1, luego reparación, reemplazo y destrucción); el "lineage" es el identificador de este state, distinto en tu caso.
Vuelve a la raíz del kit y lanza el contador:
Windows (PowerShell):
cd ..\..
.\labo.ps1 etatLinux, macOS, WSL 2, Git Bash:
cd ../..
./labo.sh etattravail atelier-1 : aucune ressource
Ressources encore gérées : 0 (0 attendu à la fin d'une séance).Respuesta esperada: aucune ressource y Ressources encore gérées : 0. Es el final de sesión limpio que cada proyecto del curso exigirá, cuando los recursos cuesten dinero.
bonjour.txt a mano y viste a plan proponer will be created sin que el código hubiera cambiado: una deriva, detectada en el Refreshing state....apply recreó el archivo con el mismo id.content y leíste las tres pistas del reemplazo: must be replaced, # forces replacement, Plan: 1 to add, 0 to change, 1 to destroy.apply mostró Destroying... luego Creating..., y Resources: 1 added, 0 changed, 1 destroyed.; el id cambió.destroy pidió yes con There is no undo. y respondió Destroy complete! Resources: 1 destroyed.state list no devuelve nada; state show devuelve No instance found for the given address!; terraform.tfstate contiene "resources": [].etat devuelve Ressources encore gérées : 0.state list responde No state file was found! → No estás en travail/atelier-1, o el taller 1 no se hizo hasta el apply. Verifica la carpeta (Get-ChildItem -Force o ls -la debe mostrar terraform.tfstate), si no, rehaz el taller 1.state list no responde nada → El recurso ya fue destruido (un destroy al final del taller 1). Vuelve a lanzar terraform apply, yes, luego retoma en el paso 2.No changes. → El archivo no fue eliminado (error de tipeo en Remove-Item o rm, o carpeta equivocada). Verifica con Get-ChildItem o ls, elimínalo, vuelve a lanzar plan.No changes. → main.tf no se guardó después de la modificación (VS Code: punto blanco en la pestaña). Ctrl+S y vuelve a lanzar.Error: Unterminated template string → La comilla de cierre del nuevo valor content se perdió durante la edición. Vuelve a ponerla.# forces replacement no es content → Modificaste otro argumento al mismo tiempo (por ejemplo file_permission = "0644"). Para un local_file, ese también fuerza el reemplazo (verificado: ~ file_permission = "0777" -> "0644" # forces replacement). Vuelve a poner el archivo como en el paso 5, solo la línea content debe cambiar.Apply cancelled. → Se escribió algo distinto de yes. Vuelve a lanzar, escribe yes completo.destroy dice No changes. No objects need to be destroyed. y luego Destroy complete! Resources: 0 destroyed. → Ya no hay nada en el state (Either you have not created any objects yet or the existing objects were already deleted outside of Terraform.). Pasa al paso 9.destroy, crees que el state no se limpió porque terraform.tfstate todavía existe → Es normal. Ábrelo: "resources": []. El archivo permanece, vacío, con un .backup al lado.etat responde Ressources encore gérées : 1 → El destroy se canceló o se hizo en otra carpeta. Vuelve a travail/atelier-1, terraform destroy, yes, vuelve a lanzar etat.