<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[vadymkhodak]]></title><description><![CDATA[vadymkhodak]]></description><link>https://blog.vadymkhodak.com</link><generator>RSS for Node</generator><lastBuildDate>Sat, 18 Apr 2026 00:11:24 GMT</lastBuildDate><atom:link href="https://blog.vadymkhodak.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Creating AWS resources using Boto3 for deploying Django project]]></title><description><![CDATA[This is a bonus blog post to Django with AWS Lambda series. If you are a Python developer and you need to create AWS resources but you don't want to learn Terraform or use AWS Management Console this blog post is for you. 
Boto3 Introduction
We will ...]]></description><link>https://blog.vadymkhodak.com/creating-aws-resources-using-boto3-for-deploying-django-project</link><guid isPermaLink="true">https://blog.vadymkhodak.com/creating-aws-resources-using-boto3-for-deploying-django-project</guid><category><![CDATA[Python]]></category><category><![CDATA[Django]]></category><category><![CDATA[AWS]]></category><category><![CDATA[infrastructure]]></category><dc:creator><![CDATA[Vadym Khodak]]></dc:creator><pubDate>Sat, 11 Sep 2021 21:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1631438832333/ZfjwMFak5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is a bonus blog post to <a target="_blank" href="https://blog.vadymkhodak.com/series/django-with-aws-lambda">Django with AWS Lambda</a> series. If you are a Python developer and you need to create AWS resources but you don't want to learn <a target="_blank" href="https://blog.vadymkhodak.com/deploying-a-django-project-on-aws-lambda-using-serverless-part-3">Terraform</a> or use <a target="_blank" href="https://blog.vadymkhodak.com/deploy-django-app-on-aws-lambda-using-serverless-part-2">AWS Management Console</a> this blog post is for you. </p>
<h2 id="boto3-introduction">Boto3 Introduction</h2>
<p>We will use <a target="_blank" href="https://boto3.amazonaws.com/v1/documentation/api/latest/index.html">Boto3 - the AWS SDK for Python</a>. Boto3 makes it easy to integrate your Python application, library, or script with AWS services including Amazon S3, Amazon EC2, Amazon DynamoDB, and more.</p>
<p>Boto3 has two levels of APIs:</p>
<ul>
<li><a target="_blank" href="https://boto3.amazonaws.com/v1/documentation/api/latest/guide/clients.html">Client (or "low-level") APIs</a> provide one-to-one mappings to the underlying HTTP API operations.</li>
<li><a target="_blank" href="https://boto3.amazonaws.com/v1/documentation/api/latest/guide/resources.html">Resource APIs</a> hide explicit network calls but instead provide resource objects and collections to access attributes and perform actions.</li>
</ul>
<h2 id="lets-start-codding">Let's start codding</h2>
<p>First, we need to install <a target="_blank" href="https://pypi.org/project/boto3/"><code>boto3</code> python library</a>:</p>
<pre><code class="lang-bash">pip install boto3
</code></pre>
<blockquote>
<p>I used version <code>1.18.40</code> of <code>boto3</code> which was the latest version on the day of writing this post. It is always better to use the latest version of <code>boto3</code> as the AWS team is actively working on this library.</p>
</blockquote>
<p>Second (optional), we can install <a target="_blank" href="https://pypi.org/project/python-dotenv/"><code>python-dotenv</code> python library</a> to read environment variables from <code>.env</code> file:</p>
<pre><code class="lang-bash">pip install python-dotenv
</code></pre>
<blockquote>
<p>This step is optional, but I do recommend using environment variables for any sensitive or configurable parameters like passwords, AWS credentials, and such.</p>
</blockquote>
<p>Third (if you decided to use environment variables), we should create <code>.env</code> file with the following variables:</p>
<pre><code>AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION_NAME=

DEFAULT_VPC_ID=
DB_INSTANCE_IDENTIFIER=
RDS_DB_NAME=
RDS_USERNAME=
RDS_PASSWORD=
S3_BUCKET_NAME=
</code></pre><blockquote>
<p>You can find your DEFAULT_VPC_ID using <a target="_blank" href="https://console.aws.amazon.com/vpc/home?region=us-east-1#vpcs:">AWS Management Console</a></p>
</blockquote>
<p>Fourth, we need to create Python file where we will write our code to create all the necessary AWS resources for deploying a Django project on AWS Lambda.</p>
<pre><code class="lang-bash">touch django_aws_resources.py
</code></pre>
<h2 id="using-boto3-the-aws-sdk-for-pythonhttpsboto3amazonawscomv1documentationapilatestindexhtml">Using <a target="_blank" href="https://boto3.amazonaws.com/v1/documentation/api/latest/index.html">Boto3 - the AWS SDK for Python</a></h2>
<p>First, we need to import all libraries we are going to use, load the environment variables, and set up some helpful variables.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json <span class="hljs-comment"># to dump Python object with S3 bucket policy to JSON string</span>
<span class="hljs-keyword">import</span> os  <span class="hljs-comment"># to get the necessary environment variables  </span>
<span class="hljs-keyword">import</span> time  <span class="hljs-comment"># to wait until AWS RDS instance will be created</span>
<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime  <span class="hljs-comment"># to generate a unique timestamp for unique CallerReference</span>

<span class="hljs-keyword">import</span> boto3
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv

load_dotenv()

S3_BUCKET_NAME = os.environ[<span class="hljs-string">"S3_BUCKET_NAME"</span>]
REGION_NAME = os.getenv(<span class="hljs-string">"AWS_REGION_NAME"</span>) <span class="hljs-keyword">or</span> <span class="hljs-string">"us-east-1"</span>
</code></pre>
<p>Second, we should create a <a target="_blank" href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.ServiceResource.create_security_group">SecurityGroup</a> and add <a target="_blank" href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.SecurityGroup.authorize_ingress">inbound</a> and <a target="_blank" href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.SecurityGroup.authorize_egress">outbound</a> rules:</p>
<pre><code class="lang-python">ec2_resource = boto3.resource(<span class="hljs-string">"ec2"</span>, region_name=REGION_NAME)

security_group = ec2_resource.create_security_group(
    Description=<span class="hljs-string">"sg-for-lambdas"</span>,
    GroupName=<span class="hljs-string">"django-rds-security-group"</span>,
    VpcId=os.environ[<span class="hljs-string">"DEFAULT_VPC_ID"</span>],
    TagSpecifications=[
        {
            <span class="hljs-string">"ResourceType"</span>: <span class="hljs-string">"security-group"</span>,
            <span class="hljs-string">"Tags"</span>: [
                {<span class="hljs-string">"Key"</span>: <span class="hljs-string">"Name"</span>, <span class="hljs-string">"Value"</span>: <span class="hljs-string">"django-demo-rds-security-group"</span>},
            ],
        },
    ],
    DryRun=<span class="hljs-literal">False</span>,
)

_ = security_group.authorize_egress(
    DryRun=<span class="hljs-literal">False</span>,
    IpPermissions=[
        {
            <span class="hljs-string">"FromPort"</span>: <span class="hljs-number">0</span>,
            <span class="hljs-string">"IpProtocol"</span>: <span class="hljs-string">"-1"</span>,
            <span class="hljs-string">"IpRanges"</span>: [],
            <span class="hljs-string">"Ipv6Ranges"</span>: [
                {<span class="hljs-string">"CidrIpv6"</span>: <span class="hljs-string">"::/0"</span>, <span class="hljs-string">"Description"</span>: <span class="hljs-string">"allow all (demo only)"</span>},
            ],
            <span class="hljs-string">"PrefixListIds"</span>: [],
            <span class="hljs-string">"ToPort"</span>: <span class="hljs-number">0</span>,
        },
    ],
    TagSpecifications=[
        {
            <span class="hljs-string">"ResourceType"</span>: <span class="hljs-string">"security-group-rule"</span>,
            <span class="hljs-string">"Tags"</span>: [
                {<span class="hljs-string">"Key"</span>: <span class="hljs-string">"Name"</span>, <span class="hljs-string">"Value"</span>: <span class="hljs-string">"egress rule"</span>},
            ],
        },
    ],
)

_ = security_group.authorize_ingress(
    DryRun=<span class="hljs-literal">False</span>,
    IpPermissions=[
        {
            <span class="hljs-string">"FromPort"</span>: <span class="hljs-number">0</span>,
            <span class="hljs-string">"IpProtocol"</span>: <span class="hljs-string">"-1"</span>,
            <span class="hljs-string">"IpRanges"</span>: [
                {<span class="hljs-string">"CidrIp"</span>: <span class="hljs-string">"0.0.0.0/0"</span>, <span class="hljs-string">"Description"</span>: <span class="hljs-string">"allow all (demo only)"</span>},
            ],
            <span class="hljs-string">"Ipv6Ranges"</span>: [
                {<span class="hljs-string">"CidrIpv6"</span>: <span class="hljs-string">"::/0"</span>, <span class="hljs-string">"Description"</span>: <span class="hljs-string">"allow all (demo only)"</span>},
            ],
            <span class="hljs-string">"PrefixListIds"</span>: [],
            <span class="hljs-string">"ToPort"</span>: <span class="hljs-number">0</span>,
        },
    ],
    TagSpecifications=[
        {
            <span class="hljs-string">"ResourceType"</span>: <span class="hljs-string">"security-group-rule"</span>,
            <span class="hljs-string">"Tags"</span>: [
                {<span class="hljs-string">"Key"</span>: <span class="hljs-string">"Name"</span>, <span class="hljs-string">"Value"</span>: <span class="hljs-string">"ingress rule"</span>},
            ],
        },
    ],
)
</code></pre>
<p>Third, we need to create an <a target="_blank" href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rds.html#RDS.Client.create_db_instance">RDS instance</a> with Postgres engine:</p>
<pre><code class="lang-python">rds_client = boto3.client(<span class="hljs-string">"rds"</span>, region_name=REGION_NAME)
_ = rds_client.create_db_instance(
    DBName=os.environ[<span class="hljs-string">"RDS_DB_NAME"</span>],
    DBInstanceIdentifier=os.environ[<span class="hljs-string">"DB_INSTANCE_IDENTIFIER"</span>],
    AllocatedStorage=<span class="hljs-number">20</span>,
    DBInstanceClass=<span class="hljs-string">"db.t2.micro"</span>,
    Engine=<span class="hljs-string">"postgres"</span>,
    EngineVersion=<span class="hljs-string">"12.5"</span>,
    MasterUsername=os.environ[<span class="hljs-string">"RDS_USERNAME"</span>],
    MasterUserPassword=os.environ[<span class="hljs-string">"RDS_PASSWORD"</span>],
    VpcSecurityGroupIds=[security_group.id],
    Tags=[{<span class="hljs-string">"Key"</span>: <span class="hljs-string">"name"</span>, <span class="hljs-string">"Value"</span>: <span class="hljs-string">"django_demo_rds"</span>}],
)
</code></pre>
<p>Fourth, we should create an <a target="_blank" href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.create_bucket">S3 bucket</a> for static files:</p>
<pre><code class="lang-python">s3_client = boto3.client(<span class="hljs-string">"s3"</span>, region_name=REGION_NAME)
_ = s3_client.create_bucket(
    ACL=<span class="hljs-string">"private"</span>,
    Bucket=S3_BUCKET_NAME,
)
</code></pre>
<p>Fifth, we need to create a <a target="_blank" href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudfront.html#CloudFront.Client.create_cloud_front_origin_access_identity">CloudFront Origin Access Identity</a> and update the <a target="_blank" href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.BucketPolicy.put">S3 bucket policy</a> to allow a CloudFront distribution serving static files from the bucket:</p>
<pre><code class="lang-python">cloudfront_client = boto3.client(<span class="hljs-string">"cloudfront"</span>, region_name=REGION_NAME)

response = cloudfront_client.create_cloud_front_origin_access_identity(
    CloudFrontOriginAccessIdentityConfig={
        <span class="hljs-string">"CallerReference"</span>: str(datetime.utcnow().timestamp()),
        <span class="hljs-string">"Comment"</span>: <span class="hljs-string">f'access-identity-<span class="hljs-subst">{S3_BUCKET_NAME}</span>.s3.amazonaws.com"'</span>,
    }
)
origin_access_identity_id = response[<span class="hljs-string">"CloudFrontOriginAccessIdentity"</span>][<span class="hljs-string">"Id"</span>]

