Scripting Your Digital World: Python is King!

A few years ago, some of our students used to question why they did Python. But now, they often love it, especially as they see its…

Scripting Your Digital World: Python is King!

A few years ago, some of our students used to question why they did Python. But now, they often love it, especially as they see its relevance. Few job roles within a technology field will avoid its usage, so go and learn it, and script your digital world.

So let’s take an example of setting up a DNS and email service in the cloud, and see how we can use Python to make all this work.

Taking control of DNS and Email

Sometimes in your work environment, you get yourself a jolt and disrupt something. I’ve done it for years, and it normally ends up with something new and with new opportunities. For me, I want to script everything related to my work, and have things ready so that I can minimise the time I spend of the overhead of manually processing things. One problem area is DNS and associated domains, as I have them often in different domain hosts (1&1 and GoDaddy). But it wants to take back control, and be in a similar place I was before, where I’d be able to control the registration and the sending and receiving of emails.

DNS

And so the DNS hosting using Route 53 it was quest easy to transfer an existing domain from GoDaddy:

Simple Email Service (SES)

With SES, there was no cumbersome consoles to deal this — have you used the GoDaddy DNS infrastructure? — and things seem to happen almost in an instance. Next, it was setting up the email address on the domain. For this, we turn to SES (Simple Email Service) — as email, in itself, is a fair simple protocol: you send with SMTP and you receive with your client. With SES, you basically add a domain, and if it’s in Route 53, it is an easy task:

You then verify the email addresses who will be sending:

The email addresses stay pending until you verify the email address through an email sent to the account. I found I could then add several other ones who would be trusted — as much as SMTP can be ever trusted — to send emails.

Now I had to set up a user for my SMTP, for which I just clicked on the “Create My SMTP Credentials” and it gave my login details for SMTP:

This gave me a new user:

And in Outlook, it’s easy to set up a new SMTP account with the new user:

Setting up receiving rules

So, sending email was fine, but what about receiving it? Well, email is rather old and clunk in its creation of a post office where your emails are organised, but most people now have an email client to do these things. So, we now focus on getting the emails into S3 storage and then forward them to a defined email address. First, we set up a new email receiving ruleset:

We then create an S3 bucket for the email and take note of its name:

and remember the name of the bucket and its region. And now we need a script to forward the email from the bucket to the external address. For this we turn to Lambda functions.

You normally chose between Node.js or Python, but I selected Python for the task:

I took the standard script for this, and rather than attaching a MIME-encoded (EML) file to the forwarded email, I just send the actual message:

# Copyright 2010–2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# This file is licensed under the Apache License, Version 2.0 (the “License”).
# You may not use this file except in compliance with the License. A copy of the
# License is located at
#
# http://aws.amazon.com/apache2.0/
#
# This file is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS
# OF ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import os
import boto3
import email
import re
from botocore.exceptions import ClientError
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
region = os.environ[‘Region’]
def get_message_from_s3(message_id):
incoming_email_bucket = os.environ[‘MailS3Bucket’]
object_path = message_id
object_http_path = (f”http://s3.console.aws.amazon.com/s3/object/{incoming_email_bucket}/{object_path}?region={region}")
# Create a new S3 client.
client_s3 = boto3.client(“s3”)
# Get the email object from the S3 bucket.
object_s3 = client_s3.get_object(Bucket=incoming_email_bucket,
Key=object_path)
# Read the content of the message.
file = object_s3[‘Body’].read()
file_dict = {
“file”: file,
“path”: object_http_path
}
return file_dict
def create_message(file_dict):
sender = os.environ[‘MailSender’]
recipient = os.environ[‘MailRecipient’]
separator = “;”
# Parse the email body.
mailobject = email.message_from_string(file_dict[‘file’].decode(‘utf-8’))
# Create a new subject line.
subject_original = mailobject[‘Subject’]
subject = subject_original
msg = MIMEMultipart()
msg.attach(mailobject)
# Add subject, from and to lines.
msg[‘Subject’] = subject
msg[‘From’] = sender
msg[‘To’] = recipient
message = {
“Source”: sender,
“Destinations”: recipient,
“Data”: msg.as_string()
}
return message
def send_email(message):
aws_region = os.environ[‘Region’]
# Create a new SES client.
client_ses = boto3.client(‘ses’, region)
# Send the email.
try:
#Provide the contents of the email.
response = client_ses.send_raw_email(
Source=message[‘Source’],
Destinations=[
message[‘Destinations’]
],
RawMessage={
‘Data’:message[‘Data’]
}
)
# Display an error if something goes wrong.
except ClientError as e:
output = e.response[‘Error’][‘Message’]
else:
output = “Email sent! Message ID: “ + response[‘MessageId’]
return output
def lambda_handler(event, context):
# Get the unique ID of the message. This corresponds to the name of the file
# in S3.
message_id = event[‘Records’][0][‘ses’][‘mail’][‘messageId’]
print(f”Received message ID {message_id}”)
# Retrieve the file from the S3 bucket.
file_dict = get_message_from_s3(message_id)
# Create the message.
message = create_message(file_dict)
# Send the email and print the result.
result = send_email(message)
print(result)

We then have five external variables, and give the name of the email recipient, the name of the bucket we set up, the sender email and the region:

We then add the two actions for S3 storage and the email forwarding:

But that won’t work, and why we look in the log, there will be two errors generated. The first is that the script does not have the permissions to write to the S3 folder, and the second is that the Lambda function will not have the rights either to read it. So we do off to IAM to find the SMTP user we setup:

Our SMTP user should have SES sending rights:

Now, we find our Lambda function, and we need to add some extra roles. The default one is to execute but add one for S3 access, and another SES access:

And that’s it. Send an email to the email address you’ve created, and the email will be stored in S3, and then forwarded to your main email client. The Lambda function cloud logs were fantastic for debugging the code. I now have control of my domain and the email, and there are no more Web consoles for logins for 1&1 and GoDaddy.

I now have the start of a platform for automation, and I’m aiming to build a whole new teaching environment on this.