# Serverless email/SMS application

## [**Cloud tools used**](https://cloudisfree.com/#/projects/project-2/README?id=cloud-tools-used)

#### [**AWS**](https://cloudisfree.com/#/projects/project-2/README?id=aws)

* S3
    
* API Gateway
    
* Lambda
    
* Step Functions
    
* SNS
    
* SES
    

#### [**Programming**](https://cloudisfree.com/#/projects/project-2/README?id=programming)

* python
    
* js & json
    
* html
    

## [**Conditions of the Free Tier**](https://cloudisfree.com/#/projects/project-2/README?id=conditions-of-the-free-tier)

* *S3:*
    
    * 5 GB of free storage
        
    * 20,000 free GET requests & 2,000 free PUT requests
        
* *API Gateway:*
    
    * 1 Million API Calls Received
        
* *Lambda:*
    
    * 1,000,000 free requests per month
        
    * Up to 3.2 million seconds of compute time
        
* *Step Functions:*
    
    * 4,000 state transitions
        
* *SNS:*
    
    * 1,000,000 Publishes
        
    * 100,000 HTTP/S Deliveries
        
    * 1,000 Email Deliveries
        
* *SES:*
    
    * 62,000 Outbound Messages
        
    * 1,000 Inbound Messages
        

## [**What the project is about**](https://cloudisfree.com/#/projects/project-2/part-1/README?id=what-the-project-is-about)

