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 that represents a service instance: its discovery mechanism, listeners, backend dependencies, and observability settings. This guide focuses on three capabilities: DNS and Cloud Map service discovery, listener health checks, and access logging.

Virtual nodes belong to an App Mesh service 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, using DNS hostnames for discovery without external registries.

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 how this node behaves in the mesh. The backends array lists virtual services this node calls. The listener defines the port and protocol where this node receives traffic. The serviceDiscovery.dns block registers the node with a hostname that other nodes can resolve.

Register with AWS Cloud Map for dynamic discovery

Teams running containerized workloads often use AWS Cloud Map to track service instances as they scale, providing automatic registration and health checking.

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-based discovery with Cloud Map integration. The serviceName and namespaceName identify where to register this node. The attributes field adds metadata for filtering during service discovery. Cloud Map automatically updates registrations as instances start and stop.

Add health checks to listener endpoints

Production services need health checks to ensure App Mesh only routes traffic to healthy instances, removing failed nodes automatically.

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 configures active health monitoring. App Mesh sends HTTP requests to the specified path at regular intervals. The healthyThreshold and unhealthyThreshold control how many consecutive checks must pass or fail before changing the node’s status. The timeoutMillis and intervalMillis tune check timing.

Stream access logs to stdout for observability

Debugging and monitoring mesh traffic requires access logs that capture request details, which can be streamed to container logging systems.

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.accessLog.file block directs access logs to a file path. Setting path to “/dev/stdout” streams logs to standard output, where container runtimes like ECS or EKS can forward them to CloudWatch Logs or other aggregation systems.

Beyond these examples

These snippets focus on specific virtual node features: DNS and Cloud Map service discovery, listener health checks, and access logging. 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 traffic
  • Connection pooling and timeout tuning
  • Backend defaults and client policies
  • Multiple listeners on different ports

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 Virtual Node 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
How do I migrate my virtual node from provider versions before v2.3.0?
You need to make two changes: rename dns.serviceName to dns.hostname, and replace the backends attribute with backend configuration blocks that set virtualServiceName. The state will automatically migrate for existing resources.
Service Discovery
What's the difference between DNS and AWS Cloud Map service discovery?
DNS service discovery uses a hostname (e.g., serviceb.simpleapp.local), while AWS Cloud Map uses serviceName, namespaceName, and optional attributes for more advanced service registration.
How do I configure backends for my virtual node?
Add backend blocks to the spec with virtualService.virtualServiceName set to the target service name (e.g., servicea.simpleapp.local).
Health Checks & Logging
How do I configure health checks for a listener?
Add a healthCheck block to the listener with protocol, path, healthyThreshold, unhealthyThreshold, timeoutMillis, and intervalMillis.
How do I enable access logging for my virtual node?
Configure spec.logging.accessLog.file with a path (e.g., /dev/stdout).
Resource Management
What properties can't be changed after creating a virtual node?
The meshName, meshOwner, and name properties are immutable and require resource replacement if changed.
How do I import an existing virtual node?
Use the format meshName/virtualNodeName, for example: pulumi import aws:appmesh/virtualNode:VirtualNode serviceb1 simpleapp/serviceBv1.

Using a different cloud?

Explore networking guides for other cloud providers: