diff --git a/Jenkinsfile b/Jenkinsfile index 8762231..15b5eb4 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -62,21 +62,30 @@ pipeline { echo "๐Ÿ” Using Jenkins credentials to authenticate with AWS" withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', credentialsId: env.AWS_CRED_ID]]) { - echo "๐Ÿ”„ Bootstrapping Terraform backend..." + echo "๐Ÿ”„ Checking/Bootstrapping Terraform backend..." dir(tfBackendDir) { - sh """ - terraform init \\ - -var="aws_region=${TF_VAR_aws_region}" \\ - -var="backend_bucket_name=${TF_BACKEND_BUCKET}" \\ - -var="lock_table_name=${TF_DDB_TABLE}" - terraform apply -auto-approve \\ - -var="aws_region=${TF_VAR_aws_region}" \\ - -var="backend_bucket_name=${TF_BACKEND_BUCKET}" \\ - -var="lock_table_name=${TF_DDB_TABLE}" - """ + try { + sh """ + terraform init \\ + -var="aws_region=${TF_VAR_aws_region}" \\ + -var="backend_bucket_name=${TF_BACKEND_BUCKET}" \\ + -var="lock_table_name=${TF_DDB_TABLE}" + terraform apply -auto-approve \\ + -var="aws_region=${TF_VAR_aws_region}" \\ + -var="backend_bucket_name=${TF_BACKEND_BUCKET}" \\ + -var="lock_table_name=${TF_DDB_TABLE}" + """ + echo "โœ… Terraform backend created successfully" + } catch (Exception e) { + if (e.getMessage().contains("BucketAlreadyOwnedByYou") || + e.getMessage().contains("Table already exists")) { + echo "โœ… Terraform backend already exists - continuing..." + } else { + echo "โŒ Unexpected error during backend bootstrap: ${e.getMessage()}" + throw e + } + } } - - echo "โœ… Terraform backend created successfully" } } } @@ -108,8 +117,27 @@ pipeline { echo "๐Ÿšจ SECURITY NOTICE: Infrastructure changes detected - elevated permissions required" echo " Changed files: ${infrastructureFiles}" } else { - env.DEPLOYMENT_TYPE = "APPLICATION" - echo "โœ… SECURITY: Application-only deployment - using restricted permissions" + // Check if infrastructure actually exists in AWS + def clusterExists = false + try { + withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', credentialsId: env.AWS_CRED_ID]]) { + def clusterCheck = sh( + script: "aws ecs describe-clusters --clusters ${TF_VAR_cluster_name} --region ${AWS_REGION} --query 'clusters[0].status' --output text 2>/dev/null || echo 'NOTFOUND'", + returnStdout: true + ).trim() + clusterExists = (clusterCheck == "ACTIVE") + } + } catch (Exception e) { + echo "โš ๏ธ Could not check cluster status: ${e.getMessage()}" + } + + if (!clusterExists) { + env.DEPLOYMENT_TYPE = "INFRASTRUCTURE" + echo "๐Ÿšจ CLEAN AWS DETECTED: No existing infrastructure found - deploying from scratch" + } else { + env.DEPLOYMENT_TYPE = "APPLICATION" + echo "โœ… SECURITY: Application-only deployment - using restricted permissions" + } } def gitCommit = sh(script: 'git rev-parse HEAD', returnStdout: true).trim() @@ -195,6 +223,19 @@ pipeline { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', credentialsId: env.AWS_CRED_ID]]) { script { echo "๐Ÿ” SECURITY: Using ECR for secure, AWS-native container registry" + + // Create ECR repository if it doesn't exist + echo "๐Ÿ” Checking/Creating ECR repository..." + sh """ + if ! aws ecr describe-repositories --repository-names ${ECR_REPO} --region ${AWS_REGION} 2>/dev/null; then + echo "๐Ÿ“ฆ Creating ECR repository: ${ECR_REPO}" + aws ecr create-repository --repository-name ${ECR_REPO} --region ${AWS_REGION} + echo "โœ… ECR repository created successfully" + else + echo "โœ… ECR repository already exists" + fi + """ + sh """ echo "๐Ÿ” Authenticating with ECR using temporary credentials..." aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com @@ -436,14 +477,24 @@ pipeline { echo "๐Ÿš€ DEPLOYMENT: Deploying application to ECS cluster" // Create task definition + def executionRoleArn = "" + try { + executionRoleArn = sh( + script: 'cd terraform && terraform output -raw ecs_task_execution_role_arn', + returnStdout: true + ).trim() + } catch (Exception e) { + echo "โš ๏ธ Could not get execution role ARN: ${e.getMessage()}" + echo "โš ๏ธ Task definition will be created without execution role" + } + def taskDefinition = """ { "family": "${TF_VAR_cluster_name}-task", "networkMode": "bridge", "requiresCompatibilities": ["EC2"], "memory": "512", - "cpu": "256", - "executionRoleArn": "${sh(script: 'cd terraform && terraform output -raw ecs_task_execution_role_arn', returnStdout: true).trim()}", + "cpu": "256"${executionRoleArn ? ",\n \"executionRoleArn\": \"${executionRoleArn}\"" : ""}, "containerDefinitions": [ { "name": "${ECR_REPO}", @@ -497,6 +548,7 @@ pipeline { --service ${TF_VAR_cluster_name}-service \\ --task-definition ${TF_VAR_cluster_name}-task \\ --desired-count 1 \\ + --force-new-deployment \\ --region ${AWS_REGION} else echo "โœ… Creating new service..." @@ -593,7 +645,11 @@ pipeline { echo "๐Ÿงน CLEANUP: Performing post-build cleanup..." // Archive deployment artifacts - archiveArtifacts artifacts: 'task-definition.json', allowEmptyArchive: true + try { + archiveArtifacts artifacts: 'deployment-audit.json,task-definition.json', allowEmptyArchive: true + } catch (Exception e) { + echo "โš ๏ธ Could not archive artifacts: ${e.getMessage()}" + } // Clean up Docker images to save space sh ''' @@ -614,10 +670,20 @@ pipeline { echo "๐ŸŽ‰ SUCCESS: Deployment completed successfully!" echo " Version ${IMAGE_TAG} deployed to ECS cluster ${TF_VAR_cluster_name}" - // Send success notification (customize as needed) - // slackSend channel: '#deployments', - // color: 'good', - // message: "โœ… ${env.JOB_NAME} - Build #${env.BUILD_NUMBER} deployed successfully" + // Get application URL for success message + def appUrl = "" + try { + appUrl = sh( + script: "cd terraform && terraform output -raw ecs_instance_public_ip 2>/dev/null || echo 'unknown'", + returnStdout: true + ).trim() + if (appUrl != "unknown" && appUrl != "") { + echo "๐ŸŒ Application available at: http://${appUrl}:8080" + echo "๐Ÿฅ Health check: http://${appUrl}:8080/health" + } + } catch (Exception e) { + echo "โš ๏ธ Could not determine application URL" + } } } @@ -626,10 +692,18 @@ pipeline { echo "โŒ FAILURE: Deployment failed" echo " Check the logs above for error details" - // Send failure notification (customize as needed) - // slackSend channel: '#deployments', - // color: 'danger', - // message: "โŒ ${env.JOB_NAME} - Build #${env.BUILD_NUMBER} failed" + // Try to get some debug information + try { + withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', credentialsId: env.AWS_CRED_ID]]) { + echo "๐Ÿ” DEBUG: Checking ECS cluster status..." + sh """ + aws ecs describe-clusters --clusters ${TF_VAR_cluster_name} --region ${AWS_REGION} || echo "Cluster check failed" + aws ecs list-container-instances --cluster ${TF_VAR_cluster_name} --region ${AWS_REGION} || echo "Instance list failed" + """ + } + } catch (Exception e) { + echo "โš ๏ธ Could not get debug information: ${e.getMessage()}" + } } }