Blog
/
/
Using Pulumi With an Internal Developer Portal
Internal Developer Portal

Using Pulumi With an Internal Developer Portal

Jun 28, 2023
Mor Paz
Internal Developer Portal

Introduction

Infrastructure as code makes life easier both for devops and developers. It gives them a way to create infrastructure that is standardized and reproducible. It shortens time spent creating a new environment and provisioning resources, since it is easy to add new variables or edit files, letting you prototype new infrastructure in a faster, easier and safer way. Combining IaC with GitOps makes it very easy to monitor and manage infrastructure as well as the way it changes and evolves over the environment’s lifetime. 

However, infrastructure as code can be daunting for developers, as it requires them to learn a new framework, syntax or both. Developers also need to understand the underlying infrastructure which is usually something that developers are less versed in.

Since infrastructure as code is directly connected to the environment’s resources, performing the wrong change could easily introduce security vulnerabilities or even cause production downtime. Small changes can have catastrophic effects, making developers less comfortable using IaC alone.

Closing the loop with internal developer portals

Internal developer portals can close the IaC and developer loop. 

  • The software catalog that’s in the internal developer portal can show developers concise and abstracted data about the environments they work in. Usually that information already exists in IaC definitions, and the software catalog can show developers a concise form, in the context of their work, to make life easier for them. 
  • Developers can also perform self-service actions in the internal developer portal, and quickly and safely provision new infrastructure, by using self-service actions that are golden paths to use actions that have been made reusable by platform engineers. 
  • This functionality saves developers the need to understand the underlying IaC mechanism, and simply consume it with a product-like experience that gives them more confidence, unleashing productivity.

Let’s look at a live example of how to use Pulumi and internal developer portals, exemplified by Port. 

1. If you don’t already have a Pulumi account already, can sign up here for free

2. Set up the data model in Port using Pulumi

3. Deploy a developer environment and ingest its data using Pulumi 

4. Deploy a new developer environment directly from Port using self-service actions

5. Finally, tie it all together

{{cta_1}}

Setting up the data model

Let’s begin by building a data model that will be the skeleton of Port’s software catalog. In Port, data models are defined through blueprints and their relations. Port is un-opinionated by definition, so you can create the data model that’s best for you.

A Blueprint, or custom entity definition, is a data model that allows definition of the metadata associated with software catalog entities. Blueprints are the main building block of Port. Once data is ingested, populated blueprints become catalog entities. Blueprints support the representation of any asset in Port, such as microservice, environment, package, cluster, databases etc. 

Let’s use a Pulumi file to set up the initial blueprints in our software catalog. Pulumi and Port have a direct integration. This makes it possible to create a class for the Port blueprint resource, which inherits Pulumi’s component resource type, making sure any new blueprint we define in Port is following best practices and is properly documented as part of our Pulumi stack.

After setting up the Port blueprint class, it's only a matter of adding a few lines of code  to create our desired blueprints.

In Port, four blueprints were created:

  • Developer environment
  • Lambda
  • S3 bucket; and 
  • SQS Queue

These four blueprints encompass a developer environment which consists of a Lambda function, an S3 bucket and an SQS Queue. Each of these blueprints can contain any set of properties.

The Lambda blueprint, for instance, contains the memory size and the Lambda URL. For the S3 bucket we're going to take the bucket ACL permissions and the bucket URL. The SQS Queue blueprints will contain the queue URL. Our developer environment blueprint will include references to all of the resources that it is using.

Blueprints can actually be much more than this minimal set, and contain any number of properties of any type. 

Ingesting data into Port

A software catalog is made of entities, each of which is defined by the relevant blueprint. Entities are the actual instances of blueprints and they are what makes the software catalog.

In this case, we’ll see our demo Lambda function entity, the S3 entity and the SQS Queue entity. On top of that we have the developer environment entity, which isn’t tied to any particular part of our infrastructure but is related to the actual components that we provisioned using Pulumi.

This developer environment entity ties together the different components that make up the actual developer environment in our infrastructure, and makes it easy to understand which components are part of the environment at a glance, through the portal.

Using the related entities view in Port’s software catalog we can even put this developer environment on a graph and better understand how the developer environment fits in the complete architecture and what it connects to.

Developer self-service actions

To understand how to use self-service actions within a developer portal, let’s examine them from the point of view of a developer. Up until now we had set up the internal developer portal as platform engineers, and now we’ll use the perspective of the portal’s customers: the developers. 

Our developer wants to deploy a new developer environment using the self-service hub in the internal developer portal.Once the user executes the self-service action, Port generates a standardized payload with the user inputs and sends them to the selected backend. Self-service actions are defined in the blueprints created by the user. In this case, we defined two actions in the developer environment blueprint. One is for deploying a stack in an environment and the other one for tearing down that stack.

These actions were defined as part of the initial code that we ran to generate the blueprints. This is another example where we can make use of Port’s integration with Pulumi to configure standardized resources and their respective representation in Port. In this case, we added an invocation method that takes advantage of port's GitHub application. Once I click execute for the action, Port sends that payload to GitHub workflows and triggers it, this means we have a direct integration between our developer portal and our CI/CD pipeline. 

After triggering the action through Port, developers can check a job link to the GitHub workflow that was triggered, as well as view, within the portal, the logs pertaining to the self-service request. This keeps developers in the loop, and let’s them track what is going on as their new developer environment is set up. 

Once the self-service action is complete we can see it in the software catalog, under developer environments.  

When the developer is done using this environment, they need to tear the environment down. Without developer portals, this would possibly require sending a ticket to devops (and in most realistic cases, the developer\DevOps would have forgotten about the environment and it would have just stayed up indefinitely, taking up resources and increasing cloud spend).

In this case, the developer can tear down the environment through the developer portal.  Since the action tears down an existing environment, there is no need for specific user inputs. The teardown action will perform all of the actions we went through with the deployment, but in reverse, the delete action will use Port’s GitHub app to interact with GitHub and trigger a GitHub workflow and run a destroy process.

Keeping the software catalog up to date

The code for the Port and Pulumi integration contains classes and definitions for different sources, for example a Lambda function and an S3 bucket. One of the advantages of the integration is that by creating those classes for the objects and inheriting from Pulumi’s component resource class we can actually tie together the actual resource in our Cloud environment and the respective software catalog entity automatically.  

To see the full live coding session this blog is based on, go here:

{{cta}}

Book a demo right now to check out Port's developer portal yourself

Book a demo
{{jenkins}}

It's a Trap - Jenkins as Self service UI

Read more
{{gitops}}

How do GitOps affect developer experience?

Read more
{{ebook}}

It's a Trap - Jenkins as Self service UI. Click her to download the eBook

Download eBook
{{cyberark}}

Learning from CyberArk - building an internal developer platform in-house

Read more
{{dropdown}}

Example JSON block

{
  "foo": "bar"
}

Order Domain

{
  "properties": {},
  "relations": {},
  "title": "Orders",
  "identifier": "Orders"
}

Cart System

{
  "properties": {},
  "relations": {
    "domain": "Orders"
  },
  "identifier": "Cart",
  "title": "Cart"
}

Products System

{
  "properties": {},
  "relations": {
    "domain": "Orders"
  },
  "identifier": "Products",
  "title": "Products"
}

Cart Resource

{
  "properties": {
    "type": "postgress"
  },
  "relations": {},
  "icon": "GPU",
  "title": "Cart SQL database",
  "identifier": "cart-sql-sb"
}

Cart API

{
 "identifier": "CartAPI",
 "title": "Cart API",
 "blueprint": "API",
 "properties": {
   "type": "Open API"
 },
 "relations": {
   "provider": "CartService"
 },
 "icon": "Link"
}

Core Kafka Library

{
  "properties": {
    "type": "library"
  },
  "relations": {
    "system": "Cart"
  },
  "title": "Core Kafka Library",
  "identifier": "CoreKafkaLibrary"
}

Core Payment Library

{
  "properties": {
    "type": "library"
  },
  "relations": {
    "system": "Cart"
  },
  "title": "Core Payment Library",
  "identifier": "CorePaymentLibrary"
}

Cart Service JSON

{
 "identifier": "CartService",
 "title": "Cart Service",
 "blueprint": "Component",
 "properties": {
   "type": "service"
 },
 "relations": {
   "system": "Cart",
   "resources": [
     "cart-sql-sb"
   ],
   "consumesApi": [],
   "components": [
     "CorePaymentLibrary",
     "CoreKafkaLibrary"
   ]
 },
 "icon": "Cloud"
}

Products Service JSON

{
  "identifier": "ProductsService",
  "title": "Products Service",
  "blueprint": "Component",
  "properties": {
    "type": "service"
  },
  "relations": {
    "system": "Products",
    "consumesApi": [
      "CartAPI"
    ],
    "components": []
  }
}

Component Blueprint

{
 "identifier": "Component",
 "title": "Component",
 "icon": "Cloud",
 "schema": {
   "properties": {
     "type": {
       "enum": [
         "service",
         "library"
       ],
       "icon": "Docs",
       "type": "string",
       "enumColors": {
         "service": "blue",
         "library": "green"
       }
     }
   },
   "required": []
 },
 "mirrorProperties": {},
 "formulaProperties": {},
 "calculationProperties": {},
 "relations": {
   "system": {
     "target": "System",
     "required": false,
     "many": false
   },
   "resources": {
     "target": "Resource",
     "required": false,
     "many": true
   },
   "consumesApi": {
     "target": "API",
     "required": false,
     "many": true
   },
   "components": {
     "target": "Component",
     "required": false,
     "many": true
   },
   "providesApi": {
     "target": "API",
     "required": false,
     "many": false
   }
 }
}

Resource Blueprint

{
 “identifier”: “Resource”,
 “title”: “Resource”,
 “icon”: “DevopsTool”,
 “schema”: {
   “properties”: {
     “type”: {
       “enum”: [
         “postgress”,
         “kafka-topic”,
         “rabbit-queue”,
         “s3-bucket”
       ],
       “icon”: “Docs”,
       “type”: “string”
     }
   },
   “required”: []
 },
 “mirrorProperties”: {},
 “formulaProperties”: {},
 “calculationProperties”: {},
 “relations”: {}
}

API Blueprint

{
 "identifier": "API",
 "title": "API",
 "icon": "Link",
 "schema": {
   "properties": {
     "type": {
       "type": "string",
       "enum": [
         "Open API",
         "grpc"
       ]
     }
   },
   "required": []
 },
 "mirrorProperties": {},
 "formulaProperties": {},
 "calculationProperties": {},
 "relations": {
   "provider": {
     "target": "Component",
     "required": true,
     "many": false
   }
 }
}

Domain Blueprint

{
 "identifier": "Domain",
 "title": "Domain",
 "icon": "Server",
 "schema": {
   "properties": {},
   "required": []
 },
 "mirrorProperties": {},
 "formulaProperties": {},
 "calculationProperties": {},
 "relations": {}
}

System Blueprint

{
 "identifier": "System",
 "title": "System",
 "icon": "DevopsTool",
 "schema": {
   "properties": {},
   "required": []
 },
 "mirrorProperties": {},
 "formulaProperties": {},
 "calculationProperties": {},
 "relations": {
   "domain": {
     "target": "Domain",
     "required": true,
     "many": false
   }
 }
}
{{tabel-1}}

Microservices SDLC

  • Scaffold a new microservice

  • Deploy (canary or blue-green)

  • Feature flagging

  • Revert

  • Lock deployments

  • Add Secret

  • Force merge pull request (skip tests on crises)

  • Add environment variable to service

  • Add IaC to the service

  • Upgrade package version

Development environments

  • Spin up a developer environment for 5 days

  • ETL mock data to environment

  • Invite developer to the environment

  • Extend TTL by 3 days

Cloud resources

  • Provision a cloud resource

  • Modify a cloud resource

  • Get permissions to access cloud resource

SRE actions

  • Update pod count

  • Update auto-scaling group

  • Execute incident response runbook automation

Data Engineering

  • Add / Remove / Update Column to table

  • Run Airflow DAG

  • Duplicate table

Backoffice

  • Change customer configuration

  • Update customer software version

  • Upgrade - Downgrade plan tier

  • Create - Delete customer

Machine learning actions

  • Train model

  • Pre-process dataset

  • Deploy

  • A/B testing traffic route

  • Revert

  • Spin up remote Jupyter notebook

{{tabel-2}}

Engineering tools

  • Observability

  • Tasks management

  • CI/CD

  • On-Call management

  • Troubleshooting tools

  • DevSecOps

  • Runbooks

Infrastructure

  • Cloud Resources

  • K8S

  • Containers & Serverless

  • IaC

  • Databases

  • Environments

  • Regions

Software and more

  • Microservices

  • Docker Images

  • Docs

  • APIs

  • 3rd parties

  • Runbooks

  • Cron jobs

Check out Port's pre-populated demo and see what it's all about.

Check live demo

No email required

Contact sales for a technical product walkthrough

Let’s start

Open a free Port account. No credit card required

Let’s start

Watch Port live coding videos - setting up an internal developer portal & platform

Let’s start

Check out Port's pre-populated demo and see what it's all about.

(no email required)

Let’s start

Contact sales for a technical product walkthrough

Let’s start

Open a free Port account. No credit card required

Let’s start

Watch Port live coding videos - setting up an internal developer portal & platform

Let’s start

Let us walk you through the platform and catalog the assets of your choice.

I’m ready, let’s start