s3_resource = boto3.resource(<span class="hljs-string">"s3"</span>)
bucket_policy = s3_resource.BucketPolicy(S3_BUCKET_NAME)
_ = bucket_policy.put(
    Policy=json.dumps(
        {
            <span class="hljs-string">"Version"</span>: <span class="hljs-string">"2008-10-17"</span>,
            <span class="hljs-string">"Statement"</span>: [
                {
                    <span class="hljs-string">"Sid"</span>: <span class="hljs-string">"1"</span>,
                    <span class="hljs-string">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
                    <span class="hljs-string">"Principal"</span>: {
                        <span class="hljs-string">"AWS"</span>: <span class="hljs-string">f"arn:aws:iam::cloudfront:user/CloudFront "</span>
                        <span class="hljs-string">f"Origin Access Identity <span class="hljs-subst">{origin_access_identity_id}</span>"</span>,
                    },
                    <span class="hljs-string">"Action"</span>: <span class="hljs-string">"s3:GetObject"</span>,
                    <span class="hljs-string">"Resource"</span>: <span class="hljs-string">f"arn:aws:s3:::<span class="hljs-subst">{S3_BUCKET_NAME}</span>/*"</span>,
                }
            ],
        }
    ),
)
</code></pre>
<p>Sixth, we need to create a <a target="_blank" href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudfront.html#CloudFront.Client.create_distribution">CloudFront Distribution</a> to serve static files from the S3 bucket</p>
<pre><code class="lang-python">cloudfront_distribution_response = cloudfront_client.create_distribution(
    DistributionConfig={
        <span class="hljs-string">"CallerReference"</span>: str(datetime.utcnow().timestamp()),
        <span class="hljs-string">"Origins"</span>: {
            <span class="hljs-string">"Quantity"</span>: <span class="hljs-number">1</span>,
            <span class="hljs-string">"Items"</span>: [
                {
                    <span class="hljs-string">"Id"</span>: S3_BUCKET_NAME,
                    <span class="hljs-string">"DomainName"</span>: <span class="hljs-string">f"<span class="hljs-subst">{S3_BUCKET_NAME}</span>.s3.amazonaws.com"</span>,
                    <span class="hljs-string">"S3OriginConfig"</span>: {
                        <span class="hljs-string">"OriginAccessIdentity"</span>: <span class="hljs-string">f"origin-access-identity/cloudfront/<span class="hljs-subst">{origin_access_identity_id}</span>"</span>
                    },
                },
            ],
        },
        <span class="hljs-string">"Restrictions"</span>: {<span class="hljs-string">"GeoRestriction"</span>: {<span class="hljs-string">"RestrictionType"</span>: <span class="hljs-string">"none"</span>, <span class="hljs-string">"Quantity"</span>: <span class="hljs-number">0</span>}},
        <span class="hljs-string">"ViewerCertificate"</span>: {
            <span class="hljs-string">"CloudFrontDefaultCertificate"</span>: <span class="hljs-literal">True</span>,
        },
        <span class="hljs-string">"DefaultCacheBehavior"</span>: {
            <span class="hljs-string">"TargetOriginId"</span>: S3_BUCKET_NAME,
            <span class="hljs-string">"Compress"</span>: <span class="hljs-literal">True</span>,
            <span class="hljs-string">"ViewerProtocolPolicy"</span>: <span class="hljs-string">"allow-all"</span>,
            <span class="hljs-string">"AllowedMethods"</span>: {
                <span class="hljs-string">"Quantity"</span>: <span class="hljs-number">3</span>,
                <span class="hljs-string">"Items"</span>: [<span class="hljs-string">"GET"</span>, <span class="hljs-string">"HEAD"</span>, <span class="hljs-string">"OPTIONS"</span>],
                <span class="hljs-string">"CachedMethods"</span>: {
                    <span class="hljs-string">"Quantity"</span>: <span class="hljs-number">2</span>,
                    <span class="hljs-string">"Items"</span>: [<span class="hljs-string">"GET"</span>, <span class="hljs-string">"HEAD"</span>],
                },
            },
            <span class="hljs-string">"ForwardedValues"</span>: {
                <span class="hljs-string">"QueryString"</span>: <span class="hljs-literal">False</span>,
                <span class="hljs-string">"Cookies"</span>: {
                    <span class="hljs-string">"Forward"</span>: <span class="hljs-string">"none"</span>,
                },
            },
            <span class="hljs-string">"MinTTL"</span>: <span class="hljs-number">0</span>,
            <span class="hljs-string">"DefaultTTL"</span>: <span class="hljs-number">3600</span>,
            <span class="hljs-string">"MaxTTL"</span>: <span class="hljs-number">86400</span>,
        },
        <span class="hljs-string">"Enabled"</span>: <span class="hljs-literal">True</span>,
        <span class="hljs-string">"IsIPV6Enabled"</span>: <span class="hljs-literal">True</span>,
        <span class="hljs-string">"DefaultRootObject"</span>: <span class="hljs-string">"index.html"</span>,
        <span class="hljs-string">"Comment"</span>: <span class="hljs-string">"Django React static distribution"</span>,
    }
)
</code></pre>
<p>Then, we can wait until the RDS instance will be created to get the Database Host Name for Django project configurations:</p>
<pre><code class="lang-python"><span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
    rds_db_instances = rds_client.describe_db_instances(
        DBInstanceIdentifier=<span class="hljs-string">"string"</span>,
        Filters=[
            {<span class="hljs-string">"Name"</span>: <span class="hljs-string">"db-instance-id"</span>, <span class="hljs-string">"Values"</span>: [os.environ[<span class="hljs-string">"DB_INSTANCE_IDENTIFIER"</span>]]},
        ],
        MaxRecords=<span class="hljs-number">20</span>,
    )

    <span class="hljs-keyword">if</span> rds_db_instances[<span class="hljs-string">"DBInstances"</span>][<span class="hljs-number">0</span>].get(<span class="hljs-string">"Endpoint"</span>):
        db_host_name = rds_db_instances[<span class="hljs-string">"DBInstances"</span>][<span class="hljs-number">0</span>][<span class="hljs-string">"Endpoint"</span>][<span class="hljs-string">"Address"</span>]
        <span class="hljs-keyword">break</span>
    time.sleep(<span class="hljs-number">100</span>)
</code></pre>
<p>Finally, we can print Database Host Name and CloudFront Distribution Domain Name to use them in Django project configurations:</p>
<pre><code class="lang-python">print(<span class="hljs-string">f"Database Host Name: <span class="hljs-subst">{db_host_name}</span>"</span>)
print(<span class="hljs-string">f"CloudFront Domain Name: <span class="hljs-subst">{cloudfront_distribution_response[<span class="hljs-string">'Distribution'</span>][<span class="hljs-string">'DomainName'</span>]}</span>"</span>)
</code></pre>
<blockquote>
<p>Note, that this is an example of configuring AWS resources. Your production configuration can be different.</p>
</blockquote>
<h2 id="final-words">Final Words</h2>
<p>Today we saw one more way of how to prepare AWS infrastructure for a Django project. You can find a full version of the code showed here in <a target="_blank" href="https://github.com/vadym-khodak/django-aws-infra-boto3">this GitHub repository</a>.</p>
<p>Don't forget to follow me on Twitter <a target="_blank" href="https://twitter.com/vadim_khodak">@vadim_khodak</a> or on <a target="_blank" href="https://www.linkedin.com/in/vadym-khodak-0b1a05149/">LinkedIn</a> so you do not miss the next posts.</p>
]]></content:encoded></item><item><title><![CDATA[Deploying a Django project on AWS Lambda using Serverless (Part 4)]]></title><description><![CDATA[As I promised in my previous blog post Deploying a Django project on AWS Lambda using Serverless (Part 3), I'd like to show you how to add a React.JS client to a Django project and deploy it with Django on AWS Lambda using Serverless.
BLUF
Django fra...]]></description><link>https://blog.vadymkhodak.com/deploying-a-django-project-on-aws-lambda-using-serverless-part-4</link><guid isPermaLink="true">https://blog.vadymkhodak.com/deploying-a-django-project-on-aws-lambda-using-serverless-part-4</guid><category><![CDATA[Django]]></category><category><![CDATA[React]]></category><category><![CDATA[aws lambda]]></category><dc:creator><![CDATA[Vadym Khodak]]></dc:creator><pubDate>Thu, 19 Aug 2021 08:29:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1628672675662/0ZxSzOq1b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As I promised in my previous blog post <a target="_blank" href="https://blog.vadymkhodak.com/deploying-a-django-project-on-aws-lambda-using-serverless-part-3">Deploying a Django project on AWS Lambda using Serverless (Part 3)</a>, I'd like to show you how to add a React.JS client to a Django project and deploy it with Django on AWS Lambda using Serverless.</p>
<h2 id="blufhttpsbitly38hbfuc"><a target="_blank" href="https://bit.ly/38hbFUC">BLUF</a></h2>
<p>Django framework allows you to build a client using Django templates, but there are a lot of cases when this is not enough. Business requirements for the client-side of your application could require adding more complex logic on the client-side. In these cases, we will not be able to solve business problems without a JavaScript web framework (React.JS, Vue.JS, Angular, etc). I'd like to show you how to build a simple React.JS client and integrate it with a Django project using <a target="_blank" href="https://www.npmjs.com/package/axios"><code>axios</code></a> library on the client-side and <a target="_blank" href="https://pypi.org/project/djangorestframework/">Django REST Framework</a> on the server-side.</p>
<p>With this approach, I will build a React.JS client with a domain name of CloudFront distribution as a PUBLIC_URL and store it on AWS S3 bucket together with Django static files. Then, I add the built <code>index.html</code> file with React.JS app to Django templates folder and deploy it with the Django project on AWS Lambda. </p>
<h2 id="getting-started">Getting started</h2>
<p>I've already described how to create a Django project and deploy it on AWS Lambda using Serverless in <a target="_blank" href="https://blog.vadymkhodak.com/deploy-django-app-on-aws-lambda-using-serverless-part-1">my first blog post</a> of this series. I will use that project to get started. </p>
<p>Let's go through this process step by step:</p>
<ul>
<li>Clone the Django repository I used in <a target="_blank" href="https://github.com/vadym-khodak/django-aws-lambda">the first part of these series</a> and go to the cloned repository:</li>
</ul>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/vadym-khodak/django-aws-lambda
<span class="hljs-built_in">cd</span> django-aws-lambda
</code></pre>
<ul>
<li>Follow instructions from <a target="_blank" href="https://blog.vadymkhodak.com/deploy-django-app-on-aws-lambda-using-serverless-part-1">this blog post</a> to run the Django server (install requirements, configure environment variables using <code>.env</code>, apply migrations, create a superuser, collect static, run the server).</li>
</ul>
<p>If everything works, you are ready to start working on the client.</p>
<h2 id="add-reactjs-client">Add React.JS client</h2>
<p>In this part, I'll show how to create a simple React.JS client and integrate it with our Django project, but I'm sure that you can easily use Vue.JS (I'm not familiar with Angular) as steps pretty much the same.</p>
<p>There are many options to create React.JS client. I'm using <a target="_blank" href="https://www.npmjs.com/package/react-scripts"><code>react-scripts</code></a> in this example.</p>
<ul>
<li>Install <a target="_blank" href="https://www.npmjs.com/package/react-scripts"><code>react-scripts</code></a> using <a target="_blank" href="https://www.npmjs.com/"><code>npm</code></a> (Node package manager)</li>
</ul>
<pre><code class="lang-bash">npm install -g react-scripts
</code></pre>
<ul>
<li>Use react-scripts to initialize a React.JS client</li>
</ul>
<pre><code class="lang-bash">npm init react-app client
</code></pre>
<ul>
<li>Check that React.JS client was built successfully</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> client
npm run start
</code></pre>
<p>It will open your browser on <code>localhost</code> port <code>3000</code> and you will see a page like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628417085242/Wn168Hb1y.png" alt="image.png" /></p>
<h2 id="update-django-project-configuration">Update Django project configuration</h2>
<p>Let's update our Django configuration to render <code>index.html</code> file with React.JS client:</p>
<ul>
<li>Add  <code>CLIENT_DIR</code> environment variable to <code>.env</code> file:</li>
</ul>
<pre><code><span class="hljs-attr">CLIENT_DIR</span>=<span class="hljs-string">"client/build"</span>
</code></pre><ul>
<li>Update <code>django_aws_lambda/urls.py</code> file with the following code:</li>
</ul>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.contrib <span class="hljs-keyword">import</span> admin
<span class="hljs-keyword">from</span> django.urls <span class="hljs-keyword">import</span> path, include
<span class="hljs-keyword">from</span> django.views.generic <span class="hljs-keyword">import</span> TemplateView


urlpatterns = [
    path(<span class="hljs-string">'admin/'</span>, admin.site.urls),
    path(<span class="hljs-string">'hello/'</span>, include(<span class="hljs-string">'hello.urls'</span>)),
    path(<span class="hljs-string">''</span>, TemplateView.as_view(template_name=<span class="hljs-string">"index.html"</span>), {<span class="hljs-string">'resource'</span>: <span class="hljs-string">''</span>}),
    path(<span class="hljs-string">'&lt;path:resource&gt;'</span>, TemplateView.as_view(template_name=<span class="hljs-string">"index.html"</span>)),
]
</code></pre>
<ul>
<li>Update <code>STATICFILES_DIRS</code> in <code>django_aws_lambda/settings.py</code></li>
</ul>
<pre><code class="lang-python">STATICFILES_DIRS = [
    str(ROOT_DIR / env(<span class="hljs-string">'CLIENT_DIR'</span>, default=<span class="hljs-string">'client/build'</span>)),
    str(ROOT_DIR / <span class="hljs-string">'static'</span>),
]
</code></pre>
<h2 id="check-that-our-django-project-can-run-the-reactjs-client">Check that our Django project can run the React.JS client</h2>
<ul>
<li>Build production React.JS client locally the following commands:</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> client
<span class="hljs-built_in">export</span> PUBLIC_URL=static/
npm run build
<span class="hljs-built_in">cd</span> ..
</code></pre>
<ul>
<li>Collect static files by running the following command:</li>
</ul>
<pre><code class="lang-bash">python manage.py collectstatic --no-input
</code></pre>
<ul>
<li>Run this Django project locally (make sure that you have already applied migrations and created a superuser):</li>
</ul>
<pre><code class="lang-bash">python manage.py runserver
</code></pre>
<ul>
<li>Go to your browser, open this URL <a target="_blank" href="http://localhost:8000">http://localhost:8000</a> and you will see a page like this:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628429290188/jESwmYqu2.png" alt="image.png" /></p>
<p>It looks the same as we saw before by running <code>npm run start</code> in <code>client</code> folder. There are just a couple of differences - now it is run on port <code>8000</code> and it is run by our Django web server.</p>
<h2 id="make-the-reactjs-client-talk-to-the-django-server">Make the React.JS client talk to the Django server.</h2>
<p>First, we need to create an API endpoint to return some data from the server to the client. The easiest way to build REST API in Django is using <a target="_blank" href="https://pypi.org/project/djangorestframework/">Django REST framework</a> project.</p>
<ul>
<li>Install Django REST framework and add it to <code>requirements.txt</code> file</li>
</ul>
<pre><code class="lang-bash">pip install djangorestframework
</code></pre>
<ul>
<li>Create a new Django app called <code>users</code> by running the following command:</li>
</ul>
<pre><code class="lang-bash">python manage.py startapp users
</code></pre>
<ul>
<li>Update <code>users/views.py</code> file with the following code:</li>
</ul>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> rest_framework <span class="hljs-keyword">import</span> status
<span class="hljs-keyword">from</span> rest_framework.decorators <span class="hljs-keyword">import</span> api_view
<span class="hljs-keyword">from</span> rest_framework.response <span class="hljs-keyword">import</span> Response


<span class="hljs-meta">@api_view(["GET"])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_current_user</span>(<span class="hljs-params">request</span>):</span>
    <span class="hljs-keyword">return</span> Response(status=status.HTTP_200_OK, data={<span class="hljs-string">"username"</span>: request.user.username})
</code></pre>
<blockquote>
<p>This is an endpoint that returns Response object with <code>username</code> (if the user is not authorized it will return an empty string as <code>username</code>)</p>
</blockquote>
<ul>
<li>Update <code>users/urls.py</code> file with the following code:</li>
</ul>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.urls <span class="hljs-keyword">import</span> path

<span class="hljs-keyword">from</span> .views <span class="hljs-keyword">import</span> get_current_user

app_name = <span class="hljs-string">"users"</span>
urlpatterns = [
    path(<span class="hljs-string">"me/"</span>, view=get_current_user, name=<span class="hljs-string">"get-current-user"</span>),
]
</code></pre>
<ul>
<li>Update our Django project configuration</li>
</ul>
<p>Update <code>django_aws_lambda/urls.py</code> file with the following code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.contrib <span class="hljs-keyword">import</span> admin
<span class="hljs-keyword">from</span> django.urls <span class="hljs-keyword">import</span> path, include
<span class="hljs-keyword">from</span> django.views.generic <span class="hljs-keyword">import</span> TemplateView


urlpatterns = [
    path(<span class="hljs-string">'admin/'</span>, admin.site.urls),
    path(<span class="hljs-string">'hello/'</span>, include(<span class="hljs-string">'hello.urls'</span>)),
    path(<span class="hljs-string">'api/users/'</span>, include(<span class="hljs-string">'users.urls'</span>)),
    path(<span class="hljs-string">''</span>, TemplateView.as_view(template_name=<span class="hljs-string">"index.html"</span>), {<span class="hljs-string">'resource'</span>: <span class="hljs-string">''</span>}),
    path(<span class="hljs-string">'&lt;path:resource&gt;'</span>, TemplateView.as_view(template_name=<span class="hljs-string">"index.html"</span>)),
]
</code></pre>
<ul>
<li>Update <code>INSTALLED_APPS</code> in <code>django_aws_lambda/settings.py</code></li>
</ul>
<pre><code class="lang-python">INSTALLED_APPS = [
    <span class="hljs-string">'django.contrib.admin'</span>,
    <span class="hljs-string">'django.contrib.auth'</span>,
    <span class="hljs-string">'django.contrib.contenttypes'</span>,
    <span class="hljs-string">'django.contrib.sessions'</span>,
    <span class="hljs-string">'django.contrib.messages'</span>,
    <span class="hljs-string">'django.contrib.staticfiles'</span>,
    <span class="hljs-string">'hello'</span>,
    <span class="hljs-string">'rest_framework'</span>,
    <span class="hljs-string">'users'</span>,
]
</code></pre>
<h3 id="modifying-reactjs-client-to-send-requests-to-the-django-server">Modifying React.JS client to send requests to the Django server</h3>
<ul>
<li>Install <code>axios</code> library:</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> client
npm install axios -S
</code></pre>
<blockquote>
<p><code>-S</code> flag will add <code>axios</code> library to <code>package.json</code> file</p>
</blockquote>
<ul>
<li>Update <code>client/src/App.js</code> file with the following code:</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>;
<span class="hljs-keyword">import</span> logo <span class="hljs-keyword">from</span> <span class="hljs-string">'./logo.svg'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./App.css'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> loadUserDetails = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">'/api/users/me/'</span>);
    <span class="hljs-keyword">return</span> response.data;
  };
  <span class="hljs-keyword">const</span> [userData, setUserData] = useState(<span class="hljs-literal">false</span>);
  useEffect(<span class="hljs-function">() =&gt;</span> {
    loadUserDetails().then(<span class="hljs-function">(<span class="hljs-params">payload</span>) =&gt;</span> {
      setUserData(payload);
    });
  }, []);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App-header"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{logo}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App-logo"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"logo"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
          Edit <span class="hljs-tag">&lt;<span class="hljs-name">code</span>&gt;</span>src/App.js<span class="hljs-tag">&lt;/<span class="hljs-name">code</span>&gt;</span> and save to reload.
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"App-link"</span>
          <span class="hljs-attr">href</span>=<span class="hljs-string">"https://reactjs.org"</span>
          <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>
          <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener noreferrer"</span>
        &gt;</span>
          Learn React
        <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Hello, World!<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>I'm {(userData &amp;&amp; userData.username) || 'Unknown User'}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<ul>
<li>Build production optimized client by running the following command:</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> PUBLIC_URL=static/
npm run build
<span class="hljs-built_in">cd</span> ..
</code></pre>
<ul>
<li>Collect static files by running the following command:</li>
</ul>
<pre><code class="lang-bash">python manage.py collectstatic --no-input
</code></pre>
<ul>
<li>Run this Django project locally:</li>
</ul>
<pre><code class="lang-bash">python manage.py runserver
</code></pre>
<p>Go to your browser, open this URL <a target="_blank" href="http://localhost:8000">http://localhost:8000</a> and you will see a page like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628435119388/Wv7F81VzgE.png" alt="image.png" /></p>
<p>But if you authorize using your superuser username and password you will see a page like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628435553669/m4_GAmqiR.png" alt="image.png" /></p>
<h2 id="deploying-on-aws-lambda-using-serverless">Deploying on AWS Lambda using Serverless</h2>
<h4 id="prepare-aws-infrastructure">Prepare AWS infrastructure</h4>
<p>I've already described how to prepare AWS infrastructure in my previous blog posts, so you can use one of the following approaches: </p>
<ul>
<li>Prepare AWS infrastructure manually as it is described in <a target="_blank" href="https://blog.vadymkhodak.com/deploying-a-django-project-on-aws-lambda-using-serverless-part-3">Deploying a Django project on AWS Lambda using Serverless (Part 3)</a> blog post</li>
<li>Prepare AWS infrastructure automatically using terraform as it is described in <a target="_blank" href="https://blog.vadymkhodak.com/deploy-django-app-on-aws-lambda-using-serverless-part-2">Deploying a Django project on AWS Lambda using Serverless (Part 2)</a> blog post</li>
</ul>
<h4 id="update-serverless-configuration">Update Serverless configuration</h4>
<p>Add <code>client</code> folder to <code>package.exclude</code> to exclude it from deployment</p>
<h4 id="update-url-in-clientsrcappjs-file-to-be-able-to-send-requests-to-production-server">Update URL in <code>client/src/App.js</code> file to be able to send requests to production server</h4>
<pre><code class="lang-javascript">    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">'/production/api/users/me/'</span>);
</code></pre>
<h4 id="use-docker-for-deploying-your-django-project-to-aws-lambda-using-serverless">Use Docker for deploying your Django project to AWS Lambda using Serverless</h4>
<ul>
<li>Prepare Amazon Linux 2 docker image with all the necessary dependencies:</li>
</ul>
<pre><code class="lang-bash">docker run -it -v $(<span class="hljs-built_in">pwd</span>):/root/src/ -v /Users/&lt;your-username&gt;/.aws:/root/.aws amazonlinux:latest bash
<span class="hljs-comment"># install the necessary Unix dependencies:</span>
yum install sudo -y
sudo yum install -y gcc openssl-devel bzip2-devel libffi-devel wget tar sqlite-devel gcc-c++ make
<span class="hljs-comment"># install node.js version 14:</span>
curl -sL https://rpm.nodesource.com/setup_14.x | sudo -E bash - 
sudo yum install -y nodejs
<span class="hljs-comment"># install Python 3.8.7:</span>
<span class="hljs-built_in">cd</span> /opt
sudo wget https://www.python.org/ftp/python/3.8.7/Python-3.8.7.tgz
sudo tar xzf Python-3.8.7.tgz
<span class="hljs-built_in">cd</span> Python-3.8.7
sudo ./configure --enable-optimizations
sudo make altinstall
sudo rm -f /opt/Python-3.8.7.tgz
<span class="hljs-comment"># create python and pip aliases:</span>
<span class="hljs-built_in">alias</span> python=<span class="hljs-string">'python3.8'</span>
<span class="hljs-built_in">alias</span> pip=<span class="hljs-string">'pip3.8'</span>
<span class="hljs-comment"># update pip and setuptools:</span>
pip install --upgrade pip setuptools
<span class="hljs-comment"># install serverless:</span>
npm install -g serverless
<span class="hljs-comment"># move to project directory</span>
<span class="hljs-built_in">cd</span> /root/src/
<span class="hljs-comment"># install requirements inside docker container:</span>
pip install -r requirements.txt
<span class="hljs-comment"># set the necessary environment variables</span>
<span class="hljs-built_in">export</span> DJANGO_SETTINGS_MODULE=django_react_aws_lambda.production
<span class="hljs-built_in">export</span> AWS_ACCESS_KEY_ID=&lt;your-aws-access-key-id&gt;
<span class="hljs-built_in">export</span> AWS_SECRET_ACCESS_KEY=&lt;your-aws-secret-access-key&gt;
<span class="hljs-comment"># migrate database changes</span>
python manage.py migrate
<span class="hljs-comment"># create a superuser in the database</span>
python manage.py createsuperuser
<span class="hljs-comment"># build React.JS client for AWS Lambda</span>
<span class="hljs-built_in">cd</span> client
npm install 
<span class="hljs-built_in">export</span> PUBLIC_URL=<span class="hljs-string">"https://&lt;your-cloud-front-distribution&gt;.cloudfront.net/static/"</span>
npm run build
<span class="hljs-comment"># copy `index.html` from `client/build` to `templates`</span>
cp build/index.html ../templates/index.html
<span class="hljs-built_in">cd</span> ..
<span class="hljs-comment"># collect static files to AWS S3 bucket</span>
python manage.py collectstatic  --no-input
<span class="hljs-comment"># install serverless packages from package.json</span>
npm install
<span class="hljs-comment"># deploy your Django project to AWS Lambda using Serverless</span>
serverless deploy -s production
</code></pre>
<p>Now, your Django Project with React.JS client will be available at this URL: 
<code>https://&lt;some-id&gt;.execute-api.&lt;your-aws-region&gt;.amazonaws.com/production</code></p>
<p>Without authorized user:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628443592093/wlm7XuMpl.png" alt="image.png" /></p>
<p>With authorized user:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628443889989/Nc23jbKJez.png" alt="image.png" /></p>
<h2 id="how-it-works">How it works</h2>
<p>When you go to the Django project URL on your browser it will go to AWS API Gateway that will trigger AWS Lambda function with the Django WSGI server. The Django server will render <code>index.html</code>  with React.JS app, the browser will use the domain name of the CloudFront distribution to get React.JS client from the AWS S3 bucket.</p>
<h2 id="final-words">Final Words</h2>
<p>In this blog post, we saw how to add the React.JS client to the Django project and deploy them on AWS Lambda using Serverless. There is a link to the <a target="_blank" href="https://github.com/vadym-khodak/django-react-aws-lambda">GitHub repository</a> (<a target="_blank" href="https://gitlab.com/vadym-khodak/django-react-aws-lambda">GitLab copy</a>) with the code shown in this blog post.</p>
<p>It is the final blog post in this series. I showed just one of many ways to deploy a Django project on AWS Lambda, prepare AWS infrastructure, and add a React.JS client. You can find many other ways to do the same, it is up to you which approach to use.</p>
<p>Don't forget to follow me on Twitter <a target="_blank" href="https://twitter.com/vadim_khodak">@vadim_khodak</a> or on <a target="_blank" href="https://www.linkedin.com/in/vadym-khodak-0b1a05149/">LinkedIn</a> so you do not miss the next posts.</p>
]]></content:encoded></item><item><title><![CDATA[Deploying a Django project on AWS Lambda using Serverless (Part 3)]]></title><description><![CDATA[To follow up my previous blog post I decided to show you how to update existing and create new AWS resources for a Django project using Terraform (infrastructure-as-code).
Terraform is an open-source infrastructure as code software tool that provides...]]></description><link>https://blog.vadymkhodak.com/deploying-a-django-project-on-aws-lambda-using-serverless-part-3</link><guid isPermaLink="true">https://blog.vadymkhodak.com/deploying-a-django-project-on-aws-lambda-using-serverless-part-3</guid><category><![CDATA[Django]]></category><category><![CDATA[AWS]]></category><category><![CDATA[infrastructure]]></category><category><![CDATA[Terraform]]></category><dc:creator><![CDATA[Vadym Khodak]]></dc:creator><pubDate>Thu, 20 May 2021 10:42:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1621506340268/i8ZwZzx8H.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>To follow up my <a target="_blank" href="https://blog.vadymkhodak.com/deploy-django-app-on-aws-lambda-using-serverless-part-2">previous blog post</a> I decided to show you how to update existing and create new AWS resources for a Django project using <a target="_blank" href="https://www.terraform.io/">Terraform</a> (infrastructure-as-code).</p>
<p><a target="_blank" href="https://www.terraform.io/">Terraform</a> is an open-source infrastructure as code software tool that provides a consistent CLI workflow to manage hundreds of cloud services. Terraform codifies cloud APIs into declarative configuration files.</p>
<p>Terraform is a powerful tool that helps to define your AWS infrastructure as code. Using it, you can:</p>
<ul>
<li>Have a single source of truth for infrastructure definition and configuration.</li>
<li>Easily replicate AWS infrastructure for other environments or from one AWS account to another one.</li>
<li>Add the necessary updates to your AWS resources.</li>
<li>Store all changes for your AWS resources in a version control system and vet them through a code review process.</li>
<li>Define AWS infrastructure in a human-readable way.</li>
</ul>
<p>In my <a target="_blank" href="https://blog.vadymkhodak.com/deploy-django-app-on-aws-lambda-using-serverless-part-2">previous blog post</a>, you can see how to prepare AWS infrastructure manually via  <a target="_blank" href="https://console.aws.amazon.com/console/home">AWS console</a>, now, let's take a look at how we can use Terraform to prepare all the necessary AWS resources for a Django project.</p>
<h2 id="install-and-initialize-terraform-cli">Install and initialize Terraform CLI</h2>
<p><strong>Step 1</strong>: <a target="_blank" href="https://learn.hashicorp.com/tutorials/terraform/install-cli">Install Terraform CLI</a></p>
<ul>
<li>macOS:</li>
</ul>
<pre><code class="lang-bash">brew tap hashicorp/tap
brew install hashicorp/tap/terraform
</code></pre>
<ul>
<li>Linux (Ubuntu/Debian)</li>
</ul>
<pre><code class="lang-bash">sudo apt-get update &amp;&amp; sudo apt-get install -y gnupg software-properties-common curl
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository <span class="hljs-string">"deb [arch=amd64] https://apt.releases.hashicorp.com <span class="hljs-subst">$(lsb_release -cs)</span> main"</span>
sudo apt-get update &amp;&amp; sudo apt-get install terraform
</code></pre>
<p>Check Terraform CLI installation:</p>
<pre><code class="lang-bash">terraform --<span class="hljs-built_in">help</span>
</code></pre>
<p>The output should be like that:</p>
<pre><code class="lang-text">Usage: terraform [-version] [-help] &lt;command&gt; [args]

The available commands for execution are listed below.
The most common, useful commands are shown first, followed by
less common or more advanced commands. If you're just getting
started with Terraform, stick with the common commands. For the
other commands, please read the help and docs before usage.
</code></pre>
<p><strong>Step 2</strong>: Initialize your working directory using terraform CLI</p>
<ul>
<li>Go to your project and create <code>terraform</code> folder </li>
</ul>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> django-aws-lambda
mkdir terraform
<span class="hljs-built_in">cd</span> terraform
</code></pre>
<ul>
<li>Create <code>main.tf</code> file inside <code>terraform</code> folder with the following lines:</li>
</ul>
<pre><code><span class="hljs-section">terraform</span> {
  <span class="hljs-section">required_providers</span> {
    <span class="hljs-attribute">aws</span> = {
      <span class="hljs-attribute">source</span>  = <span class="hljs-string">"hashicorp/aws"</span>
      version = <span class="hljs-string">"~&gt; 3.37"</span>
    }
  }
  required_version = <span class="hljs-string">"&gt;= 0.15.0"</span>
}

