🧭 Cirugía del estado: importar, mover, recrear, olvidar¶
1. Direcciones: cómo se llama cada cosa en el estado¶
Todos los comandos de esta página reciben una dirección. El original usaba module.red.mi_ip, que no existe: dentro de un módulo la dirección sigue necesitando tipo y nombre.
| Código | Dirección | En la shell |
|---|---|---|
resource "azurerm_subnet" "web" |
azurerm_subnet.web |
Sin comillas: puntos y guiones no molestan |
… con count = 2 |
azurerm_subnet.web[0], [1] |
Comillas simples: 'azurerm_subnet.web[0]' (los corchetes son glob en bash) |
… con for_each |
azurerm_subnet.web["datos"] |
Comillas simples fuera, dobles dentro: 'azurerm_subnet.web["datos"]' |
module "red" { … } → dentro resource "azurerm_subnet" "this" |
module.red.azurerm_subnet.this["web"] |
El módulo entero: module.red (válido en moved, state mv, state rm) |
module "red" { for_each = … } |
module.red["hub"].azurerm_virtual_network.this |
Igual que arriba |
data "azurerm_client_config" "actual" |
data.azurerm_client_config.actual |
Los data están en el estado pero no se importan ni mueven: se releen |
terraform state list # todas las direcciones, la fuente de verdad para copiar y pegar
terraform state list module.red # filtrar por prefijo
terraform state show 'module.red.azurerm_subnet.this["web"]' # atributos guardados (incluye secretos si los hay)
terraform state pull | jq -r '.resources[] | .module // "root" + " " + .type + "." + .name'
2. Importar: adoptar lo que ya existe¶
Importar añade al estado un recurso que existe en Azure y lo enlaza con un bloque resource. Desde ese momento Terraform lo gestiona por completo; lo que exige es que el código describa el recurso tal como está, o el siguiente plan intentará "corregirlo". La forma moderna es el bloque import, que queda en Git, admite for_each y puede escribir el código por ti.
# 1. Obtén el id EXACTO (los ids de ARM distinguen mayúsculas: resourceGroups, networkSecurityGroups…)
az network nsg show -g rg-cmd-lab-001 -n nsg-web --query id -o tsv
# 2. Bloque import (en import.tf, temporal). El "to" aún no existe en el código
import {
to = azurerm_network_security_group.web
id = "/subscriptions/…/resourceGroups/rg-cmd-lab-001/providers/Microsoft.Network/networkSecurityGroups/nsg-web"
}
# 3. Deja que Terraform escriba el bloque resource a partir de lo que lee en Azure
terraform plan -generate-config-out=generado.tf # "1 to import"; crea generado.tf
# Revisa generado.tf: trae TODOS los atributos (muchos en null o por defecto). Recórtalo al mínimo
# que quieras gobernar, sustituye literales por referencias (resource_group_name = azurerm_resource_group.lab.name),
# muévelo a main.tf y borra generado.tf
terraform plan # "1 to import, 0 to change": el código coincide
terraform apply -auto-approve && rm import.tf # el bloque import se retira una vez aplicado
# Forma imperativa (equivalente, sin rastro en Git; el bloque resource debe existir ANTES):
terraform import azurerm_network_security_group.web "$(az network nsg show -g rg-cmd-lab-001 -n nsg-web --query id -o tsv)"
# Varios de golpe:
import {
for_each = { web = "…/networkSecurityGroups/nsg-web", datos = "…/networkSecurityGroups/nsg-datos" }
to = azurerm_network_security_group.this[each.key]
id = each.value
}
| Recurso | Id que espera import |
Cómo obtenerlo |
|---|---|---|
| Casi todos (grupo, VNet, NSG, cuenta, VM…) | El id de ARM completo | az <servicio> show … --query id -o tsv |
| Subred, regla de NSG (recursos hijo) | Id de ARM que incluye al padre: …/virtualNetworks/vnet/subnets/snet-web |
az network vnet subnet show … --query id |
Contenedor de blobs (azurerm 4.x con storage_account_id) |
…/storageAccounts/st…/blobServices/default/containers/tfstate |
az storage container-rm show … --query id (ARM, ✅ Topaz) |
| Asignación de rol | <scope>/providers/Microsoft.Authorization/roleAssignments/<guid> |
az role assignment list --scope … --query "[].id" (solo Azure real) |
random_password, tls_private_key… |
No se pueden importar: no existen fuera del estado | Se regeneran (y se rota el secreto) o se leen de Key Vault con un data |
⚠️ El código generado no es el código final.
-generate-config-outvuelca todos los atributos que el provider devuelve, incluidos los calculados y los que no deberías fijar. Aplicarlo tal cual funciona, pero deja un bloque de 60 líneas ilegible que además referencia el grupo por su nombre literal. La regla es la misma que en la página 6: gobierna lo que decides, referencia lo demás, yplanhasta ver 0 to change.
3. Mover: refactorizar sin destruir¶
Renombrar un recurso, meterlo en un módulo o cambiar su count por for_each cambia su dirección. Para Terraform, la dirección antigua "ha desaparecido" (destruir) y la nueva "es nueva" (crear). El bloque moved le dice que son la misma cosa; state mv hace lo mismo desde la terminal.
| Refactor | Bloque moved |
|---|---|
| Renombrar un recurso | moved { from = azurerm_subnet.web to = azurerm_subnet.frontal } |
| Meterlo en un módulo | moved { from = azurerm_virtual_network.lab to = module.red.azurerm_virtual_network.this } |
| Renombrar un módulo (mueve todo su contenido) | moved { from = module.red to = module.red_principal } |
count → for_each |
moved { from = azurerm_subnet.this[0] to = azurerm_subnet.this["web"] } (uno por índice) |
Recurso suelto → instancia de for_each |
moved { from = azurerm_subnet.web to = azurerm_subnet.this["web"] } |
Módulo sin for_each → con for_each |
moved { from = module.red to = module.red["hub"] } |
Bloque moved |
terraform state mv |
|
|---|---|---|
| Dónde queda | En el código y en Git: quien haga pull obtiene el mismo resultado |
En el estado, una sola vez; sin rastro salvo el historial de la terminal |
| Cuándo se aplica | En el siguiente plan/apply, con el lock tomado |
Inmediatamente (toma el lock). -dry-run para ver sin tocar |
| En módulos publicados | ✅ El autor del módulo añade el moved y todos los consumidores migran al actualizar |
❌ Cada consumidor a mano |
| Entre dos estados | ❌ | ✅ -state-out=../otro/terraform.tfstate: dividir un proyecto |
| Después | Se puede borrar el bloque una vez aplicado en todos los entornos (o dejarlo: es inofensivo) | Nada que limpiar |
4. Recrear: -replace, no taint¶
terraform taint marcaba un recurso en el estado para que el siguiente apply, de quien fuera, lo recreara. Está deprecado desde Terraform 0.15.2 y al ejecutarlo avisa: el problema es que modifica el estado compartido de forma silenciosa, y otra persona puede lanzar ese apply sin saber qué va a destruir. La opción -replace hace lo mismo pero en la operación actual, visible en el plan.
terraform plan -replace='module.red.azurerm_subnet.this["web"]' # "# module.red.azurerm_subnet.this["web"] will be replaced, as requested"
terraform apply -replace='module.red.azurerm_subnet.this["web"]' # varios: repite -replace
terraform taint 'module.red.azurerm_subnet.this["web"]' # funciona, pero: "Warning: Command taint is deprecated"
terraform untaint 'module.red.azurerm_subnet.this["web"]' # deshace la marca. Útil si heredas un estado con recursos tainted
# Declarativo: recrear B cada vez que A cambie (por ejemplo, una extensión de VM cuando cambia el script)
resource "azurerm_virtual_machine_extension" "bootstrap" {
# …
lifecycle { replace_triggered_by = [azurerm_storage_blob.script.content_md5] }
}
| Cuándo recrear a mano | Cuándo no |
|---|---|
| Una VM cuya extensión falló a medias y quedó en estado inconsistente; un recurso que Azure reporta como sano pero no funciona; probar que el módulo es reproducible | "Tiene una configuración incorrecta y no se puede actualizar" (el ejemplo del original): primero mira por qué. Si es un atributo ForceNew, el plan ya propone reemplazo sin que lo pidas. Si es un error del provider, -replace no lo arregla |
Cuentas de almacenamiento, bases de datos, Key Vaults: nunca con -replace a la ligera: se destruyen los datos. Y con prevent_destroy = true ni siquiera te dejará |
5. Olvidar, dividir y el resto de la caja de herramientas¶
| Herramienta | Para qué | Cuidado con |
|---|---|---|
removed { from = A lifecycle { destroy = false } } |
Dejar de gestionar un recurso sin borrarlo: pasa a otro proyecto o a gestión manual. Se retira el resource del código y se añade este bloque |
Con destroy = true (o sin lifecycle) lo destruye: es el comportamiento normal de borrar el bloque |
terraform state rm A |
Lo mismo, imperativo. Acepta módulos enteros | Si el resource sigue en el código, el siguiente plan intentará crearlo y fallará por nombre duplicado |
state mv -state-out=../b/terraform.tfstate A A |
Dividir un proyecto: mueve el recurso al estado de otro directorio. Después, el código se mueve a b/ y en a/ se referencia con data |
Con backend remoto: state pull en ambos, mv entre archivos locales, state push en ambos. Copia antes |
state replace-provider hashicorp/azurerm registry.terraform.io/hashicorp/azurerm |
Cambiar la dirección del provider en el estado (migraciones antiguas, forks, registries privados) | Solo la dirección; no cambia versiones ni esquemas |
apply -target=A |
Aplicar solo un recurso y sus dependencias. Para salir de un atolladero, no para el día a día | Deja el resto sin aplicar y Terraform lo recuerda: el plan siguiente lo avisa. Nunca en CI |
apply -refresh-only |
Aceptar en el estado cambios hechos fuera (drift) sin tocar Azure | Si alguien borró algo, lo olvida; si quieres recrearlo, apply normal (página 7) |
6. Laboratorio en Topaz¶
Un proyecto plano de red que iremos refactorizando sin destruir nada. Cada paso termina con un plan que debe decir No changes: es la prueba de que la cirugía salió bien.
mkdir -p ~/tf-cmd && cd ~/tf-cmd && cp ~/tf-st/providers.tf .
cat > main.tf <<'EOF'
resource "azurerm_resource_group" "lab" {
name = "rg-cmd-lab-001"
location = "eastus"
lifecycle { ignore_changes = [tags] }
}
resource "azurerm_virtual_network" "lab" {
name = "vnet-cmd-lab"
resource_group_name = azurerm_resource_group.lab.name
location = azurerm_resource_group.lab.location
address_space = ["10.80.0.0/16"]
}
resource "azurerm_subnet" "web" {
name = "snet-web"
resource_group_name = azurerm_resource_group.lab.name
virtual_network_name = azurerm_virtual_network.lab.name
address_prefixes = ["10.80.1.0/24"]
}
resource "azurerm_subnet" "datos" {
name = "snet-datos"
resource_group_name = azurerm_resource_group.lab.name
virtual_network_name = azurerm_virtual_network.lab.name
address_prefixes = ["10.80.2.0/24"]
}
EOF
terraform init && terraform apply -auto-approve # 4 to add
terraform state list # 4 direcciones planas
# ─── 1. Importar: un NSG creado "a mano" ────────────────────────────────────────
az network nsg create -g rg-cmd-lab-001 -n nsg-web -l eastus -o none
NSG_ID=$(az network nsg show -g rg-cmd-lab-001 -n nsg-web --query id -o tsv)
cat > import.tf <<EOF
import {
to = azurerm_network_security_group.web
id = "$NSG_ID"
}
EOF
terraform plan -generate-config-out=generado.tf # 1 to import
cat generado.tf # bloque completo con literales y atributos por defecto
# Sustitúyelo por la versión mínima con referencias:
cat >> main.tf <<'EOF'
resource "azurerm_network_security_group" "web" {
name = "nsg-web"
resource_group_name = azurerm_resource_group.lab.name
location = azurerm_resource_group.lab.location
}
EOF
rm generado.tf
terraform plan # 1 to import, 0 to change. Si dice "1 to change", falta o sobra un atributo
terraform apply -auto-approve && rm import.tf
terraform state show azurerm_network_security_group.web | head -5
# ─── 2. Renombrar: moved ────────────────────────────────────────────────────────
sed -i 's/"azurerm_subnet" "web"/"azurerm_subnet" "frontal"/' main.tf
terraform plan # 1 to add, 1 to destroy: renombrar = recrear. MAL
cat >> main.tf <<'EOF'
moved {
from = azurerm_subnet.web
to = azurerm_subnet.frontal
}
EOF
terraform plan # "azurerm_subnet.web has moved to azurerm_subnet.frontal" · No changes
terraform apply -auto-approve # solo escribe el estado
# ─── 3. count/recursos sueltos → for_each ───────────────────────────────────────
# Sustituye los dos bloques azurerm_subnet por uno solo:
python3 - <<'EOF'
import re
s = open("main.tf").read()
s = re.sub(r'resource "azurerm_subnet" "frontal" \{.*?\n\}\n', '', s, flags=re.S)
s = re.sub(r'resource "azurerm_subnet" "datos" \{.*?\n\}\n', '', s, flags=re.S)
s = re.sub(r'moved \{.*?\n\}\n', '', s, flags=re.S)
s += '''
resource "azurerm_subnet" "this" {
for_each = { web = "10.80.1.0/24", datos = "10.80.2.0/24" }
name = "snet-${each.key}"
resource_group_name = azurerm_resource_group.lab.name
virtual_network_name = azurerm_virtual_network.lab.name
address_prefixes = [each.value]
}
moved { from = azurerm_subnet.frontal to = azurerm_subnet.this["web"] }
moved { from = azurerm_subnet.datos to = azurerm_subnet.this["datos"] }
'''
open("main.tf", "w").write(s)
EOF
terraform fmt && terraform plan # 2 movidas · No changes
terraform apply -auto-approve
terraform state list # azurerm_subnet.this["datos"], azurerm_subnet.this["web"]
# ─── 4. Mover a un módulo ───────────────────────────────────────────────────────
mkdir -p modules/red
cat > modules/red/main.tf <<'EOF'
variable "nombre" { type = string }
variable "resource_group_name" { type = string }
variable "location" { type = string }
variable "address_space" { type = string }
variable "subredes" { type = map(string) }
resource "azurerm_virtual_network" "this" {
name = var.nombre
resource_group_name = var.resource_group_name
location = var.location
address_space = [var.address_space]
}
resource "azurerm_subnet" "this" {
for_each = var.subredes
name = "snet-${each.key}"
resource_group_name = var.resource_group_name
virtual_network_name = azurerm_virtual_network.this.name
address_prefixes = [each.value]
}
output "subnet_ids" { value = { for k, s in azurerm_subnet.this : k => s.id } }
EOF
# En main.tf: borra la VNet, las subredes y los moved anteriores; añade la llamada y los nuevos moved
python3 - <<'EOF'
import re
s = open("main.tf").read()
s = re.sub(r'resource "azurerm_virtual_network" "lab" \{.*?\n\}\n', '', s, flags=re.S)
s = re.sub(r'resource "azurerm_subnet" "this" \{.*?\n\}\n', '', s, flags=re.S)
s = re.sub(r'moved \{[^\n]*\}\n', '', s)
s += '''
module "red" {
source = "./modules/red"
nombre = "vnet-cmd-lab"
resource_group_name = azurerm_resource_group.lab.name
location = azurerm_resource_group.lab.location
address_space = "10.80.0.0/16"
subredes = { web = "10.80.1.0/24", datos = "10.80.2.0/24" }
}
moved { from = azurerm_virtual_network.lab to = module.red.azurerm_virtual_network.this }
moved { from = azurerm_subnet.this to = module.red.azurerm_subnet.this } # mueve TODAS las instancias
'''
open("main.tf", "w").write(s)
EOF
terraform init && terraform fmt && terraform plan # 3 movidas · No changes
terraform apply -auto-approve
terraform state list # module.red.azurerm_virtual_network.this, module.red.azurerm_subnet.this["web"], …
# ─── 5. Recrear un recurso concreto: -replace ───────────────────────────────────
terraform plan -replace='module.red.azurerm_subnet.this["web"]' # "will be replaced, as requested": 1 to add, 1 to destroy
terraform apply -replace='module.red.azurerm_subnet.this["web"]' -auto-approve
terraform taint 'module.red.azurerm_subnet.this["datos"]' # "Warning: Command taint is deprecated"
terraform plan # 1 to add, 1 to destroy: la marca está en el estado, invisible para otros
terraform untaint 'module.red.azurerm_subnet.this["datos"]' # quita la marca
terraform plan # No changes
# ─── 6. Olvidar sin borrar: removed ────────────────────────────────────────────
# El NSG pasa a gestionarse desde otro proyecto: quitamos el resource y añadimos removed
python3 - <<'EOF'
import re
s = open("main.tf").read()
s = re.sub(r'resource "azurerm_network_security_group" "web" \{.*?\n\}\n', '', s, flags=re.S)
s += '''
removed {
from = azurerm_network_security_group.web
lifecycle { destroy = false }
}
'''
open("main.tf", "w").write(s)
EOF
terraform plan # "will no longer be managed by Terraform, but will not be destroyed"
terraform apply -auto-approve
az network nsg show -g rg-cmd-lab-001 -n nsg-web --query name -o tsv # nsg-web: sigue existiendo
terraform state list | grep -c nsg # 0
sed -i '/^removed {/,/^}/d' main.tf # el bloque removed se retira una vez aplicado
# ─── 7. Dividir un proyecto: state mv -state-out ───────────────────────────────
# El NSG (y cualquier otro recurso de seguridad) tendrá su propio proyecto ~/tf-cmd-seg
mkdir -p ~/tf-cmd-seg && cp providers.tf ~/tf-cmd-seg/
cat > ~/tf-cmd-seg/main.tf <<'EOF'
data "azurerm_resource_group" "lab" { name = "rg-cmd-lab-001" } # el grupo lo gobierna el otro proyecto: se lee, no se crea
resource "azurerm_network_security_group" "web" {
name = "nsg-web"
resource_group_name = data.azurerm_resource_group.lab.name
location = data.azurerm_resource_group.lab.location
}
EOF
# Primero volvemos a adoptar el NSG aquí para tener algo que mover (import imperativo, para variar):
cat >> main.tf <<'EOF'
resource "azurerm_network_security_group" "web" {
name = "nsg-web"
resource_group_name = azurerm_resource_group.lab.name
location = azurerm_resource_group.lab.location
}
EOF
terraform import azurerm_network_security_group.web "$NSG_ID"
terraform plan # No changes
# Ahora la división: copia, mueve entre estados, limpia el código de origen
terraform state pull > copia-antes-de-dividir.json
cd ~/tf-cmd-seg && terraform init && cd ~/tf-cmd
terraform state mv -state-out=../tf-cmd-seg/terraform.tfstate \
azurerm_network_security_group.web azurerm_network_security_group.web
# "Move "azurerm_network_security_group.web" to "azurerm_network_security_group.web" · Successfully moved 1 object(s)."
sed -i '/^resource "azurerm_network_security_group" "web" {/,/^}/d' main.tf
terraform plan # No changes: aquí ya no está
cd ~/tf-cmd-seg && terraform plan # No changes: aquí está, y el data lee el grupo
terraform state list # data.azurerm_resource_group.lab, azurerm_network_security_group.web
# Con backend remoto sería: state pull en ambos → mv entre los dos archivos locales → state push en ambos (página 7)
# ─── 8. Limpiar ────────────────────────────────────────────────────────────────
terraform destroy -auto-approve # tf-cmd-seg: borra el NSG (el data no se destruye)
cd ~/tf-cmd && terraform destroy -auto-approve # grupo, VNet, subredes
🔷 La regla del laboratorio. Cada paso ha terminado en No changes (o en el reemplazo que se pidió expresamente). Si en tu Topaz un
plantrasmovedmuestra un update in-place de tags o de algún atributo que el emulador devuelve vacío, no es un fallo de la cirugía: es la misma diferencia de lectura anotada en la columna "En Topaz" de las páginas anteriores. Lo que nunca debe aparecer tras unmovedo unimportcorrecto es un destroy.
7. Errores comunes¶
⚠️ Solución de problemas
Mensaje o síntoma Causa y solución Resource already managed by Terraform al importar Ya está en el estado, quizá con otra dirección. state list | grep; si es un renombrado, usamoved, noimportCannot import non-existent remote object El id está mal: mayúsculas de ARM ( resourceGroups,networkSecurityGroups), subscription equivocada o recurso ya borrado. Cópialo deaz … show --query id, nunca a manoConfiguration for import target does not exist Falta el bloque resourceal que apuntato. Escríbelo o usa-generate-config-outTras importar, plandice 1 to change (o replace)El código no coincide con la realidad. Compara state showcon tu bloque; si el atributo es ForceNew, arregla el código, no apliques: destruiría el recurso que acabas de adoptarInvalid target address / Unknown resource en state mvDirección mal escrita (el module.red.mi_ipdel original). Necesita tipo y nombre:module.red.azurerm_public_ip.mi_ip. Copia destate listno matches found: azurerm_subnet.this[0] (zsh) o el corchete desaparece (bash) La shell interpreta []como glob. Comillas simples alrededor de toda la direcciónMoved object still exists El bloque resourcecon el nombre antiguo (from) sigue en el código.movedexige que solo exista el nuevoResource type mismatch en movedmovedsolo mueve entre el mismo tipo. Cambiarazurerm_virtual_machineporazurerm_linux_virtual_machineexigeremoved+importTras mover un módulo, el planquiere recrear los recursos confor_eachFaltan las claves: moveddemodule.redamodule.red["hub"]mueve todo; pero deazurerm_subnet.aathis["web"]hace falta un bloque por instanciaWarning: Command taint is deprecated Usa apply -replace=<dir>. Si heredas un estado con recursos marcados,untaintsigue disponibleTras state rm, elplanquiere crear el recurso yapplyfalla con already existsOlvidaste quitar el resourcedel código.removedte obliga a hacerlo;state rmno. Vuelve a importarlo o borra el bloqueInstance cannot be destroyed con -replaceprevent_destroy = trueestá haciendo su trabajo. Si de verdad quieres recrearlo, quítalo en el código, aplica, y vuelve a ponerlostate mv -state-out: Failed to load state: file does not existEl destino debe existir: haz terraform init(y, si hace falta, unapplyvacío) en el otro directorio antes de moverplantras-target: Resource targeting is in effect … the plan may be incompleteAviso esperado. Lanza un applycompleto en cuanto salgas del atolladero; no dejes el estado a mediasEl importdel contenedor falla con parsing … expected …/blobServices/default/containers/…En azurerm 4.x con storage_account_idel id es el de ARM (az storage container-rm show), no la URLhttps://….blob.core.windows.net/…de 3.x
8. Autoevaluación¶
- ¿Por qué renombrar un recurso hace que
planproponga destruir y crear? El estado enlaza direcciones con ids. La dirección antigua ya no está en el código (destruir) y la nueva no está en el estado (crear).movedle dice a Terraform que son el mismo objeto. - ¿Qué está mal en
terraform state mv 'azurerm_public_ip.mi_ip' 'module.red.mi_ip'? El destino no es una dirección válida: dentro de un módulo sigue haciendo falta tipo y nombre (module.red.azurerm_public_ip.mi_ip), y el módulo debe existir en el código con ese recurso. - ¿"El recurso importado no se gestionará automáticamente" es cierto?
No. Tras importar, Terraform lo gestiona por completo. Lo que hace falta es que el código coincida con la realidad, o el siguiente
applylo "corregirá".-generate-config-outayuda a escribir ese código. - ¿Por qué
taintestá deprecado y qué lo sustituye? Marca el estado compartido de forma silenciosa: otra persona puede lanzar elapplyque destruye sin saberlo.apply -replace=<dir>hace lo mismo en la operación actual, visible en el plan. - ¿Cuándo prefieres
movedastate mvy al revés?movedsiempre que sea posible: queda en Git, se aplica con el lock y funciona en módulos publicados.state mves imprescindible para mover entre dos estados (-state-out). - ¿Cuántos bloques
movedhacen falta para pasar dos recursos sueltos a unfor_each? ¿Y para renombrar un módulo con veinte recursos? Dos (uno por instancia destino). Uno:moved { from = module.a to = module.b }arrastra todo su contenido. - ¿Qué diferencia hay entre
removedcondestroy = falsey borrar el bloqueresource? Borrar el bloque destruye el recurso en Azure.removedcondestroy = falsesolo lo saca del estado: el recurso sigue existiendo, gestionado desde otro sitio. - ¿Por qué los ids de ARM fallan al importar si los escribes a mano?
Distinguen mayúsculas en los segmentos (
resourceGroups,virtualNetworks). Se obtienen conaz … show --query id -o tsv. - ¿Qué recursos no se pueden importar?
Los que no existen fuera del estado:
random_*,tls_private_key,null_resource. Se regeneran (rotando el secreto) o se sustituyen por undataque lea el valor de Key Vault. - ¿Qué comando es el juez de que una cirugía salió bien?
terraform plancon No changes. Cualquier destroy tras unmovedo unimportsignifica que algo está mal escrito; no apliques.
9. Referencias¶
- Direcciones de recursos y comandos
terraform state(list,show,mv,rm,pull,push,replace-provider) - Bloque
import, generar configuración con-generate-config-outyterraform import(CLI) - Bloque
moved, refactorizar módulos y bloqueremoved - Opción
-replace,taint(deprecado) yreplace_triggered_by - Resource targeting (
-target) y modo-refresh-only - Sección Import de cada recurso azurerm (formato exacto del id) y reglas de nombres e ids de ARM
- Azure Export for Terraform (aztfexport): importación masiva de un grupo de recursos existente
- Azure Local Emulator (Topaz)