Deploy Kubernetes Resources from YAML

The kubernetes:yaml:ConfigGroup resource, part of the Pulumi Kubernetes provider, creates multiple Kubernetes resources from YAML manifests. A newer version is available as kubernetes.yaml/v2.ConfigGroup; this resource is maintained for backwards compatibility. This guide focuses on three capabilities: file and pattern-based loading, inline YAML strings, and runtime transformations.

ConfigGroup requires a configured Kubernetes cluster and access to YAML manifest files for file-based deployments. The examples are intentionally small. Combine them with your own manifests and cluster configuration.

Deploy resources from a single YAML file

Most deployments start with YAML manifests stored in version control, applying them to the cluster through ConfigGroup.

import * as k8s from "@pulumi/kubernetes";

const example = new k8s.yaml.ConfigGroup("example", {
    files: "foo.yaml",
});
from pulumi_kubernetes.yaml import ConfigGroup

example = ConfigGroup(
    "example",
    files=["foo.yaml"],
)
package main

import (
	"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/yaml"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := yaml.NewConfigGroup(ctx, "example",
			&yaml.ConfigGroupArgs{
				Files: []string{"foo.yaml"},
			},
		)
		if err != nil {
			return err
		}

		return nil
	})
}
using System.Threading.Tasks;
using Pulumi;
using Pulumi.Kubernetes.Yaml;

class YamlStack : Stack
{
    public YamlStack()
    {
        var helloWorld = new ConfigGroup("example", new ConfigGroupArgs
        {
            Files = new[] { "foo.yaml" }
        });
    }
}

The files property accepts a path to a YAML file containing one or more Kubernetes resource definitions. ConfigGroup parses the file and creates each resource it finds.

Deploy all manifests matching a file pattern

When manifests are organized into directories, file patterns apply entire directories without listing each file individually.

import * as k8s from "@pulumi/kubernetes";

const example = new k8s.yaml.ConfigGroup("example", {
    files: "yaml/*.yaml",
});
from pulumi_kubernetes.yaml import ConfigGroup

example = ConfigGroup(
    "example",
    files=["yaml/*.yaml"],
)
package main

import (
	"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/yaml"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := yaml.NewConfigGroup(ctx, "example",
			&yaml.ConfigGroupArgs{
				Files: []string{"yaml/*.yaml"},
			},
		)
		if err != nil {
			return err
		}

		return nil
	})
}
using System.Threading.Tasks;
using Pulumi;
using Pulumi.Kubernetes.Yaml;

class YamlStack : Stack
{
    public YamlStack()
    {
        var helloWorld = new ConfigGroup("example", new ConfigGroupArgs
        {
            Files = new[] { "yaml/*.yaml" }
        });
    }
}

