Dev Blog 1 - How To Spiral Out

Monday 8th April 2024

This blog is not music related. I am intending to document as much of my process as possible, including collating the process steps from setting up the website to testing, building and deploying it to AWS using automated CI/CD pipelines. It will be a little dry, because a lot happened over the course of the last week. Step-by-step stuff will start in part 2. This is mostly about my approach, and will only really be of interest to people who are interested in web development and DevOps.

I am writing this in April 2024, so if you're reading this in six months, something is probably out of date; sorry about that.

First, I chose my framework. I chose Laravel, because it is a high quality industry standard framework with a powerful toolkit under the hood, and I wanted to stretch my skills with it. Also, I want to create a solid, secure website, which gets extremely difficult outside of modern frameworks.

I decided to use TailwindCSS for the front end styling because again, it seemed like a really powerful tool to enable front end design, and I wanted to learn how to use it.

I started with vue but as this was a new language entirely progress was slow, and I had a homepage to launch, so I stuck to mostly static html templates initially.

I build the website layout by hacking out the starter kit example homepage to begin with, to get something visual.

Once happy with v1.0 I registered a new account with Amazon AWS, and set up a basic Amazon Linux EC2 Instance and configured it manually.

I pointed the domain at the server and I used SCP to upload the website files, and after a bit of debugging I got everything up and running.

I replaced the mailing list link with a simple mailchimp subscription form.

I heavily redesigned and restuctured the site to make it feel more like an online portfolio and less like a website from 1994.

I then rebuilt the shows widget to pull from a CSV file so I didn't have to configure a database and could update shows on the fly.

I rebuilt the entire thing one element at a time splitting everything out into separate components and learning a lot about tailwindCSS practices in the process

I created a new repository on bitbucket, signed up to Snyk and set up automated CI/CD vulnerability testing on the repo. I also created a jira board for feature tracking.

I fixed some security issues raised by Snyk, and also set up Pint CS Fixer and Pest PHP testing on the repository.

I created models and migrations for the shows, links and layout components, filled them out and ran them to create the base tables, then populated them with the data from the html page.

From here I replaced the links and shows with dynamic database driven elements

There were security issues with the CSS files being thrown on the site so I registered an SSL certificate and moved everything over to HTTPS to resolve the errors.

Happy again with the static web version, I also set up a bitbucket pipeline template ready to configure for automated pint/pest running during deployment, ultimately planning to run the entire server build this way.

Realising it would be a good exercise to have a blog or something on the growth of this project, I decided to bring user auth to the top of the cue so I can get to work making or setting up a simple CMS. For the auth framework I chose laravel breeze

After pricing up the cost of running the site in a complete ELB stack (Load Balancer > Application Layer > Database) I decided that paying £600/year for a technical demo was a bit more than I would like, so I set up and configured an ubuntu server which I could host everything on. This isn't an ideal solution as this will create a lot of extra overhead on the application server, and a service interruption would be potentially nuclear, but this is a personal project so I'm willing to roll those dice to reduce the bill by about 85%.

Next was the big task... pipeline deployment. I set about configuring AWS with CodeDeploy using Bitbucket as a Web Identity Provider. This took an entire day, and I had some further debugging to do the following day.

After a LOT of debugging I finally got to see the thing I was looking for. A big fat green tick to say the deployment was complete. I had tested, built and deployed my first application to AWS via an automated CI/CD pipeline. Now I just had to clean up after myself and finish building the next parts of my website, because I'd used up the last of my free deployment minutes with that final push!

Dev Blog 2 - How I Learned To Stop Worrying And Love The Pipe

Fridat 12th April 2024

AWS is big. AWS is complicated. Bitbucket pipelines are complicated. Laravel is... well, it's not complicated, but we are going to do that anyway. This is my messy and very rough guide to setting up an AWS environment and deploying a laravel app to it using bitbucket and AWS codedeploy. It will be very no-frills for the time being unless I get really into styling, but right now that is not my priority, but I wanted to document my process in a way that might be of use to others.

Setting Up The AWS Account

Create your aws account.
Log in as root user.
Set up MFA (Recommended), & select authenticator app.
Make sure your local zone (top left) is set correctly - London for me, but the default is Virginia!
Go to IAM identity center and enable it.
Select enable with organisations if prompted.
In the IAM identity center, under multi account permissions, select permission sets.
Create permission set.
Select predefined permission set.
Select administrative access.
Click next.
Click create.
Go to users.
Select create user.
Add name.
Add your user email.
Click next.
Create group.
Enter a name.
Hit save and go back to your user window.
Select your new group.
Click next.
Confirm.
In IAM identity center, under multi account permissions, select AWS accounts.
Select assign users and groups.
Select your group and click next.
Select your policy and click next.
Submit.
Go to your email inbox.
Accept the invite.
Create a new password.
Set up MFA.
Make sure you're in the right region (for me it randomly decided to put me in stockholm this time...).
Go to the IAM console (not identity center).
Click create role.
Select custom trust policy (we want to limit it to everything except the regions we're using).
Copy and paste:

   
{
    "Version": "2012-10-17",
    "Statement": [
        {
         "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "codedeploy.eu-west-1.amazonaws.com",
                    "codedeploy.eu-west-2.amazonaws.com",
                    "codedeploy.eu-west-3.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
   

Click next.
Attach AWSCodeDeployRole.
Click next.
Add a role name.
Create role.

Launch EC2 Instance in AWS Cloud

Click launch instance.
Add server name.
Select the Ubuntu AMI (x86).
Instance type: t2.micro.
Create new key pair.
Enter key pair name.
Select rsa.
Select .pem.
Click Create Key Pair and download your key.
Select create security group.
Allows ssh traffic from your IP only if you have a fixed IP, or anywhere if you don't. most people don't have a fixed IP.
Allow HTTP and HTTPS traffic from anywhere.
Add an 8gb root volume.
Launch instance.

Create CodeDeploy Application

Go to CodeDeploy dashboard in AWS.
Under deploy, click applications.
Create application.
Name it.
Select EC2/On Premises.
Create.
Create deployment group.
Name it.
Select your SERVICE ROLE, not the regular role. hopefully you gave it a different name :).
Choose 'in place'
In environment configuration, select Amazon EC2 Instances.
In 'Key' Select Name.
Select or input the name you tagged your EC2 instance with.
In agent configuration, make sure 'Now and schedule updates' is selected.
Leave the default allatonce selection in deployment configuration.
Deselect load balancing because we're not using a load balancer.
Create deployment group.

Set Up Bitbucket Pipeline

Some of this is missing because I did the basic enabling etc before writing everything down.
The process was simple though, go to bitbucket, enable pipelines, configure default pipeline, basically does the first part for you.
Go to repo settings.
Under workflow, go to branch restrictions.
Select by branch type.
Set as follows:

Master/
No write access (I'm skipping a step here because my 'production' environment is a development site. I'll split to dev and release when I push this to live.
No changes requested
No unresolved tasks
Maximum number of commits behind destination branch: 1 (trust me. you don't want to make your features disappear on production without explanation)
Reset requested changes when branch is modified

In repository settings go to deployments
In production add the following key value pairs (ticking 'secured' masks the value and makes it uneditable. do this for passwords and secret keys and any information which could compromise your security if leaked):

                            
AWS_ACCESS_KEY_ID: (From access keys in your AWS SSO dashboard)
AWS_SECRET_ACCESS_KEY: (From access keys in your AWS SSO dashboard)
AWS_DEFAULT_REGION: (whatever you chose earlier)
APP_NAME: An app name 
APP ENV: production
APP_URL: the url you pointed at your server with your domain provider
APP_TIMEZONE: Europe/London
DB_HOST: localhost
DB_PORT: 3306
DB_DATABASE: the db you created earlier
DB_USERNAME: the db user you created eatlier
DB_PASSWORD: you know the drill by now
CD_APP_NAME: your codeDeploy application name
CD_GROUP: your codeDeploy deployment group
S3_BUCKET: The name of the S3 bucket to use to deployment artifacts
                            
                        

That's almost it for Bitbucket, now we just need to allow it to communicate with AWS.

Set Up Bitbucket as an Identity Provider

In bitbucket, open repository settings
OpenID connect.
Copy the provider URL.
In a new tab open AWS go to IAM dashboard.
Identity providers.
Create identity provider.
Select OpenID Connect.
Paste the provider url.
Click get thumbprint.
Back in your bitbucket tab copy the audience.
Paste the audience in AWS.
Create provider.
Go to roles.
Create role.
Select web identity.
Select your provider and audience.
Click next.
In permissions, select AWSCodeDeployFullAccess and AmazonS3FullAccess (we will restrict this later).
Click next.
Enter a name.
Create role.

Building A Laravel Application (From A Starter Kit)

Honestly, I am already covering so many steps here, that I am going to skip over the part where you already installed WSL2 and Docker on your local machine...
in your local environment

                        
curl -s https://laravel.build/app-name?with=mysql,redis,selenium | bash
cd app-name
                        
                   

Edit your .env file to personalise your site, then...

                        
./vendor/bin/sail up
                        
                   

Press Ctrl Z .if it hangs on the console log.

                        
./vendor/bin/sail composer require laravel/breeze --dev
./vendor/bin/sail composer require laravel/pint --dev
./vendor/bin/sail artisan migrate
./vendor/bin/sail npm install -D tailwindcss postcss autoprefixer
./vendor/bin/sail npx tailwindcss init -p
                       
                   

In tailwind.config.js, update your content array to match below:

                       
content: [
    "./resources/**/*.blade.php",
    "./resources/**/*.js",
    "./resources/**/*.vue",
  ]
                       
                    

In your resources/css/app.css file, add:

                                        
@tailwind base;
@tailwind components;
@tailwind utilities;
                       
                    

To use, in the head of your html template, add:

                                        
@vite('resources/css/app.css')                       
                    

Finally in your console:

                                        
./vendor/bin/sail npm run dev &
                       
                    

Your local environment should now be up and running and visible at http://localhost

Configure The Pipeline

In your local repo, open bitbucket-pipelines.yml and copy and paste:

                       
image: php:8.2-fpm

definitions: 
  steps:
    - step: &build-test
        name: Build and test
        script:
          - apt-get update && apt-get install -y zip unzip
          - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
          - cp .env.pipe .env
          - COMPOSER_ALLOW_SUPERUSER=1 composer install
          - php artisan test --parallel --processes=4
    - step: &build-lint
        name: Lint
        script:
          - apt-get update && apt-get install -y zip unzip
          - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
          - cp .env.pipe .env
          - COMPOSER_ALLOW_SUPERUSER=1 composer install
          - php ./vendor/bin/pint

    - step: &build-deploy
        name: Deploy
        deployment: Production
        oidc: true
        script:
          - apt-get update && apt-get install -y zip unzip
          - zip -r $APP_NAME-$RELEASE .
          - pipe: atlassian/aws-code-deploy:1.5.0
            variables:
              AWS_DEFAULT_REGION: $AWS_DEFAULT_REGION
              AWS_OIDC_ROLE_ARN: $AWS_ROLE_ARN
              APPLICATION_NAME: $CD_APP_NAME
              DEPLOYMENT_GROUP: $CD_GROUP
              COMMAND: 'upload'
              S3_BUCKET: $S3_BUCKET
              ZIP_FILE: $APP_NAME-$RELEASE

          - pipe: atlassian/aws-code-deploy:1.5.0
            variables:
              AWS_DEFAULT_REGION: $AWS_DEFAULT_REGION
              AWS_OIDC_ROLE_ARN: $AWS_ROLE_ARN
              COMMAND: 'deploy'
              APPLICATION_NAME: $CD_APP_NAME
              DEPLOYMENT_GROUP: $CD_GROUP
              S3_BUCKET: $S3_BUCKET
              WAIT: 'true'
              VERSION_LABEL: '$BITBUCKET_REPO_FULL_NAME-$BITBUCKET_BUILD_NUMBER'
pipelines:
  default:
    - parallel:
        - step: *build-test
        - step: *build-lint
  branches:
    master:
        - step: *build-test
        - step: *build-deploy
                       
                    

If everything else was set up correctly, bitbucket should be able to use this to communicate with your EC2 instance.

Configure The Application Deployment

This is the home straight! All we need to do now is make CodeDeploy put our files in the right place and run any scripts which are required.
I also have not written it yet.