1. Docs
  2. Using Pulumi
  3. Defining and Provisioning Resources

Defining and Provisioning Resources


    In Pulumi, resources represent the fundamental units that make up your infrastructure, such as virtual machines, networks, storage, and databases. A resource is used to define and manage an infrastructure object in your Pulumi configuration.

    In this tutorial, we’ll demonstrate how to create a simple Nginx web server. You will then refer to documentation in the Pulumi Registry to create a security group resource to make the application publically accessible.

    Pre-Requisites

    This tutorial assumes that you are familiar with the basics of the Pulumi workflow. If you are new to Pulumi, complete the Get Started series first.

    Additionally, you will need the following tools to complete this tutorial:

    Let’s get started!

    Create a Virtual Machine

    The first step is to create a virtual machine resource that will be used to host the web server. The specific details of how to create your virtual machine differ by cloud provider. For the purposes of this tutorial, we will be creating our resources in AWS in the us-east-1 region.

    Amazon Elastic Compute Cloud (EC2)

    Amazon Elastic Compute Cloud (EC2) provides managed virtual server hosting that makes it straightforward to run applications in your AWS account. In AWS, a virtual server is referred to as an “instance”. These instances can host a variety of operating systems, tools, and applications, each configured according to your specific requirements.

    Create a New Project

    To start, login to the Pulumi CLI and ensure it is configured to use your AWS account. Next, create a new project, then use the following code snippet to scaffold your project with the required imports and overall program structure that we will fill in as we go along:

    import * as aws from "@pulumi/aws";
    import * as pulumi from "@pulumi/pulumi";
    
    // [Step 1: Create an EC2 instance.]
    
    // [Step 2: Create a security group.]
    
    import pulumi
    import pulumi_aws as aws
    
    # [Step 1: Create an EC2 instance.]
    
    # [Step 2: Create a security group.]
    
    name: aws-ec2-sg-nginx-server-yaml
    runtime: yaml
    description: A program to create an Nginx server on AWS
    
      # [Step 1: Create an EC2 instance.]
    
      # [Step 2: Create a security group.]
    

    Define an EC2 Instance

    The Pulumi Registry provides the documentation for all of the Pulumi providers and their associated resources. Open the aws.ec2.Instance documentation page to view a description of this resource, example usage, the resource definition, and supported properties.

    We will now define our EC2 instance resource below.

    import * as aws from "@pulumi/aws";
    import * as pulumi from "@pulumi/pulumi";
    
    const userData = `
        #!/bin/bash
        sudo yum update -y
        sudo yum upgrade -y
        sudo amazon-linux-extras install nginx1 -y
        sudo systemctl enable nginx
        sudo systemctl start nginx`;
    
    });
    
    // [Step 1: Create an EC2 instance.]
    const server = new aws.ec2.Instance("webserver-www2", {
        instanceType: "t2.micro",
        ami: "ami-09538990a0c4fe9be",
        userData: userData,
    });
    
    export const publicIp = server.publicIp;
    
    import pulumi
    import pulumi_aws as aws
    
    user_data = """
    #!/bin/bash
    sudo yum update -y
    sudo yum upgrade -y
    sudo amazon-linux-extras install nginx1 -y
    sudo systemctl enable nginx
    sudo systemctl start nginx
    """
    
    # [Step 1: Create an EC2 instance.]
    server = aws.ec2.Instance(
        'webserver-www',
        instance_type="t2.micro",
        ami="ami-09538990a0c4fe9be",
        user_data=user_data,
    )
    
    pulumi.export('publicIp', server.public_ip)
    
    name: aws-ec2-sg-nginx-server-yaml
    runtime: yaml
    description: A program to create an Nginx server on AWS
    
    resources:
      # [Step 1: Create an EC2 instance.]
      webserver-www:
        type: aws:ec2:Instance
        properties:
          instanceType: t2.micro
          ami: "ami-09538990a0c4fe9be"
          userData: |
            #!/bin/bash
            sudo yum update -y
            sudo yum upgrade -y
            sudo amazon-linux-extras install nginx1 -y
            sudo systemctl enable nginx
            sudo systemctl start nginx        
    
    outputs:
      publicIp: ${webserver-www.publicIp}
    
    If you are deploying resources in a region other than the us-east-1 region, make sure to replace the AMI ID value with the ID that is specific to your region. Otherwise you may run into an InvalidAMIID.NotFound error.

    All resources have a required name argument. Each resource has both a logical name and a physical name.

    The logical name is how the resource is known inside Pulumi. This is the value provided to the required name argument.

    The physical name is the name used for the resource in the cloud provider that a Pulumi program is deploying to. It is a combination of the logical name plus a random suffix which helps to prevent resource naming collisions.

    In the above example, the logical name for our aws.ec2.Instance resource is “webserver-www”, and the physical name might typically look something like “webserver-www-d7c2fa0”.

    In addition to names, resources have properties and options.

    Properties are used to specify what type of resource to create. Properties are often resource-specific, and they can be required or optional depending on the specifications of the provider.

    The properties inside our aws.ec2.Instance resource are:

    PropertyDescription
    instance_typetells the AWS provider to create an EC2 instance of type/size t2.micro
    amitells the provider to create the instance using the ami-09538990a0c4fe9be machine image
    user_datatells the provider to initialize the instance with the script we have defined

    Options let you control certain aspects of a resource (such as showing explicit dependencies or importing existing infrastructure). We do not have any options defined for this resource, but you can learn more about options in the Pulumi documentation.

    Deploy your EC2 Instance

    Now let’s run the pulumi up command to preview and deploy the resource we just defined in our project.

    Previewing update (webserver-dev):
    
         Type                      Name                     Plan
     +   pulumi:pulumi:Stack       myproject-webserver-dev  create
     +   └─ aws:ec2:Instance       webserver-www            create
    
    Resources:
        + 2 to create
    
    Do you want to perform this update? yes
    Updating (webserver-dev):
    
         Type                      Name                     Status
     +   pulumi:pulumi:Stack       myproject-webserver-dev  created
     +   └─ aws:ec2:Instance       webserver-www            created
    
    Outputs:
        publicIp      : "34.217.110.29"
    
    Resources:
        + 2 created
    
    Duration: 17s
    

    The public IP address of your instance has been provided for you as an output, and you can use this to access your web server. However, if you try to visit this address, your request will eventually time out. This is because we have not yet configured web traffic access for our instance. We will do this by creating our security group resource.

    Create a Security Group

    In this section, you will use Pulumi documentation to configure the security group on your own. The security group must allow web traffic on port 80 in order for you to access your web server. An updated version of the project code has been provided below as a starting point.

    import * as aws from "@pulumi/aws";
    import * as pulumi from "@pulumi/pulumi";
    
    const userData = `
        #!/bin/bash
        sudo yum update -y
        sudo yum upgrade -y
        sudo amazon-linux-extras install nginx1 -y
        sudo systemctl enable nginx
        sudo systemctl start nginx`;
    
    // [Step 2: Create a security group.]
    const securityGroup = // TO-DO
    
    });
    
    // [Step 1: Create an EC2 instance.]
    const server = new aws.ec2.Instance("webserver-www2", {
        instanceType: "t2.micro",
        ami: "ami-09538990a0c4fe9be",
        userData: userData,
        vpcSecurityGroupIds: [securityGroup.id],
    });
    
    import pulumi
    import pulumi_aws as aws
    
    user_data = """
    #!/bin/bash
    sudo yum update -y
    sudo yum upgrade -y
    sudo amazon-linux-extras install nginx1 -y
    sudo systemctl enable nginx
    sudo systemctl start nginx
    """
    
    # [Step 2: Create a security group.]
    security_group = # TO-DO
    
    # [Step 1: Create an EC2 instance.]
    server = aws.ec2.Instance(
        'webserver-www',
        instance_type="t2.micro",
        ami="ami-09538990a0c4fe9be",
        user_data=user_data,
        vpc_security_group_ids=[security_group.id], # Security group property and reference
    )
    
    pulumi.export('publicIp', server.public_ip)
    
    name: aws-ec2-sg-nginx-server-yaml
    runtime: yaml
    description: A program to create an Nginx server on AWS
    
    resources:
      # [Step 2: Create a security group.]
      webserver-secgrp:
        # TO-DO
    
      # [Step 1: Create an EC2 instance.]
      webserver-www:
        type: aws:ec2:Instance
        properties:
          instanceType: t2.micro
          ami: "ami-09538990a0c4fe9be"
          userData: |
            #!/bin/bash
            sudo yum update -y
            sudo yum upgrade -y
            sudo amazon-linux-extras install nginx1 -y
            sudo systemctl enable nginx
            sudo systemctl start nginx        
          vpcSecurityGroupIds:
            - ${webserver-secgrp.id}
    
    outputs:
      publicIp: ${webserver-www.publicIp}
    

    You may have noticed that the placeholder code for the security group resource has been moved above the code for the EC2 instance resource. This was done intentionally to accommodate for variable declaration and usage order in our code.

    If we place the security group resource definition after the EC2 instance and try to deploy our program, it will fail. This is because the security group variable must be declared before we can tell our EC2 instance resource to use it.

    Use the following steps as a guide for adding the Security Group resource:

    • Navigate to the AWS Registry documentation page
    • Search for the EC2 Security Group resource
    • Define the EC2 Security Group resource in your project code
    • Configure the security group to allow traffic on port 80
    • Update the EC2 instance resource to use the security group
    • Preview and deploy your updated project code

    Once you have completed these steps, navigate to your instance IP address again. You should now be greeted with a “Welcome to nginx!” home page message that indicates your web server is running and publically accessible.

    If your web server is still timing out, make sure you are accessing your web server’s IP address via HTTP and not HTTPS.
    Click here to view the complete project code
    import * as aws from "@pulumi/aws";
    import * as pulumi from "@pulumi/pulumi";
    
    const userData = `
        #!/bin/bash
        sudo yum update -y
        sudo yum upgrade -y
        sudo amazon-linux-extras install nginx1 -y
        sudo systemctl enable nginx
        sudo systemctl start nginx`;
    
    // [Step 2: Create a security group.]
    const securityGroup = new aws.ec2.SecurityGroup("webserver-secgrp2", {
        description: "Enable HTTP access",
        ingress: [
            {
                protocol: "tcp",
                fromPort: 80,
                toPort: 80,
                cidrBlocks: ["0.0.0.0/0"],
            },
        ],
    });
    
    // [Step 1: Create an EC2 instance.]
    const server = new aws.ec2.Instance("webserver-www2", {
        instanceType: "t2.micro",
        ami: "ami-09538990a0c4fe9be",
        userData: userData,
        vpcSecurityGroupIds: [securityGroup.id],
    });
    
    export const publicIp = server.publicIp;
    
    import pulumi
    import pulumi_aws as aws
    
    user_data = """
    #!/bin/bash
    sudo yum update -y
    sudo yum upgrade -y
    sudo amazon-linux-extras install nginx1 -y
    sudo systemctl enable nginx
    sudo systemctl start nginx
    """
    
    # [Step 2: Create a security group.]
    security_group = aws.ec2.SecurityGroup(
        'webserver-secgrp',
        description='Enable HTTP access',
        ingress=[
            { 'protocol': 'tcp', 'from_port': 80, 'to_port': 80, 'cidr_blocks': ['0.0.0.0/0'] }
        ]
    )
    
    # [Step 1: Create an EC2 instance.]
    server = aws.ec2.Instance(
        'webserver-www',
        instance_type="t2.micro",
        ami="ami-09538990a0c4fe9be",
        user_data=user_data,
        vpc_security_group_ids=[security_group.id], # Security group property and reference
    )
    
    pulumi.export('publicIp', server.public_ip)
    
    name: aws-ec2-sg-nginx-server-yaml
    runtime: yaml
    description: A program to create an Nginx server on AWS
    
    resources:
      # [Step 2: Create a security group.]
      webserver-secgrp:
        type: aws:ec2:SecurityGroup
        properties:
          description: Enable HTTP access
          ingress:
            - protocol: tcp
              fromPort: 80
              toPort: 80
              cidrBlocks:
                - 0.0.0.0/0
    
      # [Step 1: Create an EC2 instance.]
      webserver-www:
        type: aws:ec2:Instance
        properties:
          instanceType: t2.micro
          ami: "ami-09538990a0c4fe9be"
          userData: |
            #!/bin/bash
            sudo yum update -y
            sudo yum upgrade -y
            sudo amazon-linux-extras install nginx1 -y
            sudo systemctl enable nginx
            sudo systemctl start nginx        
          vpcSecurityGroupIds:
            - ${webserver-secgrp.id}
    
    outputs:
      publicIp: ${webserver-www.publicIp}
    

    Clean Up

    Before moving on, tear down the resources that are part of your stack to avoid incurring any charges.

    1. Run pulumi destroy to tear down all resources. You'll be prompted to make sure you really want to delete these resources. A destroy operation may take some time, since Pulumi waits for the resources to finish shutting down before it considers the destroy operation to be complete.
    2. To delete the stack itself, run pulumi stack rm. Note that this command deletes all deployment history from the Pulumi Service.

    Next Steps

    In this tutorial, you made an EC2 instance configured as an Nginx webserver and made it publically available by referencing the Pulumi Registry to define the security group. You also reviewed resource properties and example usage.

    To learn more about creating resources in Pulumi, take a look at the following resources:

      Introducing Drift Detection, TTL Stacks, and Scheduled Deployments. Learn More.