Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ingvar-goryainov authored and ipysmennyi committed Jul 8, 2024
0 parents commit 450f76b
Show file tree
Hide file tree
Showing 58 changed files with 2,377 additions and 0 deletions.
123 changes: 123 additions & 0 deletions .github/workflows/terraform-validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
name: Terraform Validate

run-name: ${{ github.actor }} - ${{ github.ref_name }}

on:
pull_request:
branches:
- main
push:
branches:
- main

workflow_dispatch:

jobs:
terraform_validate:
name: "Format and Validate Code"
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.8.4

- name: Check resource group configuration
run: |
if ! grep -q 'resource "azurerm_resource_group"' main.tf; then
echo "Resource group configuration not found in main.tf!"
exit 1
fi
echo "Resource group configuration is present."
- name: Check storage account configuration
run: |
if ! grep -q 'resource "azurerm_storage_account"' modules/storage/main.tf; then
echo "Storage account configuration not found in modules/storage/main.tf!"
exit 1
fi
echo "Storage account configuration is present."
- name: Check container configuration
run: |
if ! grep -q 'resource "azurerm_storage_container"' modules/storage/main.tf; then
echo "Storage container configuration not found in modules/storage/main.tf!"
exit 1
fi
echo "Storage container configuration is present."
- name: Check network security group configuration
run: |
if ! grep -q 'resource "azurerm_network_security_group"' modules/network/main.tf; then
echo "Network security group configuration not found in modules/network/main.tf!"
exit 1
fi
echo "Network security group configuration is present."
- name: Check virtual network configuration
run: |
if ! grep -q 'resource "azurerm_virtual_network"' modules/network/main.tf; then
echo "Virtual network configuration not found in modules/network/main.tf!"
exit 1
fi
echo "Virtual network configuration is present."
- name: Check subnet configuration
run: |
if ! grep -q 'resource "azurerm_subnet"' modules/network/main.tf; then
echo "Subnet configuration not found in modules/network/main.tf!"
exit 1
fi
echo "Subnet configuration is present."
- name: Check public IP configuration
run: |
if ! grep -q 'resource "azurerm_public_ip"' modules/network/main.tf; then
echo "Public IP configuration not found in modules/network/main.tf!"
exit 1
fi
echo "Public IP configuration is present."
- name: Check virtual machine configuration
run: |
if ! grep -q 'resource "azurerm_linux_virtual_machine"' modules/compute/main.tf; then
echo "Virtual machine configuration not found in modules/compute/main.tf!"
exit 1
fi
echo "Virtual machine configuration is present."
- name: Check network interface configuration
run: |
if ! grep -q 'resource "azurerm_network_interface"' modules/compute/main.tf; then
echo "Network interface configuration not found in modules/compute/main.tf!"
exit 1
fi
echo "Network interface configuration is present."
- name: Check VM extension configuration
run: |
if ! grep -q 'resource "azurerm_virtual_machine_extension"' modules/compute/main.tf; then
echo "VM extension configuration not found in modules/compute/main.tf!"
exit 1
fi
echo "VM extension configuration is present."
- name: Terraform Fmt
run: terraform fmt -check -recursive -diff

- name: Log in to Azure
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- name: Terraform Init
run: terraform init

- name: Terraform Validate
run: terraform validate
69 changes: 69 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Deploying Todo List Application with Terraform

In this task, you will create an Azure Virtual Machine using Terraform and deploy a ToDo List application on it using a VM extension. The Terraform configuration will create all necessary resources, including a resource group, storage account, network security group, virtual network, subnet, public IP, and the virtual machine itself.

## Prerequisites

To complete this task, you must have Terraform and Azure CLI installed and configured on your machine.

## Steps to Complete the Task

**1. Fork this Repository**

**2. Create Directory Structure**

- Create the following directory structure:
![alt text](image.png)

**3. Define Network Module**

- The network module will create a virtual network, subnet, network security group, and public IP address.
* Virtual Network: Name it `vnet`, address prefix `10.0.0.0/16`.
* Subnet: Name it `default`, address prefix `10.0.0.0/24`.
* Network Security Group: Name it `defaultnsg`.
* Public IP Address: Name it `linuxboxpip`, dynamic allocation method, and assign a DNS label generated by concatenating `matetask` and a random number.

**4. Define Compute Module**

- The compute module will create a network interface, virtual machine, and VM extension for deploying the ToDo List application.
* Network Interface: Name it `${var.vm_name}-nic`.
* Virtual Machine: Name it `matebox`, image `Ubuntu2204`, size `Standard_B1s`, SSH key `linuxboxsshkey`.
* VM Extension: Use `CustomScript` extension to execute `install-app.sh` script.


**5. Define Storage Module**

- The storage module will create a storage account and a storage container.
* Storage Account: Any name.
* Storage Container: Name it `task-artifacts`.

**6. Configure Remote State Backend**

- Define the backend configuration in backend.tf to store the state in Azure Blob Storage. Use the following parameters:
* Resource Group: `mate-azure-task-12`.
* Storage Account: `yourstorageaccount`.
* Container Name: `tfstate`.
* Key: `terraform.tfstate`.
- Ensure that you have a storage account and container created for the remote state. This can be included in your `main.tf` or managed separately.

