Autonomous Instagram Automation with AWS Strands Agents, Nova Canvas & Bedrock
This article walks through a production-grade, fully autonomous Instagram posting system built on AWS Strands Agents SDK 1.0. The system uses a multi-agent architecture where four specialized AI agents Image, Analytics, Publisher, and Slack collaborate under a master Orchestrator agent to automatically generate 4K images, craft compelling captions, post to Instagram, and notify your team on Slack, every week, without any human intervention.
Introduction
Social media consistency is one of the hardest challenges for any brand or creator. Missing a post on Tuesday because you were in a meeting, or forgetting Thursday's drop because the team was heads-down on a release these small failures compound over time into lost reach, lower engagement, and diminishing returns.
This article walks through a production-grade, fully autonomous Instagram posting system built on AWS Strands Agents SDK 1.0. The system uses a multi-agent architecture where four specialized AI agents Image, Analytics, Publisher, and Slack collaborate under a master Orchestrator agent to automatically generate 4K images, craft compelling captions, post to Instagram, and notify your team on Slack, every Tuesday and Thursday, without any human intervention.
AWS Strands Agents, open-sourced in May 2025 and reaching 1.0 in May 2026, takes a model-driven approach: you define a prompt and a list of tools, and the LLM decides the execution path. There are no graphs to define, no node/edge configurations to maintain, and no router logic to debug. This makes it ideal for autonomous content workflows.
System Architecture
The system is composed of four specialized Strands agents orchestrated by a master agent, triggered automatically by Amazon EventBridge on a cron schedule:
| Agent | Model | Responsibility |
|---|---|---|
| Orchestrator | Claude 3.5 Sonnet | Master coordinator runs the full workflow end to end |
| Image Agent | Nova Pro + Nova Canvas | Analyzes raw images, generates enhanced 4K versions |
| Analytics Agent | Claude 3.5 Sonnet | Scans 30-day post history, finds best time and topics |
| Publisher Agent | (inline tools) | Uploads media to Instagram, publishes, logs to DynamoDB |
| Slack Agent | Claude 3.5 Sonnet | Sends rich Block Kit notifications on success or failure |
Scheduling with EventBridge
The system is triggered by Amazon EventBridge Scheduler on a cron expression that fires every Tuesday and Thursday at 14:00 UTC. The schedule invokes a Lambda function which instantiates the Orchestrator agent and passes it the current day context.
| # EventBridge cron every Tuesday and Thursday at 14:00 UTC aws scheduler create-schedule \ --name instagram-auto-post \ --schedule-expression "cron(0 14 ? * TUE,THU *)" \ --flexible-time-window '{"Mode": "OFF"}' \ --target '{ "Arn": "arn:aws:lambda:us-east-1:ACCOUNT:function:instagram-agent", "RoleArn": "arn:aws:iam::ACCOUNT:role/scheduler-role" }' |
|---|
Content Calendar in DynamoDB
Posts are queued in a DynamoDB table called instagram_posts. Each item stores the topic, a reference to the raw image in S3, and status tracking. The Orchestrator agent queries the table at runtime and picks up the next pending post.
| # DynamoDB item schema { "post_id": "uuid-001", "status": "pending", # pending | published | failed "topic": "AWS Cost Tips for Developers", "media_s3_key": "raw/aws-tips.jpg", "scheduled_day":"TUESDAY", "caption": null, "ig_media_id": null, "published_at": null, "reach_7d": 0, "likes_7d": 0 } |
|---|
Image Agent: From Raw S3 to 4K
The Image Agent is the most technically complex component. It takes a raw image from S3, analyzes it using Amazon Nova Pro's vision capabilities, regenerates an enhanced version using Nova Canvas, then upscales it to 4K resolution using PIL's Lanczos algorithm before saving back to S3.
Step 1 Vision Analysis with Nova Pro
Nova Pro inspects the raw image and generates a rich, detailed description optimized for use as a Nova Canvas generation prompt. This includes subject matter, lighting conditions, color palette, mood, composition style, and background elements.
| # Nova Pro vision analysis response = bedrock.invoke_model( modelId="amazon.nova-pro-v1:0", body=json.dumps({ "messages": [{ "role": "user", "content": [ {"image": {"format": "jpeg", "source": {"bytes": b64}}}, {"text": "Describe this image in rich detail for AI image generation..."} ] }], "inferenceConfig": {"max_new_tokens": 500} }) ) |
|---|
Step 2 Generation with Nova Canvas
Nova Canvas generates a premium-quality image at its maximum supported resolution. Key Nova Canvas constraints to be aware of: each side must be between 320 and 4096 pixels, divisible by 16; the aspect ratio must be between 1:4 and 4:1; and the total pixel count must be under 4,194,304 (approximately 4.19 megapixels). This means the effective maximum native output is 2048x2048 pixels.
| Nova Canvas Resolution Limits Maximum native output: 2048x2048 (4.19MP). Each side: 320-4096px, divisible by 16. Aspect ratio: 1:4 to 4:1. Total pixels: < 4,194,304. The system upscales to 3840x2160 (4K UHD) post-generation using PIL Lanczos with an UnsharpMask sharpening pass. |
|---|
| # Nova Canvas image generation at max resolution response = bedrock.invoke_model( modelId="amazon.nova-canvas-v1:0", body=json.dumps({ "taskType": "TEXT_IMAGE", "textToImageParams": { "text": enhanced_prompt, "negativeText": "blurry, low quality, watermark, distorted" }, "imageGenerationConfig": { "numberOfImages": 1, "width": 2048, "height": 2048, "quality": "premium", "cfgScale": 8.0, "seed": 42 } }) ) |
|---|
Step 3 4K Upscaling with PIL Lanczos
After Nova Canvas generates the 2048x2048 image, PIL resizes it to 3840x2160 (4K UHD) using the Lanczos resampling filter the highest quality option available. An UnsharpMask filter is applied post-upscale to restore fine detail clarity that can soften slightly during enlargement.
| # Upscale to 4K and save to S3 img = Image.open(io.BytesIO(base64.b64decode(base64_image))) img_4k = img.resize((3840, 2160), Image.LANCZOS) img_4k = img_4k.filter( ImageFilter.UnsharpMask(radius=1.5, percent=120, threshold=3) ) output_key = f"generated/4k/{post_id}.png" s3.put_object(Bucket=BUCKET, Key=output_key, Body=buffer) |
|---|
Analytics Agent: Data-Driven Decisions
Rather than posting at a fixed time or with generic captions, the Analytics Agent scans the last 30 days of published posts in DynamoDB and extracts actionable intelligence: the posting hour that historically produces the highest average reach, the top three performing content topics, and the overall average reach per post.
This data is fed back to the Orchestrator agent, which uses it to inform caption generation writing content that gravitates toward proven high-performing topics and to guide timing recommendations for future scheduling improvements.
| # Analytics summary returned to Orchestrator { "best_hour": 14, "top_topics": ["AWS Cost Tips", "AI Tools", "Lambda Best Practices"], "avg_reach": 4200 } |
|---|
The Orchestrator Agent
The Orchestrator is the master Strands agent. It coordinates the entire workflow using the Agent-as-Tool pattern introduced in Strands 1.0 each specialized agent is wrapped as a callable tool, making the system composable and independently testable.
The Orchestrator's system prompt defines the workflow as a numbered sequence of steps. Strands' model-driven architecture means Claude reasons through tool selection autonomously if the Image Agent fails, it can route to the failure notification path without explicit branching logic in code.
| orchestrator = Agent( model=BedrockModel(model_id="anthropic.claude-3-5-sonnet-20241022-v2:0"), system_prompt=""" Every Tuesday and Thursday, run this workflow: 1. fetch_pending_post → get topic and raw S3 image key 2. run_analytics_agent → get best hour, avg reach, top topics 3. run_image_agent → enhance raw image to 4K 4. Generate caption → 150-200 words, 10-15 hashtags 5. publish_to_instagram → upload and publish 6. run_slack_agent → notify team (success or failure) """, tools=[ fetch_pending_post, run_analytics_agent, run_image_agent, publish_to_instagram, run_slack_agent, ] ) |
|---|
Publishing to Instagram
Instagram publishing uses the Meta Graph API v19.0 in a two-step process: first create a media container by providing a presigned S3 URL and caption, then publish the container using its creation ID. The presigned URL approach keeps S3 private while allowing Meta's servers to fetch the image temporarily.
All credentials the long-lived Instagram access token, the IG User ID, and the S3 bucket name are stored in AWS Secrets Manager and retrieved at runtime. They are never hardcoded or passed through environment variables.
| # Step 1: Create media container r1 = requests.post( f"https://graph.facebook.com/v19.0/{ig_user_id}/media", params={ "image_url": presigned_s3_url, "caption": generated_caption, "access_token": token } ) creation_id = r1.json()['id'] # Step 2: Publish r2 = requests.post( f"https://graph.facebook.com/v19.0/{ig_user_id}/media_publish", params={"creation_id": creation_id, "access_token": token} ) ig_media_id = r2.json()['id'] |
|---|
Slack Notification Agent
The Slack Agent closes the loop by sending a rich Block Kit notification to a designated Slack channel after every workflow run. On success, the card includes the post topic, timestamp, IG Media ID, a caption preview, and the full analytics summary. On failure, it sends an urgent alert with the error details and a pointer to CloudWatch logs.
A separate EventBridge rule fires every Sunday to trigger a weekly digest a consolidated summary of all posts from the week with reach and likes metrics for quick performance review.
| Slack Notification Contents (Success) Post topic | Published timestamp | IG Media ID | Post ID | Caption preview (200 chars) | Best posting hour (UTC) | Average reach (30-day) | Top 3 performing topics | 4K image S3 key |
|---|
Security & IAM
The Lambda execution role requires the following minimum permissions. Following the principle of least privilege, all resource ARNs should be scoped to specific resources rather than wildcards in production.
| # Required IAM permissions for the Lambda role { "Action": [ "dynamodb:Query", "dynamodb:Scan", "dynamodb:UpdateItem", "dynamodb:PutItem", "s3:GetObject", "s3:PutObject", "secretsmanager:GetSecretValue", "bedrock:InvokeModel" ], "Resource": [ "arn:aws:bedrock:*::foundation-model/amazon.nova-canvas-v1:0", "arn:aws:bedrock:*::foundation-model/amazon.nova-pro-v1:0", "arn:aws:bedrock:*::foundation-model/anthropic.claude-3-5-sonnet*" ] } |
|---|
Cost Breakdown
The system runs twice a week (104 times per year) with the following approximate cost per run:
| Service | Usage per Run | Approx. Cost |
|---|---|---|
| Nova Canvas | 1 image @ premium quality | ~$0.08 |
| Nova Pro | Vision analysis (~500 tokens) | ~$0.002 |
| Claude 3.5 Sonnet | Caption + orchestration | ~$0.01 |
| Lambda | ~60s execution, 1GB RAM | ~$0.001 |
| S3 | 1 x 4K PNG (~8MB) | ~$0.0002 |
| DynamoDB | ~10 reads/writes | < $0.001 |
| Total per run | ~$0.09–$0.12 | |
| Total per year (104 runs) | ~$10–$13 |
Adding Posts to the Queue
Adding a new post to the automation queue is a single DynamoDB put_item call. The agent picks it up automatically on the next scheduled Tuesday or Thursday run.
| import boto3, uuid from datetime import datetime table = boto3.resource("dynamodb").Table("instagram_posts") table.put_item(Item={ "post_id": str(uuid.uuid4()), "status": "pending", "topic": "5 AWS Lambda Best Practices for 2026", "media_s3_key": "raw/lambda-tips.jpg", "scheduled_day": "THURSDAY", "created_at": datetime.utcnow().isoformat() }) |
|---|
Installation
| pip install strands-agents \ strands-agents-tools[slack] \ boto3 \ requests \ pillow |
|---|
Conclusion
This system demonstrates what becomes possible when you combine AWS Strands Agents' model-driven orchestration with Bedrock's foundation models. The entire multi-agent pipeline image enhancement, analytics, publishing, and team notification runs autonomously with under 200 lines of meaningful application code per agent.
Key architectural takeaways: use DynamoDB as a lightweight content calendar and audit log; use the Agent-as-Tool pattern to keep agents independently testable; store all secrets in Secrets Manager; and let the LLM handle branching logic through its system prompt rather than explicit conditional code.
The total infrastructure cost is approximately $10-13 per year for twice-weekly posting a compelling case for fully agentic content automation on AWS.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article