# AWS Strands harness on Azure: five defaults to change first

AWS released Strands harness on 21 September 2026, an open-source Python and TypeScript agent harness that AWS says cuts token cost by 28% and that can run on Azure Container Apps. Out of the box it calls Amazon Bedrock, approves every tool call and keeps memory in a local folder, so Swedish and EU Azure teams need to change five defaults before any company data goes near it.

- Published: 2026-09-22 · Category: AI & Cloud Infrastructure · Tags: Strands Agents, AWS, AI Agents, Agent Harness, Azure OpenAI, Microsoft Foundry, Azure Container Apps, Microsoft Agent Framework, GDPR, NIS2
- Author: Technspire AB, Stockholm (https://technspire.com)
- Canonical: https://technspire.com/en/blog/aws-strands-harness-on-azure-five-defaults-to-change

On 21 September 2026 the AWS Strands Agents team released Strands harness, an Apache 2.0 agent harness for Python and TypeScript that installs with `pip install strands-harness` or `npm install @strands-agents/harness`. AWS says it costs 28% less in tokens than Claude Code, Codex and other harnesses across six benchmarks at comparable accuracy. The launch post lists Azure Container Apps among the places it can run, next to Modal, Cloudflare Containers, Google Cloud Run, Amazon ECS and Amazon Bedrock AgentCore.

So an Azure team can pick it up today. The catch is in the defaults. Out of the box the harness calls a model on Amazon Bedrock, approves every tool call without asking, and writes sessions and long-term memory to a local folder. None of that is wrong for a laptop demo. All of it needs changing before the harness touches company data in a Swedish or EU Azure tenant.

## What AWS actually shipped

Strands harness is a fully assembled agent built on the Strands Agents SDK, which AWS open-sourced in May 2025. You call one factory function and get an agent with tools, context management, sessions, memory and a tuned system prompt already wired together:

```
from strands_harness import create_harness

agent = create_harness(model="anthropic/claude-opus-5")
agent("Research the top three vector databases...")
```

The documented configuration reference shows what you get without passing anything else:

- **Built-in tools:** `shell`, `read`, `write`, `edit`, `web_fetch`, `web_search`, `programmatic_tool_caller` and `subagent`, all on by default.
- **Context handling:** prompt caching on `"auto"`, tool results truncated at roughly 1,500 tokens, summarisation when the context window reaches 85%, and recovery inside the loop if the context still overflows.
- **State:** sessions resumable by ID, long-term memory on by default, skills loaded from `./.agent/skills`, and a checklist plugin for multi-step work.
- **Extensibility:** MCP server connections, your own tools and plugins, and a CLI that prototypes an agent in plain English and exports it to Python or TypeScript with `/export`.

Because the factory returns a standard Strands `Agent`, tracing uses the SDK's native OpenTelemetry integration. Set `OTEL_EXPORTER_OTLP_ENDPOINT` and the traces can land in the same collector that feeds Application Insights for the rest of your Azure estate.

## Reading the 28% claim

The cost figure comes from AWS's own benchmarking, run distributed on EC2 with the Harbor evaluation framework. The launch post gives two headline numbers: 28% lower token cost than comparable harnesses across six benchmarks, and 77% lower cost than Claude Code on Terminal Bench 2.1 when both ran Claude Fable 5, with Strands harness scoring higher. AWS also notes that the DeepSeek harness was the most token-efficient overall but had the lowest accuracy. The post does not publish a per-benchmark table for all six.

Treat that as a vendor benchmark on vendor infrastructure. The mechanism behind it is plausible, though, and mostly visible in the defaults above. Truncating tool output to about 1,500 tokens stops a single noisy `grep` or log dump from sitting in context for every following turn. Aggressive caching makes the repeated prefix cheap. Neither trick is proprietary, which matters for the decision later in this article.

For budgeting, turn the claim into your own numbers rather than borrowing AWS's:

```
Monthly saving estimate (illustrative inputs, replace with yours)
  current agent token spend            S   e.g. 40,000 SEK/month
  vendor-claimed reduction             r   0.28 (AWS benchmark, not yours)
  your measured reduction              m   run the same 20 tasks on both harnesses
  gross saving                         S x m
  one-off migration effort             E   engineer-days x day rate
  payback months                       E / (S x m)

  With S = 40,000 and m = 0.15 (half the claim):
    gross saving   = 6,000 SEK/month
    E = 8 days x 9,000 SEK = 72,000 SEK
    payback        = 12 months
```

The inputs are placeholders, not a customer result. The point of the arithmetic is that a double-digit percentage on a small monthly spend rarely pays for a harness migration on its own. It becomes interesting when agent spend is already large, or when you are choosing a harness for the first time and the switching cost is close to zero.

## Default 1: the model points at Amazon Bedrock

The configuration reference lists the default model as `bedrock/global.anthropic.claude-opus-4-8`, and the model documentation says a string without a provider prefix "is treated as a bare Amazon Bedrock id". The `global.` prefix is a cross-region inference profile. If a developer runs `create_harness()` with no model argument on a machine that happens to hold AWS credentials, prompts go to Bedrock with global routing, which is not where an EU data-residency decision usually ends up.

The harness has provider aliases for `bedrock`, `bedrock-mantle`, `anthropic`, `openai`, `google`, `ollama` and `litellm`. There is no `azure` alias. The documented escape hatch is to pass a `Model` instance, which the harness "uses as-is". Azure OpenAI's v1 API accepts the standard OpenAI client with a `base_url` ending in `/openai/v1/`, and Microsoft documents passing an Entra ID token provider as the `api_key`. The Strands OpenAI provider forwards `client_args` to that client:

```
# pip install strands-harness 'strands-agents[openai]' azure-identity
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
from strands.models.openai import OpenAIModel
from strands_harness import create_harness

token_provider = get_bearer_token_provider(
    DefaultAzureCredential(), "https://ai.azure.com/.default"
)

model = OpenAIModel(
    client_args={
        "base_url": "https://<your-resource>.openai.azure.com/openai/v1/",
        "api_key": token_provider,          # Entra ID, no static key
    },
    model_id="<your-deployment-name>",    # deployment name, not model name
)

agent = create_harness(model=model)
```

Two alternatives exist. The LiteLLM provider supports Azure through LiteLLM's `azure/` model prefix, which suits teams that already run a LiteLLM gateway. For Claude deployed in Microsoft Foundry, Microsoft's examples use Anthropic's `AnthropicFoundry` client against `https://.services.ai.azure.com/anthropic`; the Strands Anthropic provider documentation does not cover that endpoint, so test it before you promise it to anyone. Whichever route you pick, make the model argument mandatory in your wrapper code so nobody inherits the Bedrock default by accident.

## Default 2: every tool call runs without asking

The interventions documentation is direct: "By default Strands harness applies none, so every call proceeds." Combined with shell, file writes and web access all enabled, the default harness is an autonomous agent with no approval gate.

Three options are built in. `interventions="ask"` gates every tool call for human approval. `interventions="smart"` runs a risk classifier and gates only calls it flags. Any other string is treated as a natural-language risk policy for that classifier, and a string ending in `.cedar` loads a Cedar policy file for deterministic authorisation. For anything that touches production systems, prefer Cedar: a written policy can be reviewed and versioned, and it gives the same answer every time. A classifier is a useful second layer, and we have argued before that [a classifier is not a sandbox](/en/blog/coding-agent-sandboxing-auto-mode-break).

## Default 3: sessions and memory live in ./.agent

Sessions default to `./.agent/sessions` and long-term memory to `./.agent/memory`. AWS's own production guide flags this as a problem in containers with ephemeral storage. On Azure Container Apps, a restarted replica loses both unless you mount durable storage or supply a different backend.

The Strands storage API ships local-file, in-memory and S3 implementations, plus integrations such as AgentCore Memory and a Valkey session manager. There is no Azure Blob Storage backend. Your options are an Azure Files mount pointed at by `LocalFileStorage("/mnt/agent/sessions")` and `memory={"dir": "/mnt/agent/memory"}`, or a custom store class written against the storage interface. The first is quick. The second is the one to build if several replicas must share state.

Memory deserves a separate look. The harness distils durable facts from conversations into markdown files, running extraction in the background "every few turns using a small model". The documentation does not say which model that is when you override the main one. Confirm it follows your Azure model before real conversations flow through, because those markdown files are exactly where names, email addresses and customer details will accumulate. If you cannot answer that question yet, set `memory=False`.

## Default 4: web search follows the model provider

When the model provider has native search, `web_search` is a switch that turns it on. When it does not, the harness logs a warning and leaves search out. The documented fallback is `{"web_search": "exa"}`, which sends queries to Exa's hosted service, keyless on the free tier. The documentation says it plainly: "Exa is a third party; queries are sent externally and handled under Exa's terms."

Search queries written by an agent that is reading your internal documents can contain fragments of those documents. Decide whether search is needed per agent, and pass an explicit list such as `builtin_tools=["read", "edit", "shell"]` rather than relying on the default set. `web_fetch` has the same egress question in the other direction.

## Default 5: the sandbox runs on the host

The shell tool is routed through the SDK's sandbox layer rather than calling the host shell directly, but the shell documentation states that "by default, the sandbox runs locally on the host machine". Docker and SSH sandboxes are supported without changing the tools. The companion Strands Shell project describes itself as "a mediation layer, not a hardened sandbox": it runs in the same process as your code, with a virtual filesystem, an SSRF guard that blocks private ranges and the instance metadata endpoint, and per-URL secret injection. Its security model recommends pairing it with containers or microVMs for untrusted workloads.

`programmatic_tool_caller` runs model-written Python in Monty, an isolated interpreter in a separate worker with memory and time limits. The documentation adds the caveat that matters: "Monty bounds the code, not the tools it calls: `shell` still runs commands on the host."

On Azure, the practical answer is a dedicated Container Apps environment with its own managed identity holding only the roles the agent needs, no network path to internal subnets it has no business reaching, and the Docker sandbox or equivalent inside it. The metadata endpoint guard is useful precisely because a managed identity token is one HTTP request away from any process in that container.

## Strands harness or Microsoft Agent Framework Harness?

Most Azure teams evaluating Strands will already have looked at Microsoft's equivalent, which reached general availability in August. We covered that release in [Agent Framework Harness GA: build or buy your agent loop](/en/blog/agent-framework-harness-vs-rolling-your-own-agent-loop). The two overlap heavily, and the differences are about ecosystem rather than capability.

| Question | Strands harness | Microsoft Agent Framework Harness |
| --- | --- | --- |
| Languages | Python, TypeScript | Python, .NET |
| Azure models | Custom model object or LiteLLM; the default is Bedrock | Foundry and Azure OpenAI are first-party targets |
| Tool approval | Off by default; opt in to ask, smart or Cedar | Approval middleware with "don't ask again" rules |
| Managed hosting on Azure | Your own container, for example Container Apps | Foundry Hosted Agents with an Entra agent identity |
| Code execution | Monty interpreter; Docker or SSH sandbox for the shell | CodeAct in a Hyperlight micro-VM |
| State backends | Local file, S3 or custom; no Azure Blob backend | File-based memory, managed state on Hosted Agents |
| Observability | OpenTelemetry, bring your own collector | OpenTelemetry into Application Insights by default |

Pick Strands when your agent code is TypeScript, when you run workloads on both AWS and Azure and want one harness, or when you want to benchmark its context defaults against what you already have. Pick Agent Framework when the team writes .NET, when Entra agent identities and Foundry Hosted Agents are part of your governance story, or when you would rather not write a storage backend. If the 28% is the only draw, copy the idea instead: tool-output truncation and summarisation thresholds are configurable in any serious harness, and measuring them on your own tasks costs far less than a migration.

## The Swedish and EU angle

**Residency is decided by the model object, not the container region.** Running the harness in Sweden Central does nothing for residency if the model string points at a global Bedrock profile. Point it at an Azure OpenAI deployment of the Data Zone EU type when data must stay in the EU. For Claude in Foundry, Microsoft's documentation lists Global Standard for all Claude models and Data Zone Standard only in the US, so an EU-bound Claude workload does not have an EU-only deployment option today. We went through the [cost side of Data Zone EU](/en/blog/foundry-eu-data-zone-premium-doubles-swedish-cost-math) earlier this month.

**Memory files are personal data stores.** Under GDPR, the markdown memory directory is a record system with its own retention and access-request obligations. Put it on storage you can audit and purge, and document it in your record of processing before a pilot, not after.

**A third-party search service is a new processor.** Enabling Exa as the search backend adds a processor outside your Azure agreement. That needs the same assessment as any other new vendor, and for public-sector buyers probably a firm no until it has one.

**NIS2 supply-chain duties apply to the harness itself.** An Apache 2.0 package from a hyperscaler is still a dependency that executes shell commands in your environment. Pin versions, mirror the package in your own feed, and review releases before upgrading, as you would any component with that level of access.

## A pilot checklist

- **1. Wrap the factory.** Write a thin internal `create_agent()` that requires a model object and sets your defaults, so no team calls `create_harness()` bare.
- **2. Point the model at Azure.** Azure OpenAI v1 endpoint with an Entra token provider, deployment in Data Zone EU where residency matters.
- **3. Set interventions.** `"ask"` for the pilot, a reviewed Cedar policy before production.
- **4. Trim the tools.** Pass an explicit `builtin_tools` list per agent. Leave out `web_search` unless someone has approved where queries go.
- **5. Move state.** Sessions and memory on an Azure Files mount or a custom backend, with retention defined. Or disable memory until you know which model extracts it.
- **6. Isolate execution.** Dedicated Container Apps environment, least-privilege managed identity, Docker sandbox for the shell.
- **7. Export traces.** `OTEL_EXPORTER_OTLP_ENDPOINT` to your collector, so agent runs appear next to the rest of your telemetry.
- **8. Measure the claim.** Run 20 representative tasks through Strands and your current harness on the same model, and record tokens, cost and pass rate before deciding anything.

## Sources

- [Strands Agents: Introducing Strands harness (21 September 2026)](https://strandsagents.com/blog/introducing-strands-harness/)
- [Strands harness: configuration reference (defaults)](https://strandsagents.com/docs/user-guide/harness/reference/configuration/)
- [Strands harness: model and reasoning selection](https://strandsagents.com/docs/user-guide/harness/configure/model/)
- [Strands harness: tool call interventions](https://strandsagents.com/docs/user-guide/harness/configure/interventions/)
- [Strands harness: session persistence](https://strandsagents.com/docs/user-guide/harness/configure/sessions/) and [long-term memory](https://strandsagents.com/docs/user-guide/harness/configure/memory/)
- [Strands harness: web access](https://strandsagents.com/docs/user-guide/harness/tools/web-access/) and [programmatic tool calling](https://strandsagents.com/docs/user-guide/harness/tools/programmatic-tool-calling/)
- [Strands harness: take it to production](https://strandsagents.com/docs/user-guide/harness/production/)
- [Strands Shell: security model](https://strandsagents.com/docs/user-guide/shell/security/)
- [Strands Agents SDK: OpenAI model provider](https://strandsagents.com/docs/user-guide/sdk/model-providers/openai/)
- [Strands Agents SDK: traces and OpenTelemetry](https://strandsagents.com/docs/user-guide/sdk/observability-evaluation/traces/)
- [Microsoft Learn: Azure OpenAI v1 API](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/api-version-lifecycle)
- [Microsoft Learn: Deploy and use Claude models in Microsoft Foundry](https://learn.microsoft.com/en-us/azure/ai-foundry/foundry-models/how-to/use-foundry-models-claude)

---

Technspire AB builds AI agents, Azure OpenAI solutions, and production web platforms for Swedish and EU enterprises. Book a call: https://calendly.com/technspire · hello@technspire.com · More articles: https://technspire.com/en/blog · Site overview for agents: https://technspire.com/llms.txt
