diff --git a/.github/workflows/apply-napv5.yml b/.github/workflows/apply-napv5.yml new file mode 100644 index 00000000..6ce373dc --- /dev/null +++ b/.github/workflows/apply-napv5.yml @@ -0,0 +1,376 @@ +name: "NGINX NAP/NIC-V5 Deployment" +on: + push: + branches: [apply-nap] +env: + AWS_REGION: us-east-1 +jobs: + bootstrap_infra: + name: "Bootstrap S3/DynamoDB" + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./s3 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} + aws-region: ${{ env.AWS_REGION }} + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + - name: Initialize Terraform (S3 Backend) + run: | + terraform init + + - name: Terraform Plan + run: terraform plan -no-color -input=false -out=tfplan + + - name: Check for Changes + id: check_changes + run: | + if grep -q "No changes." <(terraform show -no-color tfplan); then + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "has_changes=true" >> $GITHUB_OUTPUT + fi + + - name: Terraform Apply + if: github.event_name == 'push' && github.ref == 'refs/heads/apply-nap' && steps.check_changes.outputs.has_changes == 'true' + run: terraform apply -auto-approve tfplan + + + + terraform_infra: + name: "AWS Infra" + runs-on: ubuntu-latest + needs: bootstrap_infra + permissions: + contents: read + defaults: + run: + working-directory: ./infra + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} + aws-region: ${{ env.AWS_REGION }} + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + + - name: Initialize Terraform (S3 Backend) + run: | + terraform init + + - name: Terraform Plan + if: github.event_name == 'pull_request' || github.event_name == 'push' + run: | + terraform plan -no-color -input=false -out=tfplan + terraform show -no-color tfplan > plan.txt + + - name: Check Changes + id: check_changes + run: | + if grep -q "No changes." plan.txt; then + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "has_changes=true" >> $GITHUB_OUTPUT + fi + + - name: Terraform Apply + if: github.event_name == 'push' && github.ref == 'refs/heads/apply-nap' && steps.check_changes.outputs.has_changes == 'true' + run: terraform apply -auto-approve tfplan + + + terraform_eks: + name: "AWS EKS" + runs-on: ubuntu-latest + needs: terraform_infra + defaults: + run: + working-directory: ./eks-cluster + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} + aws-region: ${{ env.AWS_REGION }} + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + + - name: Terraform Init + run: terraform init + + - name: Terraform Plan + if: github.event_name == 'pull_request' || github.event_name == 'push' + run: | + terraform plan -no-color -input=false -out=tfplan + terraform show -no-color tfplan > plan.txt + + - name: Check Changes + id: check_changes + run: | + if grep -q "No changes." plan.txt; then + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "has_changes=true" >> $GITHUB_OUTPUT + fi + + - name: Terraform Apply + if: github.event_name == 'push' && github.ref == 'refs/heads/apply-nap' && steps.check_changes.outputs.has_changes == 'true' + run: terraform apply -auto-approve tfplan + + terraform_nap: + name: "NGINX App Protect" + runs-on: ubuntu-latest + needs: terraform_eks + defaults: + run: + working-directory: ./nap + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} + aws-region: ${{ env.AWS_REGION }} + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + - name: Terraform Init + run: terraform init + + - name: Terraform Plan + if: github.event_name == 'pull_request' || github.event_name == 'push' + run: | + terraform plan -no-color -input=false -lock=false -out=tfplan \ + -var="workspace_path=${{ env.WORKSPACE_PATH }}" \ + -var="nginx_jwt=${{ secrets.NGINX_JWT }}" \ + -var="nginx_pwd=none" + terraform show -no-color tfplan > plan.txt + env: + WORKSPACE_PATH: "./nap" + + + - name: Check Changes + id: check_changes + run: | + if grep -q "No changes." plan.txt; then + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "has_changes=true" >> $GITHUB_OUTPUT + fi + + - name: Terraform Apply + if: github.event_name == 'push' && github.ref == 'refs/heads/apply-nap' && steps.check_changes.outputs.has_changes == 'true' + run: terraform apply -auto-approve -lock=false tfplan + + terraform_policy: + name: "NGINX Policy" + runs-on: ubuntu-latest + needs: terraform_nap + defaults: + run: + working-directory: ./policy + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} + aws-region: ${{ env.AWS_REGION }} + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + + - name: Terraform Init (EKS) + run: terraform init + working-directory: ./eks-cluster + + - name: Print EKS Terraform Outputs + run: terraform output + working-directory: ./eks-cluster + + - name: Fetch EKS Cluster Name and Region + run: | + echo "EKS_CLUSTER_NAME=$(terraform output -raw cluster_name)" >> $GITHUB_ENV + echo "AWS_REGION=$AWS_REGION" >> $GITHUB_ENV + working-directory: ./eks-cluster + + - name: Configure kubectl for EKS + run: | + aws eks update-kubeconfig --name $EKS_CLUSTER_NAME --region $AWS_REGION + + - name: Verify kubectl connectivity + run: kubectl get nodes -n nginx-ingress + + - name: Install Docker and Docker Compose + run: | + sudo apt-get update -y + sudo apt-get remove -y containerd containerd.io + sudo apt-get install apt-transport-https ca-certificates curl software-properties-common -y + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - + sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" + sudo apt-get update -y + sudo apt-get install docker-ce docker-ce-cli containerd.io -y + sudo service docker start + sudo usermod -aG docker $USER + + sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose + + - name: Create Certificates Directory for Docker + run: | + sudo mkdir -p /etc/docker/certs.d/private-registry.nginx.com + + - name: Create NGINX Repository Certificate for Docker + run: echo "${{ secrets.NGINX_REPO_CRT }}" | sudo tee /etc/docker/certs.d/private-registry.nginx.com/client.cert > /dev/null + + - name: Create NGINX Repository Key for Docker + run: echo "${{ secrets.NGINX_REPO_KEY }}" | sudo tee /etc/docker/certs.d/private-registry.nginx.com/client.key > /dev/null + + - name: Create Certificates Directory for NGINX + run: | + sudo mkdir -p /etc/ssl/nginx + + - name: Save NGINX Repository Certificate for NGINX + run: echo "${{ secrets.NGINX_REPO_CRT }}" | sudo tee /etc/ssl/nginx/nginx-repo.crt > /dev/null + + - name: Save NGINX Repository Key for NGINX + run: echo "${{ secrets.NGINX_REPO_KEY }}" | sudo tee /etc/ssl/nginx/nginx-repo.key > /dev/null + + - name: Build Docker Image + run: | + docker build --no-cache \ + --secret id=nginx-crt,src=/etc/ssl/nginx/nginx-repo.crt \ + --secret id=nginx-key,src=/etc/ssl/nginx/nginx-repo.key \ + -t waf-compiler-5.4.0:custom . + + - name: Ensure correct permissions for nap/charts directory + run: | + sudo chown -R $USER:$USER ${{ github.workspace }} + sudo chmod -R 777 ${{ github.workspace }} + + - name: Run Docker Container as Root + run: | + docker run --rm \ + -v ${{ github.workspace }}:/workspace \ + waf-compiler-5.4.0:custom \ + -p /workspace/policy/policy.json -o /workspace/policy/compiled_policy.tgz + + - name: Fix permissions for compiled files + run: | + sudo chown -R $USER:$USER ${{ github.workspace }}/policy + chmod 644 ${{ github.workspace }}/policy/compiled_policy.tgz + ls -lh ${{ github.workspace }}/policy + + + - name: Copy Compiled Policy to NGINX Ingress Controller + run: | + NGINX_POD=$(kubectl get pods -n nginx-ingress -l app.kubernetes.io/name=nginx-ingress -o jsonpath='{.items[0].metadata.name}') + if [ -z "$NGINX_POD" ]; then + echo "Error: NGINX Ingress Controller pod not found!" + exit 1 + fi + kubectl cp ${{ github.workspace }}/policy/compiled_policy.tgz $NGINX_POD:/etc/app_protect/bundles/compiled_policy.tgz -n nginx-ingress + + - name: Terraform Init + run: terraform init + + - name: Terraform Plan + run: | + terraform plan -no-color -input=false -out=tfplan + terraform show -no-color tfplan > plan.txt + + - name: Check Changes + id: check_changes + run: | + if grep -q "No changes." plan.txt; then + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "has_changes=true" >> $GITHUB_OUTPUT + fi + + - name: Terraform Apply + if: github.event_name == 'push' && github.ref == 'refs/heads/apply-nap' && steps.check_changes.outputs.has_changes == 'true' + run: terraform apply -auto-approve tfplan + + terraform_arcadia: + name: "Arcadia WebApp" + runs-on: ubuntu-latest + needs: terraform_policy + defaults: + run: + working-directory: ./arcadia + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} + aws-region: ${{ env.AWS_REGION }} + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + - name: Terraform Init + run: terraform init + + - name: Terraform Validate + run: terraform validate -no-color + + - name: Terraform Plan + if: github.event_name == 'pull_request' || github.event_name == 'push' + run: | + terraform plan -no-color -input=false -out=tfplan + terraform show -no-color tfplan > plan.txt + + - name: Check Changes + id: check_changes + run: | + if grep -q "No changes." plan.txt; then + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "has_changes=true" >> $GITHUB_OUTPUT + fi + + - name: Terraform Apply + if: github.event_name == 'push' && github.ref == 'refs/heads/apply-nap' && steps.check_changes.outputs.has_changes == 'true' + run: terraform apply -auto-approve tfplan diff --git a/.github/workflows/destroy-nic-nap.yml b/.github/workflows/destroy-nic-nap.yml new file mode 100644 index 00000000..4fecb09e --- /dev/null +++ b/.github/workflows/destroy-nic-nap.yml @@ -0,0 +1,365 @@ +name: "NGINX V5-NIC/NAP Destroy" +on: + push: + branches: + - destroy-nic-napv5 + pull_request: +env: + AWS_REGION: us-east-1 +jobs: + terraform_arcadia: + name: "Destroy Arcadia WebApp" + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./arcadia + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} + aws-region: ${{ env.AWS_REGION }} + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + - name: Terraform Init + run: terraform init + + - name: Terraform Validate + run: terraform validate -no-color + + - name: Terraform Plan (Destroy) + if: github.event_name == 'pull_request' || github.event_name == 'push' + run: | + terraform plan -destroy -no-color -input=false -lock=false -out=tfplan + terraform show -no-color tfplan > plan.txt + + - name: Check Changes + id: check_changes + run: | + if grep -q "No changes." plan.txt; then + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "has_changes=true" >> $GITHUB_OUTPUT + fi + + - name: Terraform Destroy + if: github.event_name == 'push' && github.ref == 'refs/heads/destroy-nic-napv5' && steps.check_changes.outputs.has_changes == 'true' + run: terraform destroy -auto-approve -lock=false -input=false + + terraform_policy: + name: "Destroy NGINX Policy" + runs-on: ubuntu-latest + needs: terraform_arcadia + defaults: + run: + working-directory: ./policy + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} + aws-region: ${{ env.AWS_REGION }} + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + - name: Terraform Init + run: terraform init + + - name: Terraform Destroy + run: terraform destroy -auto-approve -lock=false + + terraform_nap: + name: "Destroy NGINX NIC/App Protect" + runs-on: ubuntu-latest + needs: terraform_policy + defaults: + run: + working-directory: ./nap + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} + aws-region: ${{ env.AWS_REGION }} + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + - name: Terraform Init + run: terraform init + + - name: Terraform Plan (Destroy) + run: | + terraform plan -destroy -no-color -input=false -lock=false -out=tfplan \ + -var="workspace_path=${{ env.WORKSPACE_PATH }}" \ + -var="nginx_jwt=${{ secrets.NGINX_JWT }}" \ + -var="nginx_pwd=none" + env: + WORKSPACE_PATH: "./nap" + + - name: Check Changes + id: check_changes + run: | + if grep -q "No changes." plan.txt; then + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "has_changes=true" >> $GITHUB_OUTPUT + fi + + - name: Terraform Destroy + if: github.event_name == 'push' && github.ref == 'refs/heads/destroy-nic-napv5' && steps.check_changes.outputs.has_changes == 'true' + run: | + terraform destroy -auto-approve -input=false -lock=false \ + -var="workspace_path=${{ env.WORKSPACE_PATH }}" \ + -var="nginx_jwt=${{ secrets.NGINX_JWT }}" \ + -var="nginx_pwd=none" + env: + WORKSPACE_PATH: "./nap" + + terraform_eks: + name: "Destroy AWS EKS" + runs-on: ubuntu-latest + needs: terraform_nap + defaults: + run: + working-directory: ./eks-cluster + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} + aws-region: ${{ env.AWS_REGION }} + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + - name: Terraform Init + run: terraform init + + - name: Terraform Plan (Destroy) + if: github.event_name == 'pull_request' || github.event_name == 'push' + run: | + terraform plan -destroy -no-color -input=false -out=tfplan -lock=false + terraform show -no-color tfplan > plan.txt + + - name: Check Changes + id: check_changes + run: | + if grep -q "No changes." plan.txt; then + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "has_changes=true" >> $GITHUB_OUTPUT + fi + + - name: Terraform Destroy + if: github.event_name == 'push' && github.ref == 'refs/heads/destroy-nic-napv5' && steps.check_changes.outputs.has_changes == 'true' + run: terraform destroy -auto-approve -input=false -lock=false + + terraform_infra: + name: "Destroy AWS Infra" + runs-on: ubuntu-latest + needs: terraform_eks + defaults: + run: + working-directory: ./infra + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} + aws-region: ${{ env.AWS_REGION }} + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + - name: Terraform Init + run: terraform init + + - name: Terraform Plan (Destroy) + if: github.event_name == 'pull_request' || github.event_name == 'push' + run: | + terraform plan -destroy -no-color -input=false -out=tfplan -lock=false + terraform show -no-color tfplan > plan.txt + + - name: Check Changes + id: check_changes + run: | + if grep -q "No changes." plan.txt; then + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "has_changes=true" >> $GITHUB_OUTPUT + fi + + - name: Terraform Destroy + if: github.event_name == 'push' && github.ref == 'refs/heads/destroy-nic-napv5' && steps.check_changes.outputs.has_changes == 'true' + run: terraform destroy -auto-approve -input=false -lock=false + + + terraform_S3: + name: "Delete S3/DynamoDB" + needs: terraform_infra + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./s3 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install jq + run: sudo apt-get install -y jq + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} + aws-region: ${{ env.AWS_REGION }} + + - name: Set Bucket Name + id: set_bucket + run: | + echo "bucket_name=akash-terraform-state-bucket" >> $GITHUB_OUTPUT + + - name: Nuclear S3 Bucket Deletion + run: | + set -e + BUCKET_NAME="${{ steps.set_bucket.outputs.bucket_name }}" + + # 1. Delete all object versions (with null checks) + echo "🔥 Deleting ALL object versions..." + versions=$(aws s3api list-object-versions --bucket $BUCKET_NAME --output json || echo '{"Versions":[],"DeleteMarkers":[]}') + versions_to_delete=$(echo $versions | jq '{Objects: [.Versions[]? | {Key:.Key, VersionId:.VersionId}]}' || echo '{"Objects":[]}') + if [ "$(echo $versions_to_delete | jq '.Objects | length')" -gt 0 ]; then + aws s3api delete-objects --bucket $BUCKET_NAME --delete "$versions_to_delete" || true + fi + + # 2. Delete all delete markers (with null checks) + echo "🗑️ Deleting ALL delete markers..." + markers_to_delete=$(echo $versions | jq '{Objects: [.DeleteMarkers[]? | {Key:.Key, VersionId:.VersionId}]}' || echo '{"Objects":[]}') + if [ "$(echo $markers_to_delete | jq '.Objects | length')" -gt 0 ]; then + aws s3api delete-objects --bucket $BUCKET_NAME --delete "$markers_to_delete" || true + fi + + # 3. Force delete any remaining objects + echo "💥 Force deleting any remaining objects..." + aws s3 rm s3://$BUCKET_NAME --recursive --include "*" || true + + # 4. Delete bucket + echo "🚀 Deleting bucket..." + aws s3api delete-bucket --bucket $BUCKET_NAME || true + + # 5. Final verification + if aws s3api head-bucket --bucket $BUCKET_NAME 2>/dev/null; then + echo "::error::Bucket $BUCKET_NAME still exists after deletion attempts!" + exit 1 + else + echo "✅ Bucket $BUCKET_NAME successfully deleted" + fi + + - name: Delete DynamoDB Table + run: | + set -e + TABLE_NAME="terraform-lock-table" + echo "💥 Deleting DynamoDB table..." + if aws dynamodb describe-table --table-name $TABLE_NAME 2>/dev/null; then + aws dynamodb delete-table --table-name $TABLE_NAME || true + echo "⌛ Waiting for table to be deleted..." + aws dynamodb wait table-not-exists --table-name $TABLE_NAME || true + fi + if aws dynamodb describe-table --table-name $TABLE_NAME 2>/dev/null; then + echo "::error::Table $TABLE_NAME still exists!" + exit 1 + else + echo "✅ Table $TABLE_NAME successfully deleted" + fi + + - name: Clean Up IAM Resources + run: | + set -e + # Delete policy + POLICY_ARN=$(aws iam list-policies --query "Policies[?PolicyName=='TerraformStateAccess'].Arn" --output text || echo "") + if [ -n "$POLICY_ARN" ]; then + echo "🔗 Detaching policy from roles..." + ATTACHED_ROLES=$(aws iam list-entities-for-policy --policy-arn $POLICY_ARN --query "PolicyRoles[].RoleName" --output text || echo "") + for ROLE in $ATTACHED_ROLES; do + aws iam detach-role-policy --role-name $ROLE --policy-arn $POLICY_ARN || true + done + + echo "🗑️ Deleting policy..." + aws iam delete-policy --policy-arn $POLICY_ARN || true + fi + + # Delete role + ROLE_NAME="TerraformCIExecutionRole" + if aws iam get-role --role-name $ROLE_NAME 2>/dev/null; then + echo "🗑️ Deleting role..." + aws iam delete-role --role-name $ROLE_NAME || true + fi + + - name: Verify Deletion + run: | + echo "✅ Verification:" + + # Verify S3 bucket + BUCKET_NAME="${{ steps.set_bucket.outputs.bucket_name }}" + if aws s3api head-bucket --bucket "$BUCKET_NAME" 2>/dev/null; then + echo "::error::Bucket $BUCKET_NAME still exists!" + exit 1 + else + echo "Bucket $BUCKET_NAME deleted successfully" + fi + + # Verify DynamoDB table + TABLE_NAME="terraform-lock-table" + if aws dynamodb describe-table --table-name "$TABLE_NAME" 2>/dev/null; then + echo "::error::Table $TABLE_NAME still exists!" + exit 1 + else + echo "Table $TABLE_NAME deleted successfully" + fi + + # Verify IAM resources + if aws iam get-policy --policy-arn "arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):policy/TerraformStateAccess" 2>/dev/null; then + echo "::error::IAM Policy still exists!" + exit 1 + else + echo "IAM Policy deleted successfully" + fi + + if aws iam get-role --role-name "TerraformCIExecutionRole" 2>/dev/null; then + echo "::error::IAM Role still exists!" + exit 1 + else + echo "IAM Role deleted successfully" + fi diff --git a/README.md b/README.md index 052d9357..39239896 100644 --- a/README.md +++ b/README.md @@ -80,18 +80,27 @@ This workflow requires the following secrets to be configured in your GitHub rep * resource_owner = "Your-name" * aws_region = "AWS Region" ex. us-east-1 * azs = ["us-east-1a", "us-east1b"] - Change to Correct Availability Zones based on selected Region - * Also update assets boolean value as per your work-flows -**STEP 3:** In the `S3 directory`, inside the `variable.tf` file modify the following data - * description = "S3 bucket for Terraform remote state storage" + +**STEP 3:** Modify the `S3/variable.tf` file inside the `S3 directory`. * default = "your-unique-bucket-name" # Replace with your actual bucket name - -**STEP 4:** Commit and push your build branch to your forked repo +**STEP 4:** Modify the `Backend.tf` file in the `Infra/Backend.tf`, `eks-cluster/Backend.tf`, `Nap/Backend.tf`, `Policy/Backend.tf`, `Arcadia/Backend.tf` directory. + * bucket = "your-unique-bucket-name" # Your S3 bucket name + * key = "infra/terraform.tfstate" # Path to state file + * region = "your-aws-region-name" By default us-east-1 + +**STEP 5:** Add the name of your S3 bucket inside the `NGINX V5-NIC/NAP Destroy` workflow file, which is located in the Terraform _S3 job. + * name: Set Bucket Name + * id: set_bucket + * run: | + * echo "bucket_name="your-unique-bucket-name" >> $GITHUB_OUTPUT + +**STEP 6:** Commit and push your build branch to your forked repo * Build will run and can be monitored in the GitHub Actions tab and TF Cloud console -**STEP 5:** Once the pipeline is complete, verify that your assets were deployed or destroyed based on your workflow. +**STEP 7:** Once the pipeline is complete, verify that your assets were deployed or destroyed based on your workflow. **NOTE:** The autocert process takes time. It may be 5 to 10 minutes before Let's Encrypt has provided the cert. diff --git a/arcadia/backend.tf b/arcadia/backend.tf new file mode 100644 index 00000000..8c2b3dab --- /dev/null +++ b/arcadia/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "akash-terraform-state-bucket" # Your S3 bucket name + key = "arcadia/terraform.tfstate" # Path to state file + region = "us-east-1" # AWS region + dynamodb_table = "terraform-lock-table" # DynamoDB table for state locking + encrypt = true # Encrypt state file at rest + } +} \ No newline at end of file diff --git a/arcadia/data.tf b/arcadia/data.tf index dbe26856..cc8f10b4 100644 --- a/arcadia/data.tf +++ b/arcadia/data.tf @@ -1,21 +1,33 @@ -data "tfe_outputs" "infra" { - organization = var.tf_cloud_organization - workspace = "infra" +# Read infra state from S3 +data "terraform_remote_state" "infra" { + backend = "s3" + config = { + bucket = "akash-terraform-state-bucket" # Your S3 bucket name + key = "infra/terraform.tfstate" # Path to infra's state file + region = "us-east-1" # AWS region + } } -data "tfe_outputs" "eks" { - organization = var.tf_cloud_organization - workspace = "eks" + + +data "terraform_remote_state" "nap" { + backend = "s3" + config = { + bucket = "akash-terraform-state-bucket" # Your S3 bucket name + key = "nap/terraform.tfstate" # Path to NAP state file + region = "us-east-1" # AWS region + } } -data "tfe_outputs" "nap" { - count = data.tfe_outputs.infra.values.nap ? 1 : 0 - organization = var.tf_cloud_organization - workspace = "nap" -} -data "tfe_outputs" "nic" { - count = data.tfe_outputs.infra.values.nic ? 1 : 0 - organization = var.tf_cloud_organization - workspace = "nic" + +data "terraform_remote_state" "eks" { + backend = "s3" + config = { + bucket = "akash-terraform-state-bucket" # Your S3 bucket name + key = "eks-cluster/terraform.tfstate" # Path to EKS state file + region = "us-east-1" # AWS region + } } + +# Get EKS cluster auth using S3 state data "aws_eks_cluster_auth" "auth" { - name = data.tfe_outputs.eks.values.cluster_name -} + name = data.terraform_remote_state.eks.outputs.cluster_name +} \ No newline at end of file diff --git a/arcadia/locals.tf b/arcadia/locals.tf index e38109f0..b92cfbdb 100644 --- a/arcadia/locals.tf +++ b/arcadia/locals.tf @@ -1,9 +1,8 @@ locals { - project_prefix = data.tfe_outputs.infra.values.project_prefix - #external_name = try(data.tfe_outputs.nap.values.external_name, data.tfe_outputs.nic.values.external_name, "arcadia-cd-demo.sr.f5-cloud-demo.com") - external_name = try(data.tfe_outputs.nap[0].values.external_name, data.tfe_outputs.nic[0].values.external_name) - aws_region = data.tfe_outputs.infra.values.aws_region - host = data.tfe_outputs.eks.values.cluster_endpoint - cluster_ca_certificate = data.tfe_outputs.eks.values.kubeconfig-certificate-authority-data - cluster_name = data.tfe_outputs.eks.values.cluster_name -} \ No newline at end of file + project_prefix = data.terraform_remote_state.infra.outputs.project_prefix + aws_region = data.terraform_remote_state.infra.outputs.aws_region + external_name = try(data.terraform_remote_state.nap.outputs.external_name, "arcadia-cd-demo.sr.f5-cloud-demo.com") + host = data.terraform_remote_state.eks.outputs.cluster_endpoint + cluster_ca_certificate = data.terraform_remote_state.eks.outputs.kubeconfig-certificate-authority-data + cluster_name = data.terraform_remote_state.eks.outputs.cluster_name +} \ No newline at end of file diff --git a/arcadia/versions.tf b/arcadia/versions.tf index 49810ae2..fc823ab0 100644 --- a/arcadia/versions.tf +++ b/arcadia/versions.tf @@ -1,14 +1,23 @@ terraform { - required_version = ">= 0.14.0" + required_version = ">= 1.6.0" + required_providers { - aws = ">= 4" + aws = { + source = "hashicorp/aws" + version = ">= 4.0.0" + } kubernetes = { - source = "hashicorp/kubernetes" - version = "2.16.1" + source = "hashicorp/kubernetes" + version = ">= 2.23.0" } helm = { source = "hashicorp/helm" - version = ">=2.7.0" + version = ">= 2.12.0" + } + kubectl = { + source = "gavinbunney/kubectl" + version = ">= 1.15.0" } } -} \ No newline at end of file + +} diff --git a/arcadia/virtual.tf b/arcadia/virtual.tf new file mode 100644 index 00000000..14822512 --- /dev/null +++ b/arcadia/virtual.tf @@ -0,0 +1,70 @@ +resource "kubernetes_manifest" "arcadia_virtualserver" { + manifest = { + apiVersion = "k8s.nginx.org/v1" + kind = "VirtualServer" + metadata = { + name = "arcadia-virtualserver" + namespace = "default" + } + spec = { + host = try(data.terraform_remote_state.nap.outputs.external_name, "arcadia-cd-demo.sr.f5-cloud-demo.com") + + # Reference the WAF policy + policies = [ + { + name = "waf-policy" # Name of the WAF policy + namespace = "default" # Namespace where the WAF policy is deployed + } + ] + + upstreams = [ + { + name = "main-upstream" + service = kubernetes_service.main.metadata[0].name + port = 80 + }, + { + name = "backend-upstream" + service = kubernetes_service.backend.metadata[0].name + port = 80 + }, + { + name = "app2-upstream" + service = kubernetes_service.app_2.metadata[0].name + port = 80 + }, + { + name = "app3-upstream" + service = kubernetes_service.app_3.metadata[0].name + port = 80 + } + ] + routes = [ + { + path = "/" + action = { + pass = "main-upstream" + } + }, + { + path = "/files" + action = { + pass = "backend-upstream" + } + }, + { + path = "/api" + action = { + pass = "app2-upstream" + } + }, + { + path = "/app3" + action = { + pass = "app3-upstream" + } + } + ] + } + } +} \ No newline at end of file diff --git a/arcadia/wafpolicy.tf b/arcadia/wafpolicy.tf new file mode 100644 index 00000000..e68eefa3 --- /dev/null +++ b/arcadia/wafpolicy.tf @@ -0,0 +1,16 @@ +resource "kubernetes_manifest" "waf_policy" { + manifest = { + apiVersion = "k8s.nginx.org/v1" + kind = "Policy" + metadata = { + name = "waf-policy" + namespace = "default" # Replace with your desired namespace + } + spec = { + waf = { + enable = true + apBundle = "compiled_policy.tgz" + } + } + } +} diff --git a/eks-cluster/.terraform/modules/modules.json b/eks-cluster/.terraform/modules/modules.json new file mode 100644 index 00000000..9fed0aeb --- /dev/null +++ b/eks-cluster/.terraform/modules/modules.json @@ -0,0 +1 @@ +{"Modules":[{"Key":"","Source":"","Dir":"."},{"Key":"subnet_addrs","Source":"registry.terraform.io/hashicorp/subnets/cidr","Version":"1.0.0","Dir":".terraform/modules/subnet_addrs"}]} \ No newline at end of file diff --git a/eks-cluster/.terraform/modules/subnet_addrs b/eks-cluster/.terraform/modules/subnet_addrs new file mode 160000 index 00000000..52ca061a --- /dev/null +++ b/eks-cluster/.terraform/modules/subnet_addrs @@ -0,0 +1 @@ +Subproject commit 52ca061aaea2e8f58c91ac03ca1fae45e44c28bf diff --git a/eks-cluster/backend.tf b/eks-cluster/backend.tf new file mode 100644 index 00000000..aa26cccc --- /dev/null +++ b/eks-cluster/backend.tf @@ -0,0 +1,10 @@ +terrafrom { + backend "s3" { + bucket = "akash-terraform-state-bucket" # Your S3 bucket name + key = "eks-cluster/terraform.tfstate" # Path to state file + region = "us-east-1" # AWS region + dynamodb_table = "terraform-lock-table" # DynamoDB table for state locking + encrypt = true + + } +} \ No newline at end of file diff --git a/eks-cluster/data.tf b/eks-cluster/data.tf index 27125a9a..50c442bb 100644 --- a/eks-cluster/data.tf +++ b/eks-cluster/data.tf @@ -1,5 +1,9 @@ -data "tfe_outputs" "infra" { - organization = var.tf_cloud_organization - workspace = "infra" +data "terraform_remote_state" "infra" { + backend = "s3" + config = { + bucket = "akash-terraform-state-bucket" # Your S3 bucket name + key = "infra/terraform.tfstate" # Path to infra's state file + region = "us-east-1" # AWS region + } } diff --git a/eks-cluster/eks_cluster.tf b/eks-cluster/eks_cluster.tf index bf00a428..9fa0f0df 100644 --- a/eks-cluster/eks_cluster.tf +++ b/eks-cluster/eks_cluster.tf @@ -1,4 +1,4 @@ - +# eks-cluster/ekscluster.tf # Create EKS cluster and node groups resource "aws_eks_cluster" "eks-tf" { @@ -28,8 +28,8 @@ resource "aws_eks_node_group" "private-node-group-1-tf" { instance_types = ["t3.medium"] scaling_config { - desired_size = 2 - max_size = 3 + desired_size = 1 + max_size = 2 min_size = 1 } @@ -44,13 +44,6 @@ resource "aws_eks_node_group" "private-node-group-1-tf" { ] } - resource "aws_eks_addon" "cluster-addons" { - for_each = { for addon in var.eks_addons : addon.name => addon } - cluster_name = aws_eks_cluster.eks-tf.id - addon_name = each.value.name - #addon_version = each.value.version - resolve_conflicts = "OVERWRITE" - } resource "aws_eks_node_group" "private-node-group-2-tf" { cluster_name = aws_eks_cluster.eks-tf.name @@ -61,8 +54,8 @@ resource "aws_eks_node_group" "private-node-group-2-tf" { instance_types = ["t3.medium"] scaling_config { - desired_size = 2 - max_size = 3 + desired_size = 1 + max_size = 2 min_size = 1 } @@ -76,3 +69,52 @@ resource "aws_eks_node_group" "private-node-group-2-tf" { aws_iam_role_policy_attachment.AmazonEC2ContainerRegistryReadOnly, ] } + +# Create EKS Addons +resource "aws_eks_addon" "cluster-addons" { + for_each = { for addon in var.eks_addons : addon.name => addon } + cluster_name = aws_eks_cluster.eks-tf.id + addon_name = each.value.name + resolve_conflicts = "OVERWRITE" + + # Add service account role ARN for EBS CSI driver + service_account_role_arn = each.value.name == "aws-ebs-csi-driver" ? aws_iam_role.ebs_csi_driver.arn : null + + depends_on = [ + aws_eks_node_group.private-node-group-1-tf, + aws_eks_node_group.private-node-group-2-tf, + aws_iam_role.ebs_csi_driver + ] +} + +output "kubeconfig" { + value = <:none" + } + } + }) + } +} \ No newline at end of file diff --git a/nap/storage.tf b/nap/storage.tf new file mode 100644 index 00000000..c2a7fa2f --- /dev/null +++ b/nap/storage.tf @@ -0,0 +1,16 @@ +resource "kubernetes_storage_class_v1" "aws_csi" { + metadata { + name = "ebs-sc" + annotations = { + "storageclass.kubernetes.io/is-default-class" = "true" + } + } + storage_provisioner = "ebs.csi.aws.com" + reclaim_policy = "Delete" + parameters = { + type = "gp3" + fsType = "ext4" + } + allow_volume_expansion = true + volume_binding_mode = "WaitForFirstConsumer" +} diff --git a/nap/variables.tf b/nap/variables.tf index bbe59469..f2318eb7 100644 --- a/nap/variables.tf +++ b/nap/variables.tf @@ -1,9 +1,3 @@ -# Terraform Cloud Organization -variable "tf_cloud_organization" { - type = string - description = "Terraform Cloud Organization (set in GitHub secrets)" -} - # NGINX Configuration variable "nginx_registry" { type = string @@ -11,19 +5,20 @@ variable "nginx_registry" { default = "private-registry.nginx.com" } -variable "nginx_jwt" { - type = string - description = "JWT for pulling NGINX image" -} - variable "nginx_pwd" { type = string description = "Password for NGINX (if required)" default = "none" } -# SSH Key (for potential SSH-based configurations) -variable "ssh_key" { +variable "workspace_path" { + description = "The path to the workspace directory" + type = string +} + +variable "nginx_jwt" { + description = "The JWT token for NGINX" type = string - description = "Unneeded for NGINX App Protect, only used for TF Cloud variable warnings" + sensitive = true # Mark as sensitive to avoid exposing it in logs } + diff --git a/nap/versions.tf b/nap/versions.tf index a19b134a..cc6970f9 100644 --- a/nap/versions.tf +++ b/nap/versions.tf @@ -1,18 +1,32 @@ terraform { - required_version = ">= 0.14.0" + required_version = ">= 1.6.0" + required_providers { - aws = ">= 4" + aws = { + source = "hashicorp/aws" + version = ">= 4.0.0" + } kubernetes = { - source = "hashicorp/kubernetes" - version = "2.16.1" + source = "hashicorp/kubernetes" + version = ">= 2.23.0" } helm = { source = "hashicorp/helm" - version = ">=2.7.0" + version = ">= 2.12.0" + } + github = { + source = "integrations/github" + version = "6.6.0" } kubectl = { source = "gavinbunney/kubectl" - version = ">= 1.7.0" + version = ">= 1.15.0" + } + docker = { + source = "kreuzwerker/docker" + version = ">= 3.0.2" } + } -} \ No newline at end of file + +} diff --git a/policy/Dockerfile b/policy/Dockerfile new file mode 100755 index 00000000..f39c3be8 --- /dev/null +++ b/policy/Dockerfile @@ -0,0 +1,35 @@ +# syntax=docker/dockerfile:1 +ARG BASE_IMAGE=private-registry.nginx.com/nap/waf-compiler:5.4.0 +FROM ${BASE_IMAGE} + +# Installing packages as root +USER root + +ENV DEBIAN_FRONTEND="noninteractive" + +RUN --mount=type=secret,id=nginx-crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \ + --mount=type=secret,id=nginx-key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \ + apt-get update \ + && apt-get install -y \ + apt-transport-https \ + lsb-release \ + ca-certificates \ + wget \ + gnupg2 \ + ubuntu-keyring \ + && wget -qO - https://cs.nginx.com/static/keys/app-protect-security-updates.key | gpg --dearmor | \ + tee /usr/share/keyrings/app-protect-security-updates.gpg >/dev/null \ + && printf "deb [signed-by=/usr/share/keyrings/app-protect-security-updates.gpg] \ + https://pkgs.nginx.com/app-protect-security-updates/ubuntu `lsb_release -cs` nginx-plus\n" | \ + tee /etc/apt/sources.list.d/nginx-app-protect.list \ + && wget -P /etc/apt/apt.conf.d https://cs.nginx.com/static/files/90pkgs-nginx \ + && apt-get update \ + && apt-get install -y \ + app-protect-attack-signatures \ + app-protect-bot-signatures \ + app-protect-threat-campaigns \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# non-root default user (UID 101) +USER nginx diff --git a/policy/backend.tf b/policy/backend.tf new file mode 100644 index 00000000..c496426d --- /dev/null +++ b/policy/backend.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "akash-terraform-state-bucket" # Your S3 bucket name + key = "policy/terraform.tfstate" # Path to state file + region = "us-east-1" # AWS region + dynamodb_table = "terraform-lock-table" # DynamoDB table for state locking + encrypt = true # Encrypt state file at rest + } +} \ No newline at end of file diff --git a/policy/data.tf b/policy/data.tf new file mode 100755 index 00000000..c70d87c6 --- /dev/null +++ b/policy/data.tf @@ -0,0 +1,35 @@ +# Read infra state from S3 +data "terraform_remote_state" "infra" { + backend = "s3" + config = { + bucket = "akash-terraform-state-bucket" # Your S3 bucket name + key = "infra/terraform.tfstate" # Path to infra's state file + region = "us-east-1" # AWS region + } +} + +# Read eks state from S3 +data "terraform_remote_state" "eks" { + backend = "s3" + config = { + bucket = "akash-terraform-state-bucket" # Your S3 bucket name + key = "eks-cluster/terraform.tfstate" # Path to EKS state file + region = "us-east-1" # AWS region + } +} + +# Read nap state from S3 +data "terraform_remote_state" "nap" { + backend = "s3" + config = { + bucket = "akash-terraform-state-bucket" # Your S3 bucket name + key = "nap/terraform.tfstate" # Path to NAP state file + region = "us-east-1" # AWS region + } +} + +# Keep existing data sources for Kubernetes +data "aws_eks_cluster_auth" "auth" { + name = data.terraform_remote_state.eks.outputs.cluster_name +} + diff --git a/policy/locals.tf b/policy/locals.tf new file mode 100755 index 00000000..709ae8d7 --- /dev/null +++ b/policy/locals.tf @@ -0,0 +1,9 @@ +locals { + project_prefix = data.terraform_remote_state.infra.outputs.project_prefix + build_suffix = data.terraform_remote_state.infra.outputs.build_suffix + aws_region = data.terraform_remote_state.infra.outputs.aws_region + host = data.terraform_remote_state.eks.outputs.cluster_endpoint + cluster_ca_certificate = data.terraform_remote_state.eks.outputs.kubeconfig-certificate-authority-data + cluster_name = data.terraform_remote_state.eks.outputs.cluster_name + app = format("%s-nap-%s", local.project_prefix, local.build_suffix) +} \ No newline at end of file diff --git a/policy/main.tf b/policy/main.tf new file mode 100755 index 00000000..38df203a --- /dev/null +++ b/policy/main.tf @@ -0,0 +1,3 @@ +provider "aws" { + region = local.aws_region +} diff --git a/policy/policy.json b/policy/policy.json new file mode 100755 index 00000000..b0767da5 --- /dev/null +++ b/policy/policy.json @@ -0,0 +1,8 @@ +{ + "policy": { + "name": "policy_name", + "template": { "name": "POLICY_TEMPLATE_NGINX_BASE" }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking" + } +} diff --git a/policy/versions.tf b/policy/versions.tf new file mode 100755 index 00000000..760e7d43 --- /dev/null +++ b/policy/versions.tf @@ -0,0 +1,18 @@ +terraform { + required_version = ">= 1.6.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.0.0" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = ">= 2.23.0" + } + kubectl = { + source = "gavinbunney/kubectl" + version = ">= 1.15.0" + } + } +} diff --git a/s3/bootstrap.tf b/s3/bootstrap.tf new file mode 100644 index 00000000..58fa9e40 --- /dev/null +++ b/s3/bootstrap.tf @@ -0,0 +1,73 @@ +data "external" "s3_bucket_check" { + program = ["bash", "-c", </dev/null 2>&1; then + printf '{"exists":"true"}' + else + printf '{"exists":"false"}' + fi + EOT + ] +} + +data "external" "dynamodb_table_check" { + program = ["bash", "-c", </dev/null 2>&1; then + printf '{"exists":"true"}' + else + printf '{"exists":"false"}' + fi + EOT + ] +} + +# Create S3 bucket only if missing +resource "aws_s3_bucket" "terraform_state_bucket" { + count = data.external.s3_bucket_check.result.exists == "true" ? 0 : 1 + + bucket = var.tf_state_bucket + force_destroy = true + + tags = { + Name = "Terraform State Bucket" + } +} + +# Configure bucket versioning +resource "aws_s3_bucket_versioning" "state_bucket" { + count = data.external.s3_bucket_check.result.exists == "true" ? 0 : 1 + + bucket = aws_s3_bucket.terraform_state_bucket[0].id + versioning_configuration { + status = "Enabled" + } +} + +# Configure bucket encryption +resource "aws_s3_bucket_server_side_encryption_configuration" "state_bucket" { + count = data.external.s3_bucket_check.result.exists == "true" ? 0 : 1 + + bucket = aws_s3_bucket.terraform_state_bucket[0].id + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } +} + +# Create DynamoDB table only if missing +resource "aws_dynamodb_table" "terraform_state_lock" { + count = data.external.dynamodb_table_check.result.exists == "true" ? 0 : 1 + + name = "terraform-lock-table" + billing_mode = "PAY_PER_REQUEST" + hash_key = "LockID" + + attribute { + name = "LockID" + type = "S" + } + + tags = { + Name = "Terraform State Lock Table" + } +} \ No newline at end of file diff --git a/s3/iam.tf b/s3/iam.tf new file mode 100644 index 00000000..017bedef --- /dev/null +++ b/s3/iam.tf @@ -0,0 +1,54 @@ +data "aws_caller_identity" "current" {} + +# Create IAM role if it doesn't exist +resource "aws_iam_role" "terraform_execution_role" { + count = var.create_iam_resources ? 1 : 0 + + name = "TerraformCIExecutionRole" + description = "Role for basic Terraform CI/CD executions" + max_session_duration = 3600 + + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [{ + Effect = "Allow", + Principal = { + AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" + }, + Action = "sts:AssumeRole" + }] + }) +} + +# Create IAM policy if it doesn't exist +resource "aws_iam_policy" "terraform_state_access" { + count = var.create_iam_resources ? 1 : 0 + + name = "TerraformStateAccess" + description = "Minimum permissions for S3 state management" + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [{ + Effect = "Allow", + Action = [ + "s3:GetObject", + "s3:PutObject", + "s3:DeleteObject", + "s3:ListBucket" + ], + Resource = [ + "arn:aws:s3:::${var.tf_state_bucket}", + "arn:aws:s3:::${var.tf_state_bucket}/*" + ] + }] + }) +} + +# Attach the policy to the IAM role +resource "aws_iam_role_policy_attachment" "state_access" { + count = var.create_iam_resources ? 1 : 0 + + role = aws_iam_role.terraform_execution_role[0].name + policy_arn = aws_iam_policy.terraform_state_access[0].arn +} \ No newline at end of file diff --git a/s3/outputs.tf b/s3/outputs.tf new file mode 100644 index 00000000..0bf83ec2 --- /dev/null +++ b/s3/outputs.tf @@ -0,0 +1,51 @@ +# S3 Bucket Details +output "s3_bucket_created" { + description = "Whether the S3 bucket was created." + value = length(aws_s3_bucket.terraform_state_bucket) > 0 ? true : false +} + +output "s3_bucket_name" { + description = "The name of the S3 bucket." + value = length(aws_s3_bucket.terraform_state_bucket) > 0 ? aws_s3_bucket.terraform_state_bucket[0].bucket : null +} + +# DynamoDB Table Details +output "dynamodb_table_created" { + description = "Whether the DynamoDB table was created." + value = length(aws_dynamodb_table.terraform_state_lock) > 0 ? true : false +} + +output "dynamodb_table_name" { + description = "The name of the DynamoDB table." + value = length(aws_dynamodb_table.terraform_state_lock) > 0 ? aws_dynamodb_table.terraform_state_lock[0].name : null +} + +# Output the ARN of the created IAM role (if created) +output "terraform_execution_role_arn" { + description = "The ARN of the IAM role created for Terraform CI/CD executions." + value = var.create_iam_resources ? aws_iam_role.terraform_execution_role[0].arn : null +} + +# Output the ARN of the created IAM policy (if created) +output "terraform_state_access_policy_arn" { + description = "The ARN of the IAM policy created for Terraform state access." + value = var.create_iam_resources ? aws_iam_policy.terraform_state_access[0].arn : null +} + +# Output the name of the created IAM role (if created) +output "terraform_execution_role_name" { + description = "The name of the IAM role created for Terraform CI/CD executions." + value = var.create_iam_resources ? aws_iam_role.terraform_execution_role[0].name : null +} + +# Output the name of the created IAM policy (if created) +output "terraform_state_access_policy_name" { + description = "The name of the IAM policy created for Terraform state access." + value = var.create_iam_resources ? aws_iam_policy.terraform_state_access[0].name : null +} + +# Output the ID of the AWS account (for reference) +output "aws_account_id" { + description = "The ID of the AWS account where the resources are being created." + value = data.aws_caller_identity.current.account_id +} \ No newline at end of file diff --git a/s3/provider.tf b/s3/provider.tf new file mode 100644 index 00000000..0996a6ed --- /dev/null +++ b/s3/provider.tf @@ -0,0 +1,5 @@ +# AWS Provider Configuration +provider "aws" { + region = var.aws_region +} + diff --git a/s3/variables.tf b/s3/variables.tf new file mode 100644 index 00000000..9659d412 --- /dev/null +++ b/s3/variables.tf @@ -0,0 +1,16 @@ +variable "tf_state_bucket" { + type = string + description = "S3 bucket for Terraform state" + default = "akash-terraform-state-bucket" +} +variable "create_iam_resources" { + description = "Whether to create IAM resources (role and policy)." + type = bool + default = true +} + +variable "aws_region" { + description = "aws region" + type = string + default = "us-east-1" +} diff --git a/s3/versions.tf b/s3/versions.tf new file mode 100644 index 00000000..fe90661a --- /dev/null +++ b/s3/versions.tf @@ -0,0 +1,15 @@ +terraform { + required_version = ">= 1.0.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + random = { + source = "hashicorp/random" + version = ">= 3.5" + } + + } + } + \ No newline at end of file