... and thanks for stopping in. This site is a living portfolio to demonstrate technical skills in an enjoyable way. I want to create something special for both my friends and future employers alike. A calm dark corner on the internet to chill and catch up with me, let me know what you've been up to, or browse through what I've been building.
Click the Blog-App and say hi, or keep scrolling down and find out how it works.
More soon,
Nick <3
#!/home/ubuntu/resume-server/env/bin/python
from flask import Flask, request, render_template, redirect, url_for, session
from authlib.integrations.flask_client import OAuth
import os
import boto3
from dotenv import load_dotenv
import pyodbc
load_dotenv()
app = Flask(__name__)
#authorize configuration
app.secret_key = os.environ.get("flask_secret_key", os.urandom(24))
oauth = OAuth(app)
oauth.register(
name = 'oidc',
authority=os.environ.get("oidc_authority"),
client_id=os.environ.get("oidc_client_id"),
client_secret=os.environ.get("oidc_client_secret"),
server_metadata_url=os.environ.get("oidc_metadata_url"),
client_kwargs={'scope' : 'openid email'}
)
#connect DB
def get_db_connection():
conn_str = (
f"DRIVER={{ODBC Driver 17 for SQL Server}};"
f"SERVER={os.environ['DB_HOST']},{os.environ['DB_PORT']};"
f"DATABASE={os.environ['DB_NAME']};"
f"UID={os.environ['DB_USER']};"
f"PWD={os.environ['DB_PASSWORD']}"
)
conn = pyodbc.connect(conn_str)
return conn
#set routes
@app.route("/")
def home():
user = session.get('user')
return render_template('home.html', user=user)
@app.route('/login')
def login():
#alternate option to redirect to /authorize
redirect_uri = url_for('authorize', _external=True)
return oauth.oidc.authorize_redirect(redirect_uri)
#return oauth.oidc.authorize_redirect('https://nickonthe.net')
@app.route('/authorize')
def authorize():
token = oauth.oidc.authorize_access_token()
user = oauth.oidc.userinfo(token=token)
print ("user info: ", user)
#store session info
session['user'] = user
#store email and sub
email = user.get("email")
sub = user.get("sub")
#add to DB
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("""
MERGE WebsiteUser AS target
USING (SELECT ? AS email, ? AS sub) AS source
ON target.email = source.email
WHEN MATCHED THEN
UPDATE SET sub = source.sub
WHEN NOT MATCHED THEN
INSERT (email, sub) VALUES (source.email, source.sub);
""", (email, sub))
conn.commit()
cursor.close()
conn.close()
return redirect(url_for('home'))
@app.route('/logout')
def logout():
session.pop('user', None)
return redirect(url_for('home'))
@app.route("/about")
def about():
return render_template('about.html')
@app.route("/testwall")
def testwall():
conn = get_db_connection()
cursor = conn.cursor()
#sql for table joins to gather needed info
cursor.execute("""
SELECT b.Title, b.TextContent, u.username
FROM BlogPost b
LEFT JOIN WebsiteUser u ON b.sub = u.sub
ORDER BY b.CreatedAt DESC
""")
#get results as list of dicts.
columns = [column[0] for column in cursor.description]
items = [dict(zip(columns, row)) for row in cursor.fetchall()]
cursor.close()
conn.close()
return render_template('testwall.html', items=items)
@app.route('/testpost', methods=['GET', 'POST'])
def testpost():
#check if logged in
if 'user' not in session:
return redirect(url_for('login'))
if request.method == 'POST':
user = session['user']
sub = user.get('sub') #cognito uniqueID
#get form data
title = request.form.get('title')
text_content = request.form.get('text_content')
if title and text_content:
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("""
INSERT INTO BlogPost (sub, Title, TextContent)
VALUES (?, ?, ?)
""", (sub, title, text_content))
conn.commit()
cursor.close()
conn.close()
return redirect(url_for('testwall')) # redirect to the wall to see the post
#GET request: show form
return render_template('testpost.html')
print ('running server')
import boto3
import os
import sys
import time
from datetime import datetime, timezone
from time import gmtime, strftime
def start_rds_all():
region=os.environ['REGION']
key=os.environ['KEY']
value=os.environ['VALUE']
client = boto3.client('rds', region_name=region)
response = client.describe_db_instances()
v_readReplica=[]
for i in response['DBInstances']:
readReplica=i['ReadReplicaDBInstanceIdentifiers']
v_readReplica.extend(readReplica)
for i in response['DBInstances']:
#The if condition below filters aurora clusters from single instance databases as boto3 commands defer to start the aurora clusters.
if i['Engine'] not in ['aurora-mysql','aurora-postgresql']:
#The if condition below filters Read replicas.
if i['DBInstanceIdentifier'] not in v_readReplica and len(i['ReadReplicaDBInstanceIdentifiers']) == 0:
arn=i['DBInstanceArn']
resp2=client.list_tags_for_resource(ResourceName=arn)
#check if the RDS instance is part of the Auto-Shutdown group.
if 0==len(resp2['TagList']):
print('DB Instance {0} is not part of autoshutdown'.format(i['DBInstanceIdentifier']))
else:
for tag in resp2['TagList']:
if tag['Key']==key and tag['Value']==value:
if i['DBInstanceStatus'] == 'available':
print('{0} DB instance is already available'.format(i['DBInstanceIdentifier']))
elif i['DBInstanceStatus'] == 'stopped':
client.start_db_instance(DBInstanceIdentifier = i['DBInstanceIdentifier'])
print('Started DB Instance {0}'.format(i['DBInstanceIdentifier']))
elif i['DBInstanceStatus']=='starting':
print('DB Instance {0} is already in starting state'.format(i['DBInstanceIdentifier']))
elif i['DBInstanceStatus']=='stopping':
print('DB Instance {0} is in stopping state. Please wait before starting'.format(i['DBInstanceIdentifier']))
elif tag['Key']!=key and tag['Value']!=value:
print('DB instance {0} is not part of autoshutdown'.format(i['DBInstanceIdentifier']))
elif len(tag['Key']) == 0 or len(tag['Value']) == 0:
print('DB Instance {0} is not part of autoShutdown'.format(i['DBInstanceIdentifier']))
elif i['DBInstanceIdentifier'] in v_readReplica:
print('DB Instance {0} is a Read Replica.'.format(i['DBInstanceIdentifier']))
else:
print('DB Instance {0} has a read replica. Cannot shutdown & start a database with Read Replica'.format(i['DBInstanceIdentifier']))
response=client.describe_db_clusters()
for i in response['DBClusters']:
cluarn=i['DBClusterArn']
resp2=client.list_tags_for_resource(ResourceName=cluarn)
if 0==len(resp2['TagList']):
print('DB Cluster {0} is not part of autoshutdown'.format(i['DBClusterIdentifier']))
else:
for tag in resp2['TagList']:
if tag['Key']==key and tag['Value']==value:
if i['Status'] == 'available':
print('{0} DB Cluster is already available'.format(i['DBClusterIdentifier']))
elif i['Status'] == 'stopped':
client.start_db_cluster(DBClusterIdentifier=i['DBClusterIdentifier'])
print('Started Cluster {0}'.format(i['DBClusterIdentifier']))
elif i['Status']=='starting':
print('cluster {0} is already in starting state.'.format(i['DBClusterIdentifier']))
elif i['Status']=='stopping':
print('cluster {0} is in stopping state. Please wait before starting'.format(i['DBClusterIdentifier']))
elif tag['Key'] != key and tag['Value'] != value:
print('DB Cluster {0} is not part of autoshutdown'.format(i['DBClusterIdentifier']))
else:
print('DB Instance {0} is not part of autoShutdown'.format(i['DBClusterIdentifier']))
def lambda_handler(event, context):
start_rds_all()
import boto3
import os
import sys
import time
from datetime import datetime, timezone
from time import gmtime, strftime
def shut_rds_all():
region=os.environ['REGION']
key=os.environ['KEY']
value=os.environ['VALUE']
client = boto3.client('rds', region_name=region)
response = client.describe_db_instances()
v_readReplica=[]
for i in response['DBInstances']:
readReplica=i['ReadReplicaDBInstanceIdentifiers']
v_readReplica.extend(readReplica)
for i in response['DBInstances']:
#The if condition below filters aurora clusters from single instance databases as boto3 commands defer to stop the aurora clusters.
if i['Engine'] not in ['aurora-mysql','aurora-postgresql']:
#The if condition below filters Read replicas.
if i['DBInstanceIdentifier'] not in v_readReplica and len(i['ReadReplicaDBInstanceIdentifiers']) == 0:
arn=i['DBInstanceArn']
resp2=client.list_tags_for_resource(ResourceName=arn)
#check if the RDS instance is part of the Auto-Shutdown group.
if 0==len(resp2['TagList']):
print('DB Instance {0} is not part of autoshutdown'.format(i['DBInstanceIdentifier']))
else:
for tag in resp2['TagList']:
#If the tags match, then stop the instances by validating the current status.
if tag['Key']==key and tag['Value']==value:
if i['DBInstanceStatus'] == 'available':
client.stop_db_instance(DBInstanceIdentifier = i['DBInstanceIdentifier'])
print('stopping DB instance {0}'.format(i['DBInstanceIdentifier']))
elif i['DBInstanceStatus'] == 'stopped':
print('DB Instance {0} is already stopped'.format(i['DBInstanceIdentifier']))
elif i['DBInstanceStatus']=='starting':
print('DB Instance {0} is in starting state. Please stop the cluster after starting is complete'.format(i['DBInstanceIdentifier']))
elif i['DBInstanceStatus']=='stopping':
print('DB Instance {0} is already in stopping state.'.format(i['DBInstanceIdentifier']))
elif tag['Key']!=key and tag['Value']!=value:
print('DB instance {0} is not part of autoshutdown'.format(i['DBInstanceIdentifier']))
elif len(tag['Key']) == 0 or len(tag['Value']) == 0:
print('DB Instance {0} is not part of auroShutdown'.format(i['DBInstanceIdentifier']))
elif i['DBInstanceIdentifier'] in v_readReplica:
print('DB Instance {0} is a Read Replica. Cannot shutdown a Read Replica instance'.format(i['DBInstanceIdentifier']))
else:
print('DB Instance {0} has a read replica. Cannot shutdown a database with Read Replica'.format(i['DBInstanceIdentifier']))
response=client.describe_db_clusters()
for i in response['DBClusters']:
cluarn=i['DBClusterArn']
resp2=client.list_tags_for_resource(ResourceName=cluarn)
if 0==len(resp2['TagList']):
print('DB Cluster {0} is not part of autoshutdown'.format(i['DBClusterIdentifier']))
else:
for tag in resp2['TagList']:
if tag['Key']==key and tag['Value']==value:
if i['Status'] == 'available':
client.stop_db_cluster(DBClusterIdentifier=i['DBClusterIdentifier'])
print('stopping DB cluster {0}'.format(i['DBClusterIdentifier']))
elif i['Status'] == 'stopped':
print('DB Cluster {0} is already stopped'.format(i['DBClusterIdentifier']))
elif i['Status']=='starting':
print('DB Cluster {0} is in starting state. Please stop the cluster after starting is complete'.format(i['DBClusterIdentifier']))
elif i['Status']=='stopping':
print('DB Cluster {0} is already in stopping state.'.format(i['DBClusterIdentifier']))
elif tag['Key'] != key and tag['Value'] != value:
print('DB Cluster {0} is not part of autoshutdown'.format(i['DBClusterIdentifier']))
else:
print('DB Instance {0} is not part of auroShutdown'.format(i['DBClusterIdentifier']))
def lambda_handler(event, context):
shut_rds_all()
AWSTemplateFormatVersion: "2010-09-09"
Description: Flask Blog VPC + Public Subnet + EC2 + Elastic IP (edited for publishing)
Resources:
BlogVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: BlogVPC
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref BlogVPC
CidrBlock: 10.0.1.0/24
MapPublicIpOnLaunch: true
AvailabilityZone: !Select [0, !GetAZs '']
Tags:
- Key: Name
Value: BlogPublicSubnet
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: BlogIGW
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref BlogVPC
InternetGatewayId: !Ref InternetGateway
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref BlogVPC
Tags:
- Key: Name
Value: PublicRouteTable
PublicRoute:
Type: AWS::EC2::Route
DependsOn: AttachGateway
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
SubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref PublicRouteTable
BlogSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow HTTP, HTTPS, and SSH
VpcId: !Ref BlogVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: x.x.x.x/32
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
BlogInstance:
Type: AWS::EC2::Instance
Properties:
InstanceType: t3.micro
ImageId: #specific ami should be linked below :)
SubnetId: !Ref PublicSubnet
SecurityGroupIds: [!Ref BlogSecurityGroup]
Tags:
- Key: Name
Value: BlogEC2
BlogEIPAssociation:
Type: AWS::EC2::EIPAssociation
Properties:
AllocationId: eipalloc-ChangeMe!
InstanceId: !Ref BlogInstance
...
...
...
The web server runs on an Ubuntu EC2 instance. The AMI is linked in the sources section below. The main app is built with Flask, served through Gunicorn, and reverse proxied by Nginx. I took an extra step to configure Gunicorn to enable HTTPS using Let's Encrypt SSL/TLS certs. In my case, it was a cost effective alternative to using AWS App Load Balancers required for AWS Certificate Manager
Database configuration, design, and development all by Michael Karpinski.