

# Creación de una aplicación de procesamiento de archivos sin servidor
<a name="file-processing-app"></a>

Uno de los casos de uso más comunes de Lambda es llevar a cabo tareas de procesamiento de archivos. Por ejemplo, puede utilizar una función de Lambda para crear archivos PDF de forma automática a partir de archivos HTML o imágenes, o para crear miniaturas cuando un usuario carga una imagen.

En este ejemplo, crea una aplicación que cifra los archivos PDF de forma automática cuando se cargan en un bucket de Amazon Simple Storage Service (Amazon S3). Para implementar esta aplicación, necesita crear los siguientes recursos:
+ Un bucket de S3 para que los usuarios puedan cargar los archivos PDF
+ Una función de Lambda en Python que lea el archivo cargado y cree una versión cifrada y protegida por contraseña
+ Un segundo bucket de S3 en el que Lambda pueda guardar el archivo cifrado

También debe crear una política de AWS Identity and Access Management (IAM) para conceder permiso a la función de Lambda para llevar a cabo operaciones de lectura y escritura en los buckets de S3.

![\[\]](http://docs.aws.amazon.com/es_es/lambda/latest/dg/images/ExampleApps/file_process_resources.png)


**sugerencia**  
Si es la primera vez que utiliza Lambda, recomendamos que comience por el tutorial de [Creación de su primera función de Lambda](getting-started.md) antes de crear esta aplicación de ejemplo.

Para implementar la aplicación de forma manual, cree y configure los recursos mediante la Consola de administración de AWS o con la AWS Command Line Interface (AWS CLI). También puede implementar la aplicación mediante AWS Serverless Application Model (AWS SAM). AWS SAM es una herramienta de Infraestructura como código (IaC). Con la IaC no se crean recursos de forma manual, sino que se definen en código y, a continuación, se implementan automáticamente.

Si desea obtener más información sobre el uso de Lambda con la IaC antes de implementar esta aplicación de ejemplo, consulte [Uso de Lambda con la infraestructura como código (IaC)](foundation-iac.md).

## Crear los archivos de código fuente de la función de Lambda
<a name="file-processing-app-download"></a>

Cree los siguientes archivos en el directorio del proyecto:
+ `lambda_function.py`: es el código de la función Python para la función de Lambda que lleva a cabo el cifrado de archivos
+ `requirements.txt`: es un archivo de manifiesto que define las dependencias que requiere el código de la función de Python

Expanda las siguientes secciones para ver el código y obtener más información sobre la función de cada archivo. Para crear los archivos en la máquina local, copie y pegue el siguiente código o descargue los archivos del [repositorio de GitHub aws-lambda-developer-guide](https://github.com/awsdocs/aws-lambda-developer-guide/tree/main/sample-apps/file-processing-python).

### Código de la función de Python
<a name="file-processing-app-function-code"></a>

Copie y pegue el siguiente código en un archivo denominado `lambda_function.py`.

```
from pypdf import PdfReader, PdfWriter
import uuid
import os
from urllib.parse import unquote_plus
import boto3

# Create the S3 client to download and upload objects from S3
s3_client = boto3.client('s3')

def lambda_handler(event, context):
    # Iterate over the S3 event object and get the key for all uploaded files
    for record in event['Records']:
        bucket = record['s3']['bucket']['name']
        key = unquote_plus(record['s3']['object']['key']) # Decode the S3 object key to remove any URL-encoded characters
        download_path = f'/tmp/{uuid.uuid4()}.pdf' # Create a path in the Lambda tmp directory to save the file to 
        upload_path = f'/tmp/converted-{uuid.uuid4()}.pdf' # Create another path to save the encrypted file to
        
        # If the file is a PDF, encrypt it and upload it to the destination S3 bucket
        if key.lower().endswith('.pdf'):
            s3_client.download_file(bucket, key, download_path)
            encrypt_pdf(download_path, upload_path)
            encrypted_key = add_encrypted_suffix(key)
            s3_client.upload_file(upload_path, f'{bucket}-encrypted', encrypted_key)

# Define the function to encrypt the PDF file with a password
def encrypt_pdf(file_path, encrypted_file_path):
    reader = PdfReader(file_path)
    writer = PdfWriter()
    
    for page in reader.pages:
        writer.add_page(page)

    # Add a password to the new PDF
    writer.encrypt("my-secret-password")

    # Save the new PDF to a file
    with open(encrypted_file_path, "wb") as file:
        writer.write(file)

# Define a function to add a suffix to the original filename after encryption
def add_encrypted_suffix(original_key):
    filename, extension = original_key.rsplit('.', 1)
    return f'{filename}_encrypted.{extension}'
```

**nota**  
En este código de ejemplo, la contraseña del archivo cifrado (`my-secret-password`) está codificada en el código de la función. En aplicaciones de producción, no incluya información confidencial como contraseñas en el código de la función. En su lugar, [cree un secreto de AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html) y, a continuación, [utilice la extensión de Lambda AWS Parameters and Secrets](with-secrets-manager.md) para recuperar las credenciales en la función de Lambda.

El código de la función de Python contiene tres funciones: la [función de controlador](python-handler.md) que Lambda ejecuta cuando se invoca la función y dos funciones independientes denominadas `add_encrypted_suffix` y `encrypt_pdf` a las que el controlador llama para que lleve a cabo el cifrado del PDF.

Cuando Amazon S3 invoca la función, Lambda pasa un argumento de *evento* con formato JSON a la función que contiene detalles sobre el evento que provocó la invocación. En este caso, la información incluye el nombre del bucket de S3 y las claves de objeto de los archivos cargados. Para obtener más información sobre el formato del objeto de evento de Amazon S3, consulte [Procese las notificaciones de eventos de Amazon S3 con Lambda.](with-s3.md).

A continuación, la función utiliza AWS SDK para Python (Boto3) para descargar los archivos PDF especificados en el objeto de evento a su directorio de almacenamiento temporal local, antes de cifrarlos mediante la biblioteca [https://pypi.org/project/pypdf/](https://pypi.org/project/pypdf/).

Por último, la función utiliza el SDK de Boto3 para almacenar el archivo cifrado en el bucket de destino de S3.

### archivo de manifiesto `requirements.txt`
<a name="file-processing-app-dependencies"></a>

Copie y pegue el siguiente código en un archivo denominado `requirements.txt`.

```
boto3
pypdf
```

En este ejemplo, el código de la función solo tiene dos dependencias que no forman parte de la biblioteca estándar de Python: el SDK para Python (Boto3) y el paquete `pypdf` que la función utiliza para cifrar el PDF.

**nota**  
Como parte del tiempo de ejecución de Lambda, se incluye una versión del SDK para Python (Boto3), de modo que el código se ejecute sin agregar Boto3 al paquete de implementación de la función. Sin embargo, para mantener el control total de las dependencias de la función y evitar posibles problemas de alineación de versiones, la práctica recomendada para Python es incluir todas las dependencias de la función en el paquete de implementación de la función. Consulte [Dependencias de tiempo de ejecución en Python](python-package.md#python-package-dependencies) para obtener más información.

## Implementar la aplicación
<a name="file-processing-app-deploy"></a>

Puede crear e implementar los recursos para esta aplicación de ejemplo de forma manual o mediante AWS SAM. En un entorno de producción, recomendamos que utilice una herramienta de IaC como AWS SAM para implementar aplicaciones sin servidor de forma rápida y repetible sin utilizar procesos manuales.

### Implementación de los recursos de forma manual
<a name="file-processing-app-deploy-manual"></a>

Para implementar la aplicación manualmente:
+ Cree buckets de Amazon S3 de origen y de destino
+ Cree una función de Lambda que cifre un archivo PDF y guarde la versión cifrada de este en un bucket de S3
+ Configure un desencadenador de Lambda que invoque la función cuando se carguen objetos en el bucket de origen

Antes de comenzar, asegúrese de que [Python](https://www.python.org/downloads/) esté instalado en el equipo de compilación.

#### Cree dos buckets de S3
<a name="file-processing-app-deploy-manual-create-buckets"></a>

Primero, cree dos buckets de S3. El primer bucket es el bucket de origen al que subirá los archivos PDF. Lambda utiliza el segundo bucket para guardar el archivo cifrado cuando se invoca la función.

------
#### [ Console ]

**Para crear buckets de S3 (consola)**

1. En la consola de Amazon S3, abra la página [Buckets de uso general](https://console.aws.amazon.com/s3/buckets).

1. Seleccione la Región de AWS más cercana a su ubicación geográfica. Puede cambiar la región por medio de la lista desplegable de la parte superior de la pantalla.  
![\[\]](http://docs.aws.amazon.com/es_es/lambda/latest/dg/images/console_region_select.png)

1. Elija **Crear bucket**.

1. En **Configuración general**, haga lo siguiente:

   1. En **Tipo de bucket**, asegúrese de que **Uso general** está seleccionado.

   1. Para el **Nombre del bucket**, ingrese un nombre único a nivel mundial que cumpla las [reglas de nomenclatura de bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html) de Amazon S3. Los nombres de bucket pueden contener únicamente letras minúsculas, números, puntos (.) y guiones (-).

1. Deje el resto de las opciones con sus valores predeterminados y seleccione **Crear bucket**.

1. Repita los pasos 1 a 4 para crear el bucket de destino. En **Nombre del bucket**, introduzca `amzn-s3-demo-bucket-encrypted`, donde `amzn-s3-demo-bucket` es el nombre del bucket de origen que acaba de crear.

------
#### [ AWS CLI ]

Antes de comenzar, asegúrese de que la [AWS CLI esté instalada](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) en el equipo de compilación.

**Para crear buckets de Amazon S3 (AWS CLI)**

1. Ejecute el siguiente comando de la CLI para crear el bucket de origen. El nombre que elija para el bucket debe ser globalmente único y seguir las [reglas de nomenclatura de buckets](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html) de Amazon S3. Los nombres pueden contener únicamente letras minúsculas, números, puntos (.) y guiones (-). Para `region` y `LocationConstraint`, elija la [Región de AWS](https://docs.aws.amazon.com/general/latest/gr/lambda-service.html) más cercana a su ubicación geográfica.

   ```
   aws s3api create-bucket --bucket amzn-s3-demo-bucket --region us-east-2 \
   --create-bucket-configuration LocationConstraint=us-east-2
   ```

   Más adelante en el tutorial, debe crear la función de Lambda en la misma Región de AWS que la del bucket de origen, por lo que debe anotar la región que eligió.

1. Ejecute el siguiente comando para crear el bucket de destino. Para el nombre del bucket, debe utilizar `amzn-s3-demo-bucket-encrypted`, donde `amzn-s3-demo-bucket` es el nombre del bucket de origen que creó en el paso 1. Para `region` y `LocationConstraint`, elija la misma Región de AWS que usó para crear el bucket de origen.

   ```
   aws s3api create-bucket --bucket amzn-s3-demo-bucket-encrypted --region us-east-2 \
   --create-bucket-configuration LocationConstraint=us-east-2
   ```

------

#### Creación de un rol de ejecución
<a name="file-processing-app-deploy-manual-create-execution-role"></a>

Un rol de ejecución es un rol de IAM que concede a la función de Lambda permiso para acceder a servicios y recursos de Servicios de AWS. Para conceder acceso de lectura y escritura a Amazon S3 a la función, debe adjuntar la [política administrada de AWS](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html#aws-managed-policies) `AmazonS3FullAccess`.

------
#### [ Console ]

**Para crear un rol de ejecución y adjuntar la política administrada `AmazonS3FullAccess` (consola)**

1. Abra la [página Roles](https://console.aws.amazon.com/iam/home/roles) en la consola de IAM.

1. Elija **Crear rol**.

1. En **Tipo de entidad de confianza**, seleccione **Servicio de AWS** y, en **Caso de uso**, seleccione **Lambda**.

1. Elija **Siguiente**.

1. Agregue la política administrada `AmazonS3FullAccess` de la siguiente manera:

   1. En **Políticas de permisos**, ingrese **AmazonS3FullAccess** en la barra de búsqueda.

   1. Seleccione la casilla situada junto a la política administrada.

   1. Elija **Siguiente**.

1. En **Detalles del rol**, para **Nombre de rol**, escriba **LambdaS3Role**.

1. Seleccione **Creación de rol**.

------
#### [ AWS CLI ]

**Crear un rol de ejecución y adjuntar la política administrada `AmazonS3FullAccess` (AWS CLI)**

1. Guarde el siguiente JSON en un archivo llamado `trust-policy.json`. Esta política de confianza permite a Lambda utilizar los permisos del rol al dar permiso al servicio principal `lambda.amazonaws.com` para llamar a la acción de AWS Security Token Service (AWS STS) `AssumeRole`.  
****  

   ```
   {
     "Version":"2012-10-17",		 	 	 
     "Statement": [
       {
         "Effect": "Allow",
         "Principal": {
           "Service": "lambda.amazonaws.com"
         },
         "Action": "sts:AssumeRole"
       }
     ]
   }
   ```

1. Desde el directorio en el que guardó el documento de política de confianza JSON, ejecute el siguiente comando de la CLI para crear el rol de ejecución.

   ```
   aws iam create-role --role-name LambdaS3Role --assume-role-policy-document file://trust-policy.json
   ```

1. Para adjuntar la política administrada `AmazonS3FullAccess`, ejecute el siguiente comando de la CLI:

   ```
   aws iam attach-role-policy --role-name LambdaS3Role --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess
   ```

------

#### Crear el paquete de despliegue de la función
<a name="file-processing-app-deploy-manual-create-function-package"></a>

Para crear una función, debe crear un *paquete de despliegue* que contenga el código y las dependencias de la función. Para esta aplicación, el código de la función utiliza una biblioteca independiente para el cifrado de PDF.

**Creación del paquete de implementación**

1. Navegue hasta el directorio del proyecto que contiene los archivos `lambda_function.py` y `requirements.txt` que creó o descargó de GitHub anteriormente y cree un nuevo directorio denominado `package`.

1. Instale las dependencias especificadas en el archivo `requirements.txt` del directorio `package` ejecutando el siguiente comando:

   ```
   pip install -r requirements.txt --target ./package/
   ```

1. Cree un archivo .zip que contenga el código de la aplicación y todas sus dependencias. En Linux o macOS, ejecute los siguientes comandos desde la interfaz de la línea de comandos.

   ```
   cd package
   zip -r ../lambda_function.zip .
   cd ..
   zip lambda_function.zip lambda_function.py
   ```

    En Windows, utilice la herramienta de compresión que prefiera para crear el archivo `lambda_function.zip`. Asegúrese de que el archivo `lambda_function.py` y las carpetas que contienen las dependencias estén en la raíz del archivo .zip.

También puede crear su paquete de implementación mediante un entorno virtual de Python. Consulte [Uso de archivos .zip para funciones de Lambda en Python](python-package.md)

#### Crear la función de Lambda
<a name="file-processing-app-deploy-manual-createfunction"></a>

Ahora use el paquete de implementación que creó en el paso anterior para implementar la función de Lambda.

------
#### [ Console ]

**Para crear la función (consola)**

Para crear una función de Lambda con la consola, primero debe crear una función básica que contenga el código “Hola, mundo”. A continuación, reemplace este código por su propio código de función mediante la carga del archivo .zip que creó en el paso anterior.

Para garantizar que no se agote el tiempo de espera de la función cuando cifra archivos PDF de gran tamaño, debe configurar los ajustes de memoria y tiempo de espera de la función. El formato de registro de la función también se establece como JSON. Es necesario configurar los registros con formato JSON cuando se utiliza el script de prueba proporcionado para que pueda leer el estado de invocación de la función desde los registros de CloudWatch y confirmar que la invocación se llevó a cabo de forma correcta.

1. Abra la página de [Funciones](https://console.aws.amazon.com/lambda/home#/functions) en la consola de Lambda.

1. Asegúrese de trabajar en la misma Región de AWS en la que creó el bucket de S3. Puede cambiar la región por medio de la lista desplegable de la parte superior de la pantalla.  
![\[\]](http://docs.aws.amazon.com/es_es/lambda/latest/dg/images/console_region_select.png)

1. Elija **Crear función**.

1. Elija **Crear desde cero**.

1. Bajo **Información básica**, haga lo siguiente:

   1. En **Nombre de la función**, introduzca `EncryptPDF`.

   1. En **Tiempo de ejecución**, seleccione **Python 3.12**.

   1. En **Arquitectura**, elija **x86\$164**.

1. Adjunte el rol de ejecución que creó en el paso anterior de la siguiente manera:

   1. Expanda la sección **Cambiar el rol de ejecución predeterminado**.

   1. Seleccione **Usar un rol existente**.

   1. En **Rol existente**, seleccione su rol (`LambdaS3Role`).

1. Seleccione **Creación de función**.

**Para cargar el código de la función (consola)**

1. En el panel **Código fuente**, elija **Cargar desde**.

1. Elija un **archivo .zip**.

1. Seleccione **Cargar**.

1. En el selector de archivos, seleccione un archivo .zip y luego elija **Abrir**.

1. Seleccione **Save**.

**Configuración de la memoria y el tiempo de espera de la función (consola)**

1. Seleccione la pestaña **Configuración** de la función.

1. En el panel **Configuración general**, seleccione **Editar**.

1. Establezca la **Memoria** en 256 MB y el **Tiempo de espera** en 15 segundos.

1. Seleccione **Save**.

**Configuración del formato de registro (consola)**

1. Seleccione la pestaña **Configuración** de la función.

1. Seleccione **Herramientas de supervisión y operaciones**.

1. En el panel **Configuración de registros**, seleccione **Editar**.

1. Para la **configuración de registro**, seleccione **JSON**.

1. Seleccione **Save**.

------
#### [ AWS CLI ]

**Para crear la función (AWS CLI)**
+ Ejecute el siguiente comando desde el directorio que contiene el archivo `lambda_function.zip`. Para el parámetro `region`, reemplace `us-east-2` por la región en la que creó los buckets de S3.

  ```
  aws lambda create-function --function-name EncryptPDF \
  --zip-file fileb://lambda_function.zip --handler lambda_function.lambda_handler \
  --runtime python3.12 --timeout 15 --memory-size 256 \
  --role arn:aws:iam::123456789012:role/LambdaS3Role --region us-east-2 \
  --logging-config LogFormat=JSON
  ```

------

#### Configuración de un desencadenador de Amazon S3 para invocar una función
<a name="file-processing-app-deploy-manual-configure-s3-trigger"></a>

Para que la función de Lambda se ejecute cuando carga un archivo al bucket de origen, debe configurar un desencadenador para la función. Puede configurar el desencadenador de Amazon S3 mediante la consola o la AWS CLI.

**importante**  
Este procedimiento configura el bucket de S3 para invocar su función cada vez que se crea un objeto en el bucket. Asegúrese de configurar esto solo en el bucket de origen. Si la función de Lambda crea objetos en el mismo bucket que la invoca, la función se puede [invocar de forma continua en un bucle](https://serverlessland.com/content/service/lambda/guides/aws-lambda-operator-guide/recursive-runaway). Esto puede provocar que se facturen cargos imprevistos en su Cuenta de AWS.

------
#### [ Console ]

**Para configurar el desencadenador de Amazon S3 (consola)**

1. Abra la [página Funciones](https://console.aws.amazon.com/lambda/home#/functions) de la consola de Lambda y elija la función (`EncryptPDF`).

1. Elija **Add trigger (Añadir disparador)**.

1. Seleccione **S3**.

1. En **Bucket**, seleccione el bucket de origen.

1. En **Tipos de eventos**, seleccione **Todos los eventos de creación de objetos**.

1. En **Recursive invocation** (Invocación recursiva), marque la casilla de verificación para confirmar que no se recomienda utilizar el mismo bucket de S3 para la entrada y la salida. Puede obtener más información sobre los patrones de invocación recursiva en Lambda en [Patrones recursivos que provocan funciones de Lambda descontroladas](https://serverlessland.com/content/service/lambda/guides/aws-lambda-operator-guide/recursive-runaway) en Serverless Land.

1. Elija **Agregar**.

   Al crear un desencadenador mediante la consola de Lambda, Lambda crea automáticamente una [política basada en recursos](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html) para conceder permiso al servicio que seleccione para invocar la función. 

------
#### [ AWS CLI ]

**Para configurar el desencadenador de Amazon S3 (AWS CLI)**

1. Agregue una [política basada en recursos](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html) a la función que permita que el bucket de origen de Amazon S3 invoque la función al agregar un archivo. Una instrucción de política basada en recursos concede permisos a otros Servicios de AWS para invocar la función. Para conceder permisos a Amazon S3 para invocar la función, ejecute el siguiente comando de la CLI. Asegúrese de reemplazar el parámetro `source-account` por su propio ID de Cuenta de AWS y de utilizar su propio nombre de bucket de origen.

   ```
   aws lambda add-permission --function-name EncryptPDF \
   --principal s3.amazonaws.com --statement-id s3invoke --action "lambda:InvokeFunction" \
   --source-arn arn:aws:s3:::amzn-s3-demo-bucket \
   --source-account 123456789012
   ```

   La política que defina con este comando permite a Amazon S3 invocar la función solo cuando se lleva a cabo una acción en el bucket de origen.
**nota**  
Si bien los nombres de los buckets de S3 son únicos a nivel mundial, cuando se utilizan políticas basadas en recursos, se recomienda aclarar que el bucket debe pertenecer a la cuenta. Esto sucede porque si elimina un bucket, es posible que otra Cuenta de AWS cree un bucket con el mismo Nombre de recurso de Amazon (ARN).

1. Guarde el siguiente JSON en un archivo llamado `notification.json`. Cuando se aplica al bucket de origen, este JSON configura el bucket para enviar una notificación a la función de Lambda cada vez que se agrega un objeto nuevo. Reemplace el número de Cuenta de AWS y la Región de AWS en el ARN de la función de Lambda por su número de cuenta y su región.

   ```
   {
   "LambdaFunctionConfigurations": [
       {
         "Id": "EncryptPDFEventConfiguration",
         "LambdaFunctionArn": "arn:aws:lambda:us-east-2:123456789012:function:EncryptPDF",
         "Events": [ "s3:ObjectCreated:Put" ]
       }
     ]
   }
   ```

1. Ejecute el siguiente comando de la CLI para aplicar la configuración de notificación del archivo JSON que creó al bucket de origen. Reemplace `amzn-s3-demo-bucket` por el nombre del bucket de origen.

   ```
   aws s3api put-bucket-notification-configuration --bucket amzn-s3-demo-bucket \
   --notification-configuration file://notification.json
   ```

   Para obtener más información sobre el comando `put-bucket-notification-configuration` y la opción `notification-configuration`, consulte [put-bucket-notification-configuration](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-bucket-notification-configuration.html) en la *Referencia de comandos de la CLI AWS*.

------

### Implementación de los recursos mediante AWS SAM
<a name="file-processing-app-deploy-sam"></a>

Antes de empezar, asegúrese de que [Docker](https://docs.docker.com/get-docker/) y [la última versión de AWS SAMCLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html) estén instalados en su máquina de compilación.

1. En el directorio del proyecto, copie y pegue el siguiente código en un nuevo archivo denominado `template.yaml`. Sustituya los nombres de los buckets del marcador de posición:
   + Para el bucket de origen, sustituya `amzn-s3-demo-bucket` por cualquier nombre que cumpla con las [reglas de nomenclatura del bucket de S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html).
   + Para el bucket de destino, reemplace `amzn-s3-demo-bucket-encrypted` por `<source-bucket-name>-encrypted` cuando `<source-bucket>` sea el nombre que escogió para el bucket de origen.

   ```
   AWSTemplateFormatVersion: '2010-09-09'
   Transform: AWS::Serverless-2016-10-31
   
   Resources:
     EncryptPDFFunction:
       Type: AWS::Serverless::Function
       Properties:
         FunctionName: EncryptPDF
         Architectures: [x86_64]
         CodeUri: ./
         Handler: lambda_function.lambda_handler
         Runtime: python3.12
         Timeout: 15
         MemorySize: 256
         LoggingConfig:
           LogFormat: JSON
         Policies:
           - AmazonS3FullAccess
         Events:
           S3Event:
             Type: S3
             Properties:
               Bucket: !Ref PDFSourceBucket
               Events: s3:ObjectCreated:*
   
     PDFSourceBucket:
       Type: AWS::S3::Bucket
       Properties:
         BucketName: amzn-s3-demo-bucket
   
     EncryptedPDFBucket:
       Type: AWS::S3::Bucket
       Properties:
         BucketName: amzn-s3-demo-bucket-encrypted
   ```

   La plantilla AWS SAM define los recursos que crea para la aplicación. En este ejemplo, la plantilla define una función de Lambda con el tipo `AWS::Serverless::Function` y dos buckets de S3 con el tipo `AWS::S3::Bucket`. Los nombres de los buckets especificados en la plantilla son marcadores de posición. Antes de implementar la aplicación mediante AWS SAM, debe editar la plantilla para cambiar el nombre de los buckets por nombres únicos a nivel mundial que cumplan con las [reglas de nomenclatura para los buckets de S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html). Este paso se explica con más detalle en [Implementación de los recursos mediante AWS SAM](#file-processing-app-deploy-sam).

   La definición del recurso de la función de Lambda configura un desencadenador para la función mediante la propiedad del evento `S3Event`. Este desencadenador hace que se invoque la función cada vez que se crea un objeto en el bucket de origen.

   La definición de la función también especifica una política de AWS Identity and Access Management (IAM) que se adjuntará al [rol de ejecución](lambda-intro-execution-role.md) de la función. La [política administrada de AWS](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html#aws-managed-policies) `AmazonS3FullAccess` proporciona a su función los permisos que necesita para leer y escribir objetos en Amazon S3.

1. Ejecute el comando a continuación en el directorio en el que guardó los archivos `template.yaml`, `lambda_function.py` y `requirements.txt`.

   ```
   sam build --use-container
   ```

   Este comando recopila los artefactos de compilación de la aplicación y los coloca en el formato y la ubicación adecuados para implementarlos. Con la opción `--use-container`, la función se crea dentro de un contenedor de Docker tipo Lambda. Lo usamos aquí para que no necesite tener Python 3.12 instalado en su máquina local para que la compilación funcione.

   Durante el proceso de compilación, AWS SAM busca el código de la función de Lambda en la ubicación que especificó con la propiedad `CodeUri` en la plantilla. En este caso, especificamos el directorio actual como la ubicación (`./`).

   Si hay un archivo `requirements.txt`, AWS SAM lo usa para recopilar las dependencias especificadas. De forma predeterminada, AWS SAM crea un paquete de implementación .zip con el código y las dependencias de la función. También puede optar por implementar la función como una imagen de contenedor mediante la propiedad [PackageType](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html#sam-function-packagetype).

1. Para implementar la aplicación y crear los recursos de Lambda y Amazon S3 especificados en la plantilla de AWS SAM, ejecute el siguiente comando:

   ```
   sam deploy --guided
   ```

   El uso de la marca `--guided` significa que AWS SAM le mostrará instrucciones para guiarlo a lo largo del proceso de implementación. Para esta implementación, acepte las opciones predeterminadas pulsando Intro.

Durante el proceso de implementación, AWS SAM crea los siguientes recursos en su Cuenta de AWS:
+ Una [pila](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-whatis-concepts.html#cfn-concepts-stacks) de CloudFormation llamada `sam-app`
+ Una función de Lambda con el nombre `EncryptPDF`
+ Dos buckets de S3 con los nombres que eligió cuando editó el archivo de plantilla `template.yaml` de AWS SAM
+ Un rol de ejecución de IAM para la función con el formato de nombre `sam-app-EncryptPDFFunctionRole-2qGaapHFWOQ8`

Cuando AWS SAM termine de crear los recursos, debería ver el siguiente mensaje:

```
Successfully created/updated stack - sam-app in us-east-2
```

## Probar la aplicación
<a name="file-processing-app-test"></a>

Para probar la aplicación, cargue un archivo PDF en el bucket de origen y confirme que Lambda crea una versión cifrada del archivo en el bucket de destino. En este ejemplo, puede probarlo de forma manual mediante la consola o la AWS CLI o usando el script de prueba proporcionado.

Para las aplicaciones de producción, puede utilizar métodos y técnicas de prueba tradicionales, como las pruebas unitarias, para confirmar el correcto funcionamiento del código de la función de Lambda. La mejor práctica también es implementar pruebas como las del script de prueba proporcionado, que llevan a cabo pruebas de integración con recursos reales basados en la nube. Las pruebas de integración en la nube confirman que la infraestructura se desplegó correctamente y que los eventos fluyen entre los distintos servicios según lo esperado. Para obtener más información, consulte [Cómo realizar pruebas de funciones y aplicaciones sin servidor](testing-guide.md).

### Prueba de la aplicación de forma manual
<a name="file-processing-app-test-manual"></a>

Para probar la función de forma manual, agregue un archivo PDF a su bucket de origen de Amazon S3. Cuando agrega el archivo al bucket de origen, la función de Lambda se debe invocar de forma automática y debe almacenar una versión cifrada del archivo en el bucket de destino.

------
#### [ Console ]

**Prueba de la aplicación cargando un archivo (consola)**

1. Para cargar un archivo PDF en el bucket de S3, haga lo siguiente:

   1. Abra la página [Buckets](https://console.aws.amazon.com/s3/buckets) de la consola de Amazon S3 y elija el bucket de origen.

   1. Seleccione **Cargar**.

   1. Elija **Agregar archivos** y utilice el selector de archivos para elegir el archivo PDF que desea cargar.

   1. Elija **Abrir** y, a continuación, **Cargar**.

1. Compruebe que Lambda haya guardado una versión cifrada del archivo PDF en el bucket de destino, de la siguiente manera:

   1. Vuelva a la página [Buckets](https://console.aws.amazon.com/s3/buckets) de la consola de Amazon S3 y elija el bucket de destino.

   1. En el panel **Objetos**, ahora debería ver un archivo con el formato de nombre `filename_encrypted.pdf` (en el que `filename.pdf` es el nombre del archivo que cargó en el bucket de origen). Para descargar el PDF cifrado, seleccione el archivo y luego elija **Descargar**.

   1. Confirme que puede abrir el archivo descargado con la contraseña con la que lo protegió la función de Lambda (`my-secret-password`).

------
#### [ AWS CLI ]

**Prueba de la aplicación cargando un archivo (AWS CLI)**

1. Desde el directorio que contiene el archivo PDF que desea cargar, ejecute el siguiente comando de la CLI: Reemplace el parámetro `--bucket` con el nombre del bucket de origen. Para los parámetros `--key` y `--body`, utilice el nombre de archivo del archivo de prueba.

   ```
   aws s3api put-object --bucket amzn-s3-demo-bucket --key test.pdf --body ./test.pdf
   ```

1. Compruebe que la función creó una versión cifrada del archivo y la guardó en el bucket de S3 de destino. Ejecute el siguiente comando de la CLI, para ello reemplace `amzn-s3-demo-bucket-encrypted` por el nombre del bucket de destino.

   ```
   aws s3api list-objects-v2 --bucket amzn-s3-demo-bucket-encrypted
   ```

   Si la función se ejecuta correctamente, verá un resultado similar al siguiente. El bucket de destino debe contener un archivo con el formato de nombre `<your_test_file>_encrypted.pdf`, en el que `<your_test_file>` es el nombre del archivo cargado.

   ```
   {
       "Contents": [
           {
               "Key": "test_encrypted.pdf",
               "LastModified": "2023-06-07T00:15:50+00:00",
               "ETag": "\"7781a43e765a8301713f533d70968a1e\"",
               "Size": 2763,
               "StorageClass": "STANDARD"
           }
       ]
   }
   ```

1. Para descargar el archivo que Lambda guardó en el bucket de destino, ejecute el siguiente comando de la CLI: Reemplace el parámetro `--bucket` con el nombre del bucket de destino. Para el parámetro `--key`, utilice el nombre de archivo `<your_test_file>_encrypted.pdf`, en el que `<your_test_file>` es el nombre del archivo de prueba que cargó.

   ```
   aws s3api get-object --bucket amzn-s3-demo-bucket-encrypted --key test_encrypted.pdf my_encrypted_file.pdf
   ```

   Este comando descarga el archivo en el directorio actual y lo guarda como `my_encrypted_file.pdf`.

1. Confirme que puede abrir el archivo descargado con la contraseña con la que lo protegió la función de Lambda (`my-secret-password`).

------

### Prueba de la aplicación con el script automatizado
<a name="file-processing-app-test-auto"></a>

Cree los siguientes archivos en el directorio del proyecto:
+ `test_pdf_encrypt.py`: es un script de prueba que puede usar para probar la aplicación de forma automática
+ `pytest.ini`: es un archivo de configuración para el script de prueba

Expanda las siguientes secciones para ver el código y obtener más información sobre la función de cada archivo.

#### Script de prueba automatizado
<a name="file-processing-app-test-script"></a>

Copie y pegue el siguiente código en un archivo denominado `test_pdf_encrypt.py`. Asegúrese de reemplazar los nombres de los buckets del marcador de posición:
+ En la función `test_source_bucket_available`, reemplace `amzn-s3-demo-bucket` por el nombre de su bucket de origen.
+ En la función `test_encrypted_file_in_bucket`, reemplace `amzn-s3-demo-bucket-encrypted` por `source-bucket-encrypted`, cuando `source-bucket>` sea el nombre del bucket de origen.
+ En la función `cleanup`, sustituya `amzn-s3-demo-bucket` por el nombre del bucket de origen y reemplace `amzn-s3-demo-bucket-encrypted` por el nombre del bucket de destino.

```
import boto3
import json
import pytest
import time
import os

@pytest.fixture
def lambda_client():
    return boto3.client('lambda')
    
@pytest.fixture
def s3_client():
    return boto3.client('s3')

@pytest.fixture
def logs_client():
    return boto3.client('logs')

@pytest.fixture(scope='session')
def cleanup():
    # Create a new S3 client for cleanup
    s3_client = boto3.client('s3')

    yield
    # Cleanup code will be executed after all tests have finished

    # Delete test.pdf from the source bucket
    source_bucket = 'amzn-s3-demo-bucket'
    source_file_key = 'test.pdf'
    s3_client.delete_object(Bucket=source_bucket, Key=source_file_key)
    print(f"\nDeleted {source_file_key} from {source_bucket}")

    # Delete test_encrypted.pdf from the destination bucket
    destination_bucket = 'amzn-s3-demo-bucket-encrypted'
    destination_file_key = 'test_encrypted.pdf'
    s3_client.delete_object(Bucket=destination_bucket, Key=destination_file_key)
    print(f"Deleted {destination_file_key} from {destination_bucket}")
        

@pytest.mark.order(1)
def test_source_bucket_available(s3_client):
    s3_bucket_name = 'amzn-s3-demo-bucket'
    file_name = 'test.pdf'
    file_path = os.path.join(os.path.dirname(__file__), file_name)

    file_uploaded = False
    try:
        s3_client.upload_file(file_path, s3_bucket_name, file_name)
        file_uploaded = True
    except:
        print("Error: couldn't upload file")

    assert file_uploaded, "Could not upload file to S3 bucket"

    

@pytest.mark.order(2)
def test_lambda_invoked(logs_client):

    # Wait for a few seconds to make sure the logs are available
    time.sleep(5)

    # Get the latest log stream for the specified log group
    log_streams = logs_client.describe_log_streams(
        logGroupName='/aws/lambda/EncryptPDF',
        orderBy='LastEventTime',
        descending=True,
        limit=1
    )

    latest_log_stream_name = log_streams['logStreams'][0]['logStreamName']

    # Retrieve the log events from the latest log stream
    log_events = logs_client.get_log_events(
        logGroupName='/aws/lambda/EncryptPDF',
        logStreamName=latest_log_stream_name
    )

    success_found = False
    for event in log_events['events']:
        message = json.loads(event['message'])
        status = message.get('record', {}).get('status')
        if status == 'success':
            success_found = True
            break

    assert success_found, "Lambda function execution did not report 'success' status in logs."

@pytest.mark.order(3)
def test_encrypted_file_in_bucket(s3_client):
    # Specify the destination S3 bucket and the expected converted file key
    destination_bucket = 'amzn-s3-demo-bucket-encrypted'
    converted_file_key = 'test_encrypted.pdf'

    try:
        # Attempt to retrieve the metadata of the converted file from the destination S3 bucket
        s3_client.head_object(Bucket=destination_bucket, Key=converted_file_key)
    except s3_client.exceptions.ClientError as e:
        # If the file is not found, the test will fail
        pytest.fail(f"Converted file '{converted_file_key}' not found in the destination bucket: {str(e)}")

def test_cleanup(cleanup):
    # This test uses the cleanup fixture and will be executed last
    pass
```

El script de prueba automatizado ejecuta tres funciones de prueba para confirmar el correcto funcionamiento de la aplicación:
+ La prueba `test_source_bucket_available` confirma que el bucket de origen se creó correctamente mediante la carga de un archivo PDF de prueba en el bucket.
+ La prueba `test_lambda_invoked` consulta el último flujo de registro de los registros de CloudWatch de la función para confirmar que la función de Lambda se ejecutó correctamente cuando cargó el archivo de prueba.
+ La prueba `test_encrypted_file_in_bucket` confirma que el bucket de destino contiene el archivo cifrado `test_encrypted.pdf`.

Una vez ejecutadas todas estas pruebas, el script ejecuta un paso de limpieza adicional para eliminar los archivos `test.pdf` y `test_encrypted.pdf` de los buckets de origen y destino.

Los nombres de los buckets especificados en este archivo son marcadores de posición, al igual que en la plantilla AWS SAM. Antes de ejecutar la prueba, debe editar este archivo con los nombres reales de los buckets de la aplicación. Este paso se explica con más detalle en [Prueba de la aplicación con el script automatizado](#file-processing-app-test-auto)

#### Prueba del archivo de configuración del script
<a name="file-processing-app-test-config"></a>

Copie y pegue el siguiente código en un archivo denominado `pytest.ini`.

```
[pytest]
markers =
    order: specify test execution order
```

Esto es necesario para especificar el orden en el que se ejecutan las pruebas del script `test_pdf_encrypt.py`.

Para ejecutar las pruebas, haga lo siguiente:

1. Asegúrese de que el módulo `pytest` esté instalado en el entorno local. Puede instalar `pytest` ejecutando el siguiente comando:

   ```
   pip install pytest
   ```

1. Guarde un archivo PDF con el nombre `test.pdf` en el directorio que contiene los archivos `test_pdf_encrypt.py` y `pytest.ini`.

1. Abra un programa de terminal o intérprete de comandos y ejecute el siguiente comando desde el directorio que contiene los archivos de prueba:

   ```
   pytest -s -v
   ```

   Cuando concluya la prueba, la salida debe tener el siguiente aspecto:

   ```
   ============================================================== test session starts =========================================================
   platform linux -- Python 3.12.2, pytest-7.2.2, pluggy-1.0.0 -- /usr/bin/python3
   cachedir: .pytest_cache
   hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/pdf_encrypt_app/.hypothesis/examples')
   Test order randomisation NOT enabled. Enable with --random-order or --random-order-bucket=<bucket_type>
   rootdir: /home/pdf_encrypt_app, configfile: pytest.ini
   plugins: anyio-3.7.1, hypothesis-6.70.0, localserver-0.7.1, random-order-1.1.0
   collected 4 items
   
   test_pdf_encrypt.py::test_source_bucket_available PASSED
   test_pdf_encrypt.py::test_lambda_invoked PASSED
   test_pdf_encrypt.py::test_encrypted_file_in_bucket PASSED
   test_pdf_encrypt.py::test_cleanup PASSED
   Deleted test.pdf from amzn-s3-demo-bucket
   Deleted test_encrypted.pdf from amzn-s3-demo-bucket-encrypted
   
   
   =============================================================== 4 passed in 7.32s ==========================================================
   ```

## Siguientes pasos
<a name="file-processing-app-next-steps"></a>

Ahora que creó esta aplicación de ejemplo, puede usar el código proporcionado como base para crear otros tipos de aplicaciones de procesamiento de archivos. Modifique el código del archivo `lambda_function.py` para implementar la lógica de procesamiento de archivos para su caso de uso.

Muchos casos de uso típicos del procesamiento de archivos implican el procesamiento de imágenes. Cuando se usa Python, las bibliotecas de procesamiento de imágenes más populares, como [pillow](https://pypi.org/project/pillow/), suelen contener componentes de C o C\$1\$1. Para garantizar que el paquete de implementación de la función sea compatible con el entorno de ejecución de Lambda, es importante utilizar la distribución binaria de origen correcta.

Cuando implemente los recursos con AWS SAM, debe tomar algunas medidas adicionales para incluir la distribución de origen correcta en el paquete de implementación. Como AWS SAM no instalará dependencias para una plataforma diferente a la de la máquina de compilación, especificar la distribución de origen (archivo `.whl`) correcta en su archivo `requirements.txt` no funcionará si la máquina de compilación usa un sistema operativo o una arquitectura diferente del entorno de ejecución de Lambda. En su lugar, lleve a cabo una de las siguientes acciones:
+ Use la opción `--use-container` cuando ejecute `sam build`. Cuando especifica esta opción, AWS SAM descarga una imagen base de contenedor compatible con el entorno de ejecución de Lambda y compila el paquete de implementación de la función en un contenedor de Docker con esa imagen. Para obtener más información, consulte [Creación de una función de Lambda dentro de un contenedor proporcionado](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/using-sam-cli-build.html#using-sam-cli-build-options-container).
+ Cree usted mismo el paquete de implementación .zip de la función con la distribución binaria de origen correcta y guarde el archivo .zip en el directorio que especifique como `CodeUri` en la plantilla AWS SAM. Para obtener más información sobre la compilación de paquetes de implementación .zip para Python mediante distribuciones binarias, consulte [Creación de un paquete de despliegue .zip con dependencias](python-package.md#python-package-create-dependencies) y [Creación de paquetes de despliegue .zip con bibliotecas nativas](python-package.md#python-package-native-libraries).