---
title: Create a component
url: /docs/iac/get-started/kubernetes/create-component/
---


## Create a component

[**Components**](/docs/iac/concepts/components/) are infrastructure abstractions that encapsulate
complexity and enable sharing and reuse. Instead of copy-pasting common patterns, you can encode them as components.

You will now create your first component that packages up your Kubernetes NGINX deployment so you can stamp out
entire NGINX services in a few lines of code:

<!-- chooser: language -->
<!-- option: typescript -->

```typescript
const nginx = new KubernetesNginxService("my-nginx", {
    isMinikube: config.requireBoolean("isMinikube")
});
export const ip = nginx.ip;
```

<!-- /option -->

<!-- option: python -->

```python
nginx = KubernetesNginxService('my-nginx', is_minikube=config.require_bool("isMinikube"))
pulumi.export("ip", nginx.ip)
```

<!-- /option -->

<!-- option: go -->

```go
nginx, err := NewKubernetesNginxService(ctx, "my-nginx", KubernetesNginxServiceArgs{
    IsMinikube: config.RequireBool(ctx, "isMinikube"),
})
if err != nil {
    return err
}
ctx.Export("ip", nginx.Ip)
```

<!-- /option -->

<!-- option: csharp -->

```csharp
var nginx = new KubernetesNginxService("my-nginx", new KubernetesNginxServiceArgs()
{
    IsMinikube = config.RequireBoolean("isMinikube")
});

return new Dictionary<string, object?>
{
    ["ip"] = nginx.Ip
};
```

<!-- /option -->

<!-- option: java -->

```java
var nginx = new KubernetesNginxService("my-nginx",
    new KubernetesNginxServiceArgs(config.requireBoolean("isMinikube")));
ctx.export("ip", nginx.ip);
```

<!-- /option -->

<!-- option: yaml -->

> **Warning:** Unfortunately, YAML lacks the language facilities to author components. Feel free to [skip ahead](/docs/iac/get-started/kubernetes/destroy-stack/).

<!-- /option -->

<!-- option: hcl -->

```hcl
module "my-nginx" {
  source     = "./website"
  isMinikube = var.isMinikube
}

output "ip" {
  value = module.my-nginx.ip
}
```

<!-- /option -->
<!-- /chooser -->

Using components here also has the benefit that, as the requirements for your NGINX service change, you can
update the one component definition and have all uses of it benefit.

### Define a new component

<!-- chooser: language -->
<!-- option: typescript -->

To define a new component, create a class called `KubernetesNginxService` that derives from `ComponentResource`. Its constructor
starts out empty except for the base call, and you will add the Kubernetes resources to it in the next step. You'll also define the
component's inputs -- the `isMinikube` flag that determines the service type -- and its outputs -- a single property with the service `ip`.

<!-- /option -->

<!-- option: hcl -->

In HCL a component is a **module**: a directory of `.tf` files that a program instantiates with a `module` block. It starts out
with only its input declared, and you will add the Kubernetes resources to it in the next step. The module's `variable` blocks
declare the inputs for the component -- the `isMinikube` flag to determine service type -- and its `output` blocks declare
the outputs -- a single service `ip`.

<!-- /option -->
<!-- /chooser -->

To get going, create a new file <!-- chooser: language -->
<!-- option: javascript -->
`website.js`
<!-- /option -->
<!-- option: typescript -->
`website.ts`
<!-- /option -->
<!-- option: python -->
`website.py`
<!-- /option -->
<!-- option: go -->
`website.go`
<!-- /option -->
<!-- option: csharp -->
`Website.cs`
<!-- /option -->
<!-- option: java -->
`Website.java`
<!-- /option -->
<!-- /chooser -->
 alongside <!-- chooser: language -->
<!-- option: javascript -->
`index.js`
<!-- /option -->
<!-- option: typescript -->
`index.ts`
<!-- /option -->
<!-- option: python -->
`__main__.py`
<!-- /option -->
<!-- option: go -->
`main.go`
<!-- /option -->
<!-- option: csharp -->
`Program.cs`
<!-- /option -->
<!-- option: fsharp -->
`Program.fs`
<!-- /option -->
<!-- option: visualbasic -->
`Program.vb`
<!-- /option -->
<!-- option: java -->
`App.java`
<!-- /option -->
<!-- option: yaml -->
`Pulumi.yaml`
<!-- /option -->
<!-- option: hcl -->
`main.tf`
<!-- /option -->
<!-- /chooser -->
 and add the following:

<!-- chooser: language -->
<!-- option: typescript -->

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

// Arguments for the Kubernetes NGINX service component.
export interface KubernetesNginxServiceArgs {
    isMinikube: boolean; // whether running on minikube.
}

