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 FREEFrequently Asked Questions
Migration & Breaking Changes
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
hostname (e.g., serviceb.simpleapp.local), while AWS Cloud Map uses serviceName, namespaceName, and optional attributes for more advanced service registration.backend blocks to the spec with virtualService.virtualServiceName set to the target service name (e.g., servicea.simpleapp.local).Health Checks & Logging
healthCheck block to the listener with protocol, path, healthyThreshold, unhealthyThreshold, timeoutMillis, and intervalMillis.spec.logging.accessLog.file with a path (e.g., /dev/stdout).Resource Management
meshName, meshOwner, and name properties are immutable and require resource replacement if changed.meshName/virtualNodeName, for example: pulumi import aws:appmesh/virtualNode:VirtualNode serviceb1 simpleapp/serviceBv1.Using a different cloud?
Explore networking guides for other cloud providers: