Skip to content
This repository has been archived by the owner on Jul 1, 2024. It is now read-only.

Commit

Permalink
docs: add blog post Next-level IaC: Pulumi Automation API and API Eco…
Browse files Browse the repository at this point in the history
…nomy
  • Loading branch information
dirien committed Apr 26, 2024
1 parent f59b46e commit 6b83cd7
Show file tree
Hide file tree
Showing 4 changed files with 357 additions and 0 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,357 @@
---
title: "Next-level IaC: Pulumi Automation API and API Economy"
date: 2024-04-26
meta_desc: Learn how to use Pulumi Automation API to build your own Service API and expose it to your organization.
meta_image: meta.png

authors:

- engin-diri

tags:

- next-level-iac
- automation-api
- api-economy
- cloudformation
- terraform
- hcl

---

When I am talking with our customers and users, there is one common theme that comes up: how can we make infrastructure as code (IaC) more accessible to our organization? This is a great question, and it is something that we have been thinking about a lot at Pulumi.

We believe that the key to making IaC more accessible is to treat it as a first-class citizen in your organization.

This means that you should be able to interact with your infrastructure in the same way that you interact with any other service in your organization. There are different ways to achieve this goal, be it through providing a self-service portal, building custom tooling, or integrating IaC into your existing workflows.

This is where the Pulumi Automation API comes in and the concept of [API Economy](https://konghq.com/blog/enterprise/api-economy).

## What is API Economy?

![API Economy](api-economy.png)

(Image [source](http://localhost:1313/blog/next-level-iac-pulumi-automation-api/api-economy.png))

As a matter of fact: — APIs enable innovation.

> Over [40% of large organizations](https://www.forbes.com/sites/patricksalyer/2021/05/04/api-stack-the-billion-dollar-opportunities-redefining-infrastructure-services--platforms/?sh=2d6c47f043f9) have 250+ APIs. And 71% plan to use even more APIs this year.
API Economy is a term that is used to describe the way that organizations are using APIs to create new business models and revenue streams. APIs are the building blocks of modern software development, and they allow developers to build on top of existing services and create new services that are greater than the sum of their parts.

Some examples of API Economy are:

- [Stripe](https://stripe.com/docs/api) - Allows companies to accept payments online
- [Google Maps API](https://developers.google.com/maps) - Allows companies to use their maps, witout having to build their own
- [Uber Eats API](https://developer.uber.com/docs/eats) - Allows companies to integrate with Uber Eats and offer food delivery

But why is API Economy important for IaC? Because it allows you to treat your infrastructure as a service. The owner of a particular piece of infrastructure (like DNS, a database, or a Kubernetes cluster) can expose that infrastructure to the rest of the organization through an API. This API can be used to provision, update, and delete the infrastructure, as well as to query its current state. This allows developers to interact with the infrastructure in a programmatic way, just like they would interact with any other service in the organization.

At the end you can then use Gateways like [Apigee](https://cloud.google.com/apigee?hl=en), [Kong](https://konghq.com/), or [AWS API Gateway](https://aws.amazon.com/api-gateway/) to register your API and make it consumable by your organization. In whatever way they want to consume it, be it through a CLI, a web interface, or a chatbot.

## What is Pulumi Automation API in a nutshell?

Pulumi Automation API is a set of SDKs that allow you to interact with your Pulumi stacks programmatically. This means that you can use the Automation API to create, update, and delete stacks, as well as to query their current state. The Automation API is built on top of the Pulumi engine, which means that it is fully compatible with all of the existing Pulumi providers and resources.

And it is this capability that allows you to build your own Service API on top of Pulumi. We can now build a REST or gRPC API endpoint that, depending on the request parameters, will create, update, or delete a Pulumi stack. This API can then be exposed to the rest of the organization, allowing developers to interact with their infrastructure in a programmatic way.

## How to build your own Service API with Pulumi Automation API?

Let's take a look at how you can build your own Service API with Pulumi Automation API.

For this purpose, we will build two basic REST services writen in [TypeScript](https://www.typescriptlang.org/) and in [Python](https://www.python.org/).

{{< chooser language "typescript,python" />}}

{{% choosable language typescript %}}

```typescript
import express from "express"
import * as dotevnv from "dotenv"
import cors from "cors"
import helmet from "helmet"
import {fullyQualifiedStackName, RemoteWorkspace} from "@pulumi/pulumi/automation";

dotevnv.config()

if (!process.env.PORT) {
console.log(`No port value specified...`)
}

const PORT = parseInt(process.env.PORT as string, 10)

const app = express()

app.use(express.json())
app.use(express.urlencoded({extended: true}))
app.use(cors())
app.use(helmet())


interface ServicePayload {
name: string;
org: string;
project: string;
stack: string;
region: string;
}

async function setUpPulumiProgram(payload: ServicePayload) {
const org = payload.org;
const project = payload.project;
const stackName = fullyQualifiedStackName(org, project, payload.stack);
const awsRegion = payload.region;

return await RemoteWorkspace.createOrSelectStack({
stackName,
url: "https://github.com/pulumi/examples.git",
branch: "refs/heads/master",
projectPath: project,
}, {
envVars: {
AWS_REGION: awsRegion,
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID ?? "",
AWS_SECRET_ACCESS_KEY: {secret: process.env.AWS_SECRET_ACCESS_KEY ?? ""},
AWS_SESSION_TOKEN: {secret: process.env.AWS_SESSION_TOKEN ?? ""},
},
});
}

app.post('/', async (req, res) => {
const payload: ServicePayload = req.body;

if ((payload.name) || !payload.org || !payload.project || !payload.stack || !payload.region) {
res.status(400).send('Name, org, project, stack and region are required');
return;
}

const stack = await setUpPulumiProgram(payload);

const upRes = await stack.up({onOutput: console.log});
res.status(200).send(`url: ${upRes.outputs.websiteUrl.value}`);
});

app.delete('/', async (req, res) => {
const { name, org, project, stack, region } = req.query;

if (!name || !org || !project || !stack || !region) {
res.status(400).send('Name, org, project, stack, and region are required');
return;
}

const payload: ServicePayload = {
name: name as string,
org: org as string,
project: project as string,
stack: stack as string,
region: region as string
}

const prog = await setUpPulumiProgram(payload);
await prog.destroy({onOutput: console.log});
res.status(200).send('Stack destroyed');;
});


app.listen(PORT, () => {
console.log(`Server is listening on port ${PORT}`)
})
```

{{% /choosable %}}

{{% choosable language python %}}

```python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import pulumi.automation as auto
import os


app = FastAPI()

class ServicePayload(BaseModel):
name: str
org: str
project: str
stack: str
region: str


def setup_pulumi_program(payload: ServicePayload):
org = payload.org
project = payload.project
stack_name = auto.fully_qualified_stack_name(org, project, payload.stack)
aws_region = payload.region

return auto.create_or_select_remote_stack_git_source(
stack_name=stack_name,
url="https://github.com/pulumi/examples.git",
branch="refs/heads/master",
project_path=project,
opts=auto.RemoteWorkspaceOptions(
env_vars={
"AWS_REGION": aws_region,
"AWS_ACCESS_KEY_ID": os.environ["AWS_ACCESS_KEY_ID"],
"AWS_SECRET_ACCESS_KEY": auto.Secret(os.environ["AWS_SECRET_ACCESS_KEY"]),
"AWS_SESSION_TOKEN": auto.Secret(os.environ["AWS_SESSION_TOKEN"]),
},
),
)

@app.post("/")
async def create(payload: ServicePayload):
stack = setup_pulumi_program(payload)
up_res = stack.up(on_output=print)
return {"url": up_res.outputs['websiteUrl'].value}

@app.delete("/")
async def delete(name: str, org: str, project: str, stack: str, region: str):
payload = ServicePayload(name=name, org=org, project=project, stack=stack, region=region)
stack = setup_pulumi_program(payload)
stack.destroy(on_output=print)
return {"message": "Stack destroyed"}
```

{{% /choosable %}}

To test the REST services, you can use a tool like `curl` or Postman. Here is an example of how you can create a new stack using `curl`:

```bash
curl -X POST -H "Content-Type: application/json" -d '{"name": "myexample", "org": "myorg", "project": "myproject", "stack": "mystack", "region": "us-west-2"}' http://localhost:3000
```

And here is an example of how you can delete a stack using `curl`:

```bash
curl -X DELETE "http://localhost:8000/?name=myexample&org=myorg&project=myproject&stack=mystack&region=us-west-2"
```

## Next Steps

Now that we have our basic REST services, we can deploy them on a server (using a service like AWS Lambda, Google Cloud Functions, or Azure Functions) and expose them to the rest of the organization via an API Gateway. Parallel you can publish release notes and documentation to your organization, so that they can start using your new Service API.

On way could be to create an OpenAPI specification for your Service API and use a tool like Swagger to generate a client library for your organization. This way, developers can interact with your Service API in a programmatic way, without having to write any code.

Here is an example of an OpenAPI specification for our Service API:

```yaml
openapi: 3.0.0
info:
title: Pulumi Infrastructure Management API
description: API for creating and destroying infrastructure using Pulumi.
version: 1.0.0
servers:
- url: 'http://localhost:8000/'
description: Development server
paths:
/:
post:
summary: Create Infrastructure
operationId: createInfrastructure
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ServicePayload'
responses:
'200':
description: Infrastructure successfully created
content:
application/json:
schema:
type: object
properties:
url:
type: string
description: URL of the created infrastructure
'400':
description: Bad request if the input payload is incorrect
'500':
description: Internal server error
delete:
summary: Destroy Infrastructure
operationId: destroyInfrastructure
parameters:
- in: query
name: name
schema:
type: string
required: true
description: Name of the service
- in: query
name: org
schema:
type: string
required: true
description: Organization in Pulumi
- in: query
name: project
schema:
type: string
required: true
description: Project name in Pulumi
- in: query
name: stack
schema:
type: string
required: true
description: Stack name in Pulumi
- in: query
name: region
schema:
type: string
required: true
description: AWS region where the service will be deployed
responses:
'200':
description: Infrastructure successfully destroyed
content:
application/json:
schema:
type: object
properties:
message:
type: string
description: Confirmation message of the destruction
'400':
description: Bad request if the required parameters are missing
'500':
description: Internal server error
components:
schemas:
ServicePayload:
type: object
required:
- name
- org
- project
- stack
- region
properties:
name:
type: string
description: Name of the service
org:
type: string
description: Organization in Pulumi
project:
type: string
description: Project name in Pulumi
stack:
type: string
description: Stack name in Pulumi
region:
type: string
description: AWS region where the service will be deployed
```
Navigate to the [Swagger Editor](https://editor.swagger.io/) and paste the above OpenAPI specification to see how it looks like.
![Swagger Editor](swagger.png)
## Conclusion
In this blog post, we have seen how you can use Pulumi Automation API to build your own Service API and expose it to your organization. This allows you to treat your infrastructure as a service and interact with it in a programmatic way. We have also seen how you can use API Economy to create new business models and revenue streams by exposing your infrastructure to the rest of the organization. I hope that this blog post has given you some ideas on how you can take your IaC to the next level and make it more accessible to your organization.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 6b83cd7

Please sign in to comment.