# Tema 5 · 2 · Ejercicio 1: ETL Titanic (S3 a RDS) Presentación del Tema 5: «Ejercicio 1: Titanic» (la demostración de `ejemplo-lambda-titanic.py` está en «AWS Lambda»). Duración: 15-20 min. Coste: prácticamente nulo en esta actividad (Lambda factura solo mientras se ejecuta); lo que cuesta es la infraestructura base de la carpeta 05.01, que sigue encendida (y que **se apaga sola a las 4 horas**: ver más abajo). > **Estado de la verificación:** el script (un único archivo, con el código de la Lambda embebido y los comandos `crear` / `estado` / `probar` / `eliminar` / `extraer`) se ha probado en AWS real sobre la infraestructura del Paso 0, incluido el caso de la RDS parada por el apagado automático. Las salidas de abajo son las que escribe el script. > **¿Dudas con un script?** Todos los scripts de esta carpeta traen su propia ayuda: `./gestionar-etl-titanic.sh --ayuda` (también vale `--help` o `-h`) explica los pasos y las opciones. Este `README.md` es el paso a paso de la actividad: tenlo a mano y consúltalo antes de preguntar. > **Etiquetas de lo que creas.** Este ejercicio no crea recursos propios: trabaja dentro de los de `05.01-infraestructurabase`, que ya llevan las etiquetas `curso` = `G214`, `universidad` = `CUNEF` y `actividad` = `05.01-infraestructurabase`. Más detalle (cómo etiquetar con la CLI, cómo buscar y qué hacer en una emergencia): [README principal](../../README.md), sección «Etiquetas». ## Qué vas a hacer Vas a sustituir el código de marcador de la Lambda creada en la carpeta 05.01 por el código real de un proceso ETL: la función descarga `titanic.csv` del bucket S3 (**Extract**), lo limpia (**Transform**) y lo carga en una tabla de la base de datos MariaDB (**Load**). Después lo compruebas con consultas SQL en Adminer, y haces el experimento de la presentación (borrar pasajeros y volver a ejecutar). **Qué se crea en AWS:** ningún recurso nuevo. Se actualiza el código de la Lambda del Paso 0 y su primera ejecución crea en la RDS la base `titanic` y la tabla `passengers`. **Qué hace la Lambda** (código `lambda-etl-titanic.py`, embebido en el script; ver `extraer` más abajo): descarga `titanic/raw/titanic.csv` del bucket (Extract), lo limpia (Transform: edad media para rellenar edades vacías, `Ticket` solo con dígitos, `Fare` a 2 decimales, `Cabin` vacía a `C00`, nombre separado en `LastName` y `FirstName`, `Survived` a verdadero/falso, entre otros) y lo inserta en `passengers` de la base `titanic` (Load), creándolas si no existen. La tabla tiene `PassengerId` como clave primaria y la carga usa `INSERT ... ON DUPLICATE KEY UPDATE`. Las credenciales y el bucket los lee de las variables de entorno que puso el Paso 0 en la función; no tienes que escribir nada en el código. ## Qué contiene esta carpeta | Fichero | Para qué sirve | En qué paso se usa | |---|---|---| | `gestionar-etl-titanic.sh` | **Un solo script** con todo: empaqueta el código con sus librerías y lo sube a la Lambda del Paso 0 (`crear`), comprueba su estado y su última ejecución (`estado`), la invoca (`probar`) y la deja como estaba (`eliminar`). **Lleva dentro el código de la Lambda y `requirements.txt`.** | Paso 2 (y Limpieza) | | `titanic.csv` | El dataset (891 pasajeros). Ya lo subió el Paso 0 al bucket; se incluye aquí por si hay que volver a subirlo (el script `crear` lo sube solo si falta; ver también Errores frecuentes, `NoSuchKey`). | Solo si falta en S3 | | `ejemplo-lambda-titanic.py` | Ejemplo corto de ETL de S3 a S3 (lee el CSV y guarda un resumen en JSON). **No lo despliega ningún script.** Sirve para entender el patrón Extract-Transform-Load. | Solo lectura / demostración (ver más abajo) | | `README.md` | Este documento. | Siempre | **Por qué el código de la Lambda va dentro del script y `ejemplo-lambda-titanic.py` no.** El script **sí despliega** el código del ETL, así que va embebido (al final del `.sh`, entre las marcas `# >>>>> INICIO ARCHIVO EMBEBIDO: lambda-etl-titanic.py` y `# <<<<< FIN ARCHIVO EMBEBIDO`, igual que `requirements.txt`): un único archivo que no se puede desincronizar de su código. En cambio `ejemplo-lambda-titanic.py` es un ejemplo **para leer** que no despliega ningún script, y `titanic.csv` son datos: los dos siguen siendo ficheros aparte. **Quiero leer o editar el código de la Lambda.** Vuélcalo a ficheros con: ```bash ./gestionar-etl-titanic.sh extraer # escribe lambda-etl-titanic.py y requirements.txt en la carpeta actual USAR_LOCAL=1 ./gestionar-etl-titanic.sh crear # despliega TU copia editada en lugar de la embebida ``` Si en la carpeta ya existe un fichero distinto con ese nombre, `extraer` no lo sobrescribe (añade `--si` para forzarlo). ## Qué hace el script `crear`, en orden y sin pedirte datos Comprueba `aws` y tus credenciales; lee de las salidas del stack `cunef-etl` el nombre de la función y comprueba que existe; comprueba que `titanic.csv` está en el bucket (si falta y lo tienes en la carpeta, lo sube); escribe el código y `requirements.txt` en una **carpeta temporal**; comprueba que el código compila; instala `pymysql==1.1.0` en esa carpeta con `pip` (reintenta si falla la red); copia el código como `lambda_function.py`; lo comprime en un zip (con `zip` y, si no hay, con Python); sube el zip con `update-function-code` (esperando si la función está ocupada); fija el *handler* a `lambda_function.lambda_handler` si hace falta (el código de marcador del Paso 0 usa otro) y muestra la configuración. Al terminar **borra la carpeta temporal**: ya no se queda ningún `lambda_function.zip` ni `lambda_package/` en tu carpeta. Se puede repetir sin problema. ## Antes de empezar - **Depende de [Tema 5 · 1 · Infraestructura base](../05.01-infraestructurabase/README.md)**: el stack `cunef-etl` debe estar en `CREATE_COMPLETE` (bucket con `titanic/raw/titanic.csv`, RDS, Lambda de marcador). Sin él, el script se detiene con `No existe el stack cunef-etl` y te dice qué hacer. - **La RDS del Paso 0 debe estar en marcha** (`available`). El Paso 0 la **apaga a las 4 horas** (la para, no la borra): si ya pasó ese tiempo, `estado`, `crear` y `probar` te dicen que está parada y qué hacer. Para arrancarla (unos 7 minutos, renueva otras 4 horas), en la carpeta 05.01: ```bash cd ../05.01-infraestructurabase && ./gestionar-etl-base.sh crear ``` - Región **eu-north-1 (Estocolmo)** seleccionada en la consola (el script fuerza `eu-north-1` en todas sus llamadas). - CloudShell con `aws`, `python3` y `pip3` (en CloudShell vienen todos; `zip` es opcional: si no está, se usa Python). `jq` ya no hace falta. ## Cómo llevar los ficheros a CloudShell 1. En tu PC, comprime **esta carpeta entera** (`05.02-etltitanic`) con clic derecho → *Comprimir en archivo ZIP* (Windows 11), *Enviar a > Carpeta comprimida en zip* (Windows 10) o *Comprimir* (Mac). 2. En CloudShell (región eu-north-1): **Actions → Upload file** y elige `05.02-etltitanic.zip`. 3. Ejecuta: ```bash cd ~ unzip -o 05.02-etltitanic.zip && cd 05.02-etltitanic chmod -R u+rwX . && chmod +x *.sh ls ``` > Si ya subiste `g214-materiales.zip` (README principal), no necesitas este zip: entra con `cd ~/g214-materiales/tema5/05.02-etltitanic` y, si algún fichero da `Permission denied`, ejecuta allí `chmod -R u+rwX . && chmod +x *.sh`. **Qué verás:** los ficheros de la tabla de arriba: `gestionar-etl-titanic.sh`, `titanic.csv`, `ejemplo-lambda-titanic.py` y `README.md`. Lanza el script **dentro de esta carpeta** (así encuentra `titanic.csv` si hace falta subirlo). Los ficheros de CloudShell se conservan entre sesiones, pero **las variables de shell se pierden** al cerrar la pestaña o por inactividad. ## Comandos del script ```bash ./gestionar-etl-titanic.sh # menú numérico (solo si hay un terminal) ./gestionar-etl-titanic.sh crear # empaqueta y despliega (o actualiza) el ETL en la Lambda ./gestionar-etl-titanic.sh estado # existencia, configuración, estado de la RDS, última ejecución y logs (solo lectura) ./gestionar-etl-titanic.sh probar # invoca la Lambda y muestra el resultado (no invoca si la RDS está parada) ./gestionar-etl-titanic.sh eliminar # devuelve la Lambda al código de marcador y borra sus logs (pide confirmación; --si o -y no pregunta) ./gestionar-etl-titanic.sh extraer # escribe lambda-etl-titanic.py y requirements.txt en la carpeta actual ./gestionar-etl-titanic.sh ayuda # muestra el uso ``` Alias que también entiende: `create`, `desplegar`, `deploy`, `empaquetar`, `actualizar` (= crear); `status`, `info`, `ver`, `verificar`, `logs` (= estado); `invocar`, `invoke`, `test`, `ejecutar` (= probar); `borrar`, `delete`, `destroy`, `limpiar`, `revertir` (= eliminar); `extract` (= extraer); `help`, `-h`, `--help` (= ayuda). **Sin comando y en un terminal abre un menú** (`1) Crear`, `2) Estado`, `3) Probar`, `4) Eliminar`, `5) Extraer`, `0) Salir`); **sin comando y sin terminal imprime el uso y termina**. Atención: antes este script desplegaba directamente al lanzarlo sin nada; ahora hay que escribir `crear` (o elegir `1` en el menú). ## Paso a paso **0. Recupera las variables del Paso 0** (también si cerraste CloudShell): ```bash OUT() { aws cloudformation describe-stacks --stack-name cunef-etl --region eu-north-1 \ --query "Stacks[0].Outputs[?OutputKey=='$1'].OutputValue" --output text; } BUCKET=$(OUT BucketName); RDS=$(OUT RDSEndpoint); FN=$(OUT LambdaName); ADMINER=$(OUT AdminerURL) echo "Bucket : $BUCKET"; echo "RDS : $RDS"; echo "Lambda : $FN"; echo "Adminer: $ADMINER" ``` **Qué verás:** cuatro líneas con valores. Si alguna sale vacía, el stack `cunef-etl` no existe o no está en `CREATE_COMPLETE`: vuelve a la carpeta 05.01. Si pasaron más de 4 horas desde el Paso 0, **comprueba primero que la RDS sigue en marcha** (`./gestionar-etl-titanic.sh estado` lo dice; si está parada, arráncala como se explica en *Antes de empezar*). `ADMINER` guarda la URL del primer arranque: tras volver a arrancar la EC2, usa la URL que muestra `./gestionar-etl-base.sh estado` en la carpeta 05.01. **1. Despliega el código:** ```bash ./gestionar-etl-titanic.sh crear ``` **Qué verás (menos de 1 minuto):** "Comprobando el entorno" (AWS CLI y credenciales en verde), "Función Lambda encontrada: cunef-etl-ETLLambda-...", "titanic.csv está en s3://.../titanic/raw/titanic.csv", "Empaquetando la función Lambda ETL Titanic" con "Instalando dependencias de Python (pymysql==1.1.0 )...", "Copiando código Lambda (como lambda_function.py)...", "Creando archivo ZIP..." y "Paquete creado (NNNNN bytes)"; después "Desplegando código en Lambda...", "Código desplegado exitosamente en ...", "Ajustando el handler a lambda_function.lambda_handler (ahora es 'index.lambda_handler')..." (si ya era ese, dice "El handler ya es ..."), una tabla "Información de la Función Lambda" (Runtime `python3.12`, Handler, Timeout `900`, Memoria `512`, Estado normalmente `Active`), "¡Despliegue completado!" con el comando para probar y el de los logs, y por último "¡Proceso completado!". Si la función está ocupada con otra actualización, verás líneas "Esperando a que la función esté lista..." con un contador y el script espera solo (hasta 5 minutos). **2. Invoca la función:** ```bash ./gestionar-etl-titanic.sh probar ``` (o a mano, como antes:) ```bash aws lambda invoke --function-name "$FN" --region eu-north-1 output.json cat output.json ``` **Qué verás:** tarda unos segundos. Con `probar`: la línea `StatusCode / FunctionError: 200 / None` y la respuesta de la función; si falla, `200 / Unhandled` y el aviso "La función ha FALLADO". Con el comando manual: `"StatusCode": 200` y `"ExecutedVersion": "$LATEST"`, y **si además aparece `"FunctionError": "Unhandled"`, la función ha fallado**: mira la respuesta y los logs (paso 3). Si fue bien, la respuesta es un JSON con `"statusCode": 200` y un `body` que incluye el mensaje `ETL completado exitosamente`, `records_processed` con el número de filas cargadas, `database` `titanic` y `table` `passengers`. **3. Mira los logs** (la herramienta que más te va a servir para depurar): ```bash ./gestionar-etl-titanic.sh estado aws logs tail /aws/lambda/$FN --since 10m --region eu-north-1 ``` **Qué verás:** `estado` enseña la configuración de la función (si tiene el código del ETL o aún el marcador), si `titanic.csv` está en S3, las últimas líneas de la última ejecución y un veredicto (`La última ejecución terminó bien` o `parece haber FALLADO` con pistas). Con `aws logs tail` ves los mensajes completos de la función, en orden: "Paso 1: Descargando archivo desde S3...", "Paso 2: Procesando y limpiando datos...", "Paso 3: Conectando a la base de datos...", "Paso 4: Verificando/creando base de datos y tabla...", "Paso 5: Insertando datos en la tabla...", varias líneas "Insertados N/891 registros" (de 100 en 100), "Total de registros en la tabla: ..." y "ETL completado exitosamente". Añade `--follow` en lugar de `--since 10m` para verlos en directo mientras invocas desde otra pestaña. **Si justo después de invocar solo ves el arranque anterior (o nada), espera 5-10 segundos y repite el comando:** los logs tardan unos segundos en aparecer en CloudWatch. **4. Comprueba el resultado en Adminer.** Abre la URL de `$ADMINER` (`http://`, raíz) e inicia sesión como se explica en la carpeta 05.01: Sistema `MySQL` (o `MySQL / MariaDB`), Servidor `$RDS` (sin `http://`), usuario `admin`, contraseña `CunefAdmin!`; puedes dejar la base de datos vacía. En la columna izquierda elige la base `titanic` y pulsa **SQL command**. Ejecuta: ```sql SELECT COUNT(*) FROM passengers; ``` **Qué verás:** `891`, tantas filas como pasajeros tiene `titanic.csv`. Si pulsas en la tabla `passengers` y luego en **Select data**, verás las filas con las columnas `PassengerId, Survived, Pclass, LastName, FirstName, Sex, Age, SibSp, Parch, Ticket, Fare, Cabin, Embarked, created_at`. Comprobaciones de referencia (en el mismo **SQL command**): ```sql SELECT Sex, COUNT(*) AS pasajeros, SUM(Survived) AS supervivientes, ROUND(100 * AVG(Survived), 2) AS tasa_pct FROM passengers GROUP BY Sex ORDER BY Sex; SELECT Pclass, COUNT(*) AS pasajeros, SUM(Survived) AS supervivientes, ROUND(100 * AVG(Survived), 2) AS tasa_pct FROM passengers GROUP BY Pclass ORDER BY Pclass; SELECT COUNT(*) FROM passengers WHERE Cabin = 'C00'; SELECT COUNT(*) FROM passengers WHERE Age = 29.70; ``` **Qué verás:** `female` 314 / 233 / 74.20; `male` 577 / 109 / 18.89; clase 1: 216 / 136 / 62.96, clase 2: 184 / 87 / 47.28, clase 3: 491 / 119 / 24.24; `Cabin = 'C00'`: 687 filas; `Age = 29.70`: 177 filas (el redondeo puede salir como `74.2` según la versión). **5. Sigue las diapositivas del Ejercicio 1.** Puedes invocar la función cuantas veces quieras. Lo que pide la presentación (borrar pasajeros en Adminer y volver a ejecutar la Lambda) es el experimento que debes hacer y razonar tú: observa qué pasa con las filas antes y después. **6. (Si te lo piden) Informe de Power BI**. Usa como servidor el endpoint de la RDS, como base `titanic`, como tabla `passengers`, usuario `admin` y contraseña `CunefAdmin!`; la RDS acepta conexiones desde cualquier IP (puerto 3306 abierto), así que no hay que configurar red. **Aviso:** el conector *MySQL database* del servicio web de Power BI (powerbi.com) no se ha podido verificar con una cuenta de CUNEF y puede no aparecer o pedir una *puerta de enlace* (gateway). **Plan B:** en Adminer ejecuta `SELECT * FROM titanic.passengers`, pulsa **Export** (formato CSV) y carga ese CSV en Power BI (o usa Power BI Desktop, que necesita instalar antes *MySQL Connector/NET*). Para la tasa de supervivencia crea una medida DAX: `Tasa supervivencia = AVERAGE(passengers[Survived])` (si `Survived` llega como número 0/1) o `DIVIDE(CALCULATE(COUNTROWS(passengers), passengers[Survived] = TRUE()), COUNTROWS(passengers))` (si llega como verdadero/falso), con formato porcentaje. Valores de control: mujeres 74,20 %, hombres 18,89 %. Los datos solo cambian en Power BI si cambia la tabla: borra 10 pasajeros en Adminer, refresca (881), vuelve a ejecutar la Lambda y refresca (891). **Este ejercicio no requiere cambios en IAM.** El rol que creó el Paso 0 ya permite escribir logs y leer del bucket, que es todo lo que necesita esta función. La conexión a la base de datos va con usuario y contraseña, no con permisos de IAM. Tampoco hay que tocar la red: la función **no está dentro de una VPC**, sale a Internet y llega a la RDS por su endpoint público; que la RDS acepte el puerto 3306 lo decide el grupo de seguridad de la RDS, no el rol de la función. ### Sobre `ejemplo-lambda-titanic.py` (solo lectura) Es un ejemplo de referencia, más corto, de ETL de S3 a S3: lee `raw/titanic.csv`, calcula el número de pasajeros, la edad media y la tarifa media, y guarda un resumen en `curated/titanic_summary.json`. **No forma parte del Ejercicio 1 ni lo despliega ningún script.** Lleva un nombre de bucket de ejemplo (`cunef-etl-demo-tu-nombre`) y rutas propias (`raw/...`, `curated/...`) distintas de las del Paso 0 (`titanic/raw/titanic.csv`), y el rol del Paso 0 no puede escribir en S3. Úsalo para leer el patrón Extract-Transform-Load en pocas líneas. Si lo pruebas (demostración), cambia `BUCKET` por **tu propio bucket** (con `raw/titanic.csv`) y usa una función con un **rol propio** que pueda leer y escribir en él (la demo le adjunta `AmazonS3FullAccess`); el fichero lo avisa en su cabecera. ## Errores frecuentes - **`Permission denied` al ejecutar `./gestionar-etl-titanic.sh`.** Solución: `chmod +x *.sh` (o `bash gestionar-etl-titanic.sh crear`). - **Lanzo el script sin nada y solo me enseña el uso.** No hay terminal (o lo lanzaste desde otro programa): escribe el comando, por ejemplo `./gestionar-etl-titanic.sh crear`. - **El script dice `No existe el stack cunef-etl (la infraestructura base del Paso 0)`.** El stack `cunef-etl` no existe en esta cuenta (no hiciste el Paso 0, o ya lo borraste). Vuelve a la carpeta 05.01 y ejecuta `./gestionar-etl-base.sh crear` (o `estado` para ver qué hay). Si dice que el stack está ocupado o fallido, el mensaje te indica qué hacer. - **`La función ... no existe (alguien la borró fuera del stack)`.** Alguien borró la Lambda a mano. Recréala volviendo a la carpeta 05.01: `./gestionar-etl-base.sh crear` (borra el stack estropeado y lo recrea). - **Al invocar la Lambda recién creada (Paso 0), `"FunctionError": "Unhandled"` y `Runtime.ImportModuleError: Unable to import module 'lambda_function'`.** Tu copia de la plantilla es de una versión anterior: el código de marcador se guarda como `index.py` y el *handler* apuntaba a otro nombre. No es un fallo tuyo: este ejercicio lo arregla (el script ajusta el handler). - **`No se pudieron instalar las dependencias de Python` / `No encuentro pip`.** `pip` ha fallado (sin red o sin `pip`). El script reintenta 3 veces solo; si sigue fallando, comprueba `pip3 --version` y que estás en CloudShell, y repite `crear`. - **`No he podido crear el ZIP`.** No hay `zip` ni `python3`. En CloudShell vienen los dos: usa CloudShell. - **`El código de la Lambda tiene un error de sintaxis (¿editaste tu copia?)`.** Solo ocurre si usas `USAR_LOCAL=1` con tu copia editada de `lambda-etl-titanic.py`: corrige el error que indica o borra tu copia y usa la embebida (sin `USAR_LOCAL`). - **`aws lambda invoke` dice que `FunctionName` es inválido.** `$FN` está vacío. Repite el paso 0. (O usa `./gestionar-etl-titanic.sh probar`, que no necesita variables.) - **`"FunctionError": "Unhandled"`**: mira el final de la respuesta y los logs (`./gestionar-etl-titanic.sh estado` o `aws logs tail /aws/lambda/$FN --since 10m --region eu-north-1`). Los errores típicos: - `Runtime.ImportModuleError: Unable to import module 'lambda_function'` o `No module named 'pymysql'`: el paquete no lleva el fichero o la librería. Vuelve a lanzar `./gestionar-etl-titanic.sh crear`. - `Runtime.HandlerNotFound: Handler 'xxx' missing on module 'lambda_function'`: el fichero existe pero la función que nombra el *handler* no. Lo corrige `./gestionar-etl-titanic.sh crear`, o a mano en *Configuration → Runtime settings → Handler* o con `aws lambda update-function-configuration --function-name "$FN" --handler lambda_function.lambda_handler --region eu-north-1`. - `NoSuchKey`: `titanic.csv` no está en `s3://$BUCKET/titanic/raw/titanic.csv`. `./gestionar-etl-titanic.sh crear` lo sube si lo tienes en la carpeta; o a mano: `aws s3 ls s3://$BUCKET/ --recursive` y, si falta, `aws s3 cp titanic.csv s3://$BUCKET/titanic/raw/titanic.csv --region eu-north-1`. - `AccessDenied` al leer de S3: no debería ocurrir, porque el rol del Paso 0 ya lee ese bucket. Comprueba que `$BUCKET` es el del stack `cunef-etl` y que no has cambiado el rol de la función. - `OperationalError (2003, "Can't connect to MySQL server ... (timed out)")`: la RDS no es alcanzable. Lo más probable es que el **apagado automático la haya parado** (`./gestionar-etl-titanic.sh estado` te lo dice; arráncala con `./gestionar-etl-base.sh crear` en la carpeta 05.01). Otras causas: el stack no terminó, o cambiaste el grupo de seguridad del puerto 3306. La función **no** espera los 900 s: el código conecta con `connect_timeout=10` y falla a los 10 segundos aproximadamente. - `OperationalError (1045, "Access denied for user 'admin'...")`: la contraseña de la función no es la de la RDS. Deben coincidir con las del stack (`CunefAdmin!`). - **Adminer no carga o no deja entrar:** ver "Errores frecuentes" de la carpeta 05.01. ## Limpieza Esta actividad **no crea recursos propios en AWS**: la función, el rol, el bucket y la base `titanic` (con la tabla `passengers`) pertenecen al stack `cunef-etl` y se borran con él (carpeta 05.01, al final del Tema 5). Por eso `eliminar` aquí **no borra la función ni toca la infraestructura base**. Lo que hace es **deshacer el ejercicio**: ```bash ./gestionar-etl-titanic.sh eliminar ``` Te pide confirmación (`¿Continuar? [s/N]`; con `--si` o `-y` no pregunta) y entonces: (1) devuelve la Lambda al **código de marcador** del Paso 0 (y su handler `index.lambda_handler`), (2) borra el grupo de logs `/aws/lambda/` y (3) borra los restos locales `lambda_function.zip` / `lambda_package/` de versiones antiguas del script, si existen. Después **verifica** y lo cuenta. Es idempotente: si no hay nada que deshacer, lo dice y termina bien. **No toca la base de datos**: la base `titanic` y la tabla `passengers` siguen en la RDS (se borran con la RDS en la carpeta 05.01); si solo quieres vaciar los datos, en Adminer ejecuta `DROP DATABASE titanic;`. Para volver a dejar el ETL desplegado: `./gestionar-etl-titanic.sh crear`. Lo opcional: 1. Si probaste `ejemplo-lambda-titanic.py` (demostración) con recursos propios, borra solo lo que hayas creado tú: - La función de la demo (por ejemplo `lambda-etl-demo`): `aws lambda delete-function --function-name NOMBRE --region eu-north-1`. Después su rol (la consola le pone un nombre como `lambda-etl-demo-role-xxxxxxxx`): `aws iam list-attached-role-policies --role-name ROL`, `aws iam detach-role-policy --role-name ROL --policy-arn ARN_DE_CADA_POLITICA`, `aws iam delete-role-policy --role-name ROL --policy-name NOMBRE` (si tiene políticas en línea) y `aws iam delete-role --role-name ROL`. - La regla de EventBridge de la demo (la tiene un nombre aleatorio), si la creaste: ver la carpeta 05.06, o **EventBridge → Rules → Delete**. - El bucket de la demo (`cunef-etl-demo-`): `aws s3 rb s3://NOMBRE_DEL_BUCKET --force --region eu-north-1`. Comprueba antes con `aws s3 ls | grep cunef-etl-demo` que es tuyo. 2. Opcional: borra el grupo de logs de tu función de demo (`/aws/lambda/lambda-etl-demo`, si lo creaste) con `aws logs delete-log-group --log-group-name /aws/lambda/NOMBRE --region eu-north-1`. El grupo de logs de la Lambda del Paso 0 (`/aws/lambda/cunef-etl-ETLLambda-...`) lo borra `eliminar` (aquí y en la carpeta 05.01). 3. Ficheros de CloudShell: `cd ~ && rm -rf 05.02-etltitanic 05.02-etltitanic.zip`. **No borres la infraestructura base todavía** (carpeta 05.01): la necesitan las actividades siguientes. Su borrado debe ser el último paso de todo el Tema 5. Mientras tanto, recuerda que **se apaga sola a las 4 horas** (con `./gestionar-etl-base.sh extender` la amplías); la RDS parada conserva la base `titanic` y sus datos. ## Cómo sabes que has terminado - [ ] La invocación devolvió `StatusCode` 200 y `statusCode` 200 en la respuesta (`probar` o `output.json`). - [ ] En Adminer, `SELECT COUNT(*) FROM passengers;` da 891 y las comprobaciones de referencia coinciden. - [ ] Hiciste el experimento de la presentación (borrar pasajeros y volver a ejecutar) y sabes explicar lo que observaste. - [ ] (Si te lo pidieron) Tienes el informe de Power BI con la tasa de supervivencia (mujeres 74,20 %, hombres 18,89 %). --- Anterior: [Tema 5 · 1 · Infraestructura base](../05.01-infraestructurabase/README.md) · Siguiente: [Tema 5 · 3 · CoinGecko a S3](../05.03-cryptoas3/README.md)