Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# terraform
.terraform/
.terraform.lock.hcl
terraform.tfstate
terraform.tfstate.backup

# dependencies
/node_modules
/.pnp
Expand Down Expand Up @@ -35,4 +41,4 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
.vscode
.vscode
1 change: 0 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ RUN npm install -g pnpm
RUN pnpm install && pnpm add sharp

# Copy the rest of the application code
COPY .env .
COPY . .

# Build the application
Expand Down
155 changes: 155 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
terraform {
required_providers {
oci = {
source = "oracle/oci"
version = "6.25.0"
}
}
}

provider "oci" {
tenancy_ocid = var.tenancy_ocid
user_ocid = var.user_ocid
private_key = replace(var.private_key, "\\n", "\n")
fingerprint = var.fingerprint
region = var.region
}

# Create Virtual Cloud Network (VCN)
resource "oci_core_vcn" "devops_dynamics_vcn" {
compartment_id = var.compartment_id
cidr_blocks = var.vcn_cidr_blocks
display_name = var.vcn_display_name
dns_label = var.vcn_dns_label
}

# Internet Gateway for Public Access
resource "oci_core_internet_gateway" "devops_igw" {
compartment_id = var.compartment_id
vcn_id = oci_core_vcn.devops_dynamics_vcn.id
display_name = "devops_internet_gateway"
enabled = true
}

# Route Table for Internet Access
resource "oci_core_route_table" "devops_rt" {
compartment_id = var.compartment_id
vcn_id = oci_core_vcn.devops_dynamics_vcn.id
display_name = "devops_route_table"

route_rules {
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
network_entity_id = oci_core_internet_gateway.devops_igw.id
}
}

# Security List (Allow SSH, HTTP, HTTPS)
resource "oci_core_security_list" "devops_security_list" {
compartment_id = var.compartment_id
vcn_id = oci_core_vcn.devops_dynamics_vcn.id
display_name = "devops_security_list"

# Allow inbound SSH
ingress_security_rules {
protocol = "6"
source = "0.0.0.0/0"
source_type = "CIDR_BLOCK"

tcp_options {
min = 22
max = 22
}
}

# Allow HTTP (Port 80)
ingress_security_rules {
protocol = "6"
source = "0.0.0.0/0"
source_type = "CIDR_BLOCK"

tcp_options {
min = 80
max = 80
}
}

# Allow HTTPS (Port 443)
ingress_security_rules {
protocol = "6"
source = "0.0.0.0/0"
source_type = "CIDR_BLOCK"

tcp_options {
min = 443
max = 443
}
}

# Allow all outbound traffic
egress_security_rules {
protocol = "all"
destination = "0.0.0.0/0"
}
}

# Create Subnet for Compute Instances
resource "oci_core_subnet" "devops_subnet" {
compartment_id = var.compartment_id
vcn_id = oci_core_vcn.devops_dynamics_vcn.id
cidr_block = var.subnet_cidr_block
display_name = "devops_subnet"
route_table_id = oci_core_route_table.devops_rt.id
security_list_ids = [oci_core_security_list.devops_security_list.id]
dns_label = "devsubnet"
prohibit_public_ip_on_vnic = false
}

# Fetch Availability Domains
data "oci_identity_availability_domains" "ads" {
compartment_id = var.tenancy_ocid
}

# Create AMD Compute Instance for Production
resource "oci_core_instance" "prod_server" {
compartment_id = var.compartment_id
availability_domain = data.oci_identity_availability_domains.ads.availability_domains[0].name
shape = "VM.Standard.E2.1.Micro"
display_name = "prod-server"

create_vnic_details {
subnet_id = oci_core_subnet.devops_subnet.id
assign_public_ip = true
}

source_details {
source_type = "image"
source_id = var.image_ocid
}

metadata = {
ssh_authorized_keys = replace(var.ssh_public_keys, "\\n", "\n")
}
}

# Create AMD Compute Instance for Staging
resource "oci_core_instance" "staging_server" {
compartment_id = var.compartment_id
availability_domain = data.oci_identity_availability_domains.ads.availability_domains[0].name
shape = "VM.Standard.E2.1.Micro"
display_name = "staging-server"

create_vnic_details {
subnet_id = oci_core_subnet.devops_subnet.id
assign_public_ip = true
}

source_details {
source_type = "image"
source_id = var.image_ocid
}

metadata = {
ssh_authorized_keys = replace(var.ssh_public_keys, "\\n", "\n")
}
}
115 changes: 115 additions & 0 deletions terraform-oci-state.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!/bin/bash
# Automate Terraform state file management in OCI with "pull" and "push" commands

BUCKET_NAME="devops-dynamics-terraform-state"
PROFILE="DEVOPS-DYNAMICS"
STATE_FILE="terraform.tfstate"
BACKUP_FILE="terraform.tfstate.backup"
REMOTE_FILE="terraform.tfstate.remote"