provider <span class="hljs-string">"aws"</span> {
  <span class="hljs-attribute">region</span>     = <span class="hljs-string">"us-east-1"</span>
}
</code></pre><ul>
<li>Initialize Terraform CLI</li>
</ul>
<pre><code class="lang-bash">terraform init
</code></pre>
<p>The output should be like that:</p>
<pre><code class="lang-text">Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
</code></pre>
<h2 id="import-and-update-existing-aws-resources-using-terraform-cli">Import and update existing AWS resources using Terraform CLI</h2>
<p><strong>Step 3</strong>: Add VPC resource and import it</p>
<ul>
<li>Create <code>vpc.tf</code> file inside <code>terraform</code> folder with the following code:</li>
</ul>
<pre><code><span class="hljs-attribute">resource</span> <span class="hljs-string">"aws_vpc"</span> <span class="hljs-string">"default"</span> {
  <span class="hljs-attribute">cidr_block</span>           = <span class="hljs-string">"172.31.0.0/16"</span>
  enable_dns_hostnames = <span class="hljs-literal">true</span>
  enable_dns_support   = <span class="hljs-literal">true</span>
  instance_tenancy     = <span class="hljs-string">"default"</span>
}
</code></pre><ul>
<li>Export your AWS credentials:</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> AWS_ACCESS_KEY_ID=&lt;YOUR_ AWS_ACCESS_KEY_ID&gt;
<span class="hljs-built_in">export</span> AWS_SECRET_ACCESS_KEY=&lt;YOUR_ AWS_SECRET_ACCESS_KEY&gt;
</code></pre>
<ul>
<li>Import existing default VPC:</li>
</ul>
<pre><code class="lang-bash">terraform import aws_vpc.default &lt;your-vpc-id&gt;
</code></pre>
<p>You should go to your <a target="_blank" href="https://console.aws.amazon.com/console/home">AWS console</a> to get your VPC id (make sure that you use the right region)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1621493361388/ByNY6Ehoa.png" alt="image.png" /></p>
<p><strong>Step 4</strong>: Add a Security Groups resource and import it</p>
<ul>
<li>Create <code>security_groups.tf</code> file inside <code>terraform</code> folder with the following code:</li>
</ul>
<pre><code><span class="hljs-attribute">resource</span> <span class="hljs-string">"aws_security_group"</span> <span class="hljs-string">"default"</span> {
  <span class="hljs-attribute">name</span>        = <span class="hljs-string">"default"</span>
  description = <span class="hljs-string">"default VPC security group"</span>
  vpc_id      = aws_vpc.default.id

  ingress {
    <span class="hljs-attribute">from_port</span>       = <span class="hljs-number">0</span>
    to_port         = <span class="hljs-number">0</span>
    protocol        = <span class="hljs-string">"-1"</span>
    security_groups = []
    self            = <span class="hljs-literal">true</span>
  }

  egress {
    <span class="hljs-attribute">from_port</span>   = <span class="hljs-number">0</span>
    to_port     = <span class="hljs-number">0</span>
    protocol    = <span class="hljs-string">"-1"</span>
    cidr_blocks = [<span class="hljs-string">"0.0.0.0/0"</span>]
  }

}
</code></pre><ul>
<li>Import the existing default Security Group:</li>
</ul>
<pre><code class="lang-bash">terraform import aws_security_group.default &lt;your-security-group-id&gt;
</code></pre>
<p><strong>Step 5</strong>: Check infrastructure changes and apply them</p>
<ul>
<li>Run <code>terraform plan</code> command to check changes to be applied for your AWS infrastructure. If there is nothing to change your output should be like this:</li>
</ul>
<pre><code class="lang-text">aws_vpc.default: Refreshing state... [id=vpc-&lt;some-id&gt;]
aws_security_group.default: Refreshing state... [id=sg-&lt;some-id&gt;]

No changes. Infrastructure is up-to-date.

This means that Terraform did not detect any differences between your configuration and the remote system(s). As a result, there are no actions to take.
</code></pre>
<ul>
<li>Add inbound and outbound rules for PostgreSQL database in <code>security_groups.tf</code> (add the following lines inside <code>aws_security_groups</code> resource)</li>
</ul>
<pre><code>  <span class="hljs-section">ingress</span> {
    <span class="hljs-attribute">from_port</span> = <span class="hljs-number">5432</span>
    to_port   = <span class="hljs-number">5432</span>
    protocol  = <span class="hljs-string">"tcp"</span>
    cidr_blocks = [<span class="hljs-string">"0.0.0.0/0"</span>]
    ipv6_cidr_blocks = [<span class="hljs-string">"::/0"</span>]
  }

  egress {
    <span class="hljs-attribute">from_port</span>        = <span class="hljs-number">5432</span>
    to_port          = <span class="hljs-number">5432</span>
    protocol         = <span class="hljs-string">"tcp"</span>
    cidr_blocks      = [<span class="hljs-string">"0.0.0.0/0"</span>]
    ipv6_cidr_blocks = [<span class="hljs-string">"::/0"</span>]
  }
</code></pre><ul>
<li>Run <code>terraform plan</code> command to check changes again. Now, the output should look like that:</li>
</ul>
<pre><code class="lang-text">aws_vpc.default: Refreshing state... [id=vpc-&lt;some-id&gt;]
aws_security_group.default: Refreshing state... [id=sg-&lt;some-id&gt;]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_security_group.default will be updated in-place
  ~ resource "aws_security_group" "default" {
      ~ egress                 = [
          - {
              - cidr_blocks      = [
                  - "0.0.0.0/0",
                ]
              - description      = ""
              - from_port        = 0
              - ipv6_cidr_blocks = []
              - prefix_list_ids  = []
              - protocol         = "-1"
              - security_groups  = []
              - self             = false
              - to_port          = 0
            },
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = ""
              + from_port        = 5432
              + ipv6_cidr_blocks = [
                  + "::/0",
                ]
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 5432
            },
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = null
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
            },
        ]
        id                     = "sg-5d23015f"
      ~ ingress                = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = ""
              + from_port        = 5432
              + ipv6_cidr_blocks = [
                  + "::/0",
                ]
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 5432
            },
          - {
              - cidr_blocks      = []
              - description      = ""
              - from_port        = 0
              - ipv6_cidr_blocks = []
              - prefix_list_ids  = []
              - protocol         = "-1"
              - security_groups  = []
              - self             = true
              - to_port          = 0
            },
          + {
              + cidr_blocks      = []
              + description      = null
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = true
              + to_port          = 0
            },
        ]
        name                   = "default"
        tags                   = {}
        # (6 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.

─────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
</code></pre>
<p>You can see in this output that existing rules are going to be deleted and new rules are going to be added. If it is what you expected you are ready to apply all these changes.</p>
<ul>
<li>Run <code>terraform apply</code> command to apply all infrastructure changes to resources in your AWS account. It will show you all changes again and will ask you to confirm your changes before applying them.</li>
</ul>
<pre><code class="lang-text">Plan: 0 to add, 1 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes
</code></pre>
<h2 id="create-new-aws-resources-using-terraform-cli">Create new AWS resources using Terraform CLI</h2>
<p><strong>Step 6</strong>: Create <code>variables.tf</code> and <code>outputs.tf</code> files</p>
<ul>
<li>Create <code>variables.tf</code> file inside <code>terraform</code> folder with the following lines:</li>
</ul>
<pre><code>variable <span class="hljs-string">"REGION"</span> {
  <span class="hljs-keyword">type</span>    = <span class="hljs-keyword">string</span>
  <span class="hljs-keyword">default</span> = <span class="hljs-string">"us-east-1"</span>
}

variable <span class="hljs-string">"RDS_DB_NAME"</span> {
  <span class="hljs-keyword">type</span> = <span class="hljs-keyword">string</span>
}

variable <span class="hljs-string">"RDS_USERNAME"</span> {
  <span class="hljs-keyword">type</span> = <span class="hljs-keyword">string</span>
}

variable <span class="hljs-string">"RDS_PASSWORD"</span> {
  <span class="hljs-keyword">type</span> = <span class="hljs-keyword">string</span>
}

variable <span class="hljs-string">"S3_ORIGIN_ID"</span> {
  <span class="hljs-keyword">type</span>    = <span class="hljs-keyword">string</span>
  <span class="hljs-keyword">default</span> = <span class="hljs-string">"django-react-static-assets"</span>
}
</code></pre><ul>
<li>Update <code>main.tf</code> file to use AWS region from environment variable:</li>
</ul>
<pre><code><span class="hljs-section">terraform</span> {
  <span class="hljs-section">required_providers</span> {
    <span class="hljs-attribute">aws</span> = {
      <span class="hljs-attribute">source</span>  = <span class="hljs-string">"hashicorp/aws"</span>
      version = <span class="hljs-string">"~&gt; 3.37"</span>
    }
  }
  required_version = <span class="hljs-string">"&gt;= 0.15.0"</span>
}

provider <span class="hljs-string">"aws"</span> {
  <span class="hljs-attribute">region</span>     = var.REGION
}
</code></pre><p>Terraform variables allow us to use environment variables to protect secrets (like passwords) and apply the same infrastructure for different stages, regions, etc.</p>
<p>You should export these variables in your OS with adding <code>TF_VAR_</code> before variable names, for example:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> TF_VAR_REGION=<span class="hljs-string">"us-east-1"</span>
<span class="hljs-built_in">export</span> TF_VAR_RDS_DB_NAME=<span class="hljs-string">"django_aws"</span>
<span class="hljs-built_in">export</span> TF_VAR_RDS_USERNAME=<span class="hljs-string">"&lt;your-database-master-username&gt;"</span>
<span class="hljs-built_in">export</span> TF_VAR_RDS_PASSWORD=<span class="hljs-string">"&lt;your-database-master-password&gt;"</span>
<span class="hljs-built_in">export</span> TF_VAR_S3_ORIGIN_ID=<span class="hljs-string">"&lt;your-s3-bucket-name&gt;"</span>
</code></pre>
<ul>
<li>Create <code>outputs.tf</code> file inside <code>terraform</code> folder with the following lines:</li>
</ul>
<pre><code>output "cf_domain" {
  <span class="hljs-keyword">value</span> = aws_cloudfront_distribution.static_distribution.domain_name
}
output "db_domain" {
  <span class="hljs-keyword">value</span> = aws_db_instance.django-aws.address
}
output "exec_lambda_role" {
  <span class="hljs-keyword">value</span> = aws_iam_role.exec_lambda.arn
}
output "security_group_id" {
  <span class="hljs-keyword">value</span> = aws_security_group.<span class="hljs-keyword">default</span>.id
}
</code></pre><p><strong>Step 6</strong>: Create an RDS resource</p>
<ul>
<li>Create <code>rds.tf</code> file inside <code>terraform</code> folder with the following lines:</li>
</ul>
<pre><code>resource "aws_db_instance" "django-aws" {
  identifier                 = "django-aws"
  allocated_storage          = <span class="hljs-number">20</span>
  storage_type               = "gp2"
  engine                     = "postgres"
  engine_version             = "12.5"
  instance_class             = "db.t2.micro"
  <span class="hljs-type">name</span>                       = var.RDS_DB_NAME
  username                   = "postgres"
  <span class="hljs-keyword">password</span>                   = var.RDS_PASSWORD
  port                       = <span class="hljs-number">5432</span>
  publicly_accessible        = <span class="hljs-keyword">true</span>
  availability_zone          = "us-east-1a"
  security_group_names       = []
  vpc_security_group_ids     = [aws_security_group.<span class="hljs-keyword">default</span>.id]
  parameter_group_name       = "default.postgres12"
  multi_az                   = <span class="hljs-keyword">false</span>
  backup_retention_period    = <span class="hljs-number">0</span>
  backup_window              = "10:05-10:35"
  maintenance_window         = "sun:07:47-sun:08:17"
  auto_minor_version_upgrade = <span class="hljs-keyword">false</span>
  copy_tags_to_snapshot      = <span class="hljs-keyword">true</span>
  skip_final_snapshot        = <span class="hljs-keyword">true</span>
}
</code></pre><p><strong>Step 7</strong>: Create S3 resources</p>
<ul>
<li>Create <code>s3.tf</code> file inside <code>terraform</code> folder with the following lines to create S3 bucket, block public access, and S3 bucket policy resources:</li>
</ul>
<pre><code><span class="hljs-attribute">resource</span> <span class="hljs-string">"aws_s3_bucket"</span> <span class="hljs-string">"static-assets-django-react"</span> {
  <span class="hljs-attribute">bucket</span> = var.S3_ORIGIN_ID
  acl = <span class="hljs-string">"public-read"</span>
  force_destroy = <span class="hljs-literal">true</span>
}

resource <span class="hljs-string">"aws_s3_bucket_public_access_block"</span> <span class="hljs-string">"public_access_block"</span> {
  <span class="hljs-attribute">bucket</span> = aws_s3_bucket.static-assets-django-react.id

  ignore_public_acls = <span class="hljs-literal">false</span>
  restrict_public_buckets = <span class="hljs-literal">false</span>
  block_public_acls   = <span class="hljs-literal">false</span>
  block_public_policy = <span class="hljs-literal">false</span>
}

resource <span class="hljs-string">"aws_s3_bucket_policy"</span> <span class="hljs-string">"app_static"</span> {
  <span class="hljs-attribute">bucket</span> = aws_s3_bucket.static-assets-django-react.id
  policy = jsonencode({
    <span class="hljs-attribute">Version</span> = <span class="hljs-string">"2008-10-17"</span>
    Id      = <span class="hljs-string">"PolicyForCloudFrontPrivateContent"</span>
    Statement = [
      {
        <span class="hljs-attribute">Sid</span>    = <span class="hljs-string">"1"</span>
        Effect = <span class="hljs-string">"Allow"</span>
        Principal = {
          <span class="hljs-attribute">AWS</span> = <span class="hljs-string">"arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <span class="hljs-variable">${aws_cloudfront_origin_access_identity.origin_access_identity.id}</span>"</span>
        },
        Action   = <span class="hljs-string">"s3:GetObject"</span>,
        Resource = <span class="hljs-string">"<span class="hljs-variable">${aws_s3_bucket.static-assets-django-react.arn}</span>/*"</span>
      }
    ]
  })
}
</code></pre><p><strong>Step 8</strong>: Create CloudFront resources</p>
<ul>
<li>Create <code>cloud_front.tf</code> file inside <code>terraform</code> folder with the following lines to create CloudFront distribution, and CloudFront Origin Access Identity resources:</li>
</ul>
<pre><code><span class="hljs-attribute">resource</span> <span class="hljs-string">"aws_cloudfront_origin_access_identity"</span> <span class="hljs-string">"origin_access_identity"</span> {
  <span class="hljs-attribute">comment</span> = <span class="hljs-string">"access-identity-<span class="hljs-variable">${var.S3_ORIGIN_ID}</span>.s3.amazonaws.com"</span>
}

resource <span class="hljs-string">"aws_cloudfront_distribution"</span> <span class="hljs-string">"static_distribution"</span> {
  <span class="hljs-section">origin</span> {
    <span class="hljs-attribute">domain_name</span> = aws_s3_bucket.static-assets-django-react.bucket_regional_domain_name
    origin_id   = var.S3_ORIGIN_ID
    s3_origin_config {
      <span class="hljs-attribute">origin_access_identity</span> = aws_cloudfront_origin_access_identity.origin_access_identity.cloudfront_access_identity_path
    }
  }

  enabled             = <span class="hljs-literal">true</span>
  is_ipv6_enabled     = <span class="hljs-literal">true</span>
  comment             = <span class="hljs-string">"Some Comment"</span>

  default_cache_behavior {
    <span class="hljs-attribute">allowed_methods</span>  = [<span class="hljs-string">"GET"</span>, <span class="hljs-string">"HEAD"</span>, <span class="hljs-string">"OPTIONS"</span>]
    cached_methods   = [<span class="hljs-string">"GET"</span>, <span class="hljs-string">"HEAD"</span>]
    compress         = <span class="hljs-literal">false</span>
    target_origin_id = <span class="hljs-string">"S3-<span class="hljs-variable">${var.S3_ORIGIN_ID}</span>"</span>

    forwarded_values {
      <span class="hljs-attribute">query_string</span> = <span class="hljs-literal">false</span>

      cookies {
        <span class="hljs-attribute">forward</span> = <span class="hljs-string">"none"</span>
      }
    }

    viewer_protocol_policy = <span class="hljs-string">"redirect-to-https"</span>
    min_ttl                = <span class="hljs-number">0</span>
    default_ttl            = <span class="hljs-number">0</span>
    max_ttl                = <span class="hljs-number">0</span>
  }

  restrictions {
    <span class="hljs-section">geo_restriction</span> {
      <span class="hljs-attribute">restriction_type</span> = <span class="hljs-string">"none"</span>
    }
  }

  viewer_certificate {
    <span class="hljs-attribute">cloudfront_default_certificate</span> = <span class="hljs-literal">true</span>
  }
}
</code></pre><p><strong>Step 9</strong>: Create IAM resources</p>
<ul>
<li>Create <code>iam</code> file inside <code>terraform</code> folder with the following lines to create IAM Role, IAM Policy, and Attach Policy to the Role resources:</li>
</ul>
<pre><code>resource "aws_iam_role" "exec_lambda" {
  <span class="hljs-type">name</span> = "exec_lambda"
  <span class="hljs-type">path</span> = "/"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    <span class="hljs-keyword">Statement</span> = [
      {
        Effect = "Allow"
        Principal = {
          Service = [
            "lambda.amazonaws.com"
          ]
        },
        Action = "sts:AssumeRole"
      },
    ]
  })
}
resource "aws_iam_policy" "exec_lambda_policy" {
  <span class="hljs-type">name</span>        = "exec_lambda_policy"
  description = "Lambda policy"

  <span class="hljs-keyword">policy</span> = jsonencode({
    Version = "2012-10-17"
    <span class="hljs-keyword">Statement</span> = [
      {
        Effect = "Allow",
        Action = [
          "lambda:InvokeFunction",
        ],
        Resource = "*"
      },
    ]
  })
}

resource "aws_iam_role_policy_attachment" "attach_role" {
  <span class="hljs-keyword">role</span>       = aws_iam_role.exec_lambda.name
  policy_arn = aws_iam_policy.exec_lambda_policy.arn
}
</code></pre><p><strong>Step 10</strong>: Apply all changes to create the necessary AWS resources</p>
<ul>
<li>Run <code>terraform plan</code> to check changes that will be applied.</li>
<li>Run <code>terraform apply</code> to create all the necessary resources. </li>
</ul>
<p>As a result, you will have a long output with all logs and custom output we configured in <code>outputs.tf</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1621505723962/eIP7oiCE6.png" alt="image.png" /></p>
<p>You should use these outputs in your Django and Serverless configurations.</p>
<h2 id="final-advice">Final Advice</h2>
<p>I'd like to highlight that this is just an example, so your production configuration can be different. You need to be sure that your infrastructure configuration satisfies your security policy.</p>
<p>There is some advice:</p>
<ul>
<li><p>Use a version control system to track changes in your infrastructure and for Code Review.</p>
</li>
<li><p>Don't store your terraform state in GitHub, GitLab, Bitbucket, etc.</p>
</li>
<li><p>Configure an S3 bucket as a backend for terraform, to enable collaboration:</p>
</li>
</ul>
<pre><code><span class="hljs-section">terraform</span> {
  <span class="hljs-section">required_providers</span> {
    <span class="hljs-attribute">aws</span> = {
      <span class="hljs-attribute">source</span>  = <span class="hljs-string">"hashicorp/aws"</span>
      version = <span class="hljs-string">"~&gt; 3.37"</span>
    }
  }
  required_version = <span class="hljs-string">"&gt;= 0.15.0"</span>

  backend <span class="hljs-string">"s3"</span> {
    <span class="hljs-attribute">region</span>         = var.REGION
    bucket         = var.BECKEND_BUCKET_NAME
    key            = var.STATE_KEY
    dynamodb_table = var.DYNAMO_DB_TABLE
    encrypt        = <span class="hljs-literal">true</span>
  }
}