**7. Use Modules in Main Configuration**
- Define variables in variables.tf with the following parameters:
* location: `uksouth`.
* resource_group_name: `mate-azure-task-12`.
* virtual_network_name: `vnet`.
* vnet_address_prefix: `10.0.0.0/16`.
* subnet_name: `default`.
* subnet_address_prefix: `10.0.0.0/24`.
* network_security_group_name: `defaultnsg`.
* public_ip_address_name: `linuxboxpip`.
* vm_name: `matebox`.
* vm_size: `Standard_B1s`.
* ssh_key_public: `your-public-key-content`.
* dns_label: `matetask` (you can append a random number in your script).

**8. Initialize and Apply the Configuration**

**9. Verify the Deployment**

- Access the virtual machine using the public IP address.
- Verify the application is running by visiting the public IP in a web browser.
23 changes: 23 additions & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# python related files
*.pyc
*.pyo
*.egg
*.egg-info
__pycache__

# sqlite db
*.db
*.sqlite3

# sublime related files
*.sublime-project
*.sublime-workspace

# test, build, log
build
docs/_build
dist
results
logs
*.logs
venv
21 changes: 21 additions & 0 deletions app/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2015 Christian Rotzoll

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
34 changes: 34 additions & 0 deletions app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Django ToDo list

This is a todo list web application with basic features of most web apps, i.e., accounts/login, API, and interactive UI. To do this task, you will need:

- CSS | [Skeleton](http://getskeleton.com/)
- JS | [jQuery](https://jquery.com/)

## How to launch the application

Install system pre-requirements:
```
sudo apt install python3-pip
```

Install the requirements:

```
pip install -r requirements.txt
```

Create a database schema:

```
python3 manage.py migrate
```

And then start the server (default is http://localhost:8000):


```
python manage.py runserver 0.0.0.0:8080
```

Now you can browse the [API](http://localhost:8000/api/) or start on the [landing page](http://localhost:8000/).
Empty file added app/accounts/__init__.py
Empty file.
3 changes: 3 additions & 0 deletions app/accounts/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
64 changes: 64 additions & 0 deletions app/accounts/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from django import forms
from django.contrib.auth.models import User


def widget_attrs(placeholder):
return {"class": "u-full-width", "placeholder": placeholder}


def form_kwargs(widget, label="", max_length=64):
return {"widget": widget, "label": label, "max_length": max_length}


class LoginForm(forms.Form):

username = forms.CharField(
**form_kwargs(widget=forms.TextInput(attrs=widget_attrs("Username")))
)
password = forms.CharField(
**form_kwargs(widget=forms.PasswordInput(attrs=widget_attrs("Password")))
)

def clean(self):
# Don't check if we already have errors.
if self.errors:
return self.cleaned_data

username = self.cleaned_data.get("username")
password = self.cleaned_data.get("password")
user = User.objects.filter(username=username).first()

if not user or not user.check_password(password):
raise forms.ValidationError("Incorrect username and/or password.")

return self.cleaned_data


class RegistrationForm(forms.Form):

username = forms.CharField(
**form_kwargs(widget=forms.TextInput(attrs=widget_attrs("Username")))
)

email = forms.EmailField(
**form_kwargs(widget=forms.TextInput(attrs=widget_attrs("Email")))
)

password = forms.CharField(
**form_kwargs(widget=forms.PasswordInput(attrs=widget_attrs("Password")))
)

password_confirmation = forms.CharField(
**form_kwargs(
widget=forms.PasswordInput(attrs=widget_attrs("Password confirmation"))
)
)

def clean(self):
password = self.cleaned_data.get("password")
password_confirmation = self.cleaned_data.get("password_confirmation")

if password and password != password_confirmation:
raise forms.ValidationError("Passwords don't match.")

return self.cleaned_data
Empty file.
3 changes: 3 additions & 0 deletions app/accounts/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.db import models

# Create your models here.
20 changes: 20 additions & 0 deletions app/accounts/templates/accounts/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{% extends "base.html" %}

{% block body %}
<section class="header">
<h2 class="title">Login</h2>
<div class="row">
<div class="three columns value-prop"></div>
<div class="six columns">
<form method=post>
{% csrf_token %}
<dl>
<dd>{{ form }}
<dt><input type="submit" class="button button-primary" value="Login">
</dl>
</form>
<p>New user? <a href="{% url 'auth:register' %}">Click here to register</a>.</p>
</div>
</div>
</section>
{% endblock %}
20 changes: 20 additions & 0 deletions app/accounts/templates/accounts/register.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{% extends "base.html" %}

{% block body %}
<section class="header">
<h2 class="title">Register</h2>
<div class="row">
<div class="three columns value-prop"></div>
<div class="six columns">
<form method=post>
{% csrf_token %}
<dl>
<dd>{{ form }}
<dt><input type="submit" class="button button-primary" value="Register">
</dl>
</form>
<p>Already registerd? <a href="{% url 'auth:login' %}">Click here to login</a>.</p>
</div>
</div>
</section>
{% endblock %}
Loading

0 comments on commit 450f76b

Please sign in to comment.