Hola a todos! En este laboratorio práctico vamos a explorar AWS CloudFormation y descubrir cómo la infraestructura como código (IaC) puede ayudarnos a trabajar de forma más rápida, ordenada y profesional como arquitectos de soluciones.
Que es Infraestructura como codigo (Iac)?
Infraestructura como Código (IaC) es una práctica que consiste en definir y gestionar la infraestructura de TI mediante archivos de código, en lugar de realizar configuraciones manuales. Esto permite automatizar el aprovisionamiento, configuración y gestión de recursos como servidores, redes y bases de datos de forma consistente, reproducible y escalable.
Con IaC, puedes versionar tus plantillas, revisarlas como cualquier otro código y desplegar entornos completos en minutos, lo que mejora la eficiencia, reduce errores humanos y facilita el trabajo en equipo.
Ejemplo practico.
Imagina que trabajas con bases de datos RDS con frecuencia. En lugar de crear cada base de datos manualmente desde la consola de AWS lo cual puede ser lento, repetitivo y propenso a errores puedes usar una plantilla de CloudFormation para definir ese recurso con código.
Una vez que tienes esa plantilla, solo necesitas subirla y, si es necesario, modificar algunos parámetros (como la capacidad o el nombre de la base de datos). Esto te permite desplegar nuevas instancias de RDS en minutos, de forma rápida, consistente y controlada.
Sintaxis y herramienta para crear una plantilla cloudformation
Para crear una plantilla de CloudFormation puedes utilizar cualquier editor de código. En mi caso, suelo utilizar Visual Studio Code por su facilidad de uso y soporte para extensiones relacionadas con AWS.
CloudFormation permite definir las plantillas en dos formatos: JSON y YAML. A continuación te muestro un ejemplo simple en ambos formatos, para que elijas el que más te convenga o te resulte más legible.
Definamos una instancia EC2:
Sintaxis YAML
Resources:
MiInstanciaEC2:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: ami-06c8f2ec674c67112
Sintaxis JSON:
{
"Resources": {
"MiInstanciaEC2": {
"Type": "AWS::EC2::Instance",
"Properties": {
"InstanceType": "t2.micro",
"ImageId": "ami-06c8f2ec674c67112"
}
}
}
}
Como puedes ver, YAML es más conciso y fácil de leer, por lo que suele ser la opción preferida de muchos desarrolladores y arquitectos al trabajar con plantillas de CloudFormation.
En mi caso, como he trabajado con Python, un lenguaje que también utiliza indentación significativa, me siento más cómodo usando YAML.
Tú puedes elegir el formato con el que te sientas más cómodo, ya que ambos son compatibles y tienen el mismo poder expresivo.
Definición de una plantilla cloudformationn
Para comenzar, necesitas crear un archivo de plantilla usando tu editor de código preferido. Puedes usar cualquiera de los dos formatos soportados por CloudFormation: YAML o JSON.
Por convención, los nombres de archivo suelen terminar en .yaml o .json, según el formato elegido.
Estructura de un template
Lo primero que debemos especificar como buena practica es la version de la plantilla, se hace de la siguiente manera.
AWSTemplateFormatVersion:
Descripción: Es un campo opcional que define la versión de la plantilla de AWS CloudFormation. Aunque puedes omitirlo, es una buena práctica incluirlo para que quede claro con qué versión del formato se está trabajando.
Formato: Usualmente se especifica en formato de fecha, como "2010-09-09".
Description:
Campo opcional que proporciona una breve descripción del propósito de la plantilla.
Posición: Puede colocarse en cualquier parte de la plantilla, aunque es habitual ponerla justo después de AWSTemplateFormatVersion (si está presente).
Estos dos segmentos la versión del formato y la descripción suelen colocarse al inicio de toda plantilla de CloudFormation, justo en la parte superior. No son obligatorios, pero es una buena práctica incluirlos para mantener claridad y orden.
Hasta el momento nuestra plantilla se ve asi:
AWSTemplateFormatVersion: "2010-09-09"
Description: This my first template
*Comencemos a definir un recurso.
Al definir un recurso en CloudFormation, es importante tener a mano la documentación oficial, ya que cada tipo de recurso tiene múltiples propiedades y no es necesario aprenderlas todas de memoria.
Tampoco necesitas usar todas las propiedades en cada caso, dependerá de lo que quieras lograr con el recurso. Usa solo lo necesario según tu objetivo.
Puedes encontrar la documentación oficial de los recursos de CloudFormation aquí:
AWS resource and property types reference
Resources:
Descripción: La sección Resources es la única obligatoria en una plantilla de CloudFormation y se utiliza para definir los recursos de infraestructura que deseas crear y gestionar con CloudFormation.
Sin la sección Resources, una plantilla de CloudFormation no sería válida, ya que no tendría recursos que crear, actualizar o eliminar.
Cada recurso se define con un identificador lógico (el nombre que tú eliges), un tipo de recurso por ejemplo, AWS::EC2::Instance para una instancia EC2 y un conjunto de propiedades que lo configuran.
Aunque esta sección suele ubicarse al final del archivo, en este laboratorio la abordamos primero porque a partir de ella aprenderemos de forma más práctica y clara cómo funcionan el resto de los segmentos de una plantilla.
Definamos una instancia EC2 en nuestro segmento de recursos, si quieres ver las distintas propiedades de una EC2 y conocer que propiedades son obligatorias u opcionales te dejo el link Definicion de una EC2, para este ejemplo usare las propiedades minimas necesarias.
Resources:
Myinstance:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: ami-06c8f2ec674c67112 # Una AMI valida para tu region
Explicación rapida:
MyInstance: es el nombre lógico del recurso dentro de la plantilla.
No es el nombre que veras en AWS, ese se puede definir con una etiqueta (Tag) si se desea.
Type: especifica el tipo de recurso que estamos creando, en este caso una instancia EC2.
Properties: aquí se definen todas las propiedades necesarias del recurso. Algunas son obligatorias (como InstanceType o ImageId para EC2), y otras dependerán del caso de uso.
Hora de desplegar nuestra primera plantilla
Hasta este punto, ya tenemos una plantilla válida y funcional que define una instancia EC2 con las propiedades mínimas. Así que estamos listos para desplegarla en AWS.
Nuestra plantilla se ve así hasta el momento:
AWSTemplateFormatVersion: "2010-09-09"
Description: This my first template
Resources:
Myinstance:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: ami-06c8f2ec674c67112 # Una AMI valida para tu region
Pasos para desplegar la plantilla desde nuestro computador usando la consola de AWS.
Ingresemos a nuestra consola y entremos al servicio de cloudformation.
Selecciona: "Crear Stack"
Selecciona: "Elegir una platilla existente"
Selecciona: "Subir un template file" y sube tu archivo.
Selecciona: Siguiente y asigna un nombre al stack
El resto de opciones dejalas por defecto y lanza el stack.
✅ Si seguiste correctamente la sintaxis (YAML) en mi caso (respetando la indentación) y escribiste las propiedades tal como indica la documentación como hicimos en el ejemplo anterior, tu stack debería haberse creado sin errores.
Al finalizar el despliegue, verás algo como esto en la consola de CloudFormation:
El estado del stack como: CREATE_COMPLETE
Una instancia EC2 creada en el servicio EC2
🎉 ¡Felicidades! Acabas de lanzar tu primer recurso en AWS usando CloudFormation.
📝 Nota importante:
En la consola de CloudFormation encontrarás muchas opciones y funcionalidades adicionales, pero ese no es nuestro enfoque en este laboratorio.
Por ahora, estamos concentrados en lo esencial: crear correctamente una plantilla y desplegar recursos.
En futuras ediciones de estos laboratorios exploraremos a fondo las capacidades de la consola y el uso de la CLI para gestionar stacks.
Que podemos mejorar?
Actualmente, la AMI y el tipo de instancia están definidos de forma estática (hardcodeados), lo que significa que siempre se usarán los mismos valores al desplegar.
Esto no es ideal si queremos reutilizar la plantilla en diferentes escenarios.
¿La solución? Usar parámetros para hacerla más flexible.
Introducción a los parámetros
Parameters:
Descripción: Aunque es opcional, esta sección es muy común y poderosa. Permite que ciertos valores de la plantilla sean definidos por el usuario al momento de desplegar, en lugar de estar codificados de forma fija.
Cuando usarlos: Cuando queremos que ciertas propiedades tengan valores dinámicos tales como: El tipo de una instancia, un AMI, el ID de una VPC etc, dichos parametros seran referenciados desde las propiedades de los recursos.
Ejemplo:
Parameters:
InstanceTypeParameter:
Type: String
Default: t2.micro
AllowedValues:
- t2.micro
- t2.small
Description: "Tipo de instancia EC2 para el servidor"
Los parámetros también deben tener un nombre lógico dentro de la plantilla con el cual puedan ser referenciados desde un recurso en el ejemplo: "InstanceTypeParameter".
Tipos de parámetros: String, Number, List, AWS::EC2::KeyPair::KeyName, entre otros, para nuestro ejemplo el parámetro recibirá como valor un String.
Valor por defecto: Se puede especificar un valor por defecto para un parámetro en caso que el usuario no lo especifique.
Valores permitidos: Se pueden especificar valores como permitidos, es decir que el valor de un parámetro ingresado por el usuario debe ser uno de esa lista,si no se especifica ninguno el valor sera el especificado como default.
Descripción: Útil para especificar para que se esta usando este para parametro o a que recurso ira vinculado.
Para aprender a definir parámetros podemos repasar la documentación las veces que sean necesarias Como crear parametros
Incluyendo parámetros para nuestra plantilla
En nuestro template tenemos harcodeado el valor de la propiedad InstanceType y ImageId, así que incluyamos estos dos parámetros para que nuestra plantilla se reutilizable.
Como quedarían estos parámetros en nuestra plantilla?
AWSTemplateFormatVersion: "2010-09-09"
Description: This my first template
Parameters:
InstanceTypeParameter:
Description: Valor para InstanceType
Type: String
Default: t2.micro
AllowedValues:
- t2.micro
- t2.small
ImageIdParameter:
Description: Valor para ImageId
Type: String
Resources:
Myinstance:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: ami-0953476d60561c955 # Una AMI valida
Con esto ya estamos listos?
¡Aún no!
Falta un paso muy importante: referenciar los parámetros desde las propiedades como InstanceType e ImageId, para que realmente usen los valores que el usuario indique al momento del despliegue.
¿Y cómo se hace eso?
Usaremos funciones intrínsecas.
Existen muchas funciones intrínsecas que profundizaremos en ellas mas adelante por el momento nos interesa la función Ref.
Ref: La función intrínseca Ref devuelve el valor de un parámetro especificado, recurso, u otra función intrínseca. Esta función se utiliza comúnmente para crear referencias entre recursos dentro de una plantilla de CloudFormation.
Como quedaría entonces nuestra plantilla usando la función Ref para referenciar los parámetros?
AWSTemplateFormatVersion: "2010-09-09"
Description: This my first template
Parameters:
InstanceTypeParameter:
Description: Valor para InstanceType
Type: String
Default: t2.micro
AllowedValues:
- t2.micro
- t2.small
ImageIdParameter:
Description: ID de una AMI valida
Type: String
Resources:
Myinstance:
Type: AWS::EC2::Instance
Properties:
InstanceType:
Ref: InstanceTypeParameter
ImageId:
Ref: ImageIdParameter # Una AMI valida
Como puedes ver, simplemente usamos Ref para referenciar los parámetros desde las propiedades que deben ser dinámicas y que tomaran los valores que se ingresen para cada parámetro, lo hacemos pasando su nombre lógico dentro de la plantilla a la función Ref.
Si quieres profundizar más sobre las funciones intrínsecas en CloudFormation, te dejo el enlace oficial de la documentación: Funciones Intrinseca.
Despleguemos nuestra plantilla con parámetros
Primero, eliminemos el stack anterior que usaba valores hardcodeados:
Ve a la consola de CloudFormation, selecciona el stack y haz clic en "Delete stack".
Ahora sí, subamos nuestra nueva plantilla con parámetros, siguiendo los mismos pasos que usamos anteriormente.
Selecciona: Siguiente
Asigna un nombre a tu stack
Ahora verás una sección de parámetros en la consola para asignarles un valor.
ImageIdParameter: De donde sacamos este valor?
Puedes obtenerlo desde la sección de EC2 en la consola de AWS. Abriendo una nueva pestaña, ve al Catálogo de AMIs y allí encontrarás las AMIs disponibles, las mismas que puedes seleccionar al lanzar una instancia manualmente. Solo tienes que copiar el ID de la AMI que desees usar.
En cuanto al parámetro InstanceTypeParameter, tendrá asignado un valor por defecto (t2.micro), tal como lo definimos en la plantilla. Sin embargo, también verás una lista desplegable con los valores permitidos, desde la cual puedes seleccionar otro tipo de instancia si lo deseas.
- Haz clic en "Siguiente", deja los valores por defecto en las siguientes secciones y finalmente lanza tu stack.
Si todo salió bien, deberías ver tu stack con el estado "CREATE_COMPLETE". También puedes ir al panel de EC2 y revisar tu instancia: notarás que tanto la AMI como el tipo de instancia coinciden con los valores que pasaste como parámetros al momento del despliegue.
Hasta ahora hemos creado una instancia, pero no tiene asociado ningún grupo de seguridad (SG). ¿Cómo podemos crear uno y vincularlo con nuestra instancia?
Vinculación entre recursos
Primero, consultemos la documentación oficial para ver cómo definir un Security Group (SG) en CloudFormation y cuáles son sus propiedades necesarias:: Security Groups en cloudformation
Las propiedades mas relevantes son:
VpcId: Si no se especifica, el SG se crea en la VPC por defecto de la región. Puedes hardcodear el ID o, mejor aún, usar un parámetro (como ya aprendimos) y referenciarlo con Ref. Te dejo esa parte como tarea 😉.
Description: Es recomendable incluir una descripción que indique el propósito del SG o a qué recurso está asociado.
SecurityGroupIngress: Aquí defines las reglas de entrada. Puedes incluir tantas como necesites.
SecurityGroupEgress: Sirve para definir reglas de salida. En este workshop lo omitiremos, ya que por defecto se permite todo el tráfico saliente, a menos que desees restringir alguno explícitamente.
Así quedaría la definición de nuestro SG
SecurityGroupSSH:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Permitira conexiones SSH
VpcId:
Ref: VpcIdParameter
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
Como puedes ver, estamos permitiendo conexiones SSH al especificar el protocolo tcp y el puerto 22. Además, la propiedad VpcId no está hardcodeada, sino que hace referencia a un parámetro definido previamente, tal como te sugerí que implementaras como práctica. Esto refuerza la idea de mantener nuestras plantillas dinámicas y reutilizables.
Pero como asociamos ese grupo de seguridad con nuestra instancia?
Para esto, agregamos la propiedad SecurityGroupIds a nuestra instancia (No la habíamos agregado porque no es obligatoria a menos que quieras especificar un SG. Para asignarle el grupo de seguridad, usamos la función intrínseca Ref, tal como ya aprendimos; simplemente referenciamos el nombre lógico del grupo de seguridad creado, siguiendo el mismo proceso que antes.
Miremos nuestra plantilla completa antes de ir a desplegar:
AWSTemplateFormatVersion: "2010-09-09"
Description: This my first template
Parameters:
InstanceTypeParameter:
Description: Especifica el tipo de instancia
Type: String
Default: t2.micro
AllowedValues:
- t2.micro
- t2.small
ImageIdParameter:
Description: Especifica una AMI
Type: String
VpcIdParameter:
Description: My Vpc
Type: String
Resources:
Myinstance:
Type: AWS::EC2::Instance
Properties:
InstanceType:
Ref: InstanceTypeParameter
ImageId:
Ref: ImageIdParameter # Una AMI valida
SecurityGroupIds:
Ref: SecurityGroupSSH
SecurityGroupSSH:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Permitirá conexiones SSH
VpcId:
Ref: VpcIdParameter
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
Despleguemos nuestra nueva plantilla. Ahora, al crear el stack, ahora debes ingresar el ID de la VPC en el parámetro que añadimos.
Una vez creado el stack, ve a la consola de EC2 y revisa tu instancia; en la sección de seguridad deberías ver el grupo de seguridad asociado según lo definido en la plantilla.
Ahora que ya sabemos cómo definir y vincular recursos y parámetros, vamos a aprender cómo usar condiciones para crear plantillas más flexibles que se ajusten según el entorno o situación en que desplegamos.
Condiciones: Despliegues personalizados según escenarios
Las condiciones es una sección opcional que se crea para establecer las condiciones o circunstancias en las que se debe crear un recurso o configurar la propiedad de un recurso.
Controlar recursos según el entorno (dev o prod)
Ahora vamos a establecer que el grupo de seguridad se cree solo si el entorno es "dev", y que no se cree en producción. Para lograrlo, usaremos la sección de Conditions, que trabaja en conjunto con un parámetro (donde definimos el entorno) y algunas funciones intrínsecas de CloudFormation.
Empecemos definiendo el parámetro que ya sabemos como hacerlo.
EnvironmentParameter:
Description: Define el ambiente en el que se desplegaran los recursos
Type: String
Default: Dev
AllowedValues:
- Dev
- Prod
El valor por defecto para el ambiente será Dev, pero también puedes seleccionar entre las opciones Dev y Prod al momento de desplegar la plantilla.
Ahora aprendamos a definir una condición en CloudFormation. Te dejo la documentación oficial para que puedas reforzar cualquier detalle adicional:condiciones en cloudformation
Conditions:
EnvironmentIsDev:
Fn::Equals: [Ref: EnvironmentParameter, Dev]
Al igual que otros elementos en nuestra plantilla, las condiciones también requieren un nombre lógico. En este caso usamos EnvironmentIsDev, ya que queremos evaluar si el entorno seleccionado es Dev.
Como mencionamos antes, las condiciones se apoyan en funciones intrínsecas. Ya conoces Ref, y ahora estamos usando una nueva: Fn::Equals, que compara dos valores. Puedes consultar la lista completa de funciones para condiciones aquí:: Funciones para condiciones
Veamos los parámetros que recibe Fn::Equals:
[Ref: EnvironmentParameter, Dev]
Aquí estamos usando Ref para obtener el valor del parámetro EnvironmentParameter, que el usuario define al momento del despliegue. Luego comparamos ese valor con la cadena "Dev".
Si coinciden, la condición devuelve true.
(Qué buenos recuerdos de Python, ¿verdad? 😄)
Ahora necesitamos vincular la condición al grupo de seguridad para que solo se cree si el entorno es Dev.
Para hacerlo, usamos la clave Condition antes de las propiedades del recurso. Así quedaría nuestro grupo de seguridad:
SecurityGroupSSH:
Type: AWS::EC2::SecurityGroup
Condition: EnvironmentIsDev
Properties:
GroupDescription: Permitira conexiones SSH a Myinstance
VpcId:
Ref: VpcIdParameter
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
Como puedes ver, agregamos una condición a nuestro grupo de seguridad haciendo referencia directa al nombre lógico de la condición que definimos. En este caso, no es necesario usar Ref, ya que CloudFormation interpreta automáticamente el nombre de la condición.
Además, estábamos referenciando el grupo de seguridad en nuestra instancia. Si el entorno es Prod y el grupo de seguridad no se crea, AWS mostrará un error de dependencia, ya que estamos apuntando a un recurso que no existe. Para evitar esto, debemos condicionar también esa referencia, utilizando la función intrínseca Fn::If, que nos permite usar el grupo solo si la condición se cumple.
PD: Cuando usamos una condición para controlar la creación de un recurso, como hicimos con el grupo de seguridad, debemos agregar la propiedad Condition antes de las propiedades del recurso. En cambio, si la condición afecta solo una propiedad dentro de un recurso (y no su creación), usamos directamente Fn::If sin necesidad de declarar Condition al nivel del recurso. No lo olviden… ¡más adelante tendrán un reto sobre esto!
Myinstance:
Type: AWS::EC2::Instance
Properties:
InstanceType:
Ref: InstanceTypeParameter
ImageId:
Ref: ImageIdParameter # Una AMI valida para tu region
SecurityGroupIds:
- Fn::If: [EnvironmentIsDev, Ref: SecurityGroupSSH, Ref: "AWS::NoValue"]
La función Fn::If evalúa la condición EnvironmentIsDev. Si EnvironmentIsDev devuelve true, se referencia al grupo de seguridad porque este sí se creará. En caso contrario, si devuelve false, no se referencia ningún recurso ("AWS::NoValue"), lo que evita el error de dependencia por intentar usar un grupo de seguridad que no existe
Eliminemos el stack anterior para desplegar este nuevo y ver cómo funcionan las condiciones.
Revisión del stack según el entorno seleccionado
Si en el parámetro Environment elegimos Dev, nuestra instancia debería tener el grupo de seguridad creado dentro del stack correctamente adjunto.
Si al crear el stack seleccionamos Prod como entorno, el grupo de seguridad definido en la plantilla no se creará. En su lugar, la instancia tendrá asociado el grupo de seguridad por defecto de la VPC, el cual normalmente solo permite tráfico interno dentro de la misma red.
🧠 Challenge sobre condiciones
Hasta ahora, usamos condiciones para decidir si se crea o no un recurso según el valor de un parámetro (en nuestro caso: Dev o Prod).
Pero las condiciones también pueden aplicarse a propiedades dentro de recursos. ¿Te animas a hacer que la propiedad InstanceType de nuestra instancia sea t2.micro si el ambiente es Dev, y t2.small si es Prod?
🧩 Pista: Ya no necesitarás el parámetro que usábamos antes para definir el tipo de instancia 😉
Si logras completarlo, déjame un mensaje en este post diciendo "Reto cumplido Javi". Y si te atoras en algún paso, también puedes dejar tu duda en los comentarios.
Resumen
✅ Checklist: Lo que aprendiste hoy en CloudFormation
🔹 📌 Parámetros (Parameters)
Cómo crear parámetros para personalizar tu plantilla al desplegarla.
Ejemplo: elegir el tipo de ambiente (Dev o Prod).
🔹 📌 Recursos (Resources)
Cómo definir recursos como instancias EC2 y grupos de seguridad.
Cada recurso debe tener un nombre lógico, tipo y propiedades.
🔹 📌 Condiciones (Conditions)
Cómo controlar si un recurso o una propiedad se crea, usando una condición.
Declaramos condiciones con funciones como Fn::Equals.
Las condiciones pueden aplicarse a:
Recursos completos: usando Condition antes de las propiedades.
Propiedades puntuales: usando Fn::If dentro del valor.
🔹 📌 Funciones intrínsecas
Ref: referencia a un parámetro o recurso.
Fn::Equals: compara dos valores.
Fn::If: evalúa una condición y devuelve un valor según el resultado.
AWS::NoValue: elimina una propiedad si la condición no se cumple.
Si este workshop recibe buen apoyo e interacción en los comentarios, me animaré a llevarlo al siguiente nivel con una entrega intermedia donde abordaremos temas más avanzados como:
🔧 Checklist del próximo Workshop: CloudFormation Intermedio
🔄 📌 Actualización de Stacks con Change Sets
Qué es un Change Set y por qué es útil antes de aplicar cambios.
Cómo previsualizar el impacto de una actualización sin afectar recursos activos.
Crear y ejecutar Change Sets desde la consola o CLI.
🧠 📌 Uso de Metadata (Metadata)
Agregar información adicional a recursos (útil para herramientas, documentación o automatización).
Uso práctico con cfn-init y cfn-hup en configuraciones automatizadas.
🗺️ 📌 Mappings (Mappings)
Crear estructuras de datos tipo diccionario para condiciones según la región, ambiente, AMIs, etc.
Acceder a valores con Fn::FindInMap.
💻 📌 Despliegue de Stacks desde la Línea de Comandos (CLI)
Comandos clave de aws cloudformation para:
Crear, actualizar y eliminar stacks.
Validar plantillas.
Ver eventos y salidas del stack.
Recomendaciones para automatizar despliegues y flujos de trabajo con scripts.
Hasta la próxima.
Top comments (0)