LAB 55 (3 TIER APPLICATION SETUP) - arunsinghchauhan38-arch/CLOUDARCHITECTDOC GitHub Wiki
The Three-Tier Architecture is a widely adopted enterprise design pattern that separates applications into three logical layers:
-
Web Layer (Presentation Layer)
-
Application Layer (Business Logic Layer)
-
Database Layer (Data Layer)
This model enhances security, scalability, availability, and manageability in cloud environments.
Internet β DNS β OCI WAF β Public Load Balancer β Backend Set β Application Servers β Database Servers
Internet
|
|
+------------+
| DNS |
+------------+
|
|
+------------+
| OCI WAF |
+------------+
|
|
+----------------------+
| Public Load Balancer |
| (Public Subnet) |
+----------------------+
|
|
+---------------+
| Backend Set |
+---------------+
|
|
+----------------------------------+
| Application Servers |
| (App Subnet - Private) |
+----------------------------------+
|
|
+----------------------------------+
| Database Servers |
| (DB Subnet - Private) |
+----------------------------------+
-
DNS resolves the application domain to the Load Balancerβs public IP.
-
OCI WAF inspects traffic, blocking malicious requests (SQL Injection, XSS, CSRF, DDoS).
-
Public Load Balancer distributes traffic across backend application servers.
-
Application Servers (private subnet) process business logic and API requests.
-
Database Servers (private subnet) handle persistent data storage and queries.
-
User Access β DNS resolves domain β WAF filters malicious traffic β request reaches Load Balancer.
-
Load Balancer β Listens on port 80/443, terminates SSL, performs health checks, forwards traffic to backend set.
-
Backend Set β Routes requests using policies (Round Robin, Least Connections, IP Hash).
-
Application Servers β Execute business logic, authentication, API processing.
-
Database Servers β Handle queries (MySQL 3306, Oracle 1521, PostgreSQL 5432).
-
Response Returned β Data flows back through Load Balancer β User.
Create separate subnets for the Application Tier, Load Balancer Tier, and Database Tier, with a dedicated Route Table and Security List associated with each subnet.
asus_pvt_subnet
βββ asus_rt
βββ asus_sl
asus_web_subnet
βββ asus_web_rt
βββ asus_web_sub_sl
asus_db_subnet
βββ asus_db_rt
βββ asus_db_sub_sl
Connect to the server using SSH.
yum install httpd
systemctl start httpd
systemctl enable httpd
Verify:
systemctl status httpd
The status should show:
Active: active (running)
- Go to html using below command
cd /var/www/html
- Create index file and paste python script
vi index.html
Add:
To go into Insert mode
Press i key
To Save the file
Esc+:wq! β Save and exit.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Local ITSM Tool</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
background: #f4f4f4;
color: #333;
}
.header {
background: #2c3e50;
color: white;
padding: 1rem 2rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.header h1 {
font-size: 1.5rem;
font-weight: 500;
}
.nav {
background: #34495e;
padding: 0.5rem 2rem;
display: flex;
gap: 1rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.nav button {
background: transparent;
color: white;
border: none;
padding: 0.75rem 1.5rem;
cursor: pointer;
font-size: 0.95rem;
border-radius: 4px;
transition: background 0.3s;
}
.nav button:hover {
background: rgba(255,255,255,0.1);
}
.nav button.active {
background: #3498db;
}
.container {
max-width: 1400px;
margin: 2rem auto;
padding: 0 2rem;
}
.actions {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
}
.btn {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 0.95rem;
transition: all 0.3s;
font-weight: 500;
}
.btn-primary {
background: #3498db;
color: white;
}
.btn-primary:hover {
background: #2980b9;
}
.btn-secondary {
background: #95a5a6;
color: white;
}
.btn-secondary:hover {
background: #7f8c8d;
}
.btn-success {
background: #27ae60;
color: white;
}
.btn-success:hover {
background: #229954;
}
.card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
overflow: hidden;
}
.card-header {
background: #ecf0f1;
padding: 1rem 1.5rem;
border-bottom: 1px solid #ddd;
font-weight: 600;
font-size: 1.1rem;
}
.card-body {
padding: 1.5rem;
}
table {
width: 100%;
border-collapse: collapse;
}
th {
background: #ecf0f1;
padding: 0.75rem;
text-align: left;
font-weight: 600;
border-bottom: 2px solid #bdc3c7;
}
td {
padding: 0.75rem;
border-bottom: 1px solid #ecf0f1;
}
tr:hover {
background: #f8f9fa;
}
.badge {
padding: 0.25rem 0.75rem;
border-radius: 12px;
font-size: 0.85rem;
font-weight: 500;
display: inline-block;
}
.badge-new { background: #3498db; color: white; }
.badge-inprogress { background: #f39c12; color: white; }
.badge-resolved { background: #27ae60; color: white; }
.badge-closed { background: #95a5a6; color: white; }
.badge-pending { background: #e67e22; color: white; }
.badge-approved { background: #27ae60; color: white; }
.badge-rejected { background: #e74c3c; color: white; }
.badge-draft { background: #95a5a6; color: white; }
.badge-scheduled { background: #9b59b6; color: white; }
.badge-implemented { background: #16a085; color: white; }
.priority-critical { color: #e74c3c; font-weight: 600; }
.priority-high { color: #e67e22; font-weight: 600; }
.priority-medium { color: #f39c12; font-weight: 600; }
.priority-low { color: #27ae60; font-weight: 600; }
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 1000;
overflow-y: auto;
}
.modal.active {
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
.modal-content {
background: white;
border-radius: 8px;
width: 100%;
max-width: 800px;
max-height: 90vh;
overflow-y: auto;
}
.modal-header {
padding: 1.5rem;
border-bottom: 1px solid #ecf0f1;
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-header h2 {
font-size: 1.5rem;
font-weight: 600;
}
.modal-body {
padding: 1.5rem;
}
.modal-footer {
padding: 1.5rem;
border-top: 1px solid #ecf0f1;
display: flex;
justify-content: flex-end;
gap: 1rem;
}
.close-btn {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: #7f8c8d;
}
.close-btn:hover {
color: #2c3e50;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: #2c3e50;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 0.95rem;
font-family: inherit;
}
.form-group textarea {
min-height: 100px;
resize: vertical;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.section-hidden {
display: none;
}
.record-link {
color: #3498db;
text-decoration: none;
cursor: pointer;
}
.record-link:hover {
text-decoration: underline;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.stat-card {
background: white;
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.stat-card h3 {
font-size: 2rem;
color: #3498db;
margin-bottom: 0.5rem;
}
.stat-card p {
color: #7f8c8d;
font-size: 0.9rem;
}
.empty-state {
text-align: center;
padding: 3rem;
color: #7f8c8d;
}
.empty-state-icon {
font-size: 3rem;
margin-bottom: 1rem;
}
.search-filter {
margin-bottom: 1.5rem;
}
.search-filter input {
width: 100%;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 0.95rem;
}
.action-buttons {
display: flex;
gap: 0.5rem;
}
.action-btn {
padding: 0.25rem 0.75rem;
font-size: 0.85rem;
border-radius: 4px;
border: none;
cursor: pointer;
transition: all 0.3s;
}
.action-btn-view {
background: #3498db;
color: white;
}
.action-btn-view:hover {
background: #2980b9;
}
.action-btn-edit {
background: #f39c12;
color: white;
}
.action-btn-edit:hover {
background: #e67e22;
}
.btn-danger {
background: #e74c3c;
color: white;
}
.btn-danger:hover {
background: #c0392b;
}
.detail-view {
display: grid;
gap: 1rem;
}
.detail-row {
display: grid;
grid-template-columns: 200px 1fr;
padding: 0.75rem 0;
border-bottom: 1px solid #ecf0f1;
}
.detail-label {
font-weight: 600;
color: #2c3e50;
}
.detail-value {
color: #555;
}
@media (max-width: 768px) {
.form-row {
grid-template-columns: 1fr;
}
.detail-row {
grid-template-columns: 1fr;
gap: 0.25rem;
}
.container {
padding: 0 1rem;
}
.nav {
overflow-x: auto;
}
}
</style>
</head>
<body>
<div class="header">
<h1>π§ Local ITSM Tool</h1>
</div>
<div class="nav">
<button class="active" onclick="switchSection('incidents')">Incidents</button>
<button onclick="switchSection('ritm')">Request Items (RITM)</button>
<button onclick="switchSection('changes')">Change Management</button>
<button onclick="switchSection('dashboard')">Dashboard</button>
</div>
<div class="container">
<!-- Dashboard Section -->
<div id="dashboard-section" class="section-hidden">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem;">
<h2 style="margin: 0; color: #2c3e50;">Dashboard Overview</h2>
<button class="btn btn-danger" onclick="clearAllData()">ποΈ Clear All Data</button>
</div>
<div class="stats">
<div class="stat-card">
<h3 id="stat-incidents">0</h3>
<p>Total Incidents</p>
</div>
<div class="stat-card">
<h3 id="stat-ritm">0</h3>
<p>Request Items</p>
</div>
<div class="stat-card">
<h3 id="stat-changes">0</h3>
<p>Changes</p>
</div>
<div class="stat-card">
<h3 id="stat-open">0</h3>
<p>Open Items</p>
</div>
</div>
</div>
<!-- Incidents Section -->
<div id="incidents-section">
<div class="actions">
<button class="btn btn-primary" onclick="openCreateModal('incident')">+ Create New Incident</button>
<button class="btn btn-secondary" onclick="exportData('incidents')">Export to JSON</button>
<button class="btn btn-danger" onclick="clearAllRecords('incidents')">Clear All Records</button>
</div>
<div class="search-filter">
<input type="text" placeholder="Search incidents..." oninput="filterTable('incidents', this.value)">
</div>
<div class="card">
<div class="card-header">Incident List</div>
<div class="card-body">
<table id="incidents-table">
<thead>
<tr>
<th>Number</th>
<th>Short Description</th>
<th>Priority</th>
<th>Status</th>
<th>Assigned To</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="incidents-tbody">
<tr>
<td colspan="7" class="empty-state">
<div class="empty-state-icon">π</div>
<p>No incidents found. Create your first incident to get started.</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- RITM Section -->
<div id="ritm-section" class="section-hidden">
<div class="actions">
<button class="btn btn-primary" onclick="openCreateModal('ritm')">+ Create New Request Item</button>
<button class="btn btn-secondary" onclick="exportData('ritm')">Export to JSON</button>
<button class="btn btn-danger" onclick="clearAllRecords('ritm')">Clear All Records</button>
</div>
<div class="search-filter">
<input type="text" placeholder="Search request items..." oninput="filterTable('ritm', this.value)">
</div>
<div class="card">
<div class="card-header">Request Items (RITM) List</div>
<div class="card-body">
<table id="ritm-table">
<thead>
<tr>
<th>Number</th>
<th>Short Description</th>
<th>Category</th>
<th>Status</th>
<th>Requested By</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="ritm-tbody">
<tr>
<td colspan="7" class="empty-state">
<div class="empty-state-icon">π¦</div>
<p>No request items found. Create your first RITM to get started.</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Changes Section -->
<div id="changes-section" class="section-hidden">
<div class="actions">
<button class="btn btn-primary" onclick="openCreateModal('change')">+ Create New Change</button>
<button class="btn btn-secondary" onclick="exportData('changes')">Export to JSON</button>
<button class="btn btn-danger" onclick="clearAllRecords('changes')">Clear All Records</button>
</div>
<div class="search-filter">
<input type="text" placeholder="Search changes..." oninput="filterTable('changes', this.value)">
</div>
<div class="card">
<div class="card-header">Change Management List</div>
<div class="card-body">
<table id="changes-table">
<thead>
<tr>
<th>Number</th>
<th>Short Description</th>
<th>Type</th>
<th>Risk</th>
<th>Status</th>
<th>Scheduled Date</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="changes-tbody">
<tr>
<td colspan="7" class="empty-state">
<div class="empty-state-icon">π</div>
<p>No changes found. Create your first change request to get started.</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Create/Edit Modal -->
<div id="modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2 id="modal-title">Create New Incident</h2>
<button class="close-btn" onclick="closeModal()">×</button>
</div>
<div class="modal-body" id="modal-body">
<!-- Form will be dynamically inserted here -->
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModal()">Cancel</button>
<button class="btn btn-success" id="save-btn" onclick="saveRecord()">Save</button>
</div>
</div>
</div>
<!-- View Modal -->
<div id="view-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2 id="view-modal-title">View Record</h2>
<button class="close-btn" onclick="closeViewModal()">×</button>
</div>
<div class="modal-body" id="view-modal-body">
<!-- Details will be dynamically inserted here -->
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeViewModal()">Close</button>
<button class="btn btn-primary" id="edit-from-view-btn" onclick="editFromView()">Edit</button>
</div>
</div>
</div>
<script>
// Data Storage
let incidents = JSON.parse(localStorage.getItem('itsm_incidents') || '[]');
let ritms = JSON.parse(localStorage.getItem('itsm_ritms') || '[]');
let changes = JSON.parse(localStorage.getItem('itsm_changes') || '[]');
let currentSection = 'incidents';
let currentRecordType = null;
let currentRecordId = null;
let viewingRecord = null;
// Counter for IDs
let incidentCounter = parseInt(localStorage.getItem('incident_counter') || '1000');
let ritmCounter = parseInt(localStorage.getItem('ritm_counter') || '2000');
let changeCounter = parseInt(localStorage.getItem('change_counter') || '3000');
// Initialize the app
function init() {
renderIncidents();
renderRITMs();
renderChanges();
updateDashboard();
}
// Switch between sections
function switchSection(section) {
// Hide all sections
document.getElementById('dashboard-section').classList.add('section-hidden');
document.getElementById('incidents-section').classList.add('section-hidden');
document.getElementById('ritm-section').classList.add('section-hidden');
document.getElementById('changes-section').classList.add('section-hidden');
// Show selected section
if (section === 'dashboard') {
document.getElementById('dashboard-section').classList.remove('section-hidden');
updateDashboard();
} else if (section === 'incidents') {
document.getElementById('incidents-section').classList.remove('section-hidden');
} else if (section === 'ritm') {
document.getElementById('ritm-section').classList.remove('section-hidden');
} else if (section === 'changes') {
document.getElementById('changes-section').classList.remove('section-hidden');
}
// Update nav active state
document.querySelectorAll('.nav button').forEach(btn => {
btn.classList.remove('active');
});
event.target.classList.add('active');
currentSection = section;
}
// Update Dashboard
function updateDashboard() {
document.getElementById('stat-incidents').textContent = incidents.length;
document.getElementById('stat-ritm').textContent = ritms.length;
document.getElementById('stat-changes').textContent = changes.length;
const openIncidents = incidents.filter(i => i.status !== 'Closed').length;
const openRitms = ritms.filter(r => r.status !== 'Closed').length;
const openChanges = changes.filter(c => c.status !== 'Implemented' && c.status !== 'Closed').length;
document.getElementById('stat-open').textContent = openIncidents + openRitms + openChanges;
}
// Open Create/Edit Modal
function openCreateModal(type, recordId = null) {
currentRecordType = type;
currentRecordId = recordId;
const modal = document.getElementById('modal');
const modalTitle = document.getElementById('modal-title');
const modalBody = document.getElementById('modal-body');
let title = '';
let formHTML = '';
if (type === 'incident') {
title = recordId ? 'Edit Incident' : 'Create New Incident';
formHTML = getIncidentForm(recordId);
} else if (type === 'ritm') {
title = recordId ? 'Edit Request Item' : 'Create New Request Item';
formHTML = getRITMForm(recordId);
} else if (type === 'change') {
title = recordId ? 'Edit Change' : 'Create New Change';
formHTML = getChangeForm(recordId);
}
modalTitle.textContent = title;
modalBody.innerHTML = formHTML;
modal.classList.add('active');
}
// Close Modal
function closeModal() {
document.getElementById('modal').classList.remove('active');
currentRecordType = null;
currentRecordId = null;
}
// Incident Form
function getIncidentForm(id) {
let record = id ? incidents.find(i => i.id === id) : {};
return `
<div class="form-row">
<div class="form-group">
<label>Short Description *</label>
<input type="text" id="short_description" value="${record.short_description || ''}" required>
</div>
<div class="form-group">
<label>Priority *</label>
<select id="priority">
<option value="Critical" ${record.priority === 'Critical' ? 'selected' : ''}>Critical</option>
<option value="High" ${record.priority === 'High' ? 'selected' : ''}>High</option>
<option value="Medium" ${record.priority === 'Medium' || !record.priority ? 'selected' : ''}>Medium</option>
<option value="Low" ${record.priority === 'Low' ? 'selected' : ''}>Low</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Status *</label>
<select id="status">
<option value="New" ${record.status === 'New' || !record.status ? 'selected' : ''}>New</option>
<option value="In Progress" ${record.status === 'In Progress' ? 'selected' : ''}>In Progress</option>
<option value="Resolved" ${record.status === 'Resolved' ? 'selected' : ''}>Resolved</option>
<option value="Closed" ${record.status === 'Closed' ? 'selected' : ''}>Closed</option>
</select>
</div>
<div class="form-group">
<label>Assigned To</label>
<input type="text" id="assigned_to" value="${record.assigned_to || ''}">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Category</label>
<select id="category">
<option value="">-- Select --</option>
<option value="Hardware" ${record.category === 'Hardware' ? 'selected' : ''}>Hardware</option>
<option value="Software" ${record.category === 'Software' ? 'selected' : ''}>Software</option>
<option value="Network" ${record.category === 'Network' ? 'selected' : ''}>Network</option>
<option value="Database" ${record.category === 'Database' ? 'selected' : ''}>Database</option>
<option value="Security" ${record.category === 'Security' ? 'selected' : ''}>Security</option>
<option value="Other" ${record.category === 'Other' ? 'selected' : ''}>Other</option>
</select>
</div>
<div class="form-group">
<label>Caller</label>
<input type="text" id="caller" value="${record.caller || ''}">
</div>
</div>
<div class="form-group">
<label>Description</label>
<textarea id="description">${record.description || ''}</textarea>
</div>
<div class="form-group">
<label>Resolution Notes</label>
<textarea id="resolution_notes">${record.resolution_notes || ''}</textarea>
</div>
`;
}
// RITM Form
function getRITMForm(id) {
let record = id ? ritms.find(r => r.id === id) : {};
return `
<div class="form-row">
<div class="form-group">
<label>Short Description *</label>
<input type="text" id="short_description" value="${record.short_description || ''}" required>
</div>
<div class="form-group">
<label>Category *</label>
<select id="category">
<option value="Hardware Request" ${record.category === 'Hardware Request' || !record.category ? 'selected' : ''}>Hardware Request</option>
<option value="Software Request" ${record.category === 'Software Request' ? 'selected' : ''}>Software Request</option>
<option value="Access Request" ${record.category === 'Access Request' ? 'selected' : ''}>Access Request</option>
<option value="Service Request" ${record.category === 'Service Request' ? 'selected' : ''}>Service Request</option>
<option value="Other" ${record.category === 'Other' ? 'selected' : ''}>Other</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Status *</label>
<select id="status">
<option value="Pending Approval" ${record.status === 'Pending Approval' || !record.status ? 'selected' : ''}>Pending Approval</option>
<option value="Approved" ${record.status === 'Approved' ? 'selected' : ''}>Approved</option>
<option value="Rejected" ${record.status === 'Rejected' ? 'selected' : ''}>Rejected</option>
<option value="In Progress" ${record.status === 'In Progress' ? 'selected' : ''}>In Progress</option>
<option value="Completed" ${record.status === 'Completed' ? 'selected' : ''}>Completed</option>
<option value="Closed" ${record.status === 'Closed' ? 'selected' : ''}>Closed</option>
</select>
</div>
<div class="form-group">
<label>Requested By</label>
<input type="text" id="requested_by" value="${record.requested_by || ''}">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Assigned To</label>
<input type="text" id="assigned_to" value="${record.assigned_to || ''}">
</div>
<div class="form-group">
<label>Due Date</label>
<input type="date" id="due_date" value="${record.due_date || ''}">
</div>
</div>
<div class="form-group">
<label>Description</label>
<textarea id="description">${record.description || ''}</textarea>
</div>
<div class="form-group">
<label>Fulfillment Notes</label>
<textarea id="fulfillment_notes">${record.fulfillment_notes || ''}</textarea>
</div>
`;
}
// Change Form
function getChangeForm(id) {
let record = id ? changes.find(c => c.id === id) : {};
return `
<div class="form-row">
<div class="form-group">
<label>Short Description *</label>
<input type="text" id="short_description" value="${record.short_description || ''}" required>
</div>
<div class="form-group">
<label>Type *</label>
<select id="type">
<option value="Standard" ${record.type === 'Standard' || !record.type ? 'selected' : ''}>Standard</option>
<option value="Normal" ${record.type === 'Normal' ? 'selected' : ''}>Normal</option>
<option value="Emergency" ${record.type === 'Emergency' ? 'selected' : ''}>Emergency</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Risk *</label>
<select id="risk">
<option value="Low" ${record.risk === 'Low' || !record.risk ? 'selected' : ''}>Low</option>
<option value="Medium" ${record.risk === 'Medium' ? 'selected' : ''}>Medium</option>
<option value="High" ${record.risk === 'High' ? 'selected' : ''}>High</option>
<option value="Critical" ${record.risk === 'Critical' ? 'selected' : ''}>Critical</option>
</select>
</div>
<div class="form-group">
<label>Status *</label>
<select id="status">
<option value="Draft" ${record.status === 'Draft' || !record.status ? 'selected' : ''}>Draft</option>
<option value="Pending Approval" ${record.status === 'Pending Approval' ? 'selected' : ''}>Pending Approval</option>
<option value="Approved" ${record.status === 'Approved' ? 'selected' : ''}>Approved</option>
<option value="Scheduled" ${record.status === 'Scheduled' ? 'selected' : ''}>Scheduled</option>
<option value="In Progress" ${record.status === 'In Progress' ? 'selected' : ''}>In Progress</option>
<option value="Implemented" ${record.status === 'Implemented' ? 'selected' : ''}>Implemented</option>
<option value="Closed" ${record.status === 'Closed' ? 'selected' : ''}>Closed</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Requested By</label>
<input type="text" id="requested_by" value="${record.requested_by || ''}">
</div>
<div class="form-group">
<label>Assigned To</label>
<input type="text" id="assigned_to" value="${record.assigned_to || ''}">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Scheduled Start Date</label>
<input type="datetime-local" id="scheduled_start" value="${record.scheduled_start || ''}">
</div>
<div class="form-group">
<label>Scheduled End Date</label>
<input type="datetime-local" id="scheduled_end" value="${record.scheduled_end || ''}">
</div>
</div>
<div class="form-group">
<label>Description</label>
<textarea id="description">${record.description || ''}</textarea>
</div>
<div class="form-group">
<label>Implementation Plan</label>
<textarea id="implementation_plan">${record.implementation_plan || ''}</textarea>
</div>
<div class="form-group">
<label>Backout Plan</label>
<textarea id="backout_plan">${record.backout_plan || ''}</textarea>
</div>
<div class="form-group">
<label>Test Plan</label>
<textarea id="test_plan">${record.test_plan || ''}</textarea>
</div>
`;
}
// Save Record
function saveRecord() {
const short_description = document.getElementById('short_description').value.trim();
if (!short_description) {
alert('Please fill in the required fields');
return;
}
if (currentRecordType === 'incident') {
saveIncident();
} else if (currentRecordType === 'ritm') {
saveRITM();
} else if (currentRecordType === 'change') {
saveChange();
}
closeModal();
}
// Save Incident
function saveIncident() {
const record = {
id: currentRecordId || `INC${incidentCounter++}`,
number: currentRecordId || `INC${incidentCounter - 1}`,
short_description: document.getElementById('short_description').value,
priority: document.getElementById('priority').value,
status: document.getElementById('status').value,
assigned_to: document.getElementById('assigned_to').value,
category: document.getElementById('category').value,
caller: document.getElementById('caller').value,
description: document.getElementById('description').value,
resolution_notes: document.getElementById('resolution_notes').value,
created: currentRecordId ? incidents.find(i => i.id === currentRecordId).created : new Date().toISOString(),
updated: new Date().toISOString()
};
if (currentRecordId) {
const index = incidents.findIndex(i => i.id === currentRecordId);
incidents[index] = record;
} else {
incidents.push(record);
localStorage.setItem('incident_counter', incidentCounter);
}
localStorage.setItem('itsm_incidents', JSON.stringify(incidents));
renderIncidents();
updateDashboard();
}
// Save RITM
function saveRITM() {
const record = {
id: currentRecordId || `RITM${ritmCounter++}`,
number: currentRecordId || `RITM${ritmCounter - 1}`,
short_description: document.getElementById('short_description').value,
category: document.getElementById('category').value,
status: document.getElementById('status').value,
requested_by: document.getElementById('requested_by').value,
assigned_to: document.getElementById('assigned_to').value,
due_date: document.getElementById('due_date').value,
description: document.getElementById('description').value,
fulfillment_notes: document.getElementById('fulfillment_notes').value,
created: currentRecordId ? ritms.find(r => r.id === currentRecordId).created : new Date().toISOString(),
updated: new Date().toISOString()
};
if (currentRecordId) {
const index = ritms.findIndex(r => r.id === currentRecordId);
ritms[index] = record;
} else {
ritms.push(record);
localStorage.setItem('ritm_counter', ritmCounter);
}
localStorage.setItem('itsm_ritms', JSON.stringify(ritms));
renderRITMs();
updateDashboard();
}
// Save Change
function saveChange() {
const record = {
id: currentRecordId || `CHG${changeCounter++}`,
number: currentRecordId || `CHG${changeCounter - 1}`,
short_description: document.getElementById('short_description').value,
type: document.getElementById('type').value,
risk: document.getElementById('risk').value,
status: document.getElementById('status').value,
requested_by: document.getElementById('requested_by').value,
assigned_to: document.getElementById('assigned_to').value,
scheduled_start: document.getElementById('scheduled_start').value,
scheduled_end: document.getElementById('scheduled_end').value,
description: document.getElementById('description').value,
implementation_plan: document.getElementById('implementation_plan').value,
backout_plan: document.getElementById('backout_plan').value,
test_plan: document.getElementById('test_plan').value,
created: currentRecordId ? changes.find(c => c.id === currentRecordId).created : new Date().toISOString(),
updated: new Date().toISOString()
};
if (currentRecordId) {
const index = changes.findIndex(c => c.id === currentRecordId);
changes[index] = record;
} else {
changes.push(record);
localStorage.setItem('change_counter', changeCounter);
}
localStorage.setItem('itsm_changes', JSON.stringify(changes));
renderChanges();
updateDashboard();
}
// Render Incidents
function renderIncidents() {
const tbody = document.getElementById('incidents-tbody');
if (incidents.length === 0) {
tbody.innerHTML = `
<tr>
<td colspan="7" class="empty-state">
<div class="empty-state-icon">π</div>
<p>No incidents found. Create your first incident to get started.</p>
</td>
</tr>
`;
return;
}
tbody.innerHTML = incidents.map(inc => `
<tr>
<td><a class="record-link" onclick="viewRecord('incident', '${inc.id}')">${inc.number}</a></td>
<td>${inc.short_description}</td>
<td><span class="priority-${inc.priority.toLowerCase()}">${inc.priority}</span></td>
<td><span class="badge badge-${inc.status.toLowerCase().replace(' ', '')}">${inc.status}</span></td>
<td>${inc.assigned_to || '-'}</td>
<td>${formatDate(inc.created)}</td>
<td>
<div class="action-buttons">
<button class="action-btn action-btn-view" onclick="viewRecord('incident', '${inc.id}')">View</button>
<button class="action-btn action-btn-edit" onclick="openCreateModal('incident', '${inc.id}')">Edit</button>
</div>
</td>
</tr>
`).join('');
}
// Render RITMs
function renderRITMs() {
const tbody = document.getElementById('ritm-tbody');
if (ritms.length === 0) {
tbody.innerHTML = `
<tr>
<td colspan="7" class="empty-state">
<div class="empty-state-icon">π¦</div>
<p>No request items found. Create your first RITM to get started.</p>
</td>
</tr>
`;
return;
}
tbody.innerHTML = ritms.map(ritm => `
<tr>
<td><a class="record-link" onclick="viewRecord('ritm', '${ritm.id}')">${ritm.number}</a></td>
<td>${ritm.short_description}</td>
<td>${ritm.category}</td>
<td><span class="badge badge-${ritm.status.toLowerCase().replace(' ', '')}">${ritm.status}</span></td>
<td>${ritm.requested_by || '-'}</td>
<td>${formatDate(ritm.created)}</td>
<td>
<div class="action-buttons">
<button class="action-btn action-btn-view" onclick="viewRecord('ritm', '${ritm.id}')">View</button>
<button class="action-btn action-btn-edit" onclick="openCreateModal('ritm', '${ritm.id}')">Edit</button>
</div>
</td>
</tr>
`).join('');
}
// Render Changes
function renderChanges() {
const tbody = document.getElementById('changes-tbody');
if (changes.length === 0) {
tbody.innerHTML = `
<tr>
<td colspan="7" class="empty-state">
<div class="empty-state-icon">π</div>
<p>No changes found. Create your first change request to get started.</p>
</td>
</tr>
`;
return;
}
tbody.innerHTML = changes.map(chg => `
<tr>
<td><a class="record-link" onclick="viewRecord('change', '${chg.id}')">${chg.number}</a></td>
<td>${chg.short_description}</td>
<td>${chg.type}</td>
<td><span class="priority-${chg.risk.toLowerCase()}">${chg.risk}</span></td>
<td><span class="badge badge-${chg.status.toLowerCase().replace(' ', '')}">${chg.status}</span></td>
<td>${chg.scheduled_start ? formatDate(chg.scheduled_start) : '-'}</td>
<td>
<div class="action-buttons">
<button class="action-btn action-btn-view" onclick="viewRecord('change', '${chg.id}')">View</button>
<button class="action-btn action-btn-edit" onclick="openCreateModal('change', '${chg.id}')">Edit</button>
</div>
</td>
</tr>
`).join('');
}
// View Record
function viewRecord(type, id) {
viewingRecord = { type, id };
const modal = document.getElementById('view-modal');
const modalTitle = document.getElementById('view-modal-title');
const modalBody = document.getElementById('view-modal-body');
let record, detailsHTML;
if (type === 'incident') {
record = incidents.find(i => i.id === id);
modalTitle.textContent = `Incident: ${record.number}`;
detailsHTML = `
<div class="detail-view">
<div class="detail-row">
<div class="detail-label">Number</div>
<div class="detail-value">${record.number}</div>
</div>
<div class="detail-row">
<div class="detail-label">Short Description</div>
<div class="detail-value">${record.short_description}</div>
</div>
<div class="detail-row">
<div class="detail-label">Priority</div>
<div class="detail-value"><span class="priority-${record.priority.toLowerCase()}">${record.priority}</span></div>
</div>
<div class="detail-row">
<div class="detail-label">Status</div>
<div class="detail-value"><span class="badge badge-${record.status.toLowerCase().replace(' ', '')}">${record.status}</span></div>
</div>
<div class="detail-row">
<div class="detail-label">Category</div>
<div class="detail-value">${record.category || '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Assigned To</div>
<div class="detail-value">${record.assigned_to || '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Caller</div>
<div class="detail-value">${record.caller || '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Description</div>
<div class="detail-value">${record.description || '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Resolution Notes</div>
<div class="detail-value">${record.resolution_notes || '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Created</div>
<div class="detail-value">${formatDate(record.created)}</div>
</div>
<div class="detail-row">
<div class="detail-label">Last Updated</div>
<div class="detail-value">${formatDate(record.updated)}</div>
</div>
</div>
`;
} else if (type === 'ritm') {
record = ritms.find(r => r.id === id);
modalTitle.textContent = `Request Item: ${record.number}`;
detailsHTML = `
<div class="detail-view">
<div class="detail-row">
<div class="detail-label">Number</div>
<div class="detail-value">${record.number}</div>
</div>
<div class="detail-row">
<div class="detail-label">Short Description</div>
<div class="detail-value">${record.short_description}</div>
</div>
<div class="detail-row">
<div class="detail-label">Category</div>
<div class="detail-value">${record.category}</div>
</div>
<div class="detail-row">
<div class="detail-label">Status</div>
<div class="detail-value"><span class="badge badge-${record.status.toLowerCase().replace(' ', '')}">${record.status}</span></div>
</div>
<div class="detail-row">
<div class="detail-label">Requested By</div>
<div class="detail-value">${record.requested_by || '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Assigned To</div>
<div class="detail-value">${record.assigned_to || '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Due Date</div>
<div class="detail-value">${record.due_date ? formatDate(record.due_date) : '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Description</div>
<div class="detail-value">${record.description || '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Fulfillment Notes</div>
<div class="detail-value">${record.fulfillment_notes || '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Created</div>
<div class="detail-value">${formatDate(record.created)}</div>
</div>
<div class="detail-row">
<div class="detail-label">Last Updated</div>
<div class="detail-value">${formatDate(record.updated)}</div>
</div>
</div>
`;
} else if (type === 'change') {
record = changes.find(c => c.id === id);
modalTitle.textContent = `Change: ${record.number}`;
detailsHTML = `
<div class="detail-view">
<div class="detail-row">
<div class="detail-label">Number</div>
<div class="detail-value">${record.number}</div>
</div>
<div class="detail-row">
<div class="detail-label">Short Description</div>
<div class="detail-value">${record.short_description}</div>
</div>
<div class="detail-row">
<div class="detail-label">Type</div>
<div class="detail-value">${record.type}</div>
</div>
<div class="detail-row">
<div class="detail-label">Risk</div>
<div class="detail-value"><span class="priority-${record.risk.toLowerCase()}">${record.risk}</span></div>
</div>
<div class="detail-row">
<div class="detail-label">Status</div>
<div class="detail-value"><span class="badge badge-${record.status.toLowerCase().replace(' ', '')}">${record.status}</span></div>
</div>
<div class="detail-row">
<div class="detail-label">Requested By</div>
<div class="detail-value">${record.requested_by || '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Assigned To</div>
<div class="detail-value">${record.assigned_to || '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Scheduled Start</div>
<div class="detail-value">${record.scheduled_start ? formatDate(record.scheduled_start) : '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Scheduled End</div>
<div class="detail-value">${record.scheduled_end ? formatDate(record.scheduled_end) : '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Description</div>
<div class="detail-value">${record.description || '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Implementation Plan</div>
<div class="detail-value">${record.implementation_plan || '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Backout Plan</div>
<div class="detail-value">${record.backout_plan || '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Test Plan</div>
<div class="detail-value">${record.test_plan || '-'}</div>
</div>
<div class="detail-row">
<div class="detail-label">Created</div>
<div class="detail-value">${formatDate(record.created)}</div>
</div>
<div class="detail-row">
<div class="detail-label">Last Updated</div>
<div class="detail-value">${formatDate(record.updated)}</div>
</div>
</div>
`;
}
modalBody.innerHTML = detailsHTML;
modal.classList.add('active');
}
// Close View Modal
function closeViewModal() {
document.getElementById('view-modal').classList.remove('active');
viewingRecord = null;
}
// Edit from View
function editFromView() {
if (viewingRecord) {
closeViewModal();
openCreateModal(viewingRecord.type, viewingRecord.id);
}
}
// Filter Table
function filterTable(type, searchTerm) {
const term = searchTerm.toLowerCase();
let data;
if (type === 'incidents') {
data = incidents;
} else if (type === 'ritm') {
data = ritms;
} else if (type === 'changes') {
data = changes;
}
const filtered = data.filter(record => {
return record.short_description.toLowerCase().includes(term) ||
record.number.toLowerCase().includes(term) ||
(record.status && record.status.toLowerCase().includes(term));
});
// Re-render with filtered data
const originalData = type === 'incidents' ? incidents : type === 'ritm' ? ritms : changes;
if (type === 'incidents') {
incidents = filtered;
renderIncidents();
incidents = originalData;
} else if (type === 'ritm') {
ritms = filtered;
renderRITMs();
ritms = originalData;
} else if (type === 'changes') {
changes = filtered;
renderChanges();
changes = originalData;
}
}
// Export Data
function exportData(type) {
let data, filename;
if (type === 'incidents') {
data = incidents;
filename = 'incidents.json';
} else if (type === 'ritm') {
data = ritms;
filename = 'ritms.json';
} else if (type === 'changes') {
data = changes;
filename = 'changes.json';
}
const dataStr = JSON.stringify(data, null, 2);
const dataBlob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(dataBlob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
}
// Clear All Records
function clearAllRecords(type) {
let typeName, count;
if (type === 'incidents') {
typeName = 'Incidents';
count = incidents.length;
} else if (type === 'ritm') {
typeName = 'Request Items';
count = ritms.length;
} else if (type === 'changes') {
typeName = 'Changes';
count = changes.length;
}
if (count === 0) {
alert('There are no records to delete.');
return;
}
const confirmed = confirm(
`β οΈ WARNING: This will permanently delete all ${count} ${typeName}.\n\n` +
`This action cannot be undone!\n\n` +
`Click OK to proceed with deletion, or Cancel to keep your records.`
);
if (confirmed) {
// Double confirmation for safety
const doubleConfirm = confirm(
`Are you absolutely sure?\n\n` +
`This is your last chance to cancel before deleting ${count} ${typeName}.`
);
if (doubleConfirm) {
if (type === 'incidents') {
incidents = [];
localStorage.setItem('itsm_incidents', JSON.stringify(incidents));
renderIncidents();
} else if (type === 'ritm') {
ritms = [];
localStorage.setItem('itsm_ritms', JSON.stringify(ritms));
renderRITMs();
} else if (type === 'changes') {
changes = [];
localStorage.setItem('itsm_changes', JSON.stringify(changes));
renderChanges();
}
updateDashboard();
alert(`β Successfully deleted all ${typeName}.`);
}
}
}
// Clear All Data (Everything)
function clearAllData() {
const totalRecords = incidents.length + ritms.length + changes.length;
if (totalRecords === 0) {
alert('There are no records to delete.');
return;
}
const confirmed = confirm(
`β οΈ DANGER: This will permanently delete ALL data:\n\n` +
`β’ ${incidents.length} Incidents\n` +
`β’ ${ritms.length} Request Items\n` +
`β’ ${changes.length} Changes\n\n` +
`Total: ${totalRecords} records\n\n` +
`This action CANNOT be undone!\n\n` +
`Click OK to proceed with deletion, or Cancel to keep your data.`
);
if (confirmed) {
// Triple confirmation for clearing everything
const doubleConfirm = confirm(
`π¨ FINAL WARNING π¨\n\n` +
`You are about to DELETE EVERYTHING (${totalRecords} records).\n\n` +
`This is your LAST CHANCE to cancel!\n\n` +
`Type anything in the prompt that appears next to confirm.`
);
if (doubleConfirm) {
const finalConfirm = prompt(
`To confirm deletion of ALL ${totalRecords} records, type: DELETE`
);
if (finalConfirm === 'DELETE') {
// Clear all data
incidents = [];
ritms = [];
changes = [];
// Clear localStorage
localStorage.setItem('itsm_incidents', JSON.stringify(incidents));
localStorage.setItem('itsm_ritms', JSON.stringify(ritms));
localStorage.setItem('itsm_changes', JSON.stringify(changes));
// Re-render all sections
renderIncidents();
renderRITMs();
renderChanges();
updateDashboard();
alert('β All data has been permanently deleted.');
} else {
alert('Deletion cancelled. You did not type DELETE correctly.');
}
}
}
}
// Format Date
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
}
// Initialize on load
window.onload = init;
</script>
</body>
</html>
Step 5 β Allow HTTP Port 80 in OS Firewall
systemctl start firewalld
Then add port 80:
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --reload
Verify:
firewall-cmd --list-ports
ss -lntp | grep :80
Port 80 should be in the LISTEN state.
curl http://localhost (pvt ip of instance)
Expected output:
Website content
This confirms that the web server and website are working locally.
Add an inbound rule to the Security List associated with the Bastion Server's subnet:
Source : 0.0.0.0/0
Protocol : TCP
Destination Port : 80
Description : Allow HTTP traffic from the Internet
Ensure that the Route Table associated with the Bastion Server's subnet has a default route to the Internet Gateway:
Destination : 0.0.0.0/0
Target Type : Internet Gateway
Step 10: Final Website Verification
Open a web browser and enter the Bastion Server's Public IP:
http://
Step 1 β Connect to the Server
Connect to the server using SSH/Bastion.
Step 2 β Install Apache Web Server
yum install httpd
Step 3 β Start and Enable Apache
systemctl start httpd
systemctl enable httpd
Verify:
systemctl status httpd
The status should show:
Active: active (running)
Step 4 β Create the Website
cd /var/www/html
vi index.html
Add:
:wq! β Save and exit.
Step 5 β Allow HTTP Port 80 in OS Firewall
Start firewall service
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --reload
Verify:
firewall-cmd --list-ports
http should be listed.
Step 6 β Verify Port 80
ss -lntp | grep :80
Step 7 β Verify the Website Locally
curl http://localhost
Expected output:
Website content
This confirms that the web server and website are working locally.
Note: We are currently configuring only one server. Repeat the same steps on the second server as well to ensure that both servers have the same web server, website configuration, port 80, and OS firewall settings
- Enter a suitable name for the Load Balancer.
- Select Public.
-
Select Ephemeral or Reserved, based on the requirement.
-
For a fixed public IP, select Reserved.
-
Select the appropriate Compartment.
-
Select the required VCN.
-
Select the Web Subnet where the Load Balancer will be deployed.
-
Select the required Minimum Bandwidth.
-
Select the required Maximum Bandwidth according to expected traffic.
-
Enable Web Application Firewall (WAF) if application-layer protection is required.
-
Otherwise, leave it disabled.
- Enable Acceleration if required for the application.
Click Next.
Load Balancing Policy
Select the appropriate policy based on the application requirement:
-
Weighted Round Robin β distributes traffic according to assigned weights.
-
IP Hash β directs requests from the same client to the same backend.
-
Least Connections β sends traffic to the backend with the fewest active connections.
For a standard web application, Weighted Round Robin can be selected.
Select Backend Servers
Select the Backend Compartment.
Select the required backend instances.
Add the required backend servers to the backend set.
Verify that the selected backend servers are added successfully.
Specify Health Check Policy
Configure the health check for the backend servers:
Protocol: Select TCP
Port: 80
Interval: Configure the required interval.
Timeout: Configure the required timeout.
Number of Retries: Configure the required retry count.
Backend Set
Enter a suitable Backend Set Name. Max Connections
Enable and configure this option if the application requires a maximum connection limit. Otherwise, leave it disabled. Security List
Select the appropriate option:
Manually configure security list rules, or
Automatically add security list rules For controlled environments, manually configuring the required rules provides better control.
Session Persistence
Select the appropriate option:
Disable session persistence when requests can be handled by any backend.
Enable cookie-based persistence when the application requires client sessions to remain on the same backend.
Click Next.
Step 3: Configure Listener Configure the listener:
1.Listener Name
Enter a suitable listener name. 2.Traffic Type
Select HTTP. 3.Port
Enter 80. SSL/TLS
If HTTPS is required:
Select HTTPS as the traffic type.
Configure the required SSL certificate and TLS settings.
If HTTP is being used:
Keep SSL/TLS configuration disabled. Click Next.
Step 4: Manage Logging Error Logs
Enable Error Logging.
Select an existing Log Group or create a new Log Group.
Enter a suitable Log Name.
β’Configure the required log retention period.
Access Logs
Enable Access Logging.
Select an existing Log Group or create a new Log Group.
Enter a suitable Log Name.
Configure the required log retention period.
Request ID
Enable Request ID.
Configure the request ID header name if required.
Click Next.
Step 5: Review and Create Review the Load Balancer configuration, including:
Load Balancer details
Web Subnet
Backend Set and Backend Servers
Health Check
Listener
Logging configuration
If all configurations are correct, click Submit / Create Load Balancer.
Step 6: Verify Load Balancer After the Load Balancer is created:
1.Verify the Load Balancer status is Active.
2.Verify the assigned IP address.
3.Open Backend Sets.
4.Verify all required backend servers are listed.
5.Verify the backend port.
6.Check the health status of each backend.
7.Open Listeners and verify the listener status.
8.Verify the listener is listening on the expected port.
Note: The Load Balancer is currently showing a Critical backend health status. Therefore, the required network configuration, including Security Lists and Route Tables, must be completed to allow traffic between the Load Balancer and the backend servers.
After creating the Load Balancer, configure the Security Lists and Route Tables associated with the Web, App, and DB subnets.
Subnet: Web Subnet (10.0.1.128/27)
Purpose: Hosts the public Load Balancer.
1.1 Configure Web Security List β Ingress
Add the following rule:
1.2 Configure Web Security List β Egress
1.3 Configure Web Route Table
Add:
Subnet: App Subnet (10.0.1.0/26)
Purpose: Hosts the backend application/web servers.
Add:
If SSH administration is required, also add:
Add:
If the application servers require outbound Internet access,
add:
Subnet: DB Subnet (10.0.1.96/27)
Purpose: Hosts the database server.
Add:
3.3 Configure DB Route Table
Do not add an Internet Gateway or NAT Gateway default route if the database does not require Internet access.
The VCN's local route will allow communication between the App and DB subnets.
After completing the Load Balancer and network configuration, verify that the application is accessible through the Load Balancer.
1.Open the Load Balancer in the OCI Console.
2.Copy the Public IP Address assigned to the Load Balancer.
3.Open a web browser and enter the Load Balancer Public IP using HTTP:
http://<Load-Balancer-Public-IP>
4.Verify that the application website is displayed successfully.
5.Refresh the page multiple times to verify that traffic is being distributed across the configured backend servers.
6.Confirm that the backend servers are showing Healthy status in the Load Balancer backend set.
Expected Result:
The application website should be accessible through the Load Balancer Public IP, confirming that the Load Balancer, networking, listener, and backend connectivity are working correctly.