Configure Azure Private DNS Record Sets

The azure-native:privatedns:PrivateRecordSet resource, part of the Pulumi Azure Native provider, defines DNS records within a Private DNS zone: their type, values, and TTL. This guide focuses on four capabilities: A records for IPv4 resolution, CNAME records for aliasing, MX records for mail routing, and SRV records for service discovery.

Record sets belong to Private DNS zones and reference resource groups that must exist separately. The examples are intentionally small. Combine them with your own Private DNS zones and network infrastructure.

Map hostnames to IPv4 addresses with A records

Most private DNS deployments create A records that map hostnames to IPv4 addresses, enabling internal services to resolve by name.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const privateRecordSet = new azure_native.privatedns.PrivateRecordSet("privateRecordSet", {
    aRecords: [{
        ipv4Address: "1.2.3.4",
    }],
    metadata: {
        key1: "value1",
    },
    privateZoneName: "privatezone1.com",
    recordType: "A",
    relativeRecordSetName: "recordA",
    resourceGroupName: "resourceGroup1",
    ttl: 3600,
});
import pulumi
import pulumi_azure_native as azure_native

private_record_set = azure_native.privatedns.PrivateRecordSet("privateRecordSet",
    a_records=[{
        "ipv4_address": "1.2.3.4",
    }],
    metadata={
        "key1": "value1",
    },
    private_zone_name="privatezone1.com",
    record_type="A",
    relative_record_set_name="recordA",
    resource_group_name="resourceGroup1",
    ttl=3600)
package main