provider <span class="hljs-string">"aws"</span> {
  <span class="hljs-attribute">region</span> = var.REGION
}
</code></pre><h2 id="final-words">Final words</h2>
<p>I was inspired by <a target="_blank" href="https://twitter.com/mykyta_p">Mykyta Protsenko's</a> talk <a target="_blank" href="https://www.youtube.com/watch?v=Z1lVPUxUtzo">"Infrastructure-as-code: evolving tools vs core principles"</a>  to write this blog post. Thanks a lot to Mykyta for all your help and support.</p>
<p>I'll show how to add a simple React.JS client and serve it from an S3 bucket using a CloudFront distribution in my next blog post. Follow me on Twitter <a target="_blank" href="https://twitter.com/vadim_khodak">@vadim_khodak</a> or on <a target="_blank" href="https://www.linkedin.com/in/vadym-khodak-0b1a05149/">LinkedIn</a> so you do not miss the next posts.</p>
]]></content:encoded></item><item><title><![CDATA[Deploying a Django project on AWS Lambda using Serverless (Part 2)]]></title><description><![CDATA[After my previous blog post Deploy Django App on AWS Lambda using Serverless (Part 1), people started asking me about AWS infrastructure for Django projects, so I decided to share my experience with this. 
It is not obvious (for people who don't have...]]></description><link>https://blog.vadymkhodak.com/deploy-django-app-on-aws-lambda-using-serverless-part-2</link><guid isPermaLink="true">https://blog.vadymkhodak.com/deploy-django-app-on-aws-lambda-using-serverless-part-2</guid><category><![CDATA[AWS]]></category><category><![CDATA[Django]]></category><category><![CDATA[infrastructure]]></category><dc:creator><![CDATA[Vadym Khodak]]></dc:creator><pubDate>Mon, 10 May 2021 11:21:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1620801311462/8gYMUQ4Cu.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>After my previous blog post <a target="_blank" href="https://blog.vadymkhodak.com/deploy-django-app-on-aws-lambda-using-serverless-part-1">Deploy Django App on AWS Lambda using Serverless (Part 1)</a>, people started asking me about AWS infrastructure for Django projects, so I decided to share my experience with this. </p>
<p>It is not obvious (for people who don't have enough experience with AWS resources) how to create and configure all the necessary AWS resources to deploy a Django project on AWS Lambda using Serverless. </p>
<p>Here are a list of ways of how to do this:</p>
<ul>
<li>manually via the <a target="_blank" href="https://console.aws.amazon.com/console/home">AWS console</a></li>
<li>automatically using <a target="_blank" href="https://www.terraform.io/">Terraform</a></li>
<li>automatically using <a target="_blank" href="https://aws.amazon.com/sdk-for-python/">AWS SDK</a></li>
</ul>
<p>In this blog post, I will show you how to do it manually via <a target="_blank" href="https://console.aws.amazon.com/console/home">AWS console</a>.</p>
<h2 id="update-configuration-for-existing-aws-resource-and-create-new-ones">Update configuration for existing AWS resource and create new ones</h2>
<p><strong>Step 1</strong>: Create your own AWS account (if you don't have one).
Here is <a target="_blank" href="https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/">a link to a manual for creating and activating an AWS account</a>.</p>
<p><strong>Step 2</strong>: Go to the <a target="_blank" href="https://console.aws.amazon.com/console/home">AWS Management Console</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620288323328/TCd6I26My.png" alt="image.png" /></p>
<p><strong>Step 3</strong>: Select your region. In my case, it is <code>US East (N. Virginia)us-east-1</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620288299714/EkwdprPdu.png" alt="image.png" /></p>
<p><strong>Step 4</strong>: Update <code>Security Group</code> with rules for AWS RDS service (PostgreSQL)</p>
<ul>
<li><p><em>Type <code>EC2</code> in the search bar and click on the <code>EC2</code> service in the search results</em>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620288343122/4e3Ofc0bz.png" alt="image.png" /></p>
</li>
<li><p><em>Choose the <code>Security Groups</code> option in the <code>Network &amp; Security</code> section</em></p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620288629215/A0S055gKa.png" alt="image.png" /></p>
<ul>
<li><em>Click on your security group id</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620291117330/thbXdrOe7.png" alt="image.png" /></p>
<ul>
<li><em>Click on <code>Edit inbound rules</code></em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620291210595/1ZpUcgchc.png" alt="image.png" /></p>
<ul>
<li><em>Click on <code>Add rule</code>. Then, select the <code>PostgreSQL</code> option in the <code>Type</code> column. Next, choose the <code>Anywhere</code> option in the <code>Source</code> column. Finally, click on the <code>Save rules</code> button</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620291384959/G3ebMKnBF.png" alt="image.png" /></p>
<ul>
<li><em>Go to the <code>Outbound rules</code> tab and add the same rules that were described in the previous section</em></li>
</ul>
<p><strong>Step 5</strong>: Create an <code>IAM role</code></p>
<ul>
<li><em>Type <code>IAM</code> in the search bar and click on the <code>IAM</code> service in the search results</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620636425458/fMGrWm7Zk.png" alt="image.png" /></p>
<ul>
<li><em>Click on <code>Roles</code> in the <code>IAM</code> side bar or in the <code>IAM dashboard</code></em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620637568031/JKTygqDJC.png" alt="image.png" /></p>
<ul>
<li><em>Click on the <code>Create Role</code> button</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620637765372/SUNLHVwFu.png" alt="image.png" /></p>
<ul>
<li><em>Select <code>AWS service</code>, <code>Lambda</code>, and click on the <code>Next: Permissions</code> button</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620637743531/QqZrUg1hS.png" alt="image.png" /></p>
<ul>
<li><em>Type <code>Lambda</code> in the search bar, select the <code>AWSLambda_FullAccess</code> policy, click on the <code>Next: Tags</code> button</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620637936495/IOrzQTI9V.png" alt="image.png" /></p>
<ul>
<li><em>Click on the <code>Next: Review</code> button</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620638106114/FgVR_NPJk.png" alt="image.png" /></p>
<ul>
<li><em>Type <code>Role name</code>, <code>Role description</code>, and click on the <code>Create role</code> button</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620638260257/bjTiOQvxS.png" alt="image.png" /></p>
<ul>
<li><em>Click on the created role name</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620641663209/GntQz6Uj6.png" alt="image.png" /></p>
<ul>
<li><em>Click on the <code>Copy Role ARN</code> button</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620641765824/wd4rQAKez.png" alt="image.png" /></p>
<p>Next, you should add the role ARN to the Serverless configuration directly or using environment variables (for example <code>.env</code> file)</p>
<pre><code class="lang-env">ROLE=arn:aws:iam::&lt;your-aws-account-id&gt;:role/exec_lambda
</code></pre>
<p><strong>Step 6</strong>: Create <code>S3 buckets</code> for static assets and deployment</p>
<ul>
<li><em>Type <code>S3</code> in the search bar and click on the <code>S3</code> service in the search results</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620295754837/J0C9fvFW-.png" alt="image.png" /></p>
<ul>
<li><em>Click on the <code>Create bucket</code> button</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620295871356/4l5nTIeEx.png" alt="image.png" /></p>
<ul>
<li><em>Type <code>Bucket name</code>, select your <code>AWS region</code>, unselect <code>Block all public access</code> and click on the <code>Create bucket</code> button</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620634116004/-0qbct0w7.png" alt="image.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620296394230/SjSIH3d8T.png" alt="image.png" /></p>
<p>Then, you should repeat all the steps mentioned above to create an S3 bucket for deployment.</p>
<p>Next, you should add your bucket names to your Django and Serverless configurations directly or using environment variables (for example <code>.env</code> file)</p>
<pre><code class="lang-env">AWS_STORAGE_BUCKET_NAME='django-react-static-assets'
DEPLOYMENT_BUCKET='django-react-deployments'
</code></pre>
<p><strong>Step 7</strong>: Create a <code>CloudFront distribution</code></p>
<ul>
<li><em>Type <code>CloudFront</code> in the search bar and click on the <code>CloudFront</code> service in the search results</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620296521232/jO-AFfk-R.png" alt="image.png" /></p>
<ul>
<li><em>Click on the <code>Create Distribution</code> button</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620296615377/2VeGX_DXW.png" alt="image.png" /></p>
<ul>
<li><em>Click on the <code>Get started</code> button</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620296729397/-wU2n9y4c.png" alt="image.png" /></p>
<ul>
<li><em>Select your <code>S3 bucket</code></em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620296828608/hBX8FuIPr.png" alt="image.png" /></p>
<ul>
<li><em>Select: <code>Yes</code> for <code>Restrict Bucket Access</code>, <code>Create a New Identity</code> for <code>Origin Access Identity</code>, <code>Yes, Update Bucket Policy</code> for <code>Grant Read Permissions on Bucket</code>, <code>HTTP and HTTPS</code> for <code>Viewer Protocol Policy</code></em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620297821541/KbWjnM1pd.png" alt="image.png" /></p>
<ul>
<li><em>Type "some comment" (optional) and click on the <code>Create Distribution</code> button</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620297924642/D5GAxVcSz.png" alt="image.png" /></p>
<ul>
<li><em>Go to the distributions list and copy the <code>Domain Name</code></em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620298241028/-pYeM6nmu.png" alt="image.png" /></p>
<p>Then, you should add CloudFront distribution <code>Domain Name</code> to your Django and Serverless configurations directly or using environment variables (for example <code>.env</code> file)</p>
<pre><code class="lang-env">AWS_S3_CDN_DOMAIN="&lt;domain-id&gt;.cloudfront.net"
</code></pre>
<p><strong>Step 8</strong>: Create <code>RDS</code></p>
<ul>
<li><em>Type <code>RDS</code> in the search bar and click on the <code>RDS</code> service in the search results</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620298335720/H-ysDK1f9.png" alt="image.png" /></p>
<ul>
<li><em>Click on the <code>Create database</code> button</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620552244393/XLgmatyuP.png" alt="image.png" /></p>
<ul>
<li><em>Select <code>Standard create</code>, <code>PostgreSQL</code>, and <code>Version</code> (in my example, it is PostgreSQL 12.5-R1)</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620552230437/3e7j77XK_.png" alt="image.png" /></p>
<ul>
<li><em>Select <code>Free Tier</code> as a <code>Template</code>, fill in <code>DB instance identifier</code>,  <code>Master username</code>, <code>Master password</code>, <code>Confirm password</code></em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620552456818/rnnBuaaVs.png" alt="image.png" /></p>
<ul>
<li><em>Select <code>Burstable classes (includes t classes)</code> and <code>db.t2.micro</code> for <code>DB instance class</code>, <code>General Purpose (SSD)</code> as <code>Storage type</code>, and <code>20</code> as <code>Allocated storage</code>, unselect <code>Enable storage autoscaling</code></em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620552792865/Ez6OK7jVM.png" alt="image.png" /></p>
<ul>
<li><p><em>Skip the <code>Availability &amp; durability</code> section</em></p>
</li>
<li><p><em>Configure <code>Connectivity</code></em>:</p>
<ul>
<li>Select your default VPC as <code>Virtual private cloud (VPC)</code><blockquote>
<p>After a database is created, you can't change the VPC selection.</p>
</blockquote>
</li>
<li>Select <code>default</code> as <code>Subnet group</code></li>
<li>Select <code>Yes</code> for <code>Public access</code></li>
<li>Select <code>Choose existing</code> for <code>VPC security group</code>, and select <code>default</code> for <code>Existing VPC security groups</code> section</li>
<li>Select  <code>Availability Zone</code> (in my example <code>us-east-1a</code>)</li>
</ul>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620553127175/rFZoEvtYO.png" alt="image.png" /></p>
<ul>
<li><em>Select <code>Password authentication</code> or any other you want to use as <code>Database authentication options</code>, unselect all check boxes in <code>Additional configuration</code> and type <code>Initial database name</code> (in my example, it is <code>django_aws</code>)</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620553965107/ahg7Hq3v-p.png" alt="image.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620554209446/vfBmk2x0L.png" alt="image.png" /></p>
<ul>
<li><em>Click on the <code>Create database</code> button</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620554259315/qwiicxMxk.png" alt="image.png" /></p>
<ul>
<li><em>Go to the <code>RDS</code> dashboard and click on <code>Databases</code> in the <code>RDS</code> side bar or on <code>DB instances</code></em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620642406834/-W3N8eUfG.png" alt="image.png" /></p>
<ul>
<li><em>Click on the created database identifier</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620642512235/SjJQQBwdS.png" alt="image.png" /></p>
<ul>
<li><em>Copy the database endpoint, the subnets, and the security groups</em></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620642725680/Wsr3oXQFRK.png" alt="image.png" /></p>
<p>Then, you should add this info to your Django and Serverless configurations directly or using environment variables (for example <code>.env</code> file)</p>
<pre><code><span class="hljs-attr">DB_HOST</span>=<span class="hljs-string">'django-aws.&lt;db-domain-id&gt;.us-east-1.rds.amazonaws.com'</span>
<span class="hljs-attr">DB_USER</span>=<span class="hljs-string">'&lt;your-master-db-user&gt;'</span>
<span class="hljs-attr">DB_PASSWORD</span>=<span class="hljs-string">'&lt;password-for-your-master-db-user&gt;'</span>
<span class="hljs-attr">DB_NAME</span>=<span class="hljs-string">'&lt;your-db-name&gt;'</span>
<span class="hljs-attr">SECURITY_GROUPS</span>=sg-&lt;security-group-id&gt;
<span class="hljs-attr">SUBNETS</span>=subnet-&lt;subnet-id&gt;,subnet-&lt;subnet-id&gt;,subnet-&lt;subnet-id&gt;,subnet-&lt;subnet-id&gt;,subnet-&lt;subnet-id&gt;,subnet-&lt;subnet-id&gt;
</code></pre><blockquote>
<p><strong>NOTE</strong>: This is just an example of AWS configuration I use in my example. You may use your own configuration.</p>
</blockquote>
<h2 id="automate-managing-your-aws-infrastructure">Automate managing your AWS infrastructure</h2>
<p>I showed you how to configure all the necessary AWS resources for a Django project manually using the <a target="_blank" href="https://console.aws.amazon.com/console/home">AWS Management Console</a>. There are some ways to automate this process. I'll show how to manage AWS resources using <a target="_blank" href="https://www.terraform.io/">Terraform</a> (infrastructure as code) in my next blog post. Follow me on Twitter <a target="_blank" href="https://twitter.com/vadim_khodak">@vadim_khodak</a> or on <a target="_blank" href="https://www.linkedin.com/in/vadym-khodak-0b1a05149/">LinkedIn</a> so you do not miss the next posts.</p>
]]></content:encoded></item><item><title><![CDATA[Deploying a Django project on AWS Lambda using Serverless (Part 1)]]></title><description><![CDATA[BLUF
As a follow-up to a post where we looked at the most common questions about Django in the Cloud, now, I'd like to help you deploy your Django App on Amazon Web Services and make you more independent from other developers like DevOps and CloudOps...]]></description><link>https://blog.vadymkhodak.com/deploy-django-app-on-aws-lambda-using-serverless-part-1</link><guid isPermaLink="true">https://blog.vadymkhodak.com/deploy-django-app-on-aws-lambda-using-serverless-part-1</guid><category><![CDATA[Django]]></category><category><![CDATA[serverless]]></category><category><![CDATA[AWS]]></category><category><![CDATA[aws lambda]]></category><dc:creator><![CDATA[Vadym Khodak]]></dc:creator><pubDate>Thu, 11 Feb 2021 09:07:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619165222888/xWMq_uxim.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="bluf">BLUF</h1>
<p>As a follow-up to a <a target="_blank" href="https://dev.to/vaddimart/top-6-questions-people-ask-about-django-apps-in-a-cloud-31bj">post</a> where we looked at the most common questions about Django in the Cloud, now, I'd like to help you deploy your Django App on Amazon Web Services and make you more independent from other developers like DevOps and CloudOps Engineers. There are many options for doing that but I'd like to show one of them and I hope that in the end you will be able to deploy your Django App on AWS Lambda using Serverless.</p>
<p>I was motivated by <a target="_blank" href="https://www.linkedin.com/in/bratchenko/">Daniil Bratchenko's</a> article <a target="_blank" href="https://daniil-bratchenko.medium.com/dont-let-software-vendors-dictate-your-business-processes-6bee1dd78234">Don’t Let Software Vendors Dictate Your Business Processes</a> to start writing this blog post. </p>
<p>It is so hard to find Software that will fit all your business processes as all companies are unique. This is why many companies have decided to set up dedicated teams building Software for their specific business processes and needs. From my personal point of view, Django App on AWS Lambda using Serverless is a good solution for cases like that. </p>
<p>Also, you can use this approach for prototyping your projects running them at their early stage. </p>
<p>There are a few advantages and disadvantages of using this approach.</p>
<p><strong>Advantages of using AWS Lambdas</strong>:</p>
<ul>
<li>cost (AWS Lambda is cheaper comparing to AWS EC2);</li>
<li>simplicity in running and maintaining;</li>
<li>scalability;</li>
<li>quick deployment.</li>
</ul>
<p><strong>the disadvantages</strong>:</p>
<ul>
<li>AWS Lambda requires some extra time to run your App;</li>
<li>size limit for deployment package;</li>
<li>API Gateway limitation (30-sec timeout, 6 Mb response body size);</li>
<li>it might cost more than AWS EC2 if there are too many requests.</li>
</ul>
<h2 id="prepare-aws-infrastructure">Prepare AWS infrastructure</h2>
<p>Probably, you are aware of a variety of AWS services required for web applications. In order to deploy a Django project on AWS Lambdas you should prepare your AWS infrastructure.
There is a list of AWS services I use for my Django project:</p>
<ol>
<li>Lambdas to run our wsgi application</li>
<li>API Gateway to handle HTTP request and send them to Lambdas</li>
<li>S3 buckets for Lambda deployments and storing static files</li>
<li>CloudFront distribution for serving static files from S3 bucket</li>
<li>RDS for a database (I use Postgres)</li>
<li>VPC with subnets</li>
<li>EC2 for Security Groups</li>
<li>IAM for roles and policies</li>
<li>CloudWatch for logs</li>
</ol>
<p>AWS Lambdas, API Gateway will be created automatically by Serverless. I will try to walk you though the process of creating all the necessary AWS resources in my following blog posts.</p>
<h2 id="create-a-django-project">Create a Django project</h2>
<p>Django <code>startproject</code> command allows us to create a simple Django project, in addition to that, there are some great Cookiecutter projects that can help you start your project easily (For example <a target="_blank" href="https://github.com/pydanny/cookiecutter-django">Cookiecutter Django</a>). I use default <code>django-admin startproject</code> cli command in this example.</p>
<pre><code class="lang-bash">pip install django
django-admin startproject django_aws_lambda
</code></pre>
<h2 id="configure-requirements">Configure requirements</h2>
<p>There are many options to store your project requirements, for example <code>requirements.txt</code>, <code>Pipfile</code>, <code>pyproject.toml</code>. You can one of these options. I'm using <code>requirements.txt</code> here.</p>
<ul>
<li>create <code>requirements.txt</code> file in a root directory of the project</li>
<li>add the following libraries to <code>requirements.txt</code> file:</li>
</ul>
<pre><code><span class="hljs-attribute">boto3</span>==<span class="hljs-number">1</span>.<span class="hljs-number">17</span>.<span class="hljs-number">17</span>
<span class="hljs-attribute">Collectfast</span>==<span class="hljs-number">2</span>.<span class="hljs-number">2</span>.<span class="hljs-number">0</span>
<span class="hljs-attribute">Django</span>==<span class="hljs-number">3</span>.<span class="hljs-number">1</span>.<span class="hljs-number">7</span>
<span class="hljs-attribute">django</span>-environ==<span class="hljs-number">0</span>.<span class="hljs-number">4</span>.<span class="hljs-number">5</span>
<span class="hljs-attribute">psycopg2</span>-binary==<span class="hljs-number">2</span>.<span class="hljs-number">8</span>.<span class="hljs-number">6</span>
<span class="hljs-attribute">Werkzeug</span>==<span class="hljs-number">1</span>.<span class="hljs-number">0</span>.<span class="hljs-number">1</span>
</code></pre><ul>
<li>create and activate virtual environments</li>
</ul>
<blockquote>
<p>Choose your preferred tool for managing virtual environments (like conda, pyenv, virtualenv, etc.)</p>
</blockquote>
<ul>
<li>install requirements</li>
</ul>
<pre><code class="lang-bash">pip install -r requirements.txt
</code></pre>
<h2 id="create-hello-django-app">Create <code>hello</code> Django app</h2>
<ul>
<li>create app using <code>startapp</code> Django command</li>
</ul>
<pre><code class="lang-bash">python manage.py startapp hello
</code></pre>
<ul>
<li>create <code>templates</code> folder</li>
</ul>
<pre><code class="lang-bash">mkdir templates
</code></pre>
<ul>
<li>create <code>index.html</code> file in <code>templates</code> folder with the following lines:</li>
</ul>
<pre><code class="lang-html">{% load static %}

<span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Greeting<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Hello {{ name }}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"{% static 'django.jpeg' %}"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Django"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width: 20%"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<ul>
<li><p>create folder <code>static</code> in the root directory of the project</p>
<pre><code class="lang-bash">mkdir static
</code></pre>
</li>
<li><p>add an image file to <code>static</code> folder, for example <code>django.jpeg</code></p>
</li>
<li><p>update <code>hello/views.py</code></p>
</li>
</ul>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.shortcuts <span class="hljs-keyword">import</span> render


<span class="hljs-comment"># Create your views here.</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">hello</span>(<span class="hljs-params">request, resource=None</span>):</span>

    <span class="hljs-keyword">return</span> render(request, <span class="hljs-string">"index.html"</span>, {<span class="hljs-string">"name"</span>: resource <span class="hljs-keyword">or</span> <span class="hljs-string">'World'</span>})
</code></pre>
<h2 id="configure-environments-variables">Configure environments variables:</h2>
<ul>
<li>create <code>.env</code> file in the root directory of the project</li>
<li>configure the following variables:</li>
</ul>
<pre><code class="lang-dotenv">STAGE='production'
DB_HOST=&lt;your database host&gt;
DB_USER=&lt;your database user name&gt;
DB_PASSWORD=&lt;your database password&gt;
DB_NAME=&lt;your database name&gt;
DJANGO_SECRET_KEY=&lt;some django secret key&gt;
AWS_S3_CDN_DOMAIN=&lt;your Cloud Front distribution, like: `&lt;distribution id&gt;.cloudfront.net`&gt;
AWS_S3_REGION_NAME=&lt;your AWS region&gt;
AWS_STORAGE_BUCKET_NAME=&lt;AWS s3 bucket for static files with punlic policies&gt;
DEPLOYMENT_BUCKET=&lt;AWS s3 bucket for deployment&gt;
AWS_KEY_ID=&lt;your AWS Key Id&gt;
AWS_SECRET=&lt;your AWS Secret&gt;
DJANGO_ADMIN_URL=&lt;Django admin url&gt;
DJANGO_ALLOWED_HOSTS=&lt;list of allowed hosts separated by coma&gt;
</code></pre>
<h2 id="create-configuration-for-local-development-and-production">Create configuration for local development and production</h2>
<ul>
<li>update <code>settings.py</code> in <code>django_aws_lambda</code> folder with the following lines:</li>
</ul>
<pre><code class="lang-python"><span class="hljs-string">"""
Django settings for django_aws_lambda project.

Generated by 'django-admin startproject' using Django 1.11.29.

For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""</span>

<span class="hljs-keyword">from</span> pathlib <span class="hljs-keyword">import</span> Path

<span class="hljs-keyword">import</span> environ

ROOT_DIR = Path(__file__).resolve(strict=<span class="hljs-literal">True</span>).parent.parent
env = environ.Env()
READ_DOT_ENV_FILE = env.bool(<span class="hljs-string">'DJANGO_READ_DOT_ENV_FILE'</span>, default=<span class="hljs-literal">True</span>)
<span class="hljs-keyword">if</span> READ_DOT_ENV_FILE:
    env.read_env(str(ROOT_DIR / <span class="hljs-string">'.env'</span>))

<span class="hljs-comment"># Quick-start development settings - unsuitable for production</span>
<span class="hljs-comment"># See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/</span>

<span class="hljs-comment"># SECURITY WARNING: keep the secret key used in production secret!</span>
SECRET_KEY = env(<span class="hljs-string">'DJANGO_SECRET_KEY'</span>, default=<span class="hljs-string">'&lt;some-secured-key&gt;'</span>)
<span class="hljs-comment"># SECURITY WARNING: don't run with debug turned on in production!</span>
DEBUG = <span class="hljs-literal">False</span>

ALLOWED_HOSTS = env.list(<span class="hljs-string">'DJANGO_ALLOWED_HOSTS'</span>, default=[<span class="hljs-string">'127.0.0.1'</span>, <span class="hljs-string">'localhost'</span>])
INSTALLED_APPS = [
    <span class="hljs-string">'django.contrib.admin'</span>,
    <span class="hljs-string">'django.contrib.auth'</span>,
    <span class="hljs-string">'django.contrib.contenttypes'</span>,
    <span class="hljs-string">'django.contrib.sessions'</span>,
    <span class="hljs-string">'django.contrib.messages'</span>,
    <span class="hljs-string">'django.contrib.staticfiles'</span>,
    <span class="hljs-string">'hello'</span>,
]

MIDDLEWARE = [
    <span class="hljs-string">'django.middleware.security.SecurityMiddleware'</span>,
    <span class="hljs-string">'django.contrib.sessions.middleware.SessionMiddleware'</span>,
    <span class="hljs-string">'django.middleware.common.CommonMiddleware'</span>,
    <span class="hljs-string">'django.middleware.csrf.CsrfViewMiddleware'</span>,
    <span class="hljs-string">'django.contrib.auth.middleware.AuthenticationMiddleware'</span>,
    <span class="hljs-string">'django.contrib.messages.middleware.MessageMiddleware'</span>,
    <span class="hljs-string">'django.middleware.clickjacking.XFrameOptionsMiddleware'</span>,
]

ROOT_URLCONF = <span class="hljs-string">'django_aws_lambda.urls'</span>

TEMPLATES = [
    {
        <span class="hljs-string">'BACKEND'</span>: <span class="hljs-string">'django.template.backends.django.DjangoTemplates'</span>,
        <span class="hljs-string">'DIRS'</span>: [
            str(ROOT_DIR / <span class="hljs-string">'templates'</span>),
            str(ROOT_DIR / <span class="hljs-string">'staticfiles'</span>),
        ],
        <span class="hljs-string">'OPTIONS'</span>: {
            <span class="hljs-string">'loaders'</span>: [
                <span class="hljs-string">'django.template.loaders.filesystem.Loader'</span>,
                <span class="hljs-string">'django.template.loaders.app_directories.Loader'</span>,
            ],
            <span class="hljs-string">'context_processors'</span>: [
                <span class="hljs-string">'django.template.context_processors.debug'</span>,
                <span class="hljs-string">'django.template.context_processors.request'</span>,
                <span class="hljs-string">'django.contrib.auth.context_processors.auth'</span>,
                <span class="hljs-string">'django.template.context_processors.i18n'</span>,
                <span class="hljs-string">'django.template.context_processors.media'</span>,
                <span class="hljs-string">'django.template.context_processors.static'</span>,
                <span class="hljs-string">'django.template.context_processors.tz'</span>,
                <span class="hljs-string">'django.contrib.messages.context_processors.messages'</span>,
            ],
        },
    },
]

WSGI_APPLICATION = <span class="hljs-string">'django_aws_lambda.wsgi.application'</span>


<span class="hljs-comment"># Database</span>
<span class="hljs-comment"># https://docs.djangoproject.com/en/3.0/ref/settings/#databases</span>

DATABASES = {
    <span class="hljs-string">'default'</span>: {
        <span class="hljs-string">'ENGINE'</span>: <span class="hljs-string">'django.db.backends.sqlite3'</span>,
        <span class="hljs-string">'NAME'</span>: ROOT_DIR / <span class="hljs-string">"db.sqlite3"</span>,
    }
}


<span class="hljs-comment"># Password validation</span>
<span class="hljs-comment"># https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators</span>
AUTH_PASSWORD_VALIDATORS = [
    {
        <span class="hljs-string">'NAME'</span>: <span class="hljs-string">'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'</span>,
    },
    {
        <span class="hljs-string">'NAME'</span>: <span class="hljs-string">'django.contrib.auth.password_validation.MinimumLengthValidator'</span>,
    },
    {
        <span class="hljs-string">'NAME'</span>: <span class="hljs-string">'django.contrib.auth.password_validation.CommonPasswordValidator'</span>,
    },
    {
        <span class="hljs-string">'NAME'</span>: <span class="hljs-string">'django.contrib.auth.password_validation.NumericPasswordValidator'</span>,
    },
]


<span class="hljs-comment"># Internationalization</span>
<span class="hljs-comment"># https://docs.djangoproject.com/en/1.11/topics/i18n/</span>

LANGUAGE_CODE = <span class="hljs-string">'en-us'</span>

TIME_ZONE = <span class="hljs-string">'UTC'</span>

USE_I18N = <span class="hljs-literal">True</span>

USE_L10N = <span class="hljs-literal">True</span>

USE_TZ = <span class="hljs-literal">True</span>


<span class="hljs-comment"># Static files (CSS, JavaScript, Images)</span>
<span class="hljs-comment"># https://docs.djangoproject.com/en/1.11/howto/static-files/</span>

STATIC_ROOT = str(ROOT_DIR / <span class="hljs-string">'staticfiles'</span>)
STATIC_URL = <span class="hljs-string">'/static/'</span>

STATICFILES_DIRS = [str(ROOT_DIR / <span class="hljs-string">'static'</span>)]
STATICFILES_FINDERS = [
    <span class="hljs-string">'django.contrib.staticfiles.finders.FileSystemFinder'</span>,
    <span class="hljs-string">'django.contrib.staticfiles.finders.AppDirectoriesFinder'</span>,
]
MEDIA_ROOT = str(ROOT_DIR / <span class="hljs-string">'media'</span>)
MEDIA_URL = <span class="hljs-string">'/media/'</span>
ADMIN_URL = env(<span class="hljs-string">'DJANGO_ADMIN_URL'</span>)
</code></pre>
<ul>
<li>create <code>local.py</code> and <code>production.py</code> files inside <code>django_aws_lambda</code> folder on the same level as <code>settings.py</code></li>
<li>add the following lines to <code>local.py</code>:</li>
</ul>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> .settings <span class="hljs-keyword">import</span> *  <span class="hljs-comment"># noqa</span>


DEBUG = <span class="hljs-literal">True</span>
</code></pre>
<ul>
<li>add the following lines to <code>production.py</code>:</li>
</ul>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> .settings <span class="hljs-keyword">import</span> *  <span class="hljs-comment"># noqa</span>

DEBUG = <span class="hljs-literal">False</span>
DATABASES[<span class="hljs-string">"default"</span>] = {
    <span class="hljs-string">'ENGINE'</span>: <span class="hljs-string">'django.db.backends.postgresql'</span>,
    <span class="hljs-string">'NAME'</span>: env(<span class="hljs-string">"DB_NAME"</span>),
    <span class="hljs-string">'USER'</span>: env(<span class="hljs-string">"DB_USER"</span>),
    <span class="hljs-string">'PASSWORD'</span>: env(<span class="hljs-string">"DB_PASSWORD"</span>),
    <span class="hljs-string">'HOST'</span>: env(<span class="hljs-string">"DB_HOST"</span>),
    <span class="hljs-string">'PORT'</span>: <span class="hljs-string">'5432'</span>,
}
DATABASES[<span class="hljs-string">"default"</span>][<span class="hljs-string">"ATOMIC_REQUESTS"</span>] = <span class="hljs-literal">True</span>  <span class="hljs-comment"># noqa F405</span>
DATABASES[<span class="hljs-string">"default"</span>][<span class="hljs-string">"CONN_MAX_AGE"</span>] = env.int(<span class="hljs-string">"CONN_MAX_AGE"</span>, default=<span class="hljs-number">60</span>)  <span class="hljs-comment"># noqa F405</span>
SECURE_PROXY_SSL_HEADER = (<span class="hljs-string">"HTTP_X_FORWARDED_PROTO"</span>, <span class="hljs-string">"https"</span>)
SECURE_SSL_REDIRECT = env.bool(<span class="hljs-string">"DJANGO_SECURE_SSL_REDIRECT"</span>, default=<span class="hljs-literal">False</span>)
SESSION_COOKIE_SECURE = <span class="hljs-literal">True</span>
CSRF_COOKIE_SECURE = <span class="hljs-literal">True</span>
SECURE_HSTS_SECONDS = <span class="hljs-number">60</span>
SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool(<span class="hljs-string">"DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS"</span>, default=<span class="hljs-literal">True</span>)
SECURE_HSTS_PRELOAD = env.bool(<span class="hljs-string">"DJANGO_SECURE_HSTS_PRELOAD"</span>, default=<span class="hljs-literal">True</span>)
SECURE_CONTENT_TYPE_NOSNIFF = env.bool(<span class="hljs-string">"DJANGO_SECURE_CONTENT_TYPE_NOSNIFF"</span>, default=<span class="hljs-literal">True</span>)
INSTALLED_APPS += [<span class="hljs-string">"storages"</span>]  <span class="hljs-comment"># noqa F405</span>
AWS_KEY = env(<span class="hljs-string">"AWS_KEY_ID"</span>)
AWS_SECRET = env(<span class="hljs-string">"AWS_SECRET"</span>)
AWS_STORAGE_BUCKET_NAME = env(<span class="hljs-string">"AWS_STORAGE_BUCKET_NAME"</span>)
AWS_QUERYSTRING_AUTH = <span class="hljs-literal">False</span>
_AWS_EXPIRY = <span class="hljs-number">60</span> * <span class="hljs-number">60</span> * <span class="hljs-number">24</span> * <span class="hljs-number">7</span>
AWS_S3_OBJECT_PARAMETERS = {<span class="hljs-string">"CacheControl"</span>: <span class="hljs-string">f"max-age=<span class="hljs-subst">{_AWS_EXPIRY}</span>, s-maxage=<span class="hljs-subst">{_AWS_EXPIRY}</span>, must-revalidate"</span>}
AWS_S3_REGION_NAME = env(<span class="hljs-string">"AWS_S3_REGION_NAME"</span>, default=<span class="hljs-literal">None</span>)
AWS_S3_CUSTOM_DOMAIN = env(<span class="hljs-string">"DJANGO_AWS_S3_CUSTOM_DOMAIN"</span>, default=<span class="hljs-literal">None</span>)
aws_s3_domain = AWS_S3_CUSTOM_DOMAIN <span class="hljs-keyword">or</span> <span class="hljs-string">f"<span class="hljs-subst">{AWS_STORAGE_BUCKET_NAME}</span>.s3.amazonaws.com"</span>
STATICFILES_STORAGE = <span class="hljs-string">"django_aws_lambda.utils.StaticRootS3Boto3Storage"</span>
COLLECTFAST_STRATEGY = <span class="hljs-string">"collectfast.strategies.boto3.Boto3Strategy"</span>
STATIC_URL = <span class="hljs-string">f"https://<span class="hljs-subst">{aws_s3_domain}</span>/static/"</span>
DEFAULT_FILE_STORAGE = <span class="hljs-string">"django_aws_lambda.utils.MediaRootS3Boto3Storage"</span>
MEDIA_URL = <span class="hljs-string">f"https://<span class="hljs-subst">{aws_s3_domain}</span>/media/"</span>
MEDIAFILES_LOCATION = <span class="hljs-string">"/media"</span>

STATICFILES_LOCATION = <span class="hljs-string">"/static"</span>
TEMPLATES[<span class="hljs-number">-1</span>][<span class="hljs-string">"OPTIONS"</span>][<span class="hljs-string">"loaders"</span>] = [  <span class="hljs-comment"># type: ignore[index] # noqa F405</span>
    (
        <span class="hljs-string">"django.template.loaders.cached.Loader"</span>,
        [
            <span class="hljs-string">"django.template.loaders.filesystem.Loader"</span>,
            <span class="hljs-string">"django.template.loaders.app_directories.Loader"</span>,
        ],
    )
]
</code></pre>
<ul>
<li>update <code>wsgi.py</code> file in <code>django_aws_lambda</code> folder with the following lines:</li>
</ul>
<pre><code class="lang-python"><span class="hljs-string">"""
WSGI config for django_aws_lambda project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
"""</span>

<span class="hljs-string">"""
WSGI config for django_aws_lambda project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/
"""</span>

<span class="hljs-keyword">import</span> os

<span class="hljs-keyword">from</span> django.core.wsgi <span class="hljs-keyword">import</span> get_wsgi_application

os.environ.setdefault(<span class="hljs-string">'DJANGO_SETTINGS_MODULE'</span>, <span class="hljs-string">'django_aws_lambda.production'</span>)

application = get_wsgi_application()
</code></pre>
<ul>
<li>update <code>urls.py</code> file in <code>django_aws_lambda</code> folder with the following lines:</li>
</ul>
<pre><code class="lang-python"><span class="hljs-string">"""django_aws_lambda URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.0/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""</span>
<span class="hljs-keyword">from</span> django.contrib <span class="hljs-keyword">import</span> admin
<span class="hljs-keyword">from</span> django.urls <span class="hljs-keyword">import</span> path

<span class="hljs-keyword">from</span> hello.views <span class="hljs-keyword">import</span> hello

urlpatterns = [
    path(<span class="hljs-string">'admin/'</span>, admin.site.urls),
    path(<span class="hljs-string">''</span>, hello),
    path(<span class="hljs-string">'&lt;path:resource&gt;'</span>, hello),
]
</code></pre>
<ul>
<li>update <code>manage.py</code> with the following lines:</li>
</ul>
<pre><code class="lang-python"><span class="hljs-comment">#!/usr/bin/env python</span>
<span class="hljs-string">"""Django's command-line utility for administrative tasks."""</span>
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> sys


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    os.environ.setdefault(<span class="hljs-string">'DJANGO_SETTINGS_MODULE'</span>, <span class="hljs-string">'django_aws_lambda.production'</span>)
    <span class="hljs-keyword">try</span>:
        <span class="hljs-keyword">from</span> django.core.management <span class="hljs-keyword">import</span> execute_from_command_line
    <span class="hljs-keyword">except</span> ImportError <span class="hljs-keyword">as</span> exc:
        <span class="hljs-keyword">raise</span> ImportError(
            <span class="hljs-string">"Couldn't import Django. Are you sure it's installed and "</span>
            <span class="hljs-string">"available on your PYTHONPATH environment variable? Did you "</span>
            <span class="hljs-string">"forget to activate a virtual environment?"</span>
        ) <span class="hljs-keyword">from</span> exc
    execute_from_command_line(sys.argv)


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    main()
</code></pre>
<ul>
<li>create a folder <code>utils</code> inside <code>django_aws_lambda</code> </li>
<li>create <code>storages.py</code> file inside <code>utils</code> folder with the following lines:</li>
</ul>
<pre><code class="lang-bash">from storages.backends.s3boto3 import S3Boto3Storage


class StaticRootS3Boto3Storage(S3Boto3Storage):
    location = <span class="hljs-string">"static"</span>
    default_acl = <span class="hljs-string">"public-read"</span>


class MediaRootS3Boto3Storage(S3Boto3Storage):
    location = <span class="hljs-string">"media"</span>
    file_overwrite = False
</code></pre>
<h2 id="run-django-project-locally">Run Django project locally</h2>
<ul>
<li>set environment variable with a path to Django local configuration file</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> DJANGO_SETTINGS_MODULE=django_aws_lambda.local
</code></pre>
<ul>
<li>migrate database changes</li>
</ul>
<pre><code class="lang-bash">python manage.py migrate
</code></pre>
<ul>
<li>create a superuser in the database</li>
</ul>
<pre><code class="lang-bash">python manage.py createsuperuser
</code></pre>
<blockquote>
<p>Then provide a username, user email, password, and confirm the password</p>
</blockquote>
<ul>
<li>collect static files</li>
</ul>
<pre><code class="lang-bash">python manage.py collectstatic
</code></pre>
<ul>
<li>run server locally</li>
</ul>
<pre><code class="lang-bash">python manage.py runserver
</code></pre>
<ul>
<li><p>go to <a target="_blank" href="http://127.0.0.1:8000/">http://127.0.0.1:8000/</a> and you will see this:
<img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w167cb786hfj1rm956in.png" alt="image" /></p>
</li>
<li><p>go to <a target="_blank" href="http://127.0.0.1:8000/Dev.to">http://127.0.0.1:8000/Dev.to</a> and you will see this:
<img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/85gr40o4ipjb7yh7qj83.png" alt="image" /></p>
</li>
</ul>
<h2 id="create-serverless-configuration">Create serverless configuration</h2>
<ul>
<li>initialize npm:</li>
</ul>
<pre><code class="lang-bash">npm init
</code></pre>
<ul>
<li>install serverless</li>
</ul>
<pre><code class="lang-bash">npm install -g serverless
</code></pre>
<ul>
<li>install serverless plugins</li>
</ul>
<pre><code class="lang-bash">npm install -P serverless-dotenv-plugin
npm install -P serverless-prune-plugin
npm install -P serverless-python-requirements
npm install -P serverless-wsgi
</code></pre>
<ul>
<li>create serverless.yaml file with the following configuration:</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-attr">service:</span> <span class="hljs-string">django-aws-lambda</span>

<span class="hljs-attr">plugins:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">serverless-dotenv-plugin</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">serverless-prune-plugin</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">serverless-python-requirements</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">serverless-wsgi</span>
<span class="hljs-attr">useDotenv:</span> <span class="hljs-literal">true</span>

<span class="hljs-attr">custom:</span>
  <span class="hljs-attr">dotenv:</span>
    <span class="hljs-attr">logging:</span> <span class="hljs-literal">false</span>
  <span class="hljs-attr">pythonRequirements:</span>
    <span class="hljs-attr">dockerizePip:</span> <span class="hljs-string">non-linux</span>
    <span class="hljs-attr">zip:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">fileName:</span> <span class="hljs-string">requirements.txt</span>
  <span class="hljs-attr">stage:</span> <span class="hljs-string">${env:STAGE}</span>
  <span class="hljs-attr">wsgi:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">django_aws_lambda.wsgi.application</span>
    <span class="hljs-attr">packRequirements:</span> <span class="hljs-literal">false</span>
  <span class="hljs-attr">prune:</span>
    <span class="hljs-attr">automatic:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">number:</span> <span class="hljs-number">3</span>

<span class="hljs-attr">functions:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">app:</span>
      <span class="hljs-attr">handler:</span> <span class="hljs-string">wsgi_handler.handler</span>
      <span class="hljs-attr">events:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">http:</span> <span class="hljs-string">ANY</span> <span class="hljs-string">/</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">http:</span> <span class="hljs-string">ANY</span> <span class="hljs-string">/{proxy+}</span>
      <span class="hljs-attr">timeout:</span> <span class="hljs-number">30</span>

<span class="hljs-attr">provider:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">aws</span>
  <span class="hljs-attr">role:</span> <span class="hljs-string">arn:aws:iam::&lt;role_id&gt;:role/&lt;role_name&gt;</span>
  <span class="hljs-attr">profile:</span> <span class="hljs-string">&lt;your-profile-name&gt;</span>  <span class="hljs-comment"># make sure that you configured aws profile using `aws configure --profile &lt;your-profile-name&gt;`</span>
  <span class="hljs-attr">region:</span> <span class="hljs-string">us-east-1</span>
  <span class="hljs-attr">runtime:</span> <span class="hljs-string">python3.8</span>
  <span class="hljs-attr">versionFunctions:</span> <span class="hljs-literal">false</span>
  <span class="hljs-attr">stage:</span> <span class="hljs-string">${env:STAGE}</span>
  <span class="hljs-attr">timeout:</span> <span class="hljs-number">60</span>
  <span class="hljs-attr">vpc:</span>
    <span class="hljs-attr">securityGroupIds:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">&lt;your-security-group-id&gt;</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">&lt;your-security-group-id&gt;</span>
    <span class="hljs-attr">subnetIds:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">&lt;your-subnet-id&gt;</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">&lt;your-subnet-id&gt;</span>
  <span class="hljs-attr">deploymentBucket:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">${env:DEPLOYMENT_BUCKET}</span>
  <span class="hljs-attr">apiGateway:</span>
    <span class="hljs-attr">shouldStartNameWithService:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">lambdaHashingVersion:</span> <span class="hljs-number">20201221</span>

<span class="hljs-attr">package:</span>
  <span class="hljs-attr">individually:</span>
    <span class="hljs-literal">true</span>
  <span class="hljs-attr">exclude:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">.env</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">.git/**</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">.github/**</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">.serverless/**</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">static/**</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">.cache/**</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">.pytest_cache/**</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">node_modules/**</span>
</code></pre>
<h2 id="use-docker-for-deploying-your-django-project-to-aws-lambda-using-serverless">Use Docker for deploying your Django project to AWS Lambda using Serverless</h2>
<ul>
<li>run Amazon Linux 2 docker image:</li>
</ul>
<pre><code class="lang-bash">docker run -it -v $(<span class="hljs-built_in">pwd</span>):/root/src/ -v /Users/&lt;your_user&gt;/.aws:/root/.aws amazonlinux:latest bash
</code></pre>
<ul>
<li>install the necessary Unix dependencies:</li>
</ul>
<pre><code class="lang-bash">yum install sudo -y
sudo yum install -y gcc openssl-devel bzip2-devel libffi-devel wget tar sqlite-devel gcc-c++ make
</code></pre>
<ul>
<li>install node.js version 14:</li>
</ul>
<pre><code class="lang-bash">curl -sL https://rpm.nodesource.com/setup_14.x | sudo -E bash - 
sudo yum install -y nodejs
</code></pre>
<ul>
<li>install Python 3.8.7:</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> /opt
sudo wget https://www.python.org/ftp/python/3.8.7/Python-3.8.7.tgz
sudo tar xzf Python-3.8.7.tgz
<span class="hljs-built_in">cd</span> Python-3.8.7
sudo ./configure --enable-optimizations
sudo make altinstall
sudo rm -f /opt/Python-3.8.7.tgz
</code></pre>
<ul>
<li>create python and pip aliases:</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-built_in">alias</span> python=<span class="hljs-string">'python3.8'</span>
<span class="hljs-built_in">alias</span> pip=<span class="hljs-string">'pip3.8'</span>
</code></pre>
<ul>
<li>update pip and setuptools:</li>
</ul>
<pre><code class="lang-bash">pip install --upgrade pip setuptools
</code></pre>
<ul>
<li>install serverless:</li>
</ul>
<pre><code class="lang-bash">npm install -g serverless
</code></pre>
<ul>
<li>move to project directory</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> /root/src/
</code></pre>
<ul>
<li>install requirements inside docker container:</li>
</ul>
<pre><code class="lang-bash">pip install -r requirements.txt
</code></pre>
<ul>
<li>set environment variable with a path to django production configuration file</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> DJANGO_SETTINGS_MODULE=django_aws_lambda.production
</code></pre>
<ul>
<li>migrate database changes</li>
</ul>
<pre><code class="lang-bash">python manage.py migrate
</code></pre>
<ul>
<li>create a superuser in the database</li>
</ul>
<pre><code class="lang-bash">python manage.py createsuperuser
</code></pre>
<blockquote>
<p>Then provide a username, user email, password, and confirm the password</p>
</blockquote>
<ul>
<li>collect static files to AWS S3 bucket</li>
</ul>
<pre><code class="lang-bash">python manage.py collectstatic
</code></pre>
<blockquote>
<p>If you get <code>NoCredentialsError</code> from <code>botocore</code> you should add to environment variables <code>AWS_PROFILE</code>:</p>
</blockquote>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> AWS_PROFILE=&lt;your-aws-profile-name&gt;
</code></pre>
<ul>
<li>install serverless packages from package.json</li>
</ul>
<pre><code class="lang-bash">npm install
</code></pre>
<ul>
<li>deploy your Django project to AWS Lambda using Serverless</li>
</ul>
<pre><code class="lang-bash">serverless deploy -s production
</code></pre>
<p>Your response will look like that:</p>
<pre><code>Serverless: Adding Python requirements helper to ....
Serverless: Generated requirements from /root/src/requirements.txt in /root/src/.serverless/requirements.txt...
Serverless: Installing requirements from /root/.cache/serverless-python-requirements/ ...
Serverless: Using download cache directory /root/.cache/serverless-python-requirements/downloadCacheslspyc
Serverless: Running ...
Serverless: Zipping required Python packages for ....
Serverless: Using Python specified in "runtime": python3.8
Serverless: Packaging Python WSGI handler...
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Removing Python requirements helper from ....
Serverless: Injecting required Python packages to package...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service app.zip file to S3 (60.48 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..........
Serverless: Stack update finished...
Service Information
service: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">your-serverless-service-name</span>&gt;</span></span>
stage: production
region: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">your-aws-region</span>&gt;</span></span>
stack: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">your-serverless-service-name</span>&gt;</span></span>-pronduction
resources: 8
api keys:
  None
endpoints:
  ANY - https://<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">some-id</span>&gt;</span></span>.execute-api.<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">your-aws-region</span>&gt;</span></span>.amazonaws.com/production
  ANY - https://<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">some-id</span>&gt;</span></span>.execute-api.<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">your-aws-region</span>&gt;</span></span>.amazonaws.com/production/{proxy+}
functions:
  app: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">your-serverless-service-name</span>&gt;</span></span>-production-app
layers:
  None
Serverless: Prune: Running post-deployment pruning
Serverless: Prune: Querying for deployed function versions
Serverless: Prune: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">your-serverless-service-name</span>&gt;</span></span>-production-app has 3 additional versions published and 0 aliases, 0 versions selected for deletion
Serverless: Prune: Pruning complete.
Serverless: Removing old service artifacts from S3...

<span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">**
Serverless: Announcing Metrics, CI/CD, Secrets and more built into Serverless Framework. Run "serverless login" to activate for free..
**</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span>
</code></pre><p>Now, your Django Project will be available at this URL:
<code>https://&lt;some-id&gt;.execute-api.&lt;your-aws-region&gt;.amazonaws.com/production</code>
<img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y47dnlolnp9vuqtjybyf.png" alt="image" /></p>
<p>Congratulations!</p>
<p>Here is a <a target="_blank" href="https://github.com/vadym-khodak/django-aws-lambda">link to a GitHub repository</a> with the code shown in this blog post.</p>
<p>If you want to learn more about Django projects on AWS Lambdas follow me on Twitter (<a target="_blank" href="https://twitter.com/vadim_khodak">@vadim_khodak</a>) I plan to write a post showing how to create all the necessary AWS resources for this Django project, how to add React.js client to the Django project and more.</p>
]]></content:encoded></item><item><title><![CDATA[Top 6 questions people ask about Django Apps in a Cloud]]></title><description><![CDATA[You have probably tried to look for something regarding Django and Cloud on the Internet — it’s a pretty common way to find an answer to a question. But have you found the right answer to your question? How many search requests did you need to send b...]]></description><link>https://blog.vadymkhodak.com/top-6-questions-people-ask-about-django-apps-in-a-cloud</link><guid isPermaLink="true">https://blog.vadymkhodak.com/top-6-questions-people-ask-about-django-apps-in-a-cloud</guid><category><![CDATA[Django]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[serverless]]></category><category><![CDATA[Cloud]]></category><category><![CDATA[Heroku]]></category><dc:creator><![CDATA[Vadym Khodak]]></dc:creator><pubDate>Mon, 08 Feb 2021 09:17:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619166054699/ANN-jHHRj.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You have probably tried to look for something regarding Django and Cloud on the Internet — it’s a pretty common way to find an answer to a question. But have you found the right answer to your question? How many search requests did you need to send before you got the answer? How many links did you have to open?</p>
<p>Today, we will take a look at some of the most frequent questions.</p>
<p>For better readability, these questions are not sorted by popularity, but rather by logic.</p>
<h2 id="where-can-i-host-django-for-free">Where can I host Django for free?</h2>
<p>You can use many cloud solutions for that. But you need to keep in mind that all free solutions have many limitations.</p>
<p>This is a list of some of them:</p>
<ul>
<li>CPU resources allocation</li>
<li>File storage size</li>
<li>Using custom domain name</li>
<li>Database limitations</li>
</ul>
<p>It may become hard to maintain and adjust your use of free solutions to the above constraints and limitations if you choose to deploy your production application using free products.</p>
<p>But free solutions are a good place for prototyping and testing your app.</p>
<p>Here are some of the Cloud solutions:</p>
<ul>
<li>Pythonanywhere</li>
<li>Heroku</li>
<li>AWS</li>
<li>Google Cloud Platform</li>
</ul>
<h2 id="which-cloud-solution-is-the-best-for-django">Which Cloud solution is the best for Django?</h2>
<p>From my personal point-of-view, it is kind of a philosophical question.</p>
<p>It depends on the context. First, you need to answer these questions to get an answer to your main question:</p>
<ul>
<li>What is your budget?</li>
<li>What team will work on it (will you engage DevOps or CloudOps Engineers)?</li>
<li>What database will you use?
<em>How many users will there be?
</em>How many requests do you expect to have?</li>
</ul>
<p>Then, being guided by the answers to those questions, you can try to figure out which solution fits your requirements better.</p>
<h3 id="for-example">For example:</h3>
<p><strong>Case 1.</strong> There are no DevOps or CloudOps Engineers that could help with deploying your app in your team. In this case I’d recommend looking at Heroku.</p>
<p><strong>Case 2.</strong> There is an AWS CloudOps Engineer in your team, your app should be scalable, you have a good budget for your App: AWS would serve as a good Cloud solution for your Django app.</p>
<p><strong>Case 3.</strong> You are a student and you just want to try Django. Pythonanywhere allows you to deploy a simple Django app for free. Or you can use Heroku as well.</p>
<p><strong>Case 4.</strong> You have all your existing infrastructure set up on Google Cloud Platform: it would probably be better to deploy your Django app on that infrastructure.</p>
<h2 id="how-to-deploy-a-django-application-to-pythonanywhere">How to deploy a Django application to Pythonanywhere?</h2>
<p>When I tried doing it for the first time, I used a tutorial from <a target="_blank" href="https://tutorial.djangogirls.org/en/deploy/">Django Girls</a>. But there are many articles about it on the Internet, i.e.:</p>
<p>For example:</p>
<ul>
<li><a target="_blank" href="https://help.pythonanywhere.com/pages/DeployExistingDjangoProject/">Pythonanywhere help portal</a></li>
<li><a target="_blank" href="https://dev.to/mh_shifat/host-your-django-project-in-pythonanywhere-free-3m4f">Dev.to</a></li>
<li><a target="_blank" href="https://medium.com/@AmaanPengoo/uploading-django-to-pythonanywhere-bf4990ae47cc">Medium</a></li>
</ul>
<h2 id="how-to-deploy-a-django-application-to-heroku">How to deploy a Django application to Heroku?</h2>
<p><a target="_blank" href="https://devcenter.heroku.com/articles/deploying-python">Heroku Dev Center</a> already has a good answer. Also you can find many answers to this question on Dev.to, medium.com, and many other resources.</p>
<h2 id="how-to-deploy-a-django-application-to-google-cloud">How to deploy a Django application to Google Cloud?</h2>
<p>It is possible to run Django apps on  <a target="_blank" href="https://cloud.google.com/appengine">App Engine standard environment</a>  scale dynamically according to the traffic. Here is <a target="_blank" href="https://cloud.google.com/python/django/appengine">a good tutorial from Google about how to do it</a>)</p>
<h2 id="how-to-deploy-a-django-application-on-aws">How to deploy a Django application on AWS?</h2>
<p>From my personal experience, it is the hardest task. There are a couple of AWS services where you can deploy your Django App. You can use Amazon Elastic Compute Cloud (Amazon EC2) and AWS Lambda.</p>
<p>And there is more than one way to deploy it there.</p>
<p>It will be laborious to explain even one of the methods within the scope of these paragraphs, but in my following articles I will make an effort to address the points below step by step:</p>
<ul>
<li>prepare Django app</li>
<li>set up all the necessary AWS resources</li>
<li>configure AWS to serve static files</li>
<li>and others…</li>
</ul>
<h2 id="summary">Summary</h2>
<p>There are many solutions regarding deploying a Django App in a Cloud. You can easily use one of them that fits better to your requirements. </p>
<p>I'd like to share some pieces of advice for looking for the right answer:</p>
<ul>
<li>be specific enough in your question</li>
<li>compare your requirements with the suggested requirements (python or Django versions)</li>
<li>review comments and likes to check what other developers think about the answers provided</li>
<li>look at author of the answer (check whether they have enough expertise to answer that question).</li>
</ul>
<p>In my following blog posts, I will describe how to deploy Django on AWS lambda using Serverless. Follow me not to miss that. </p>
<p>Good luck!</p>
]]></content:encoded></item></channel></rss>