<?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[Untitled Publication]]></title><description><![CDATA[Untitled Publication]]></description><link>https://samcolon.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 17 Sep 2026 05:36:42 GMT</lastBuildDate><atom:link href="https://samcolon.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Automating Log Management with Bash and S3]]></title><description><![CDATA[One of the most common tasks when working with the cloud is creating scripts to automate processes. These scripts are typically written in either Bash or Python. In this project, I’ll guide you through setting up the infrastructure needed to host an ...]]></description><link>https://samcolon.hashnode.dev/automating-log-management-with-bash-and-s3</link><guid isPermaLink="true">https://samcolon.hashnode.dev/automating-log-management-with-bash-and-s3</guid><category><![CDATA[AWS]]></category><category><![CDATA[Bash]]></category><category><![CDATA[S3]]></category><category><![CDATA[AWS CloudTrail]]></category><category><![CDATA[ec2]]></category><category><![CDATA[IAM]]></category><category><![CDATA[cronjob]]></category><category><![CDATA[automation]]></category><category><![CDATA[VPC Endpoints]]></category><dc:creator><![CDATA[Samuel Colon]]></dc:creator><pubDate>Mon, 02 Dec 2024 22:11:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1733176844736/0fa3a2bf-099b-4547-b7d1-dcc24a4edb17.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One of the most common tasks when working with the cloud is creating <strong>scripts</strong> to <strong>automate</strong> processes. These scripts are typically written in either <strong>Bash</strong> or <strong>Python</strong>. In this project, I’ll guide you through setting up the <strong>infrastructure</strong> needed to host an application while also implementing a solution to back up the <strong>logs</strong> to an <strong>S3</strong> bucket.</p>
<p><strong>Before we start, here is the plan:</strong></p>
<ol>
<li><p>We will start with creating a new <strong>VPC</strong>.</p>
</li>
<li><p>Setup an <strong>S3</strong> bucket to store the logs.</p>
</li>
<li><p>Create the right <strong>IAM Policy</strong> and <strong>Role</strong> to keep things a little bit more secure.</p>
</li>
<li><p>Provision the <strong>EC2</strong> instance that is going to host our application.</p>
</li>
<li><p>Enable <strong>CloudTrail</strong> for monitoring and auditing.</p>
</li>
<li><p>Install the application on the <strong>EC2</strong> instance.</p>
</li>
<li><p>Monitor activity with <strong>CloudTrai</strong>l.</p>
</li>
<li><p>Automate Log Backup via <strong>Cron job</strong></p>
</li>
</ol>
<p><strong>Now that we have an overview of the steps, let’s get started.</strong></p>
<h1 id="heading-create-the-vpc">Create the VPC</h1>
<p>Let’s kick things off by laying the foundation via creation of a new VPC. Start by selecting the <strong>"VPC and more"</strong> option to simplify the process. Give your VPC a name that’s easy to identify, then configure the following settings:</p>
<ul>
<li><p><strong>IPv4 CIDR block</strong>: Choose your range.</p>
</li>
<li><p><strong>Availability Zone</strong>: Set it to 1.</p>
</li>
<li><p><strong>Subnets</strong>: Add 1 Public Subnet and 1 Private Subnet.</p>
</li>
<li><p><strong>Gateways</strong>: Include a NAT Gateway and an S3 Gateway.</p>
</li>
</ul>
<p>Once everything’s set up, click <strong>"Create VPC"</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732666911444/63742cbf-41e7-412a-8390-20ace49faf0d.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732894490848/ddad6bb7-3d03-4290-9d7b-f5677125782c.png" alt class="image--center mx-auto" /></p>
<p>The <strong>S3 Gateway</strong> creates an endpoint which allows us to access an <strong>S3</strong> bucket from within the <strong>VPC</strong> vs. sending the traffic over the public internet. We are implementing this solution to ensure that only a specific <strong>EC2</strong> instance can access the <strong>S3</strong> bucket but at the same time, keep the traffic internally.</p>
<h1 id="heading-create-the-s3-bucket">Create the S3 Bucket</h1>
<p>Head over to <strong>S3</strong> to create a bucket. I will be naming mine <strong>myapachelogs.</strong> Once the bucket is created, open it, navigate to the <strong>Permissions</strong> tab, and update the <strong>Bucket Policy</strong> with the following:</p>
<pre><code class="lang-bash">{
    <span class="hljs-string">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
    <span class="hljs-string">"Statement"</span>: [
        {
            <span class="hljs-string">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-string">"Principal"</span>: <span class="hljs-string">"*"</span>,
            <span class="hljs-string">"Action"</span>: <span class="hljs-string">"s3:*"</span>,
            <span class="hljs-string">"Resource"</span>: [
                <span class="hljs-string">"arn:aws:s3:::your-s3-bucket-name"</span>,
                <span class="hljs-string">"arn:aws:s3:::your-s3-bucket-name/*"</span>
            ],
            <span class="hljs-string">"Condition"</span>: {
                <span class="hljs-string">"StringEquals"</span>: {
                    <span class="hljs-string">"aws:SourceVpce"</span>: <span class="hljs-string">"S3-vpce-id"</span>
                }
            }
        }
    ]
}
</code></pre>
<p><em>(Replace</em> <strong><em>your-s3-bucket name</em></strong> <em>with the bucket you just created. Also, replace</em> <strong><em>vpce-id</em></strong> <em>with the S3 VPC Endpoint that was created with the VPC)</em></p>
<p>You can easily grab the <strong>S3 VPC ID</strong> by searching for endpoints in the search bar.</p>
<h1 id="heading-create-the-iam-policy-and-iam-role">Create the IAM policy and IAM role</h1>
<p>Next, we’ll create an <strong>IAM</strong> role with an <strong>S3</strong> policy using the <strong>Policy Editor</strong>.</p>
<p>For <strong>S3</strong> access, we’ll allow all actions except <strong>delete</strong> actions from the <strong>EC2</strong> instance. Use the following to create the policy:</p>
<pre><code class="lang-bash">{
    <span class="hljs-string">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
    <span class="hljs-string">"Statement"</span>: [
        {
            <span class="hljs-string">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-string">"Action"</span>: [
                <span class="hljs-string">"s3:PutObject"</span>,
                <span class="hljs-string">"s3:GetObject"</span>,
                <span class="hljs-string">"s3:ListBucket"</span>
            ],
            <span class="hljs-string">"Resource"</span>: [
                <span class="hljs-string">"arn:aws:s3:::your-s3-bucket-name"</span>,
                <span class="hljs-string">"arn:aws:s3:::your-s3-bucket-name/*"</span>
            ]
        }
    ]
}
</code></pre>
<p><em>(Make sure to edit with your bucket name)</em></p>
<p>After editing the policy, go ahead and click on <strong>Create Policy</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732672399652/a2a1819c-6884-44bf-bc47-5acdda23a6fa.png" alt class="image--center mx-auto" /></p>
<p>Now we have to create the <strong>IAM</strong> role. Please click on <strong>Create role</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732672110030/adaec50e-587b-44d3-9348-c39fffbf12fd.png" alt class="image--center mx-auto" /></p>
<p>Attach the <strong>ApacheLogsPolicy</strong>, provide a name for the role, then create the role with the name <strong>ApacheS3LogsRole</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732672661373/10472621-6494-4200-87c7-9310f40b1829.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-launch-the-ec2-instance">Launch the EC2 Instance</h1>
<p>We have pretty much took care of all prerequisites. Please head over to the <strong>EC2</strong> console and launch an <strong>Ubuntu</strong> instance. Give it the name “<strong>Apache HTTP Serve</strong>r” and ensure to keep everything in <strong>free tier</strong> (AMI, Instance type).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732665762283/1b6ed89e-1f1f-4357-a5b8-1d7159092d62.png" alt class="image--center mx-auto" /></p>
<p>Create a new <strong>key pair</strong> and in <strong>Network settings</strong>, ensure that the right <strong>VPC</strong> and <strong>private subnet</strong> are selected. Also, select the <strong>default subne</strong>t group in the <strong>Firewall</strong> section.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732896273608/8c6120e0-0677-4456-8442-d32442dd6eab.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732896582394/f643eb6f-d8ff-484f-a1c7-d58c4f33634f.png" alt class="image--center mx-auto" /></p>
<p>Scroll down to <strong>Advanced details</strong> and expand it. Click on <strong>IAM instance profile</strong> and select the <strong>ApacheS3LogsRole</strong>. We can launch the instance now.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732896645170/8c443347-8aba-4442-9004-8ba8d103bd1a.png" alt class="image--center mx-auto" /></p>
<p>The next step is to log into the instance. The easiest way to do this without a bastion host is to select the instance, go to <strong>actions</strong>, <strong>Connect</strong>.</p>
<p>In the <strong>Connect to instance</strong> screen, Click on <strong>Connect using EC2 instance Connect Endpoint</strong>. Locate the <strong>EC2 Instance Connect Endpoint</strong> section and click on Select an endpoint then <strong>Create an endpoint</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732898666634/4b543232-7585-419d-aeb4-8924f604b231.png" alt class="image--center mx-auto" /></p>
<p>On the next screen, provide a name tag, change type to <strong>EC2 Instance Connect Endpoint</strong> and select the proper <strong>VPC</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732904392232/5e97a056-795f-4872-a62f-2ffa19ad327a.png" alt class="image--center mx-auto" /></p>
<p>Now select the <strong>default security group</strong> and <strong>private subnet</strong> and create the endpoint.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732904515937/8d00ac8d-a044-4455-9a4c-59a4da8bdd75.png" alt class="image--center mx-auto" /></p>
<p>Wait for the endpoint to reach <strong>Available</strong> status before attempting to access the instance.</p>
<p>We can return to the <strong>Connect to instance</strong> screen after the endpoint is ready. The endpoint that was just created should be listed in the dropdown box. Select the endpoint and click on <strong>Connect</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732906906139/b83711aa-4e81-4174-9ac6-9852bce2516a.png" alt class="image--center mx-auto" /></p>
<p>After successfully logging into the instance, please run the following commands:</p>
<pre><code class="lang-bash"><span class="hljs-comment">#update package index:</span>
sudo apt update
<span class="hljs-comment">#install required dependencies:</span>
sudo apt install -y curl unzip
<span class="hljs-comment">#download the AWS CLI installer:</span>
curl <span class="hljs-string">"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip"</span> -o <span class="hljs-string">"awscliv2.zip"</span>
<span class="hljs-comment">#unzip the installer:</span>
unzip awscliv2.zip
<span class="hljs-comment">#run the installer:</span>
sudo ./aws/install
<span class="hljs-comment">#verify the installation:</span>
aws --version
</code></pre>
<p>Before we install the application and setup the logs, we need to make sure that the <strong>EC2</strong> instance has access to <strong>S3</strong>. Let’s upload a test file.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">echo</span> <span class="hljs-string">"This is a test file"</span> | aws s3 cp - s3://myapachelogs/testfile.txt
</code></pre>
<p>Then go ahead and attempt to view the bucket contents.</p>
<pre><code class="lang-bash">aws s3 ls s3://myapachelogs
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732913074289/aafe689f-61d6-44ef-ac08-95ca5e0459ed.png" alt class="image--center mx-auto" /></p>
<p>The screenshot above shows that the <strong>EC2</strong> instance is able to list the test file.</p>
<h1 id="heading-enable-cloudtrail">Enable CloudTrail</h1>
<p>Since we should utilize a secondary bucket just for the <strong>CloudTrail</strong> logs, let’s create one more called <strong>myapachelogscloudtrail</strong>. We can accomplish this by heading to the <strong>CloudTrail</strong> console and clicking on <strong>Create a trail</strong>. On the next screen, click on <strong>Create trail</strong> up above where the <strong>Create trail</strong> link resides.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732913972683/6cdd5f38-c631-4392-bc3e-17207f508d82.png" alt class="image--center mx-auto" /></p>
<p>Follow the screenshot below for the rest of the <strong>CloudTrail</strong> configuration. Leave everything else not in the screenshot as is.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732914538872/c70016b1-1b2e-4601-b020-51bd59fc0d7d.png" alt class="image--center mx-auto" /></p>
<p>On the next screen please make to to enable the following:</p>
<p><strong>Management events</strong> - This tracks the creation or deletion of S3 buckets, security configurations and logging setup.</p>
<p><strong>Data events</strong> - This tracks Object level API operations in S3 such as GetObject, PutObject.</p>
<p><strong>Network activity events</strong> - This tracks VPC endpoint actions from private VPC to AWS services.</p>
<p>In the <strong>Data events</strong> section choose <strong>S3</strong> as Resource type as well as <strong>Log all events</strong>. For <strong>Network activity events</strong>, select <strong>EC2</strong> and also <strong>Log all events</strong>. Click next then <strong>Create the trail</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732915467027/cee2eef0-1f4e-4e4c-941f-14d12c9e3e2d.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-install-the-application-apache">Install the Application (Apache)</h1>
<p>Run the following commands to install Apache.</p>
<pre><code class="lang-bash"><span class="hljs-comment">#install apache</span>
sudo apt install apache2 -y
<span class="hljs-comment">#start apache service</span>
sudo systemctl start apache2
<span class="hljs-comment">#enable apache to start at boot</span>
sudo systemctl <span class="hljs-built_in">enable</span> apache2
<span class="hljs-comment">#check service status</span>
sudo systemctl status apache2
</code></pre>
<p>Apache logs files are usually stored in <strong>/var/log/apache2</strong>. Let’s take a quick look at the info currently present in the logs right now.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732917563802/22d12920-8c79-46a5-8183-851bf1a4272d.png" alt class="image--center mx-auto" /></p>
<p>For now, the <strong>access.log</strong> is empty and <strong>error.log</strong> has a few lines.</p>
<h1 id="heading-log-management-setup">Log Management setup</h1>
<p>Using a built in Linux editor, create the script with the code below to setup the log management:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Set variables</span>
APACHE_LOG_DIR=<span class="hljs-string">"/var/log/apache2"</span>      <span class="hljs-comment"># Directory containing Apache logs</span>
S3_BUCKET=<span class="hljs-string">"s3://apache-log-archives"</span>  <span class="hljs-comment"># S3 bucket name</span>
RETENTION_DAYS=7                      <span class="hljs-comment"># Number of days to retain logs locally</span>