The glob pattern yaml/*.yaml matches all YAML files in the yaml directory. ConfigGroup processes each matched file and creates the resources they define.

Define resources inline with YAML strings

For generated or templated resources, the yaml property accepts literal YAML strings instead of file references.

import * as k8s from "@pulumi/kubernetes";

const example = new k8s.yaml.ConfigGroup("example", {
    yaml: `
apiVersion: v1
kind: Namespace
metadata:
  name: foo
`,
})
from pulumi_kubernetes.yaml import ConfigGroup

example = ConfigGroup(
    "example",
    yaml=['''
apiVersion: v1
kind: Namespace
metadata:
  name: foo
''']
)
package main

import (
	"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/yaml"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := yaml.NewConfigGroup(ctx, "example",
			&yaml.ConfigGroupArgs{
				YAML: []string{
					`
apiVersion: v1
kind: Namespace
metadata:
  name: foo
`,
				},
			})
		if err != nil {
			return err
		}

		return nil
	})
}
using System.Threading.Tasks;
using Pulumi;
using Pulumi.Kubernetes.Yaml;

class YamlStack : Stack
{
    public YamlStack()
    {
        var helloWorld = new ConfigGroup("example", new ConfigGroupArgs
        {
            Yaml = @"
            apiVersion: v1
            kind: Namespace
            metadata:
              name: foo
            ",
        });
    }
}

The yaml property takes a string containing valid Kubernetes YAML. This approach works well when manifests are generated programmatically or constructed from templates at deployment time.

Modify resources before deployment with transformations

Production deployments often need to adjust manifests without editing source files. Transformations modify resources programmatically before creation.

import * as k8s from "@pulumi/kubernetes";

const example = new k8s.yaml.ConfigGroup("example", {
    files: "foo.yaml",
    transformations: [
        // Make every service private to the cluster, i.e., turn all services into ClusterIP instead of LoadBalancer.
        (obj: any, opts: pulumi.CustomResourceOptions) => {
            if (obj.kind === "Service" && obj.apiVersion === "v1") {
                if (obj.spec && obj.spec.type && obj.spec.type === "LoadBalancer") {
                    obj.spec.type = "ClusterIP";
                }
            }
        },

        // Set a resource alias for a previous name.
        (obj: any, opts: pulumi.CustomResourceOptions) => {
            if (obj.kind === "Deployment") {
                opts.aliases = [{ name: "oldName" }]
            }
        },

        // Omit a resource from the Chart by transforming the specified resource definition to an empty List.
        (obj: any, opts: pulumi.CustomResourceOptions) => {
            if (obj.kind === "Pod" && obj.metadata.name === "test") {
                obj.apiVersion = "v1"
                obj.kind = "List"
            }
        },
    ],
});
from pulumi_kubernetes.yaml import ConfigGroup

# Make every service private to the cluster, i.e., turn all services into ClusterIP instead of LoadBalancer.
def make_service_private(obj, opts):
    if obj["kind"] == "Service" and obj["apiVersion"] == "v1":
        try:
            t = obj["spec"]["type"]
            if t == "LoadBalancer":
                obj["spec"]["type"] = "ClusterIP"
        except KeyError:
            pass


# Set a resource alias for a previous name.
def alias(obj, opts):
    if obj["kind"] == "Deployment":
        opts.aliases = ["oldName"]


# Omit a resource from the Chart by transforming the specified resource definition to an empty List.
def omit_resource(obj, opts):
    if obj["kind"] == "Pod" and obj["metadata"]["name"] == "test":
        obj["apiVersion"] = "v1"
        obj["kind"] = "List"


example = ConfigGroup(
    "example",
    files=["foo.yaml"],
    transformations=[make_service_private, alias, omit_resource],
)
package main

import (
	"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/yaml"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := yaml.NewConfigGroup(ctx, "example",
			&yaml.ConfigGroupArgs{
				Files: []string{"foo.yaml"},
				Transformations: []yaml.Transformation{
					// Make every service private to the cluster, i.e., turn all services into ClusterIP
					// instead of LoadBalancer.
					func(state map[string]interface{}, opts ...pulumi.ResourceOption) {
						if state["kind"] == "Service" {
							spec := state["spec"].(map[string]interface{})
							spec["type"] = "ClusterIP"
						}
					},

					// Set a resource alias for a previous name.
					func(state map[string]interface{}, opts ...pulumi.ResourceOption) {
						if state["kind"] == "Deployment" {
							aliases := pulumi.Aliases([]pulumi.Alias{
								{
									Name: pulumi.String("oldName"),
								},
							})
							opts = append(opts, aliases)
						}
					},

					// Omit a resource from the Chart by transforming the specified resource definition
					// to an empty List.
					func(state map[string]interface{}, opts ...pulumi.ResourceOption) {
						name := state["metadata"].(map[string]interface{})["name"]
						if state["kind"] == "Pod" && name == "test" {
							state["apiVersion"] = "core/v1"
							state["kind"] = "List"
						}
					},
				},
			},
		)
		if err != nil {
			return err
		}

		return nil
	})
}
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using Pulumi;
using Pulumi.Kubernetes.Yaml;

class YamlStack : Stack
{
    public YamlStack()
    {
        var helloWorld = new ConfigGroup("example", new ConfigGroupArgs
        {
            Files = new[] { "foo.yaml" },
            Transformations =
               {
                   LoadBalancerToClusterIP,
                   ResourceAlias,
                   OmitTestPod,
               }
        });

        // Make every service private to the cluster, i.e., turn all services into ClusterIP instead of LoadBalancer.
        ImmutableDictionary<string, object> LoadBalancerToClusterIP(ImmutableDictionary<string, object> obj, CustomResourceOptions opts)
        {
            if ((string)obj["kind"] == "Service" && (string)obj["apiVersion"] == "v1")
            {
                var spec = (ImmutableDictionary<string, object>)obj["spec"];
                if (spec != null && (string)spec["type"] == "LoadBalancer")
                {
                    return obj.SetItem("spec", spec.SetItem("type", "ClusterIP"));
                }
            }

            return obj;
        }

        // Set a resource alias for a previous name.
        ImmutableDictionary<string, object> ResourceAlias(ImmutableDictionary<string, object> obj, CustomResourceOptions opts)
        {
            if ((string)obj["kind"] == "Deployment")
            {
                opts.Aliases.Add(new Alias { Name = "oldName" });
            }

            return obj;
        }

        // Omit a resource from the Chart by transforming the specified resource definition to an empty List.
        ImmutableDictionary<string, object> OmitTestPod(ImmutableDictionary<string, object> obj, CustomResourceOptions opts)
        {
            var metadata = (ImmutableDictionary<string, object>)obj["metadata"];
            if ((string)obj["kind"] == "Pod" && (string)metadata["name"] == "test")
            {
                return new Dictionary<string, object>
                {
                    ["apiVersion"] = "v1",
                    ["kind"] = "List",
                    ["items"] = new Dictionary<string, object>(),
                }.ToImmutableDictionary();
            }

            return obj;
        }
    }
}

Each transformation receives the resource object (obj) and resource options (opts). The function can modify properties, set aliases, or convert resources to empty Lists to skip them. Transformations run in order, allowing you to chain modifications. The example shows three common patterns: changing Service types, setting resource aliases, and filtering out specific resources.

Beyond these examples

These snippets focus on specific ConfigGroup features: file and pattern-based YAML loading, inline YAML string definitions, and runtime resource transformations. They’re intentionally minimal rather than full Kubernetes deployments.

The examples assume pre-existing infrastructure such as a Kubernetes cluster with configured access, and YAML manifest files for file-based examples. They focus on loading and transforming YAML rather than provisioning cluster infrastructure.

To keep things focused, common ConfigGroup patterns are omitted, including:

  • Resource prefix customization (resourcePrefix)
  • Pre-parsed object definitions (objs property)
  • Namespace targeting and filtering
  • Error handling for malformed YAML

These omissions are intentional: the goal is to illustrate how each ConfigGroup feature is wired, not provide drop-in deployment modules. See the ConfigGroup resource reference for all available configuration options.

Let's deploy Kubernetes Resources from YAML

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Version & Migration
Should I use ConfigGroup v1 or v2?
A newer version is available as kubernetes.yaml/v2.ConfigGroup. Consider migrating to v2 for newer features and improvements.
Loading YAML Resources
What's the difference between files, yaml, and objs?

You have three options for loading Kubernetes resources:

  1. files - Paths or URLs to YAML files (supports single files, arrays, or glob patterns)
  2. yaml - Literal YAML strings for inline definitions
  3. objs - Objects representing Kubernetes resources
Can I use glob patterns to load multiple YAML files?
Yes, use patterns like yaml/*.yaml for a single pattern, or ["foo/*.yaml", "bar/*.yaml"] for multiple patterns.
Can I combine files, patterns, and inline YAML?
Yes, the schema states you can use any combination of files, patterns, or YAML strings.
Resource Transformations
What are transformations and when should I use them?
Transformations are functions that modify Kubernetes resource definitions before registering with the engine. Use them to change resource properties, set aliases, or omit resources.
How do I convert LoadBalancer services to ClusterIP?
Use a transformation that checks if obj.kind is Service and obj.spec.type is LoadBalancer, then change obj.spec.type to ClusterIP.
How do I set resource aliases for previous names?
Use a transformation that sets opts.aliases to an array containing the old name (e.g., [{ name: "oldName" }]).
How do I skip or omit specific resources from being created?
Use a transformation that converts the resource to an empty List by setting obj.apiVersion to v1 and obj.kind to List.
Resource Naming
Can I customize the names of auto-generated resources?
Yes, use resourcePrefix to add a prefix to auto-generated names. For example, resourcePrefix="foo" produces resource names like foo-resourceName.