1+ name : Deploy to AWS (Terraform + Ansible)
2+
3+ on :
4+ workflow_dispatch :
5+ inputs :
6+ resource_prefix :
7+ description : ' Prefix for AWS resources (e.g., niko-, gemini-). Default is branch name or dev-.'
8+ required : false
9+ default : ' '
10+ enable_frontend :
11+ description : ' Deploy Frontend to EC2'
12+ required : true
13+ type : boolean
14+ default : true
15+ enable_backend :
16+ description : ' Deploy Backend to EC2'
17+ required : true
18+ type : boolean
19+ default : true
20+ branch_ref :
21+ description : ' Branch to deploy (default: current branch)'
22+ required : false
23+ default : ' '
24+ push :
25+ branches :
26+ - main # Production deployment
27+ - ' feature/**' # For feature branches, can be dev deployments
28+ - ' dev/**'
29+ pull_request :
30+ branches :
31+ - main
32+
33+ env :
34+ AWS_REGION : us-west-2
35+ TF_VERSION : " 1.5.7" # Match installed version or use latest stable
36+ ANSIBLE_VERSION : " 6.0.0" # Or your preferred version
37+ PYTHON_VERSION : ' 3.9'
38+ # Default enable flags, can be overridden by inputs
39+ ENABLE_FRONTEND : true
40+ ENABLE_BACKEND : true
41+
42+ jobs :
43+ determine_params :
44+ name : Determine Deployment Parameters
45+ runs-on : ubuntu-latest
46+ outputs :
47+ resource_prefix : ${{ steps.params.outputs.resource_prefix }}
48+ tf_action : ${{ steps.params.outputs.tf_action }}
49+ branch_name : ${{ steps.params.outputs.branch_name }}
50+ is_main_branch : ${{ steps.params.outputs.is_main_branch }}
51+ enable_frontend : ${{ steps.params.outputs.enable_frontend }}
52+ enable_backend : ${{ steps.params.outputs.enable_backend }}
53+
54+ steps :
55+ - name : Set Parameters
56+ id : params
57+ run : |
58+ BRANCH_NAME="${{ github.event.inputs.branch_ref || github.ref_name }}"
59+ echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
60+
61+ IS_MAIN_BRANCH=$(echo "$BRANCH_NAME" == "main" || echo "$BRANCH_NAME" == "refs/heads/main")
62+ echo "is_main_branch=$IS_MAIN_BRANCH" >> $GITHUB_OUTPUT
63+
64+ # Determine resource_prefix
65+ if [[ "$IS_MAIN_BRANCH" == "true" ]]; then
66+ RESOURCE_PREFIX="prod-"
67+ elif [[ -n "${{ github.event.inputs.resource_prefix }}" ]]; then
68+ RESOURCE_PREFIX="${{ github.event.inputs.resource_prefix }}-"
69+ elif [[ -n "$BRANCH_NAME" ]]; then
70+ SANITIZED_BRANCH_NAME=$(echo "$BRANCH_NAME" | sed 's/[^a-zA-Z0-9-]/-/g' | sed 's/^-*//;s/-*$//' | tr '[:upper:]' '[:lower:]')
71+ RESOURCE_PREFIX="${SANITIZED_BRANCH_NAME:0:10}-" # Max 10 chars from branch + hyphen
72+ else
73+ RESOURCE_PREFIX="dev-"
74+ fi
75+ echo "resource_prefix=$RESOURCE_PREFIX" >> $GITHUB_OUTPUT
76+
77+ # Determine Terraform action (plan for PRs, apply for push/dispatch)
78+ if [[ "${{ github.event_name }}" == "pull_request" ]]; then
79+ TF_ACTION="plan"
80+ else
81+ TF_ACTION="apply"
82+ fi
83+ echo "tf_action=$TF_ACTION" >> $GITHUB_OUTPUT
84+
85+ # Determine frontend/backend deployment based on inputs or defaults
86+ if [[ "$IS_MAIN_BRANCH" == "true" ]]; then
87+ CURRENT_ENABLE_FRONTEND=true
88+ CURRENT_ENABLE_BACKEND=true
89+ else
90+ # Use workflow input if provided, otherwise use env var default
91+ if [[ -n "${{ github.event.inputs.enable_frontend }}" ]]; then
92+ CURRENT_ENABLE_FRONTEND=${{ github.event.inputs.enable_frontend }}
93+ else
94+ CURRENT_ENABLE_FRONTEND=${{ env.ENABLE_FRONTEND }}
95+ fi
96+ if [[ -n "${{ github.event.inputs.enable_backend }}" ]]; then
97+ CURRENT_ENABLE_BACKEND=${{ github.event.inputs.enable_backend }}
98+ else
99+ CURRENT_ENABLE_BACKEND=${{ env.ENABLE_BACKEND }}
100+ fi
101+ fi
102+ echo "enable_frontend=$CURRENT_ENABLE_FRONTEND" >> $GITHUB_OUTPUT
103+ echo "enable_backend=$CURRENT_ENABLE_BACKEND" >> $GITHUB_OUTPUT
104+
105+ terraform :
106+ name : Terraform Infrastructure
107+ runs-on : ubuntu-latest
108+ needs : determine_params
109+ environment : ${{ needs.determine_params.outputs.is_main_branch == 'true' && 'production' || 'development' }}
110+ outputs :
111+ load_balancer_dns : ${{ steps.apply_terraform.outputs.load_balancer_dns }}
112+ db_endpoint : ${{ steps.apply_terraform.outputs.db_endpoint }}
113+
114+ steps :
115+ - name : Checkout code
116+ uses : actions/checkout@v4
117+ with :
118+ ref : ${{ needs.determine_params.outputs.branch_name }}
119+
120+ - name : Configure AWS credentials
121+ uses : aws-actions/configure-aws-credentials@v4
122+ with :
123+ aws-access-key-id : ${{ secrets.AWS_ACCESS_KEY_ID }}
124+ aws-secret-access-key : ${{ secrets.AWS_SECRET_ACCESS_KEY }}
125+ aws-region : ${{ env.AWS_REGION }}
126+
127+ - name : Setup Terraform
128+ uses : hashicorp/setup-terraform@v3
129+ with :
130+ terraform_version : ${{ env.TF_VERSION }}
131+
132+ - name : Terraform Init
133+ id : init
134+ working-directory : ./terraform
135+ run : terraform init
136+
137+ - name : Terraform Plan
138+ id : plan
139+ working-directory : ./terraform
140+ run : |
141+ terraform plan \
142+ -var="aws_region=${{ env.AWS_REGION }}" \
143+ -var="resource_prefix=${{ needs.determine_params.outputs.resource_prefix }}" \
144+ -var="key_name=${{ secrets.AWS_KEY_PAIR_NAME }}" \
145+ -var="db_password=${{ secrets.DB_PASSWORD }}" \
146+ -var="enable_frontend=${{ needs.determine_params.outputs.enable_frontend }}" \
147+ -var="enable_backend=${{ needs.determine_params.outputs.enable_backend }}" \
148+ -var="common_tags={Project=\"team-1-hr-app\", Environment=\"${{ needs.determine_params.outputs.is_main_branch == 'true' && 'production' || 'development' }}\", ManagedBy=\"terraform\", Branch=\"${{ needs.determine_params.outputs.branch_name }}\"}" \
149+ -out=tfplan
150+
151+ - name : Terraform Apply
152+ id : apply_terraform
153+ if : needs.determine_params.outputs.tf_action == 'apply' && (needs.determine_params.outputs.is_main_branch == 'true' || github.event_name == 'workflow_dispatch')
154+ working-directory : ./terraform
155+ run : |
156+ terraform apply -auto-approve tfplan
157+ echo "load_balancer_dns=$(terraform output -raw ec2_load_balancer_dns)" >> $GITHUB_OUTPUT
158+ echo "db_endpoint=$(terraform output -raw rds_endpoint)" >> $GITHUB_OUTPUT
159+
160+ ansible_deploy :
161+ name : Deploy Application with Ansible
162+ runs-on : ubuntu-latest
163+ needs : [determine_params, terraform]
164+ if : needs.determine_params.outputs.tf_action == 'apply' && (needs.determine_params.outputs.is_main_branch == 'true' || github.event_name == 'workflow_dispatch')
165+ environment : ${{ needs.determine_params.outputs.is_main_branch == 'true' && 'production' || 'development' }}
166+
167+ steps :
168+ - name : Checkout code
169+ uses : actions/checkout@v4
170+ with :
171+ ref : ${{ needs.determine_params.outputs.branch_name }}
172+
173+ - name : Setup Python
174+ uses : actions/setup-python@v5
175+ with :
176+ python-version : ${{ env.PYTHON_VERSION }}
177+
178+ - name : Install Ansible and dependencies
179+ run : |
180+ pip install ansible==${{ env.ANSIBLE_VERSION }}
181+ pip install boto3 botocore # For EC2 dynamic inventory if used, or AWS modules
182+
183+ - name : Configure AWS credentials (for Ansible AWS modules if needed)
184+ uses : aws-actions/configure-aws-credentials@v4
185+ with :
186+ aws-access-key-id : ${{ secrets.AWS_ACCESS_KEY_ID }}
187+ aws-secret-access-key : ${{ secrets.AWS_SECRET_ACCESS_KEY }}
188+ aws-region : ${{ env.AWS_REGION }}
189+
190+ - name : Set up SSH key for Ansible
191+ uses : webfactory/ssh-agent@v0.9.0
192+ with :
193+ ssh-private-key : ${{ secrets.ANSIBLE_SSH_PRIVATE_KEY }} # Store your EC2 key pair's private key
194+
195+ # This step assumes you have an EC2 instance to deploy to.
196+ # The public IP can be an output from Terraform, or use dynamic inventory.
197+ # For simplicity, using Terraform output. This requires instance to be ready.
198+ - name : Create Ansible Inventory
199+ run : |
200+ echo "[ec2_instances]" > inventory.ini
201+ # This needs the public IP of an instance. ALB DNS is for users, not direct SSH.
202+ # Terraform output for individual instance IPs would be needed if not using dynamic inventory.
203+ # For now, this is a placeholder. Ansible usually targets instances in ASG via dynamic inventory or known IPs.
204+ # Let's assume we need to get the IP of an instance from the ASG, which is non-trivial here directly.
205+ # Placeholder: Use ALB DNS for now, though Ansible connects to EC2 IPs.
206+ echo "${{ needs.terraform.outputs.load_balancer_dns }} ansible_user=ec2-user" >> inventory.ini
207+ echo "Warning: Ansible inventory currently uses ALB DNS as a placeholder. Needs actual EC2 IP(s)."
208+
209+ - name : Run Ansible playbook
210+ working-directory : ./ansible # Assuming playbook is in ansible/playbooks
211+ env :
212+ ANSIBLE_HOST_KEY_CHECKING : " False"
213+ run : |
214+ ansible-playbook -i ../inventory.ini playbooks/deploy.yml \
215+ -e "resource_prefix=${{ needs.determine_params.outputs.resource_prefix }}" \
216+ -e "enable_frontend=${{ needs.determine_params.outputs.enable_frontend }}" \
217+ -e "enable_backend=${{ needs.determine_params.outputs.enable_backend }}" \
218+ -e "db_endpoint=${{ needs.terraform.outputs.db_endpoint }}" \
219+ -e "db_password=${{ secrets.DB_PASSWORD }}" \
220+ -e "git_repo_url=https://github.com/${{ github.repository }}.git" \
221+ -e "git_branch=${{ needs.determine_params.outputs.branch_name }}"
222+ # Add other necessary vars for your playbook, like db_user, db_name from secrets/vars
223+
224+ notify_failure :
225+ name : Notify on Failure
226+ runs-on : ubuntu-latest
227+ needs : [terraform, ansible_deploy]
228+ if : failure() && (needs.determine_params.outputs.is_main_branch == 'true' || github.event_name == 'workflow_dispatch')
229+ steps :
230+ - name : Notify deployment failure
231+ run : echo "AWS Deployment for ${{ needs.determine_params.outputs.resource_prefix }} on branch ${{ needs.determine_params.outputs.branch_name }} FAILED!"
0 commit comments