<span class="hljs-comment"># Check if the Apache log directory exists</span>
<span class="hljs-keyword">if</span> [ ! -d <span class="hljs-string">"<span class="hljs-variable">$APACHE_LOG_DIR</span>"</span> ]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Apache log directory <span class="hljs-variable">$APACHE_LOG_DIR</span> does not exist. Exiting."</span>
    <span class="hljs-built_in">exit</span> 1
<span class="hljs-keyword">fi</span>

<span class="hljs-comment"># Upload Apache logs (including rotated logs) to S3</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"Uploading Apache logs to S3 bucket: <span class="hljs-variable">$S3_BUCKET</span>..."</span>
aws s3 cp <span class="hljs-string">"<span class="hljs-variable">$APACHE_LOG_DIR</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$S3_BUCKET</span>"</span> --recursive --exclude <span class="hljs-string">"*"</span> --include <span class="hljs-string">"*.log"</span> --include <span class="hljs-string">"*.log.gz"</span>
<span class="hljs-keyword">if</span> [ $? -eq 0 ]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Apache logs successfully uploaded to S3."</span>
<span class="hljs-keyword">else</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"S3 upload failed. Please check AWS CLI configuration and bucket permissions."</span>
    <span class="hljs-built_in">exit</span> 1
<span class="hljs-keyword">fi</span>

