Aspire.Hosting.AWS

repository·main·Indexed 41 days ago

https://github.com/aws/integrations-on-dotnet-aspire-for-aws

Integrations for .NET Aspire to simplify the development and deployment of applications to AWS. It enables provisioning AWS resources like Lambda, DynamoDB, and ECS through the Aspire AppHost, and provides a deployment path that transforms AppHost resources into AWS CDK constructs and CloudFormation templates.

Tokens
20.4K
Snippets
48
Records
73
Agent score
100%

What's inside Aspire.Hosting.AWS

  1. Understand Lambda build suppression behavior

    main

    The LambdaProjectMetadata type uses a SuppressBuild flag to control whether Aspire should run a build before execution. This behavior differs based on how the Lambda is defined:

    • Class-library wrapper projects: SuppressBuild is set to true. These are built explicitly by the LambdaBeforeStartEventHandler before DCP runs, so Aspire uses dotnet run --no-build to avoid redundant incremental builds.
    • Polyglot entry points: SuppressBuild is set to false. Since these projects are not pre-built, Aspire must build them as part of the dotnet run command to ensure binaries and runtimeconfig.json are present.
    internal sealed class LambdaProjectMetadata(string projectPath, bool suppressBuild = true) : IProjectMetadata
    {
        public string ProjectPath { get; } = projectPath;
        public bool SuppressBuild { get; } = suppressBuild;
    }
  2. How AWS publish target selection works

    main

    The system determines which AWS service to use for each Aspire resource using a three-step hierarchy. This ensures that explicit user preferences are respected first, followed by the best automated match based on resource characteristics and user-defined defaults.

    Selection Hierarchy

    1. Explicit Annotation: The system first checks if the resource has an IAWSPublishTargetAnnotation applied via a specific Publish extension method (e.g., .PublishAsECSFargateServiceWithALB()). If found, this target is used immediately.
    2. Default Target Matching: If no explicit annotation is present, the system queries all registered publish targets to see which ones match the resource based on its type and the settings in the CDKDefaultsProvider.
    3. Ranking: If multiple targets match the resource, the system selects the one with the highest Rank. Higher ranks take precedence, allowing specialized targets (like a Lambda-specific target) to override generic targets (like a standard .NET project target).

    Summary of Logic

    StepMechanismPriorityDescription
    1Explicit AnnotationHighestUses the specific extension method called on the resource.
    2Default MatchingMediumUses IsDefaultPublishTargetMatch logic and CDKDefaultsProvider settings.
    3RankingTie-breakerResolves conflicts when multiple targets claim a match.
    // Example of Step 1: Explicit Annotation
    builder.AddProject<Projects.WebApp>("webapp")
        .PublishAsECSFargateServiceWithALB(); // This forces the ECS Fargate target
  3. How automatic resource selection and reference handling works

    main

    The deployment system automates several complex configuration tasks:

    • Automatic Selection: Resources without explicit publish targets are matched automatically. This includes annotation-based detection, such as detecting AgentCore agents via the AgentCoreRuntimeAnnotation.
    • Reference Handling: The system automatically configures environment variables, VPC settings, security groups, and task-role policies (for example, ensuring AgentCore has the necessary invoke permissions).
  4. Lambda VPC attachment behavior

    main

    Lambda functions in Aspire for AWS follow a specific attachment pattern based on the resources they reference:

    • No VPC-requiring references: The Lambda runs without VPC attachment, allowing it to access the internet and AWS APIs directly.
    • Has VPC-requiring references: If a referenced resource (like ElastiCache) returns true for ReferenceRequiresVPC(), the Lambda is automatically attached to the VPC's private subnets.

    Note: When attached to a VPC, the Lambda requires a NAT Gateway to maintain internet access.

  5. Local development with AgentCore (Experimental)

    main

    For developers building agents with AWS.AgentCore.Hosting, an experimental local development experience is available. This provides an embedded runtime, chat, and memory emulator that runs in-process, meaning no Docker is required.

    Requirements:

    • Requires net10.0.
    • Requires the ASPIREAWSAGENTCORE001 experimental flag.
  6. How resource references and connectivity work in Aspire for AWS

    main

    When you use .WithReference() in your Aspire AppHost to link resources, the deployment system automatically configures the necessary connectivity. This includes setting up environment variables, VPC attachments, security group ingress rules, and IAM task role policies.

    Automatic Connectivity Types

    1. Environment Variables: Connection details (like endpoints or connection strings) are injected into the referencing resource.
    2. VPC Attachment: If a referenced resource (e.g., ElastiCache) requires it, the referencing resource (e.g., a Lambda function) is automatically attached to the VPC's private subnets.
    3. Security Group Ingress: The system automatically adds ingress rules to the referenced resource's security group to allow traffic from the referencing resource.
    4. Task Role Policies: For certain services (like AgentCore), the system automatically attaches the required IAM permissions (e.g., bedrock-agentcore:Invoke*) to the referencing resource's task role.
    var cache = builder.AddRedis("cache");
    
    // The webApp will receive environment variables for the cache
    var webApp = builder.AddProject<Projects.WebApp>("webapp")
        .WithReference(cache);
    
    // The Lambda will be automatically attached to a VPC and receive security group rules
    var lambdaFunction = builder.AddAWSLambdaFunction<Projects.MyFunction>("function", "handler")
        .WithReference(cache);
  7. How ranking resolves multiple publish target matches

    main

    When a resource qualifies for multiple deployment targets, the system uses a Rank value to resolve the conflict.

    Key Rules:

    • Higher ranks win: A target with a higher rank will be selected over one with a lower rank.
    • Specificity via Rank: The ranking system is designed so that specialized resource types take precedence over generic ones. For example, a LambdaProjectResource match will typically have a higher rank than a generic ProjectResource match, ensuring the specialized Lambda deployment is chosen.
    • Annotation-based overrides: Specialized tools like AgentCore use high ranks (e.g., DEFAULT_MATCH_RANK + 200) to ensure they win over standard web or console project defaults when their specific runtime annotation is present.
  8. Understand the CDKDefaultsProvider system

    main

    The CDKDefaultsProvider is a versioned system used to manage default values and behaviors for AWS resources within the Aspire integration. It provides a mechanism for the library to evolve its default configurations (like ElastiCache engine versions or ECS service types) without forcing breaking changes on users.

    Users can choose which version of defaults to use via the CDKDefaultsProviderFactory, allowing them to opt-in to new behaviors when they are ready. The system follows a hierarchy where you can use a standard version or implement a custom provider to override specific defaults.

    // Use the latest preview version
    builder.AddAWSCDKEnvironment(
        name: "MyApp",
        cdkDefaultsProviderFactory: CDKDefaultsProviderFactory.Preview_V1
    );
  9. Understand ATS Attributes and Metadata Mapping

    main

    The Aspire Type System uses specific .NET attributes to define how the TypeScript SDK is generated. Understanding these helps explain how C# types are exposed to Node.js:

    AttributeApplies toEffect
    [AspireExport]static methodMarks it an ATS capability (an invokable operation).
    [AspireExport]typeMarks it an ATS handle type (passed by reference as an opaque handle).
    [AspireExport("name")]static methodMarks it a capability with an explicit, pinned ID and TS method name.
    [AspireDto]typeMarks it a DTO (serialized as a JSON object / TS interface).
    [AspireExportIgnore]memberExcludes a member from an exported type.

    Mapping Logic

    • Handle Types: Become TypeScript wrapper classes. If a C# method takes a handle and returns it, it becomes a fluent instance method in TypeScript (e.g., sdkConfig.withProfile("...")).
    • DTOs: Become TypeScript interfaces. These are used for plain option objects (like DynamoDBLocalOptions) and must contain only serializable members (primitives, enums, etc.).
    • Capabilities: Static methods taking IDistributedApplicationBuilder become top-level builder methods (e.g., builder.addAWSDynamoDBLocal(...)).
  10. Understand ECS Task Roles (Execution vs. Task)

    main

    ECS task definitions utilize two distinct IAM roles:

    1. Execution role: Used by the ECS/Fargate platform to perform infrastructure tasks like pulling images, writing logs, and resolving secrets. The integration assigns a shared default (e.g., DefaultECSExpressExecutionRole).
    2. Task role: The role assumed by your application code at runtime to call AWS services (e.g., S3, DynamoDB, Bedrock AgentCore).

    Key Behavior for Task Roles:

    • Per-service creation: Unlike shared execution roles, the task role is created uniquely for each service (named {resourceName}-DefaultTaskRole) to prevent permission leakage between services.
    • Automatic Permissions: If you use the integration's default task role, the system automatically attaches necessary permissions when you use WithReference. For example, a service that WithReferences an AgentCore agent automatically receives bedrock-agentcore:Invoke* permissions on its default task role.
    • Manual Overrides: If you provide your own task role via a props callback on the publish target, the integration will not mutate it. In this case, you are responsible for manually granting any permissions required by your service's references.
  11. Resolve Lambda project paths in Node.js AppHost

    main

    When using the polyglot (Node.js) AppHost to add an AWS Lambda function, the addAWSLambdaFunction method accepts a path string. This path can be relative to the AppHost directory and can point to either the .csproj file itself or the directory containing it.

    The system automatically normalizes the path using the following logic:

    1. Relative paths: Combined with builder.AppHostDirectory to create an absolute path.
    2. Directories: If the path points to a directory, the system resolves it to the single .csproj or .fsproj file inside. If the directory contains zero or multiple project files, a DistributedApplicationException is thrown.
    3. Project files: If the path already points to a project file, it is used as-is.

    Note: The final resolved IProjectMetadata.ProjectPath is always an absolute path to the project file.