// A component that encapsulates creating a Kubernetes NGINX deployment and service.
export class KubernetesNginxService extends pulumi.ComponentResource {
    public readonly ip: pulumi.Output<string>; // the service ip.

    constructor(name: string, args: KubernetesNginxServiceArgs, opts?: pulumi.ComponentResourceOptions) {
        super("quickstart:index:KubernetesNginxService", name, args, opts);

        // Component initialization will go here next...

        this.registerOutputs({}); // Signal component completion.
    }
}
```

<!-- /option -->

<!-- option: python -->

```python
import pulumi
from pulumi_kubernetes.apps.v1 import Deployment
from pulumi_kubernetes.core.v1 import Service

# A component that encapsulates creating a Kubernetes NGINX deployment and service.
class KubernetesNginxService(pulumi.ComponentResource):
    def __init__(self, name: str, is_minikube: bool = False, opts = None):
        super().__init__('quickstart:index:KubernetesNginxService', name, { 'isMinikube': is_minikube }, opts)

        # Component initialization will go here next...

        self.register_outputs({}) # Signal component completion.
```

<!-- /option -->

<!-- option: go -->

```go
package main

import (
	appsv1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/apps/v1"
	corev1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1"
	metav1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/meta/v1"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

type KubernetesNginxService struct {
    pulumi.ResourceState
    Ip pulumi.StringOutput // the service ip.
}

type KubernetesNginxServiceArgs struct {
    IsMinikube bool // whether running on minikube.
}

func NewKubernetesNginxService(ctx *pulumi.Context, name string, args KubernetesNginxServiceArgs, opts ...pulumi.ResourceOption) (*KubernetesNginxService, error) {
    self := &KubernetesNginxService{}
    err := ctx.RegisterComponentResource("quickstart:index:KubernetesNginxService", name, self, opts...)
    if err != nil {
        return nil, err
    }

    // Component initialization will go here next...

    ctx.RegisterResourceOutputs(self, pulumi.Map{}) // Signal component completion.
    return self, nil
}
```

<!-- /option -->

<!-- option: csharp -->

```csharp
using Pulumi;
using Pulumi.Kubernetes.Core.V1;
using Pulumi.Kubernetes.Types.Inputs.Core.V1;
using Pulumi.Kubernetes.Types.Inputs.Apps.V1;
using Pulumi.Kubernetes.Types.Inputs.Meta.V1;
using System.Collections.Generic;

public class KubernetesNginxServiceArgs
{
    public bool IsMinikube { get; set; }
}

public class KubernetesNginxService : Pulumi.ComponentResource
{
    public Output<string> Ip { get; private set; }

    public KubernetesNginxService(string name, KubernetesNginxServiceArgs args, ComponentResourceOptions? opts = null)
        : base("quickstart:index:KubernetesNginxService", name, opts)
    {
        // Component initialization will go here next...

        this.RegisterOutputs(new Dictionary<string, object>{}); // Signal component completion.
    }
}
```

<!-- /option -->

<!-- option: java -->

```java
package myproject;

import com.pulumi.resources.ComponentResource;
import com.pulumi.resources.ComponentResourceOptions;
import com.pulumi.core.Output;

public class KubernetesNginxServiceArgs {
    public boolean isMinikube;
    public KubernetesNginxServiceArgs(boolean isMinikube) {
        this.isMinikube = isMinikube;
    }
}

public class KubernetesNginxService extends ComponentResource {
    public Output<String> ip;

    public KubernetesNginxService(String name, KubernetesNginxServiceArgs args, ComponentResourceOptions opts) {
        super("quickstart:index:KubernetesNginxService", name, args, opts);

        // Component initialization will go here next...

        this.registerOutputs(java.util.Map.of());
    }
}
```

<!-- /option -->

<!-- option: yaml -->

> **Warning:** Unfortunately, YAML lacks the language facilities to author components. Feel free to [skip ahead](/docs/iac/get-started/kubernetes/destroy-stack/).

<!-- /option -->

<!-- option: hcl -->

```hcl
terraform {
  required_providers {
    kubernetes = {
      source = "pulumi/kubernetes"
    }
  }
}

# Whether the service is running on minikube.
variable "isMinikube" {
  type = bool
}

# The Kubernetes resources and the service ip output will go here next...
```

<!-- /option -->
<!-- /chooser -->

This defines a component but it doesn't do much yet.

### Refactor your code into the component

<!-- chooser: language -->
<!-- option: typescript -->

Next, make three changes:

1. Move all resources from <!-- chooser: language -->
<!-- option: javascript -->
`index.js`
<!-- /option -->
<!-- option: typescript -->
`index.ts`
<!-- /option -->
<!-- option: python -->
`__main__.py`
<!-- /option -->
<!-- option: go -->
`main.go`
<!-- /option -->
<!-- option: csharp -->
`Program.cs`
<!-- /option -->
<!-- option: fsharp -->
`Program.fs`
<!-- /option -->
<!-- option: visualbasic -->
`Program.vb`
<!-- /option -->
<!-- option: java -->
`App.java`
<!-- /option -->
<!-- option: yaml -->
`Pulumi.yaml`
<!-- /option -->
<!-- option: hcl -->
`main.tf`
<!-- /option -->
<!-- /chooser -->
 into the component's constructor
2. Change each resource to use the component [as the `parent`](/docs/iac/concepts/resources/options/parent/)
3. Assign the service output to the `ip` property of the component

<!-- /option -->

<!-- option: hcl -->

Next, make two changes:

1. Move all resources from <!-- chooser: language -->
<!-- option: javascript -->
`index.js`
<!-- /option -->
<!-- option: typescript -->
`index.ts`
<!-- /option -->
<!-- option: python -->
`__main__.py`
<!-- /option -->
<!-- option: go -->
`main.go`
<!-- /option -->
<!-- option: csharp -->
`Program.cs`
<!-- /option -->
<!-- option: fsharp -->
`Program.fs`
<!-- /option -->
<!-- option: visualbasic -->
`Program.vb`
<!-- /option -->
<!-- option: java -->
`App.java`
<!-- /option -->
<!-- option: yaml -->
`Pulumi.yaml`
<!-- /option -->
<!-- option: hcl -->
`main.tf`
<!-- /option -->
<!-- /chooser -->
 into the module
1. Return the service address as the module's `ip` output

Nothing needs reparenting: everything a module declares is automatically a child of the component resource that
Pulumi creates for the module instance.

<!-- /option -->
<!-- /chooser -->

The resulting <!-- chooser: language -->
<!-- option: javascript -->
`website.js`
<!-- /option -->
<!-- option: typescript -->
`website.ts`
<!-- /option -->
<!-- option: python -->
`website.py`
<!-- /option -->
<!-- option: go -->
`website.go`
<!-- /option -->
<!-- option: csharp -->
`Website.cs`
<!-- /option -->
<!-- option: java -->
`Website.java`
<!-- /option -->
<!-- /chooser -->
 file will look like this; you can make each edit one at a time if preferred
to get a feel for things, or paste the contents of this into <!-- chooser: language -->
<!-- option: javascript -->
`website.js`
<!-- /option -->
<!-- option: typescript -->
`website.ts`
<!-- /option -->
<!-- option: python -->
`website.py`
<!-- /option -->
<!-- option: go -->
`website.go`
<!-- /option -->
<!-- option: csharp -->
`Website.cs`
<!-- /option -->
<!-- option: java -->
`Website.java`
<!-- /option -->
<!-- /chooser -->
:

<!-- chooser: language -->
<!-- option: typescript -->

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

// Arguments for the Kubernetes NGINX service component.
export interface KubernetesNginxServiceArgs {
    isMinikube: boolean; // whether running on minikube.
}

// A component that encapsulates creating a Kubernetes NGINX deployment and service.
export class KubernetesNginxService extends pulumi.ComponentResource {
    public readonly ip: pulumi.Output<string>; // the service ip.

    constructor(name: string, args: KubernetesNginxServiceArgs, opts?: pulumi.ComponentResourceOptions) {
        super("quickstart:index:KubernetesNginxService", name, args, opts);

        const appName = "nginx";
        const appLabels = { app: appName };
        const deployment = new k8s.apps.v1.Deployment(appName, {
            spec: {
                selector: { matchLabels: appLabels },
                replicas: 1,
                template: {
                    metadata: { labels: appLabels },
                    spec: { containers: [{ name: appName, image: "nginx" }] }
                }
            }
        }, {
            parent: this,
        });

        // Allocate an IP to the Deployment.
        const frontend = new k8s.core.v1.Service(appName, {
            metadata: { labels: deployment.spec.template.metadata.labels },
            spec: {
                type: args.isMinikube ? "ClusterIP" : "LoadBalancer",
                ports: [{ port: 80, targetPort: 80, protocol: "TCP" }],
                selector: appLabels
            }
        }, {
            parent: this,
        });

        // Capture the IP and make it available as a component property and output:
        this.ip = args.isMinikube
            ? frontend.spec.clusterIP
            : frontend.status.loadBalancer.apply(
                  (lb) => lb.ingress[0].ip || lb.ingress[0].hostname
              );
        this.registerOutputs({ ip: this.ip }); // Signal component completion.
    }
}
```

<!-- /option -->

<!-- option: python -->

```python
import pulumi
from pulumi_kubernetes.apps.v1 import Deployment
from pulumi_kubernetes.core.v1 import Service

# A component that encapsulates creating a Kubernetes NGINX deployment and service.
class KubernetesNginxService(pulumi.ComponentResource):
    def __init__(self, name: str, is_minikube: bool = False, opts = None):
        super().__init__('quickstart:index:KubernetesNginxService', name, { 'isMinikube': is_minikube }, opts)

        app_name = "nginx"
        app_labels = { "app": app_name }

        deployment = Deployment(
            app_name,
            spec={
                "selector": { "match_labels": app_labels },
                "replicas": 1,
                "template": {
                    "metadata": { "labels": app_labels },
                    "spec": { "containers": [{ "name": app_name, "image": "nginx" }] }
                }
            },
            opts=pulumi.ResourceOptions(parent=self))

        # Allocate an IP to the Deployment.
        frontend = Service(
            app_name,
            metadata={
                "labels": deployment.spec["template"]["metadata"]["labels"],
            },
            spec={
                "type": "ClusterIP" if is_minikube else "LoadBalancer",
                "ports": [{ "port": 80, "target_port": 80, "protocol": "TCP" }],
                "selector": app_labels,
            },
            opts=pulumi.ResourceOptions(parent=self))

        # Capture the IP and make it available as a component property and output
        result = None
        if is_minikube:
            result = frontend.spec.apply(lambda v: v["cluster_ip"] if "cluster_ip" in v else None)
        else:
            ingress = frontend.status.load_balancer.apply(lambda v: v["ingress"][0] if "ingress" in v else "output<string>")
            result = ingress.apply(lambda v: v["ip"] if v and "ip" in v else (v["hostname"] if v and "hostname" in v else "output<string>"))

        self.ip = result
        self.register_outputs({ 'ip': self.ip }) # Signal component completion.
```

<!-- /option -->

<!-- option: go -->

```go
package main

import (
	appsv1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/apps/v1"
	corev1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1"
	metav1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/meta/v1"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

type KubernetesNginxService struct {
    pulumi.ResourceState
    Ip pulumi.StringOutput // the service ip.
}

type KubernetesNginxServiceArgs struct {
    IsMinikube bool // whether running on minikube.
}

func NewKubernetesNginxService(ctx *pulumi.Context, name string, args KubernetesNginxServiceArgs, opts ...pulumi.ResourceOption) (*KubernetesNginxService, error) {
    self := &KubernetesNginxService{}
    err := ctx.RegisterComponentResource("quickstart:index:KubernetesNginxService", name, self, opts...)
    if err != nil {
        return nil, err
    }

    appName := "nginx"
    appLabels := pulumi.StringMap{
        "app": pulumi.String(appName),
    }
    deployment, err := appsv1.NewDeployment(ctx, appName, &appsv1.DeploymentArgs{
        Spec: appsv1.DeploymentSpecArgs{
            Selector: &metav1.LabelSelectorArgs{
                MatchLabels: appLabels,
            },
            Replicas: pulumi.Int(1),
            Template: &corev1.PodTemplateSpecArgs{
                Metadata: &metav1.ObjectMetaArgs{
                    Labels: appLabels,
                },
                Spec: &corev1.PodSpecArgs{
                    Containers: corev1.ContainerArray{
                        corev1.ContainerArgs{
                            Name:  pulumi.String("nginx"),
                            Image: pulumi.String("nginx"),
                        }},
                },
            },
        },
    }, pulumi.Parent(self))
    if err != nil {
        return nil, err
    }

    feType := "LoadBalancer"
    if args.IsMinikube {
        feType = "ClusterIP"
    }

    template := deployment.Spec.ApplyT(func(v appsv1.DeploymentSpec) *corev1.PodTemplateSpec {
        return &v.Template
    }).(corev1.PodTemplateSpecPtrOutput)

    meta := template.ApplyT(func(v *corev1.PodTemplateSpec) *metav1.ObjectMeta { return v.Metadata }).(metav1.ObjectMetaPtrOutput)

    frontend, _ := corev1.NewService(ctx, appName, &corev1.ServiceArgs{
        Metadata: meta,
        Spec: &corev1.ServiceSpecArgs{
            Type: pulumi.String(feType),
            Ports: &corev1.ServicePortArray{
                &corev1.ServicePortArgs{
                    Port:       pulumi.Int(80),
                    TargetPort: pulumi.Int(80),
                    Protocol:   pulumi.String("TCP"),
                },
            },
            Selector: appLabels,
        },
    }, pulumi.Parent(self))

    var ip pulumi.StringOutput

    if args.IsMinikube {
        ip = frontend.Spec.ApplyT(func(val corev1.ServiceSpec) string {
            if val.ClusterIP != nil {
                return *val.ClusterIP
            }
            return ""
        }).(pulumi.StringOutput)
    } else {
      ip = frontend.Status.ApplyT(func(val *corev1.ServiceStatus) string {
          if val.LoadBalancer.Ingress != nil && len(val.LoadBalancer.Ingress) > 0 {
              ingress := val.LoadBalancer.Ingress[0]
              if ingress.Ip != nil {
                  return *ingress.Ip
              }
              if ingress.Hostname != nil {
                  return *ingress.Hostname
              }
          }
          return ""
      }).(pulumi.StringOutput)
    }

    self.Ip = ip
    ctx.RegisterResourceOutputs(self, pulumi.Map{"ip": ip}) // Signal component completion.
    return self, nil
}
```

<!-- /option -->

<!-- option: csharp -->

```csharp
using Pulumi;
using Pulumi.Kubernetes.Core.V1;
using Pulumi.Kubernetes.Types.Inputs.Core.V1;
using Pulumi.Kubernetes.Types.Inputs.Apps.V1;
using Pulumi.Kubernetes.Types.Inputs.Meta.V1;
using System.Collections.Generic;

public class KubernetesNginxServiceArgs
{
    public bool IsMinikube { get; set; }
}

public class KubernetesNginxService : Pulumi.ComponentResource
{
    public Output<string> Ip { get; private set; }

    public KubernetesNginxService(string name, KubernetesNginxServiceArgs args, ComponentResourceOptions? opts = null)
        : base("quickstart:index:KubernetesNginxService", name, opts)
    {
        var appName = "nginx";
        var appLabels = new InputMap<string>
        {
            { "app", appName },
        };

        var deployment = new Pulumi.Kubernetes.Apps.V1.Deployment(appName, new DeploymentArgs
        {
            Spec = new DeploymentSpecArgs
            {
                Selector = new LabelSelectorArgs
                {
                    MatchLabels = appLabels,
                },
                Replicas = 1,
                Template = new PodTemplateSpecArgs
                {
                    Metadata = new ObjectMetaArgs
                    {
                        Labels = appLabels,
                    },
                    Spec = new PodSpecArgs
                    {
                        Containers =
                        {
                            new ContainerArgs
                            {
                                Name = appName,
                                Image = "nginx",
                                Ports =
                                {
                                    new ContainerPortArgs
                                    {
                                        ContainerPortValue = 80
                                    },
                                },
                            },
                        },
                    },
                },
            },
        }, new CustomResourceOptions
        {
            Parent = this,
        });

        var frontend = new Service(appName, new ServiceArgs
        {
            Metadata = new ObjectMetaArgs
            {
                Labels = deployment.Spec.Apply(spec =>
                    spec.Template.Metadata.Labels
                ),
            },
            Spec = new ServiceSpecArgs
            {
                Type = args.IsMinikube
                    ? "ClusterIP"
                    : "LoadBalancer",
                Selector = appLabels,
                Ports = new ServicePortArgs
                {
                    Port = 80,
                    TargetPort = 80,
                    Protocol = "TCP",
                },
            }
        }, new CustomResourceOptions
        {
            Parent = this,
        });

        this.Ip = args.IsMinikube
            ? frontend.Spec.Apply(spec => spec.ClusterIP)
            : frontend.Status.Apply(status =>
            {
                var ingress = status.LoadBalancer.Ingress[0];
                return ingress.Ip ?? ingress.Hostname;
            });

        this.RegisterOutputs(new Dictionary<string, object?>{
            ["ip"] = this.Ip
        });
    }
}
```

<!-- /option -->

<!-- option: java -->

```java
package myproject;

import com.pulumi.*;
import com.pulumi.core.*;
import com.pulumi.resources.*;

import com.pulumi.kubernetes.apps.v1.Deployment;
import com.pulumi.kubernetes.apps.v1.DeploymentArgs;
import com.pulumi.kubernetes.apps.v1.inputs.DeploymentSpecArgs;
import com.pulumi.kubernetes.core.v1.*;
import com.pulumi.kubernetes.core.v1.ServiceArgs;
import com.pulumi.kubernetes.core.v1.enums.ServiceSpecType;
import com.pulumi.kubernetes.core.v1.inputs.*;
import com.pulumi.kubernetes.meta.v1.inputs.LabelSelectorArgs;
import com.pulumi.kubernetes.meta.v1.inputs.ObjectMetaArgs;
import java.util.Map;

class KubernetesNginxServiceArgs extends ResourceArgs {
    public boolean isMinikube;
    public KubernetesNginxServiceArgs(boolean isMinikube) {
        this.isMinikube = isMinikube;
    }
}

class KubernetesNginxService extends ComponentResource {
    public Output<String> ip;

    public KubernetesNginxService(String name, KubernetesNginxServiceArgs args) {
        this(name, args, null);
    }

    public KubernetesNginxService(String name, KubernetesNginxServiceArgs args, ComponentResourceOptions opts) {
        super("quickstart:index:KubernetesNginxService", name, args, opts);

        var labels = Map.of("app", "nginx");

        var deployment = new Deployment("nginx", DeploymentArgs.builder()
            .spec(DeploymentSpecArgs.builder()
                .selector(LabelSelectorArgs.builder()
                    .matchLabels(labels)
                    .build())
                .replicas(1)
                .template(PodTemplateSpecArgs.builder()
                    .metadata(ObjectMetaArgs.builder()
                        .labels(labels)
                        .build())
                    .spec(PodSpecArgs.builder()
                        .containers(ContainerArgs.builder()
                            .name("nginx")
                            .image("nginx")
                            .ports(ContainerPortArgs.builder()
                                .containerPort(80)
                                .build())
                            .build())
                        .build())
                    .build())
                .build())
            .build(), CustomResourceOptions.builder().parent(this).build());

        var frontend = new Service("nginx", ServiceArgs.builder()
            .metadata(ObjectMetaArgs.builder()
                .labels(labels)
                .build())
            .spec(ServiceSpecArgs.builder()
                .type(args.isMinikube ? ServiceSpecType.ClusterIP : ServiceSpecType.LoadBalancer)
                .selector(labels)
                .ports(ServicePortArgs.builder()
                    .port(80)
                    .targetPort(80)
                    .protocol("TCP")
                    .build())
                .build())
            .build(), CustomResourceOptions.builder().parent(this).build());

        // Export the service cluster IP (available for both ClusterIP and LoadBalancer types)
        this.ip = frontend.spec().applyValue(spec -> spec.clusterIP().orElse("pending"));
        this.registerOutputs(Map.of("ip", this.ip));
    }
}
```

<!-- /option -->

<!-- option: yaml -->

> **Warning:** Unfortunately, YAML lacks the language facilities to author components. Feel free to [skip ahead](/docs/iac/get-started/kubernetes/destroy-stack/).

<!-- /option -->

<!-- option: hcl -->

```hcl
terraform {
  required_providers {
    kubernetes = {
      source = "pulumi/kubernetes"
    }
  }
}

# Whether the service is running on minikube.
variable "isMinikube" {
  type = bool
}

locals {
  app_labels = {
    app = "nginx"
  }
}

resource "kubernetes_apps_v1_deployment" "nginx" {
  spec = {
    selector = {
      match_labels = local.app_labels
    }
    replicas = 1
    template = {
      metadata = {
        labels = local.app_labels
      }
      spec = {
        containers = [{
          name  = "nginx"
          image = "nginx"
        }]
      }
    }
  }

  # A module prefixes the Pulumi names of the resources it contains, so set each
  # name explicitly (here and below) to keep the names free of that prefix.
  pulumi {
    name = "nginx"
  }
}

# Allocate an IP to the Deployment.
resource "kubernetes_core_v1_service" "nginx" {
  metadata = {
    labels = local.app_labels
  }
  spec = {
    type     = var.isMinikube ? "ClusterIP" : "LoadBalancer"
    selector = local.app_labels
    ports = [{
      port        = 80
      target_port = 80
      protocol    = "TCP"
    }]
  }

  pulumi {
    name = "nginx"
  }
}

# When the service is a LoadBalancer, capture its external address; with minikube,
# capture the cluster IP for use with port forwarding.
output "ip" {
  value = var.isMinikube ? kubernetes_core_v1_service.nginx.spec.cluster_ip : coalesce(
    kubernetes_core_v1_service.nginx.status.load_balancer.ingress[0].ip,
    kubernetes_core_v1_service.nginx.status.load_balancer.ingress[0].hostname,
  )
}
```

Naming the service explicitly matters here: Kubernetes service names must be DNS labels, and the module-prefixed
name would contain a dot.

<!-- /option -->
<!-- /chooser -->

### Instantiate the component

Now go back to your original file <!-- chooser: language -->
<!-- option: javascript -->
`index.js`
<!-- /option -->
<!-- option: typescript -->
`index.ts`
<!-- /option -->
<!-- option: python -->
`__main__.py`
<!-- /option -->
<!-- option: go -->
`main.go`
<!-- /option -->
<!-- option: csharp -->
`Program.cs`
<!-- /option -->
<!-- option: fsharp -->
`Program.fs`
<!-- /option -->
<!-- option: visualbasic -->
`Program.vb`
<!-- /option -->
<!-- option: java -->
`App.java`
<!-- /option -->
<!-- option: yaml -->
`Pulumi.yaml`
<!-- /option -->
<!-- option: hcl -->
`main.tf`
<!-- /option -->
<!-- /chooser -->
. The resources have moved into the component, so start that file over from scratch:
delete everything still in it, then build it back up by importing and instantiating the new component.

Add this to your now-empty <!-- chooser: language -->
<!-- option: javascript -->
`index.js`
<!-- /option -->
<!-- option: typescript -->
`index.ts`
<!-- /option -->
<!-- option: python -->
`__main__.py`
<!-- /option -->
<!-- option: go -->
`main.go`
<!-- /option -->
<!-- option: csharp -->
`Program.cs`
<!-- /option -->
<!-- option: fsharp -->
`Program.fs`
<!-- /option -->
<!-- option: visualbasic -->
`Program.vb`
<!-- /option -->
<!-- option: java -->
`App.java`
<!-- /option -->
<!-- option: yaml -->
`Pulumi.yaml`
<!-- /option -->
<!-- option: hcl -->
`main.tf`
<!-- /option -->
<!-- /chooser -->
:

<!-- chooser: language -->
<!-- option: typescript -->

```typescript
import * as pulumi from "@pulumi/pulumi";

// Import from the new component module:
import { KubernetesNginxService } from "./website";

// Read the configuration value:
const config = new pulumi.Config();

// Create an instance of the component:
const nginx = new KubernetesNginxService("my-nginx", {
    isMinikube: config.requireBoolean("isMinikube")
});

// And export its autoassigned IP:
export const ip = nginx.ip;
```

<!-- /option -->

<!-- option: python -->

```python
import pulumi

# Import from the new component module:
from website import KubernetesNginxService

# Read the configuration value:
config = pulumi.Config()

# Create an instance of the component:
nginx = KubernetesNginxService('my-nginx', is_minikube=config.require_bool("isMinikube"))

# And export its autoassigned IP:
pulumi.export("ip", nginx.ip)
```

<!-- /option -->

<!-- option: go -->

```go
package main

import (
    "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    "github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
)

func main() {
    pulumi.Run(func(ctx *pulumi.Context) error {
        // Read the configuration value:
        isMinikube := config.RequireBool(ctx, "isMinikube")

        // Create an instance of the component:
        nginx, err := NewKubernetesNginxService(ctx, "my-nginx", KubernetesNginxServiceArgs{
            IsMinikube: isMinikube,
        })
        if err != nil {
            return err
        }

        // And export its autoassigned IP:
        ctx.Export("ip", nginx.Ip)
        return nil
    })
}

```

<!-- /option -->

<!-- option: csharp -->

```csharp
using Pulumi;
using System.Collections.Generic;

return await Pulumi.Deployment.RunAsync(() =>
{
    // Read the configuration value:
    var config = new Pulumi.Config();

    // Create an instance of the component:
    var nginx = new KubernetesNginxService("my-nginx", new KubernetesNginxServiceArgs()
    {
        IsMinikube = config.RequireBoolean("isMinikube")
    });

   // And export its autoassigned IP:
   return new Dictionary<string, object?>
   {
      ["ip"] = nginx.Ip
   };
});
```

<!-- /option -->

<!-- option: java -->

```java
package myproject;

import com.pulumi.Pulumi;

public class App {
    public static void main(String[] args) {
        Pulumi.run(ctx -> {
            // Read the configuration value:
            var config = ctx.config();

            // Create an instance of the component:
            var nginx = new KubernetesNginxService("my-nginx",
                new KubernetesNginxServiceArgs(config.requireBoolean("isMinikube")));

            // And export its autoassigned IP:
            ctx.export("ip", nginx.ip);
        });
    }
}
```

<!-- /option -->

<!-- option: yaml -->

> **Warning:** Unfortunately, YAML lacks the language facilities to author components. Feel free to [skip ahead](/docs/iac/get-started/kubernetes/destroy-stack/).

<!-- /option -->

<!-- option: hcl -->

```hcl
# Read the configuration value:
variable "isMinikube" {
  type = bool
}

# Instantiate the new component; the source is the directory the module lives in:
module "my-nginx" {
  source     = "./website"
  isMinikube = var.isMinikube
}

# And export its autoassigned IP:
output "ip" {
  value = module.my-nginx.ip
}
```

<!-- /option -->
<!-- /chooser -->

### Deploy the component

Now deploy the resulting component instantiation. To do so, run `pulumi up` as usual:

<!-- chooser: language -->
<!-- option: typescript -->

```output
$ pulumi up
Previewing update (dev)

     Type                                        Name                  Plan
     pulumi:pulumi:Stack                         quickstart-dev
 +   ├─ quickstart:index:KubernetesNginxService  my-nginx              create
 +   │  ├─ kubernetes:apps/v1:Deployment         nginx                 create
 +   │  └─ kubernetes:core/v1:Service            nginx                 create
 -   ├─ kubernetes:core/v1:Service               nginx                 delete
 -   └─ kubernetes:apps/v1:Deployment            nginx                 delete

Outputs:
  ~ ip: "10.110.183.208" => "10.96.0.0"

Resources:
    + 3 to create
    - 2 to delete
    5 changes. 1 unchanged

Do you want to perform this update?  [Use arrows to move, type to filter]
> yes
  no
  details
```

<!-- /option -->

<!-- option: hcl -->

```output
$ pulumi up
Previewing update (dev)

     Type                                 Name                  Plan
     pulumi:pulumi:Stack                  quickstart-dev
 +   ├─ components:index:Website          my-nginx              create
 +   │  ├─ kubernetes:apps/v1:Deployment  nginx                 create
 +   │  └─ kubernetes:core/v1:Service     nginx                 create
 -   ├─ kubernetes:core/v1:Service        nginx                 delete
 -   └─ kubernetes:apps/v1:Deployment     deployment            delete

Outputs:
  ~ ip: "10.110.183.208" => "10.96.0.0"

Resources:
    + 3 to create
    - 2 to delete
    5 changes. 1 unchanged

Do you want to perform this update?  [Use arrows to move, type to filter]
> yes
  no
  details
```

The `Website` segment of the component's type, `components:index:Website`, is the title-cased name of the
directory the module lives in; the `components:index:` prefix is fixed.

<!-- /option -->
<!-- /chooser -->

This preview shows you a few things. First, you'll see your new NGINX component with all of its child resources neatly parented underneath it, which makes it easy to see which resources belong to which component. Next, you'll see that your old resources are being destroyed.

> **Note:** If you're wondering why Pulumi didn't update the resources in place, it's because certain changes -- like
refactoring resources into a component -- fundamentally change a resource's identity. Most changes, such as updating
properties or moving resources between files, aren't disruptive in this way. When a change does affect identity, you can assign
[aliases](/docs/iac/concepts/resources/options/aliases/) to prevent the deletions.

Accept the changes by selecting `yes` and the deployment will occur:

<!-- chooser: language -->
<!-- option: typescript -->

```output
Updating (dev)

     Type                                        Name                  Status
     pulumi:pulumi:Stack                         quickstart-dev
 +   ├─ quickstart:index:KubernetesNginxService  my-nginx              created (2s)
 +   │  ├─ kubernetes:apps/v1:Deployment         nginx                 created (1s)
 +   │  └─ kubernetes:core/v1:Service            nginx                 created (9s)
 -   ├─ kubernetes:core/v1:Service               nginx                 deleted (4s)
 -   └─ kubernetes:apps/v1:Deployment            nginx                 deleted (0.92s)

Outputs:
  ~ ip: "10.110.183.208" => "10.103.199.118"

Resources:
    + 3 created
    - 2 deleted
    5 changes. 1 unchanged

Duration: 13s
```

<!-- /option -->

<!-- option: hcl -->

```output
Updating (dev)

     Type                                 Name                  Status
     pulumi:pulumi:Stack                  quickstart-dev
 +   ├─ components:index:Website          my-nginx              created (2s)
 +   │  ├─ kubernetes:apps/v1:Deployment  nginx                 created (1s)
 +   │  └─ kubernetes:core/v1:Service     nginx                 created (9s)
 -   ├─ kubernetes:core/v1:Service        nginx                 deleted (4s)
 -   └─ kubernetes:apps/v1:Deployment     deployment            deleted (0.92s)

Outputs:
  ~ ip: "10.110.183.208" => "10.103.199.118"

Resources:
    + 3 created
    - 2 deleted
    5 changes. 1 unchanged

Duration: 13s
```

<!-- /option -->
<!-- /chooser -->

Now test out your new NGINX service -- it works like before, just with a tidier codebase now!

> **Note:** **If using Minikube:** Minikube does not support type `LoadBalancer`. Instead, forward the NGINX service:

```bash
$ kubectl get service
NAME             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes       ClusterIP   10.96.0.1        <none>        443/TCP   44h
nginx-9e5d5cd4   ClusterIP   10.103.199.118   <none>        80/TCP    6m47s
```

The assigned name for this particular nginx service is `nginx-9e5d5cd4`; yours will be different. In a new terminal window, run:

```bash
$ kubectl port-forward service/nginx-9e5d5cd4 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
```

You can curl NGINX to verify it is running:

<!-- chooser: os -->
<!-- option: macos -->

```bash
$ $(pulumi config get isMinikube) && curl "http://localhost:8080" || curl $(pulumi stack output ip)
```

<!-- /option -->

<!-- option: windows -->

```powershell
> if (pulumi config get isMinikube) { curl "http://localhost:8080" } else { curl $(pulumi stack output ip) }
```

<!-- /option -->
<!-- /chooser -->

Expected output:

```html
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```

Once you are ready to move on, [destroy everything you've provisioned](/docs/iac/get-started/kubernetes/destroy-stack/) in this tutorial.

---
[← Previous step](/docs/iac/get-started/kubernetes/modify-program/)
[Next: Destroy stack →](/docs/iac/get-started/kubernetes/destroy-stack/)