[Through this project, yo](https://cloudisfree.com/#/projects/project-2/part-1/README?id=what-the-project-is-about)u [will be able to send an e](https://cloudisfree.com/#/projects/project-2/part-1/README?id=what-the-project-is-about)mail or a SMS from a website using AWS services. Along the way, you will learn how to:

* Execute email and SMS send[ings from a website using](https://cloudisfree.com/#/projects/project-2/part-1/README?id=what-the-project-is-about) S3, API Gateway and Lambda;
    
* Manage email and SMS sendi[ngs using Step Functions;](https://cloudisfree.com/#/projects/project-2/part-1/README?id=what-the-project-is-about)
    
* Create and send an email w[ith AWS using SES and Lam](https://cloudisfree.com/#/projects/project-2/part-1/README?id=what-the-project-is-about)bda;
    
* Create and send a SMS with [AWS using SNS and Lambda](https://cloudisfree.com/#/projects/project-2/part-1/README?id=what-the-project-is-about);
    

Take a closer look at the [infrastructure deployed w](https://cloudisfree.com/#/projects/project-2/part-1/README?id=what-the-project-is-about)ith this project :

![Infrastructure](https://cloudisfree.com/projects/project-2/part-1/images/infrastructure.png align="left")

Time to start the project, [but before that, the bes](https://cloudisfree.com/#/projects/project-2/part-1/README?id=what-the-project-is-about)t is to first learn how Lambda works.

## [**How Lambda works**](https://cloudisfree.com/#/projects/project-2/part-2/README?id=how-lambda-works)

Before starting, it is worth recalling what Lambda is.

See how AWS defines it:

*AWS Lambda is a serverless, event-driven compute service that lets you run code for virtually any type of application or backend service without provisioning or managing servers. You can trigger Lambda from over 200 AWS services and software as a service (SaaS) applications, and only pay for what you use.*

> [**https://aws.amazon.com/lambda/**](https://aws.amazon.com/lambda/)

This definition says enough about how Lambda works, but let's look at it in more detail.

#### [**Lambda Event and Response**](https://cloudisfree.com/#/projects/project-2/part-2/README?id=lambda-event-and-response)

To work with Lambda, it is **essential** to know what an event and a response represent.

**Events are actions or occurrences that take place in a system.** For example, when a user clicks a button on a site, it generates an event containing the information that "a user has clicked a button". An event represents the Input of Lambda.

**A response is the result of information processed by a program.** For example, when a user asks a program how much is 2+2, the program processes the information "2+2" and returns the result "4" as a response. A response represents the Output of Lambda.

In a traditional scheme, this is referred to as the "Input-Process-Output" (IPO) model:

![IPO model](https://cloudisfree.com/projects/project-2/part-2/images/input-process-output.png align="left")

*Link:* [***https://en.wikipedia.org/wiki/IPO\_model***](https://en.wikipedia.org/wiki/IPO_model)

For Lambda, the IPO model translates in this way:

![Event, Lambda and Response](https://cloudisfree.com/projects/project-2/part-2/images/event-lambda-response.png align="left")

The purpose of Lambda is therefore to use it to process the event received, and then to send the desired response after processing.

#### [**Link with other services**](https://cloudisfree.com/#/projects/project-2/part-2/README?id=link-with-other-services)

Lambda's strength lies in its ability to interact with other AWS services. It is possible to make instructions such as "send the received data to SQS" or "change the format of an image uploaded to S3" using Lambda.

For this project, Lambda interacts with:

* SNS for sending SMS;
    
* SES for sending email;
    
* Step Functions to send data received from an API.
    

But in order to interact with other services, you need to have permissions to use them.

#### [**Permissions**](https://cloudisfree.com/#/projects/project-2/part-2/README?id=permissions)

In order for a Lambda function to interact with a service, the function must have what is called a *Lambda Role*. The Lambda Role contains the permissions of our Lambda function, such as the permission to use all actions of the SNS service for example.

So let's start the practice by creating a Lambda Role that will be used by our Lambda functions.

## [**Create a Lambda Role**](https://cloudisfree.com/#/projects/project-2/part-3/README?id=create-a-lambda-role)

[Lambda interacts wi](https://cloudisfree.com/#/projects/project-2/part-3/README?id=create-a-lambda-role)th [SES, SNS and Step F](https://cloudisfree.com/#/projects/project-2/part-3/README?id=create-a-lambda-role)unctions, so a Lambda Role must be created with execution rights for these three services.

First, go to **IAM**, and [select **Roles** on the](https://cloudisfree.com/#/projects/project-2/part-3/README?id=create-a-lambda-role) left, then **Create Role**:

![IAM Create Role](https://cloudisfree.com/projects/project-2/part-3/images/iam-create-role.png align="left")

Next, select **AWS Serv**[**ice** as the **Trusted e**](https://cloudisfree.com/#/projects/project-2/part-3/README?id=create-a-lambda-role)**ntity type**, and for the **Use case** select **Lambda** to perform a Lambda Role:

![Select Trusted Entity](https://cloudisfree.com/projects/project-2/part-3/images/select-trusted-entity.png align="left")

A menu for adding per[missions appears:](https://cloudisfree.com/#/projects/project-2/part-3/README?id=create-a-lambda-role)

![Permissions Policies](https://cloudisfree.com/projects/project-2/part-3/images/permissions-policies.png align="left")

From now on, search f[or the name of each](https://cloudisfree.com/#/projects/project-2/part-3/README?id=create-a-lambda-role) service, and select *Full Access* rights in order to send instructions and information to the services.

> **It is best practice t**[**o refine the permiss**](https://cloudisfree.com/#/projects/project-2/part-3/README?id=create-a-lambda-role)**ions by using only the rights necessary for the actions you want to do with the services, and to do this you must create our own permissions. Here, to facilitate the execution of the project, the *Full Access* permissions created by default by AWS are used.**

Add the *Full Access* p[ermissions of SES:](https://cloudisfree.com/#/projects/project-2/part-3/README?id=create-a-lambda-role)

![SES Permissions](https://cloudisfree.com/projects/project-2/part-3/images/ses-permissions.png align="left")

Same for SNS:

![SNS Permissions](https://cloudisfree.com/projects/project-2/part-3/images/sns-permissions.png align="left")

And s[ame for Step Functions:](https://cloudisfree.com/#/projects/project-2/part-3/README?id=create-a-lambda-role)

![Step Functions Permissions](https://cloudisfree.com/projects/project-2/part-3/images/step-functions-permissions.png align="left")

[Now all th](https://cloudisfree.com/#/projects/project-2/part-3/README?id=create-a-lambda-role)at remains [to be done is to val](https://cloudisfree.com/#/projects/project-2/part-3/README?id=create-a-lambda-role)idate and create the Lambda Role with the desired name.

Once the Lambda Role [has been created, it](https://cloudisfree.com/#/projects/project-2/part-3/README?id=create-a-lambda-role) is time to create the first Lambda function: [*email.py*](http://email.py).

## [**Send Email using SES and Lambda**](https://cloudisfree.com/#/projects/project-2/part-4/README?id=send-email-using-ses-and-lambda)

The first step is to create a Lambda function capable of sending to SES the instruction to send an email.

When writing an email, there are three fields that must be filled in:

* the sender's email address
    
* The content of the message
    
* the recipient's email address
    

The sender's email address is automaticly filled in, and the two other fields will be filled in from the website hosted on S3.

It is therefore necessary to create a Lambda function capable of retrieving the content of the message and the address of the recipient as an event, and to add them to the instruction sent to SES.

#### [**Prerequisites before starting**](https://cloudisfree.com/#/projects/project-2/part-4/README?id=prerequisites-before-starting)

What you should know before using SES is that the service can send emails only from verified email adresses and to verified email addresses. For this project, two addresses will be verified: a source email address and a destination email address.

To do this, go to SES, and select **Create Identity**:

![SES Create Identity](https://cloudisfree.com/projects/project-2/part-4/images/create-identity.png align="left")

Select **Email address** as **Identity type** and fill in the "email address" field with the address that will serve as the source email address. For this example, the source email address is *cif-source@yopmail.com*:

![Create Source Email Address](https://cloudisfree.com/projects/project-2/part-4/images/source-email.png align="left")

Check the mailbox and verify the mail address by clicking the link inside the email you just received from AWS. Once validated, this is what should appear in the AWS console:

![Source Email Address Verified](https://cloudisfree.com/projects/project-2/part-4/images/source-verified.png align="left")

Do the same for the destination email address:

![Create Destination Email Address](https://cloudisfree.com/projects/project-2/part-4/images/destination-email.png align="left")

And verify it as well:

![Destination Email Address Verified](https://cloudisfree.com/projects/project-2/part-4/images/destination-verified.png align="left")

Everything is ready. Now time to create the Lambda function *email.py*.

#### [**Create Lambda function *email.py***](https://cloudisfree.com/#/projects/project-2/part-4/README?id=create-lambda-function-emailpy)

Go to Lambda to create the first function.

The function is called *email* and uses the **Python 3.9** language. In the **Permissions** tab, choose the newly created Lambda role. You can then create the function:

![Lambda Create Function](https://cloudisfree.com/projects/project-2/part-4/images/create-function.png align="left")

Once the function is created, go to the **Code source** menu. This can be seen:

![Code Source](https://cloudisfree.com/projects/project-2/part-4/images/code-source.png align="left")

Delete the code contained in *lambda\_function*. Everything is ready, it's time to code.

In order to interact with AWS services in Python, you need to know the Software Development Kit (SDK) used by AWS. An SDK is a toolkit that a platform provides to developers to interact with its services.

> **For those who struggle to understand what an SDK is, I recommend you watch this great video from IBM explaining it:** [**https://www.youtube.com/watch?v=kG-fLp9BTRo**](https://www.youtube.com/watch?v=kG-fLp9BTRo)

Here, AWS provides its own SDK for python3 named boto3. More details on this link: [**https://docs.aws.amazon.com/lambda/latest/dg/lambda-python.html**](https://docs.aws.amazon.com/lambda/latest/dg/lambda-python.html)

In order to interact with AWS services, you must first import the boto3 library inside your code:

```plaintext
import boto3
```

Here, the service to be interacted with is SES. To do this, you have to call the *client('ses')* class of the boto3 library. This class will be stored in a *ses* variable to simplify the code:

```plaintext
ses = boto3.client('ses')
```

The library is imported, and the *ses* variable is ready, you now have to work on the *lambda\_handler* function. The *lambda\_handler* function is the default function in Lambda. It is inside this function that the events received are managed. It is declared in this way:

```plaintext
def lambda_handler(event, context):
```

The function contains two arguments: *event* and *context*.

*Event* is the event value received by Lambda (the Input), and *context* contains information specific to the function (execution time, name, version etc.).

> **The context argument is of no use here, but for the more curious who want to learn more about the context argument, go to this link:** [**https://docs.aws.amazon.com/lambda/latest/dg/python-context.html**](https://docs.aws.amazon.com/lambda/latest/dg/python-context.html)

Within this function, the SES appropriate function for sending an email must be filled in. To find the function, let's have a look at the boto3 documentation for SES: [**https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ses.html**](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ses.html)

After a little research, the function that fits the bill seems to be *send\_email()*:

![Function Send Email](https://cloudisfree.com/projects/project-2/part-4/images/function-send-email.png align="left")

And according to the documentation, three fields are mandatory: **Source**, **Destination** and **Message**. Perfect, the source and destination addresses are ready, and the message will contain what you want.

The *send\_email* function to be added to *lambda\_handler* should look like this:

```plaintext
    ses.send_email(
        Source='cif-source@yopmail.com',
        Destination={
            'ToAddresses': [
                'cif-destination@yopmail.com',
            ]
        },
        Message={
            'Subject': {
                'Data': 'Cloudisfree - Project 2'
            },
            'Body': {
                'Text': {
                    'Data': 'This is my message'
                }
            }
        }
    )
```

![warning](https://github.githubassets.com/images/icons/emoji/unicode/26a0.png?v8.png align="left")

`Be careful, here you do not want to insert a hard-coded destination e-mail address and a hard-coded message, but ONLY a hard-coded source address. The destination address and the message come from the website, so these values will arrive in the event of the Lambda function (remember, the Input)!`

To do this, add two variables "destinationEmail" and "message" from the event, in this way:

```plaintext
    ses.send_email(
        Source='cif-source@yopmail.com',
        Destination={
            'ToAddresses': [
                event['destinationEmail'],
            ]
        },
        Message={
            'Subject': {
                'Data': 'Cloudisfree - Project 2'
            },
            'Body': {
                'Text': {
                    'Data': event['message']
                }
            }
        }
    )
```

If you look closely, the destination email address and message have been replaced by **event\['destinationEmail'\]** and **event\['message'\]**. This way, the values of the destination email address and the message will be those retrieved from the event.

All that remains is to add a **return** at the end of the function. It is important to know that the return is received by the one who called the Lambda function. According to the project diagram, it is Step Functions that calls this Lambda function, and Step Functions needs to receive a return in order to operate, but the value of the return does not matter.

Fill in the return at the end of the function, with the value of your choice (in this case the value is *"Email sent!"*):

```plaintext
    return 'Email sent!'
```

the Lambda function should look like this (with your own source email address):

```plaintext
import boto3

ses = boto3.client('ses')

def lambda_handler(event, context):
    ses.send_email(
        Source='cif-source@yopmail.com',
        Destination={
            'ToAddresses': [
                event['destinationEmail'],
            ]
        },
        Message={
            'Subject': {
                'Data': 'Cloudisfree - Project 2'
            },
            'Body': {
                'Text': {
                    'Data': event['message']
                }
            }
        }
    )
    return 'Email sent!'
```

It is now time to Deploy and Test the code:

![Email Function Finished](https://cloudisfree.com/projects/project-2/part-4/images/email-function-finished.png align="left")

The last step is to configure a test event to simulate the reception of an event. To do this, fill in the **destinationEmail** and **message** variables with the desired values. In this example, **destinationEmail** has the value *cif-destination@yopmail.com* and **message** has the value *Time to test*:

![Configure Test Event](https://cloudisfree.com/projects/project-2/part-4/images/configure-test-event.png align="left")

All that remains is to press the **Test** button, and there should be a *Succeeded* status displayed in the execution results:

![Execution Results](https://cloudisfree.com/projects/project-2/part-4/images/execution-results.png align="left")

Let's take a look at the destination Mailbox:

![Email Received](https://cloudisfree.com/projects/project-2/part-4/images/email-received.png align="left")

The message was received at the right address with the right message: **Mission completed**.

The same must now be done for the *sms.py* function.

## [**Send SMS using SNS and Lambda**](https://cloudisfree.com/#/projects/project-2/part-5/README?id=send-sms-using-sns-and-lambda)

The first step is to create a Lambda function capable of sending to SNS the instruction to send a SMS.

When sending a SMS, there are two fields that must be filled in:

* The content of the message
    
* the recipient's phone number
    

Those two fields will be filled in from the website hosted on S3.

Like *email.py*, it is necessary to create a Lambda function capable of retrieving the content of the message and the phone number of the recipient as an event, and to add them to the instruction sent to SNS.

#### [**Prerequisites before starting**](https://cloudisfree.com/#/projects/project-2/part-5/README?id=prerequisites-before-starting)

Like SES does with emails, SNS can only send messages to verified numbers, but no need to have a source phone number, AWS will bring one for you. All you have to do is add a single number that will receive the SMS to the list of verified phone numbers, your phone number for example.

To do so, go to SNS, and select **Text Messaging (SMS)** at the left:

![Text Messaging](https://cloudisfree.com/projects/project-2/part-5/images/text-messaging.png align="left")

Then, in the **Account Information** menu, you should see this:

![SNS Account Information](https://cloudisfree.com/projects/project-2/part-5/images/sns-account-information.png align="left")

By default, an AWS account is in an SMS sandbox, so the phone number must be verified. If this is your case, it's not a problem, you just need to verify the phone number for the project.

For those who are not in the SMS sandbox, it may be that someone on your team with access to the AWS account has asked to leave it, so you can skip the phone number verification step and move directly to the **Create Lambda function *sms.py*** part.

> **For those interested, you can have more information about SMS sandbox right there:** [**https://docs.aws.amazon.com/sns/latest/dg/sns-sms-sandbox.html**](https://docs.aws.amazon.com/sns/latest/dg/sns-sms-sandbox.html)

To check the phone number, go to the **Sandbox destination phone numbers** menu and select **Add phone number**:

![Sandbox Destination Phone Number](https://cloudisfree.com/projects/project-2/part-5/images/sandbox-destination-phone.png align="left")

Add your phone number **with the area code**, and select the verification message language of your choice:

![Verify Phone Number](https://cloudisfree.com/projects/project-2/part-5/images/verify-phone-number.png align="left")

All that remains is to enter the Verification code received by SMS:

![Verification Code](https://cloudisfree.com/projects/project-2/part-5/images/verification-code.png align="left")

The number is now verified:

![Verified Phone Number](https://cloudisfree.com/projects/project-2/part-5/images/verified-phone-number.png align="left")

Everything is ready. Now time to create the Lambda function *sms.py*.

#### [**Create Lambda function *sms.py***](https://cloudisfree.com/#/projects/project-2/part-5/README?id=create-lambda-function-smspy)

Go to Lambda to create the second function.

The function is called *sms* and uses the **Python 3.9** language. In the **Permissions** tab, choose the newly created Lambda role. You can then create the function:

![SMS Create Function](https://cloudisfree.com/projects/project-2/part-5/images/create-sms-function.png align="left")

Once the function is created, go to the **Code source** menu. This can be seen:

![Code Source](https://cloudisfree.com/projects/project-2/part-5/images/code-source.png align="left")

Delete the code contained in *lambda\_function*. Everything is ready, it's time to code.

As before, in order to interact with AWS services, you must first import the boto3 libraries inside your code:

```plaintext
import boto3
```

Here, the service to be interacted with is SNS. To do this, you have to call the *client('sns')* class of the boto3 library. This class will be stored in a *sns* variable to simplify the code:

```plaintext
sns = boto3.client('sns')
```

Create the function *lambda\_handler*, which, to remind you, is the default function in Lambda where the events received are managed:

```plaintext
def lambda_handler(event, context):
```

Within this function, the SNS appropriate function for sending a SMS must be filled in. To find the function, let's have a look at the boto3 documentation for SNS: [**https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sns.html**](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sns.html)

After a little research, the function that fits the bill seems to be *publish()*:

![Function Publish](https://cloudisfree.com/projects/project-2/part-5/images/function-publish.png align="left")

And according to the documentation, two fields are required: **Message**, and something to send the message (**PhoneNumber** in our case). The phone number is ready, and the message will contain what you want.

The *publish* function to be added to *lambda\_handler* should look like this:

```plaintext
    sns.publish(
        PhoneNumber='+33678901234',
        Message='Time to test'
    )
```

So, as for *email.py*, by adding the variables contained in event, we get this:

```plaintext
    sns.publish(
        PhoneNumber=event['phoneNumber'],
        Message=event['message']
    )
```

All that remains is to add a **return** at the end of the function, which is REQUIRED for Step Functions to operate:

```plaintext
    return 'SMS sent!'
```

the Lambda function should look like this:

```plaintext
import boto3

sns = boto3.client('sns')

def lambda_handler(event, context):
    sns.publish(
        PhoneNumber=event['phoneNumber'],
        Message=event['message']
    )
    return 'SMS sent!'
```

It is now time to Deploy and Test the code:

![SMS Function Finished](https://cloudisfree.com/projects/project-2/part-5/images/sms-function-finished.png align="left")

The last step is to configure a test event to simulate the reception of an event. To do this, fill in the **phoneNumber** and **message** variables with the desired values. In this example, **phoneNumber** has the value *+33678901234* and **message** has the value *Time to test*:

![Configure Test Event](https://cloudisfree.com/projects/project-2/part-5/images/configure-test-event.png align="left")

All that remains is to press the **Test** button, and there should be a *Succeeded* status displayed in the execution results:

![Execution Results](https://cloudisfree.com/projects/project-2/part-5/images/execution-results.png align="left")

You received an SMS a few seconds after running the test: **Congratulations, you know how to send an SMS with Lambda.**

The *email.py* and *sms.py* scripts are ready, it's time to link them to Step Functions, which will transfer the data and execute the scripts.

## [**Manage how to send using Step Functions**](https://cloudisfree.com/#/projects/project-2/part-6/README?id=manage-how-to-send-using-step-functions)

Before starting, it is worth explaning what Step Functions is.

See how AWS defines it:

*AWS Step Functions is a low-code, visual workflow service that developers use to build distributed applications, automate IT and business processes, and build data and machine learning pipelines using AWS services. Workflows manage failures, retries, parallelization, service integrations, and observability so developers can focus on higher-value business logic.*

> [**https://aws.amazon.com/step-functions/**](https://aws.amazon.com/step-functions/)

This definition helps to understand how Step Functions works in broad terms, but makes it difficult to visualise the tool. The following will give a much better understanding of Step Functions, which is, contrary to its complex definition, a simple tool to use.

### [**Create *Sending* State Machine**](https://cloudisfree.com/#/projects/project-2/part-6/README?id=create-sending-state-machine)

First, go to Step Functions and select **State machines** on the left:

![Select State Machines](https://cloudisfree.com/projects/project-2/part-6/images/select-state-machines.png align="left")

Then select **Create state machine**:

![Create State Machine](https://cloudisfree.com/projects/project-2/part-6/images/create-state-machine.png align="left")

There are now three choices:

* **Design your worflow visually**: This method allows you to create state machines via block diagrams. A new method that is easier to handle, but less effective than the other two.
    
* **Write your workflow in code**: As the title suggests, workflow is written in code, nothing more to add.
    
* **Run a sample project**: All you have to do is get a sample project using CloudFormation.
    

The choice here is to **write the workflow in code**, which is the best way to get the hang of and understand the tool:

![Define State Machine](https://cloudisfree.com/projects/project-2/part-6/images/define-state-machine.png align="left")

For the **Type**, to make the wise choice, click on the **Help me decide** text, and take a look at the comparison table of the *Standard* and *Express* types.

What needs to be compared is the pricing between the two types, and ESPECIALLY which of the two types is compatible with the Free Tier. Here is what is offered with the Free Tier for Step Functions:

![Step Functions Free Tier](https://cloudisfree.com/projects/project-2/part-6/images/step-functions-free-tier.png align="left")

AWS offers 4,000 state transitions per month, and if you take a look at the Standard pricing, this is what you observe:

![Standard Pricing](https://cloudisfree.com/projects/project-2/part-6/images/standard-pricing.png align="left")

The Standard type is priced per state transitions, where the Express type is priced by the number of execution you run, their duration and memory consumption. In other words, **the type eligible for Free Tier is the Standard type**. You must therefore select **Standard**:

![Type Standard](https://cloudisfree.com/projects/project-2/part-6/images/type-standard.png align="left")

Go to the **Definition** menu:

![Definition Menu](https://cloudisfree.com/projects/project-2/part-6/images/definition-menu.png align="left")

This is where it starts to get interesting. On the left you can see some code written in JSON, which makes the workflow diagram on the right.

All workflows start with a *Start* and end with an *End* (it makes sense doesn't it), and the aim is to establish actions between these two. The actions will use the data received at the Input of the workflow (at the *Start* more precisely). The objective for the project is therefore to **set up actions that will process the data received from the website: message, destinationEmail, and phoneNumber**.

You have to :

* Send the values of the **message** and **destinationEmail** variables to the script *email.py*;
    
* Send the values of the **message** and **phoneNumber** variables to the script *sms.py*.
    

But you should also consider what type of sending you want to do. Send an email or a SMS? Sometimes you will want to send an email to your uncle, and other times you will want to send an SMS to your friend. For this, you will need a new variable that will be used by your state machine: **typeOfSending**.

The final goal is to have this workflow diagram:

![Workflow Diagram](https://cloudisfree.com/projects/project-2/part-6/images/workflow-diagram.png align="left")

The first action is to select the type of sending, then the chosen sending is processed (an SMS if **typeOfSending** = *sms*, or an email if **typeOfSending** = *email*).

Enough talk, it's time to create your first state machine, and there's nothing like the documentation to help you: [**https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-state-machine-structure.html**](https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-state-machine-structure.html)

You can see that a state machine MUST consist of the **StartAt** and **States** fields, but to help you, I recommend you add the **Comment** field which will explain the functionality of the state machine (in case you forget in 2 years why this state machine exists):

```plaintext
{
    "Comment": "State machine for sending SMS & email",
    "StartAt": "Select Type of Sending",
    "States": {
```

In doing so, **Comment** explains the functionality of the state machine, **StartAt** allows you to define which action to start with (in this case *Select Type of Sending*), and **States** contains all the states that contain the actions you want to perform.

As seen in the diagram above, *Select Type of Sending*, *Email* and *SMS* are the states, and each performs its own action.

These three states must therefore be created, with an action assigned to them:

* the *Select type of Sending* state will make the choice between the *Email* or *SMS* state based on the **typeOfSending** variable;
    
* the *Email* state sends the **destinationEmail** and **message** variables to the Lambda function *email.py*;
    
* the *SMS* state sends the **phoneNumber** and **message** variables to the Lambda function *sms.py*.
    

> **Find out more about the states:** [**https://docs.aws.amazon.com/step-functions/latest/dg/concepts-states.html**](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-states.html)

Start with the first state, *Select Type of Sending* :

```plaintext
        "Select Type of Sending": {
            "Type": "Choice",
```

The state is of type *Choice*, which as its name indicates, allows a choice between several states. Here, the choice will be between the *SMS* and *Email* states:

```plaintext
            "Choices": [
                {
                    "Variable": "$.typeOfSending",
                    "StringEquals": "email",
                    "Next": "Email"
                },
                {
                    "Variable": "$.typeOfSending",
                    "StringEquals": "sms",
                    "Next": "SMS"
                }
            ]
        },
```

In the **Choices** field, two elements are filled in.

![warning](https://github.githubassets.com/images/icons/emoji/unicode/26a0.png?v8.png align="left")

`The following is very important to understand. Once this is understood, the rest is relatively simple:` In order to make its choice, the *Select Type Of Sending* state checks the **typeOfSending** variable received as Input, checking whether its value is equal to the value of the **StringEquals** field.

In the first choice, the **StringEquals** field has the value *email*, so if **typeOfSending** = *email*, then the chosen state will be the value contained in the **Next** field: the *Email* state.

In the second choice, the **StringEquals** field has the value *sms*, so if **typeOfSending** = *sms*, then the chosen state will be the value contained in the **Next** field: the *SMS* state.

The *Type Of Sending* state is now ready, it is time to create the *Email* and *SMS* states.

Continue with the second state, *Email* :

```plaintext
        "Email": {
            "Type" : "Task",
            "Resource": "<lambda_email.py_arn>",
            "End": true
        },
```

The state is of type *Task*, which allows to execute a resource by specifying its ARN. Here the resource is the Lambda function *email.py*. The **"End": true** field is used to say that this is the last state to be executed.

Finish with the third state, *SMS* :

```plaintext
        "SMS": {
            "Type" : "Task",
            "Resource": "<lambda_sms.py_arn>",
            "End": true
        }
    }
}
```

The state is of type *Task*, and this time, the resource is the Lambda function *sms.py*. The **"End": true** field is used to say that this is the last state to be executed too.

In the end, the code looks like this:

```plaintext
{
    "Comment": "State machine for sending SMS & email",
    "StartAt": "Select Type of Sending",
    "States": {
        "Select Type of Sending": {
            "Type": "Choice",
            "Choices": [
                {
                    "Variable": "$.typeOfSending",
                    "StringEquals": "email",
                    "Next": "Email"
                },
                {
                    "Variable": "$.typeOfSending",
                    "StringEquals": "sms",
                    "Next": "SMS"
                }
            ]
        },
        "Email": {
            "Type" : "Task",
            "Resource": "<lambda_email.py_arn>",
            "End": true
        },
        "SMS": {
            "Type" : "Task",
            "Resource": "<lambda_sms.py_arn>",
            "End": true
        }
    }
}
```

All that remains to be done is to add the ARNs of the Lambda functions *email.py* and *sms.py* to the corresponding states:

![email.py ARN](https://cloudisfree.com/projects/project-2/part-6/images/email-arn.png align="left")

![sms.py ARN](https://cloudisfree.com/projects/project-2/part-6/images/sms-arn.png align="left")

Once the code is ready, the workflow diagram should change to this one:

![Workflow Diagram](https://cloudisfree.com/projects/project-2/part-6/images/workflow-diagram.png align="left")

> **If the diagram to the right of the code does not change or does not look like the one seen above, there is an error in the code (a syntax error for example). When the code is correct, the workflow diagram appears automatically. Check that your workflow diagram is the same as the one above.**

And it is time to finalise the creation of the state machine, give a name to the state machine:

![State Machine Name](https://cloudisfree.com/projects/project-2/part-6/images/state-machine-name.png align="left")

Leave the other values as default and create the state machine.

It is time to run the newly created state machine. Click on **Start execution**:

![Start Execution](https://cloudisfree.com/projects/project-2/part-6/images/start-execution.png align="left")

A window appears, asking for a Test Input for the state machine (just like the Test Event for Lambda). The Input is encoded in JSON, and must contain the variables **typeOfSending**, **destinationEmail**, **phoneNumber** and **message**:

```plaintext
{
    "typeOfSending": "email",
    "destinationEmail" : "cif-destination@yopmail.com",
    "phoneNumber": "+33678901234",
    "message": "Sent from Step Functions"
}
```

All that remains is to launch the execution with the desired values:

![Input Step Functions](https://cloudisfree.com/projects/project-2/part-6/images/input-step-functions.png align="left")

For this execution, an email must be sent since the **typeOfSending** variable is set to *email*, so let's see how this works:

![Graph Email Success](https://cloudisfree.com/projects/project-2/part-6/images/graph-email-success.png align="left")

According to Step Functions, everything went well, so the email must have been sent. See the mailbox of the destination email:

![Email Received](https://cloudisfree.com/projects/project-2/part-6/images/email-received.png align="left")

The mail has been received: the state machine works perfectly to send an email. Now time to check if all goes well to send an SMS, by changing the **typeOfSending** variable to *sms*:

![Graph SMS Success](https://cloudisfree.com/projects/project-2/part-6/images/graph-sms-success.png align="left")

According to Step Functions, everything went well, and the SMS has been received.

**The *Sending* state machine is now ready for use**. It's time to go back to Lambda to attack the third and final Lambda function: *restApiHandler.py*.

## [**Create the REST API Handler using Lambda**](https://cloudisfree.com/#/projects/project-2/part-7/README?id=create-the-rest-api-handler-using-lambda)

### [**What is a REST API Handler ?**](https://cloudisfree.com/#/projects/project-2/part-7/README?id=what-is-a-rest-api-handler-)

A REST API Handler is a function that retrieves and processes data sent by a REST API.

> **To learn more about what a REST API is, I refer you to this video from IBM which explains it very well:** [**https://www.youtube.com/watch?v=lsMQRaeKNDk**](https://www.youtube.com/watch?v=lsMQRaeKNDk)

When a REST API receives data, it stores it in a variable named **body**, and then sends the **body** variable to a REST API Handler which will then process the data.

The objective is therefore to create a Lambda function capable of receiving data sent by a REST API (**body** variable) and returning this data to the desired service (here Step Functions).

Let's see how to create the Lambda function *restApiHandler.py*.

### [**Create Lambda Function *restApiHandler.py***](https://cloudisfree.com/#/projects/project-2/part-7/README?id=create-lambda-function-restapihandlerpy)

Go to Lambda to create the third and last function.

The function is called *restApiHandler.py* and uses the **Python 3.9** language. In the **Permissions** tab, choose the Lambda role you created. You can then create the function:

![Rest API Handler Create Function](https://cloudisfree.com/projects/project-2/part-7/images/create-api-function.png align="left")

Once the function is created, go to the **Code source** menu. This can be seen:

![Code Source](https://cloudisfree.com/projects/project-2/part-7/images/code-source.png align="left")

Delete the code contained in *lambda\_function*. Everything is ready, it's time to code.

As always, in order to interact with AWS services, you must first import the boto3 libraries inside your code:

```plaintext
import boto3
```

New thing here, this Lambda function will use functions to manipulate JSON data, so add the JSON library:

```plaintext
import json
```

Here, the service to be interacted with is Step Functions. To do this, you have to call the *client('stepfunctions')* class of the boto3 library. This class will be stored in a *sfn* variable to simplify the code:

```plaintext
sfn = boto3.client('stepfunctions')
```

Create the function *lambda\_handler*, which, to remind you, is the default function in Lambda where the events received are managed:

```plaintext
def lambda_handler(event, context):
```

Within this function, the Step Functions appropriate function for executing a state machine must be filled in. To find the function, let's have a look at the boto3 documentation for Step Functions: [**https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/stepfunctions.html**](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/stepfunctions.html)

After a little research, the function that addresses the issue seems to be *start\_execution()*:

![Function Start Execution](https://cloudisfree.com/projects/project-2/part-7/images/function-start-execution.png align="left")

And according to the documentation, only one field is required: **stateMachineArn**. However, it is necessary here to send data as Input, so the **input** variable will also be used.

The *start\_execution* function to be added to *lambda\_handler* should look like this:

```plaintext
    sfn.start_execution(
        stateMachineArn="<state_machine_arn>",
        input=event['body']
    )
```

![warning](https://github.githubassets.com/images/icons/emoji/unicode/26a0.png?v8.png align="left")

`It is worth noting that for the input, the event variable called is 'body', since as explained earlier, the API stores the data it receives into a 'body' variable and sends it. Therefore it is in this variable that all other variables are contained.`

All that remains is to add a **return** at the end of the function, which gives the sending result to the API Gateway. Unlike the other scripts (*email.py* and *sms.py*), the value returned is important. The Gateway API needs to receive a **Status Code** and a **body** in JSON format containing the **Status**.

> **Learn more about status codes:** [**https://en.wikipedia.org/wiki/List\_of\_HTTP\_status\_codes**](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes)

The **Status Code** to be returned when the Lambda function executes correctly is *code 200*. The **Status** can contain the value of your choice (try to be as explicit as possible), which gives a result like this:

```plaintext
    return {
        "statusCode": 200,
        "body": json.dumps(
            {"Status": "Instruction sent to the REST API Handler!"},
        )
    }
```

In the end, the Lambda function should look like this:

```plaintext
import boto3
import json

sfn = boto3.client('stepfunctions')

def lambda_handler(event, context):
    sfn.start_execution(
        stateMachineArn="<state_machine_arn>",
        input=event['body']
    )
    
    return {
        "statusCode": 200,
        "body": json.dumps(
            {"Status": "Instruction sent to the REST API Handler!"},
        )
    }
```

It is now time to see if it works. First copy the ARN of the state machine previously created:

![Copy State Machine ARN](https://cloudisfree.com/projects/project-2/part-7/images/copy-state-machine-arn.png align="left")

Add it to your code, and now Deploy and Test the code:

![REST API Handler Function Finished](https://cloudisfree.com/projects/project-2/part-7/images/api-function-finished.png align="left")

The last step is to configure a test event to simulate the reception of an event. To do this, fill in the **body** variable, but not in just any way. The format must correspond to what Step Functions receives as Input, and according to the documentation, the format for the Input is written as follows:

![Input Format](https://cloudisfree.com/projects/project-2/part-7/images/input-format.png align="left")

Following the requested format, the Input must look like this:

```plaintext
{
    "body": "{\"typeOfSending\":\"sms\", \"destinationEmail\":\"cif-destination@yopmail.com\", \"phoneNumber\":\"+33678901234\", \"message\":\"Sent from REST API Handler\"}"
}
```

All that remains is to press the **Test** button, and there should be a *Succeeded* status displayed in the execution results:

![Execution Results](https://cloudisfree.com/projects/project-2/part-7/images/execution-results.png align="left")

If you received an email or an SMS based on what you have chosen: **Congratulations, you know how to receive data from an API and send it to Step Functions.**

The *restApiHandler.py* script is ready, and it's time to link it to API Gateway.

## [**Create and configure the API Gateway**](https://cloudisfree.com/#/projects/project-2/part-8/README?id=create-and-configure-the-api-gateway)

Before starting once more, it is worth recalling what API Gateway is.

See how AWS defines it:

*Amazon API Gateway is a fully managed service that makes it easy for developers to create, publish, maintain, monitor, and secure APIs at any scale. APIs act as the "front door" for applications to access data, business logic, or functionality from your backend services. Using API Gateway, you can create RESTful APIs and WebSocket APIs that enable real-time two-way communication applications.*

> [**https://aws.amazon.com/api-gateway/**](https://aws.amazon.com/api-gateway/)

The definition explains very well what API Gateway is. Just remember that API Gateway allows you to create and use APIs.

### [**Create a REST API using API Gateway**](https://cloudisfree.com/#/projects/project-2/part-8/README?id=create-a-rest-api-using-api-gateway)

First, go to API Gateway and **Build** a REST API:

![Build REST API](https://cloudisfree.com/projects/project-2/part-8/images/build-rest-api.png align="left")

Here, choose to create a *New API* of type *REST*. Give the API a suitable name (*SendingAPI* in this example) and create the API:

![REST API Creation](https://cloudisfree.com/projects/project-2/part-8/images/rest-api-creation.png align="left")

Once the API is created, click on the **Actions** button and select **Create Resource**:

![Create Resource](https://cloudisfree.com/projects/project-2/part-8/images/create-resource.png align="left")

Enter the name of the resource (here *sending*) and create the resource:

![Resource Configuration](https://cloudisfree.com/projects/project-2/part-8/images/resource-configuration.png align="left")

Once the resource created, click on the **Actions** button and select **Create Method**

![Create Method](https://cloudisfree.com/projects/project-2/part-8/images/create-method.png align="left")

A field will appear, where the *POST* method should be selected, since the purpose will be to publish the data from the website to the API.

When setting up the POST method, choose *Lambda Function* as the **Integration type**, entering the newly created REST API Handler (*restApiHandler*) in the **Lambda Function** field, and do NOT forget to **Use Lambda Proxy Integration**:

![POST Setup](https://cloudisfree.com/projects/project-2/part-8/images/post-setup.png align="left")

A window will appear asking to add permissions to the Lambda Function, click on **OK**:

![Add Permissions](https://cloudisfree.com/projects/project-2/part-8/images/add-permissions.png align="left")

The method is now created and points to the Lambda function *restApiHandler.py*, so at each POST method from the website, the API Gateway will receive the data and pass it directly to the REST API Handler.

All that remains is to deploy the API, doing **Action** -&gt; **Deploy API**:

![Deploy API](https://cloudisfree.com/projects/project-2/part-8/images/deploy-api.png align="left")

Make a *\[New Stage\]*, and choose the **Stage name** of your choice (*sendingStage* in this example):

![Deployment Stage](https://cloudisfree.com/projects/project-2/part-8/images/deployment-stage.png align="left")

The API is deployed in the newly created stage. It will now be possible to send data to API Gateway via the **Invoke URL** of the stage:

![Invoke URL](https://cloudisfree.com/projects/project-2/part-8/images/invoke-url.png align="left")

The **Invoke URL** is ready. All that remains to be done is to create the website *index.html* on which the data will be entered, as well as the script *formToApi.js* that will send the data to the Invoke URL.

## [**Code a website and upload it to S3**](https://cloudisfree.com/#/projects/project-2/part-9/README?id=code-a-website-and-upload-it-to-s3)

Last step before finishing the project: the website.

For this part, you have to:

* create a main page on which to fill in the **message** and **destinationEmail**/**phoneNumber** data;
    
* write a script capable of sending the data from the website to the REST API;
    
* upload the website to S3.
    

It is now time to see how to carry out these three steps.

### [**Create main page *index.html***](https://cloudisfree.com/#/projects/project-2/part-9/README?id=create-main-page-indexhtml)

First, for those working on Visual Studio Code, create an HTML file, and write an exclamation mark '**!**' then press enter to generate a basic HTML template.

For those who do not use Visual Studio Code, copy the HTML page below:

```plaintext
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>
</html>
```

In order to fill in the **message** and **destinationEmail**/**phoneNumber** data, set up a form in the *&lt;body&gt;*, containing **Message**, **Email** and **SMS** fields, as well as two buttons, one for **sending an email** and the other for **sending an SMS**. The result should look like this:

```plaintext
<body>
    <form>
        <div>
            <label>Message:</label>
            <input type="text" name="message">
            <br><br>
            <label>Email:</label>
            <input name="email">
            <button>Send an Email</button>
            <br><br>
            <label>SMS:</label>
            <input name="sms">
            <button>Send a SMS</button>
        </div>
    </form>
</body>
```

![warning](https://github.githubassets.com/images/icons/emoji/unicode/26a0.png?v8.png align="left")

`It is REQUIRED to fill in the name attributes for the <input>, this will be very important for later.`

The main page should look like this:

![Main Page Form](https://cloudisfree.com/projects/project-2/part-9/images/main-page-form.png align="left")

Now you want to send the form data to the *formToApi.js* script, to do this you must first declare in the *&lt;head&gt;* the use of the script:

```plaintext
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Project 2 - Cloud Is Free</title>
    <script src="formToApi.js"></script>
</head>
```

The objective now is to send an email when the **Send an Email** button is clicked, and to send a SMS when the **Send a SMS** button is clicked.

In other words: on click, send the data and the **typeOfSending** corresponding to the button clicked to the script. To do this, use the **onClick** attribute, which will launch the *formToApi.js* script when a button is clicked.

> **To know more about OnClick:** [**https://www.w3schools.com/tags/ev\_onclick.asp**](https://www.w3schools.com/tags/ev_onclick.asp)

This is what it looks like:

```plaintext
<body>
    <form>
        <div>
            <label>Message:</label>
            <input type="text" name="message">
            <br><br>
            <label>Email:</label>
            <input name="email">
            <button onClick="formToApi(event,'email')">Send an Email</button>
            <br><br>
            <label>SMS:</label>
            <input name="sms">
            <button onClick="formToApi(event,'sms')">Send a SMS</button>
        </div>
    </form>
</body>
```

> **The FormToApi() function and its arguments will be explained below when creating the *FormToApi.js* script.**

In the end, the main page should look like this:

```plaintext
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Project 2 - Cloud Is Free</title>
    <script src="formToApi.js"></script>
</head>
<body>
    <form>
        <div>
            <label>Message:</label>
            <input type="text" name="message">
            <br><br>
            <label>Email:</label>
            <input name="email">
            <button onClick="formToApi(event,'email')">Send an Email</button>
            <br><br>
            <label>SMS:</label>
            <input name="sms">
            <button onClick="formToApi(event,'sms')">Send a SMS</button>
        </div>
    </form>
</body>
</html>
```

The main page *index.html* is ready, now it's time for the *formToApi.js* script.

### [**Create POST script *formToApi.js***](https://cloudisfree.com/#/projects/project-2/part-9/README?id=create-post-script-formtoapijs)

First, create a JavaScript file *formToApi.js*, in which the **formToApi()** function will be created:

```plaintext
function formToApi(event, typeOfSending) {
```

The function has **event** and **typeOfSending** as arguments.

The **typeOfSending** argument is already known to everyone, if its value is *email* then an email will be sent, and if its value is *sms* then a SMS will be sent.

The **event** argument contains the event that occurred when the JS script was called. Here, it is the click on a button (**onClick** argument) that launched the function **formToApi()**, and the **onClick** argument by default redirects to the file containing the function (*formToApi.js*). You must therefore prevent the redirection to the *formToApi.js* file and remain on the main page *index.html* when you click on a button.

And to do this, use the **preventDefault()** method, which prevents the default action of the **onClick** argument:

```plaintext
    event.preventDefault()
```

> **To better understand the preventDefault() method:** [**https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault**](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)

All you need to remember is that:

* **event** is used to prevent page redirection;
    
* **typeOfSending** is used to specify the type of sending.
    

Now that the arguments of the **formToApi()** function are understood, it is time to send the data to the API.

To do this, create a **data** variable containing all the **variables** to be sent to the API, **typeOfSending**, **destinationEmail**, **phoneNumber** and **message**:

```plaintext
    var data = {
        typeOfSending: ,
        destinationEmail:,
        phoneNumber: ,
        message:
    }
```

But how can this data be retrieved? It's simple: the value of **typeOfSending** has been retrieved in the argument **typeOfSending** of the function, so you just need to call the argument of the same name:

```plaintext
    var data = {
        typeOfSending: typeOfSending,
        destinationEmail: ,
        phoneNumber: ,
        message:
    }
```

And for the other three arguments, you need to retrieve the values contained in the form, and this will be possible using the **name** attribute previously filled in. Remember, in *index.html*, each *&lt;input&gt;* has a **name** (for example name="message" for the &lt;input&gt; containing the message).

To do this, use the **getElementsByName** method, which, as its name suggests, retrieves an element (or elements) based on the name. This gives:

```plaintext
    var data = {
        typeOfSending: typeOfSending,
        destinationEmail: document.getElementsByName('email')[0].value,
        phoneNumber: document.getElementsByName('sms')[0].value,
        message: document.getElementsByName('message')[0].value
    }
```

> **To learn more about getElementsByName:** [**https://www.w3schools.com/jsref/met\_doc\_getelementsbyname.asp**](https://www.w3schools.com/jsref/met_doc_getelementsbyname.asp)

You now need to send this data to the previously created REST API using the **fetch()** method. Here, this is how **fetch()** should be written:

```plaintext
    fetch( "<API_url>" , {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify(data),
        mode: "no-cors"
    })
}
```

> **To learn more about fetch():** [**https://javascript.info/fetch-api**](https://javascript.info/fetch-api)

The method **fetch()** has two arguments, the first containing the URL of the API, and the second containing several options (here **method**, **headers**, **body** and **mode**). Let's look at these options in detail.

**method** designates the method to use to interact with the API. Here you want to send data, so you have to use the *POST* method.

**headers** contains the headers you want to add to the request. Here you specify that the content (what is in the **body**) is of type JSON, so you fill in: *"Content-Type": "application/json "*

**body** contains the content to be sent during the request. You have to add the data (*data* variable) in the body, and in JSON format (*JSON.stringify*) to be readable by the API.

**mode** specifies the mode you want to use for the request. The mode used here is *no-cors* in order to receive an opaque response from the API. In some cases, we prefer to get an explicit response from the API (to get an image with a GET method for example), but in this case, it is not a problem to receive an opaque response since the only method that will be used is POST. The *no-cors* mode strengthens the security since potential attackers won't be able to retrieve anything from the API.

> **For those who want to know more about opaque responses:** [**https://tpiros.dev/blog/what-is-an-opaque-response/**](https://tpiros.dev/blog/what-is-an-opaque-response/)

The script *formToApi.js* should now look like this:

```plaintext
function formToApi(event, typeOfSending) {

    event.preventDefault()

    var data = {
        typeOfSending: typeOfSending,
        destinationEmail: document.getElementsByName('email')[0].value,
        phoneNumber: document.getElementsByName('sms')[0].value,
        message: document.getElementsByName('message')[0].value
    }

    fetch( "<API_url>" , {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify(data),
        mode: "no-cors"
    })
}
```

![warning](https://github.githubassets.com/images/icons/emoji/unicode/26a0.png?v8.png align="left")

`When filling the API URL, you need to put the Invoke URL of the API AND the resource containing the POST method at the end of the Invoke URL.`

**Example:**

| **Invoke URL** | **Resource** | **API URL** |
| --- | --- | --- |
| [**https://tgykfcp526.execute-api.us-east-1.amazonaws.com/sendingStage**](https://tgykfcp526.execute-api.us-east-1.amazonaws.com/sendingStage) | sending | [**https://tgykfcp526.execute-api.us-east-1.amazonaws.com/sendingStage/sending**](https://tgykfcp526.execute-api.us-east-1.amazonaws.com/sendingStage/sending) |

Now is the time to test the website.

> **In order to test the website, I strongly advise you to set up a local web server. For VSCode users, this tutorial explains very well how to do it:** [**https://www.youtube.com/watch?v=tk8wLMJ7diQ**](https://www.youtube.com/watch?v=tk8wLMJ7diQ)

Test the website by going to the main page, and trying to send an email and a SMS:

![Sending Email](https://cloudisfree.com/projects/project-2/part-9/images/sending-email.png align="left")

Wait a few seconds, before going to the mailbox of the destination mail:

![Email Received](https://cloudisfree.com/projects/project-2/part-9/images/email-received.png align="left")

The email has been received, now it's time to send a SMS:

![Sending SMS](https://cloudisfree.com/projects/project-2/part-9/images/sending-sms.png align="left")

Wait a few seconds, and the SMS should be received.

The website works perfectly, only one last step left in the project: upload the website on S3.

### [**Upload the website on S3**](https://cloudisfree.com/#/projects/project-2/part-9/README?id=upload-the-website-on-s3)

Go to S3 and create a bucket. Assign the bucket name of your choice:

![Create Bucket](https://cloudisfree.com/projects/project-2/part-9/images/create-bucket.png align="left")

Then allow the bucket to be publicly available on the Internet:

![Public Access Settings Bucket](https://cloudisfree.com/projects/project-2/part-9/images/public-access-settings-bucket.png align="left")

You can then create the bucket.

Once the bucket is created, go to its configuration and upload the *index.html* and *formToApi.js* files:

![Upload Files](https://cloudisfree.com/projects/project-2/part-9/images/upload-files.png align="left")

Go to the **Properties** tab, section **Static website hosting** (at the very bottom) and click on **Edit**:

![Static Web Hosting](https://cloudisfree.com/projects/project-2/part-9/images/static-website-hosting.png align="left")

*Enable* the **Static website hosting**, and fill in the *index.html* file in the **Index document** field before validating the **Static website hosting** configuration:

![Configure Hosting](https://cloudisfree.com/projects/project-2/part-9/images/configure-hosting.png align="left")

Move to **Permissions** to modify the Bucket Policy:

![Edit Bucket policy](https://cloudisfree.com/projects/project-2/part-9/images/edit-bucket-policy.png align="left")

Insert the following policy and modify it slightly before validating:

```plaintext
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::bucket_name/*"
            ]
        }
    ]
}
```

This Bucket policy allows:

* Assign read rights to objects via the Action ''s3:GetObject'' ;
    
* The Targeted Resources are the objects in your S3 Bucket.
    

![warning](https://github.githubassets.com/images/icons/emoji/unicode/26a0.png?v8.png align="left")

`Do not forget to replace the *bucket_name* field with the name of the S3 Bucket, and keep the /* at the end of the Resource field, otherwise you won't give access to the Bucket objects!`

In this example, the Bucket policy now looks like this:

![Bucket policy added](https://cloudisfree.com/projects/project-2/part-9/images/bucket-policy-added.png align="left")

All that remains is to apply the new Bucket policy; the pages of the website will now be publicly accessible. It should be displayed *publicly accessible* below the name of the S3 Bucket:

![Publicly Accessible](https://cloudisfree.com/projects/project-2/part-9/images/publicly-accessible.png align="left")

Go back to **Properties** section **Static website hosting** and copy the website link into a new tab:

![Copy URL](https://cloudisfree.com/projects/project-2/part-9/images/copy-url.png align="left")

The website hosted on S3 is now accessible:

![Website Accessible](https://cloudisfree.com/projects/project-2/part-9/images/website-accessible.png align="left")

Do a final test, by sending an email from S3:

![Email Sent From S3](https://cloudisfree.com/projects/project-2/part-9/images/send-email-from-s3.png align="left")

Let's see the destination mailbox:

![Email from S3 Received](https://cloudisfree.com/projects/project-2/part-9/images/email-s3-received.png align="left")

It works. Now time to send a SMS from S3:

![SMS Sent From S3](https://cloudisfree.com/projects/project-2/part-9/images/send-sms-from-s3.png align="left")

If you receive the message: **Congratulations!**

**You have completed Project 2. You are now fully capable of making Serverless services interact with each other and send instructions to them from a website, all using AWS.**

# [**Homepage**](https://cloudisfree.com/#/../?id=homepage)

> **If you are on the GitHub repository:** [**Go to the website**](https://cloudisfree.com/)

This site aims to help those wishing to set up practical projects through an interactive and detailed documentation, and this while taking advantage of the Free Tier from Cloud platforms.

**No more fear of receiving a hefty bill:** follow these steps properly and deploy your first Cloud projects for cheap.

### [**You will learn how to :**](https://cloudisfree.com/#/../?id=you-will-learn-how-to-)

* Deploy a static website;
    
* Create a Serverless sending application;
    
* Create and manipulate databases using containers;
    

and other projects that will come in the future.

The projects are currently deployed on AWS, but will be deployed on other Cloud platforms in the future.

Here is an overview of the technologies used for each project:

![Technologies Used](https://cloudisfree.com/images/technologies-used.svg align="left")

Ready to start your first project? Then head to the Projects section.