# Function to get object ETag (checksum) from OCI Storage
get_etag() {
local object_name=$1
oci os object head \
--bucket-name "$BUCKET_NAME" \
--name "$object_name" \
--profile "$PROFILE" \
--query 'etag' 2>/dev/null | tr -d '"'
}

# Ensure a valid argument is passed
if [[ $# -ne 1 ]]; then
echo "❌ Error: No valid argument provided."
echo "Usage: $0 <pull|push>"
exit 1
fi

# 📥 **Pull Command: Download the latest Terraform state from OCI**
if [[ "$1" == "pull" ]]; then
echo "📥 Downloading the latest Terraform state from OCI..."
oci os object get \
--bucket-name "$BUCKET_NAME" \
--name "$STATE_FILE" \
--file "$STATE_FILE" \
--profile "$PROFILE" \
2>/dev/null

if [[ $? -eq 0 ]]; then
echo "✅ Successfully pulled latest Terraform state from OCI."
else
echo "⚠️ No existing Terraform state found in OCI."
fi
exit 0
fi

# 🚀 **Push Command: Upload updated Terraform state**
if [[ "$1" == "push" ]]; then
# Ensure Terraform state file exists before proceeding
if [[ ! -f "$STATE_FILE" ]]; then
echo "❌ Error: Terraform state file '$STATE_FILE' not found!"
exit 1
fi

# Step 1: Download the current Terraform state for comparison
echo "📥 Fetching the latest Terraform state from OCI..."
oci os object get \
--bucket-name "$BUCKET_NAME" \
--name "$STATE_FILE" \
--file "$REMOTE_FILE" \
--profile "$PROFILE" \
2>/dev/null || echo "ℹ️ No existing state file found in OCI."

# Step 2: Compute local and remote checksums
LOCAL_ETAG=$(openssl dgst -sha256 -binary "$STATE_FILE" | openssl base64)
REMOTE_ETAG=""
if [[ -f "$REMOTE_FILE" ]]; then
REMOTE_ETAG=$(openssl dgst -sha256 -binary "$REMOTE_FILE" | openssl base64)
fi

# Remove the temporary remote state file
rm -f "$REMOTE_FILE"

# Step 3: If checksums match, skip upload
if [[ "$LOCAL_ETAG" == "$REMOTE_ETAG" ]]; then
echo "✅ No changes detected in Terraform state. Skipping upload."
exit 0
else
echo "🔄 Changes detected in Terraform state! Updating OCI Storage..."

# Step 4: Delete the old backup if it exists
if get_etag "$BACKUP_FILE" &>/dev/null; then
echo "🗑️ Removing old backup file from OCI..."
oci os object delete \
--bucket-name "$BUCKET_NAME" \
--name "$BACKUP_FILE" \
--force \
--profile "$PROFILE"
fi

# Step 5: Rename current `terraform.tfstate` to `terraform.tfstate.backup`
if [[ -n "$REMOTE_ETAG" ]]; then
echo "📂 Renaming existing state file to backup in OCI..."
oci os object rename \
--bucket-name "$BUCKET_NAME" \
--source-name "$STATE_FILE" \
--new-name "$BACKUP_FILE" \
--profile "$PROFILE"
fi

# Step 6: Upload the new Terraform state file
echo "⬆️ Uploading new Terraform state to OCI..."
oci os object put \
--bucket-name "$BUCKET_NAME" \
--file "$STATE_FILE" \
--name "$STATE_FILE" \
--profile "$PROFILE"

echo "✅ Terraform state successfully updated in OCI!"
fi
exit 0
fi

# ❌ If an invalid argument is passed
echo "❌ Error: Invalid argument '$1'."
echo "Usage: $0 <pull|push>"
exit 1
65 changes: 65 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
variable "tenancy_ocid" {
description = "The OCID of the tenancy"
type = string
}

variable "user_ocid" {
description = "The OCID of the user"
type = string
}

variable "private_key" {
description = "The private key for API signing"
type = string
sensitive = true
}

variable "fingerprint" {
description = "The fingerprint for the private key"
type = string
}

variable "region" {
description = "The region for OCI operations"
type = string
}

variable "compartment_id" {
description = "Compartment ID"
type = string
}

variable "vcn_cidr_blocks" {
description = "VCN CIDR Blocks"
type = list(string)
}

variable "vcn_display_name" {
description = "VCN Display Name"
type = string
}

variable "vcn_dns_label" {
description = "VCN DNS Label"
type = string
}

variable "oci_group_name" {
description = "OCI group name that will be allowed to manage VCN resources"
type = string
}

variable "subnet_cidr_block" {
description = "Subnet CIDR Block"
type = string
}

variable "image_ocid" {
description = "OCID of the image to be used for the Compute instance"
type = string
}

variable "ssh_public_keys" {
description = "SSH Public Keys"
type = string
}
Loading