Terraform: Desbloqueando el temido ConditionalCheckFailedException

El error <code>ConditionalCheckFailedException</code> de Terraform es frustrante y común. Te explico por qué ocurre y cómo puedes resolverlo de forma segura.

La gestión de estado en Terraform es una bendición y una maldición al mismo tiempo. Entiendo la necesidad de los bloqueos, que son críticos para evitar desastres cuando varios procesos intentan modificar la infraestructura a la vez. Pero, uff, me frustra cuando un proceso fantasma deja el estado bloqueado con un ConditionalCheckFailedException. Es una situación que te frena en seco y que, de hecho, he visto más veces de las que me gustaría en proyectos de clientes y hasta en mis propios despliegues.

El error ConditionalCheckFailedException no es más que la forma que tiene Terraform de decirte: "¡Alto ahí! Alguien más (o algo) está trabajando con este estado en este momento, o al menos eso creo." Generalmente, aparece cuando una operación de terraform plan o terraform apply no termina correctamente. Piensa en una interrupción de red, un proceso de CI/CD que se cae a medio camino, o simplemente que alguien cerró la terminal antes de que Terraform pudiera liberar el bloqueo.

Cuando esto pasa, Terraform deja un bloqueo en tu backend de estado (si usas uno, que deberías) para proteger la integridad de tu infraestructura. El problema es que, si el proceso original ya no existe, ese bloqueo se queda ahí, huérfano, impidiéndote hacer cualquier otra operación. Es como una llave que se pierde y la puerta queda cerrada.

Antes de meter las manos, lo más importante es estar 100% seguro de que no hay otro proceso ejecutándose. Esto es crítico. Me ha pasado revisar un repo donde un colega había iniciado un apply y se le había quedado la ventana de la terminal abierta, y yo, al intentar correr mi plan, me topaba con esto. Si fuerzas el desbloqueo mientras hay un proceso real, créeme, vas a terminar con tu estado de Terraform corrupto, y eso es un dolor de cabeza que no le deseo a nadie. Revisar tus pipelines, hablar con el equipo, o simplemente esperar unos minutos (o una hora si estás paranoico, como recomiendan algunos) es lo más sensato.

Una vez que confirmaste que no hay nadie más en la cancha, tienes dos opciones principales para lidiar con este bloqueo fantasma.

Opción 1: El Desbloqueo Forzado

La forma más directa es usar el comando terraform force-unlock. Este comando le dice a Terraform que ignore el bloqueo existente y lo libere. Necesitarás el ID del bloqueo, que siempre viene en el mensaje de error del ConditionalCheckFailedException. Algo así:

terraform force-unlock <ID_DEL_BLOQUEO>

Por ejemplo, si tu error te muestra un ID como 9db590f1-b6fe-c5f2-2678-8804f089deba, el comando sería:

terraform force-unlock 9db590f1-b6fe-c5f2-2678-8804f089deba

A veces, si por alguna razón el ID no aparece claro o simplemente quieres ser más directo (y estás *muy* seguro), puedes usar la flag -force directamente:

terraform force-unlock -force

Yo prefiero usar el ID específico, me da más tranquilidad. Es una medida de seguridad extra, por si acaso.

Opción 2: Ejecutar sin Bloqueo (con Cuidado)

Existe otra opción, que es decirle a Terraform que simplemente no intente adquirir un bloqueo para la operación actual. Esto lo haces con la flag -lock=false:

terraform plan -lock=false

Aunque funciona y te saca del apuro, esta opción no me convence del todo para un uso habitual. Deshabilitar el bloqueo es como andar en auto sin cinturón de seguridad: sabes que puedes, pero no deberías. Te saltas una capa de protección fundamental que Terraform pone para evitar corrupción del estado. Solo la usaría en casos muy específicos, donde tengo un control absoluto del entorno y sé que no hay ni la más mínima posibilidad de concurrencia. De hecho, rara vez la uso, prefiero arreglar el problema de raíz con el force-unlock.

Este error es un buen recordatorio de la fragilidad del estado de Terraform y la importancia de tener pipelines robustos. Esto me costó un par de canas al principio, especialmente cuando trabajaba en equipos más grandes donde la comunicación sobre quién estaba corriendo qué, no era perfecta.

Si tuviera que empezar de cero con Terraform hoy, lo que haría diferente es asegurar mi backend de estado desde el día uno con un mecanismo de bloqueo sólido y bien configurado (S3 con DynamoDB es un clásico por algo). Y, además, me enfocaría en construir pipelines de CI/CD que sean lo más resilientes posible a fallos, para minimizar la probabilidad de que se queden procesos huérfanos con bloqueos activos. Una buena instrumentación para monitorear esos bloqueos también sería clave, para no tener que depender de un error para enterarme de que algo anda mal.

Jorge RequenaDeveloper full-stack · Chile