<span class="hljs-comment"># Delete logs older than RETENTION_DAYS</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"Deleting Apache logs older than <span class="hljs-variable">$RETENTION_DAYS</span> days from <span class="hljs-variable">$APACHE_LOG_DIR</span>..."</span>
find <span class="hljs-string">"<span class="hljs-variable">$APACHE_LOG_DIR</span>"</span> -<span class="hljs-built_in">type</span> f -mtime +<span class="hljs-variable">$RETENTION_DAYS</span> -name <span class="hljs-string">"*.log"</span> -o -name <span class="hljs-string">"*.log.gz"</span> -<span class="hljs-built_in">exec</span> rm -v {} \;

<span class="hljs-comment"># Notify the user</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"Apache log management completed. Logs uploaded to S3 and old logs deleted."</span>

<span class="hljs-comment"># Exit script</span>
<span class="hljs-built_in">exit</span> 0
</code></pre>
<p>Copy and paste the script and save it as <strong>apache_log_management.sh</strong>. Then make it executable with:</p>
<pre><code class="lang-bash">chmod +x apache_log_management.sh
</code></pre>
<p><strong>Execute</strong> the shell script and verify that the upload was successful.</p>
<pre><code class="lang-bash">./apache_log_management.sh
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732928265197/4d613ca6-737c-4561-8bac-536cbe7294df.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732928332267/17a99fc7-2435-47f4-b53d-af70aa1bf30d.png" alt class="image--center mx-auto" /></p>
<p>As you can see from both screenshots, the log uploads were successful.</p>
<h1 id="heading-monitor-with-cloudtrail">Monitor with CloudTrail</h1>
<p>If we check the CloudTrail S3 bucket, we should see some data already present.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732928754513/70535e58-e26e-464c-839c-3d473b29f000.png" alt class="image--center mx-auto" /></p>
<p>I found the trail showing when the script executed the put action into <strong>S3</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732928998371/feb92660-c6e7-4567-abae-bc730a36b8ef.png" alt class="image--center mx-auto" /></p>
<p><strong>CloudTrail</strong> is a very good way to keep tabs on all <strong>API</strong> activity. In this case we are using it to monitor <strong>EC2</strong> and <strong>S3</strong> actions.</p>
<h1 id="heading-automate-the-log-backup-process">Automate the log backup process</h1>
<p>A very simple way to automate the log management process is to create a Cron job. For example:</p>
<pre><code class="lang-bash">crontab -e
</code></pre>
<pre><code class="lang-bash"><span class="hljs-comment"># this line runs the script at 2 AM</span>
0 2 * * * /path/to/apache_log_management.sh &gt;&gt; /var/<span class="hljs-built_in">log</span>/apache_log_management.log 2&gt;&amp;1
</code></pre>
<p>This <strong>cron job</strong> executes the script <strong>apache_log_management.sh</strong> located at /path/to/ every day at <strong>2:00 AM</strong>. Here's a breakdown of its components:</p>
<ol>
<li><p><strong>0 2 * * *</strong>: Specifies the schedule for the job.</p>
<ul>
<li><p><strong>0</strong> means it runs at the start of the hour.</p>
</li>
<li><p><strong>2</strong> indicates <strong>2:00 AM</strong>.</p>
</li>
<li><p>The <strong>* * *</strong> means it runs every day, every month, and every weekday.</p>
</li>
</ul>
</li>
<li><p><strong>/path/to/apache_log_management.sh</strong>: The path to the script being executed.</p>
</li>
<li><p>&gt;&gt; <strong>/var/log/apache_log_management.log</strong>: Appends the output of the script to a log file at <strong>/var/log/apache_log_management.log</strong>.</p>
</li>
<li><p><strong>2&gt;&amp;1</strong>: Redirects any errors (<strong>stderr</strong>) to the same log file, combining both standard output (<strong>stdout</strong>) and errors into a single log.</p>
</li>
</ol>
<h1 id="heading-conclusion">Conclusion</h1>
<p>So in this project we tackled key <strong>DevOps</strong> and <strong>Cloud Engineering</strong> tasks like standing up and configuring infrastructure, implementing log management as well as automating it (with <strong>bash</strong>) and integrating it with <strong>S3</strong> for secure storage. We also ensured efficient networking with a <strong>private subnet</strong>, <strong>VPC</strong> &amp; <strong>EC2</strong> endpoints and setting up <strong>CloudTrail</strong> to keep a close watch on what is going on in the environment.</p>
<p>One last thing, it’s important to clean up resources to avoid unnecessary costs and maintain a tidy <strong>AWS</strong> environment. <strong>Thanks for reading!!!</strong></p>
]]></content:encoded></item></channel></rss>