Sitio web sin servidor con AWS SAM
Utilizando Amazon S3 para alojar nuestro sitio web con ReactJS

AWS tiene una amplia variedad de servicios serverless. En esta ocasión estaremos creando un sitio web serverless (sin servidor) con ReactJS. La tecnología serverless ha permitido que nos concentremos en el valor comercial de nuestro negocio, en lugar de preocuparnos por la administración de infraestructura, como el aprovisionamiento de capacidad y la aplicación de parches, dejando estas tareas en manos de AWS.

Para esta solución usaremos:
ReactJS
Amazon S3
Amazon CloudFront
AWS Lambda
AWS CLI
SAM CLI
Utilizaremos SAM CLI para construir nuestra aplicación a través de una plantilla de CloudFormation para crear los recursos necesarios en la nube y de esta manera tener también una plantilla que puede ser reutilizada en otros proyectos.
Empezaremos creando nuestro proyecto de ReactJS. Para ello necesitamos tener instalado Node >= 14.0.0 y npm >= 5.6.
¿Cómo crear una aplicación con React?
Una vez creado nuestro proyecto en la raíz del mismo crearemos un archivo template.yaml que sera nuestro template de CloudFormation que desplegaremos usando SAM CLI.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
Sitio web con AWS SAM
Parameters:
DomainName:
Type: String
Description: "Nombre de dominio"
IndexDocument:
Type: String
Description: "Documento index"
Default: "index.html"
Resources:
CloudFrontOriginAccessIdentity:
Type: 'AWS::CloudFront::CloudFrontOriginAccessIdentity'
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: 'Desplegando un sitio web con AWS SAM'
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref DomainName
S3BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref S3Bucket
PolicyDocument:
Statement:
- Effect: Allow
Action: 's3:GetObject'
Resource:
- !Sub "arn:aws:s3:::${S3Bucket}/*"
Principal:
AWS: !Sub "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${CloudFrontOriginAccessIdentity}"
CloudfrontDistribution:
Type: "AWS::CloudFront::Distribution"
Properties:
DistributionConfig:
Comment: "Distribucion de Cloudfront"
DefaultRootObject: "index.html"
Enabled: true
HttpVersion: http2
Origins:
- Id: s3-website
DomainName: !Sub "${S3Bucket}.s3.${AWS::Region}.amazonaws.com"
S3OriginConfig:
OriginAccessIdentity:
Fn::Sub: 'origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}'
DefaultCacheBehavior:
Compress: 'true'
AllowedMethods:
- GET
- HEAD
- OPTIONS
ForwardedValues:
QueryString: false
TargetOriginId: s3-website
ViewerProtocolPolicy : redirect-to-https
Outputs:
BucketName:
Description: "Nombre del bucket"
Value: !Ref S3Bucket
CloudFrontDistribution:
Description: "ID de la distribucion de CloudFormation"
Value: !Ref CloudfrontDistribution
WebsiteUrl:
Description: "Sitio web"
Value: !Sub "https://${DomainName}/"
Esta plantilla nos permitirá crear los siguientes servicios:
CloudFrontOriginAccessIdentity: no necesitamos otorgar acceso al publico al bucket S3 directamente ni habilitar el alojamiento de sitios web usando una OriginAccessIdentity, en su lugar podemos conectar nuestro bucket S3 con CloudFront, la manera más segura para dirigir el acceso a los archivos del bucket a las solicitudes que llegan a través de CloudFront.
Más información: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/quickref-cloudfront.html
S3Bucket: nuestro bucket de S3 donde subiremos nuestra carpeta build del proyecto.
S3BucketPolicy: política que define quién puede acceder y qué tipo de operaciones se permiten en el bucket. Aquí restringimos con OriginAccessIdentity el acceso de lectura solo a CloudFront.
CloudFrontDistribution: distribución de CloudFront para indicar a CloudFront desde dónde desea enviar el contenido y los detalles acerca de cómo realizar un seguimiento y administrar la entrega de contenido con baja latencia a nivel mundial.
Implementación de la solución
Primero necesitamos generar el build de nuestro proyecto ReactJS. Para ello ejecutamos el siguiente comando en la raíz del proyecto donde esta ubicado el archivo package.json
npm run buildEsto generará una carpeta con el compilado de nuestra solución que subiremos luego a nuestro bucket S3 usando AWS CLI
Ahora necesitamos desplegar nuestra plantilla de CloudFormation para que se creen los recursos en nuestra cuenta de AWS en la nube
Para ello ejecutamos el siguiente comando en la raíz de nuestro proyecto donde tenemos el archivo template.yaml
sam build
Luego para el despliegue, ejecutamos el siguiente comando y completamos la información solicitada:
sam deploy --guided


Veremos que los recursos que definimos en nuestra plantilla se empezarán a crear. La distribución de CloudFront llevará al inicio algo de tiempo completar, una vez completada la implementación, tendrá un nombre de dominio generado por AWS con el formato d3ue2ep9txxx01.cloudfront.net que usaremos para realizar nuestra solicitud al sitio web.
Ahora debemos subir el contenido de nuestra carpeta build del proyecto de ReactJS al bucket podemos usar el comando :
aws s3 sync ./build s3://my-bucket-with-oac

Una vez que se carguen los archivos, deberíamos poder ver en nombre de dominio de Cloudfront nuestro sitio web
https://d3ue2ep9txxx01.cloudfront.net/

Ahora nos queda pendiente la opción de invalidar la distribución de CloudFront (que veremos en otra entrada) cuando se detecten cambios en el bucket S3, para ello utilizaremos una función lambda que nos ayudará con ese trabajo.

Nota:
En ocasiones me sucedia, que al deployar los cambios estos no se veian reflejados. Por lo que tenia que ejecutar este comando para forzar la actualización
sam deploy --force-upload
