308 lines
7.7 KiB
Bash
308 lines
7.7 KiB
Bash
#!/bin/bash
|
|
|
|
# Cloudflare Infrastructure Deployment Automation
|
|
# Automated Terraform deployment with safety checks and rollback capabilities
|
|
|
|
set -e
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Configuration
|
|
TERRAFORM_DIR="terraform"
|
|
BACKUP_DIR="terraform_backups"
|
|
STATE_FILE="terraform.tfstate"
|
|
PLAN_FILE="deployment_plan.tfplan"
|
|
LOG_FILE="deployment_$(date +%Y%m%d_%H%M%S).log"
|
|
|
|
# Function to log messages
|
|
log() {
|
|
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
# Function to log success
|
|
success() {
|
|
echo -e "${GREEN}✅ $1${NC}" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
# Function to log warning
|
|
warning() {
|
|
echo -e "${YELLOW}⚠️ $1${NC}" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
# Function to log error
|
|
error() {
|
|
echo -e "${RED}❌ $1${NC}" | tee -a "$LOG_FILE"
|
|
exit 1
|
|
}
|
|
|
|
# Function to check prerequisites
|
|
check_prerequisites() {
|
|
log "Checking prerequisites..."
|
|
|
|
# Check if .env file exists
|
|
if [[ ! -f "../.env" ]]; then
|
|
error "Missing .env file. Run setup_credentials.sh first."
|
|
fi
|
|
|
|
# Source environment variables
|
|
source "../.env"
|
|
|
|
# Check required variables
|
|
if [[ -z "$CLOUDFLARE_API_TOKEN" ]]; then
|
|
error "CLOUDFLARE_API_TOKEN not set in .env"
|
|
fi
|
|
|
|
if [[ -z "$CLOUDFLARE_ACCOUNT_ID" ]]; then
|
|
error "CLOUDFLARE_ACCOUNT_ID not set in .env"
|
|
fi
|
|
|
|
# Check Terraform installation
|
|
if ! command -v terraform &> /dev/null; then
|
|
error "Terraform not found. Please install Terraform first."
|
|
fi
|
|
|
|
# Check Terraform version
|
|
TF_VERSION=$(terraform version | head -n1 | awk '{print $2}' | sed 's/v//')
|
|
log "Terraform version: $TF_VERSION"
|
|
|
|
success "Prerequisites check passed"
|
|
}
|
|
|
|
# Function to backup current state
|
|
backup_state() {
|
|
log "Creating backup of current state..."
|
|
|
|
# Create backup directory
|
|
mkdir -p "$BACKUP_DIR"
|
|
|
|
# Backup state file if it exists
|
|
if [[ -f "$STATE_FILE" ]]; then
|
|
BACKUP_NAME="${BACKUP_DIR}/state_backup_$(date +%Y%m%d_%H%M%S).tfstate"
|
|
cp "$STATE_FILE" "$BACKUP_NAME"
|
|
success "State backed up to: $BACKUP_NAME"
|
|
else
|
|
warning "No existing state file found"
|
|
fi
|
|
|
|
# Backup terraform.tfvars
|
|
if [[ -f "terraform.tfvars" ]]; then
|
|
cp "terraform.tfvars" "${BACKUP_DIR}/terraform.tfvars.backup"
|
|
fi
|
|
}
|
|
|
|
# Function to prepare terraform.tfvars
|
|
prepare_config() {
|
|
log "Preparing Terraform configuration..."
|
|
|
|
# Update terraform.tfvars with actual credentials
|
|
cat > terraform.tfvars << EOF
|
|
cloudflare_api_token = "$CLOUDFLARE_API_TOKEN"
|
|
cloudflare_account_id = "$CLOUDFLARE_ACCOUNT_ID"
|
|
cloudflare_account_name = "" # Use account_id from .env
|
|
EOF
|
|
|
|
# Add optional Zone ID if set
|
|
if [[ -n "$CLOUDFLARE_ZONE_ID" ]]; then
|
|
echo "cloudflare_zone_id = \"$CLOUDFLARE_ZONE_ID\"" >> terraform.tfvars
|
|
fi
|
|
|
|
success "Configuration prepared"
|
|
}
|
|
|
|
# Function to initialize Terraform
|
|
init_terraform() {
|
|
log "Initializing Terraform..."
|
|
|
|
if terraform init -upgrade; then
|
|
success "Terraform initialized successfully"
|
|
else
|
|
error "Terraform initialization failed"
|
|
fi
|
|
}
|
|
|
|
# Function to validate Terraform configuration
|
|
validate_config() {
|
|
log "Validating Terraform configuration..."
|
|
|
|
if terraform validate; then
|
|
success "Configuration validation passed"
|
|
else
|
|
error "Configuration validation failed"
|
|
fi
|
|
}
|
|
|
|
# Function to create deployment plan
|
|
create_plan() {
|
|
log "Creating deployment plan..."
|
|
|
|
if terraform plan -out="$PLAN_FILE" -detailed-exitcode; then
|
|
case $? in
|
|
0)
|
|
success "No changes needed"
|
|
return 0
|
|
;;
|
|
2)
|
|
success "Plan created successfully"
|
|
return 2
|
|
;;
|
|
*)
|
|
error "Plan creation failed"
|
|
;;
|
|
esac
|
|
else
|
|
error "Plan creation failed"
|
|
fi
|
|
}
|
|
|
|
# Function to show plan summary
|
|
show_plan_summary() {
|
|
log "Plan Summary:"
|
|
terraform show -json "$PLAN_FILE" | jq -r '
|
|
.resource_changes[] |
|
|
select(.change.actions != ["no-op"]) |
|
|
"\(.change.actions | join(",")) \(.type).\(.name)"
|
|
' | sort | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
# Function to confirm deployment
|
|
confirm_deployment() {
|
|
echo
|
|
echo "=================================================="
|
|
echo "🚀 DEPLOYMENT CONFIRMATION"
|
|
echo "=================================================="
|
|
echo
|
|
echo "The following changes will be applied:"
|
|
show_plan_summary
|
|
echo
|
|
echo "Log file: $LOG_FILE"
|
|
echo "Backup directory: $BACKUP_DIR"
|
|
echo
|
|
read -p "Do you want to proceed with deployment? (y/n): " -n 1 -r
|
|
echo
|
|
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
log "Deployment cancelled by user"
|
|
exit 0
|
|
fi
|
|
}
|
|
|
|
# Function to apply deployment
|
|
apply_deployment() {
|
|
log "Applying deployment..."
|
|
|
|
if terraform apply "$PLAN_FILE"; then
|
|
success "Deployment applied successfully"
|
|
else
|
|
error "Deployment failed"
|
|
fi
|
|
}
|
|
|
|
# Function to verify deployment
|
|
verify_deployment() {
|
|
log "Verifying deployment..."
|
|
|
|
# Check if resources were created successfully
|
|
OUTPUTS=$(terraform output -json)
|
|
|
|
if [[ -n "$OUTPUTS" ]]; then
|
|
success "Deployment verification passed"
|
|
echo "Outputs:"
|
|
terraform output
|
|
else
|
|
warning "No outputs generated - manual verification required"
|
|
fi
|
|
}
|
|
|
|
# Function to cleanup temporary files
|
|
cleanup() {
|
|
log "Cleaning up temporary files..."
|
|
|
|
if [[ -f "$PLAN_FILE" ]]; then
|
|
rm "$PLAN_FILE"
|
|
success "Plan file removed"
|
|
fi
|
|
}
|
|
|
|
# Function to show deployment summary
|
|
deployment_summary() {
|
|
echo
|
|
echo "=================================================="
|
|
echo "🎉 DEPLOYMENT SUMMARY"
|
|
echo "=================================================="
|
|
echo
|
|
echo "✅ Infrastructure deployed successfully"
|
|
echo "📋 Log file: $LOG_FILE"
|
|
echo "💾 Backups: $BACKUP_DIR"
|
|
echo "🌐 Resources deployed:"
|
|
terraform state list
|
|
echo
|
|
echo "Next steps:"
|
|
echo "1. Check Cloudflare dashboard for deployed resources"
|
|
echo "2. Test DNS resolution for your domains"
|
|
echo "3. Verify WAF rules are active"
|
|
echo "4. Test tunnel connectivity"
|
|
echo
|
|
}
|
|
|
|
# Function to handle rollback
|
|
rollback() {
|
|
error "Deployment failed - rolling back..."
|
|
|
|
# Check if we have a backup
|
|
LATEST_BACKUP=$(ls -t "${BACKUP_DIR}/state_backup_*.tfstate" 2>/dev/null | head -n1)
|
|
|
|
if [[ -n "$LATEST_BACKUP" ]]; then
|
|
log "Restoring from backup: $LATEST_BACKUP"
|
|
cp "$LATEST_BACKUP" "$STATE_FILE"
|
|
warning "State restored from backup. Manual verification required."
|
|
else
|
|
error "No backup available for rollback"
|
|
fi
|
|
}
|
|
|
|
# Main deployment function
|
|
main() {
|
|
echo "🚀 Cloudflare Infrastructure Deployment"
|
|
echo "=================================================="
|
|
echo
|
|
|
|
# Change to Terraform directory
|
|
cd "$TERRAFORM_DIR" || error "Terraform directory not found"
|
|
|
|
# Set trap for cleanup on exit
|
|
trap cleanup EXIT
|
|
|
|
# Execute deployment steps
|
|
check_prerequisites
|
|
backup_state
|
|
prepare_config
|
|
init_terraform
|
|
validate_config
|
|
|
|
# Create plan and check if changes are needed
|
|
if create_plan; then
|
|
case $? in
|
|
0)
|
|
success "No changes needed - infrastructure is up to date"
|
|
exit 0
|
|
;;
|
|
2)
|
|
confirm_deployment
|
|
apply_deployment
|
|
verify_deployment
|
|
deployment_summary
|
|
;;
|
|
esac
|
|
fi
|
|
}
|
|
|
|
# Handle errors
|
|
trap 'rollback' ERR
|
|
|
|
# Run main function
|
|
main "$@" |