import (
	privatedns "github.com/pulumi/pulumi-azure-native-sdk/privatedns/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := privatedns.NewPrivateRecordSet(ctx, "privateRecordSet", &privatedns.PrivateRecordSetArgs{
			ARecords: privatedns.ARecordArray{
				&privatedns.ARecordArgs{
					Ipv4Address: pulumi.String("1.2.3.4"),
				},
			},
			Metadata: pulumi.StringMap{
				"key1": pulumi.String("value1"),
			},
			PrivateZoneName:       pulumi.String("privatezone1.com"),
			RecordType:            pulumi.String("A"),
			RelativeRecordSetName: pulumi.String("recordA"),
			ResourceGroupName:     pulumi.String("resourceGroup1"),
			Ttl:                   pulumi.Float64(3600),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var privateRecordSet = new AzureNative.PrivateDns.PrivateRecordSet("privateRecordSet", new()
    {
        ARecords = new[]
        {
            new AzureNative.PrivateDns.Inputs.ARecordArgs
            {
                Ipv4Address = "1.2.3.4",
            },
        },
        Metadata = 
        {
            { "key1", "value1" },
        },
        PrivateZoneName = "privatezone1.com",
        RecordType = "A",
        RelativeRecordSetName = "recordA",
        ResourceGroupName = "resourceGroup1",
        Ttl = 3600,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.privatedns.PrivateRecordSet;
import com.pulumi.azurenative.privatedns.PrivateRecordSetArgs;
import com.pulumi.azurenative.privatedns.inputs.ARecordArgs;
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 privateRecordSet = new PrivateRecordSet("privateRecordSet", PrivateRecordSetArgs.builder()
            .aRecords(ARecordArgs.builder()
                .ipv4Address("1.2.3.4")
                .build())
            .metadata(Map.of("key1", "value1"))
            .privateZoneName("privatezone1.com")
            .recordType("A")
            .relativeRecordSetName("recordA")
            .resourceGroupName("resourceGroup1")
            .ttl(3600.0)
            .build());

    }
}
resources:
  privateRecordSet:
    type: azure-native:privatedns:PrivateRecordSet
    properties:
      aRecords:
        - ipv4Address: 1.2.3.4
      metadata:
        key1: value1
      privateZoneName: privatezone1.com
      recordType: A
      relativeRecordSetName: recordA
      resourceGroupName: resourceGroup1
      ttl: 3600

When a client queries the hostname, DNS returns the IPv4 address from aRecords. The recordType property specifies “A” for IPv4 address records. The relativeRecordSetName sets the hostname within the zone (e.g., “recordA” becomes “recordA.privatezone1.com”). The ttl property controls how long clients cache the response, measured in seconds.

Create aliases with CNAME records

Applications often need DNS aliases that point one hostname to another, allowing you to change targets without updating clients.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const privateRecordSet = new azure_native.privatedns.PrivateRecordSet("privateRecordSet", {
    cnameRecord: {
        cname: "contoso.com",
    },
    metadata: {
        key1: "value1",
    },
    privateZoneName: "privatezone1.com",
    recordType: "CNAME",
    relativeRecordSetName: "recordCNAME",
    resourceGroupName: "resourceGroup1",
    ttl: 3600,
});
import pulumi
import pulumi_azure_native as azure_native

private_record_set = azure_native.privatedns.PrivateRecordSet("privateRecordSet",
    cname_record={
        "cname": "contoso.com",
    },
    metadata={
        "key1": "value1",
    },
    private_zone_name="privatezone1.com",
    record_type="CNAME",
    relative_record_set_name="recordCNAME",
    resource_group_name="resourceGroup1",
    ttl=3600)
package main

import (
	privatedns "github.com/pulumi/pulumi-azure-native-sdk/privatedns/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := privatedns.NewPrivateRecordSet(ctx, "privateRecordSet", &privatedns.PrivateRecordSetArgs{
			CnameRecord: &privatedns.CnameRecordArgs{
				Cname: pulumi.String("contoso.com"),
			},
			Metadata: pulumi.StringMap{
				"key1": pulumi.String("value1"),
			},
			PrivateZoneName:       pulumi.String("privatezone1.com"),
			RecordType:            pulumi.String("CNAME"),
			RelativeRecordSetName: pulumi.String("recordCNAME"),
			ResourceGroupName:     pulumi.String("resourceGroup1"),
			Ttl:                   pulumi.Float64(3600),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var privateRecordSet = new AzureNative.PrivateDns.PrivateRecordSet("privateRecordSet", new()
    {
        CnameRecord = new AzureNative.PrivateDns.Inputs.CnameRecordArgs
        {
            Cname = "contoso.com",
        },
        Metadata = 
        {
            { "key1", "value1" },
        },
        PrivateZoneName = "privatezone1.com",
        RecordType = "CNAME",
        RelativeRecordSetName = "recordCNAME",
        ResourceGroupName = "resourceGroup1",
        Ttl = 3600,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.privatedns.PrivateRecordSet;
import com.pulumi.azurenative.privatedns.PrivateRecordSetArgs;
import com.pulumi.azurenative.privatedns.inputs.CnameRecordArgs;
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 privateRecordSet = new PrivateRecordSet("privateRecordSet", PrivateRecordSetArgs.builder()
            .cnameRecord(CnameRecordArgs.builder()
                .cname("contoso.com")
                .build())
            .metadata(Map.of("key1", "value1"))
            .privateZoneName("privatezone1.com")
            .recordType("CNAME")
            .relativeRecordSetName("recordCNAME")
            .resourceGroupName("resourceGroup1")
            .ttl(3600.0)
            .build());

    }
}
resources:
  privateRecordSet:
    type: azure-native:privatedns:PrivateRecordSet
    properties:
      cnameRecord:
        cname: contoso.com
      metadata:
        key1: value1
      privateZoneName: privatezone1.com
      recordType: CNAME
      relativeRecordSetName: recordCNAME
      resourceGroupName: resourceGroup1
      ttl: 3600

The cnameRecord property contains a single cname value that points to the target hostname. CNAME records create aliases; when a client queries the alias, DNS returns the target name, which the client then resolves separately. This indirection lets you update the target without changing the alias.

Route email with MX records

Private zones can include MX records for internal mail routing, directing email to specific mail servers with priority-based failover.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const privateRecordSet = new azure_native.privatedns.PrivateRecordSet("privateRecordSet", {
    metadata: {
        key1: "value1",
    },
    mxRecords: [{
        exchange: "mail.privatezone1.com",
        preference: 0,
    }],
    privateZoneName: "privatezone1.com",
    recordType: "MX",
    relativeRecordSetName: "recordMX",
    resourceGroupName: "resourceGroup1",
    ttl: 3600,
});
import pulumi
import pulumi_azure_native as azure_native

private_record_set = azure_native.privatedns.PrivateRecordSet("privateRecordSet",
    metadata={
        "key1": "value1",
    },
    mx_records=[{
        "exchange": "mail.privatezone1.com",
        "preference": 0,
    }],
    private_zone_name="privatezone1.com",
    record_type="MX",
    relative_record_set_name="recordMX",
    resource_group_name="resourceGroup1",
    ttl=3600)
package main

import (
	privatedns "github.com/pulumi/pulumi-azure-native-sdk/privatedns/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := privatedns.NewPrivateRecordSet(ctx, "privateRecordSet", &privatedns.PrivateRecordSetArgs{
			Metadata: pulumi.StringMap{
				"key1": pulumi.String("value1"),
			},
			MxRecords: privatedns.MxRecordArray{
				&privatedns.MxRecordArgs{
					Exchange:   pulumi.String("mail.privatezone1.com"),
					Preference: pulumi.Int(0),
				},
			},
			PrivateZoneName:       pulumi.String("privatezone1.com"),
			RecordType:            pulumi.String("MX"),
			RelativeRecordSetName: pulumi.String("recordMX"),
			ResourceGroupName:     pulumi.String("resourceGroup1"),
			Ttl:                   pulumi.Float64(3600),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var privateRecordSet = new AzureNative.PrivateDns.PrivateRecordSet("privateRecordSet", new()
    {
        Metadata = 
        {
            { "key1", "value1" },
        },
        MxRecords = new[]
        {
            new AzureNative.PrivateDns.Inputs.MxRecordArgs
            {
                Exchange = "mail.privatezone1.com",
                Preference = 0,
            },
        },
        PrivateZoneName = "privatezone1.com",
        RecordType = "MX",
        RelativeRecordSetName = "recordMX",
        ResourceGroupName = "resourceGroup1",
        Ttl = 3600,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.privatedns.PrivateRecordSet;
import com.pulumi.azurenative.privatedns.PrivateRecordSetArgs;
import com.pulumi.azurenative.privatedns.inputs.MxRecordArgs;
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 privateRecordSet = new PrivateRecordSet("privateRecordSet", PrivateRecordSetArgs.builder()
            .metadata(Map.of("key1", "value1"))
            .mxRecords(MxRecordArgs.builder()
                .exchange("mail.privatezone1.com")
                .preference(0)
                .build())
            .privateZoneName("privatezone1.com")
            .recordType("MX")
            .relativeRecordSetName("recordMX")
            .resourceGroupName("resourceGroup1")
            .ttl(3600.0)
            .build());

    }
}
resources:
  privateRecordSet:
    type: azure-native:privatedns:PrivateRecordSet
    properties:
      metadata:
        key1: value1
      mxRecords:
        - exchange: mail.privatezone1.com
          preference: 0
      privateZoneName: privatezone1.com
      recordType: MX
      relativeRecordSetName: recordMX
      resourceGroupName: resourceGroup1
      ttl: 3600

The mxRecords array lists mail servers with their exchange hostnames and preference values. Lower preference numbers indicate higher priority; mail clients try servers in priority order. This enables failover: if the primary server is unavailable, clients automatically try the next priority level.

Service discovery protocols use SRV records to locate services by protocol and domain, encoding port numbers and load distribution.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const privateRecordSet = new azure_native.privatedns.PrivateRecordSet("privateRecordSet", {
    metadata: {
        key1: "value1",
    },
    privateZoneName: "privatezone1.com",
    recordType: "SRV",
    relativeRecordSetName: "recordSRV",
    resourceGroupName: "resourceGroup1",
    srvRecords: [{
        port: 80,
        priority: 0,
        target: "contoso.com",
        weight: 10,
    }],
    ttl: 3600,
});
import pulumi
import pulumi_azure_native as azure_native

private_record_set = azure_native.privatedns.PrivateRecordSet("privateRecordSet",
    metadata={
        "key1": "value1",
    },
    private_zone_name="privatezone1.com",
    record_type="SRV",
    relative_record_set_name="recordSRV",
    resource_group_name="resourceGroup1",
    srv_records=[{
        "port": 80,
        "priority": 0,
        "target": "contoso.com",
        "weight": 10,
    }],
    ttl=3600)
package main

import (
	privatedns "github.com/pulumi/pulumi-azure-native-sdk/privatedns/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := privatedns.NewPrivateRecordSet(ctx, "privateRecordSet", &privatedns.PrivateRecordSetArgs{
			Metadata: pulumi.StringMap{
				"key1": pulumi.String("value1"),
			},
			PrivateZoneName:       pulumi.String("privatezone1.com"),
			RecordType:            pulumi.String("SRV"),
			RelativeRecordSetName: pulumi.String("recordSRV"),
			ResourceGroupName:     pulumi.String("resourceGroup1"),
			SrvRecords: privatedns.SrvRecordArray{
				&privatedns.SrvRecordArgs{
					Port:     pulumi.Int(80),
					Priority: pulumi.Int(0),
					Target:   pulumi.String("contoso.com"),
					Weight:   pulumi.Int(10),
				},
			},
			Ttl: pulumi.Float64(3600),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var privateRecordSet = new AzureNative.PrivateDns.PrivateRecordSet("privateRecordSet", new()
    {
        Metadata = 
        {
            { "key1", "value1" },
        },
        PrivateZoneName = "privatezone1.com",
        RecordType = "SRV",
        RelativeRecordSetName = "recordSRV",
        ResourceGroupName = "resourceGroup1",
        SrvRecords = new[]
        {
            new AzureNative.PrivateDns.Inputs.SrvRecordArgs
            {
                Port = 80,
                Priority = 0,
                Target = "contoso.com",
                Weight = 10,
            },
        },
        Ttl = 3600,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.privatedns.PrivateRecordSet;
import com.pulumi.azurenative.privatedns.PrivateRecordSetArgs;
import com.pulumi.azurenative.privatedns.inputs.SrvRecordArgs;
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 privateRecordSet = new PrivateRecordSet("privateRecordSet", PrivateRecordSetArgs.builder()
            .metadata(Map.of("key1", "value1"))
            .privateZoneName("privatezone1.com")
            .recordType("SRV")
            .relativeRecordSetName("recordSRV")
            .resourceGroupName("resourceGroup1")
            .srvRecords(SrvRecordArgs.builder()
                .port(80)
                .priority(0)
                .target("contoso.com")
                .weight(10)
                .build())
            .ttl(3600.0)
            .build());

    }
}
resources:
  privateRecordSet:
    type: azure-native:privatedns:PrivateRecordSet
    properties:
      metadata:
        key1: value1
      privateZoneName: privatezone1.com
      recordType: SRV
      relativeRecordSetName: recordSRV
      resourceGroupName: resourceGroup1
      srvRecords:
        - port: 80
          priority: 0
          target: contoso.com
          weight: 10
      ttl: 3600

The srvRecords array defines service endpoints with port, priority, target, and weight properties. Priority controls failover order (lower values first). Weight enables load distribution among servers with the same priority; higher weights receive proportionally more traffic. The target specifies the hostname, and port indicates which TCP or UDP port the service listens on.

Beyond these examples

These snippets focus on specific record set features: DNS record types (A, CNAME, MX, SRV) and TTL and metadata configuration. They’re intentionally minimal rather than complete DNS zone configurations.

The examples reference pre-existing infrastructure such as Private DNS zones (privateZoneName) and Azure resource groups. They focus on configuring individual record sets rather than provisioning the surrounding DNS infrastructure.

To keep things focused, common record types are omitted, including:

  • AAAA records for IPv6 addressing
  • PTR records for reverse DNS lookups
  • SOA records for zone authority configuration
  • TXT records for verification and SPF

These omissions are intentional: the goal is to illustrate how each record type is wired, not provide drop-in DNS modules. See the Private DNS PrivateRecordSet resource reference for all available configuration options.

Let's configure Azure Private DNS Record Sets

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Naming & Immutability
What properties can't I change after creating a record set?
The privateZoneName, recordType, and relativeRecordSetName properties are immutable. Changing any of these requires replacing the resource.
What's the difference between relativeRecordSetName and the full DNS name?
relativeRecordSetName is the record name relative to the zone. For example, if your zone is privatezone1.com and relativeRecordSetName is recordA, the full DNS name (FQDN) will be recordA.privatezone1.com.
How do I create a record at the zone apex (root of the zone)?
Set relativeRecordSetName to @ to create a record at the zone apex, as shown in the SOA record example.
Why does privateZoneName not include a terminating dot?
Azure Private DNS zones are specified without the trailing dot (e.g., privatezone1.com instead of privatezone1.com.).
Record Types & Configuration
What DNS record types are supported in Private DNS zones?
Private DNS zones support A, AAAA, CNAME, MX, PTR, SOA, SRV, and TXT record types.
How do I configure different record types?
Use the record type-specific property: aRecords for A records, cnameRecord for CNAME, mxRecords for MX, etc. Set recordType to match the record type you’re creating.
Can I create multiple record types with the same name?
No, each record set has a unique combination of relativeRecordSetName and recordType. You can’t have both an A record and a CNAME record with the same name, for example.
How do I create reverse DNS (PTR) records?
Use a reverse DNS zone name format like 0.0.127.in-addr.arpa for IPv4 or the appropriate format for IPv6, then create PTR records with the IP address octets as the relativeRecordSetName.
Computed Properties & Behavior
What's the fqdn output property?
The fqdn (fully qualified domain name) is the complete DNS name of the record set, combining relativeRecordSetName and privateZoneName.
What does isAutoRegistered mean?
isAutoRegistered indicates whether the record set was automatically created through a virtual network link’s auto-registration feature, rather than being manually created.

Using a different cloud?

Explore networking guides for other cloud providers: