← Files AWS CoreARCHIVED FILE
skills/aws-observability/references/appsignals-guides/ecs-dotnet.md
8.28 KB · Sep 30, 2026 · 22:47 UTC
# Enable AWS Application Signals for .NET on ECS
## Overview
This guide provides complete steps to enable AWS Application Signals for ECS services (both EC2 and Fargate launch types), including distributed tracing, performance monitoring, and service mapping.
## Prerequisites
- Services running on ECS (EC2 or Fargate launch types)
- Applications using .NET language
## Implementation Steps
**Constraints:**
You must strictly follow the steps in the order below, do not skip or combine steps.
### Step 1: Setup CloudWatch Agent Task
#### 1.1 Add CloudWatch Agent Permissions to ECS Task Role
```typescript
const taskRole = new iam.Role(this, 'EcsTaskRole', {
assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName('AWSXRayDaemonWriteAccess'),
iam.ManagedPolicy.fromAwsManagedPolicyName('CloudWatchAgentServerPolicy'),
],
});
```
#### 1.2 Create CloudWatch Agent Log Group
```typescript
const cwAgentLogGroup = new logs.LogGroup(this, 'CwAgentLogGroup', {
logGroupName: '/ecs/ecs-cwagent',
removalPolicy: cdk.RemovalPolicy.DESTROY,
retention: logs.RetentionDays.ONE_MONTH,
});
```
#### 1.3 Add CloudWatch Agent Container to Each Task Definition
```typescript
const cwAgentContainer = taskDefinition.addContainer('ecs-cwagent-{{SERVICE_NAME}}', {
image: ecs.ContainerImage.fromRegistry('public.ecr.aws/cloudwatch-agent/cloudwatch-agent:latest'),
essential: false,
memoryReservationMiB: 128,
cpu: 64,
environment: {
CW_CONFIG_CONTENT: JSON.stringify({
"traces": {
"traces_collected": {
"application_signals": {}
}
},
"logs": {
"metrics_collected": {
"application_signals": {}
}
}
}),
},
logging: ecs.LogDrivers.awsLogs({
streamPrefix: 'ecs',
logGroup: cwAgentLogGroup,
}),
});
```
### Step 2: Add AWS Distro for OpenTelemetry Zero-Code Auto-Instrumentation to Main Service
#### 2.1 Add Bind Mount Volumes to Task Definition
```typescript
const taskDefinition = new ecs.FargateTaskDefinition(this, '{{SERVICE_NAME}}TaskDefinition', {
// Existing configuration...
volumes: [
{
name: "opentelemetry-auto-instrumentation-dotnet"
}
],
});
```
#### 2.2 Add ADOT Auto-instrumentation Init Container
##### For Linux Containers:
```typescript
const initContainer = taskDefinition.addContainer('init', {
image: ecs.ContainerImage.fromRegistry('public.ecr.aws/aws-observability/adot-autoinstrumentation-dotnet:v1.9.2'),
essential: false,
memoryReservationMiB: 64,
cpu: 32,
command: ['cp', '-a', '/autoinstrumentation/.', '/otel-auto-instrumentation-dotnet'],
logging: ecs.LogDrivers.awsLogs({
streamPrefix: 'init-{{SERVICE_NAME}}',
logGroup: serviceLogGroup,
}),
});
initContainer.addMountPoints({
sourceVolume: 'opentelemetry-auto-instrumentation-dotnet',
containerPath: '/otel-auto-instrumentation-dotnet',
readOnly: false,
});
```
##### For Windows Server Containers:
```typescript
const initContainer = taskDefinition.addContainer('init', {
image: ecs.ContainerImage.fromRegistry('public.ecr.aws/aws-observability/adot-autoinstrumentation-dotnet:v1.9.2'),
essential: false,
memoryReservationMiB: 64,
cpu: 32,
command: ['CMD', '/c', 'xcopy', '/e', 'C:\\autoinstrumentation\\*', 'C:\\otel-auto-instrumentation', '&&', 'icacls', 'C:\\otel-auto-instrumentation', '/grant', '*S-1-1-0:R', '/T'],
logging: ecs.LogDrivers.awsLogs({
streamPrefix: 'init-{{SERVICE_NAME}}',
logGroup: serviceLogGroup,
}),
});
initContainer.addMountPoints({
sourceVolume: 'opentelemetry-auto-instrumentation-dotnet',
containerPath: 'C:\\otel-auto-instrumentation',
readOnly: false,
});
```
#### 2.3 Configure Main Application Container OpenTelemetry Environment Variables
##### For Linux Containers:
```typescript
const mainContainer = taskDefinition.addContainer('{{SERVICE_NAME}}-container', {
// Existing configuration...
environment: {
// Existing environment variables...
OTEL_RESOURCE_ATTRIBUTES: 'service.name={{SERVICE_NAME}}',
OTEL_METRICS_EXPORTER: 'none',
OTEL_LOGS_EXPORTER: 'none',
DOTNET_STARTUP_HOOKS: '/otel-auto-instrumentation-dotnet/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll',
DOTNET_ADDITIONAL_DEPS: '/otel-auto-instrumentation-dotnet/AdditionalDeps',
DOTNET_SHARED_STORE: '/otel-auto-instrumentation-dotnet/store',
OTEL_DOTNET_AUTO_HOME: '/otel-auto-instrumentation-dotnet',
OTEL_TRACES_EXPORTER: 'otlp',
OTEL_EXPORTER_OTLP_PROTOCOL: 'http/protobuf',
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: 'http://localhost:4316/v1/traces',
OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT: 'http://localhost:4316/v1/metrics',
OTEL_AWS_APPLICATION_SIGNALS_ENABLED: 'true',
CORECLR_ENABLE_PROFILING: '1',
CORECLR_PROFILER: '{918728DD-259F-4A6A-AC2B-B85E1B658318}',
CORECLR_PROFILER_PATH: '/otel-auto-instrumentation-dotnet/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so',
OTEL_DOTNET_AUTO_PLUGINS: 'AWS.Distro.OpenTelemetry.AutoInstrumentation.Plugin, AWS.Distro.OpenTelemetry.AutoInstrumentation',
},
});
```
##### For Windows Server Containers:
```typescript
const mainContainer = taskDefinition.addContainer('{{SERVICE_NAME}}-container', {
// Existing configuration...
environment: {
// Existing environment variables...
OTEL_RESOURCE_ATTRIBUTES: 'service.name={{SERVICE_NAME}}',
OTEL_METRICS_EXPORTER: 'none',
OTEL_LOGS_EXPORTER: 'none',
DOTNET_STARTUP_HOOKS: 'C:\\otel-auto-instrumentation\\net\\OpenTelemetry.AutoInstrumentation.StartupHook.dll',
DOTNET_ADDITIONAL_DEPS: 'C:\\otel-auto-instrumentation\\AdditionalDeps',
DOTNET_SHARED_STORE: 'C:\\otel-auto-instrumentation\\store',
OTEL_DOTNET_AUTO_HOME: 'C:\\otel-auto-instrumentation',
OTEL_TRACES_EXPORTER: 'otlp',
OTEL_EXPORTER_OTLP_PROTOCOL: 'http/protobuf',
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: 'http://localhost:4316/v1/traces',
OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT: 'http://localhost:4316/v1/metrics',
OTEL_AWS_APPLICATION_SIGNALS_ENABLED: 'true',
CORECLR_ENABLE_PROFILING: '1',
CORECLR_PROFILER: '{918728DD-259F-4A6A-AC2B-B85E1B658318}',
CORECLR_PROFILER_PATH: 'C:\\otel-auto-instrumentation\\win-x64\\OpenTelemetry.AutoInstrumentation.Native.dll',
OTEL_DOTNET_AUTO_PLUGINS: 'AWS.Distro.OpenTelemetry.AutoInstrumentation.Plugin, AWS.Distro.OpenTelemetry.AutoInstrumentation',
},
});
```
#### 2.4 Add Mount Point to Main Container
##### For Linux Containers:
```typescript
mainContainer.addMountPoints({
sourceVolume: 'opentelemetry-auto-instrumentation-dotnet',
containerPath: '/otel-auto-instrumentation-dotnet',
readOnly: false,
});
```
##### For Windows Server Containers:
```typescript
mainContainer.addMountPoints({
sourceVolume: 'opentelemetry-auto-instrumentation-dotnet',
containerPath: 'C:\\otel-auto-instrumentation',
readOnly: false,
});
```
#### 2.5 Configure Container Dependencies
```typescript
mainContainer.addContainerDependencies({
container: initContainer,
condition: ecs.ContainerDependencyCondition.SUCCESS,
});
mainContainer.addContainerDependencies({
container: cwAgentContainer,
condition: ecs.ContainerDependencyCondition.START,
});
```
## Completion
**Tell the user:**
"I've completed the Application Signals enablement for your .NET application. Here's what I modified:
**Files Changed:**
- IAM role: Added CloudWatchAgentServerPolicy
- ECS container: Installed and configured CloudWatch Agent as sidecar
- ADOT SDK container: Mounted ADOT SDK dependencies into Application container
- Application container: Enabled zero-code auto-instrumentation for .NET Application
**Next Steps:**
1. Ensure that [Application Signals is enabled in AWS account](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Application-Signals-Enable.html).
2. Review the changes I made using `git diff`
3. Deploy your infrastructure
4. After deployment, wait 5-10 minutes for telemetry data to start flowing
**Verification:**
- Open AWS CloudWatch Console → Application Signals → Services
- Look for your service (named: {{SERVICE_NAME}})
**Troubleshooting**
Refer to the [CloudWatch APM troubleshooting guide](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Application-Signals-Enable-Troubleshoot.html).
Let me know if you'd like me to make any adjustments before you deploy!"
SHA-256: 36bb4d645595c7b8c01561e17e7961b56ad352033f983b624c4bb2aadcd07fc2