If you’ve ever managed cloud services, you know how tricky it can get to keep everything running smoothly — especially when services need to be started or stopped outside normal business hours. At my company, our AWS ECS services are scheduled to run only during office hours to save costs.
But here’s the thing: developers and QA folks often need these services late at night or on weekends for urgent deliveries. And that means I get constant pings like:
“Hey, can you start the QA servers now? I need them running ASAP.”

Honestly, it was frustrating. Changing the scheduler multiple times, responding to those messages, and manually controlling the services became a real pain. I knew there had to be a better way.
💡The Idea
What if I could let authorized people start or stop ECS services themselves — anytime, anywhere — just by sending a WhatsApp message? Everyone uses WhatsApp, it’s simple, and it’s fast.
Everyone uses WhatsApp, it’s simple, fast, and always available. So I built a lightweight WhatsApp bot that lets a few authorized users control our AWS ECS clusters using simple text commands.
No more waiting on me, no manual schedule overrides — just chat and control.

🤔 How It Works?
Here’s what the user experience looks like:
- You send
Hito the bot’s WhatsApp number. - The bot replies with a menu.
- You reply with a desired choice.
- The bot checks if your WhatsApp number is authorized.
- If you’re allowed, it talks to AWS ECS and updates the services accordingly.
- You get a confirmation message listing the services started/stopped.
And that’s it — no apps, no AWS Console login, just WhatsApp.
👀Behind the Scenes (Technical Overview)
- Frontend: Twilio’s WhatsApp sandbox to receive/send messages.
- Backend: An AWS Lambda function triggered via a public Lambda Function URL
- Logic: The Lambda function uses
boto3to interact with ECS clusters and services. - Security: Only authorized numbers can issue commands. Logs are stored in CloudWatch for audit and debugging.
How to Set It Up — Step by Step 👇
Here’s the full setup so you can build this too:
1. Create a Twilio Account and Setup WhatsApp Sandbox
- Go to Twilio and sign up for a free account if you don’t have one.

- Navigate to Messaging > Try it out > WhatsApp Sandbox in the Twilio Console.

- Follow the instructions to join the sandbox on your WhatsApp. Usually, you’ll need to scan the bar code and send a code message from your WhatsApp number to the sandbox number.


- Make a note of your WhatsApp Sandbox number — this is the number you’ll use to message your bot during testing. Also, copy the Webhook URL field (we’ll come back and update it later once your Lambda function is ready).
2. Create an AWS Lambda Function
- Go to AWS Console → Lambda → Create Function
- Name it something like
ControlECSService. - Runtime: Python 3.13
- Architecture: x86_64
- Permissions: Create a new role with basic Lambda permissions.
- After creation, we’ll attach an inline policy for ECS access.
3. Set Up IAM Role and Permissions
Your Lambda function needs permissions to access ECS cluster and services:
- Go to IAM service in AWS Console.
- Create a new policy LambdaECSControlPolicy with the following ECS permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:ListClusters",
"ecs:ListServices"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ecs:DescribeServices"
],
"Resource": [
"arn:aws:ecs:<AWS_REGION>:<AWS_ACCOUNT_ID>:cluster/development-cluster",
"arn:aws:ecs:<AWS_REGION>:<AWS_ACCOUNT_ID>:cluster/qa-cluster"
]
},
{
"Effect": "Allow",
"Action": [
"ecs:UpdateService"
],
"Resource": [
"arn:aws:ecs:<AWS_REGION>:<AWS_ACCOUNT_ID>:service/development-cluster/*",
"arn:aws:ecs:<AWS_REGION>:<AWS_ACCOUNT_ID>:service/qa-cluster/*"
]
}
]
}
- Attach this policy to the IAM role used by your Lambda function.
4. Add the Lambda Code
- Navigate to your Lambda function, replace the default code with the below code.
import json
import boto3
import base64
import logging
from datetime import datetime
from urllib.parse import parse_qs
# Initialize logger
logger = logging.getLogger()
logger.setLevel(logging.INFO)
# Map command environments to ECS cluster names
CLUSTER_MAP = {
"DEV": "development-cluster",
"QA": "qa-cluster"
}
# Map numbers to commands
OPTIONS_MAP = {
"1": "DEV-START",
"2": "DEV-STOP",
"3": "QA-START",
"4": "QA-STOP"
}
# Authorized WhatsApp numbers
AUTHORIZED_USERS = [
"whatsapp:+91xxxxxxxxxx" # Authorized numbers who joined twilio sandbox in step 1
]
ecs_client = boto3.client('ecs')
def lambda_handler(event, context):
try:
# Decode base64 body if needed
if event.get("isBase64Encoded"):
body_bytes = base64.b64decode(event["body"])
decoded_body = body_bytes.decode("utf-8")
else:
decoded_body = event["body"]
# Parse form-urlencoded body
form_data = parse_qs(decoded_body)
user_message = form_data.get("Body", [""])[0].strip().lower()
from_number = form_data.get("From", [""])[0]
timestamp = datetime.utcnow().isoformat()
# Log incoming request
logger.info(f"[{timestamp}] Message received from {from_number}: {user_message}")
# Authorization check
if from_number not in AUTHORIZED_USERS:
logger.warning(f"[{timestamp}] Unauthorized access attempt from {from_number}")
return twilio_response("⛔ You are not authorized to perform this action. Please contact the admin.")
# Welcome message
if user_message == "hi":
return twilio_response(
"👋 Hey there!\n\nI'm your friendly AWS helper bot.\nPlease choose an option by replying with a number:\n\n"
"1️⃣ Start DEV services\n"
"2️⃣ Stop DEV services\n"
"3️⃣ Start QA services\n"
"4️⃣ Stop QA services\n\n"
"Just reply with 1, 2, 3, or 4 to proceed."
)
# Map user input to command
command = OPTIONS_MAP.get(user_message)
if not command:
logger.info(f"[{timestamp}] Invalid input from {from_number}: {user_message}")
return twilio_response(
"⚠️ Oops! I didn't understand that.\n"
"Please type *Hi* to see the list of valid options."
)
# Process command
env, action = command.split("-")
cluster_name = CLUSTER_MAP.get(env)
desired_count = 1 if action == "START" else 0
# List services in the cluster
paginator = ecs_client.get_paginator('list_services')
service_arns = []
for page in paginator.paginate(cluster=cluster_name):
service_arns.extend(page["serviceArns"])
if not service_arns:
logger.warning(f"[{timestamp}] No services found in {env} cluster for {from_number}")
return twilio_response(f"⚠️ No services found in the *{env}* cluster to {action.lower()}.")
# Start or stop services
results = []
for service_arn in service_arns:
service_name = service_arn.split("/")[-1]
ecs_client.update_service(
cluster=cluster_name,
service=service_name,
desiredCount=desired_count
)
results.append(service_name)
logger.info(f"[{timestamp}] {action} action executed in {env} by {from_number}: {results}")
action_word = "✅ Started" if desired_count == 1 else "🛑 Stopped"
message = (
f"{action_word} the following services in *{env}* environment:\n" +
"\n".join(f"• {name}" for name in results) +
"\n\n👍 If you want to do something else, just type *Hi*."
)
if desired_count == 1:
message += "\n⏳ Please wait a few minutes for changes to take effect."
message += "\n📞 If you encounter any issues, please contact the administrator."
return twilio_response(message)
except Exception as e:
error_time = datetime.utcnow().isoformat()
logger.error(f"[{error_time}] Exception occurred for {from_number}: {str(e)}", exc_info=True)
return twilio_response(f"❌ Something went wrong:\n{str(e)}\nPlease try again or type *Hi* for help.")
def twilio_response(message):
return {
"statusCode": 200,
"headers": {
"Content-Type": "application/xml"
},
"body": f"<Response><Message>{message}</Message></Response>"
}
- Update the
AUTHORIZED_USERSlist with the WhatsApp numbers allowed to control your services (numbers must be inwhatsapp:+<countrycode><number>format, e.g.whatsapp:+1234567890).
5. Enable Lambda Function URL (Public Endpoint)
- Go to Lambda → Configuration → Function URL → Create function URL
- Auth type: None (so Twilio can invoke it publicly).

- Save and copy the generated function URL — it’ll be your webhook.
6. Configure Twilio Webhook to Point to Lambda URL
- Go back to Twilio Console → WhatsApp Sandbox settings.
- Paste your Lambda URL in the field “When a message comes in”.
- Set method: POST

- Save the webhook URL.
7. Test Your Bot 🤖
- Open WhatsApp on your authorized number.
- Send a message
Hito your Twilio WhatsApp sandbox number. - You should receive a menu with options to start or stop DEV or QA ECS services.
- Reply with
1,2,3, or4to test starting/stopping services. - Check your AWS ECS Console to see if services have updated accordingly.

8. Monitor Logs and Debug
- Go to CloudWatch Logs in AWS Console.
- Find the log group for your Lambda function.
- Check logs for incoming requests, errors, and actions taken.
- This helps in debugging and ensuring everything works smoothly.
😊 Why This Helps
- No more late-night/weekend calls to start or stop servers.
- Developers and QA get control on-demand, reducing delays.
- No need to edit schedules manually again and again.
- Keeps cloud resources optimized, running only when needed.
💰 Estimated Monthly Cost
Here’s a rough idea of what this setup might cost if you use it 50–100 times a month:
- Twilio WhatsApp Sandbox: Free for development and testing.
- AWS Lambda: Around $0.20 to $0.40 depending on usage.
- Lambda Function URL: Free.
- CloudWatch Logs: About $0.50 to $1.00 depending on how much logging you do.
- ECS API calls: Usually a few cents, roughly $0.10 to $0.25.
Total estimated cost: Around $1 to $1.5/month.
Note: Prices may vary a bit depending on your AWS region and usage patterns.
Future Improvements & Ideas 🚀
Here are some optional enhancements you can build next:
- ✅ Add a confirmation step before stopping services (with timeout)
- ⏱ Schedule reminders if no reply is received after a prompt
- 🔒 Add passphrase like
secret+1instead of plain numbers - 📊 Add status command to show running/stopped services
- 📁 Add DynamoDB to track pending or delayed actions
Conclusion
Getting a “Can you start the QA server?” message at 1 AM is a guaranteed way to ruin your night’s sleep. That’s why I built this simple WhatsApp + AWS bot: so I don’t have to be the middleman anymore, and the devs and QA folks get exactly what they need — quick, secure control of ECS services right from their favorite chat app.
Whether you’re a small team trying to save cloud costs or just someone who values uninterrupted sleep (same here!), this little automation can go a long way in making DevOps more humane.
I hope you found this content informative and enjoyable. For more insightful blogs and updates, consider following and clicking the 👏 button below to show your support. Happy coding! 🚀
Thank you for reading! 💚
