Configure AWS App Mesh Virtual Nodes

The aws:appmesh/virtualNode:VirtualNode resource, part of the Pulumi AWS provider, defines a virtual node in App Mesh: a logical pointer to a service instance with its discovery mechanism, listeners, and backend dependencies. This guide focuses on three capabilities: DNS and Cloud Map service discovery, health check configuration, and access logging.

Virtual nodes belong to a mesh and reference virtual services as backends. The examples are intentionally small. Combine them with your own mesh, virtual services, and routing configuration.

Register a node with DNS-based service discovery

Most App Mesh deployments start by defining virtual nodes that represent service instances, declaring which services they call and how clients discover them.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const serviceb1 = new aws.appmesh.VirtualNode("serviceb1", {
    name: "serviceBv1",
    meshName: simple.id,
    spec: {
        backends: [{
            virtualService: {
                virtualServiceName: "servicea.simpleapp.local",
            },
        }],
        listeners: [{
            portMapping: {
                port: 8080,
                protocol: "http",
            },
        }],
        serviceDiscovery: {
            dns: {
                hostname: "serviceb.simpleapp.local",
            },
        },
    },
});
import pulumi
import pulumi_aws as aws

serviceb1 = aws.appmesh.VirtualNode("serviceb1",
    name="serviceBv1",
    mesh_name=simple["id"],
    spec={
        "backends": [{
            "virtual_service": {
                "virtual_service_name": "servicea.simpleapp.local",
            },
        }],
        "listeners": [{
            "port_mapping": {
                "port": 8080,
                "protocol": "http",
            },
        }],
        "service_discovery": {
            "dns": {
                "hostname": "serviceb.simpleapp.local",
            },
        },
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/appmesh"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := appmesh.NewVirtualNode(ctx, "serviceb1", &appmesh.VirtualNodeArgs{
			Name:     pulumi.String("serviceBv1"),
			MeshName: pulumi.Any(simple.Id),
			Spec: &appmesh.VirtualNodeSpecArgs{
				Backends: appmesh.VirtualNodeSpecBackendArray{
					&appmesh.VirtualNodeSpecBackendArgs{
						VirtualService: &appmesh.VirtualNodeSpecBackendVirtualServiceArgs{
							VirtualServiceName: pulumi.String("servicea.simpleapp.local"),
						},
					},
				},
				Listeners: appmesh.VirtualNodeSpecListenerArray{
					&appmesh.VirtualNodeSpecListenerArgs{
						PortMapping: &appmesh.VirtualNodeSpecListenerPortMappingArgs{
							Port:     pulumi.Int(8080),
							Protocol: pulumi.String("http"),
						},
					},
				},
				ServiceDiscovery: &appmesh.VirtualNodeSpecServiceDiscoveryArgs{
					Dns: &appmesh.VirtualNodeSpecServiceDiscoveryDnsArgs{
						Hostname: pulumi.String("serviceb.simpleapp.local"),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var serviceb1 = new Aws.AppMesh.VirtualNode("serviceb1", new()
    {
        Name = "serviceBv1",
        MeshName = simple.Id,
        Spec = new Aws.AppMesh.Inputs.VirtualNodeSpecArgs
        {
            Backends = new[]
            {
                new Aws.AppMesh.Inputs.VirtualNodeSpecBackendArgs
                {
                    VirtualService = new Aws.AppMesh.Inputs.VirtualNodeSpecBackendVirtualServiceArgs
                    {
                        VirtualServiceName = "servicea.simpleapp.local",
                    },
                },
            },
            Listeners = new[]
            {
                new Aws.AppMesh.Inputs.VirtualNodeSpecListenerArgs
                {
                    PortMapping = new Aws.AppMesh.Inputs.VirtualNodeSpecListenerPortMappingArgs
                    {
                        Port = 8080,
                        Protocol = "http",
                    },
                },
            },
            ServiceDiscovery = new Aws.AppMesh.Inputs.VirtualNodeSpecServiceDiscoveryArgs
            {
                Dns = new Aws.AppMesh.Inputs.VirtualNodeSpecServiceDiscoveryDnsArgs
                {
                    Hostname = "serviceb.simpleapp.local",
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.appmesh.VirtualNode;
import com.pulumi.aws.appmesh.VirtualNodeArgs;
import com.pulumi.aws.appmesh.inputs.VirtualNodeSpecArgs;
import com.pulumi.aws.appmesh.inputs.VirtualNodeSpecServiceDiscoveryArgs;
import com.pulumi.aws.appmesh.inputs.VirtualNodeSpecServiceDiscoveryDnsArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var serviceb1 = new VirtualNode("serviceb1", VirtualNodeArgs.builder()
            .name("serviceBv1")
            .meshName(simple.id())
            .spec(VirtualNodeSpecArgs.builder()
                .backends(VirtualNodeSpecBackendArgs.builder()
                    .virtualService(VirtualNodeSpecBackendVirtualServiceArgs.builder()
                        .virtualServiceName("servicea.simpleapp.local")
                        .build())
                    .build())
                .listeners(VirtualNodeSpecListenerArgs.builder()
                    .portMapping(VirtualNodeSpecListenerPortMappingArgs.builder()
                        .port(8080)
                        .protocol("http")
                        .build())
                    .build())
                .serviceDiscovery(VirtualNodeSpecServiceDiscoveryArgs.builder()
                    .dns(VirtualNodeSpecServiceDiscoveryDnsArgs.builder()
                        .hostname("serviceb.simpleapp.local")
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  serviceb1:
    type: aws:appmesh:VirtualNode
    properties:
      name: serviceBv1
      meshName: ${simple.id}
      spec:
        backends:
          - virtualService:
              virtualServiceName: servicea.simpleapp.local
        listeners:
          - portMapping:
              port: 8080
              protocol: http
        serviceDiscovery:
          dns:
            hostname: serviceb.simpleapp.local

The spec defines the node’s behavior. The backends array lists virtual services this node calls; each backend references a virtual service by name. The listeners array defines ports this node accepts traffic on, with portMapping specifying the port and protocol. The serviceDiscovery block tells App Mesh how clients find this node; dns uses a hostname that resolves to service instances.

Use AWS Cloud Map for dynamic service discovery

When service instances register dynamically or need attribute-based routing, Cloud Map provides a registry that tracks instance metadata and health.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.servicediscovery.HttpNamespace("example", {name: "example-ns"});
const serviceb1 = new aws.appmesh.VirtualNode("serviceb1", {
    name: "serviceBv1",
    meshName: simple.id,
    spec: {
        backends: [{
            virtualService: {
                virtualServiceName: "servicea.simpleapp.local",
            },
        }],
        listeners: [{
            portMapping: {
                port: 8080,
                protocol: "http",
            },
        }],
        serviceDiscovery: {
            awsCloudMap: {
                attributes: {
                    stack: "blue",
                },
                serviceName: "serviceb1",
                namespaceName: example.name,
            },
        },
    },
});
import pulumi
import pulumi_aws as aws

example = aws.servicediscovery.HttpNamespace("example", name="example-ns")
serviceb1 = aws.appmesh.VirtualNode("serviceb1",
    name="serviceBv1",
    mesh_name=simple["id"],
    spec={
        "backends": [{
            "virtual_service": {
                "virtual_service_name": "servicea.simpleapp.local",
            },
        }],
        "listeners": [{
            "port_mapping": {
                "port": 8080,
                "protocol": "http",
            },
        }],
        "service_discovery": {
            "aws_cloud_map": {
                "attributes": {
                    "stack": "blue",
                },
                "service_name": "serviceb1",
                "namespace_name": example.name,
            },
        },
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/appmesh"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/servicediscovery"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := servicediscovery.NewHttpNamespace(ctx, "example", &servicediscovery.HttpNamespaceArgs{
			Name: pulumi.String("example-ns"),
		})
		if err != nil {
			return err
		}
		_, err = appmesh.NewVirtualNode(ctx, "serviceb1", &appmesh.VirtualNodeArgs{
			Name:     pulumi.String("serviceBv1"),
			MeshName: pulumi.Any(simple.Id),
			Spec: &appmesh.VirtualNodeSpecArgs{
				Backends: appmesh.VirtualNodeSpecBackendArray{
					&appmesh.VirtualNodeSpecBackendArgs{
						VirtualService: &appmesh.VirtualNodeSpecBackendVirtualServiceArgs{
							VirtualServiceName: pulumi.String("servicea.simpleapp.local"),
						},
					},
				},
				Listeners: appmesh.VirtualNodeSpecListenerArray{
					&appmesh.VirtualNodeSpecListenerArgs{
						PortMapping: &appmesh.VirtualNodeSpecListenerPortMappingArgs{
							Port:     pulumi.Int(8080),
							Protocol: pulumi.String("http"),
						},
					},
				},
				ServiceDiscovery: &appmesh.VirtualNodeSpecServiceDiscoveryArgs{
					AwsCloudMap: &appmesh.VirtualNodeSpecServiceDiscoveryAwsCloudMapArgs{
						Attributes: pulumi.StringMap{
							"stack": pulumi.String("blue"),
						},
						ServiceName:   pulumi.String("serviceb1"),
						NamespaceName: example.Name,
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.ServiceDiscovery.HttpNamespace("example", new()
    {
        Name = "example-ns",
    });

    var serviceb1 = new Aws.AppMesh.VirtualNode("serviceb1", new()
    {
        Name = "serviceBv1",
        MeshName = simple.Id,
        Spec = new Aws.AppMesh.Inputs.VirtualNodeSpecArgs
        {
            Backends = new[]
            {
                new Aws.AppMesh.Inputs.VirtualNodeSpecBackendArgs
                {
                    VirtualService = new Aws.AppMesh.Inputs.VirtualNodeSpecBackendVirtualServiceArgs
                    {
                        VirtualServiceName = "servicea.simpleapp.local",
                    },
                },
            },
            Listeners = new[]
            {
                new Aws.AppMesh.Inputs.VirtualNodeSpecListenerArgs
                {
                    PortMapping = new Aws.AppMesh.Inputs.VirtualNodeSpecListenerPortMappingArgs
                    {
                        Port = 8080,
                        Protocol = "http",
                    },
                },
            },
            ServiceDiscovery = new Aws.AppMesh.Inputs.VirtualNodeSpecServiceDiscoveryArgs
            {
                AwsCloudMap = new Aws.AppMesh.Inputs.VirtualNodeSpecServiceDiscoveryAwsCloudMapArgs
                {
                    Attributes = 
                    {
                        { "stack", "blue" },
                    },
                    ServiceName = "serviceb1",
                    NamespaceName = example.Name,
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.servicediscovery.HttpNamespace;
import com.pulumi.aws.servicediscovery.HttpNamespaceArgs;
import com.pulumi.aws.appmesh.VirtualNode;
import com.pulumi.aws.appmesh.VirtualNodeArgs;
import com.pulumi.aws.appmesh.inputs.VirtualNodeSpecArgs;
import com.pulumi.aws.appmesh.inputs.VirtualNodeSpecServiceDiscoveryArgs;
import com.pulumi.aws.appmesh.inputs.VirtualNodeSpecServiceDiscoveryAwsCloudMapArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var example = new HttpNamespace("example", HttpNamespaceArgs.builder()
            .name("example-ns")
            .build());

        var serviceb1 = new VirtualNode("serviceb1", VirtualNodeArgs.builder()
            .name("serviceBv1")
            .meshName(simple.id())
            .spec(VirtualNodeSpecArgs.builder()
                .backends(VirtualNodeSpecBackendArgs.builder()
                    .virtualService(VirtualNodeSpecBackendVirtualServiceArgs.builder()
                        .virtualServiceName("servicea.simpleapp.local")
                        .build())
                    .build())
                .listeners(VirtualNodeSpecListenerArgs.builder()
                    .portMapping(VirtualNodeSpecListenerPortMappingArgs.builder()
                        .port(8080)
                        .protocol("http")
                        .build())
                    .build())
                .serviceDiscovery(VirtualNodeSpecServiceDiscoveryArgs.builder()
                    .awsCloudMap(VirtualNodeSpecServiceDiscoveryAwsCloudMapArgs.builder()
                        .attributes(Map.of("stack", "blue"))
                        .serviceName("serviceb1")
                        .namespaceName(example.name())
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:servicediscovery:HttpNamespace
    properties:
      name: example-ns
  serviceb1:
    type: aws:appmesh:VirtualNode
    properties:
      name: serviceBv1
      meshName: ${simple.id}
      spec:
        backends:
          - virtualService:
              virtualServiceName: servicea.simpleapp.local
        listeners:
          - portMapping:
              port: 8080
              protocol: http
        serviceDiscovery:
          awsCloudMap:
            attributes:
              stack: blue
            serviceName: serviceb1
            namespaceName: ${example.name}

The awsCloudMap block replaces dns for dynamic discovery. The serviceName and namespaceName point to a Cloud Map service registration. The attributes map adds metadata (like “stack: blue”) that can be used for routing decisions or filtering.

Configure health checks for listener endpoints

Production deployments need health checks to detect when service instances become unhealthy and should be removed from load balancing.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const serviceb1 = new aws.appmesh.VirtualNode("serviceb1", {
    name: "serviceBv1",
    meshName: simple.id,
    spec: {
        backends: [{
            virtualService: {
                virtualServiceName: "servicea.simpleapp.local",
            },
        }],
        listeners: [{
            portMapping: {
                port: 8080,
                protocol: "http",
            },
            healthCheck: {
                protocol: "http",
                path: "/ping",
                healthyThreshold: 2,
                unhealthyThreshold: 2,
                timeoutMillis: 2000,
                intervalMillis: 5000,
            },
        }],
        serviceDiscovery: {
            dns: {
                hostname: "serviceb.simpleapp.local",
            },
        },
    },
});
import pulumi
import pulumi_aws as aws

serviceb1 = aws.appmesh.VirtualNode("serviceb1",
    name="serviceBv1",
    mesh_name=simple["id"],
    spec={
        "backends": [{
            "virtual_service": {
                "virtual_service_name": "servicea.simpleapp.local",
            },
        }],
        "listeners": [{
            "port_mapping": {
                "port": 8080,
                "protocol": "http",
            },
            "health_check": {
                "protocol": "http",
                "path": "/ping",
                "healthy_threshold": 2,
                "unhealthy_threshold": 2,
                "timeout_millis": 2000,
                "interval_millis": 5000,
            },
        }],
        "service_discovery": {
            "dns": {
                "hostname": "serviceb.simpleapp.local",
            },
        },
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/appmesh"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := appmesh.NewVirtualNode(ctx, "serviceb1", &appmesh.VirtualNodeArgs{
			Name:     pulumi.String("serviceBv1"),
			MeshName: pulumi.Any(simple.Id),
			Spec: &appmesh.VirtualNodeSpecArgs{
				Backends: appmesh.VirtualNodeSpecBackendArray{
					&appmesh.VirtualNodeSpecBackendArgs{
						VirtualService: &appmesh.VirtualNodeSpecBackendVirtualServiceArgs{
							VirtualServiceName: pulumi.String("servicea.simpleapp.local"),
						},
					},
				},
				Listeners: appmesh.VirtualNodeSpecListenerArray{
					&appmesh.VirtualNodeSpecListenerArgs{
						PortMapping: &appmesh.VirtualNodeSpecListenerPortMappingArgs{
							Port:     pulumi.Int(8080),
							Protocol: pulumi.String("http"),
						},
						HealthCheck: &appmesh.VirtualNodeSpecListenerHealthCheckArgs{
							Protocol:           pulumi.String("http"),
							Path:               pulumi.String("/ping"),
							HealthyThreshold:   pulumi.Int(2),
							UnhealthyThreshold: pulumi.Int(2),
							TimeoutMillis:      pulumi.Int(2000),
							IntervalMillis:     pulumi.Int(5000),
						},
					},
				},
				ServiceDiscovery: &appmesh.VirtualNodeSpecServiceDiscoveryArgs{
					Dns: &appmesh.VirtualNodeSpecServiceDiscoveryDnsArgs{
						Hostname: pulumi.String("serviceb.simpleapp.local"),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var serviceb1 = new Aws.AppMesh.VirtualNode("serviceb1", new()
    {
        Name = "serviceBv1",
        MeshName = simple.Id,
        Spec = new Aws.AppMesh.Inputs.VirtualNodeSpecArgs
        {
            Backends = new[]
            {
                new Aws.AppMesh.Inputs.VirtualNodeSpecBackendArgs
                {
                    VirtualService = new Aws.AppMesh.Inputs.VirtualNodeSpecBackendVirtualServiceArgs
                    {
                        VirtualServiceName = "servicea.simpleapp.local",
                    },
                },
            },
            Listeners = new[]
            {
                new Aws.AppMesh.Inputs.VirtualNodeSpecListenerArgs
                {
                    PortMapping = new Aws.AppMesh.Inputs.VirtualNodeSpecListenerPortMappingArgs
                    {
                        Port = 8080,
                        Protocol = "http",
                    },
                    HealthCheck = new Aws.AppMesh.Inputs.VirtualNodeSpecListenerHealthCheckArgs
                    {
                        Protocol = "http",
                        Path = "/ping",
                        HealthyThreshold = 2,
                        UnhealthyThreshold = 2,
                        TimeoutMillis = 2000,
                        IntervalMillis = 5000,
                    },
                },
            },
            ServiceDiscovery = new Aws.AppMesh.Inputs.VirtualNodeSpecServiceDiscoveryArgs
            {
                Dns = new Aws.AppMesh.Inputs.VirtualNodeSpecServiceDiscoveryDnsArgs
                {
                    Hostname = "serviceb.simpleapp.local",
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.appmesh.VirtualNode;
import com.pulumi.aws.appmesh.VirtualNodeArgs;
import com.pulumi.aws.appmesh.inputs.VirtualNodeSpecArgs;
import com.pulumi.aws.appmesh.inputs.VirtualNodeSpecServiceDiscoveryArgs;
import com.pulumi.aws.appmesh.inputs.VirtualNodeSpecServiceDiscoveryDnsArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var serviceb1 = new VirtualNode("serviceb1", VirtualNodeArgs.builder()
            .name("serviceBv1")
            .meshName(simple.id())
            .spec(VirtualNodeSpecArgs.builder()
                .backends(VirtualNodeSpecBackendArgs.builder()
                    .virtualService(VirtualNodeSpecBackendVirtualServiceArgs.builder()
                        .virtualServiceName("servicea.simpleapp.local")
                        .build())
                    .build())
                .listeners(VirtualNodeSpecListenerArgs.builder()
                    .portMapping(VirtualNodeSpecListenerPortMappingArgs.builder()
                        .port(8080)
                        .protocol("http")
                        .build())
                    .healthCheck(VirtualNodeSpecListenerHealthCheckArgs.builder()
                        .protocol("http")
                        .path("/ping")
                        .healthyThreshold(2)
                        .unhealthyThreshold(2)
                        .timeoutMillis(2000)
                        .intervalMillis(5000)
                        .build())
                    .build())
                .serviceDiscovery(VirtualNodeSpecServiceDiscoveryArgs.builder()
                    .dns(VirtualNodeSpecServiceDiscoveryDnsArgs.builder()
                        .hostname("serviceb.simpleapp.local")
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  serviceb1:
    type: aws:appmesh:VirtualNode
    properties:
      name: serviceBv1
      meshName: ${simple.id}
      spec:
        backends:
          - virtualService:
              virtualServiceName: servicea.simpleapp.local
        listeners:
          - portMapping:
              port: 8080
              protocol: http
            healthCheck:
              protocol: http
              path: /ping
              healthyThreshold: 2
              unhealthyThreshold: 2
              timeoutMillis: 2000
              intervalMillis: 5000
        serviceDiscovery:
          dns:
            hostname: serviceb.simpleapp.local

The healthCheck block inside listeners defines how App Mesh probes the service. The protocol and path specify the health endpoint (here, HTTP GET /ping). The healthyThreshold and unhealthyThreshold control how many consecutive checks must pass or fail before changing state. The timeoutMillis and intervalMillis control check timing.

Stream access logs to stdout for observability

Debugging and monitoring require visibility into request patterns. App Mesh can stream access logs to a file path, typically stdout for container-based deployments.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const serviceb1 = new aws.appmesh.VirtualNode("serviceb1", {
    name: "serviceBv1",
    meshName: simple.id,
    spec: {
        backends: [{
            virtualService: {
                virtualServiceName: "servicea.simpleapp.local",
            },
        }],
        listeners: [{
            portMapping: {
                port: 8080,
                protocol: "http",
            },
        }],
        serviceDiscovery: {
            dns: {
                hostname: "serviceb.simpleapp.local",
            },
        },
        logging: {
            accessLog: {
                file: {
                    path: "/dev/stdout",
                },
            },
        },
    },
});
import pulumi
import pulumi_aws as aws

serviceb1 = aws.appmesh.VirtualNode("serviceb1",
    name="serviceBv1",
    mesh_name=simple["id"],
    spec={
        "backends": [{
            "virtual_service": {
                "virtual_service_name": "servicea.simpleapp.local",
            },
        }],
        "listeners": [{
            "port_mapping": {
                "port": 8080,
                "protocol": "http",
            },
        }],
        "service_discovery": {
            "dns": {
                "hostname": "serviceb.simpleapp.local",
            },
        },
        "logging": {
            "access_log": {
                "file": {
                    "path": "/dev/stdout",
                },
            },
        },
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/appmesh"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := appmesh.NewVirtualNode(ctx, "serviceb1", &appmesh.VirtualNodeArgs{
			Name:     pulumi.String("serviceBv1"),
			MeshName: pulumi.Any(simple.Id),
			Spec: &appmesh.VirtualNodeSpecArgs{
				Backends: appmesh.VirtualNodeSpecBackendArray{
					&appmesh.VirtualNodeSpecBackendArgs{
						VirtualService: &appmesh.VirtualNodeSpecBackendVirtualServiceArgs{
							VirtualServiceName: pulumi.String("servicea.simpleapp.local"),
						},
					},
				},
				Listeners: appmesh.VirtualNodeSpecListenerArray{
					&appmesh.VirtualNodeSpecListenerArgs{
						PortMapping: &appmesh.VirtualNodeSpecListenerPortMappingArgs{
							Port:     pulumi.Int(8080),
							Protocol: pulumi.String("http"),
						},
					},
				},
				ServiceDiscovery: &appmesh.VirtualNodeSpecServiceDiscoveryArgs{
					Dns: &appmesh.VirtualNodeSpecServiceDiscoveryDnsArgs{
						Hostname: pulumi.String("serviceb.simpleapp.local"),
					},
				},
				Logging: &appmesh.VirtualNodeSpecLoggingArgs{
					AccessLog: &appmesh.VirtualNodeSpecLoggingAccessLogArgs{
						File: &appmesh.VirtualNodeSpecLoggingAccessLogFileArgs{
							Path: pulumi.String("/dev/stdout"),
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var serviceb1 = new Aws.AppMesh.VirtualNode("serviceb1", new()
    {
        Name = "serviceBv1",
        MeshName = simple.Id,
        Spec = new Aws.AppMesh.Inputs.VirtualNodeSpecArgs
        {
            Backends = new[]
            {
                new Aws.AppMesh.Inputs.VirtualNodeSpecBackendArgs
                {
                    VirtualService = new Aws.AppMesh.Inputs.VirtualNodeSpecBackendVirtualServiceArgs
                    {
                        VirtualServiceName = "servicea.simpleapp.local",
                    },
                },
            },
            Listeners = new[]
            {
                new Aws.AppMesh.Inputs.VirtualNodeSpecListenerArgs
                {
                    PortMapping = new Aws.AppMesh.Inputs.VirtualNodeSpecListenerPortMappingArgs
                    {
                        Port = 8080,
                        Protocol = "http",
                    },
                },
            },
            ServiceDiscovery = new Aws.AppMesh.Inputs.VirtualNodeSpecServiceDiscoveryArgs
            {
                Dns = new Aws.AppMesh.Inputs.VirtualNodeSpecServiceDiscoveryDnsArgs
                {
                    Hostname = "serviceb.simpleapp.local",
                },
            },
            Logging = new Aws.AppMesh.Inputs.VirtualNodeSpecLoggingArgs
            {
                AccessLog = new Aws.AppMesh.Inputs.VirtualNodeSpecLoggingAccessLogArgs
                {
                    File = new Aws.AppMesh.Inputs.VirtualNodeSpecLoggingAccessLogFileArgs
                    {
                        Path = "/dev/stdout",
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.appmesh.VirtualNode;
import com.pulumi.aws.appmesh.VirtualNodeArgs;
import com.pulumi.aws.appmesh.inputs.VirtualNodeSpecArgs;
import com.pulumi.aws.appmesh.inputs.VirtualNodeSpecServiceDiscoveryArgs;
import com.pulumi.aws.appmesh.inputs.VirtualNodeSpecServiceDiscoveryDnsArgs;
import com.pulumi.aws.appmesh.inputs.VirtualNodeSpecLoggingArgs;
import com.pulumi.aws.appmesh.inputs.VirtualNodeSpecLoggingAccessLogArgs;
import com.pulumi.aws.appmesh.inputs.VirtualNodeSpecLoggingAccessLogFileArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var serviceb1 = new VirtualNode("serviceb1", VirtualNodeArgs.builder()
            .name("serviceBv1")
            .meshName(simple.id())
            .spec(VirtualNodeSpecArgs.builder()
                .backends(VirtualNodeSpecBackendArgs.builder()
                    .virtualService(VirtualNodeSpecBackendVirtualServiceArgs.builder()
                        .virtualServiceName("servicea.simpleapp.local")
                        .build())
                    .build())
                .listeners(VirtualNodeSpecListenerArgs.builder()
                    .portMapping(VirtualNodeSpecListenerPortMappingArgs.builder()
                        .port(8080)
                        .protocol("http")
                        .build())
                    .build())
                .serviceDiscovery(VirtualNodeSpecServiceDiscoveryArgs.builder()
                    .dns(VirtualNodeSpecServiceDiscoveryDnsArgs.builder()
                        .hostname("serviceb.simpleapp.local")
                        .build())
                    .build())
                .logging(VirtualNodeSpecLoggingArgs.builder()
                    .accessLog(VirtualNodeSpecLoggingAccessLogArgs.builder()
                        .file(VirtualNodeSpecLoggingAccessLogFileArgs.builder()
                            .path("/dev/stdout")
                            .build())
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  serviceb1:
    type: aws:appmesh:VirtualNode
    properties:
      name: serviceBv1
      meshName: ${simple.id}
      spec:
        backends:
          - virtualService:
              virtualServiceName: servicea.simpleapp.local
        listeners:
          - portMapping:
              port: 8080
              protocol: http
        serviceDiscovery:
          dns:
            hostname: serviceb.simpleapp.local
        logging:
          accessLog:
            file:
              path: /dev/stdout

The logging block enables access logs. The accessLog.file.path property specifies where logs are written; /dev/stdout sends them to standard output, where container runtimes can capture them for CloudWatch or other log aggregators.

Beyond these examples

These snippets focus on specific virtual node features: service discovery (DNS and Cloud Map), health checks and access logging, and backend service dependencies. They’re intentionally minimal rather than full service mesh deployments.

The examples reference pre-existing infrastructure such as App Mesh service meshes, virtual services for backend dependencies, and Cloud Map namespaces for Cloud Map discovery. They focus on configuring the virtual node rather than provisioning the entire mesh topology.

To keep things focused, common virtual node patterns are omitted, including:

  • TLS configuration for encrypted communication
  • Connection pool tuning (timeouts, max connections)
  • Outlier detection for circuit breaking
  • Retry policies and timeout configuration

These omissions are intentional: the goal is to illustrate how each virtual node feature is wired, not provide drop-in service mesh modules. See the App Mesh VirtualNode resource reference for all available configuration options.

Let's configure AWS App Mesh Virtual Nodes

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Migration & Breaking Changes
What changed in provider version 2.3.0 that affects my virtual nodes?
Provider v2.3.0 introduced breaking API changes. You need to rename dns.service_name to dns.hostname and replace the backends attribute with backend configuration blocks using virtual_service_name. Existing resource state migrates automatically.
Configuration & Setup
What properties can't I change after creating a virtual node?
The meshName, meshOwner, and name properties are immutable after creation. Plan these values carefully during initial setup.
How do I configure backends for my virtual node?
Use backend configuration blocks within the spec, setting virtualService.virtualServiceName to point to your backend service (e.g., “servicea.simpleapp.local”).
Service Discovery & Monitoring
What are my options for service discovery?
You can use DNS-based discovery with serviceDiscovery.dns.hostname or AWS Cloud Map with serviceDiscovery.awsCloudMap (specifying serviceName, namespaceName, and optional attributes).
How do I add health checks to a listener?
Configure healthCheck within the listener, specifying protocol, path, healthyThreshold, unhealthyThreshold, timeoutMillis, and intervalMillis.
How do I enable access logging for my virtual node?
Add logging.accessLog.file.path to the spec, typically set to “/dev/stdout” to send logs to standard output.

Using a different cloud?

Explore networking guides for other cloud providers: