├── .gitignore ├── 00-installing-prerequisites.md ├── README.md ├── labs ├── aws │ ├── in-person │ │ ├── csharp │ │ │ ├── README.md │ │ │ ├── lab-01 │ │ │ │ ├── 01-creating-a-new-project.md │ │ │ │ ├── 02-configuring-aws.md │ │ │ │ ├── 03-provisioning-infrastructure.md │ │ │ │ ├── 04-updating-your-infrastructure.md │ │ │ │ ├── 05-making-your-stack-configurable.md │ │ │ │ ├── 06-creating-a-second-stack.md │ │ │ │ ├── 07-destroying-your-infrastructure.md │ │ │ │ └── code │ │ │ │ │ ├── 02-configuring-aws │ │ │ │ │ └── step2.cs │ │ │ │ │ ├── 03-provisioning-infrastructure │ │ │ │ │ ├── step1.cs │ │ │ │ │ └── step4.cs │ │ │ │ │ ├── 04-updating-your-infrastructure │ │ │ │ │ ├── step1.cs │ │ │ │ │ └── step2.cs │ │ │ │ │ └── 05-making-your-stack-configurable │ │ │ │ │ ├── step2.cs │ │ │ │ │ └── step4.cs │ │ │ ├── lab-02 │ │ │ │ ├── README.md │ │ │ │ └── code │ │ │ │ │ ├── step1.cs │ │ │ │ │ ├── step3.cs │ │ │ │ │ └── step4.cs │ │ │ ├── lab-03 │ │ │ │ ├── README.md │ │ │ │ └── code │ │ │ │ │ ├── step1.cs │ │ │ │ │ ├── step2.cs │ │ │ │ │ ├── step3.cs │ │ │ │ │ └── step5.cs │ │ │ └── lab-04 │ │ │ │ ├── README.md │ │ │ │ └── code │ │ │ │ ├── step1.cs │ │ │ │ ├── step2.cs │ │ │ │ ├── step3.cs │ │ │ │ ├── step4.cs │ │ │ │ ├── step5.cs │ │ │ │ └── step7.cs │ │ ├── go │ │ │ ├── README.md │ │ │ ├── lab-01 │ │ │ │ ├── 01-creating-a-new-project.md │ │ │ │ ├── 02-configuring-aws.md │ │ │ │ ├── 03-provisioning-infrastructure.md │ │ │ │ ├── 04-updating-your-infrastructure.md │ │ │ │ ├── 05-making-your-stack-configurable.md │ │ │ │ ├── 06-creating-a-second-stack.md │ │ │ │ ├── 07-destroying-your-infrastructure.md │ │ │ │ └── code │ │ │ │ │ ├── 02-configuring-aws │ │ │ │ │ └── step2.go │ │ │ │ │ ├── 03-provisioning-infrastructure │ │ │ │ │ ├── step1.go │ │ │ │ │ └── step4.go │ │ │ │ │ ├── 04-updating-your-infrastructure │ │ │ │ │ ├── step1.go │ │ │ │ │ └── step2.go │ │ │ │ │ └── 05-making-your-stack-configurable │ │ │ │ │ ├── step2.go │ │ │ │ │ └── step4.go │ │ │ ├── lab-02 │ │ │ │ ├── README.md │ │ │ │ └── code │ │ │ │ │ ├── step1.go │ │ │ │ │ ├── step3.go │ │ │ │ │ └── step4.go │ │ │ ├── lab-03 │ │ │ │ ├── README.md │ │ │ │ └── code │ │ │ │ │ ├── step1.go │ │ │ │ │ ├── step2.go │ │ │ │ │ ├── step3.go │ │ │ │ │ └── step5.go │ │ │ ├── lab-04 │ │ │ │ ├── README.md │ │ │ │ └── code │ │ │ │ │ ├── step1.go │ │ │ │ │ ├── step2.go │ │ │ │ │ ├── step3.go │ │ │ │ │ ├── step4.go │ │ │ │ │ ├── step5.go │ │ │ │ │ └── step6.go │ │ │ └── lab-05 │ │ │ │ ├── README.md │ │ │ │ └── code │ │ │ │ ├── step1.go │ │ │ │ ├── step2.go │ │ │ │ ├── step3.go │ │ │ │ ├── step4.go │ │ │ │ └── step6.go │ │ ├── python │ │ │ ├── README.md │ │ │ ├── lab-01 │ │ │ │ ├── 01-creating-a-new-project.md │ │ │ │ ├── 02-configuring-aws.md │ │ │ │ ├── 03-provisioning-infrastructure.md │ │ │ │ ├── 04-updating-your-infrastructure.md │ │ │ │ ├── 05-making-your-stack-configurable.md │ │ │ │ ├── 06-creating-a-second-stack.md │ │ │ │ ├── 07-destroying-your-infrastructure.md │ │ │ │ └── code │ │ │ │ │ ├── 02-configuring-aws │ │ │ │ │ └── step2.py │ │ │ │ │ ├── 03-provisioning-infrastructure │ │ │ │ │ ├── step1.py │ │ │ │ │ └── step4.py │ │ │ │ │ ├── 04-updating-your-infrastructure │ │ │ │ │ ├── step1.py │ │ │ │ │ └── step2.py │ │ │ │ │ └── 05-making-your-stack-configurable │ │ │ │ │ ├── step2.py │ │ │ │ │ └── step4.py │ │ │ ├── lab-02 │ │ │ │ ├── README.md │ │ │ │ └── code │ │ │ │ │ ├── step1.py │ │ │ │ │ ├── step3.py │ │ │ │ │ └── step4.py │ │ │ ├── lab-03 │ │ │ │ ├── README.md │ │ │ │ └── code │ │ │ │ │ ├── step1.py │ │ │ │ │ ├── step2.py │ │ │ │ │ ├── step3.py │ │ │ │ │ └── step5.py │ │ │ ├── lab-04 │ │ │ │ ├── README.md │ │ │ │ └── code │ │ │ │ │ ├── step1.py │ │ │ │ │ ├── step2.py │ │ │ │ │ ├── step3.py │ │ │ │ │ ├── step4.py │ │ │ │ │ ├── step5.py │ │ │ │ │ └── step6.py │ │ │ └── lab-05 │ │ │ │ ├── README.md │ │ │ │ └── code │ │ │ │ ├── step1.py │ │ │ │ ├── step2.py │ │ │ │ ├── step3.py │ │ │ │ ├── step4.py │ │ │ │ └── step6.py │ │ └── typescript │ │ │ ├── README.md │ │ │ ├── lab-01 │ │ │ ├── 01-creating-a-new-project.md │ │ │ ├── 02-configuring-aws.md │ │ │ ├── 03-provisioning-infrastructure.md │ │ │ ├── 04-updating-your-infrastructure.md │ │ │ ├── 05-making-your-stack-configurable.md │ │ │ ├── 06-creating-a-second-stack.md │ │ │ ├── 07-destroying-your-infrastructure.md │ │ │ └── code │ │ │ │ ├── 02-configuring-aws │ │ │ │ └── step2.ts │ │ │ │ ├── 03-provisioning-infrastructure │ │ │ │ ├── step1.ts │ │ │ │ └── step4.ts │ │ │ │ ├── 04-updating-your-infrastructure │ │ │ │ ├── step1.ts │ │ │ │ └── step2.ts │ │ │ │ └── 05-making-your-stack-configurable │ │ │ │ ├── step2.ts │ │ │ │ └── step4.ts │ │ │ ├── lab-02 │ │ │ ├── README.md │ │ │ └── code │ │ │ │ ├── step1.ts │ │ │ │ ├── step2.ts │ │ │ │ ├── step3.ts │ │ │ │ ├── step4.ts │ │ │ │ ├── step5.ts │ │ │ │ └── step6.ts │ │ │ ├── lab-03 │ │ │ ├── README.md │ │ │ └── code │ │ │ │ ├── step1.ts │ │ │ │ ├── step2.ts │ │ │ │ ├── step4.ts │ │ │ │ └── step5.ts │ │ │ ├── lab-04 │ │ │ ├── README.md │ │ │ └── code │ │ │ │ ├── step1.ts │ │ │ │ ├── step2.ts │ │ │ │ └── step3.ts │ │ │ ├── lab-05 │ │ │ ├── README.md │ │ │ └── code │ │ │ │ ├── step1.ts │ │ │ │ ├── step2.ts │ │ │ │ ├── step3.ts │ │ │ │ ├── step4.ts │ │ │ │ └── step6.ts │ │ │ └── lab-06 │ │ │ ├── README.md │ │ │ └── code │ │ │ ├── step1.ts │ │ │ ├── step2.ts │ │ │ ├── step3.ts │ │ │ ├── step4.ts │ │ │ └── step6.ts │ └── pulumi-in-practice │ │ └── python │ │ ├── README.md │ │ ├── lab-01 │ │ └── README.md │ │ ├── lab-02 │ │ ├── README.md │ │ └── code │ │ │ ├── step1.py │ │ │ ├── step3.py │ │ │ ├── step4.py │ │ │ └── www │ │ │ └── index.html │ │ ├── lab-03 │ │ ├── README.md │ │ └── code │ │ │ ├── step1.py │ │ │ ├── step3.py │ │ │ └── step4.py │ │ └── lab-04 │ │ ├── README.md │ │ └── code │ │ ├── step1.py │ │ ├── step2.py │ │ ├── step3.py │ │ └── step5.py ├── azure │ ├── csharp │ │ ├── 00-installing-prerequisites.md │ │ ├── 01-iac │ │ │ ├── 01-creating-a-new-project.md │ │ │ ├── 02-configuring-azure.md │ │ │ ├── 03-provisioning-infrastructure.md │ │ │ ├── 04-updating-your-infrastructure.md │ │ │ ├── 05-making-your-stack-configurable.md │ │ │ ├── 06-creating-a-second-stack.md │ │ │ ├── 07-destroying-your-infrastructure.md │ │ │ └── code │ │ │ │ ├── 01-creating-a-new-project │ │ │ │ └── step3.cs │ │ │ │ ├── 02-configuring-azure │ │ │ │ └── step2.cs │ │ │ │ ├── 03-provisioning-infrastructure │ │ │ │ └── step1.cs │ │ │ │ ├── 04-updating-your-infrastructure │ │ │ │ ├── step1.cs │ │ │ │ ├── step2.cs │ │ │ │ └── step4.cs │ │ │ │ └── 05-making-your-stack-configurable │ │ │ │ └── step2.cs │ │ ├── 02-serverless │ │ │ ├── README.md │ │ │ └── code │ │ │ │ ├── step1.cs │ │ │ │ ├── step2.cs │ │ │ │ ├── step3.cs │ │ │ │ ├── step4.cs │ │ │ │ └── step6.cs │ │ ├── 03-aci │ │ │ ├── README.md │ │ │ └── code │ │ │ │ ├── step2.cs │ │ │ │ └── step4.cs │ │ ├── 04-vms │ │ │ ├── README.md │ │ │ └── code │ │ │ │ ├── step1.cs │ │ │ │ └── step2.cs │ │ ├── 05-kubernetes │ │ │ ├── README.md │ │ │ └── code │ │ │ │ ├── step2.cs │ │ │ │ ├── step3.cs │ │ │ │ ├── step4.cs │ │ │ │ ├── step5.cs │ │ │ │ └── step7.cs │ │ ├── Infrastructure as Code Workshop Azure.pdf │ │ └── README.md │ └── python │ │ ├── 00-installing-prerequisites.md │ │ ├── 01-iac │ │ ├── 01-creating-a-new-project.md │ │ ├── 02-configuring-azure.md │ │ ├── 03-provisioning-infrastructure.md │ │ ├── 04-updating-your-infrastructure.md │ │ ├── 05-making-your-stack-configurable.md │ │ ├── 06-creating-a-second-stack.md │ │ ├── 07-destroying-your-infrastructure.md │ │ └── code │ │ │ ├── 03-provisioning-infrastructure │ │ │ └── step1.py │ │ │ ├── 04-updating-your-infrastructure │ │ │ ├── step1.py │ │ │ ├── step2.py │ │ │ └── step4.py │ │ │ └── 05-making-your-stack-configurable │ │ │ └── step2.py │ │ ├── Infrastructure as Code Workshop Azure.pdf │ │ └── README.md └── intro │ ├── python │ ├── README.md │ ├── lab-01 │ │ └── README.md │ ├── lab-02 │ │ └── README.md │ ├── lab-03 │ │ └── README.md │ └── lab-04 │ │ └── README.md │ └── typescript │ ├── README.md │ ├── lab-01 │ └── README.md │ ├── lab-02 │ ├── README.md │ └── code │ │ ├── .gitignore │ │ ├── Pulumi.dev.yaml │ │ ├── Pulumi.yaml │ │ ├── app │ │ ├── Dockerfile │ │ ├── index.js │ │ ├── index.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ └── tsconfig.json │ │ ├── index.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ └── tsconfig.json │ ├── lab-03 │ └── README.md │ └── lab-04 │ └── README.md └── slides └── InfrastructureAsCodeWorkshop.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | **/.idea/ 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repo is being deprecated in favor of https://github.com/pulumi/workshops. 2 | ![deprecated](https://img.shields.io/badge/repo%20status-deprecated-red) 3 | 4 | The workshops in this repo have been moved over to that repo and placed in the archive as newer workshops based off these workshops have been generated. 5 | 6 | # Infrastructure as Code Workshops 7 | 8 | This repo contains workshops for getting started with Pulumi through a series of hands-on labs. The workshops are organized by cloud provider and then separated into language specific workshops. 9 | 10 | ## Introductory Workshop 11 | 12 | The [introductory workshop](./labs/intro) is designed to guide you through some Pulumi fundamentals. It has a related GitHub repo with a pre-built development environment [here](https://github.com/pulumi/introduction-to-pulumi). 13 | 14 | ## AWS Workshops 15 | 16 | The AWS workshops are designed to guide you through examples of interacting with AWS using Pulumi. You can find more AWS workshops at the [AWS Workshops](https://pulumi.awsworkshop.io/) page. 17 | 18 | ### Prerequisities 19 | 20 | If you want to follow along at home, you'll need to install some dependencies on your local machine. 21 | 22 | * [Installing Prerequisites](./00-installing-prerequisites.md) 23 | 24 | ## In Person Workshop Outline 25 | 26 | The following is an overview of the in-person workshops and their current status: 27 | 28 | | | TypeScript | Python | Go | C# | 29 | | ------ | ------- | ------- | ------- | ------- | 30 | | AWS | [Get started][1] | [Get started][2] | [Get started][4] | [Get started][5] | 31 | | Azure | Coming soon | [Get started][6] | Coming soon | [Get started][3] | 32 | | GCP | Coming Soon | Coming soon | Coming soon | Coming soon | 33 | 34 | ## Next Steps 35 | 36 | After completing these labs, there are several topics you may want to explore. 37 | 38 | * [Continuous delivery](https://www.pulumi.com/docs/guides/continuous-delivery/) 39 | * [Secrets management](https://www.pulumi.com/blog/managing-secrets-with-pulumi/) 40 | * [Multi-project infrastructure architectures](https://www.pulumi.com/blog/architect-aws-application-infra-with-pulumi-stack-references/) 41 | * [Test-driven infrastructure](https://www.pulumi.com/blog/unit-testing-infrastructure-in-nodejs-and-mocha/) 42 | 43 | Thank you for checking out the Infrastructure as Code Workshop! More labs are on their way. Please [file an issue](https://github.com/pulumi/infrastructure-as-code-workshop/issues/new) if there are topics you'd like to see covered in the future. 44 | 45 | [1]: ./labs/aws/in-person/typescript/README.md 46 | [2]: ./labs/aws/in-person/python/README.md 47 | [3]: ./labs/azure/csharp/README.md 48 | [4]: ./labs/aws/in-person/go/README.md 49 | [5]: ./labs/aws/in-person/csharp/README.md 50 | [6]: ./labs/azure/python/README.md 51 | -------------------------------------------------------------------------------- /labs/aws/in-person/csharp/README.md: -------------------------------------------------------------------------------- 1 | ### Lab 1 — Modern Infrastructure as Code 2 | 3 | The first lab takes you on a tour of infrastructure as code concepts: 4 | 5 | 1. [Creating a New Project](./lab-01/01-creating-a-new-project.md) 6 | 2. [Configuring AWS](./lab-01/02-configuring-aws.md) 7 | 3. [Provisioning Infrastructure](./lab-01/03-provisioning-infrastructure.md) 8 | 4. [Updating your Infrastructure](./lab-01/04-updating-your-infrastructure.md) 9 | 5. [Making Your Stack Configurable](./lab-01/05-making-your-stack-configurable.md) 10 | 6. [Creating a Second Stack](./lab-01/06-creating-a-second-stack.md) 11 | 7. [Destroying Your Infrastructure](./lab-01/07-destroying-your-infrastructure.md) 12 | 13 | ### Lab 2 - Provision EC2 Virtual Machines 14 | 15 | In this [lab](./lab-02/README.md), you will learn about the creation of Virtual Machines in EC2. This lab will include 16 | the use of loops to create loadbalanced infrastructure in EC2. 17 | 18 | ### Lab 3 - Deploying Containers to Elastic Container Service (ECS) 19 | 20 | In this [lab](./lab-03/README.md), you will learn about how to create and deploy container based applications to Elastic 21 | Container Service (ECS). 22 | 23 | ### Lab 4 - Deploying Containers to a Kubernetes Cluster 24 | 25 | In this [lab](./lab-04/README.md), you will learn about how to create and deploy container based applications to a Kubernetes 26 | Cluster. 27 | 28 | -------------------------------------------------------------------------------- /labs/aws/in-person/csharp/lab-01/01-creating-a-new-project.md: -------------------------------------------------------------------------------- 1 | # Creating a New Project 2 | 3 | Infrastructure in Pulumi is organized into projects. Each project is a single program that, when run, declares the desired infrastructure for Pulumi to manage. 4 | 5 | ## Step 1 — Create a Directory 6 | 7 | Each Pulumi project lives in its own directory. Create one now and change into it: 8 | 9 | ```bash 10 | mkdir iac-workshop 11 | cd iac-workshop 12 | ``` 13 | 14 | > Pulumi will use the directory name as your project name by default. To create an independent project, simply name the directory differently. 15 | 16 | ## Step 2 — Initialize Your Project 17 | 18 | A Pulumi project is just a directory with some files in it. It's possible for you to create a new one by hand. The `pulumi new` command, however, automates the process: 19 | 20 | ```bash 21 | pulumi new csharp -y 22 | ``` 23 | 24 | This will print output similar to the following with a bit more information and status as it goes: 25 | 26 | ``` 27 | Created project 'iac-workshop' 28 | Created stack 'dev' 29 | 30 | ..... 31 | 32 | Your new project is ready to go! ✨ 33 | 34 | To perform an initial deployment, run 'pulumi up' 35 | ``` 36 | 37 | ## Step 3 — Inspect Your New Project 38 | 39 | Our project is comprised of multiple files: 40 | 41 | * **`MyStack.cs`**: your program's file for resource management 42 | * **`Program.cs`**: your program's main entrypoint file 43 | * **`iac-workshop.csproj`**: your project's project file 44 | * **`Pulumi.yaml`**: your project's metadata, containing its name and language 45 | 46 | Run `cat MyStack.cs` to see the contents of your project's empty program: 47 | 48 | ```csharp 49 | using Pulumi; 50 | 51 | class MyStack : Stack 52 | { 53 | public MyStack() 54 | { 55 | // Add your resources here 56 | } 57 | } 58 | ``` 59 | 60 | Feel free to explore the other files, although we won't be editing any of them by hand. 61 | 62 | # Next Steps 63 | 64 | * [Configuring AWS](./02-configuring-aws.md) 65 | -------------------------------------------------------------------------------- /labs/aws/in-person/csharp/lab-01/02-configuring-aws.md: -------------------------------------------------------------------------------- 1 | # Configuring AWS 2 | 3 | Now that you have a basic project, let's configure AWS support for it. 4 | 5 | ## Step 1 — Install the AWS Package 6 | 7 | To install the AWS package, run the following command: 8 | 9 | ```bash 10 | dotnet add package Pulumi.Aws --version 1.28.0-preview 11 | ``` 12 | 13 | ## Step 2 — Import the AWS Package 14 | 15 | Now that the AWS package is installed, add the following line to our using statements in `MyStack.cs` to import it: 16 | 17 | ```csharp 18 | using Aws = Pulumi.Aws; 19 | ``` 20 | 21 | 22 | > :white_check_mark: After this change, your `MyStack.cs` should [look like this](code/02-configuring-aws/step2.cs). 23 | 24 | ## Step 3 — Configure an AWS Region 25 | 26 | Configure the AWS region you would like to deploy to: 27 | 28 | ```bash 29 | pulumi config set aws:region us-east-1 30 | ``` 31 | 32 | > If you are doing an interactive lab, please use the recommended region. This will ensure AWS limits are not exceeded during the labs. 33 | 34 | Feel free to choose any AWS region that supports the services used in these labs ([see this table](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions) for a list of available regions). 35 | 36 | ## (Optional) Step 4 — Configure an AWS Profile 37 | 38 | If you're using an alternative AWS profile, you can tell Pulumi which to use in one of two ways: 39 | 40 | * Using an environment variable: `export AWS_PROFILE=` 41 | * Using configuration: `pulumi config set aws:profile ` 42 | 43 | ## Next Steps 44 | 45 | * [Provisioning a S3 Bucket](./03-provisioning-infrastructure.md) 46 | -------------------------------------------------------------------------------- /labs/aws/in-person/csharp/lab-01/06-creating-a-second-stack.md: -------------------------------------------------------------------------------- 1 | # Creating a Second Stack 2 | 3 | It is easy to create multiple instances of the same project. This is called a stack. This is handy for multiple development 4 | or test environments, staging versus production, and scaling a given infrastructure across many regions. 5 | 6 | ## Step 1 — Create and Configure a New Stack 7 | 8 | Create a new stack: 9 | 10 | ```bash 11 | pulumi stack init prod 12 | ``` 13 | 14 | Next, configure its two required variables: 15 | 16 | ```bash 17 | pulumi config set aws:region eu-west-1 18 | pulumi config set iac-workshop:siteDir wwwprod 19 | ``` 20 | 21 | If you are ever curious to see the list of stacks for your current project, run this command: 22 | 23 | ```bash 24 | pulumi stack ls 25 | ``` 26 | 27 | It will print all stacks for this project that are available to you: 28 | 29 | ``` 30 | NAME LAST UPDATE RESOURCE COUNT URL 31 | dev 30 minutes ago 5 https://app.pulumi.com/joeduffy/iac-workshop/dev 32 | prod* 3 minutes ago 0 https://app.pulumi.com/joeduffy/iac-workshop/prod 33 | ``` 34 | 35 | ## Step 2 — Populate the New Site Directory 36 | 37 | It would have been possible to use the existing `www` directory for the `siteDir`. In this example, you will use a 38 | different `wwwprod` directory, to demonstrate the value of having configurability. 39 | 40 | Create this new directory: 41 | 42 | ```bash 43 | mkdir wwwprod 44 | ``` 45 | 46 | Add a new `index.html` file to it: 47 | 48 | ```html 49 | 50 | 51 |

Hello Pulumi

52 |

(in production!)

53 | 54 | 55 | ``` 56 | 57 | ## Step 3 — Deploy the New Stack 58 | 59 | Now deploy all of the changes: 60 | 61 | ```bash 62 | pulumi up 63 | ``` 64 | 65 | This will create an entirely new set of resources from scratch, unrelated to the existing `dev` stack's resources. 66 | 67 | ``` 68 | Updating (prod): 69 | 70 | Type Name Status 71 | + pulumi:pulumi:Stack iac-workshop-prod created 72 | + ├─ aws:s3:Bucket my-bucket created 73 | + └─ aws:s3:BucketObject index.html created 74 | 75 | Outputs: 76 | WebsiteEndpoint: "http://my-bucket-c7318c1.s3-website-eu-west-1.amazonaws.com" 77 | BucketName : "my-bucket-c7318c1" 78 | 79 | Resources: 80 | + 3 created 81 | 82 | Duration: 28s 83 | 84 | Permalink: https://app.pulumi.com/joeduffy/iac-workshop/prod/updates/1 85 | ``` 86 | 87 | Now fetch your new website: 88 | 89 | ```bash 90 | curl $(pulumi stack output WebsiteEndpoint) 91 | ``` 92 | 93 | Notice that it's the new production version of your content: 94 | 95 | ``` 96 | 97 | 98 |

Hello Pulumi

99 |

(in production!)

100 | 101 | 102 | ``` 103 | 104 | ## Next Steps 105 | 106 | * [Destroying Your Infrastructure](./07-destroying-your-infrastructure.md) 107 | -------------------------------------------------------------------------------- /labs/aws/in-person/csharp/lab-01/code/02-configuring-aws/step2.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | using Aws = Pulumi.Aws; 3 | 4 | class MyStack : Stack 5 | { 6 | public MyStack() 7 | { 8 | // Add your resources here 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /labs/aws/in-person/csharp/lab-01/code/03-provisioning-infrastructure/step1.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | using Pulumi.Aws.S3; 3 | using Aws = Pulumi.Aws; 4 | 5 | class MyStack : Stack 6 | { 7 | public MyStack() 8 | { 9 | var bucket = new Aws.S3.Bucket("my-bucket", new BucketArgs()); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /labs/aws/in-person/csharp/lab-01/code/03-provisioning-infrastructure/step4.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | using Pulumi.Aws.S3; 3 | using Aws = Pulumi.Aws; 4 | 5 | class MyStack : Stack 6 | { 7 | public MyStack() 8 | { 9 | var bucket = new Aws.S3.Bucket("my-bucket", new BucketArgs()); 10 | this.BucketName = bucket.BucketName; 11 | } 12 | 13 | [Output] public Output BucketName { get; set; } 14 | } 15 | -------------------------------------------------------------------------------- /labs/aws/in-person/csharp/lab-01/code/04-updating-your-infrastructure/step1.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Pulumi; 3 | using Pulumi.Aws.S3; 4 | using Aws = Pulumi.Aws; 5 | 6 | class MyStack : Stack 7 | { 8 | public MyStack() 9 | { 10 | var bucket = new Aws.S3.Bucket("my-bucket", new BucketArgs()); 11 | this.BucketName = bucket.BucketName; 12 | 13 | var bucketFile = Path.Combine("site", "index.html"); 14 | var bucketObject = new Aws.S3.BucketObject("index.html", new BucketObjectArgs 15 | { 16 | Bucket = bucket.BucketName, 17 | Source = new FileAsset(bucketFile) 18 | }); 19 | } 20 | 21 | [Output] public Output BucketName { get; set; } 22 | } 23 | -------------------------------------------------------------------------------- /labs/aws/in-person/csharp/lab-01/code/04-updating-your-infrastructure/step2.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Pulumi; 3 | using Pulumi.Aws.S3; 4 | using Pulumi.Aws.S3.Inputs; 5 | using Aws = Pulumi.Aws; 6 | 7 | class MyStack : Stack 8 | { 9 | public MyStack() 10 | { 11 | var bucket = new Aws.S3.Bucket("my-bucket", new BucketArgs 12 | { 13 | Website = new BucketWebsiteArgs 14 | { 15 | IndexDocument = "index.html", 16 | } 17 | }); 18 | this.BucketName = bucket.BucketName; 19 | this.WebsiteEndpoint = bucket.WebsiteEndpoint; 20 | 21 | var bucketFile = Path.Combine("site", "index.html"); 22 | var bucketObject = new Aws.S3.BucketObject("index.html", new BucketObjectArgs 23 | { 24 | Bucket = bucket.BucketName, 25 | Source = new FileAsset(bucketFile), 26 | Acl = "public-read", 27 | ContentType = "text/html", 28 | }); 29 | } 30 | 31 | [Output] public Output BucketName { get; set; } 32 | [Output] public Output WebsiteEndpoint { get; set; } 33 | } 34 | -------------------------------------------------------------------------------- /labs/aws/in-person/csharp/lab-01/code/05-making-your-stack-configurable/step2.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Pulumi; 3 | using Pulumi.Aws.S3; 4 | using Pulumi.Aws.S3.Inputs; 5 | using Aws = Pulumi.Aws; 6 | 7 | class MyStack : Stack 8 | { 9 | public MyStack() 10 | { 11 | var config = new Pulumi.Config(); 12 | var siteDir = config.Get("siteDir"); 13 | 14 | var bucket = new Aws.S3.Bucket("my-bucket", new BucketArgs 15 | { 16 | Website = new BucketWebsiteArgs 17 | { 18 | IndexDocument = "index.html", 19 | } 20 | }); 21 | this.BucketName = bucket.BucketName; 22 | this.WebsiteEndpoint = bucket.WebsiteEndpoint; 23 | 24 | var bucketFile = Path.Combine(siteDir, "index.html"); 25 | var bucketObject = new Aws.S3.BucketObject("index.html", new BucketObjectArgs 26 | { 27 | Bucket = bucket.BucketName, 28 | Source = new FileAsset(bucketFile), 29 | Acl = "public-read", 30 | ContentType = "text/html", 31 | }); 32 | } 33 | 34 | [Output] public Output BucketName { get; set; } 35 | [Output] public Output WebsiteEndpoint { get; set; } 36 | } 37 | -------------------------------------------------------------------------------- /labs/aws/in-person/csharp/lab-01/code/05-making-your-stack-configurable/step4.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Pulumi; 3 | using Pulumi.Aws.S3; 4 | using Pulumi.Aws.S3.Inputs; 5 | using Aws = Pulumi.Aws; 6 | 7 | class MyStack : Stack 8 | { 9 | public MyStack() 10 | { 11 | var config = new Pulumi.Config(); 12 | var siteDir = config.Get("siteDir"); 13 | 14 | var bucket = new Aws.S3.Bucket("my-bucket", new BucketArgs 15 | { 16 | Website = new BucketWebsiteArgs 17 | { 18 | IndexDocument = "index.html", 19 | } 20 | }); 21 | this.BucketName = bucket.BucketName; 22 | this.WebsiteEndpoint = bucket.WebsiteEndpoint; 23 | 24 | foreach (var file in Directory.EnumerateFiles(siteDir)) 25 | { 26 | var name = Path.GetFileName(file); 27 | var bucketObject = new Aws.S3.BucketObject(name, new BucketObjectArgs 28 | { 29 | Bucket = bucket.BucketName, 30 | Source = new FileAsset(Path.Combine(siteDir, Path.GetFileName(file))), 31 | Acl = "public-read", 32 | ContentType = "text/html", 33 | }); 34 | } 35 | } 36 | 37 | [Output] public Output BucketName { get; set; } 38 | [Output] public Output WebsiteEndpoint { get; set; } 39 | } 40 | -------------------------------------------------------------------------------- /labs/aws/in-person/csharp/lab-02/code/step1.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | 3 | class MyStack : Stack 4 | { 5 | public MyStack() 6 | { 7 | var ami = Output.Create(Pulumi.Aws.Invokes.GetAmi(new Pulumi.Aws.GetAmiArgs 8 | { 9 | MostRecent = true, 10 | Owners = {"amazon"}, 11 | Filters = {new Pulumi.Aws.Inputs.GetAmiFiltersArgs 12 | { 13 | Name = "name", Values = {"amzn2-ami-k*-hvm-*-x86_64-gp2"} 14 | }} 15 | })); 16 | 17 | var group = new Pulumi.Aws.Ec2.SecurityGroup("web-secgrp", new Pulumi.Aws.Ec2.SecurityGroupArgs 18 | { 19 | Description = "Enable HTTP access", 20 | Ingress = 21 | { 22 | new Pulumi.Aws.Ec2.Inputs.SecurityGroupIngressArgs 23 | { 24 | Protocol = "tcp", 25 | FromPort = 80, 26 | ToPort = 80, 27 | CidrBlocks = {"0.0.0.0/0"} 28 | }, 29 | new Pulumi.Aws.Ec2.Inputs.SecurityGroupIngressArgs 30 | { 31 | Protocol = "icmp", 32 | FromPort = 8, 33 | ToPort = 80, 34 | CidrBlocks = {"0.0.0.0/0"} 35 | } 36 | } 37 | }); 38 | 39 | var userData = @" 40 | #!/bin/bash 41 | echo ""Hello, World!"" > index.html 42 | nohup python -m SimpleHTTPServer 80 & 43 | "; 44 | 45 | var server = new Pulumi.Aws.Ec2.Instance("web-server-www", new Pulumi.Aws.Ec2.InstanceArgs 46 | { 47 | InstanceType = "t2.micro", 48 | VpcSecurityGroupIds = {group.Id}, 49 | UserData = userData, 50 | Ami = ami.Apply(a => a.Id) 51 | }); 52 | 53 | this.PublicIp = server.PublicIp; 54 | this.PublicDns = server.PublicDns; 55 | } 56 | 57 | [Output] public Output PublicIp { get; set; } 58 | [Output] public Output PublicDns { get; set; } 59 | } 60 | -------------------------------------------------------------------------------- /labs/aws/in-person/csharp/lab-02/code/step3.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Immutable; 4 | using System.Linq; 5 | using Pulumi; 6 | 7 | class MyStack : Stack 8 | { 9 | public MyStack() 10 | { 11 | var ami = Output.Create(Pulumi.Aws.GetAmi.InvokeAsync(new Pulumi.Aws.GetAmiArgs 12 | { 13 | MostRecent = true, 14 | Owners = {"amazon"}, 15 | Filters = {new Pulumi.Aws.Inputs.GetAmiFiltersArgs 16 | { 17 | Name = "name", Values = {"amzn2-ami-k*-hvm-*-x86_64-gp2"} 18 | }} 19 | })); 20 | 21 | var group = new Pulumi.Aws.Ec2.SecurityGroup("web-secgrp", new Pulumi.Aws.Ec2.SecurityGroupArgs 22 | { 23 | Description = "Enable HTTP access", 24 | Ingress = 25 | { 26 | new Pulumi.Aws.Ec2.Inputs.SecurityGroupIngressArgs 27 | { 28 | Protocol = "tcp", 29 | FromPort = 80, 30 | ToPort = 80, 31 | CidrBlocks = {"0.0.0.0/0"} 32 | }, 33 | new Pulumi.Aws.Ec2.Inputs.SecurityGroupIngressArgs 34 | { 35 | Protocol = "icmp", 36 | FromPort = 8, 37 | ToPort = 80, 38 | CidrBlocks = {"0.0.0.0/0"} 39 | } 40 | } 41 | }); 42 | 43 | var userData = @" 44 | #!/bin/bash 45 | echo ""Hello, World!"" > index.html 46 | nohup python -m SimpleHTTPServer 80 & 47 | "; 48 | 49 | var azs = Pulumi.Aws.GetAvailabilityZones.InvokeAsync(new Pulumi.Aws.GetAvailabilityZonesArgs()).Result; 50 | var hostnames = new List>(); 51 | var ips = new List>(); 52 | foreach (var az in azs.Names) 53 | { 54 | var server = new Pulumi.Aws.Ec2.Instance($"web-server-{az}", new Pulumi.Aws.Ec2.InstanceArgs 55 | { 56 | InstanceType = "t2.micro", 57 | VpcSecurityGroupIds = {group.Id}, 58 | UserData = userData, 59 | Ami = ami.Apply(a => a.Id), 60 | AvailabilityZone = az, 61 | }); 62 | 63 | hostnames.Add(server.PublicDns); 64 | ips.Add(server.PublicIp); 65 | } 66 | 67 | this.PublicIps = Output.All(ips.ToImmutableArray()); 68 | this.PublicDns = Output.All(hostnames.ToImmutableArray()); 69 | } 70 | 71 | [Output] public Output> PublicIps { get; set; } 72 | [Output] public Output> PublicDns { get; set; } 73 | } 74 | -------------------------------------------------------------------------------- /labs/aws/in-person/csharp/lab-03/code/step1.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | 3 | class MyStack : Stack 4 | { 5 | public MyStack() 6 | { 7 | var cluster = new Pulumi.Aws.Ecs.Cluster("app-cluster"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /labs/aws/in-person/csharp/lab-03/code/step2.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | 3 | class MyStack : Stack 4 | { 5 | public MyStack() 6 | { 7 | var cluster = new Pulumi.Aws.Ecs.Cluster("app-cluster"); 8 | 9 | // Read back the default VPC and public subnets, which we will use. 10 | var vpc = Output.Create(Pulumi.Aws.Ec2.GetVpc.InvokeAsync(new Pulumi.Aws.Ec2.GetVpcArgs { Default = true })); 11 | var vpcId = vpc.Apply(vpc => vpc.Id); 12 | var subnet = vpcId.Apply(id => Pulumi.Aws.Ec2.GetSubnetIds.InvokeAsync(new Pulumi.Aws.Ec2.GetSubnetIdsArgs { VpcId = id })); 13 | var subnetIds = subnet.Apply(s => s.Ids); 14 | 15 | // Create a SecurityGroup that permits HTTP ingress and unrestricted egress. 16 | var webSg = new Pulumi.Aws.Ec2.SecurityGroup("web-sg", new Pulumi.Aws.Ec2.SecurityGroupArgs 17 | { 18 | VpcId = vpcId, 19 | Egress = 20 | { 21 | new Pulumi.Aws.Ec2.Inputs.SecurityGroupEgressArgs 22 | { 23 | Protocol = "-1", 24 | FromPort = 0, 25 | ToPort = 0, 26 | CidrBlocks = {"0.0.0.0/0"} 27 | } 28 | }, 29 | Ingress = 30 | { 31 | new Pulumi.Aws.Ec2.Inputs.SecurityGroupIngressArgs 32 | { 33 | Protocol = "tcp", 34 | FromPort = 80, 35 | ToPort = 80, 36 | CidrBlocks = {"0.0.0.0/0"} 37 | } 38 | } 39 | }); 40 | 41 | // Create a load balancer to listen for HTTP traffic on port 80. 42 | var webLb = new Pulumi.Aws.LB.LoadBalancer("web-lb", new Pulumi.Aws.LB.LoadBalancerArgs 43 | { 44 | Subnets = subnetIds, 45 | SecurityGroups = { webSg.Id } 46 | }); 47 | var webTg = new Pulumi.Aws.LB.TargetGroup("web-tg", new Pulumi.Aws.LB.TargetGroupArgs 48 | { 49 | Port = 80, 50 | Protocol = "HTTP", 51 | TargetType = "ip", 52 | VpcId = vpcId 53 | }); 54 | var webListener = new Pulumi.Aws.LB.Listener("web-listener", new Pulumi.Aws.LB.ListenerArgs 55 | { 56 | LoadBalancerArn = webLb.Arn, 57 | Port = 80, 58 | DefaultActions = 59 | { 60 | new Pulumi.Aws.LB.Inputs.ListenerDefaultActionArgs 61 | { 62 | Type = "forward", 63 | TargetGroupArn = webTg.Arn, 64 | } 65 | } 66 | }); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/README.md: -------------------------------------------------------------------------------- 1 | ### Lab 1 — Modern Infrastructure as Code 2 | 3 | The first lab takes you on a tour of infrastructure as code concepts: 4 | 5 | 1. [Creating a New Project](./lab-01/01-creating-a-new-project.md) 6 | 2. [Configuring AWS](./lab-01/02-configuring-aws.md) 7 | 3. [Provisioning Infrastructure](./lab-01/03-provisioning-infrastructure.md) 8 | 4. [Updating your Infrastructure](./lab-01/04-updating-your-infrastructure.md) 9 | 5. [Making Your Stack Configurable](./lab-01/05-making-your-stack-configurable.md) 10 | 6. [Creating a Second Stack](./lab-01/06-creating-a-second-stack.md) 11 | 7. [Destroying Your Infrastructure](./lab-01/07-destroying-your-infrastructure.md) 12 | 13 | ### Lab 2 - Provision EC2 Virtual Machines 14 | 15 | In this [lab](./lab-02/README.md), you will learn about the creation of Virtual Machines in EC2. This lab will include 16 | the use of loops to create loadbalanced infrastructure in EC2. 17 | 18 | ### Lab 3 - Deploying Containers to Elastic Container Service (ECS) 19 | 20 | In this [lab](./lab-03/README.md), you will learn about how to create and deploy container based applications to Elastic 21 | Container Service (ECS). 22 | 23 | ### Lab 4 - Deploying a Kubernetes Cluster 24 | 25 | In this [lab](./lab-04/README.md), you will learn about how to create and deploy a Kubernetes Cluster in EKS. 26 | Cluster. 27 | 28 | ### Lab 5 - Deploying Containers to a Kubernetes Cluster 29 | 30 | In this [lab](./lab-05/README.md), you will learn about how to create and deploy container based applications to a Kubernetes 31 | Cluster. 32 | 33 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-01/01-creating-a-new-project.md: -------------------------------------------------------------------------------- 1 | # Creating a New Project 2 | 3 | Infrastructure in Pulumi is organized into projects. Each project is a single program that, when run, declares the desired infrastructure for Pulumi to manage. 4 | 5 | ## Step 1 — Create a Directory 6 | 7 | Each Pulumi project lives in its own directory. Create one now and change into it: 8 | 9 | ```bash 10 | mkdir iac-workshop 11 | cd iac-workshop 12 | ``` 13 | 14 | > Pulumi will use the directory name as your project name by default. To create an independent project, simply name the directory differently. 15 | 16 | ## Step 2 — Initialize Your Project 17 | 18 | A Pulumi project is just a directory with some files in it. It's possible for you to create a new one by hand. The `pulumi new` command, however, automates the process: 19 | 20 | ```bash 21 | pulumi new go -y 22 | ``` 23 | 24 | This will print output similar to the following with a bit more information and status as it goes: 25 | 26 | ``` 27 | Created project 'iac-workshop' 28 | Created stack 'dev' 29 | 30 | Your new project is ready to go! ✨ 31 | 32 | To perform an initial deployment, run 'dep ensure', then, run 'pulumi up' 33 | ``` 34 | 35 | This command has created all the files we need, initialized a new stack named `dev` (an instance of our project). We now need 36 | to install our dependencies using [`go dep`](https://github.com/golang/dep). 37 | 38 | ## Step 3 — Inspect Your New Project 39 | 40 | Our project is comprised of multiple files: 41 | 42 | * **`main.go`**: your program's main entrypoint file 43 | * **`Gopkg.toml`**: your project's go dependency information 44 | * **`Pulumi.yaml`**: your project's metadata, containing its name and language 45 | 46 | Run `cat main.go` to see the contents of your project's empty program: 47 | 48 | ```go 49 | package main 50 | 51 | import ( 52 | "github.com/pulumi/pulumi/sdk/go/pulumi" 53 | ) 54 | 55 | func main() { 56 | pulumi.Run(func(ctx *pulumi.Context) error { 57 | return nil 58 | }) 59 | } 60 | ``` 61 | 62 | Feel free to explore the other files, although we won't be editing any of them by hand. 63 | 64 | # Next Steps 65 | 66 | * [Configuring AWS](./02-configuring-aws.md) 67 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-01/02-configuring-aws.md: -------------------------------------------------------------------------------- 1 | # Configuring AWS 2 | 3 | Now that you have a basic project, let's configure AWS support for it. 4 | 5 | ## Step 1 — Install the AWS Package 6 | 7 | We can edit the contents of our Gopkg.toml file to install the AWS Package. Add the following: 8 | 9 | ```toml 10 | [[constraint]] 11 | name = "github.com/pulumi/pulumi-aws" 12 | version = "1.27.0" 13 | ``` 14 | 15 | ## Step 2 — Import the AWS Package 16 | 17 | Now that the AWS package is installed, add the following line to our imports in `main.go` to import it: 18 | 19 | ```go 20 | github.com/pulumi/pulumi-aws/sdk/go/aws/s3 21 | ``` 22 | 23 | 24 | > :white_check_mark: After this change, your `main.go` should [look like this](code/02-configuring-aws/step2.go). 25 | 26 | ## Step 3 — Configure an AWS Region 27 | 28 | Configure the AWS region you would like to deploy to: 29 | 30 | ```bash 31 | pulumi config set aws:region us-east-1 32 | ``` 33 | 34 | > If you are doing an interactive lab, please use the recommended region. This will ensure AWS limits are not exceeded during the labs. 35 | 36 | Feel free to choose any AWS region that supports the services used in these labs ([see this table](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions) for a list of available regions). 37 | 38 | ## (Optional) Step 4 — Configure an AWS Profile 39 | 40 | If you're using an alternative AWS profile, you can tell Pulumi which to use in one of two ways: 41 | 42 | * Using an environment variable: `export AWS_PROFILE=` 43 | * Using configuration: `pulumi config set aws:profile ` 44 | 45 | ## Next Steps 46 | 47 | * [Provisioning a S3 Bucket](./03-provisioning-infrastructure.md) 48 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-01/06-creating-a-second-stack.md: -------------------------------------------------------------------------------- 1 | # Creating a Second Stack 2 | 3 | It is easy to create multiple instances of the same project. This is called a stack. This is handy for multiple development 4 | or test environments, staging versus production, and scaling a given infrastructure across many regions. 5 | 6 | ## Step 1 — Create and Configure a New Stack 7 | 8 | Create a new stack: 9 | 10 | ```bash 11 | pulumi stack init prod 12 | ``` 13 | 14 | Next, configure its two required variables: 15 | 16 | ```bash 17 | pulumi config set aws:region eu-west-1 18 | pulumi config set iac-workshop:siteDir wwwprod 19 | ``` 20 | 21 | If you are ever curious to see the list of stacks for your current project, run this command: 22 | 23 | ```bash 24 | pulumi stack ls 25 | ``` 26 | 27 | It will print all stacks for this project that are available to you: 28 | 29 | ``` 30 | NAME LAST UPDATE RESOURCE COUNT URL 31 | dev 30 minutes ago 5 https://app.pulumi.com/joeduffy/iac-workshop/dev 32 | prod* 3 minutes ago 0 https://app.pulumi.com/joeduffy/iac-workshop/prod 33 | ``` 34 | 35 | ## Step 2 — Populate the New Site Directory 36 | 37 | It would have been possible to use the existing `www` directory for the `siteDir`. In this example, you will use a 38 | different `wwwprod` directory, to demonstrate the value of having configurability. 39 | 40 | Create this new directory: 41 | 42 | ```bash 43 | mkdir wwwprod 44 | ``` 45 | 46 | Add a new `index.html` file to it: 47 | 48 | ```html 49 | 50 | 51 |

Hello Pulumi

52 |

(in production!)

53 | 54 | 55 | ``` 56 | 57 | ## Step 3 — Deploy the New Stack 58 | 59 | Now deploy all of the changes: 60 | 61 | ```bash 62 | pulumi up 63 | ``` 64 | 65 | This will create an entirely new set of resources from scratch, unrelated to the existing `dev` stack's resources. 66 | 67 | ``` 68 | Updating (prod): 69 | 70 | Type Name Status 71 | + pulumi:pulumi:Stack iac-workshop-prod created 72 | + ├─ aws:s3:Bucket my-bucket created 73 | + └─ aws:s3:BucketObject index.html created 74 | 75 | Outputs: 76 | bucketEndpoint: "http://my-bucket-c7318c1.s3-website-eu-west-1.amazonaws.com" 77 | bucketName : "my-bucket-c7318c1" 78 | 79 | Resources: 80 | + 3 created 81 | 82 | Duration: 28s 83 | 84 | Permalink: https://app.pulumi.com/joeduffy/iac-workshop/prod/updates/1 85 | ``` 86 | 87 | Now fetch your new website: 88 | 89 | ```bash 90 | curl $(pulumi stack output bucket_endpoint) 91 | ``` 92 | 93 | Notice that it's the new production version of your content: 94 | 95 | ``` 96 | 97 | 98 |

Hello Pulumi

99 |

(in production!)

100 | 101 | 102 | ``` 103 | 104 | ## Next Steps 105 | 106 | * [Destroying Your Infrastructure](./07-destroying-your-infrastructure.md) 107 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-01/code/02-configuring-aws/step2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/pulumi/pulumi/sdk/go/pulumi" 5 | "github.com/pulumi/pulumi-aws/sdk/go/aws/s3" 6 | ) 7 | 8 | func main() { 9 | pulumi.Run(func(ctx *pulumi.Context) error { 10 | return nil 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-01/code/03-provisioning-infrastructure/step1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/pulumi/pulumi/sdk/go/pulumi" 5 | "github.com/pulumi/pulumi-aws/sdk/go/aws/s3" 6 | ) 7 | 8 | func main() { 9 | pulumi.Run(func(ctx *pulumi.Context) error { 10 | 11 | _, err := s3.NewBucket(ctx, "my-bucket", nil) 12 | if err != nil { 13 | return err 14 | } 15 | 16 | return nil 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-01/code/03-provisioning-infrastructure/step4.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/pulumi/pulumi/sdk/go/pulumi" 5 | "github.com/pulumi/pulumi-aws/sdk/go/aws/s3" 6 | ) 7 | 8 | func main() { 9 | pulumi.Run(func(ctx *pulumi.Context) error { 10 | 11 | bucket, err := s3.NewBucket(ctx, "my-bucket", nil) 12 | if err != nil { 13 | return err 14 | } 15 | 16 | ctx.Export("bucketName", bucket.Bucket) 17 | 18 | return nil 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-01/code/04-updating-your-infrastructure/step1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "path/filepath" 5 | 6 | "github.com/pulumi/pulumi/sdk/go/pulumi" 7 | "github.com/pulumi/pulumi-aws/sdk/go/aws/s3" 8 | ) 9 | 10 | func main() { 11 | pulumi.Run(func(ctx *pulumi.Context) error { 12 | 13 | bucket, err := s3.NewBucket(ctx, "my-bucket", nil) 14 | if err != nil { 15 | return err 16 | } 17 | 18 | bucketFile := filepath.Join("site", "index.html") 19 | _, err = s3.NewBucketObject(ctx, "index.html", &s3.BucketObjectArgs{ 20 | Bucket: bucket.Bucket, 21 | Source: pulumi.NewFileAsset(bucketFile), 22 | }) 23 | 24 | ctx.Export("bucketName", bucket.Bucket) 25 | 26 | return nil 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-01/code/04-updating-your-infrastructure/step2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "mime" 5 | "path" 6 | "path/filepath" 7 | 8 | "github.com/pulumi/pulumi-aws/sdk/go/aws/s3" 9 | "github.com/pulumi/pulumi/sdk/go/pulumi" 10 | ) 11 | 12 | func main() { 13 | pulumi.Run(func(ctx *pulumi.Context) error { 14 | 15 | bucket, err := s3.NewBucket(ctx, "my-bucket", &s3.BucketArgs{ 16 | Website: s3.BucketWebsiteArgs{ 17 | IndexDocument: pulumi.String("index.html"), 18 | }, 19 | }) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | bucketFile := filepath.Join("site", "index.html") 25 | _, err = s3.NewBucketObject(ctx, "index.html", &s3.BucketObjectArgs{ 26 | Bucket: bucket.Bucket, 27 | Source: pulumi.NewFileAsset(bucketFile), 28 | Acl: pulumi.String("public-read"), 29 | ContentType: pulumi.String(mime.TypeByExtension(path.Ext(bucketFile))), 30 | }) 31 | 32 | ctx.Export("bucketName", bucket.Bucket) 33 | ctx.Export("bucketEndpoint", bucket.WebsiteEndpoint) 34 | 35 | return nil 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-01/code/05-making-your-stack-configurable/step2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "mime" 5 | "path" 6 | "path/filepath" 7 | 8 | "github.com/pulumi/pulumi-aws/sdk/go/aws/s3" 9 | "github.com/pulumi/pulumi/sdk/go/pulumi" 10 | "github.com/pulumi/pulumi/sdk/go/pulumi/config" 11 | ) 12 | 13 | func main() { 14 | pulumi.Run(func(ctx *pulumi.Context) error { 15 | conf := config.New(ctx, "") 16 | siteDir := conf.Get("siteDir") 17 | 18 | bucket, err := s3.NewBucket(ctx, "my-bucket", &s3.BucketArgs{ 19 | Website: s3.BucketWebsiteArgs{ 20 | IndexDocument: pulumi.String("index.html"), 21 | }, 22 | }) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | bucketFile := filepath.Join(siteDir, "index.html") 28 | mimeType := mime.TypeByExtension(path.Ext(bucketFile)) 29 | _, err = s3.NewBucketObject(ctx, "index.html", &s3.BucketObjectArgs{ 30 | Bucket: bucket.Bucket, 31 | Source: pulumi.NewFileAsset(bucketFile), 32 | Acl: pulumi.String("public-read"), 33 | ContentType: pulumi.String(mimeType), 34 | }) 35 | 36 | ctx.Export("bucketName", bucket.Bucket) 37 | ctx.Export("bucketEndpoint", bucket.WebsiteEndpoint) 38 | 39 | return nil 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-01/code/05-making-your-stack-configurable/step4.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/ioutil" 5 | "mime" 6 | "path" 7 | "path/filepath" 8 | 9 | "github.com/pulumi/pulumi-aws/sdk/go/aws/s3" 10 | "github.com/pulumi/pulumi/sdk/go/pulumi" 11 | "github.com/pulumi/pulumi/sdk/go/pulumi/config" 12 | ) 13 | 14 | func main() { 15 | pulumi.Run(func(ctx *pulumi.Context) error { 16 | conf := config.New(ctx, "") 17 | siteDir := conf.Get("siteDir") 18 | 19 | bucket, err := s3.NewBucket(ctx, "my-bucket", &s3.BucketArgs{ 20 | Website: s3.BucketWebsiteArgs{ 21 | IndexDocument: pulumi.String("index.html"), 22 | }, 23 | }) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | // For each file in the directory, create an S3 object stored in `bucket` 29 | files, err := ioutil.ReadDir(siteDir) 30 | if err != nil { 31 | return err 32 | } 33 | for _, item := range files { 34 | name := item.Name() 35 | if _, err := s3.NewBucketObject(ctx, name, &s3.BucketObjectArgs{ 36 | Bucket: bucket.Bucket, // reference to the s3.Bucket object 37 | Source: pulumi.NewFileAsset(filepath.Join(siteDir, name)), // use FileAsset to point to a file 38 | ContentType: pulumi.String(mime.TypeByExtension(path.Ext(name))), // set the MIME type of the file 39 | Acl: pulumi.String("public-read"), 40 | }); err != nil { 41 | return err 42 | } 43 | } 44 | 45 | ctx.Export("bucketName", bucket.Bucket) 46 | ctx.Export("bucketEndpoint", bucket.WebsiteEndpoint) 47 | 48 | return nil 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-02/code/step1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/pulumi/pulumi-aws/sdk/go/aws" 5 | "github.com/pulumi/pulumi-aws/sdk/go/aws/ec2" 6 | "github.com/pulumi/pulumi/sdk/go/pulumi" 7 | ) 8 | 9 | func main() { 10 | pulumi.Run(func(ctx *pulumi.Context) error { 11 | mostRecent := true 12 | ami, err := aws.GetAmi(ctx, &aws.GetAmiArgs{ 13 | Filters: []aws.GetAmiFilter{ 14 | { 15 | Name: "name", 16 | Values: []string{"amzn2-ami-k*-hvm-*-x86_64-gp2"}, 17 | }, 18 | }, 19 | Owners: []string{"amazon"}, 20 | MostRecent: &mostRecent, 21 | }) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | group, err := ec2.NewSecurityGroup(ctx, "web-secgrp", &ec2.SecurityGroupArgs{ 27 | Ingress: ec2.SecurityGroupIngressArray{ 28 | ec2.SecurityGroupIngressArgs{ 29 | Protocol: pulumi.String("tcp"), 30 | FromPort: pulumi.Int(80), 31 | ToPort: pulumi.Int(80), 32 | CidrBlocks: pulumi.StringArray{pulumi.String("0.0.0.0/0")}, 33 | }, 34 | ec2.SecurityGroupIngressArgs{ 35 | Protocol: pulumi.String("icmp"), 36 | FromPort: pulumi.Int(8), 37 | ToPort: pulumi.Int(80), 38 | CidrBlocks: pulumi.StringArray{pulumi.String("0.0.0.0/0")}, 39 | }, 40 | }, 41 | }) 42 | if err != nil { 43 | return err 44 | } 45 | 46 | srv, err := ec2.NewInstance(ctx, "web-server-www", &ec2.InstanceArgs{ 47 | Tags: pulumi.Map{"Name": pulumi.String("web-server-www")}, 48 | InstanceType: pulumi.String("t2.micro"), // t2.micro is available in the AWS free tier. 49 | VpcSecurityGroupIds: pulumi.StringArray{group.ID()}, 50 | Ami: pulumi.String(ami.Id), 51 | UserData: pulumi.String(`#!/bin/bash 52 | echo "Hello, World!" > index.html 53 | nohup python -m SimpleHTTPServer 80 &`), 54 | }) 55 | 56 | ctx.Export("publicIp", srv.PublicIp) 57 | ctx.Export("publicHostName", srv.PublicDns) 58 | return nil 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-02/code/step3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/pulumi/pulumi-aws/sdk/go/aws" 7 | "github.com/pulumi/pulumi-aws/sdk/go/aws/ec2" 8 | "github.com/pulumi/pulumi/sdk/go/pulumi" 9 | ) 10 | 11 | func main() { 12 | pulumi.Run(func(ctx *pulumi.Context) error { 13 | mostRecent := true 14 | ami, err := aws.GetAmi(ctx, &aws.GetAmiArgs{ 15 | Filters: []aws.GetAmiFilter{ 16 | { 17 | Name: "name", 18 | Values: []string{"amzn2-ami-k*-hvm-*-x86_64-gp2"}, 19 | }, 20 | }, 21 | Owners: []string{"amazon"}, 22 | MostRecent: &mostRecent, 23 | }) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | group, err := ec2.NewSecurityGroup(ctx, "web-secgrp", &ec2.SecurityGroupArgs{ 29 | Ingress: ec2.SecurityGroupIngressArray{ 30 | ec2.SecurityGroupIngressArgs{ 31 | Protocol: pulumi.String("tcp"), 32 | FromPort: pulumi.Int(80), 33 | ToPort: pulumi.Int(80), 34 | CidrBlocks: pulumi.StringArray{pulumi.String("0.0.0.0/0")}, 35 | }, 36 | ec2.SecurityGroupIngressArgs{ 37 | Protocol: pulumi.String("icmp"), 38 | FromPort: pulumi.Int(8), 39 | ToPort: pulumi.Int(80), 40 | CidrBlocks: pulumi.StringArray{pulumi.String("0.0.0.0/0")}, 41 | }, 42 | }, 43 | }) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | azs, err := aws.GetAvailabilityZones(ctx, nil) 49 | if err != nil { 50 | return err 51 | } 52 | 53 | var ips pulumi.StringArray 54 | var hostnames pulumi.StringArray 55 | for _, az := range azs.Names { 56 | srv, err := ec2.NewInstance(ctx, fmt.Sprintf("web-server-%s", az), &ec2.InstanceArgs{ 57 | Tags: pulumi.Map{"Name": pulumi.String("web-server-www")}, 58 | InstanceType: pulumi.String("t2.micro"), // t2.micro is available in the AWS free tier. 59 | VpcSecurityGroupIds: pulumi.StringArray{group.ID()}, 60 | Ami: pulumi.String(ami.Id), 61 | AvailabilityZone: pulumi.String(az), 62 | UserData: pulumi.String(fmt.Sprintf(`#!/bin/bash 63 | echo "Hello, World -- from %s!" > index.html 64 | nohup python -m SimpleHTTPServer 80 &`, az)), 65 | }) 66 | if err != nil { 67 | return err 68 | } 69 | 70 | ips = append(ips, srv.PublicIp) 71 | hostnames = append(hostnames, srv.PublicDns) 72 | } 73 | 74 | ctx.Export("ips", ips) 75 | ctx.Export("hostnames", hostnames) 76 | return nil 77 | }) 78 | } 79 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-03/code/step1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/pulumi/pulumi-aws/sdk/go/aws/ecs" 5 | "github.com/pulumi/pulumi/sdk/go/pulumi" 6 | ) 7 | 8 | func main() { 9 | pulumi.Run(func(ctx *pulumi.Context) error { 10 | // Create an ECS cluster to run a container-based service. 11 | cluster, err := ecs.NewCluster(ctx, "app-cluster", nil) 12 | if err != nil { 13 | return err 14 | } 15 | 16 | return nil 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-03/code/step2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/pulumi/pulumi-aws/sdk/go/aws/ecs" 5 | "github.com/pulumi/pulumi-aws/sdk/go/aws/ec2" 6 | "github.com/pulumi/pulumi-aws/sdk/go/aws/lb" 7 | "github.com/pulumi/pulumi/sdk/go/pulumi" 8 | ) 9 | 10 | func main() { 11 | pulumi.Run(func(ctx *pulumi.Context) error { 12 | // Create an ECS cluster to run a container-based service. 13 | cluster, err := ecs.NewCluster(ctx, "app-cluster", nil) 14 | if err != nil { 15 | return err 16 | } 17 | 18 | t := true 19 | vpc, err := ec2.LookupVpc(ctx, &ec2.LookupVpcArgs{Default: &t}) 20 | if err != nil { 21 | return err 22 | } 23 | subnet, err := ec2.GetSubnetIds(ctx, &ec2.GetSubnetIdsArgs{VpcId: vpc.Id}) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | // Create a SecurityGroup that permits HTTP ingress and unrestricted egress. 29 | webSg, err := ec2.NewSecurityGroup(ctx, "web-sg", &ec2.SecurityGroupArgs{ 30 | VpcId: pulumi.String(vpc.Id), 31 | Egress: ec2.SecurityGroupEgressArray{ 32 | ec2.SecurityGroupEgressArgs{ 33 | Protocol: pulumi.String("-1"), 34 | FromPort: pulumi.Int(0), 35 | ToPort: pulumi.Int(0), 36 | CidrBlocks: pulumi.StringArray{pulumi.String("0.0.0.0/0")}, 37 | }, 38 | }, 39 | Ingress: ec2.SecurityGroupIngressArray{ 40 | ec2.SecurityGroupIngressArgs{ 41 | Protocol: pulumi.String("tcp"), 42 | FromPort: pulumi.Int(80), 43 | ToPort: pulumi.Int(80), 44 | CidrBlocks: pulumi.StringArray{pulumi.String("0.0.0.0/0")}, 45 | }, 46 | }, 47 | }) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | loadBalancer, err := lb.NewLoadBalancer(ctx, "external-loadbalancer", &lb.LoadBalancerArgs{ 53 | Internal: pulumi.Bool(false), 54 | SecurityGroups: pulumi.StringArray{webSg.ID().ToStringOutput()}, 55 | Subnets: toPulumiStringArray(subnet.Ids), 56 | LoadBalancerType: pulumi.String("application"), 57 | }) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | targetGroup, err := lb.NewTargetGroup(ctx, "target-group", &lb.TargetGroupArgs{ 63 | Port: pulumi.Int(80), 64 | Protocol: pulumi.String("HTTP"), 65 | TargetType: pulumi.String("ip"), 66 | VpcId: pulumi.String(vpc.Id), 67 | }) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | _, err = lb.NewListener(ctx, "listener", &lb.ListenerArgs{ 73 | LoadBalancerArn: loadBalancer.Arn, 74 | Port: pulumi.Int(80), 75 | DefaultActions: lb.ListenerDefaultActionArray{ 76 | lb.ListenerDefaultActionArgs{ 77 | Type: pulumi.String("forward"), 78 | TargetGroupArn: targetGroup.Arn, 79 | }, 80 | }, 81 | }) 82 | if err != nil { 83 | return err 84 | } 85 | 86 | return nil 87 | }) 88 | } 89 | 90 | func toPulumiStringArray(a []string) pulumi.StringArrayInput { 91 | var res []pulumi.StringInput 92 | for _, s := range a { 93 | res = append(res, pulumi.String(s)) 94 | } 95 | return pulumi.StringArray(res) 96 | } 97 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-04/code/step1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pulumi/pulumi-aws/sdk/go/aws/iam" 6 | "github.com/pulumi/pulumi/sdk/go/pulumi" 7 | ) 8 | 9 | func main() { 10 | } 11 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-04/code/step2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/pulumi/pulumi-aws/sdk/go/aws/iam" 7 | "github.com/pulumi/pulumi/sdk/go/pulumi" 8 | ) 9 | 10 | func main() { 11 | pulumi.Run(func(ctx *pulumi.Context) error { 12 | eksRole, err := iam.NewRole(ctx, "eks-iam-eksRole", &iam.RoleArgs{ 13 | AssumeRolePolicy: pulumi.String(`{ 14 | "Version": "2008-10-17", 15 | "Statement": [{ 16 | "Sid": "", 17 | "Effect": "Allow", 18 | "Principal": { 19 | "Service": "eks.amazonaws.com" 20 | }, 21 | "Action": "sts:AssumeRole" 22 | }] 23 | }`), 24 | }) 25 | if err != nil { 26 | return err 27 | } 28 | eksPolicies := []string{ 29 | "arn:aws:iam::aws:policy/AmazonEKSServicePolicy", 30 | "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy", 31 | } 32 | for i, eksPolicy := range eksPolicies { 33 | _, err := iam.NewRolePolicyAttachment(ctx, fmt.Sprintf("rpa-%d", i), &iam.RolePolicyAttachmentArgs{ 34 | PolicyArn: pulumi.String(eksPolicy), 35 | Role: eksRole.Name, 36 | }) 37 | if err != nil { 38 | return err 39 | } 40 | } 41 | // Create the EC2 NodeGroup Role 42 | nodeGroupRole, err := iam.NewRole(ctx, "nodegroup-iam-role", &iam.RoleArgs{ 43 | AssumeRolePolicy: pulumi.String(`{ 44 | "Version": "2012-10-17", 45 | "Statement": [{ 46 | "Sid": "", 47 | "Effect": "Allow", 48 | "Principal": { 49 | "Service": "ec2.amazonaws.com" 50 | }, 51 | "Action": "sts:AssumeRole" 52 | }] 53 | }`), 54 | }) 55 | if err != nil { 56 | return err 57 | } 58 | nodeGroupPolicies := []string{ 59 | "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy", 60 | "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy", 61 | "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly", 62 | } 63 | for i, nodeGroupPolicy := range nodeGroupPolicies { 64 | _, err := iam.NewRolePolicyAttachment(ctx, fmt.Sprintf("ngpa-%d", i), &iam.RolePolicyAttachmentArgs{ 65 | Role: nodeGroupRole.Name, 66 | PolicyArn: pulumi.String(nodeGroupPolicy), 67 | }) 68 | if err != nil { 69 | return err 70 | } 71 | } 72 | 73 | return nil 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-05/code/step1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pulumi/pulumi-kubernetes/sdk/go/kubernetes/providers" 7 | "github.com/pulumi/pulumi/sdk/go/pulumi" 8 | "github.com/pulumi/pulumi/sdk/go/pulumi/config" 9 | ) 10 | 11 | func main() { 12 | pulumi.Run(func(ctx *pulumi.Context) error { 13 | c := config.New(ctx, "") 14 | stackRef := c.Require("clusterStackRef") 15 | infra, err := pulumi.NewStackReference(ctx, stackRef, nil) 16 | 17 | kubeconfig := infra.GetOutput(pulumi.String("kubeconfig")).ApplyString( 18 | func(in interface{}) string { 19 | kc, err := json.Marshal(in) 20 | if err != nil { 21 | panic(err) 22 | } 23 | return string(kc) 24 | }, 25 | ) 26 | k8sProvider, err := providers.NewProvider(ctx, "k8sprovider", &providers.ProviderArgs{ 27 | Kubeconfig: kubeconfig, 28 | }) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | return nil 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-05/code/step2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | corev1 "github.com/pulumi/pulumi-kubernetes/sdk/go/kubernetes/core/v1" 7 | metav1 "github.com/pulumi/pulumi-kubernetes/sdk/go/kubernetes/meta/v1" 8 | "github.com/pulumi/pulumi-kubernetes/sdk/go/kubernetes/providers" 9 | "github.com/pulumi/pulumi/sdk/go/pulumi" 10 | "github.com/pulumi/pulumi/sdk/go/pulumi/config" 11 | ) 12 | 13 | func main() { 14 | pulumi.Run(func(ctx *pulumi.Context) error { 15 | c := config.New(ctx, "") 16 | stackRef := c.Require("clusterStackRef") 17 | infra, err := pulumi.NewStackReference(ctx, stackRef, nil) 18 | 19 | kubeconfig := infra.GetOutput(pulumi.String("kubeconfig")).ApplyString( 20 | func(in interface{}) string { 21 | kc, err := json.Marshal(in) 22 | if err != nil { 23 | panic(err) 24 | } 25 | return string(kc) 26 | }, 27 | ) 28 | k8sProvider, err := providers.NewProvider(ctx, "k8sprovider", &providers.ProviderArgs{ 29 | Kubeconfig: kubeconfig, 30 | }) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | namespace, err := corev1.NewNamespace(ctx, "app-ns", &corev1.NamespaceArgs{ 36 | Metadata: &metav1.ObjectMetaArgs{ 37 | Name: pulumi.String("joe-duffy"), 38 | }, 39 | }, pulumi.Provider(k8sProvider)) 40 | if err != nil { 41 | return err 42 | } 43 | 44 | return nil 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /labs/aws/in-person/go/lab-05/code/step3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | appsv1 "github.com/pulumi/pulumi-kubernetes/sdk/go/kubernetes/apps/v1" 7 | corev1 "github.com/pulumi/pulumi-kubernetes/sdk/go/kubernetes/core/v1" 8 | metav1 "github.com/pulumi/pulumi-kubernetes/sdk/go/kubernetes/meta/v1" 9 | "github.com/pulumi/pulumi-kubernetes/sdk/go/kubernetes/providers" 10 | "github.com/pulumi/pulumi/sdk/go/pulumi" 11 | "github.com/pulumi/pulumi/sdk/go/pulumi/config" 12 | ) 13 | 14 | func main() { 15 | pulumi.Run(func(ctx *pulumi.Context) error { 16 | c := config.New(ctx, "") 17 | stackRef := c.Require("clusterStackRef") 18 | infra, err := pulumi.NewStackReference(ctx, stackRef, nil) 19 | 20 | kubeconfig := infra.GetOutput(pulumi.String("kubeconfig")).ApplyString( 21 | func(in interface{}) string { 22 | kc, err := json.Marshal(in) 23 | if err != nil { 24 | panic(err) 25 | } 26 | return string(kc) 27 | }, 28 | ) 29 | k8sProvider, err := providers.NewProvider(ctx, "k8sprovider", &providers.ProviderArgs{ 30 | Kubeconfig: kubeconfig, 31 | }) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | namespace, err := corev1.NewNamespace(ctx, "app-ns", &corev1.NamespaceArgs{ 37 | Metadata: &metav1.ObjectMetaArgs{ 38 | Name: pulumi.String("joe-duffy"), 39 | }, 40 | }, pulumi.Provider(k8sProvider)) 41 | if err != nil { 42 | return err 43 | } 44 | 45 | appLabels := pulumi.StringMap{ 46 | "app": pulumi.String("iac-workshop"), 47 | } 48 | _, err = appsv1.NewDeployment(ctx, "app-dep", &appsv1.DeploymentArgs{ 49 | Metadata: &metav1.ObjectMetaArgs{ 50 | Namespace: namespace.Metadata.Elem().Name(), 51 | }, 52 | Spec: appsv1.DeploymentSpecArgs{ 53 | Selector: &metav1.LabelSelectorArgs{ 54 | MatchLabels: appLabels, 55 | }, 56 | Replicas: pulumi.Int(1), 57 | Template: &corev1.PodTemplateSpecArgs{ 58 | Metadata: &metav1.ObjectMetaArgs{ 59 | Labels: appLabels, 60 | }, 61 | Spec: &corev1.PodSpecArgs{ 62 | Containers: corev1.ContainerArray{ 63 | corev1.ContainerArgs{ 64 | Name: pulumi.String("iac-workshop"), 65 | Image: pulumi.String("jocatalin/kubernetes-bootcamp:v2"), 66 | }}, 67 | }, 68 | }, 69 | }, 70 | }, pulumi.Provider(k8sProvider)) 71 | if err != nil { 72 | return err 73 | } 74 | 75 | return nil 76 | }) 77 | } 78 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/README.md: -------------------------------------------------------------------------------- 1 | ### Lab 1 — Modern Infrastructure as Code 2 | 3 | The first lab takes you on a tour of infrastructure as code concepts: 4 | 5 | 1. [Creating a New Project](./lab-01/01-creating-a-new-project.md) 6 | 2. [Configuring AWS](./lab-01/02-configuring-aws.md) 7 | 3. [Provisioning Infrastructure](./lab-01/03-provisioning-infrastructure.md) 8 | 4. [Updating your Infrastructure](./lab-01/04-updating-your-infrastructure.md) 9 | 5. [Making Your Stack Configurable](./lab-01/05-making-your-stack-configurable.md) 10 | 6. [Creating a Second Stack](./lab-01/06-creating-a-second-stack.md) 11 | 7. [Destroying Your Infrastructure](./lab-01/07-destroying-your-infrastructure.md) 12 | 13 | ### Lab 2 - Provision EC2 Virtual Machines 14 | 15 | In this [lab](./lab-02/README.md), you will learn about the creation of Virtual Machines in EC2. This lab will include 16 | the use of loops to create loadbalanced infrastructure in EC2. 17 | 18 | ### Lab 3 - Deploying Containers to Elastic Container Service (ECS) 19 | 20 | In this [lab](./lab-03/README.md), you will learn about how to create and deploy container based applications to Elastic 21 | Container Service (ECS). 22 | 23 | ### Lab 4 - Deploying a Kubernetes Cluster 24 | 25 | In this [lab](./lab-04/README.md), you will learn about how to create and deploy a Kubernetes Cluster in EKS. 26 | 27 | ### Lab 5 - Deploying Containers to a Kubernetes Cluster 28 | 29 | In this [lab](./lab-05/README.md), you will learn about how to create and deploy container based applications to a Kubernetes 30 | Cluster. 31 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-01/01-creating-a-new-project.md: -------------------------------------------------------------------------------- 1 | # Creating a New Project 2 | 3 | Infrastructure in Pulumi is organized into projects. Each project is a single program that, when run, declares the desired infrastructure for Pulumi to manage. 4 | 5 | ## Step 1 — Create a Directory 6 | 7 | Each Pulumi project lives in its own directory. Create one now and change into it: 8 | 9 | ```bash 10 | mkdir iac-workshop 11 | cd iac-workshop 12 | ``` 13 | 14 | > Pulumi will use the directory name as your project name by default. To create an independent project, simply name the directory differently. 15 | 16 | ## Step 2 — Initialize Your Project 17 | 18 | A Pulumi project is just a directory with some files in it. It's possible for you to create a new one by hand. The `pulumi new` command, however, automates the process: 19 | 20 | ```bash 21 | pulumi new python -y 22 | ``` 23 | 24 | This will print output similar to the following with a bit more information and status as it goes: 25 | 26 | ``` 27 | Created project 'iac-workshop' 28 | Created stack 'dev' 29 | 30 | Your new project is ready to go! ✨ 31 | 32 | To perform an initial deployment, run the following commands: 33 | 34 | 1. python3 -m venv venv 35 | 2. source venv/bin/activate 36 | 3. pip3 install -r requirements.txt 37 | 38 | Then, run 'pulumi up' 39 | ``` 40 | 41 | This command has created all the files we need, initialized a new stack named `dev` (an instance of our project). We now need 42 | to install our dependencies as part of our virtualenv 43 | 44 | ## Step 3 — Setup Virtual Environment 45 | 46 | We now need to create our virtual environment as suggested in the output of `pulumi new` in step 2 by following the commands: 47 | 48 | ```bash 49 | $ python3 -m venv venv 50 | ``` 51 | 52 | ```bash 53 | $ source venv/bin/activate 54 | ``` 55 | 56 | ```bash 57 | pip3 install -r requirements.txt 58 | ``` 59 | 60 | ## Step 4 — Inspect Your New Project 61 | 62 | Our project is comprised of multiple files: 63 | 64 | * **`__main__.py`**: your program's main entrypoint file 65 | * **`requirements.txt`**: your project's pip dependency information 66 | * **`Pulumi.yaml`**: your project's metadata, containing its name and language 67 | 68 | Run `cat __main__.py` to see the contents of your project's empty program: 69 | 70 | ```python 71 | import pulumi 72 | ``` 73 | 74 | Feel free to explore the other files, although we won't be editing any of them by hand. 75 | 76 | # Next Steps 77 | 78 | * [Configuring AWS](./02-configuring-aws.md) 79 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-01/02-configuring-aws.md: -------------------------------------------------------------------------------- 1 | # Configuring AWS 2 | 3 | Now that you have a basic project, let's configure AWS support for it. 4 | 5 | ## Step 1 — Install the AWS Package 6 | 7 | Run the following command to install the AWS package: 8 | 9 | ```bash 10 | pip3 install pulumi-aws 11 | ``` 12 | 13 | ## Step 2 — Import the AWS Package 14 | 15 | Now that the AWS package is installed, add the following line to `__main__.py` to import it: 16 | 17 | ```python 18 | # ... 19 | import pulumi_aws as aws 20 | ``` 21 | 22 | > :white_check_mark: After this change, your `__main__.py` should [look like this](./code/02-configuring-aws/step2.py). 23 | 24 | ## Step 3 — Configure an AWS Region 25 | 26 | Configure the AWS region you would like to deploy to: 27 | 28 | ```bash 29 | pulumi config set aws:region us-east-1 30 | ``` 31 | 32 | > If you are doing an interactive lab, please use the recommended region. This will ensure AWS limits are not exceeded during the labs. 33 | 34 | Feel free to choose any AWS region that supports the services used in these labs ([see this table](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions) for a list of available regions). 35 | 36 | ## (Optional) Step 4 — Configure an AWS Profile 37 | 38 | If you're using an alternative AWS profile, you can tell Pulumi which to use in one of two ways: 39 | 40 | * Using an environment variable: `export AWS_PROFILE=` 41 | * Using configuration: `pulumi config set aws:profile ` 42 | 43 | ## Next Steps 44 | 45 | * [Provisioning a S3 Bucket](./03-provisioning-infrastructure.md) 46 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-01/06-creating-a-second-stack.md: -------------------------------------------------------------------------------- 1 | # Creating a Second Stack 2 | 3 | It is easy to create multiple instances of the same project. This is called a stack. This is handy for multiple development or test environments, staging versus production, and scaling a given infrastructure across many regions. 4 | 5 | ## Step 1 — Create and Configure a New Stack 6 | 7 | Create a new stack: 8 | 9 | ```bash 10 | pulumi stack init prod 11 | ``` 12 | 13 | Next, configure its two required variables: 14 | 15 | ```bash 16 | pulumi config set aws:region eu-west-1 17 | pulumi config set iac-workshop:siteDir wwwprod 18 | ``` 19 | 20 | If you are ever curious to see the list of stacks for your current project, run this command: 21 | 22 | ```bash 23 | pulumi stack ls 24 | ``` 25 | 26 | It will print all stacks for this project that are available to you: 27 | 28 | ``` 29 | NAME LAST UPDATE RESOURCE COUNT URL 30 | dev 30 minutes ago 5 https://app.pulumi.com/joeduffy/iac-workshop/dev 31 | prod* 3 minutes ago 0 https://app.pulumi.com/joeduffy/iac-workshop/prod 32 | ``` 33 | 34 | ## Step 2 — Populate the New Site Directory 35 | 36 | It would have been possible to use the existing `www` directory for the `siteDir`. In this example, you will use a different `wwwprod` directory, to demonstrate the value of having configurability. 37 | 38 | Create this new directory: 39 | 40 | ```bash 41 | mkdir wwwprod 42 | ``` 43 | 44 | Add a new `index.html` file to it: 45 | 46 | ```html 47 | 48 | 49 |

Hello Pulumi

50 |

(in production!)

51 | 52 | 53 | ``` 54 | 55 | ## Step 3 — Deploy the New Stack 56 | 57 | Now deploy all of the changes: 58 | 59 | ```bash 60 | pulumi up 61 | ``` 62 | 63 | This will create an entirely new set of resources from scratch, unrelated to the existing `dev` stack's resources. 64 | 65 | ``` 66 | Updating (prod): 67 | 68 | Type Name Status 69 | + pulumi:pulumi:Stack iac-workshop-prod created 70 | + ├─ aws:s3:Bucket my-bucket created 71 | + └─ aws:s3:BucketObject index.html created 72 | 73 | Outputs: 74 | bucket_endpoint: "http://my-bucket-c7318c1.s3-website-eu-west-1.amazonaws.com" 75 | bucket_name : "my-bucket-c7318c1" 76 | 77 | Resources: 78 | + 3 created 79 | 80 | Duration: 28s 81 | 82 | Permalink: https://app.pulumi.com/joeduffy/iac-workshop/prod/updates/1 83 | ``` 84 | 85 | Now fetch your new website: 86 | 87 | ```bash 88 | curl $(pulumi stack output bucket_endpoint) 89 | ``` 90 | 91 | Notice that it's the new production version of your content: 92 | 93 | ``` 94 | 95 | 96 |

Hello Pulumi

97 |

(in production!)

98 | 99 | 100 | ``` 101 | 102 | ## Next Steps 103 | 104 | * [Destroying Your Infrastructure](./07-destroying-your-infrastructure.md) 105 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-01/code/02-configuring-aws/step2.py: -------------------------------------------------------------------------------- 1 | import pulumi 2 | import pulumi_aws as aws 3 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-01/code/03-provisioning-infrastructure/step1.py: -------------------------------------------------------------------------------- 1 | import pulumi 2 | import pulumi_aws as aws 3 | 4 | bucket = aws.s3.Bucket("my-bucket") 5 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-01/code/03-provisioning-infrastructure/step4.py: -------------------------------------------------------------------------------- 1 | import pulumi 2 | import pulumi_aws as aws 3 | 4 | bucket = aws.s3.Bucket("my-bucket") 5 | 6 | pulumi.export('bucket_name', bucket.bucket) 7 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-01/code/04-updating-your-infrastructure/step1.py: -------------------------------------------------------------------------------- 1 | import pulumi 2 | import pulumi_aws as aws 3 | import os 4 | 5 | bucket = aws.s3.Bucket("my-bucket") 6 | 7 | filepath = os.path.join("site", "index.html") 8 | obj = aws.s3.BucketObject("index.html", 9 | bucket=bucket.bucket, 10 | source=pulumi.FileAsset(filepath) 11 | ) 12 | 13 | pulumi.export('bucket_name', bucket.bucket) 14 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-01/code/04-updating-your-infrastructure/step2.py: -------------------------------------------------------------------------------- 1 | import pulumi 2 | import pulumi_aws as aws 3 | import os 4 | import mimetypes 5 | 6 | bucket = aws.s3.Bucket("my-bucket", 7 | website={ 8 | "index_document": "index.html" 9 | }) 10 | 11 | filepath = os.path.join("site", "index.html") 12 | mime_type, _ = mimetypes.guess_type(filepath) 13 | obj = aws.s3.BucketObject("index.html", 14 | bucket=bucket.bucket, 15 | source=pulumi.FileAsset(filepath), 16 | acl="public-read", 17 | content_type=mime_type 18 | ) 19 | 20 | pulumi.export('bucket_name', bucket.bucket) 21 | pulumi.export('bucket_endpoint', pulumi.Output.concat("http://", bucket.website_endpoint)) 22 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-01/code/05-making-your-stack-configurable/step2.py: -------------------------------------------------------------------------------- 1 | import pulumi 2 | import pulumi_aws as aws 3 | import os 4 | import mimetypes 5 | 6 | config = pulumi.Config() 7 | site_dir = config.require("siteDir") 8 | 9 | bucket = aws.s3.Bucket("my-bucket", 10 | website={ 11 | "index_document": "index.html" 12 | }) 13 | 14 | filepath = os.path.join(site_dir, "index.html") 15 | mime_type, _ = mimetypes.guess_type(filepath) 16 | 17 | obj = aws.s3.BucketObject("index.html", 18 | bucket=bucket.bucket, 19 | source=pulumi.FileAsset(filepath), 20 | acl="public-read", 21 | content_type=mime_type 22 | ) 23 | 24 | pulumi.export('bucket_name', bucket.bucket) 25 | pulumi.export('bucket_endpoint', pulumi.Output.concat("http://", bucket.website_endpoint)) 26 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-01/code/05-making-your-stack-configurable/step4.py: -------------------------------------------------------------------------------- 1 | import pulumi 2 | import pulumi_aws as aws 3 | import os 4 | import mimetypes 5 | 6 | config = pulumi.Config() 7 | site_dir = config.require("siteDir") 8 | 9 | bucket = aws.s3.Bucket("my-bucket", 10 | website={ 11 | "index_document": "index.html" 12 | }) 13 | 14 | for file in os.listdir(site_dir): 15 | filepath = os.path.join(site_dir, file) 16 | mime_type, _ = mimetypes.guess_type(filepath) 17 | obj = aws.s3.BucketObject(file, 18 | bucket=bucket.bucket, 19 | source=pulumi.FileAsset(filepath), 20 | acl="public-read", 21 | content_type=mime_type 22 | ) 23 | 24 | pulumi.export('bucket_name', bucket.bucket) 25 | pulumi.export('bucket_endpoint', pulumi.Output.concat("http://", bucket.website_endpoint)) 26 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-02/code/step1.py: -------------------------------------------------------------------------------- 1 | from pulumi import export 2 | import pulumi_aws as aws 3 | 4 | ami = aws.ec2.get_ami( 5 | most_recent="true", 6 | owners=["amazon"], 7 | filters=[{"name":"name","values":["amzn2-ami-k*-hvm-*-x86_64-gp2"]}]) 8 | 9 | group = aws.ec2.SecurityGroup( 10 | "web-secgrp", 11 | description='Enable HTTP access', 12 | ingress=[ 13 | { 'protocol': 'icmp', 'from_port': 8, 'to_port': 0, 'cidr_blocks': ['0.0.0.0/0'] }, 14 | { 'protocol': 'tcp', 'from_port': 80, 'to_port': 80, 'cidr_blocks': ['0.0.0.0/0'] } 15 | ]) 16 | 17 | server = aws.ec2.Instance( 18 | 'web-server', 19 | instance_type="t2.micro", 20 | vpc_security_group_ids=[group.id], 21 | ami=ami.id, 22 | user_data=""" 23 | #!/bin/bash 24 | echo "Hello, World!" > index.html 25 | nohup python -m SimpleHTTPServer 80 & 26 | """, 27 | tags={ 28 | "Name": "web-server", 29 | }, 30 | ) 31 | 32 | export('ip', server.public_ip) 33 | export('hostname', server.public_dns) 34 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-02/code/step3.py: -------------------------------------------------------------------------------- 1 | from pulumi import export 2 | import pulumi_aws as aws 3 | 4 | ami = aws.ec2.get_ami( 5 | most_recent="true", 6 | owners=["amazon"], 7 | filters=[{"name":"name","values":["amzn2-ami-k*-hvm-*-x86_64-gp2"]}]) 8 | 9 | group = aws.ec2.SecurityGroup( 10 | "web-secgrp", 11 | description='Enable HTTP access', 12 | ingress=[ 13 | { 'protocol': 'icmp', 'from_port': 8, 'to_port': 0, 'cidr_blocks': ['0.0.0.0/0'] }, 14 | { 'protocol': 'tcp', 'from_port': 80, 'to_port': 80, 'cidr_blocks': ['0.0.0.0/0'] } 15 | ]) 16 | 17 | ips = [] 18 | hostnames = [] 19 | for az in aws.get_availability_zones().names: 20 | server = aws.ec2.Instance(f'web-server-{az}', 21 | instance_type="t2.micro", 22 | vpc_security_group_ids=[group.id], 23 | ami=ami.id, 24 | availability_zone=az, 25 | user_data="""#!/bin/bash 26 | echo \"Hello, World -- from {}!\" > index.html 27 | nohup python -m SimpleHTTPServer 80 & 28 | """.format(az), 29 | tags={ 30 | "Name": "web-server", 31 | }, 32 | ) 33 | ips.append(server.public_ip) 34 | hostnames.append(server.public_dns) 35 | 36 | export('ips', ips) 37 | export('hostnames', hostnames) 38 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-02/code/step4.py: -------------------------------------------------------------------------------- 1 | from pulumi import export 2 | import pulumi_aws as aws 3 | 4 | ami = aws.ec2.get_ami( 5 | most_recent="true", 6 | owners=["amazon"], 7 | filters=[{"name":"name","values":["amzn2-ami-k*-hvm-*-x86_64-gp2"]}]) 8 | 9 | group = aws.ec2.SecurityGroup( 10 | "web-secgrp", 11 | description='Enable HTTP access', 12 | ingress=[ 13 | { 'protocol': 'icmp', 'from_port': 8, 'to_port': 0, 'cidr_blocks': ['0.0.0.0/0'] }, 14 | { 'protocol': 'tcp', 'from_port': 80, 'to_port': 80, 'cidr_blocks': ['0.0.0.0/0'] }, 15 | ], 16 | egress=[ 17 | { 'protocol': 'tcp', 'from_port': 80, 'to_port': 80, 'cidr_blocks': ['0.0.0.0/0'] }, 18 | ] 19 | ) 20 | 21 | default_vpc = aws.ec2.get_vpc(default="true") 22 | default_vpc_subnets = aws.ec2.get_subnet_ids(vpc_id=default_vpc.id) 23 | 24 | lb = aws.lb.LoadBalancer("external-loadbalancer", 25 | internal="false", 26 | security_groups=[group.id], 27 | subnets=default_vpc_subnets.ids, 28 | load_balancer_type="application", 29 | ) 30 | 31 | target_group = aws.lb.TargetGroup("target-group", 32 | port=80, 33 | protocol="HTTP", 34 | target_type="ip", 35 | vpc_id=default_vpc.id 36 | ) 37 | 38 | listener = aws.lb.Listener("listener", 39 | load_balancer_arn=lb.arn, 40 | port=80, 41 | default_actions=[{ 42 | "type": "forward", 43 | "target_group_arn": target_group.arn 44 | }] 45 | ) 46 | 47 | ips = [] 48 | hostnames = [] 49 | for az in aws.get_availability_zones().names: 50 | server = aws.ec2.Instance(f'web-server-{az}', 51 | instance_type="t2.micro", 52 | vpc_security_group_ids=[group.id], 53 | ami=ami.id, 54 | user_data="""#!/bin/bash 55 | echo \"Hello, World -- from {}!\" > index.html 56 | nohup python -m SimpleHTTPServer 80 & 57 | """.format(az), 58 | availability_zone=az, 59 | tags={ 60 | "Name": "web-server", 61 | }, 62 | ) 63 | ips.append(server.public_ip) 64 | hostnames.append(server.public_dns) 65 | 66 | attachment = aws.lb.TargetGroupAttachment(f'web-server-{az}', 67 | target_group_arn=target_group.arn, 68 | target_id=server.private_ip, 69 | port=80, 70 | ) 71 | 72 | export('ips', ips) 73 | export('hostnames', hostnames) 74 | export("url", lb.dns_name) 75 | 76 | 77 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-03/code/step1.py: -------------------------------------------------------------------------------- 1 | from pulumi import export 2 | import pulumi_aws as aws 3 | 4 | cluster = aws.ecs.Cluster("cluster") 5 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-03/code/step2.py: -------------------------------------------------------------------------------- 1 | from pulumi import export 2 | import pulumi_aws as aws 3 | 4 | cluster = aws.ecs.Cluster("cluster") 5 | 6 | default_vpc = aws.ec2.get_vpc(default="true") 7 | default_vpc_subnets = aws.ec2.get_subnet_ids(vpc_id=default_vpc.id) 8 | 9 | group = aws.ec2.SecurityGroup( 10 | "web-secgrp", 11 | vpc_id=default_vpc.id, 12 | description='Enable HTTP access', 13 | ingress=[ 14 | { 'protocol': 'icmp', 'from_port': 8, 'to_port': 0, 'cidr_blocks': ['0.0.0.0/0'] }, 15 | { 'protocol': 'tcp', 'from_port': 80, 'to_port': 80, 'cidr_blocks': ['0.0.0.0/0'] } 16 | ], 17 | egress=[ 18 | { 'protocol': "-1", 'from_port': 0, 'to_port': 0, 'cidr_blocks': ['0.0.0.0/0'] } 19 | ]) 20 | 21 | alb = aws.lb.LoadBalancer("app-lb", 22 | internal="false", 23 | security_groups=[group.id], 24 | subnets=default_vpc_subnets.ids, 25 | load_balancer_type="application", 26 | ) 27 | 28 | atg = aws.lb.TargetGroup("app-tg", 29 | port=80, 30 | protocol="HTTP", 31 | target_type="ip", 32 | vpc_id=default_vpc.id 33 | ) 34 | 35 | wl = aws.lb.Listener("web", 36 | load_balancer_arn=alb.arn, 37 | port=80, 38 | default_actions=[{ 39 | "type": "forward", 40 | "target_group_arn": atg.arn 41 | }] 42 | ) 43 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-03/code/step3.py: -------------------------------------------------------------------------------- 1 | from pulumi import export, ResourceOptions 2 | import pulumi_aws as aws 3 | import json 4 | 5 | cluster = aws.ecs.Cluster("cluster") 6 | 7 | default_vpc = aws.ec2.get_vpc(default="true") 8 | default_vpc_subnets = aws.ec2.get_subnet_ids(vpc_id=default_vpc.id) 9 | 10 | group = aws.ec2.SecurityGroup( 11 | "web-secgrp", 12 | vpc_id=default_vpc.id, 13 | description='Enable HTTP access', 14 | ingress=[ 15 | { 'protocol': 'icmp', 'from_port': 8, 'to_port': 0, 'cidr_blocks': ['0.0.0.0/0'] }, 16 | { 'protocol': 'tcp', 'from_port': 80, 'to_port': 80, 'cidr_blocks': ['0.0.0.0/0'] } 17 | ], 18 | egress=[ 19 | { 'protocol': "-1", 'from_port': 0, 'to_port': 0, 'cidr_blocks': ['0.0.0.0/0'] } 20 | ]) 21 | 22 | alb = aws.lb.LoadBalancer("app-lb", 23 | security_groups=[group.id], 24 | subnets=default_vpc_subnets.ids, 25 | ) 26 | 27 | atg = aws.lb.TargetGroup("app-tg", 28 | port=80, 29 | protocol="HTTP", 30 | target_type="ip", 31 | vpc_id=default_vpc.id 32 | ) 33 | 34 | wl = aws.lb.Listener("web", 35 | load_balancer_arn=alb.arn, 36 | port=80, 37 | default_actions=[{ 38 | "type": "forward", 39 | "target_group_arn": atg.arn 40 | }] 41 | ) 42 | 43 | role = aws.iam.Role("task-exec-role", 44 | assume_role_policy=json.dumps({ 45 | "Version": "2008-10-17", 46 | "Statement": [{ 47 | "Sid": "", 48 | "Effect": "Allow", 49 | "Principal": { 50 | "Service": "ecs-tasks.amazonaws.com" 51 | }, 52 | "Action": "sts:AssumeRole" 53 | }] 54 | }) 55 | ) 56 | 57 | rpa = aws.iam.RolePolicyAttachment("task-exec-policy", 58 | role=role.name, 59 | policy_arn="arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" 60 | ) 61 | 62 | task_definition = aws.ecs.TaskDefinition("app-task", 63 | family="fargate-task-definition", 64 | cpu="256", 65 | memory="512", 66 | network_mode="awsvpc", 67 | requires_compatibilities=["FARGATE"], 68 | execution_role_arn=role.arn, 69 | container_definitions=json.dumps([{ 70 | "name": "my-app", 71 | "image": "nginx", 72 | "portMappings": [{ 73 | "containerPort": 80, 74 | "hostPort": 80, 75 | "protocol": "tcp" 76 | }] 77 | }]) 78 | ) 79 | 80 | service = aws.ecs.Service("app-svc", 81 | cluster=cluster.arn, 82 | desired_count=1, 83 | launch_type="FARGATE", 84 | task_definition=task_definition.arn, 85 | network_configuration={ 86 | "assign_public_ip": "true", 87 | "subnets": default_vpc_subnets.ids, 88 | "security_groups": [group.id] 89 | }, 90 | load_balancers=[{ 91 | "target_group_arn": atg.arn, 92 | "container_name": "my-app", 93 | "container_port": 80 94 | }], 95 | opts=ResourceOptions(depends_on=[wl]) 96 | ) 97 | 98 | export("url", alb.dns_name) 99 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-03/code/step5.py: -------------------------------------------------------------------------------- 1 | from pulumi import export, ResourceOptions 2 | import pulumi_aws as aws 3 | import json 4 | 5 | cluster = aws.ecs.Cluster("cluster") 6 | 7 | default_vpc = aws.ec2.get_vpc(default="true") 8 | default_vpc_subnets = aws.ec2.get_subnet_ids(vpc_id=default_vpc.id) 9 | 10 | group = aws.ec2.SecurityGroup( 11 | "web-secgrp", 12 | vpc_id=default_vpc.id, 13 | description='Enable HTTP access', 14 | ingress=[ 15 | { 'protocol': 'icmp', 'from_port': 8, 'to_port': 0, 'cidr_blocks': ['0.0.0.0/0'] }, 16 | { 'protocol': 'tcp', 'from_port': 80, 'to_port': 80, 'cidr_blocks': ['0.0.0.0/0'] } 17 | ], 18 | egress=[ 19 | { 'protocol': "-1", 'from_port': 0, 'to_port': 0, 'cidr_blocks': ['0.0.0.0/0'] } 20 | ]) 21 | 22 | alb = aws.lb.LoadBalancer("app-lb", 23 | security_groups=[group.id], 24 | subnets=default_vpc_subnets.ids, 25 | ) 26 | 27 | atg = aws.lb.TargetGroup("app-tg", 28 | port=80, 29 | protocol="HTTP", 30 | target_type="ip", 31 | vpc_id=default_vpc.id 32 | ) 33 | 34 | wl = aws.lb.Listener("web", 35 | load_balancer_arn=alb.arn, 36 | port=80, 37 | default_actions=[{ 38 | "type": "forward", 39 | "target_group_arn": atg.arn 40 | }] 41 | ) 42 | 43 | role = aws.iam.Role("task-exec-role", 44 | assume_role_policy=json.dumps({ 45 | "Version": "2008-10-17", 46 | "Statement": [{ 47 | "Sid": "", 48 | "Effect": "Allow", 49 | "Principal": { 50 | "Service": "ecs-tasks.amazonaws.com" 51 | }, 52 | "Action": "sts:AssumeRole" 53 | }] 54 | }) 55 | ) 56 | 57 | rpa = aws.iam.RolePolicyAttachment("task-exec-policy", 58 | role=role.name, 59 | policy_arn="arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" 60 | ) 61 | 62 | task_definition = aws.ecs.TaskDefinition("app-task", 63 | family="fargate-task-definition", 64 | cpu="256", 65 | memory="512", 66 | network_mode="awsvpc", 67 | requires_compatibilities=["FARGATE"], 68 | execution_role_arn=role.arn, 69 | container_definitions=json.dumps([{ 70 | "name": "my-app", 71 | "image": "nginx", 72 | "portMappings": [{ 73 | "containerPort": 80, 74 | "hostPort": 80, 75 | "protocol": "tcp" 76 | }] 77 | }]) 78 | ) 79 | 80 | service = aws.ecs.Service("app-svc", 81 | cluster=cluster.arn, 82 | desired_count=3, 83 | launch_type="FARGATE", 84 | task_definition=task_definition.arn, 85 | network_configuration={ 86 | "assign_public_ip": "true", 87 | "subnets": default_vpc_subnets.ids, 88 | "security_groups": [group.id] 89 | }, 90 | load_balancers=[{ 91 | "target_group_arn": atg.arn, 92 | "container_name": "my-app", 93 | "container_port": 80 94 | }], 95 | opts=ResourceOptions(depends_on=[wl]) 96 | ) 97 | 98 | export("url", alb.dns_name) 99 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-04/code/step1.py: -------------------------------------------------------------------------------- 1 | from pulumi import export, Output 2 | import pulumi_aws as aws 3 | import json, hashlib 4 | 5 | h = hashlib.new('sha1') 6 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-04/code/step2.py: -------------------------------------------------------------------------------- 1 | from pulumi import export, Output 2 | import pulumi_aws as aws 3 | import json, hashlib 4 | 5 | h = hashlib.new('sha1') 6 | 7 | # Create the EKS Service Role and the correct role attachments 8 | service_role = aws.iam.Role("eks-service-role", 9 | assume_role_policy=json.dumps({ 10 | "Version": "2012-10-17", 11 | "Statement": [{ 12 | "Sid": "", 13 | "Effect": "Allow", 14 | "Principal": { 15 | "Service": "eks.amazonaws.com" 16 | }, 17 | "Action": "sts:AssumeRole" 18 | }] 19 | }) 20 | ) 21 | 22 | service_role_managed_policy_arns = [ 23 | "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy", 24 | "arn:aws:iam::aws:policy/AmazonEKSServicePolicy" 25 | ] 26 | 27 | for policy in service_role_managed_policy_arns: 28 | h.update(policy.encode('utf-8')) 29 | role_policy_attachment = aws.iam.RolePolicyAttachment(f"eks-service-role-{h.hexdigest()[0:8]}", 30 | policy_arn=policy, 31 | role=service_role.name 32 | ) 33 | 34 | # Create the EKS NodeGroup Role and the correct role attachments 35 | node_group_role = aws.iam.Role("eks-nodegroup-role", 36 | assume_role_policy=json.dumps({ 37 | "Version": "2012-10-17", 38 | "Statement": [{ 39 | "Sid": "", 40 | "Effect": "Allow", 41 | "Principal": { 42 | "Service": "ec2.amazonaws.com" 43 | }, 44 | "Action": "sts:AssumeRole" 45 | }] 46 | }) 47 | ) 48 | 49 | nodegroup_role_managed_policy_arns = [ 50 | "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy", 51 | "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy", 52 | "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly" 53 | ] 54 | 55 | for policy in nodegroup_role_managed_policy_arns: 56 | h.update(policy.encode('utf-8')) 57 | role_policy_attachment = aws.iam.RolePolicyAttachment(f"eks-nodegroup-role-{h.hexdigest()[0:8]}", 58 | policy_arn=policy, 59 | role=node_group_role.name 60 | ) 61 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-04/code/step3.py: -------------------------------------------------------------------------------- 1 | from pulumi import export, Output 2 | import pulumi_aws as aws 3 | import json, hashlib 4 | 5 | h = hashlib.new('sha1') 6 | 7 | # Create the EKS Service Role and the correct role attachments 8 | service_role = aws.iam.Role("eks-service-role", 9 | assume_role_policy=json.dumps({ 10 | "Version": "2012-10-17", 11 | "Statement": [{ 12 | "Sid": "", 13 | "Effect": "Allow", 14 | "Principal": { 15 | "Service": "eks.amazonaws.com" 16 | }, 17 | "Action": "sts:AssumeRole" 18 | }] 19 | }) 20 | ) 21 | 22 | service_role_managed_policy_arns = [ 23 | "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy", 24 | "arn:aws:iam::aws:policy/AmazonEKSServicePolicy" 25 | ] 26 | 27 | for policy in service_role_managed_policy_arns: 28 | h.update(policy.encode('utf-8')) 29 | role_policy_attachment = aws.iam.RolePolicyAttachment(f"eks-service-role-{h.hexdigest()[0:8]}", 30 | policy_arn=policy, 31 | role=service_role.name 32 | ) 33 | 34 | # Create the EKS NodeGroup Role and the correct role attachments 35 | node_group_role = aws.iam.Role("eks-nodegroup-role", 36 | assume_role_policy=json.dumps({ 37 | "Version": "2012-10-17", 38 | "Statement": [{ 39 | "Sid": "", 40 | "Effect": "Allow", 41 | "Principal": { 42 | "Service": "ec2.amazonaws.com" 43 | }, 44 | "Action": "sts:AssumeRole" 45 | }] 46 | }) 47 | ) 48 | 49 | nodegroup_role_managed_policy_arns = [ 50 | "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy", 51 | "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy", 52 | "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly" 53 | ] 54 | 55 | for policy in nodegroup_role_managed_policy_arns: 56 | h.update(policy.encode('utf-8')) 57 | role_policy_attachment = aws.iam.RolePolicyAttachment(f"eks-nodegroup-role-{h.hexdigest()[0:8]}", 58 | policy_arn=policy, 59 | role=node_group_role.name 60 | ) 61 | 62 | # Get the VPC and subnets to launch the EKS cluster into 63 | default_vpc = aws.ec2.get_vpc(default="true") 64 | default_vpc_subnets = aws.ec2.get_subnet_ids(vpc_id=default_vpc.id) 65 | 66 | # Create the Security Group that allows access to the cluster pods 67 | sg = aws.ec2.SecurityGroup("eks-cluster-security-group", 68 | vpc_id=default_vpc.id, 69 | revoke_rules_on_delete="true", 70 | ingress=[{ 71 | 'cidr_blocks' : ["0.0.0.0/0"], 72 | 'from_port' : '80', 73 | 'to_port' : '80', 74 | 'protocol' : 'tcp', 75 | }] 76 | ) 77 | 78 | sg_rule = aws.ec2.SecurityGroupRule("eks-cluster-security-group-egress-rule", 79 | type="egress", 80 | from_port=0, 81 | to_port=0, 82 | protocol="-1", 83 | cidr_blocks=["0.0.0.0/0"], 84 | security_group_id=sg.id 85 | ) 86 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-05/code/step1.py: -------------------------------------------------------------------------------- 1 | from pulumi import export, StackReference, Output, ResourceOptions 2 | from pulumi_kubernetes import Provider 3 | import pulumi 4 | 5 | # Create StackReference to the Kubernetes cluster stack 6 | config = pulumi.Config() 7 | stackRef = config.require("clusterStackRef"); 8 | infra = StackReference(f"{stackRef}") 9 | 10 | # Declare a provider using the KubeConfig we created 11 | # This will be used to interact with the EKS cluster 12 | k8s_provider = Provider("k8s-provider", kubeconfig=infra.get_output("kubeconfig")) 13 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-05/code/step2.py: -------------------------------------------------------------------------------- 1 | from pulumi import export, StackReference, Output, ResourceOptions 2 | from pulumi_kubernetes import Provider 3 | from pulumi_kubernetes.apps.v1 import Deployment 4 | from pulumi_kubernetes.core.v1 import Service, Namespace 5 | import pulumi 6 | 7 | # Create StackReference to the Kubernetes cluster stack 8 | config = pulumi.Config() 9 | stackRef = config.require("clusterStackRef"); 10 | infra = StackReference(f"{stackRef}") 11 | 12 | # Declare a provider using the KubeConfig we created 13 | # This will be used to interact with the EKS cluster 14 | k8s_provider = Provider("k8s-provider", kubeconfig=infra.get_output("kubeconfig")) 15 | 16 | # Create a Namespace object https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ 17 | ns = Namespace("app-ns", 18 | metadata={ 19 | "name": "joe-duffy", 20 | }, 21 | opts=ResourceOptions(provider=k8s_provider) 22 | ) 23 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-05/code/step3.py: -------------------------------------------------------------------------------- 1 | from pulumi import export, StackReference, Output, ResourceOptions 2 | from pulumi_kubernetes import Provider 3 | from pulumi_kubernetes.apps.v1 import Deployment 4 | from pulumi_kubernetes.core.v1 import Service, Namespace 5 | import pulumi 6 | 7 | # Create StackReference to the Kubernetes cluster stack 8 | config = pulumi.Config() 9 | stackRef = config.require("clusterStackRef"); 10 | infra = StackReference(f"{stackRef}") 11 | 12 | # Declare a provider using the KubeConfig we created 13 | # This will be used to interact with the EKS cluster 14 | k8s_provider = Provider("k8s-provider", kubeconfig=infra.get_output("kubeconfig")) 15 | 16 | # Create a Namespace object https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ 17 | ns = Namespace("app-ns", 18 | metadata={ 19 | "name": "joe-duffy", 20 | }, 21 | opts=ResourceOptions(provider=k8s_provider) 22 | ) 23 | 24 | app_labels = { 25 | "app": "iac-workshop" 26 | } 27 | app_deployment = Deployment("app-dep", 28 | metadata={ 29 | "namespace": ns.metadata["name"] 30 | }, 31 | spec={ 32 | "selector": { 33 | "match_labels": app_labels, 34 | }, 35 | "replicas": 1, 36 | "template": { 37 | "metadata": { 38 | "labels": app_labels, 39 | }, 40 | "spec": { 41 | "containers": [{ 42 | "name": "iac-workshop", 43 | "image": "gcr.io/google-samples/kubernetes-bootcamp:v1", 44 | }], 45 | }, 46 | }, 47 | }, 48 | opts=ResourceOptions(provider=k8s_provider) 49 | ) 50 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-05/code/step4.py: -------------------------------------------------------------------------------- 1 | from pulumi import export, StackReference, Output, ResourceOptions 2 | from pulumi_kubernetes import Provider 3 | from pulumi_kubernetes.apps.v1 import Deployment 4 | from pulumi_kubernetes.core.v1 import Service, Namespace 5 | import pulumi 6 | 7 | # Create StackReference to the Kubernetes cluster stack 8 | config = pulumi.Config() 9 | stackRef = config.require("clusterStackRef"); 10 | infra = StackReference(f"{stackRef}") 11 | 12 | # Declare a provider using the KubeConfig we created 13 | # This will be used to interact with the EKS cluster 14 | k8s_provider = Provider("k8s-provider", kubeconfig=infra.get_output("kubeconfig")) 15 | 16 | # Create a Namespace object https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ 17 | ns = Namespace("app-ns", 18 | metadata={ 19 | "name": "joe-duffy", 20 | }, 21 | opts=ResourceOptions(provider=k8s_provider) 22 | ) 23 | 24 | app_labels = { 25 | "app": "iac-workshop" 26 | } 27 | app_deployment = Deployment("app-dep", 28 | metadata={ 29 | "namespace": ns.metadata["name"] 30 | }, 31 | spec={ 32 | "selector": { 33 | "match_labels": app_labels, 34 | }, 35 | "replicas": 1, 36 | "template": { 37 | "metadata": { 38 | "labels": app_labels, 39 | }, 40 | "spec": { 41 | "containers": [{ 42 | "name": "iac-workshop", 43 | "image": "gcr.io/google-samples/kubernetes-bootcamp:v1", 44 | }], 45 | }, 46 | }, 47 | }, 48 | opts=ResourceOptions(provider=k8s_provider) 49 | ) 50 | 51 | service = Service("app-service", 52 | metadata={ 53 | "namespace": ns.metadata["name"], 54 | "labels": app_labels 55 | }, 56 | spec={ 57 | "ports": [{ 58 | "port": 80, 59 | "target_port": 8080, 60 | }], 61 | "selector": app_labels, 62 | "type": "LoadBalancer", 63 | }, 64 | opts=ResourceOptions(provider=k8s_provider) 65 | ) 66 | 67 | export('url', Output.all(service.status['load_balancer']['ingress'][0]['hostname'], service.spec['ports'][0]['port']) \ 68 | .apply(lambda args: f"http://{args[0]}:{round(args[1])}")) 69 | -------------------------------------------------------------------------------- /labs/aws/in-person/python/lab-05/code/step6.py: -------------------------------------------------------------------------------- 1 | from pulumi import export, StackReference, Output, ResourceOptions 2 | from pulumi_kubernetes import Provider 3 | from pulumi_kubernetes.apps.v1 import Deployment 4 | from pulumi_kubernetes.core.v1 import Service, Namespace 5 | import pulumi 6 | 7 | # Create StackReference to the Kubernetes cluster stack 8 | config = pulumi.Config() 9 | stackRef = config.require("clusterStackRef"); 10 | infra = StackReference(f"{stackRef}") 11 | 12 | # Declare a provider using the KubeConfig we created 13 | # This will be used to interact with the EKS cluster 14 | k8s_provider = Provider("k8s-provider", kubeconfig=infra.get_output("kubeconfig")) 15 | 16 | # Create a Namespace object https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ 17 | ns = Namespace("app-ns", 18 | metadata={ 19 | "name": "joe-duffy", 20 | }, 21 | opts=ResourceOptions(provider=k8s_provider) 22 | ) 23 | 24 | app_labels = { 25 | "app": "iac-workshop" 26 | } 27 | app_deployment = Deployment("app-dep", 28 | metadata={ 29 | "namespace": ns.metadata["name"] 30 | }, 31 | spec={ 32 | "selector": { 33 | "match_labels": app_labels, 34 | }, 35 | "replicas": 3, 36 | "template": { 37 | "metadata": { 38 | "labels": app_labels, 39 | }, 40 | "spec": { 41 | "containers": [{ 42 | "name": "iac-workshop", 43 | "image": "jocatalin/kubernetes-bootcamp:v2", 44 | }], 45 | }, 46 | }, 47 | }, 48 | opts=ResourceOptions(provider=k8s_provider) 49 | ) 50 | 51 | service = Service("app-service", 52 | metadata={ 53 | "namespace": ns.metadata["name"], 54 | "labels": app_labels 55 | }, 56 | spec={ 57 | "ports": [{ 58 | "port": 80, 59 | "target_port": 8080, 60 | }], 61 | "selector": app_labels, 62 | "type": "LoadBalancer", 63 | }, 64 | opts=ResourceOptions(provider=k8s_provider) 65 | ) 66 | 67 | export('url', Output.all(service.status['load_balancer']['ingress'][0]['hostname'], service.spec['ports'][0]['port']) \ 68 | .apply(lambda args: f"http://{args[0]}:{round(args[1])}")) 69 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/README.md: -------------------------------------------------------------------------------- 1 | ### Lab 1 — Modern Infrastructure as Code 2 | 3 | The first lab takes you on a tour of infrastructure as code concepts: 4 | 5 | 1. [Creating a New Project](./lab-01/01-creating-a-new-project.md) 6 | 2. [Configuring AWS](./lab-01/02-configuring-aws.md) 7 | 3. [Provisioning Infrastructure](./lab-01/03-provisioning-infrastructure.md) 8 | 4. [Updating your Infrastructure](./lab-01/04-updating-your-infrastructure.md) 9 | 5. [Making Your Stack Configurable](./lab-01/05-making-your-stack-configurable.md) 10 | 6. [Creating a Second Stack](./lab-01/06-creating-a-second-stack.md) 11 | 7. [Destroying Your Infrastructure](./lab-01/07-destroying-your-infrastructure.md) 12 | 13 | 14 | ### Lab 2 - Provision EC2 Virtual Machines 15 | 16 | In this [lab](./lab-02/README.md), you will learn about the creation of Virtual Machines in EC2. This lab will include 17 | the use of loops to create loadbalanced infrastructure in EC2. 18 | 19 | ### Lab 3 - Deploying Containers to Elastic Container Service (ECS) 20 | 21 | In this [lab](./lab-03/README.md), you will learn about how to create and deploy container based applications to Elastic 22 | Container Service (ECS). 23 | 24 | ### Lab 4 - Deploying a Kubernetes Cluster 25 | 26 | In this [lab](./lab-04/README.md), you will learn about how to create and deploy a Kubernetes Cluster in EKS. 27 | Cluster. 28 | 29 | ### Lab 5 - Deploying Containers to a Kubernetes Cluster 30 | 31 | In this [lab](./lab-05/README.md), you will learn about how to create and deploy container based applications to a Kubernetes 32 | Cluster. 33 | 34 | ### Lab 6 - Using AWS Lambda for Serverless Application Patterns 35 | 36 | In this [lab](./lab-06/README.md), you will learn about how to create and deploy serverless applications to AWS Lambda. 37 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-01/01-creating-a-new-project.md: -------------------------------------------------------------------------------- 1 | # Creating a New Project 2 | 3 | Infrastructure in Pulumi is organized into projects. Each project is a single program that, when run, declares the desired infrastructure for Pulumi to manage. 4 | 5 | ## Step 1 — Create a Directory 6 | 7 | Each Pulumi project lives in its own directory. Create one now and change into it: 8 | 9 | ```bash 10 | mkdir iac-workshop 11 | cd iac-workshop 12 | ``` 13 | 14 | > Pulumi will use the directory name as your project name by default. To create an independent project, simply name the directory differently. 15 | 16 | ## Step 2 — Initialize Your Project 17 | 18 | A Pulumi project is just a directory with some files in it. It's possible for you to create a new one by hand. The `pulumi new` command, however, automates the process: 19 | 20 | ```bash 21 | pulumi new typescript -y 22 | ``` 23 | 24 | This will print output similar to the following with a bit more information and status as it goes: 25 | 26 | ``` 27 | Created project 'iac-workshop' 28 | Created stack 'dev' 29 | Saved config 30 | Installing dependencies... 31 | Finished installing dependencies 32 | 33 | Your new project is ready to go! 34 | ``` 35 | 36 | This command has created all the files we need, initialized a new stack named `dev` (an instance of our project), and installed the needed package dependencies from NPM. 37 | 38 | ## Step 3 — Inspect Your New Project 39 | 40 | Our project is comprised of multiple files: 41 | 42 | * **`index.ts`**: your program's main entrypoint file 43 | * **`package.json`** and **`package-lock.json`**: your project's NPM dependency information 44 | * **`Pulumi.yaml`**: your project's metadata, containing its name and language 45 | * **`tsconfig.json`**: your project's TypeScript settings 46 | * **`node_modules/`**: a directory containing your project's installed NPM dependencies 47 | 48 | Run `cat index.ts` to see the contents of your project's empty program: 49 | 50 | ```typescript 51 | import * as pulumi from "@pulumi/pulumi"; 52 | ``` 53 | 54 | Feel free to explore the other files, although we won't be editing any of them by hand. 55 | 56 | # Next Steps 57 | 58 | * [Configuring AWS](./02-configuring-aws.md) 59 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-01/02-configuring-aws.md: -------------------------------------------------------------------------------- 1 | # Configuring AWS 2 | 3 | Now that you have a basic project, let's configure AWS support for it. 4 | 5 | ## Step 1 — Install the AWS Package 6 | 7 | Run the following command to install the AWS package: 8 | 9 | ```bash 10 | npm install @pulumi/aws 11 | ``` 12 | 13 | The package will be added to `node_modules/`, `package.json`, and `package-lock.json`. 14 | 15 | ## Step 2 — Import the AWS Package 16 | 17 | Now that the AWS package is installed, add the following line to `index.ts` to import it: 18 | 19 | ```typescript 20 | ... 21 | import * as aws from "@pulumi/aws"; 22 | ``` 23 | 24 | > :white_check_mark: After this change, your `index.ts` should [look like this](./code/02-configuring-aws/step2.ts). 25 | 26 | ## Step 3 — Configure an AWS Region 27 | 28 | Configure the AWS region you would like to deploy to: 29 | 30 | ```bash 31 | pulumi config set aws:region us-east-1 32 | ``` 33 | 34 | > If you are doing an interactive lab, please use the recommended region. This will ensure AWS limits are not exceeded during the labs. 35 | 36 | Feel free to choose any AWS region that supports the services used in these labs ([see this table](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions) for a list of available regions). 37 | 38 | ## (Optional) Step 4 — Configure an AWS Profile 39 | 40 | If you're using an alternative AWS profile, you can tell Pulumi which to use in one of two ways: 41 | 42 | * Using an environment variable: `export AWS_PROFILE=` 43 | * Using configuration: `pulumi config set aws:profile ` 44 | 45 | ## Next Steps 46 | 47 | * [Provisioning a S3 Bucket](./03-provisioning-infrastructure.md) 48 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-01/06-creating-a-second-stack.md: -------------------------------------------------------------------------------- 1 | # Creating a Second Stack 2 | 3 | It is easy to create multiple instances of the same project. This is called a stack. This is handy for multiple development or test environments, staging versus production, and scaling a given infrastructure across many regions. 4 | 5 | ## Step 1 — Create and Configure a New Stack 6 | 7 | Create a new stack: 8 | 9 | ```bash 10 | pulumi stack init prod 11 | ``` 12 | 13 | Next, configure its two required variables: 14 | 15 | ```bash 16 | pulumi config set aws:region eu-west-1 17 | pulumi config set iac-workshop:siteDir wwwprod 18 | ``` 19 | 20 | If you are ever curious to see the list of stacks for your current project, run this command: 21 | 22 | ```bash 23 | pulumi stack ls 24 | ``` 25 | 26 | It will print all stacks for this project that are available to you: 27 | 28 | ``` 29 | NAME LAST UPDATE RESOURCE COUNT URL 30 | dev 30 minutes ago 5 https://app.pulumi.com/joeduffy/iac-workshop/dev 31 | prod* 3 minutes ago 0 https://app.pulumi.com/joeduffy/iac-workshop/prod 32 | ``` 33 | 34 | ## Step 2 — Populate the New Site Directory 35 | 36 | It would have been possible to use the existing `www` directory for the `siteDir`. In this example, you will use a different `wwwprod` directory, to demonstrate the value of having configurability. 37 | 38 | Create this new directory: 39 | 40 | ```bash 41 | mkdir wwwprod 42 | ``` 43 | 44 | Add a new `index.html` file to it: 45 | 46 | ```html 47 | 48 | 49 |

Hello Pulumi

50 |

(in production!)

51 | 52 | 53 | ``` 54 | 55 | ## Step 3 — Deploy the New Stack 56 | 57 | Now deploy all of the changes: 58 | 59 | ```bash 60 | pulumi up 61 | ``` 62 | 63 | This will create an entirely new set of resources from scratch, unrelated to the existing `dev` stack's resources. 64 | 65 | ``` 66 | Updating (prod): 67 | 68 | Type Name Status 69 | + pulumi:pulumi:Stack iac-workshop-prod created 70 | + ├─ aws:s3:Bucket my-bucket created 71 | + └─ aws:s3:BucketObject index.html created 72 | 73 | Outputs: 74 | bucketEndpoint: "http://my-bucket-c7318c1.s3-website-eu-west-1.amazonaws.com" 75 | bucketName : "my-bucket-c7318c1" 76 | 77 | Resources: 78 | + 3 created 79 | 80 | Duration: 28s 81 | 82 | Permalink: https://app.pulumi.com/joeduffy/iac-workshop/prod/updates/1 83 | ``` 84 | 85 | Now fetch your new website: 86 | 87 | ```bash 88 | curl $(pulumi stack output bucketEndpoint) 89 | ``` 90 | 91 | Notice that it's the new production version of your content: 92 | 93 | ``` 94 | 95 | 96 |

Hello Pulumi

97 |

(in production!)

98 | 99 | 100 | ``` 101 | 102 | ## Next Steps 103 | 104 | * [Destroying Your Infrastructure](./07-destroying-your-infrastructure.md) 105 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-01/code/02-configuring-aws/step2.ts: -------------------------------------------------------------------------------- 1 | import * as pulumi from "@pulumi/pulumi"; 2 | import * as aws from "@pulumi/aws"; 3 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-01/code/03-provisioning-infrastructure/step1.ts: -------------------------------------------------------------------------------- 1 | import * as pulumi from "@pulumi/pulumi"; 2 | import * as aws from "@pulumi/aws"; 3 | 4 | const myBucket = new aws.s3.Bucket("my-bucket"); 5 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-01/code/03-provisioning-infrastructure/step4.ts: -------------------------------------------------------------------------------- 1 | import * as pulumi from "@pulumi/pulumi"; 2 | import * as aws from "@pulumi/aws"; 3 | 4 | const myBucket = new aws.s3.Bucket("my-bucket"); 5 | 6 | export const bucketName = myBucket.bucket; 7 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-01/code/04-updating-your-infrastructure/step1.ts: -------------------------------------------------------------------------------- 1 | import * as pulumi from "@pulumi/pulumi"; 2 | import * as aws from "@pulumi/aws"; 3 | import * as path from "path"; 4 | 5 | const myBucket = new aws.s3.Bucket("my-bucket"); 6 | 7 | const myObject = new aws.s3.BucketObject("index.html", { 8 | bucket: myBucket, 9 | source: new pulumi.asset.FileAsset(path.join("site", "index.html")), 10 | }); 11 | 12 | export const bucketName = myBucket.bucket; 13 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-01/code/04-updating-your-infrastructure/step2.ts: -------------------------------------------------------------------------------- 1 | import * as pulumi from "@pulumi/pulumi"; 2 | import * as aws from "@pulumi/aws"; 3 | import * as path from "path"; 4 | 5 | const myBucket = new aws.s3.Bucket("my-bucket", { 6 | website: { 7 | indexDocument: "index.html", 8 | }, 9 | }); 10 | 11 | const myObject = new aws.s3.BucketObject("index.html", { 12 | acl: "public-read", 13 | bucket: myBucket, 14 | contentType: "text/html", 15 | source: new pulumi.asset.FileAsset(path.join("site", "index.html")), 16 | }); 17 | 18 | export const bucketName = myBucket.bucket; 19 | export const bucketEndpoint = pulumi.interpolate`http://${myBucket.websiteEndpoint}`; 20 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-01/code/05-making-your-stack-configurable/step2.ts: -------------------------------------------------------------------------------- 1 | import * as pulumi from "@pulumi/pulumi"; 2 | import * as aws from "@pulumi/aws"; 3 | import * as path from "path"; 4 | import { siteDir } from "./config"; 5 | 6 | const myBucket = new aws.s3.Bucket("my-bucket", { 7 | website: { 8 | indexDocument: "index.html", 9 | }, 10 | }); 11 | 12 | const myObject = new aws.s3.BucketObject("index.html", { 13 | acl: "public-read", 14 | bucket: myBucket, 15 | contentType: "text/html", 16 | source: path.join(siteDir, "index.html"), 17 | }); 18 | 19 | export const bucketName = myBucket.bucket; 20 | export const bucketEndpoint = pulumi.interpolate`http://${myBucket.websiteEndpoint}`; 21 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-01/code/05-making-your-stack-configurable/step4.ts: -------------------------------------------------------------------------------- 1 | import * as pulumi from "@pulumi/pulumi"; 2 | import * as aws from "@pulumi/aws"; 3 | import * as path from "path"; 4 | import { siteDir } from "./config"; 5 | import * as mime from "mime"; 6 | import * as nodedir from "node-dir"; 7 | 8 | const myBucket = new aws.s3.Bucket("my-bucket", { 9 | website: { 10 | indexDocument: "index.html", 11 | }, 12 | }); 13 | 14 | const files = nodedir.files(siteDir, { sync: true }); 15 | for (const file of files) { 16 | const name = file.substring(siteDir.length+1); 17 | const myObject = new aws.s3.BucketObject(name, { 18 | acl: "public-read", 19 | bucket: myBucket, 20 | contentType: mime.getType(file) || undefined, 21 | source: file, 22 | }); 23 | } 24 | 25 | export const bucketName = myBucket.bucket; 26 | export const bucketEndpoint = pulumi.interpolate`http://${myBucket.websiteEndpoint}`; 27 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-02/code/step1.ts: -------------------------------------------------------------------------------- 1 | import * as awsx from "@pulumi/awsx"; 2 | 3 | // Variable we will use for naming purpose 4 | const name = "demo"; 5 | 6 | // Creating a VPC with a Single Nat Gateway Strategy (To save cost) 7 | const myvpc = new awsx.ec2.Vpc(`${name}-vpc`, { 8 | cidrBlock: "10.0.0.0/24", 9 | numberOfAvailabilityZones: 3, 10 | enableDnsHostnames: true, 11 | natGateways: { 12 | strategy: "Single", // This is mainly to save cost. You do this only in dev 13 | }, 14 | }); 15 | 16 | // VPC Outputs 17 | export const vpc_id = myvpc.vpcId; 18 | export const vpc_natgateways = myvpc.natGateways[0].id; 19 | export const vpc_public_subnetids = myvpc.publicSubnetIds; 20 | export const vpc_private_subnetids = myvpc.privateSubnetIds; -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-02/code/step2.ts: -------------------------------------------------------------------------------- 1 | import * as awsx from "@pulumi/awsx"; 2 | import * as aws from "@pulumi/aws"; 3 | 4 | // Variable we will use for naming purpose 5 | const name = "demo"; 6 | 7 | // Creating a VPC with a Single Nat Gateway Strategy (To save cost) 8 | const myvpc = new awsx.ec2.Vpc(`${name}-vpc`, { 9 | cidrBlock: "10.0.0.0/24", 10 | numberOfAvailabilityZones: 3, 11 | enableDnsHostnames: true, 12 | natGateways: { 13 | strategy: "Single", // This is mainly to save cost. You do this only in dev 14 | }, 15 | }); 16 | 17 | // VPC Outputs 18 | export const vpc_id = myvpc.vpcId; 19 | export const vpc_natgateways = myvpc.natGateways[0].id; 20 | export const vpc_public_subnetids = myvpc.publicSubnetIds; 21 | export const vpc_private_subnetids = myvpc.privateSubnetIds; 22 | 23 | // Creating Security Group within VPC 24 | const mysecuritygroup = new aws.ec2.SecurityGroup(`${name}-securitygroup`, { 25 | vpcId:myvpc.vpcId, 26 | ingress: [ 27 | { protocol: "tcp", 28 | fromPort: 443, 29 | toPort: 443, 30 | cidrBlocks: ["0.0.0.0/0"], 31 | description: "Allow inbound access via https", 32 | self: true, // Add the securitygroup itself as a source 33 | }, 34 | { 35 | protocol: "tcp", 36 | fromPort: 80, 37 | toPort: 80, 38 | cidrBlocks: ["0.0.0.0/0"], 39 | description: "Allow inbound access via http", 40 | self: true, // Add the securitygroup itself as a source 41 | }, 42 | ], 43 | egress: [ 44 | { protocol: "tcp", 45 | fromPort: 443, 46 | toPort: 443, 47 | cidrBlocks: ["0.0.0.0/0"], 48 | description: "Allow outbound access via https" 49 | }, 50 | { 51 | protocol: "tcp", 52 | fromPort: 80, 53 | toPort: 80, 54 | cidrBlocks: ["0.0.0.0/0"], 55 | description: "Allow outbound access via http" 56 | }, 57 | ], 58 | tags: {"Name": `${name}-securitygroup`}, 59 | }, { parent: myvpc, dependsOn: myvpc }); 60 | 61 | // Exporting security group outputs 62 | export const security_group_name = mysecuritygroup.id; 63 | export const security_group_vpc = mysecuritygroup.vpcId; 64 | export const security_group_egress = mysecuritygroup.egress; 65 | export const security_group_ingress = mysecuritygroup.ingress; -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-02/code/step3.ts: -------------------------------------------------------------------------------- 1 | import * as awsx from "@pulumi/awsx"; 2 | import * as aws from "@pulumi/aws"; 3 | 4 | // Variable we will use for naming purpose 5 | const name = "demo"; 6 | 7 | // Creating a VPC with a Single Nat Gateway Strategy (To save cost) 8 | const myvpc = new awsx.ec2.Vpc(`${name}-vpc`, { 9 | cidrBlock: "10.0.0.0/24", 10 | numberOfAvailabilityZones: 3, 11 | enableDnsHostnames: true, 12 | natGateways: { 13 | strategy: "Single", // This is mainly to save cost. You do this only in dev 14 | }, 15 | }); 16 | 17 | // VPC Outputs 18 | export const vpc_id = myvpc.vpcId; 19 | export const vpc_natgateways = myvpc.natGateways[0].id; 20 | export const vpc_public_subnetids = myvpc.publicSubnetIds; 21 | export const vpc_private_subnetids = myvpc.privateSubnetIds; 22 | 23 | // Creating Security Group within VPC 24 | const mysecuritygroup = new aws.ec2.SecurityGroup(`${name}-securitygroup`, { 25 | vpcId:myvpc.vpcId, 26 | ingress: [ 27 | { protocol: "tcp", 28 | fromPort: 443, 29 | toPort: 443, 30 | cidrBlocks: ["0.0.0.0/0"], 31 | description: "Allow inbound access via https", 32 | self: true, // Add the securitygroup itself as a source 33 | }, 34 | { 35 | protocol: "tcp", 36 | fromPort: 80, 37 | toPort: 80, 38 | cidrBlocks: ["0.0.0.0/0"], 39 | description: "Allow inbound access via http", 40 | self: true, // Add the securitygroup itself as a source 41 | }, 42 | ], 43 | egress: [ 44 | { protocol: "tcp", 45 | fromPort: 443, 46 | toPort: 443, 47 | cidrBlocks: ["0.0.0.0/0"], 48 | description: "Allow outbound access via https" 49 | }, 50 | { 51 | protocol: "tcp", 52 | fromPort: 80, 53 | toPort: 80, 54 | cidrBlocks: ["0.0.0.0/0"], 55 | description: "Allow outbound access via http" 56 | }, 57 | ], 58 | tags: {"Name": `${name}-securitygroup`}, 59 | }, { parent: myvpc, dependsOn: myvpc }); 60 | 61 | // Exporting security group outputs 62 | export const security_group_name = mysecuritygroup.id; 63 | export const security_group_vpc = mysecuritygroup.vpcId; 64 | export const security_group_egress = mysecuritygroup.egress; 65 | export const security_group_ingress = mysecuritygroup.ingress; 66 | 67 | // get Function for AMI 68 | const myami = aws.ec2.getAmi({ 69 | filters: [{ name: "name", values: ["amzn2-ami-k*-hvm-*-x86_64-gp2"] }], 70 | owners: [ "amazon" ], 71 | mostRecent: true, 72 | }); 73 | 74 | // Exporting AMI_ID First Time 75 | //export const ami_id = myami; 76 | 77 | // Exporting AMI_ID with what we want. 78 | export const ami_id = myami.then(ami=>ami.id); -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-02/code/step4.ts: -------------------------------------------------------------------------------- 1 | import * as awsx from "@pulumi/awsx"; 2 | import * as aws from "@pulumi/aws"; 3 | 4 | // Variable we will use for naming purpose 5 | const name = "demo"; 6 | 7 | // Creating a VPC with a Single Nat Gateway Strategy (To save cost) 8 | const myvpc = new awsx.ec2.Vpc(`${name}-vpc`, { 9 | cidrBlock: "10.0.0.0/24", 10 | numberOfAvailabilityZones: 3, 11 | enableDnsHostnames: true, 12 | natGateways: { 13 | strategy: "Single", // This is mainly to save cost. You do this only in dev 14 | }, 15 | }); 16 | 17 | // VPC Outputs 18 | export const vpc_id = myvpc.vpcId; 19 | export const vpc_natgateways = myvpc.natGateways[0].id; 20 | export const vpc_public_subnetids = myvpc.publicSubnetIds; 21 | export const vpc_private_subnetids = myvpc.privateSubnetIds; 22 | 23 | // Creating Security Group within VPC 24 | const mysecuritygroup = new aws.ec2.SecurityGroup(`${name}-securitygroup`, { 25 | vpcId:myvpc.vpcId, 26 | ingress: [ 27 | { protocol: "tcp", 28 | fromPort: 443, 29 | toPort: 443, 30 | cidrBlocks: ["0.0.0.0/0"], 31 | description: "Allow inbound access via https", 32 | self: true, // Add the securitygroup itself as a source 33 | }, 34 | { 35 | protocol: "tcp", 36 | fromPort: 80, 37 | toPort: 80, 38 | cidrBlocks: ["0.0.0.0/0"], 39 | description: "Allow inbound access via http", 40 | self: true, // Add the securitygroup itself as a source 41 | }, 42 | ], 43 | egress: [ 44 | { protocol: "tcp", 45 | fromPort: 443, 46 | toPort: 443, 47 | cidrBlocks: ["0.0.0.0/0"], 48 | description: "Allow outbound access via https" 49 | }, 50 | { 51 | protocol: "tcp", 52 | fromPort: 80, 53 | toPort: 80, 54 | cidrBlocks: ["0.0.0.0/0"], 55 | description: "Allow outbound access via http" 56 | }, 57 | ], 58 | tags: {"Name": `${name}-securitygroup`}, 59 | }, { parent: myvpc, dependsOn: myvpc }); 60 | 61 | // Exporting security group outputs 62 | export const security_group_name = mysecuritygroup.id; 63 | export const security_group_vpc = mysecuritygroup.vpcId; 64 | export const security_group_egress = mysecuritygroup.egress; 65 | export const security_group_ingress = mysecuritygroup.ingress; 66 | 67 | // get Function for AMI 68 | const myami = aws.ec2.getAmi({ 69 | filters: [{ name: "name", values: ["amzn2-ami-k*-hvm-*-x86_64-gp2"] }], 70 | owners: [ "amazon" ], 71 | mostRecent: true, 72 | }); 73 | 74 | // Exporting AMI_ID First Time 75 | //export const ami_id = myami; 76 | 77 | // Exporting AMI_ID with what we want. 78 | export const ami_id = myami.then(ami=>ami.id); 79 | 80 | // Single ec2 instance 81 | const myserver = new aws.ec2.Instance(`${name}-web-server`, { 82 | ami: ami_id, 83 | instanceType: "t2.nano", 84 | subnetId: vpc_public_subnetids[0], 85 | vpcSecurityGroupIds: [mysecuritygroup.id], 86 | tags: { Name: `${name}-web-server` }, 87 | userData: 88 | "#!/bin/bash\n" + 89 | "echo 'Hello, World!' > index.html\n" + 90 | "nohup python -m SimpleHTTPServer 80 &", 91 | }); 92 | 93 | export const ip = myserver.publicIp; 94 | export const hostname = myserver.publicDns; -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-03/code/step1.ts: -------------------------------------------------------------------------------- 1 | import * as awsx from "@pulumi/awsx"; 2 | import * as pulumi from "@pulumi/pulumi"; 3 | 4 | const cluster = new awsx.ecs.Cluster("cluster"); 5 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-03/code/step2.ts: -------------------------------------------------------------------------------- 1 | import * as awsx from "@pulumi/awsx"; 2 | import * as pulumi from "@pulumi/pulumi"; 3 | 4 | const cluster = new awsx.ecs.Cluster("cluster"); 5 | 6 | const alb = new awsx.elasticloadbalancingv2.ApplicationLoadBalancer( 7 | "app-lb", { external: true, securityGroups: cluster.securityGroups }); 8 | const atg = alb.createTargetGroup( 9 | "app-tg", { port: 80, deregistrationDelay: 0 }); 10 | const web = atg.createListener("web", { port: 80 }); 11 | 12 | const appService = new awsx.ecs.FargateService("app-svc", { 13 | cluster, 14 | taskDefinitionArgs: { 15 | container: { 16 | image: "nginx", 17 | portMappings: [ web ], 18 | }, 19 | }, 20 | desiredCount: 1, 21 | }); 22 | 23 | export const url = pulumi.interpolate`${web.endpoint.hostname}`; 24 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-03/code/step4.ts: -------------------------------------------------------------------------------- 1 | import * as awsx from "@pulumi/awsx"; 2 | import * as pulumi from "@pulumi/pulumi"; 3 | 4 | const cluster = new awsx.ecs.Cluster("cluster"); 5 | 6 | const alb = new awsx.elasticloadbalancingv2.ApplicationLoadBalancer( 7 | "app-lb", { external: true, securityGroups: cluster.securityGroups }); 8 | const atg = alb.createTargetGroup( 9 | "app-tg", { port: 80, deregistrationDelay: 0 }); 10 | const web = atg.createListener("web", { port: 80 }); 11 | 12 | const containerImage = awsx.ecs.Image.fromPath("app-img", "./app"); 13 | 14 | const appService = new awsx.ecs.FargateService("app-svc", { 15 | cluster, 16 | taskDefinitionArgs: { 17 | container: { 18 | image: containerImage, 19 | portMappings: [ web ], 20 | }, 21 | }, 22 | desiredCount: 1, 23 | }); 24 | 25 | export const url = pulumi.interpolate`${web.endpoint.hostname}`; 26 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-03/code/step5.ts: -------------------------------------------------------------------------------- 1 | import * as awsx from "@pulumi/awsx"; 2 | import * as pulumi from "@pulumi/pulumi"; 3 | 4 | const cluster = new awsx.ecs.Cluster("cluster"); 5 | 6 | const alb = new awsx.elasticloadbalancingv2.ApplicationLoadBalancer( 7 | "app-lb", { external: true, securityGroups: cluster.securityGroups }); 8 | const atg = alb.createTargetGroup( 9 | "app-tg", { port: 80, deregistrationDelay: 0 }); 10 | const web = atg.createListener("web", { port: 80 }); 11 | 12 | const containerImage = awsx.ecs.Image.fromPath("app-img", "./app"); 13 | 14 | const appService = new awsx.ecs.FargateService("app-svc", { 15 | cluster, 16 | taskDefinitionArgs: { 17 | container: { 18 | image: containerImage, 19 | portMappings: [ web ], 20 | }, 21 | }, 22 | desiredCount: 3, 23 | }); 24 | 25 | export const url = pulumi.interpolate`${web.endpoint.hostname}`; 26 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-04/code/step1.ts: -------------------------------------------------------------------------------- 1 | import * as eks from "@pulumi/eks" 2 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-04/code/step2.ts: -------------------------------------------------------------------------------- 1 | import * as eks from "@pulumi/eks" 2 | 3 | // Create an EKS cluster with the default VPC, and default node group with 4 | // two t2.medium node instances. 5 | const cluster = new eks.Cluster("eks", { 6 | deployDashboard: false, 7 | }); 8 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-04/code/step3.ts: -------------------------------------------------------------------------------- 1 | import * as eks from "@pulumi/eks" 2 | 3 | // Create an EKS cluster with the default VPC, and default node group with 4 | // two t2.medium node instances. 5 | const cluster = new eks.Cluster("eks", { 6 | deployDashboard: false, 7 | }); 8 | 9 | export const kubeconfig = cluster.kubeconfig; 10 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-05/code/step1.ts: -------------------------------------------------------------------------------- 1 | import * as k8s from "@pulumi/kubernetes"; 2 | import * as pulumi from "@pulumi/pulumi" 3 | 4 | let pulumiConfig = new pulumi.Config(); 5 | 6 | // Existing Pulumi stack reference in the format: 7 | // // e.g. "myUser/myProject/dev" 8 | const clusterStackRef = new pulumi.StackReference(pulumiConfig.require("clusterStackRef")); 9 | 10 | // Get the kubeconfig from the cluster stack output. 11 | const kubeconfig = clusterStackRef.getOutput("kubeconfig").apply(JSON.stringify); 12 | 13 | // Create the k8s provider with the kubeconfig. 14 | const provider = new k8s.Provider("k8sProvider", {kubeconfig}); 15 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-05/code/step2.ts: -------------------------------------------------------------------------------- 1 | import * as k8s from "@pulumi/kubernetes"; 2 | import * as pulumi from "@pulumi/pulumi"; 3 | 4 | let pulumiConfig = new pulumi.Config(); 5 | 6 | // Existing Pulumi stack reference in the format: 7 | // // e.g. "myUser/myProject/dev" 8 | const clusterStackRef = new pulumi.StackReference(pulumiConfig.require("clusterStackRef")); 9 | 10 | // Get the kubeconfig from the cluster stack output. 11 | const kubeconfig = clusterStackRef.getOutput("kubeconfig").apply(JSON.stringify); 12 | 13 | // Create the k8s provider with the kubeconfig. 14 | const provider = new k8s.Provider("k8sProvider", {kubeconfig}); 15 | 16 | const ns = new k8s.core.v1.Namespace("app-ns", { 17 | metadata: { name: "joe-duffy" }, 18 | }, {provider}); 19 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-05/code/step3.ts: -------------------------------------------------------------------------------- 1 | import * as k8s from "@pulumi/kubernetes"; 2 | import * as pulumi from "@pulumi/pulumi"; 3 | 4 | let pulumiConfig = new pulumi.Config(); 5 | 6 | // Existing Pulumi stack reference in the format: 7 | // // e.g. "myUser/myProject/dev" 8 | const clusterStackRef = new pulumi.StackReference(pulumiConfig.require("clusterStackRef")); 9 | 10 | // Get the kubeconfig from the cluster stack output. 11 | const kubeconfig = clusterStackRef.getOutput("kubeconfig").apply(JSON.stringify); 12 | 13 | // Create the k8s provider with the kubeconfig. 14 | const provider = new k8s.Provider("k8sProvider", {kubeconfig}); 15 | 16 | const ns = new k8s.core.v1.Namespace("app-ns", { 17 | metadata: { name: "joe-duffy" }, 18 | }, {provider}); 19 | 20 | const appLabels = { app: "iac-workshop" }; 21 | const deployment = new k8s.apps.v1.Deployment("app-dep", { 22 | metadata: { namespace: ns.metadata.name }, 23 | spec: { 24 | selector: { matchLabels: appLabels }, 25 | replicas: 1, 26 | template: { 27 | metadata: { labels: appLabels }, 28 | spec: { 29 | containers: [{ 30 | name: "iac-workshop", 31 | image: "gcr.io/google-samples/kubernetes-bootcamp:v1", 32 | }], 33 | }, 34 | }, 35 | }, 36 | }, {provider}); 37 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-05/code/step4.ts: -------------------------------------------------------------------------------- 1 | import * as k8s from "@pulumi/kubernetes"; 2 | import * as pulumi from "@pulumi/pulumi"; 3 | 4 | let pulumiConfig = new pulumi.Config(); 5 | 6 | // Existing Pulumi stack reference in the format: 7 | // // e.g. "myUser/myProject/dev" 8 | const clusterStackRef = new pulumi.StackReference(pulumiConfig.require("clusterStackRef")); 9 | 10 | // Get the kubeconfig from the cluster stack output. 11 | const kubeconfig = clusterStackRef.getOutput("kubeconfig").apply(JSON.stringify); 12 | 13 | // Create the k8s provider with the kubeconfig. 14 | const provider = new k8s.Provider("k8sProvider", {kubeconfig}); 15 | 16 | const ns = new k8s.core.v1.Namespace("app-ns", { 17 | metadata: { name: "joe-duffy" }, 18 | }, {provider}); 19 | 20 | const appLabels = { app: "iac-workshop" }; 21 | const deployment = new k8s.apps.v1.Deployment("app-dep", { 22 | metadata: { namespace: ns.metadata.name }, 23 | spec: { 24 | selector: { matchLabels: appLabels }, 25 | replicas: 1, 26 | template: { 27 | metadata: { labels: appLabels }, 28 | spec: { 29 | containers: [{ 30 | name: "iac-workshop", 31 | image: "gcr.io/google-samples/kubernetes-bootcamp:v1", 32 | }], 33 | }, 34 | }, 35 | }, 36 | }, {provider}); 37 | 38 | const service = new k8s.core.v1.Service("app-svc", { 39 | metadata: { namespace: ns.metadata.name }, 40 | spec: { 41 | selector: appLabels, 42 | ports: [{ port: 80, targetPort: 8080 }], 43 | type: "LoadBalancer", 44 | }, 45 | }, {provider}); 46 | 47 | const address = service.status.loadBalancer.ingress[0].hostname; 48 | const port = service.spec.ports[0].port; 49 | export const url = pulumi.interpolate`http://${address}:${port}`; 50 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-05/code/step6.ts: -------------------------------------------------------------------------------- 1 | import * as k8s from "@pulumi/kubernetes"; 2 | import * as pulumi from "@pulumi/pulumi"; 3 | 4 | let pulumiConfig = new pulumi.Config(); 5 | 6 | // Existing Pulumi stack reference in the format: 7 | // // e.g. "myUser/myProject/dev" 8 | const clusterStackRef = new pulumi.StackReference(pulumiConfig.require("clusterStackRef")); 9 | 10 | // Get the kubeconfig from the cluster stack output. 11 | const kubeconfig = clusterStackRef.getOutput("kubeconfig").apply(JSON.stringify); 12 | 13 | // Create the k8s provider with the kubeconfig. 14 | const provider = new k8s.Provider("k8sProvider", {kubeconfig}); 15 | 16 | const ns = new k8s.core.v1.Namespace("app-ns", { 17 | metadata: { name: "joe-duffy" }, 18 | }, {provider}); 19 | 20 | const appLabels = { app: "iac-workshop" }; 21 | const deployment = new k8s.apps.v1.Deployment("app-dep", { 22 | metadata: { namespace: ns.metadata.name }, 23 | spec: { 24 | selector: { matchLabels: appLabels }, 25 | replicas: 3, 26 | template: { 27 | metadata: { labels: appLabels }, 28 | spec: { 29 | containers: [{ 30 | name: "iac-workshop", 31 | image: "jocatalin/kubernetes-bootcamp:v2", 32 | }], 33 | }, 34 | }, 35 | }, 36 | }, {provider}); 37 | 38 | const service = new k8s.core.v1.Service("app-svc", { 39 | metadata: { namespace: ns.metadata.name }, 40 | spec: { 41 | selector: appLabels, 42 | ports: [{ port: 80, targetPort: 8080 }], 43 | type: "LoadBalancer", 44 | }, 45 | }, {provider}); 46 | 47 | const address = service.status.loadBalancer.ingress[0].hostname; 48 | const port = service.spec.ports[0].port; 49 | export const url = pulumi.interpolate`http://${address}:${port}`; 50 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-06/code/step1.ts: -------------------------------------------------------------------------------- 1 | import * as AWS from "aws-sdk"; 2 | import * as aws from "@pulumi/aws"; 3 | import * as awsx from "@pulumi/awsx"; 4 | import * as pulumi from "@pulumi/pulumi"; 5 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-06/code/step2.ts: -------------------------------------------------------------------------------- 1 | import * as AWS from "aws-sdk"; 2 | import * as aws from "@pulumi/aws"; 3 | import * as awsx from "@pulumi/awsx"; 4 | import * as pulumi from "@pulumi/pulumi"; 5 | 6 | const hits = new aws.dynamodb.Table("hits", { 7 | attributes: [{ name: "Site", type: "S" }], 8 | hashKey: "Site", 9 | billingMode: "PAY_PER_REQUEST", 10 | }); 11 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-06/code/step3.ts: -------------------------------------------------------------------------------- 1 | import * as AWS from "aws-sdk"; 2 | import * as aws from "@pulumi/aws"; 3 | import * as awsx from "@pulumi/awsx"; 4 | import * as pulumi from "@pulumi/pulumi"; 5 | 6 | const hits = new aws.dynamodb.Table("hits", { 7 | attributes: [{ name: "Site", type: "S" }], 8 | hashKey: "Site", 9 | billingMode: "PAY_PER_REQUEST", 10 | }); 11 | 12 | const handlerRole = new aws.iam.Role("handler-role", { 13 | assumeRolePolicy: { 14 | Version: "2012-10-17", 15 | Statement: [{ 16 | Action: "sts:AssumeRole", 17 | Principal: { 18 | Service: "lambda.amazonaws.com" 19 | }, 20 | Effect: "Allow", 21 | Sid: "", 22 | }], 23 | }, 24 | }); 25 | 26 | const handlerPolicy = new aws.iam.RolePolicy("handler-policy", { 27 | role: handlerRole, 28 | policy: hits.arn.apply(arn => JSON.stringify({ 29 | Version: "2012-10-17", 30 | Statement: [ 31 | { 32 | Action: [ 33 | "dynamodb:UpdateItem", 34 | "dynamodb:PutItem", 35 | "dynamodb:GetItem", 36 | "dynamodb:DescribeTable", 37 | ], 38 | Resource: arn, 39 | Effect: "Allow", 40 | }, 41 | { 42 | Action: ["logs:*", "cloudwatch:*"], 43 | Resource: "*", 44 | Effect: "Allow", 45 | }, 46 | ], 47 | })), 48 | }); 49 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-06/code/step4.ts: -------------------------------------------------------------------------------- 1 | import * as AWS from "aws-sdk"; 2 | import * as aws from "@pulumi/aws"; 3 | import * as awsx from "@pulumi/awsx"; 4 | import * as pulumi from "@pulumi/pulumi"; 5 | 6 | const hits = new aws.dynamodb.Table("hits", { 7 | attributes: [{ name: "Site", type: "S" }], 8 | hashKey: "Site", 9 | billingMode: "PAY_PER_REQUEST", 10 | }); 11 | 12 | const handlerRole = new aws.iam.Role("handler-role", { 13 | assumeRolePolicy: { 14 | Version: "2012-10-17", 15 | Statement: [{ 16 | Action: "sts:AssumeRole", 17 | Principal: { 18 | Service: "lambda.amazonaws.com" 19 | }, 20 | Effect: "Allow", 21 | Sid: "", 22 | }], 23 | }, 24 | }); 25 | 26 | const handlerPolicy = new aws.iam.RolePolicy("handler-policy", { 27 | role: handlerRole, 28 | policy: hits.arn.apply(arn => JSON.stringify({ 29 | Version: "2012-10-17", 30 | Statement: [ 31 | { 32 | Action: [ 33 | "dynamodb:UpdateItem", 34 | "dynamodb:PutItem", 35 | "dynamodb:GetItem", 36 | "dynamodb:DescribeTable", 37 | ], 38 | Resource: arn, 39 | Effect: "Allow", 40 | }, 41 | { 42 | Action: ["logs:*", "cloudwatch:*"], 43 | Resource: "*", 44 | Effect: "Allow", 45 | }, 46 | ], 47 | })), 48 | }); 49 | 50 | const site = new awsx.apigateway.API("site", { 51 | routes: [{ 52 | path: "/", 53 | method: "GET", 54 | eventHandler: new aws.lambda.Function("get-handler", { 55 | runtime: aws.lambda.NodeJS10dXRuntime, 56 | code: new pulumi.asset.AssetArchive({ 57 | ".": new pulumi.asset.FileArchive("handler"), 58 | }), 59 | handler: "index.handler", 60 | role: handlerRole.arn, 61 | environment: { 62 | variables: { 63 | "HITS_TABLE": hits.name, 64 | }, 65 | }, 66 | }, { dependsOn: handlerPolicy }), 67 | }], 68 | }); 69 | export const url = site.url; 70 | -------------------------------------------------------------------------------- /labs/aws/in-person/typescript/lab-06/code/step6.ts: -------------------------------------------------------------------------------- 1 | import * as AWS from "aws-sdk"; 2 | import * as aws from "@pulumi/aws"; 3 | import * as awsx from "@pulumi/awsx"; 4 | import * as pulumi from "@pulumi/pulumi"; 5 | 6 | const hits = new aws.dynamodb.Table("hits", { 7 | attributes: [{ name: "Site", type: "S" }], 8 | hashKey: "Site", 9 | billingMode: "PAY_PER_REQUEST", 10 | }); 11 | 12 | const site = new awsx.apigateway.API("site", { 13 | routes: [{ 14 | path: "/", 15 | method: "GET", 16 | eventHandler: async () => { 17 | const dc = new AWS.DynamoDB.DocumentClient(); 18 | const result = await dc.update({ 19 | TableName: hits.name.get(), 20 | Key: { "Site": "ACMECorp" }, 21 | UpdateExpression: "SET Hits = if_not_exists(Hits, :zero) + :incr", 22 | ExpressionAttributeValues: { ":zero": 0, ":incr": 1 }, 23 | ReturnValues: "UPDATED_NEW", 24 | }).promise(); 25 | return { 26 | statusCode: 200, 27 | headers: { "Content-Type": "text/html" }, 28 | body: "

Welcome to ACMECorp!

\n"+ 29 | `

${result.Attributes!.Hits} hits.

\n`, 30 | }; 31 | }, 32 | }], 33 | }); 34 | export const url = site.url; 35 | -------------------------------------------------------------------------------- /labs/aws/pulumi-in-practice/python/README.md: -------------------------------------------------------------------------------- 1 | ### Lab 1 - Preparing to use Pulumi with AWS 2 | 3 | The first lab shows you how to set up your Pulumi project so that you can use Pulumi with AWS. 4 | 5 | We'll cover: 6 | 7 | - Bootstrapping your Pulumi + AWS Python project 8 | - Setting up AWS credentials 9 | - Verifying your access to AWS is valid 10 | - Setting AWS config options in your program 11 | 12 | ### Lab 2 - Managing an S3 Bucket 13 | 14 | The [second lab](./lab-02/README.md) shows a very simple static website deployed with Pulumi. It introduces some key Pulumi features, namely the ability to use standard programming loops. Also in this lab, we'll introduce the Pulumi concept of an Output, and how to manage these outputs when managing standard programming strings. 15 | 16 | Once you have completed this lab, you should feel comfortable with: 17 | 18 | - Running your first Pulumi program in AWS 19 | - Pulumi's Python SDK and its type system 20 | - More steps in understanding Pulumi's programming model 21 | - How you can use traditional programming constructs in Pulumi 22 | 23 | ### Lab 3 - Deploying a Webserver 24 | 25 | The [third lab](./lab-03/README.md) shows how to deploy a simple, python webserver to multiple AWS regions. Once you've completed this lab, you'll feel comfortable with: 26 | 27 | - Retrieving existing resources in AWS using Pulumi's `get` methods 28 | - Looping over retrieved resources to deploy new resources using familiar programming methods 29 | 30 | ### Lab 4 - Deploying Containers to Elastic Container Service (ECS) 31 | 32 | In this lab, you will learn about how to create and deploy container based applications to Elastic Container Service (ECS). 33 | 34 | - You'll gain familiarity with with the AWS Elastic Container Service 35 | -------------------------------------------------------------------------------- /labs/aws/pulumi-in-practice/python/lab-01/README.md: -------------------------------------------------------------------------------- 1 | # Set up lab 2 | -------------------------------------------------------------------------------- /labs/aws/pulumi-in-practice/python/lab-02/code/step1.py: -------------------------------------------------------------------------------- 1 | from pulumi_aws import s3 2 | 3 | bucket = s3.Bucket( 4 | "my-website-bucket", 5 | website=s3.BucketWebsiteArgs( 6 | index_document="index.html", 7 | ), 8 | ) 9 | -------------------------------------------------------------------------------- /labs/aws/pulumi-in-practice/python/lab-02/code/step3.py: -------------------------------------------------------------------------------- 1 | from pulumi_aws import s3 2 | 3 | bucket = s3.Bucket( 4 | "my-website-bucket", 5 | website=s3.BucketWebsiteArgs( 6 | index_document="index.html", 7 | ), 8 | ) 9 | 10 | content_dir = "www" 11 | for file in os.listdir(content_dir): 12 | filepath = os.path.join(content_dir, file) 13 | mime_type, _ = mimetypes.guess_type(filepath) 14 | obj = s3.BucketObject( 15 | file, 16 | bucket=bucket.id, 17 | source=pulumi.FileAsset(filepath), 18 | content_type=mime_type, 19 | opts=pulumi.ResourceOptions(parent=bucket) 20 | ) 21 | 22 | bucket_policy = s3.BucketPolicy( 23 | "my-website-bucket-policy", 24 | bucket=bucket.id, 25 | policy=bucket.arn.apply( 26 | lambda arn: json.dumps({ 27 | "Version": "2012-10-17", 28 | "Statement": [{ 29 | "Effect": "Allow", 30 | "Principal": "*", 31 | "Action": [ 32 | "s3:GetObject" 33 | ], 34 | "Resource": [ 35 | f"{arn}/*" 36 | ] 37 | }] 38 | })), 39 | opts=pulumi.ResourceOptions(parent=bucket) 40 | ) 41 | -------------------------------------------------------------------------------- /labs/aws/pulumi-in-practice/python/lab-02/code/step4.py: -------------------------------------------------------------------------------- 1 | from pulumi_aws import s3 2 | 3 | bucket = s3.Bucket( 4 | "my-website-bucket", 5 | website=s3.BucketWebsiteArgs( 6 | index_document="index.html", 7 | ), 8 | ) 9 | 10 | content_dir = "www" 11 | for file in os.listdir(content_dir): 12 | filepath = os.path.join(content_dir, file) 13 | mime_type, _ = mimetypes.guess_type(filepath) 14 | obj = s3.BucketObject( 15 | file, 16 | bucket=bucket.id, 17 | source=pulumi.FileAsset(filepath), 18 | content_type=mime_type, 19 | opts=pulumi.ResourceOptions(parent=bucket) 20 | ) 21 | 22 | bucket_policy = s3.BucketPolicy( 23 | "my-website-bucket-policy", 24 | bucket=bucket.id, 25 | policy=bucket.arn.apply( 26 | lambda arn: json.dumps({ 27 | "Version": "2012-10-17", 28 | "Statement": [{ 29 | "Effect": "Allow", 30 | "Principal": "*", 31 | "Action": [ 32 | "s3:GetObject" 33 | ], 34 | "Resource": [ 35 | f"{arn}/*" 36 | ] 37 | }] 38 | })), 39 | opts=pulumi.ResourceOptions(parent=bucket) 40 | ) 41 | 42 | pulumi.export('website_url', bucket.website_endpoint) 43 | -------------------------------------------------------------------------------- /labs/aws/pulumi-in-practice/python/lab-02/code/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello, Pulumi! 4 | 5 |

Hello, S3!

6 |

Made with ❤️ with Pulumi and Python

7 | 8 | 9 | -------------------------------------------------------------------------------- /labs/aws/pulumi-in-practice/python/lab-03/code/step1.py: -------------------------------------------------------------------------------- 1 | from pulumi import export 2 | import pulumi_aws as aws 3 | 4 | ami = aws.get_ami( 5 | most_recent="true", 6 | owners=["137112412989"], 7 | filters=[{"name":"name","values":["amzn-ami-hvm-*-x86_64-ebs"]}]) 8 | 9 | group = aws.ec2.SecurityGroup( 10 | "web-secgrp", 11 | description='Enable HTTP access', 12 | ingress=[ 13 | { 'protocol': 'icmp', 'from_port': 8, 'to_port': 0, 'cidr_blocks': ['0.0.0.0/0'] }, 14 | { 'protocol': 'tcp', 'from_port': 80, 'to_port': 80, 'cidr_blocks': ['0.0.0.0/0'] } 15 | ]) 16 | 17 | server = aws.ec2.Instance( 18 | 'web-server', 19 | instance_type="t2.micro", 20 | security_groups=[group.name], 21 | ami=ami.id, 22 | user_data=""" 23 | #!/bin/bash 24 | echo "Hello, World!" > index.html 25 | nohup python -m SimpleHTTPServer 80 & 26 | """, 27 | tags={ 28 | "Name": "web-server", 29 | }, 30 | ) 31 | 32 | export('ip', server.public_ip) 33 | export('hostname', server.public_dns) 34 | -------------------------------------------------------------------------------- /labs/aws/pulumi-in-practice/python/lab-03/code/step3.py: -------------------------------------------------------------------------------- 1 | from pulumi import export 2 | import pulumi_aws as aws 3 | 4 | ami = aws.get_ami( 5 | most_recent="true", 6 | owners=["137112412989"], 7 | filters=[{"name":"name","values":["amzn-ami-hvm-*-x86_64-ebs"]}]) 8 | 9 | group = aws.ec2.SecurityGroup( 10 | "web-secgrp", 11 | description='Enable HTTP access', 12 | ingress=[ 13 | { 'protocol': 'icmp', 'from_port': 8, 'to_port': 0, 'cidr_blocks': ['0.0.0.0/0'] }, 14 | { 'protocol': 'tcp', 'from_port': 80, 'to_port': 80, 'cidr_blocks': ['0.0.0.0/0'] } 15 | ]) 16 | 17 | ips = [] 18 | hostnames = [] 19 | for az in aws.get_availability_zones().names: 20 | server = aws.ec2.Instance(f'web-server-{az}', 21 | instance_type="t2.micro", 22 | security_groups=[group.name], 23 | ami=ami.id, 24 | availability_zone=az, 25 | user_data="""#!/bin/bash 26 | echo \"Hello, World -- from {}!\" > index.html 27 | nohup python -m SimpleHTTPServer 80 & 28 | """.format(az), 29 | tags={ 30 | "Name": "web-server", 31 | }, 32 | ) 33 | ips.append(server.public_ip) 34 | hostnames.append(server.public_dns) 35 | 36 | export('ips', ips) 37 | export('hostnames', hostnames) 38 | -------------------------------------------------------------------------------- /labs/aws/pulumi-in-practice/python/lab-03/code/step4.py: -------------------------------------------------------------------------------- 1 | from pulumi import export 2 | import pulumi_aws as aws 3 | 4 | ami = aws.get_ami( 5 | most_recent="true", 6 | owners=["137112412989"], 7 | filters=[{"name":"name","values":["amzn-ami-hvm-*-x86_64-ebs"]}]) 8 | 9 | group = aws.ec2.SecurityGroup( 10 | "web-secgrp", 11 | ingress=[ 12 | { 'protocol': 'icmp', 'from_port': 8, 'to_port': 0, 'cidr_blocks': ['0.0.0.0/0'] }, 13 | { 'protocol': 'tcp', 'from_port': 80, 'to_port': 80, 'cidr_blocks': ['0.0.0.0/0'] }, 14 | ], 15 | egress=[ 16 | { 'protocol': 'tcp', 'from_port': 80, 'to_port': 80, 'cidr_blocks': ['0.0.0.0/0'] }, 17 | ] 18 | ) 19 | 20 | default_vpc = aws.ec2.get_vpc(default="true") 21 | default_vpc_subnets = aws.ec2.get_subnet_ids(vpc_id=default_vpc.id) 22 | 23 | lb = aws.lb.LoadBalancer("external-loadbalancer", 24 | internal="false", 25 | security_groups=[group.id], 26 | subnets=default_vpc_subnets.ids, 27 | load_balancer_type="application", 28 | ) 29 | 30 | target_group = aws.lb.TargetGroup("target-group", 31 | port=80, 32 | protocol="HTTP", 33 | target_type="ip", 34 | vpc_id=default_vpc.id 35 | ) 36 | 37 | listener = aws.lb.Listener("listener", 38 | load_balancer_arn=lb.arn, 39 | port=80, 40 | default_actions=[{ 41 | "type": "forward", 42 | "target_group_arn": target_group.arn 43 | }] 44 | ) 45 | 46 | ips = [] 47 | hostnames = [] 48 | for az in aws.get_availability_zones().names: 49 | server = aws.ec2.Instance(f'web-server-{az}', 50 | instance_type="t2.micro", 51 | security_groups=[group.name], 52 | ami=ami.id, 53 | user_data="""#!/bin/bash 54 | echo \"Hello, World -- from {}!\" > index.html 55 | nohup python -m SimpleHTTPServer 80 & 56 | """.format(az), 57 | availability_zone=az, 58 | tags={ 59 | "Name": "web-server", 60 | }, 61 | ) 62 | ips.append(server.public_ip) 63 | hostnames.append(server.public_dns) 64 | 65 | attachment = aws.lb.TargetGroupAttachment(f'web-server-{az}', 66 | target_group_arn=target_group.arn, 67 | target_id=server.private_ip, 68 | port=80, 69 | ) 70 | 71 | export('ips', ips) 72 | export('hostnames', hostnames) 73 | export("url", lb.dns_name) 74 | 75 | 76 | -------------------------------------------------------------------------------- /labs/aws/pulumi-in-practice/python/lab-04/code/step1.py: -------------------------------------------------------------------------------- 1 | from pulumi import export 2 | import pulumi_aws as aws 3 | 4 | cluster = aws.ecs.Cluster("cluster") 5 | -------------------------------------------------------------------------------- /labs/aws/pulumi-in-practice/python/lab-04/code/step2.py: -------------------------------------------------------------------------------- 1 | from pulumi import export 2 | import pulumi_aws as aws 3 | 4 | cluster = aws.ecs.Cluster("cluster") 5 | 6 | default_vpc = aws.ec2.get_vpc(default="true") 7 | default_vpc_subnets = aws.ec2.get_subnet_ids(vpc_id=default_vpc.id) 8 | 9 | group = aws.ec2.SecurityGroup( 10 | "web-secgrp", 11 | vpc_id=default_vpc.id, 12 | description='Enable HTTP access', 13 | ingress=[ 14 | { 'protocol': 'icmp', 'from_port': 8, 'to_port': 0, 'cidr_blocks': ['0.0.0.0/0'] }, 15 | { 'protocol': 'tcp', 'from_port': 80, 'to_port': 80, 'cidr_blocks': ['0.0.0.0/0'] } 16 | ], 17 | egress=[ 18 | { 'protocol': 'tcp', 'from_port': 80, 'to_port': 80, 'cidr_blocks': ['0.0.0.0/0'] } 19 | ]) 20 | 21 | alb = aws.lb.LoadBalancer("app-lb", 22 | internal="false", 23 | security_groups=[group.id], 24 | subnets=default_vpc_subnets.ids, 25 | load_balancer_type="application", 26 | ) 27 | 28 | atg = aws.lb.TargetGroup("app-tg", 29 | port=80, 30 | deregistration_delay=0, 31 | protocol="HTTP", 32 | target_type="ip", 33 | vpc_id=default_vpc.id 34 | ) 35 | 36 | wl = aws.lb.Listener("web", 37 | load_balancer_arn=alb.arn, 38 | port=80, 39 | default_actions=[{ 40 | "type": "forward", 41 | "target_group_arn": atg.arn 42 | }] 43 | ) 44 | -------------------------------------------------------------------------------- /labs/aws/pulumi-in-practice/python/lab-04/code/step3.py: -------------------------------------------------------------------------------- 1 | from pulumi import export, ResourceOptions 2 | import pulumi_aws as aws 3 | import json 4 | 5 | cluster = aws.ecs.Cluster("cluster") 6 | 7 | default_vpc = aws.ec2.get_vpc(default="true") 8 | default_vpc_subnets = aws.ec2.get_subnet_ids(vpc_id=default_vpc.id) 9 | 10 | group = aws.ec2.SecurityGroup( 11 | "web-secgrp", 12 | vpc_id=default_vpc.id, 13 | description='Enable HTTP access', 14 | ingress=[ 15 | { 'protocol': 'tcp', 'from_port': 80, 'to_port': 80, 'cidr_blocks': ['0.0.0.0/0'] } 16 | ], 17 | egress=[ 18 | { 'protocol': "-1", 'from_port': 0, 'to_port': 0, 'cidr_blocks': ['0.0.0.0/0'] } 19 | ]) 20 | 21 | alb = aws.lb.LoadBalancer("app-lb", 22 | security_groups=[group.id], 23 | subnets=default_vpc_subnets.ids, 24 | ) 25 | 26 | atg = aws.lb.TargetGroup("app-tg", 27 | port=80, 28 | protocol="HTTP", 29 | target_type="ip", 30 | vpc_id=default_vpc.id 31 | ) 32 | 33 | wl = aws.lb.Listener("web", 34 | load_balancer_arn=alb.arn, 35 | port=80, 36 | default_actions=[{ 37 | "type": "forward", 38 | "target_group_arn": atg.arn 39 | }] 40 | ) 41 | 42 | role = aws.iam.Role("task-exec-role", 43 | assume_role_policy=json.dumps({ 44 | "Version": "2008-10-17", 45 | "Statement": [{ 46 | "Sid": "", 47 | "Effect": "Allow", 48 | "Principal": { 49 | "Service": "ecs-tasks.amazonaws.com" 50 | }, 51 | "Action": "sts:AssumeRole" 52 | }] 53 | }) 54 | ) 55 | 56 | rpa = aws.iam.RolePolicyAttachment("task-exec-policy", 57 | role=role.name, 58 | policy_arn="arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" 59 | ) 60 | 61 | task_definition = aws.ecs.TaskDefinition("app-task", 62 | family="fargate-task-definition", 63 | cpu="256", 64 | memory="512", 65 | network_mode="awsvpc", 66 | requires_compatibilities=["FARGATE"], 67 | execution_role_arn=role.arn, 68 | container_definitions=json.dumps([{ 69 | "name": "my-app", 70 | "image": "nginx", 71 | "portMappings": [{ 72 | "containerPort": 80, 73 | "hostPort": 80, 74 | "protocol": "tcp" 75 | }] 76 | }]) 77 | ) 78 | 79 | service = aws.ecs.Service("app-svc", 80 | cluster=cluster.arn, 81 | desired_count=1, 82 | launch_type="FARGATE", 83 | task_definition=task_definition.arn, 84 | network_configuration={ 85 | "assign_public_ip": "true", 86 | "subnets": default_vpc_subnets.ids, 87 | "security_groups": [group.id] 88 | }, 89 | load_balancers=[{ 90 | "target_group_arn": atg.arn, 91 | "container_name": "my-app", 92 | "container_port": 80 93 | }], 94 | opts=ResourceOptions(depends_on=[wl]) 95 | ) 96 | 97 | export("url", alb.dns_name) 98 | -------------------------------------------------------------------------------- /labs/aws/pulumi-in-practice/python/lab-04/code/step5.py: -------------------------------------------------------------------------------- 1 | from pulumi import export, ResourceOptions 2 | import pulumi_aws as aws 3 | import json 4 | 5 | cluster = aws.ecs.Cluster("cluster") 6 | 7 | default_vpc = aws.ec2.get_vpc(default="true") 8 | default_vpc_subnets = aws.ec2.get_subnet_ids(vpc_id=default_vpc.id) 9 | 10 | group = aws.ec2.SecurityGroup( 11 | "web-secgrp", 12 | vpc_id=default_vpc.id, 13 | description='Enable HTTP access', 14 | ingress=[ 15 | { 'protocol': 'tcp', 'from_port': 80, 'to_port': 80, 'cidr_blocks': ['0.0.0.0/0'] } 16 | ], 17 | egress=[ 18 | { 'protocol': "-1", 'from_port': 0, 'to_port': 0, 'cidr_blocks': ['0.0.0.0/0'] } 19 | ]) 20 | 21 | alb = aws.lb.LoadBalancer("app-lb", 22 | security_groups=[group.id], 23 | subnets=default_vpc_subnets.ids, 24 | ) 25 | 26 | atg = aws.lb.TargetGroup("app-tg", 27 | port=80, 28 | protocol="HTTP", 29 | target_type="ip", 30 | vpc_id=default_vpc.id 31 | ) 32 | 33 | wl = aws.lb.Listener("web", 34 | load_balancer_arn=alb.arn, 35 | port=80, 36 | default_actions=[{ 37 | "type": "forward", 38 | "target_group_arn": atg.arn 39 | }] 40 | ) 41 | 42 | role = aws.iam.Role("task-exec-role", 43 | assume_role_policy=json.dumps({ 44 | "Version": "2008-10-17", 45 | "Statement": [{ 46 | "Sid": "", 47 | "Effect": "Allow", 48 | "Principal": { 49 | "Service": "ecs-tasks.amazonaws.com" 50 | }, 51 | "Action": "sts:AssumeRole" 52 | }] 53 | }) 54 | ) 55 | 56 | rpa = aws.iam.RolePolicyAttachment("task-exec-policy", 57 | role=role.name, 58 | policy_arn="arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" 59 | ) 60 | 61 | task_definition = aws.ecs.TaskDefinition("app-task", 62 | family="fargate-task-definition", 63 | cpu="256", 64 | memory="512", 65 | network_mode="awsvpc", 66 | requires_compatibilities=["FARGATE"], 67 | execution_role_arn=role.arn, 68 | container_definitions=json.dumps([{ 69 | "name": "my-app", 70 | "image": "nginx", 71 | "portMappings": [{ 72 | "containerPort": 80, 73 | "hostPort": 80, 74 | "protocol": "tcp" 75 | }] 76 | }]) 77 | ) 78 | 79 | service = aws.ecs.Service("app-svc", 80 | cluster=cluster.arn, 81 | desired_count=3, 82 | launch_type="FARGATE", 83 | task_definition=task_definition.arn, 84 | network_configuration={ 85 | "assign_public_ip": "true", 86 | "subnets": default_vpc_subnets.ids, 87 | "security_groups": [group.id] 88 | }, 89 | load_balancers=[{ 90 | "target_group_arn": atg.arn, 91 | "container_name": "my-app", 92 | "container_port": 80 93 | }], 94 | opts=ResourceOptions(depends_on=[wl]) 95 | ) 96 | 97 | export("url", alb.dns_name) 98 | -------------------------------------------------------------------------------- /labs/azure/csharp/00-installing-prerequisites.md: -------------------------------------------------------------------------------- 1 | # Installing Prerequisites 2 | 3 | These hands-on labs will walk you through various cloud infrastructure tasks. The prerequisites listed below are required to successfully complete them. 4 | 5 | Although Pulumi supports many clouds and many languages, you will use Microsoft Azure and C# on .NET Core for these labs. Prerequisites are all available on recent versions of Windows, macOS, and Linux. 6 | 7 | ## Pulumi 8 | 9 | You will use Pulumi to deploy infrastructure changes using code. [Install Pulumi here](https://www.pulumi.com/docs/get-started/install/). After installing the CLI, verify that it is working: 10 | 11 | ```bash 12 | $ pulumi version 13 | v1.10.1 14 | ``` 15 | 16 | The Pulumi CLI will ask you to login to your Pulumi account as needed. If you prefer to signup now, [go to the signup page](http://app.pulumi.com/signup). Multiple identity provider options are available — email, GitHub, GitLab, or Atlassian — and each of them will work equally well for these labs. 17 | 18 | ## Azure Subscription 19 | 20 | You need an active Azure subscription to deploy the components of the application. You may use your developer subscription, or create a free Azure subscription [here](https://azure.microsoft.com/free/). 21 | 22 | Be sure to clean up the resources after you complete the workshop, as described at the last step of each lab. 23 | 24 | ## Azure CLI 25 | 26 | You will use the command-line interface (CLI) tool to log in to an Azure subscription. You can install the CLI tool, as described [here](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest). 27 | 28 | After you complete the installation, open a command prompt and type `az`. You should see the welcome message: 29 | 30 | ``` 31 | $ az 32 | /\ 33 | / \ _____ _ _ ___ _ 34 | / /\ \ |_ / | | | \'__/ _\ 35 | / ____ \ / /| |_| | | | __/ 36 | /_/ \_\/___|\__,_|_| \___| 37 | 38 | 39 | Welcome to the cool new Azure CLI! 40 | ``` 41 | 42 | ## .NET Core SDK 43 | 44 | Pulumi will need the `dotnet` executable in order to build and run your Pulumi .NET application. 45 | 46 | Install .NET Core 3.1 SDK from [here](https://dotnet.microsoft.com/download). 47 | 48 | Ensure that the `dotnet` executable can be found on your path after installation. 49 | 50 | ```bash 51 | $ dotnet --version 52 | 3.1.100 53 | ``` 54 | 55 | ## Docker (Optional) 56 | 57 | If you will be completing the container labs, [install Docker Community Edition](https://docs.docker.com/install). After doing so, verify that the `docker` CLI is operational: 58 | 59 | ```bash 60 | $ docker --version 61 | Docker version 19.03.1, build 74b1e89 62 | ``` 63 | 64 | ## Kubernetes (Optional) 65 | 66 | If you will be completing the Kubernetes labs, [install the kubectl CLI](https://kubernetes.io/docs/tasks/tools/install-kubectl/). It isn't necessary to configure it to speak to a cluster — you will do that during the appropriate labs that require it. 67 | -------------------------------------------------------------------------------- /labs/azure/csharp/01-iac/01-creating-a-new-project.md: -------------------------------------------------------------------------------- 1 | # Creating a New Project 2 | 3 | Infrastructure in Pulumi is organized into projects. Each project is a single program that, when run, declares the desired infrastructure for Pulumi to manage. 4 | 5 | ## Step 1 — Create a Directory 6 | 7 | Each Pulumi project lives in its own directory. Create one now and change into it: 8 | 9 | ```bash 10 | mkdir iac-workshop 11 | cd iac-workshop 12 | ``` 13 | 14 | > Pulumi will use the directory name as your project name by default. To create an independent project, simply name the directory differently. 15 | 16 | ## Step 2 — Initialize Your Project 17 | 18 | A Pulumi project is just a directory with some files in it. It's possible for you to create a new one by hand. The `pulumi new` command, however, automates the process: 19 | 20 | ```bash 21 | pulumi new csharp -y 22 | ``` 23 | 24 | This will print output similar to the following with a bit more information and status as it goes: 25 | 26 | ``` 27 | Created project 'iac-workshop' 28 | Created stack 'dev' 29 | Installing dependencies... 30 | Build succeeded. 31 | Finished installing dependencies 32 | 33 | Your new project is ready to go! 34 | ``` 35 | 36 | This command has created all the files we need, initialized a new stack named `dev` (an instance of our project), and installed the needed package dependencies from NuGet. 37 | 38 | ## Step 3 — Inspect Your New Project 39 | 40 | Our project is comprised of multiple files: 41 | 42 | * **`Program.cs`**: your program's main entrypoint file 43 | * **`MyStack.cs`**: your stack definition file 44 | * **`iac-workshop.csproj`**: your C# project file 45 | * **`Pulumi.yaml`**: your project's metadata, containing its name and language 46 | * **`bin/`** and **`obj/`**: directories containing your project's build artifacts 47 | 48 | Open `MyStack.cs` to see the contents of the empty program of your infrastructure stack: 49 | 50 | ```csharp 51 | using Pulumi; 52 | 53 | class MyStack : Stack 54 | { 55 | public MyStack() 56 | { 57 | // Add your resources here 58 | } 59 | } 60 | ``` 61 | 62 | Feel free to explore the other files, although we won't be editing any of them by hand. 63 | 64 | # Next Steps 65 | 66 | * [Configuring Azure](./02-configuring-azure.md) 67 | -------------------------------------------------------------------------------- /labs/azure/csharp/01-iac/02-configuring-azure.md: -------------------------------------------------------------------------------- 1 | # Configuring Azure 2 | 3 | Now that you have a basic project, let's configure Azure support for it. 4 | 5 | ## Step 1 — Install the Azure Package 6 | 7 | Run the following command to install the Azure package: 8 | 9 | ```bash 10 | dotnet add package Pulumi.Azure 11 | ``` 12 | 13 | The package will be added to `csproj` and the binaries will be restored. 14 | 15 | ## Step 2 — Use the Azure Package 16 | 17 | Now that the Azure package is installed, add the following line to `MyStack.cs` to import it: 18 | 19 | ```cs 20 | ... 21 | using Azure = Pulumi.Azure; 22 | ... 23 | ``` 24 | 25 | > :white_check_mark: After this change, your `MyStack.cs` should [look like this](./code/02-configuring-azure/step2.cs). 26 | 27 | ## Step 3 — Configure an Azure Region 28 | 29 | Configure the Azure region you would like to deploy to: 30 | 31 | ```bash 32 | pulumi config set azure:location westus2 33 | ``` 34 | 35 | Feel free to choose any Azure region that supports the services used in these labs ([see this infographic](https://azure.microsoft.com/en-us/global-infrastructure/regions/) for a list of available regions). 36 | 37 | The command persists the value to the local `Pulumi.dev.yaml` file. You can view or edit this file at any time to effect the configuration of the current stack. 38 | 39 | ## Step 4 — Login to Azure 40 | 41 | Simply login to the Azure CLI and Pulumi will automatically use your credentials: 42 | 43 | ``` 44 | az login 45 | ... 46 | You have logged in. Now let us find all the subscriptions to which you have access... 47 | ... 48 | ``` 49 | 50 | The Azure CLI, and thus Pulumi, will use the Default Subscription by default, however it is possible to override the subscription, by simply setting your subscription ID to the id output from `az account list`’s output: 51 | 52 | ``` 53 | $ az account list 54 | ``` 55 | 56 | Pick out the `` from the list and run: 57 | 58 | ``` 59 | $ az account set --subscription= 60 | ``` 61 | 62 | ## Next Steps 63 | 64 | * [Provisioning a Resource Group](./03-provisioning-infrastructure.md) 65 | -------------------------------------------------------------------------------- /labs/azure/csharp/01-iac/05-making-your-stack-configurable.md: -------------------------------------------------------------------------------- 1 | # Making Your Stack Configurable 2 | 3 | Right now, the container's name is hard-coded. Next, you'll make the name configurable. 4 | 5 | ## Step 1 — Adding a Config Variable 6 | 7 | Instead of hard-coding the `"files"` container, we will use configuration to make it easy to change the name without editing the program. 8 | 9 | Create a file `Config.cs` and add this to it: 10 | 11 | ```csharp 12 | using Pulumi; 13 | 14 | static class Settings 15 | { 16 | static Settings() 17 | { 18 | var config = new Config(); 19 | ContainerName = config.Require("container"); 20 | } 21 | 22 | public static string ContainerName { get; } 23 | } 24 | ``` 25 | 26 | ## Step 2 — Populating the Container Based on Config 27 | 28 | Replace the hard-coded `"Name"` property value with the one from configuration: 29 | 30 | ```csharp 31 | var container = new Azure.Storage.Container("mycontainer", new Azure.Storage.ContainerArgs 32 | { 33 | Name = Settings.ContainerName, 34 | StorageAccountName = storageAccount.Name 35 | }); 36 | ``` 37 | 38 | > :white_check_mark: After these changes, your `MyStack.cs` should [look like this](./code/05-making-your-stack-configurable/step2.cs). 39 | 40 | ## Step 3 — Deploying the Changes 41 | 42 | Now, deploy your changes. To do so, first configure your stack. If you don't, you'll get an error: 43 | 44 | ```bash 45 | pulumi up 46 | ``` 47 | 48 | This results in an error like the following: 49 | 50 | ``` 51 | ... 52 | ConfigMissingException: Missing Required configuration variable 'iac-workshop:container' 53 | please set a value using the command `pulumi config set iac-workshop:container ` 54 | ... 55 | ``` 56 | 57 | Configure the `iac-workshop:container` variable very much like the `azure:location` variable: 58 | 59 | ```bash 60 | pulumi config set iac-workshop:container html 61 | ``` 62 | 63 | To make things interesting, I set the name to `html` which is different from the previously hard-coded value `files`. 64 | 65 | Run `pulumi up` again. This detects that the container has changed and will perform a simple update: 66 | 67 | ``` 68 | Updating (dev): 69 | 70 | Type Name Status Info 71 | pulumi:pulumi:Stack iac-workshop-dev 72 | +- └─ azure:storage:Container mycontainer replaced [diff: ~name] 73 | 74 | Outputs: 75 | AccountName: "mystorage872202e1" 76 | 77 | Resources: 78 | +-1 replaced 79 | 3 unchanged 80 | 81 | Duration: 10s 82 | 83 | Permalink: https://app.pulumi.com/myuser/iac-workshop/dev/updates/5 84 | ``` 85 | 86 | And you will see the contents added above. 87 | 88 | ## Next Steps 89 | 90 | * [Creating a Second Stack](./06-creating-a-second-stack.md) 91 | -------------------------------------------------------------------------------- /labs/azure/csharp/01-iac/06-creating-a-second-stack.md: -------------------------------------------------------------------------------- 1 | # Creating a Second Stack 2 | 3 | It is easy to create multiple instances of the same project. This is called a stack. This is handy for multiple development or test environments, staging versus production, and scaling a given infrastructure across many regions. 4 | 5 | ## Step 1 — Create and Configure a New Stack 6 | 7 | Create a new stack: 8 | 9 | ```bash 10 | pulumi stack init prod 11 | ``` 12 | 13 | Next, configure its two required variables: 14 | 15 | ```bash 16 | pulumi config set azure:location westeurope 17 | pulumi config set iac-workshop:container htmlprod 18 | ``` 19 | 20 | If you are ever curious to see the list of stacks for your current project, run this command: 21 | 22 | ```bash 23 | pulumi stack ls 24 | ``` 25 | 26 | It will print all stacks for this project that are available to you: 27 | 28 | ``` 29 | NAME LAST UPDATE RESOURCE COUNT URL 30 | dev 30 minutes ago 4 https://app.pulumi.com/myuser/iac-workshop/dev 31 | prod* 3 minutes ago 0 https://app.pulumi.com/myuser/iac-workshop/prod 32 | ``` 33 | 34 | ## Step 2 — Deploy the New Stack 35 | 36 | Now deploy all of the changes: 37 | 38 | ```bash 39 | pulumi up 40 | ``` 41 | 42 | This will create an entirely new set of resources from scratch, unrelated to the existing `dev` stack's resources. 43 | 44 | ``` 45 | Updating (prod): 46 | 47 | Type Name Status 48 | + pulumi:pulumi:Stack iac-workshop-prod created 49 | + ├─ azure:core:ResourceGroup my-group created 50 | + ├─ azure:storage:Account mystorage created 51 | + └─ azure:storage:Container mycontainer created 52 | 53 | Outputs: 54 | AccountName: "mystorage4a3f2830" 55 | 56 | Resources: 57 | + 4 created 58 | 59 | Duration: 30s 60 | 61 | Permalink: https://app.pulumi.com/myuser/iac-workshop/prod/updates/1 62 | ``` 63 | 64 | A new set of resources has been created for the `prod` stack. 65 | 66 | ## Next Steps 67 | 68 | * [Destroying Your Infrastructure](./07-destroying-your-infrastructure.md) 69 | -------------------------------------------------------------------------------- /labs/azure/csharp/01-iac/code/01-creating-a-new-project/step3.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | 3 | class MyStack : Stack 4 | { 5 | public MyStack() 6 | { 7 | // Add your resources here 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /labs/azure/csharp/01-iac/code/02-configuring-azure/step2.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | using Azure = Pulumi.Azure; 3 | 4 | class MyStack : Stack 5 | { 6 | public MyStack() 7 | { 8 | // Add your resources here 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /labs/azure/csharp/01-iac/code/03-provisioning-infrastructure/step1.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | using Azure = Pulumi.Azure; 3 | 4 | class MyStack : Stack 5 | { 6 | public MyStack() 7 | { 8 | var resourceGroup = new Azure.Core.ResourceGroup("my-group"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /labs/azure/csharp/01-iac/code/04-updating-your-infrastructure/step1.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | using Azure = Pulumi.Azure; 3 | 4 | class MyStack : Stack 5 | { 6 | public MyStack() 7 | { 8 | var resourceGroup = new Azure.Core.ResourceGroup("my-group"); 9 | 10 | var storageAccount = new Azure.Storage.Account("mystorage", new Azure.Storage.AccountArgs 11 | { 12 | ResourceGroupName = resourceGroup.Name, 13 | AccountReplicationType = "LRS", 14 | AccountTier = "Standard" 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /labs/azure/csharp/01-iac/code/04-updating-your-infrastructure/step2.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | using Pulumi.Serialization; 3 | using Azure = Pulumi.Azure; 4 | 5 | class MyStack : Stack 6 | { 7 | public MyStack() 8 | { 9 | var resourceGroup = new Azure.Core.ResourceGroup("my-group"); 10 | 11 | var storageAccount = new Azure.Storage.Account("mystorage", new Azure.Storage.AccountArgs 12 | { 13 | ResourceGroupName = resourceGroup.Name, 14 | AccountReplicationType = "LRS", 15 | AccountTier = "Standard" 16 | }); 17 | 18 | this.AccountName = storageAccount.Name; 19 | } 20 | 21 | [Output] 22 | public Output AccountName { get; set; } 23 | } 24 | -------------------------------------------------------------------------------- /labs/azure/csharp/01-iac/code/04-updating-your-infrastructure/step4.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | using Pulumi.Serialization; 3 | using Azure = Pulumi.Azure; 4 | 5 | class MyStack : Stack 6 | { 7 | public MyStack() 8 | { 9 | var resourceGroup = new Azure.Core.ResourceGroup("my-group"); 10 | 11 | var storageAccount = new Azure.Storage.Account("mystorage", new Azure.Storage.AccountArgs 12 | { 13 | ResourceGroupName = resourceGroup.Name, 14 | AccountReplicationType = "LRS", 15 | AccountTier = "Standard" 16 | }); 17 | 18 | var container = new Azure.Storage.Container("mycontainer", new Azure.Storage.ContainerArgs 19 | { 20 | Name = "files", 21 | StorageAccountName = storageAccount.Name 22 | }); 23 | 24 | this.AccountName = storageAccount.Name; 25 | } 26 | 27 | [Output] 28 | public Output AccountName { get; set; } 29 | } 30 | -------------------------------------------------------------------------------- /labs/azure/csharp/01-iac/code/05-making-your-stack-configurable/step2.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | using Pulumi.Serialization; 3 | using Azure = Pulumi.Azure; 4 | 5 | class MyStack : Stack 6 | { 7 | public MyStack() 8 | { 9 | var resourceGroup = new Azure.Core.ResourceGroup("my-group"); 10 | 11 | var storageAccount = new Azure.Storage.Account("mystorage", new Azure.Storage.AccountArgs 12 | { 13 | ResourceGroupName = resourceGroup.Name, 14 | AccountReplicationType = "LRS", 15 | AccountTier = "Standard" 16 | }); 17 | 18 | var container = new Azure.Storage.Container("files", new Azure.Storage.ContainerArgs 19 | { 20 | Name = Settings.ContainerName, 21 | StorageAccountName = storageAccount.Name 22 | }); 23 | 24 | this.AccountName = storageAccount.Name; 25 | } 26 | 27 | [Output] 28 | public Output AccountName { get; set; } 29 | } 30 | -------------------------------------------------------------------------------- /labs/azure/csharp/02-serverless/code/step1.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | 3 | using Azure = Pulumi.Azure; 4 | 5 | class MyStack : Stack 6 | { 7 | public MyStack() 8 | { 9 | var resourceGroup = new Azure.Core.ResourceGroup("my-group"); 10 | 11 | var storageAccount = new Azure.Storage.Account("storage", new Azure.Storage.AccountArgs 12 | { 13 | ResourceGroupName = resourceGroup.Name, 14 | AccountReplicationType = "LRS", 15 | AccountTier = "Standard" 16 | }); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /labs/azure/csharp/02-serverless/code/step2.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | 3 | using Azure = Pulumi.Azure; 4 | 5 | class MyStack : Stack 6 | { 7 | public MyStack() 8 | { 9 | var resourceGroup = new Azure.Core.ResourceGroup("my-group"); 10 | 11 | var storageAccount = new Azure.Storage.Account("storage", new Azure.Storage.AccountArgs 12 | { 13 | ResourceGroupName = resourceGroup.Name, 14 | AccountReplicationType = "LRS", 15 | AccountTier = "Standard" 16 | }); 17 | 18 | var plan = new Azure.AppService.Plan("asp", new Azure.AppService.PlanArgs 19 | { 20 | ResourceGroupName = resourceGroup.Name, 21 | Kind = "FunctionApp", 22 | Sku = new Azure.AppService.Inputs.PlanSkuArgs 23 | { 24 | Tier = "Dynamic", 25 | Size = "Y1" 26 | } 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /labs/azure/csharp/02-serverless/code/step3.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | 3 | using Azure = Pulumi.Azure; 4 | 5 | class MyStack : Stack 6 | { 7 | public MyStack() 8 | { 9 | var resourceGroup = new Azure.Core.ResourceGroup("my-group"); 10 | 11 | var storageAccount = new Azure.Storage.Account("storage", new Azure.Storage.AccountArgs 12 | { 13 | ResourceGroupName = resourceGroup.Name, 14 | AccountReplicationType = "LRS", 15 | AccountTier = "Standard" 16 | }); 17 | 18 | var plan = new Azure.AppService.Plan("asp", new Azure.AppService.PlanArgs 19 | { 20 | ResourceGroupName = resourceGroup.Name, 21 | Kind = "FunctionApp", 22 | Sku = new Azure.AppService.Inputs.PlanSkuArgs 23 | { 24 | Tier = "Dynamic", 25 | Size = "Y1" 26 | } 27 | }); 28 | 29 | var app = new Azure.AppService.FunctionApp("fa", new Azure.AppService.FunctionAppArgs 30 | { 31 | ResourceGroupName = resourceGroup.Name, 32 | AppServicePlanId = plan.Id, 33 | StorageConnectionString = storageAccount.PrimaryConnectionString, 34 | Version = "~2", 35 | AppSettings = 36 | { 37 | { "FUNCTIONS_WORKER_RUNTIME", "node" }, 38 | { "WEBSITE_NODE_DEFAULT_VERSION", "10.14.1" }, 39 | { "WEBSITE_RUN_FROM_PACKAGE", "https://mikhailworkshop.blob.core.windows.net/zips/app.zip" } 40 | } 41 | }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /labs/azure/csharp/02-serverless/code/step4.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | using Pulumi.Serialization; 3 | 4 | using Azure = Pulumi.Azure; 5 | 6 | class MyStack : Stack 7 | { 8 | public MyStack() 9 | { 10 | var resourceGroup = new Azure.Core.ResourceGroup("my-group"); 11 | 12 | var storageAccount = new Azure.Storage.Account("storage", new Azure.Storage.AccountArgs 13 | { 14 | ResourceGroupName = resourceGroup.Name, 15 | AccountReplicationType = "LRS", 16 | AccountTier = "Standard" 17 | }); 18 | 19 | var plan = new Azure.AppService.Plan("asp", new Azure.AppService.PlanArgs 20 | { 21 | ResourceGroupName = resourceGroup.Name, 22 | Kind = "FunctionApp", 23 | Sku = new Azure.AppService.Inputs.PlanSkuArgs 24 | { 25 | Tier = "Dynamic", 26 | Size = "Y1" 27 | } 28 | }); 29 | 30 | var app = new Azure.AppService.FunctionApp("fa", new Azure.AppService.FunctionAppArgs 31 | { 32 | ResourceGroupName = resourceGroup.Name, 33 | AppServicePlanId = plan.Id, 34 | StorageConnectionString = storageAccount.PrimaryConnectionString, 35 | Version = "~2", 36 | AppSettings = 37 | { 38 | { "FUNCTIONS_WORKER_RUNTIME", "node" }, 39 | { "WEBSITE_NODE_DEFAULT_VERSION", "10.14.1" }, 40 | { "WEBSITE_RUN_FROM_PACKAGE", "https://mikhailworkshop.blob.core.windows.net/zips/app.zip" } 41 | } 42 | }); 43 | 44 | this.Endpoint = Output.Format($"https://{app.DefaultHostname}/api/hello"); 45 | } 46 | 47 | [Output] 48 | public Output Endpoint { get; set; } 49 | } 50 | -------------------------------------------------------------------------------- /labs/azure/csharp/02-serverless/code/step6.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | using Pulumi.Serialization; 3 | 4 | using Azure = Pulumi.Azure; 5 | 6 | class MyStack : Stack 7 | { 8 | public MyStack() 9 | { 10 | var resourceGroup = new Azure.Core.ResourceGroup("my-group"); 11 | 12 | var storageAccount = new Azure.Storage.Account("storage", new Azure.Storage.AccountArgs 13 | { 14 | ResourceGroupName = resourceGroup.Name, 15 | AccountReplicationType = "LRS", 16 | AccountTier = "Standard" 17 | }); 18 | 19 | var plan = new Azure.AppService.Plan("asp", new Azure.AppService.PlanArgs 20 | { 21 | ResourceGroupName = resourceGroup.Name, 22 | Kind = "FunctionApp", 23 | Sku = new Azure.AppService.Inputs.PlanSkuArgs 24 | { 25 | Tier = "Dynamic", 26 | Size = "Y1" 27 | } 28 | }); 29 | 30 | var container = new Azure.Storage.Container("zips", new Azure.Storage.ContainerArgs 31 | { 32 | StorageAccountName = storageAccount.Name, 33 | ContainerAccessType = "private", 34 | }); 35 | 36 | var blob = new Azure.Storage.Blob("zip", new Azure.Storage.BlobArgs 37 | { 38 | StorageAccountName = storageAccount.Name, 39 | StorageContainerName = container.Name, 40 | Type = "Block", 41 | Source = new FileArchive("./functions"), 42 | }); 43 | 44 | var codeBlobUrl = Azure.Storage.SharedAccessSignature.SignedBlobReadUrl(blob, storageAccount); 45 | 46 | var app = new Azure.AppService.FunctionApp("fa", new Azure.AppService.FunctionAppArgs 47 | { 48 | ResourceGroupName = resourceGroup.Name, 49 | AppServicePlanId = plan.Id, 50 | StorageConnectionString = storageAccount.PrimaryConnectionString, 51 | Version = "~2", 52 | AppSettings = 53 | { 54 | { "FUNCTIONS_WORKER_RUNTIME", "node" }, 55 | { "WEBSITE_NODE_DEFAULT_VERSION", "10.14.1" }, 56 | { "WEBSITE_RUN_FROM_PACKAGE", codeBlobUrl } 57 | } 58 | }); 59 | 60 | this.Endpoint = Output.Format($"https://{app.DefaultHostname}/api/hello"); 61 | } 62 | 63 | [Output] 64 | public Output Endpoint { get; set; } 65 | } 66 | -------------------------------------------------------------------------------- /labs/azure/csharp/03-aci/code/step2.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | using Pulumi.Serialization; 3 | 4 | using Azure = Pulumi.Azure; 5 | using Pulumi.Azure.ContainerService; 6 | using Pulumi.Azure.ContainerService.Inputs; 7 | 8 | class MyStack : Stack 9 | { 10 | public MyStack() 11 | { 12 | var resourceGroup = new Azure.Core.ResourceGroup("my-group"); 13 | 14 | var group = new Group("aci", new GroupArgs 15 | { 16 | ResourceGroupName = resourceGroup.Name, 17 | OsType = "Linux", 18 | Containers = 19 | { 20 | new GroupContainersArgs 21 | { 22 | Cpu = 0.5, 23 | Image = "mcr.microsoft.com/azuredocs/aci-helloworld", 24 | Memory = 1.5, 25 | Name = "hello-world", 26 | Ports = 27 | { 28 | new GroupContainersPortsArgs 29 | { 30 | Port = 80, 31 | Protocol = "TCP" 32 | } 33 | } 34 | } 35 | }, 36 | IpAddressType = "public", 37 | DnsNameLabel = "my-unique-string" 38 | }); 39 | 40 | this.Endpoint = Output.Format($"http://{group.Fqdn}"); 41 | } 42 | 43 | [Output] 44 | public Output Endpoint { get; set; } 45 | } 46 | -------------------------------------------------------------------------------- /labs/azure/csharp/03-aci/code/step4.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | using Pulumi.Serialization; 3 | 4 | using Azure = Pulumi.Azure; 5 | using Pulumi.Azure.ContainerService; 6 | using Pulumi.Azure.ContainerService.Inputs; 7 | 8 | class MyStack : Stack 9 | { 10 | public MyStack() 11 | { 12 | var resourceGroup = new Azure.Core.ResourceGroup("my-group"); 13 | 14 | var registry = new Registry("registry", new RegistryArgs 15 | { 16 | ResourceGroupName = resourceGroup.Name, 17 | AdminEnabled = true, 18 | Sku = "Standard" 19 | }); 20 | 21 | var dockerImage = new Pulumi.Docker.Image("node-app", new Pulumi.Docker.ImageArgs 22 | { 23 | ImageName = Output.Format($"{registry.LoginServer}/myapp"), 24 | Build = "./app", 25 | Registry = new Pulumi.Docker.ImageRegistry 26 | { 27 | Server = registry.LoginServer, 28 | Username = registry.AdminUsername, 29 | Password = registry.AdminPassword 30 | } 31 | }); 32 | 33 | var group = new Group("aci", new GroupArgs 34 | { 35 | ResourceGroupName = resourceGroup.Name, 36 | OsType = "Linux", 37 | Containers = 38 | { 39 | new GroupContainersArgs 40 | { 41 | Cpu = 0.5, 42 | Image = dockerImage.ImageName, 43 | Memory = 1.5, 44 | Name = "hello-world", 45 | Ports = 46 | { 47 | new GroupContainersPortsArgs 48 | { 49 | Port = 80, 50 | Protocol = "TCP" 51 | } 52 | } 53 | } 54 | }, 55 | ImageRegistryCredentials = 56 | { 57 | new GroupImageRegistryCredentialsArgs 58 | { 59 | Server = registry.LoginServer, 60 | Username = registry.AdminUsername, 61 | Password = registry.AdminPassword 62 | } 63 | }, 64 | IpAddressType = "public", 65 | DnsNameLabel = "my-unique-name" 66 | }, new CustomResourceOptions { DeleteBeforeReplace = true }); 67 | 68 | this.Endpoint = Output.Format($"http://{group.Fqdn}"); 69 | } 70 | 71 | [Output] 72 | public Output Endpoint { get; set; } 73 | } 74 | -------------------------------------------------------------------------------- /labs/azure/csharp/04-vms/code/step1.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | using Pulumi.Serialization; 3 | 4 | using Azure = Pulumi.Azure; 5 | using Pulumi.Azure.Compute; 6 | using Pulumi.Azure.Compute.Inputs; 7 | using Pulumi.Azure.Network; 8 | using Pulumi.Azure.Network.Inputs; 9 | 10 | class MyStack : Stack 11 | { 12 | public MyStack() 13 | { 14 | var resourceGroup = new Azure.Core.ResourceGroup("my-group"); 15 | 16 | var network = new VirtualNetwork("server-network", new VirtualNetworkArgs 17 | { 18 | ResourceGroupName = resourceGroup.Name, 19 | AddressSpaces = { "10.0.0.0/16" }, 20 | Subnets = 21 | { 22 | new VirtualNetworkSubnetsArgs 23 | { 24 | Name = "default", 25 | AddressPrefix = "10.0.1.0/24" 26 | } 27 | }, 28 | }); 29 | 30 | var publicIp = new PublicIp("server-ip", new PublicIpArgs 31 | { 32 | ResourceGroupName = resourceGroup.Name, 33 | AllocationMethod = "Dynamic", 34 | }); 35 | 36 | var networkInterface = new NetworkInterface("server-nic", new NetworkInterfaceArgs 37 | { 38 | ResourceGroupName = resourceGroup.Name, 39 | IpConfigurations = 40 | { 41 | new NetworkInterfaceIpConfigurationsArgs 42 | { 43 | Name = "webserveripcfg", 44 | SubnetId = network.Subnets.Apply(subnets => subnets[0].Id), 45 | PrivateIpAddressAllocation = "Dynamic", 46 | PublicIpAddressId = publicIp.Id, 47 | }, 48 | } 49 | }); 50 | 51 | var vm = new VirtualMachine("server-vm", new VirtualMachineArgs 52 | { 53 | ResourceGroupName = resourceGroup.Name, 54 | NetworkInterfaceIds = { networkInterface.Id }, 55 | VmSize = "Standard_A0", 56 | DeleteDataDisksOnTermination = true, 57 | DeleteOsDiskOnTermination = true, 58 | OsProfile = new VirtualMachineOsProfileArgs 59 | { 60 | ComputerName = "hostname", 61 | AdminUsername = "testadmin", 62 | AdminPassword = "NotARealPassword123!", 63 | CustomData = 64 | @"#!/bin/bash 65 | echo ""Hello, World!"" > index.html 66 | nohup python -m SimpleHTTPServer 80 &", 67 | }, 68 | OsProfileLinuxConfig = new VirtualMachineOsProfileLinuxConfigArgs 69 | { 70 | DisablePasswordAuthentication = false, 71 | }, 72 | StorageOsDisk = new VirtualMachineStorageOsDiskArgs 73 | { 74 | CreateOption = "FromImage", 75 | Name = "myosdisk1", 76 | }, 77 | StorageImageReference = new VirtualMachineStorageImageReferenceArgs 78 | { 79 | Publisher = "canonical", 80 | Offer = "UbuntuServer", 81 | Sku = "16.04-LTS", 82 | Version = "latest", 83 | } 84 | }); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /labs/azure/csharp/05-kubernetes/code/step2.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | 3 | using K8s = Pulumi.Kubernetes; 4 | 5 | class MyStack : Stack 6 | { 7 | public MyStack() 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /labs/azure/csharp/05-kubernetes/code/step3.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | 3 | using K8s = Pulumi.Kubernetes; 4 | 5 | class MyStack : Stack 6 | { 7 | public MyStack() 8 | { 9 | var ns = new K8s.Core.V1.Namespace("app-ns", new K8s.Types.Inputs.Core.V1.NamespaceArgs 10 | { 11 | Metadata = new K8s.Types.Inputs.Meta.V1.ObjectMetaArgs { Name = "my-name" } 12 | }); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /labs/azure/csharp/05-kubernetes/code/step4.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | 3 | using K8s = Pulumi.Kubernetes; 4 | 5 | class MyStack : Stack 6 | { 7 | public MyStack() 8 | { 9 | var ns = new K8s.Core.V1.Namespace("app-ns", new K8s.Types.Inputs.Core.V1.NamespaceArgs 10 | { 11 | Metadata = new K8s.Types.Inputs.Meta.V1.ObjectMetaArgs { Name = "my-name" } 12 | }); 13 | 14 | var appLabels = new InputMap { { "app", "iac-workshop" } }; 15 | var deployment = new K8s.Apps.V1.Deployment("app-dep", new K8s.Types.Inputs.Apps.V1.DeploymentArgs 16 | { 17 | Metadata = new K8s.Types.Inputs.Meta.V1.ObjectMetaArgs { Namespace = ns.Metadata.Apply(m => m.Name) }, 18 | Spec = new K8s.Types.Inputs.Apps.V1.DeploymentSpecArgs 19 | { 20 | Selector = new K8s.Types.Inputs.Meta.V1.LabelSelectorArgs { MatchLabels = appLabels }, 21 | Replicas = 1, 22 | Template = new K8s.Types.Inputs.Core.V1.PodTemplateSpecArgs 23 | { 24 | Metadata = new K8s.Types.Inputs.Meta.V1.ObjectMetaArgs { Labels = appLabels }, 25 | Spec = new K8s.Types.Inputs.Core.V1.PodSpecArgs 26 | { 27 | Containers = 28 | { 29 | new K8s.Types.Inputs.Core.V1.ContainerArgs 30 | { 31 | Name = "iac-workshop", 32 | Image = "gcr.io/google-samples/kubernetes-bootcamp:v1" 33 | } 34 | } 35 | } 36 | } 37 | } 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /labs/azure/csharp/05-kubernetes/code/step5.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | using Pulumi.Serialization; 3 | 4 | using K8s = Pulumi.Kubernetes; 5 | 6 | class MyStack : Stack 7 | { 8 | public MyStack() 9 | { 10 | var ns = new K8s.Core.V1.Namespace("app-ns", new K8s.Types.Inputs.Core.V1.NamespaceArgs 11 | { 12 | Metadata = new K8s.Types.Inputs.Meta.V1.ObjectMetaArgs { Name = "my-name" } 13 | }); 14 | 15 | var appLabels = new InputMap { { "app", "iac-workshop" } }; 16 | var deployment = new K8s.Apps.V1.Deployment("app-dep", new K8s.Types.Inputs.Apps.V1.DeploymentArgs 17 | { 18 | Metadata = new K8s.Types.Inputs.Meta.V1.ObjectMetaArgs { Namespace = ns.Metadata.Apply(m => m.Name) }, 19 | Spec = new K8s.Types.Inputs.Apps.V1.DeploymentSpecArgs 20 | { 21 | Selector = new K8s.Types.Inputs.Meta.V1.LabelSelectorArgs { MatchLabels = appLabels }, 22 | Replicas = 1, 23 | Template = new K8s.Types.Inputs.Core.V1.PodTemplateSpecArgs 24 | { 25 | Metadata = new K8s.Types.Inputs.Meta.V1.ObjectMetaArgs { Labels = appLabels }, 26 | Spec = new K8s.Types.Inputs.Core.V1.PodSpecArgs 27 | { 28 | Containers = 29 | { 30 | new K8s.Types.Inputs.Core.V1.ContainerArgs 31 | { 32 | Name = "iac-workshop", 33 | Image = "gcr.io/google-samples/kubernetes-bootcamp:v1" 34 | } 35 | } 36 | } 37 | } 38 | } 39 | }); 40 | 41 | var service = new K8s.Core.V1.Service("app-svc", new K8s.Types.Inputs.Core.V1.ServiceArgs 42 | { 43 | Metadata = new K8s.Types.Inputs.Meta.V1.ObjectMetaArgs { Namespace = ns.Metadata.Apply(m => m.Name) }, 44 | Spec = new K8s.Types.Inputs.Core.V1.ServiceSpecArgs 45 | { 46 | Selector = appLabels, 47 | Ports = { new K8s.Types.Inputs.Core.V1.ServicePortArgs { Port = 80, TargetPort = 8080 }}, 48 | Type = "LoadBalancer" 49 | } 50 | }); 51 | 52 | var address = service.Status 53 | .Apply(s => s.LoadBalancer) 54 | .Apply(lb => lb.Ingress) 55 | .GetAt(0) 56 | .Apply(i => i.Ip); 57 | 58 | this.Url = Output.Format($"http://{address}"); 59 | } 60 | 61 | [Output] 62 | public Output Url { get; set; } 63 | } 64 | -------------------------------------------------------------------------------- /labs/azure/csharp/05-kubernetes/code/step7.cs: -------------------------------------------------------------------------------- 1 | using Pulumi; 2 | using Pulumi.Serialization; 3 | 4 | using K8s = Pulumi.Kubernetes; 5 | 6 | class MyStack : Stack 7 | { 8 | public MyStack() 9 | { 10 | var ns = new K8s.Core.V1.Namespace("app-ns", new K8s.Types.Inputs.Core.V1.NamespaceArgs 11 | { 12 | Metadata = new K8s.Types.Inputs.Meta.V1.ObjectMetaArgs { Name = "my-name" } 13 | }); 14 | 15 | var appLabels = new InputMap { { "app", "iac-workshop" } }; 16 | var deployment = new K8s.Apps.V1.Deployment("app-dep", new K8s.Types.Inputs.Apps.V1.DeploymentArgs 17 | { 18 | Metadata = new K8s.Types.Inputs.Meta.V1.ObjectMetaArgs { Namespace = ns.Metadata.Apply(m => m.Name) }, 19 | Spec = new K8s.Types.Inputs.Apps.V1.DeploymentSpecArgs 20 | { 21 | Selector = new K8s.Types.Inputs.Meta.V1.LabelSelectorArgs { MatchLabels = appLabels }, 22 | Replicas = 3, 23 | Template = new K8s.Types.Inputs.Core.V1.PodTemplateSpecArgs 24 | { 25 | Metadata = new K8s.Types.Inputs.Meta.V1.ObjectMetaArgs { Labels = appLabels }, 26 | Spec = new K8s.Types.Inputs.Core.V1.PodSpecArgs 27 | { 28 | Containers = 29 | { 30 | new K8s.Types.Inputs.Core.V1.ContainerArgs 31 | { 32 | Name = "iac-workshop", 33 | Image = "jocatalin/kubernetes-bootcamp:v2" 34 | } 35 | } 36 | } 37 | } 38 | } 39 | }); 40 | 41 | var service = new K8s.Core.V1.Service("app-svc", new K8s.Types.Inputs.Core.V1.ServiceArgs 42 | { 43 | Metadata = new K8s.Types.Inputs.Meta.V1.ObjectMetaArgs { Namespace = ns.Metadata.Apply(m => m.Name) }, 44 | Spec = new K8s.Types.Inputs.Core.V1.ServiceSpecArgs 45 | { 46 | Selector = appLabels, 47 | Ports = { new K8s.Types.Inputs.Core.V1.ServicePortArgs { Port = 80, TargetPort = 8080 }}, 48 | Type = "LoadBalancer" 49 | } 50 | }); 51 | 52 | var address = service.Status 53 | .Apply(s => s.LoadBalancer) 54 | .Apply(lb => lb.Ingress) 55 | .GetAt(0) 56 | .Apply(i => i.Ip); 57 | 58 | this.Url = Output.Format($"http://{address}"); 59 | } 60 | 61 | [Output] 62 | public Output Url { get; set; } 63 | } 64 | -------------------------------------------------------------------------------- /labs/azure/csharp/Infrastructure as Code Workshop Azure.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulumi/infrastructure-as-code-workshop/a4d142c9237d8e9a4c1868f8b15e776a244c0e2f/labs/azure/csharp/Infrastructure as Code Workshop Azure.pdf -------------------------------------------------------------------------------- /labs/azure/csharp/README.md: -------------------------------------------------------------------------------- 1 | ### Prerequisites 2 | 3 | Please follow [this guide](./00-installing-prerequisites.md) to install all the binaries needed to complete the labs. 4 | 5 | ### Lab 1 — Modern Infrastructure as Code 6 | 7 | The first lab takes you on a tour of infrastructure as code concepts: 8 | 9 | 1. [Creating a New Project](./01-iac/01-creating-a-new-project.md) 10 | 2. [Configuring Azure](./01-iac/02-configuring-azure.md) 11 | 3. [Provisioning Infrastructure](./01-iac/03-provisioning-infrastructure.md) 12 | 4. [Updating your Infrastructure](./01-iac/04-updating-your-infrastructure.md) 13 | 5. [Making Your Stack Configurable](./01-iac/05-making-your-stack-configurable.md) 14 | 6. [Creating a Second Stack](./01-iac/06-creating-a-second-stack.md) 15 | 7. [Destroying Your Infrastructure](./01-iac/07-destroying-your-infrastructure.md) 16 | 17 | [Get Started with Lab 1](./01-iac/01-creating-a-new-project.md) 18 | 19 | ### Lab 2 - Deploy Serverless Applications with Azure Functions 20 | 21 | In this lab, you will create a serverless web application that uses Azure Functions. 22 | 23 | [Get Started with Lab 2](./02-serverless/README.md) 24 | 25 | ### Lab 3 - Deploy Containers to Azure Container Instances (ACI) 26 | 27 | In this lab, you will deploy a containerized application to Azure Container Instances. 28 | 29 | [Get Started with Lab 3](./03-aci/README.md) 30 | 31 | ### Lab 4 - Provision Virtual Machines 32 | 33 | In this lab, you will learn about the creation of Virtual Machines in Azure. 34 | 35 | [Get Started with Lab 4](./04-vms/README.md) 36 | 37 | ### Lab 5 - Deploy Containers to a Kubernetes Cluster 38 | 39 | In this lab, you will deploy a containerized application to a Kubernetes cluster. 40 | 41 | [Get Started with Lab 5](./05-kubernetes/README.md) 42 | -------------------------------------------------------------------------------- /labs/azure/python/01-iac/01-creating-a-new-project.md: -------------------------------------------------------------------------------- 1 | # Creating a New Project 2 | 3 | Infrastructure in Pulumi is organized into projects. Each project is a single program that, when run, declares the desired infrastructure for Pulumi to manage. 4 | 5 | ## Step 1 — Create a Directory 6 | 7 | Each Pulumi project lives in its own directory. Create one now and change into it: 8 | 9 | ```bash 10 | mkdir iac-workshop 11 | cd iac-workshop 12 | ``` 13 | 14 | > Pulumi will use the directory name as your project name by default. To create an independent project, simply name the directory differently. 15 | 16 | ## Step 2 — Initialize Your Project 17 | 18 | A Pulumi project is just a directory with some files in it. It's possible for you to create a new one by hand. The `pulumi new` command, however, automates the process: 19 | 20 | ```bash 21 | pulumi new azure-python -y 22 | ``` 23 | 24 | This will print output similar to the following with a bit more information and status as it goes: 25 | 26 | ``` 27 | Created project 'iac-workshop' 28 | 29 | Please enter your desired stack name. 30 | Created stack 'dev' 31 | 32 | Saved config 33 | 34 | Your new project is ready to go! ✨ 35 | ``` 36 | 37 | This command has created all the files we need, initialized a new stack named `dev` (an instance of our project), and written a requirements.txt file with the relevant python dependencies. 38 | 39 | ## Step 3 — Inspect Your New Project 40 | 41 | Our project is comprised of multiple files: 42 | 43 | * **`__main__.py`**: your Pulimi program and stack definition file 44 | * **`Pulumi.yaml`**: your project's metadata, containing its name and language 45 | * **`Pulumi.dev.yaml`**: the Azure configuration for the Pulumi dev stack 46 | * **`requirements.txt`**: the python dependencies required from PyPy 47 | 48 | Open `__main__.py` to see the contents of the template program of your infrastructure stack: 49 | 50 | ```python 51 | import pulumi 52 | from pulumi_azure import core, storage 53 | 54 | # Create an Azure Resource Group 55 | resource_group = core.ResourceGroup('resource_group') 56 | 57 | # Create an Azure resource (Storage Account) 58 | account = storage.Account('storage', 59 | # The location for the storage account will be derived automatically from the resource group. 60 | resource_group_name=resource_group.name, 61 | account_tier='Standard', 62 | account_replication_type='LRS') 63 | 64 | # Export the connection string for the storage account 65 | pulumi.export('connection_string', account.primary_connection_string) 66 | 67 | ``` 68 | 69 | Feel free to explore the other files, although we won't be editing any of them by hand. 70 | 71 | # Next Steps 72 | 73 | * [Configuring Azure](./02-configuring-azure.md) 74 | -------------------------------------------------------------------------------- /labs/azure/python/01-iac/02-configuring-azure.md: -------------------------------------------------------------------------------- 1 | # Configuring Azure 2 | 3 | Now that you have a basic project, let's set up your environemnt, and configure Azure support for it. 4 | 5 | ## Step 1 — Create a virtual environment and install python dependencies 6 | Creation of a virtual environment can vary between operating systems - see the [Azure Quickstart](https://www.pulumi.com/docs/get-started/azure/review-project/) for more information. 7 | 8 | ### Windows 9 | Run the following command to create a virtual environment 10 | ```bash 11 | python -m venv venv 12 | ``` 13 | 14 | Activate the environment: 15 | ```bash 16 | venv\Scripts\activate 17 | ``` 18 | 19 | Install dependencies: 20 | ```bash 21 | pip3 install -r requirements.txt 22 | ``` 23 | ### Mac and Linux 24 | Run the following command to create a virtual environment 25 | ```bash 26 | python3 -m venv venv 27 | ``` 28 | 29 | Activate the environment: 30 | ```bash 31 | source venv/bin/activate 32 | ``` 33 | 34 | Install dependencies: 35 | ```bash 36 | pip3 install -r requirements.txt 37 | ``` 38 | 39 | At this point, dependencies will be installed into your virtual environment. **If you close your terminal at any time**, you may need to re-activate the environment: 40 | ```bash 41 | source venv/bin/activate 42 | ``` 43 | 44 | ### Point to note 45 | Going forward, instructions will refer to `python` for consistency, but if you are on mac or linux, use the appropriate `python3` command. 46 | 47 | ## Step 2 — Configure an Azure Region 48 | 49 | The Azure region to deploy to is pre-set to WestUS - but you can modify the region you would like to deploy to: 50 | 51 | ```bash 52 | pulumi config set azure:location westus2 53 | ``` 54 | 55 | Feel free to choose any Azure region that supports the services used in these labs ([see this infographic](https://azure.microsoft.com/en-us/global-infrastructure/regions/) for a list of available regions). 56 | 57 | The command updates and persists the value to the local `Pulumi.dev.yaml` file. You can view or edit this file at any time to effect the configuration of the current stack. 58 | 59 | ## Step 3 — Login to Azure 60 | 61 | Simply login to the Azure CLI and Pulumi will automatically use your credentials: 62 | 63 | ``` 64 | az login 65 | ... 66 | You have logged in. Now let us find all the subscriptions to which you have access... 67 | ... 68 | ``` 69 | 70 | The Azure CLI, and thus Pulumi, will use the Default Subscription by default, however it is possible to override the subscription, by simply setting your subscription ID to the id output from `az account list`’s output: 71 | 72 | ``` 73 | $ az account list 74 | ``` 75 | 76 | Pick out the `` from the list and run: 77 | 78 | ``` 79 | $ az account set --subscription= 80 | ``` 81 | 82 | ## Next Steps 83 | 84 | * [Provisioning a Resource Group](./03-provisioning-infrastructure.md) 85 | -------------------------------------------------------------------------------- /labs/azure/python/01-iac/05-making-your-stack-configurable.md: -------------------------------------------------------------------------------- 1 | # Making Your Stack Configurable 2 | 3 | Right now, the container's name is hard-coded. Next, you'll make the name configurable. 4 | 5 | ## Step 1 — Adding a Config Variable 6 | 7 | Instead of hard-coding the `"files"` container, we will use configuration to make it easy to change the name without editing the program. 8 | 9 | Add this line to `__main__.py` right after your import statements: 10 | 11 | ```python 12 | config = pulumi.Config() 13 | ``` 14 | 15 | ## Step 2 — Populating the Container Based on Config 16 | 17 | Replace the hard-coded `"Name"` property value with the one from configuration: 18 | 19 | ```python 20 | container = storage.Container('mycontainer', 21 | name = config.require('container'), 22 | storage_account_name = account.name) 23 | ``` 24 | 25 | > :white_check_mark: After these changes, your `__main__.py` should [look like this](./code/05-making-your-stack-configurable/step2.py). 26 | 27 | ## Step 3 — Deploying the Changes 28 | 29 | Now, deploy your changes. To do so, first configure your stack. If you don't, you'll get an error: 30 | 31 | ```bash 32 | pulumi up 33 | ``` 34 | 35 | This results in an error like the following: 36 | 37 | ``` 38 | ... 39 | ConfigMissingException: Missing Required configuration variable 'iac-workshop:container' 40 | please set a value using the command `pulumi config set iac-workshop:container ` 41 | ... 42 | ``` 43 | 44 | Configure the `iac-workshop:container` variable very much like the `azure:location` variable: 45 | 46 | ```bash 47 | pulumi config set iac-workshop:container html 48 | ``` 49 | 50 | To make things interesting, I set the name to `html` which is different from the previously hard-coded value `files`. 51 | 52 | Run `pulumi up` again. This detects that the container has changed and will perform a simple update: 53 | 54 | ``` 55 | Updating (dev): 56 | 57 | Type Name Status Info 58 | pulumi:pulumi:Stack iac-workshop-dev 59 | +- └─ azure:storage:Container mycontainer replaced [diff: ~name] 60 | 61 | Outputs: 62 | AccountName: "mystorage872202e1" 63 | 64 | Resources: 65 | +-1 replaced 66 | 3 unchanged 67 | 68 | Duration: 10s 69 | 70 | Permalink: https://app.pulumi.com/myuser/iac-workshop/dev/updates/5 71 | ``` 72 | 73 | And you will see the contents added above. 74 | 75 | ## Next Steps 76 | 77 | * [Creating a Second Stack](./06-creating-a-second-stack.md) 78 | -------------------------------------------------------------------------------- /labs/azure/python/01-iac/06-creating-a-second-stack.md: -------------------------------------------------------------------------------- 1 | # Creating a Second Stack 2 | 3 | It is easy to create multiple instances of the same project. This is called a stack. This is handy for multiple development or test environments, staging versus production, and scaling a given infrastructure across many regions. 4 | 5 | ## Step 1 — Create and Configure a New Stack 6 | 7 | Create a new stack: 8 | 9 | ```bash 10 | pulumi stack init prod 11 | ``` 12 | 13 | Next, configure its two required variables: 14 | 15 | ```bash 16 | pulumi config set azure:location westeurope 17 | pulumi config set iac-workshop:container htmlprod 18 | ``` 19 | 20 | If you are ever curious to see the list of stacks for your current project, run this command: 21 | 22 | ```bash 23 | pulumi stack ls 24 | ``` 25 | 26 | It will print all stacks for this project that are available to you: 27 | 28 | ``` 29 | NAME LAST UPDATE RESOURCE COUNT URL 30 | dev 30 minutes ago 4 https://app.pulumi.com/myuser/iac-workshop/dev 31 | prod* 3 minutes ago 0 https://app.pulumi.com/myuser/iac-workshop/prod 32 | ``` 33 | 34 | ## Step 2 — Deploy the New Stack 35 | 36 | Now deploy all of the changes: 37 | 38 | ```bash 39 | pulumi up 40 | ``` 41 | 42 | This will create an entirely new set of resources from scratch, unrelated to the existing `dev` stack's resources. 43 | 44 | ``` 45 | Updating (prod): 46 | 47 | Type Name Status 48 | + pulumi:pulumi:Stack iac-workshop-prod created 49 | + ├─ azure:core:ResourceGroup my-group created 50 | + ├─ azure:storage:Account mystorage created 51 | + └─ azure:storage:Container mycontainer created 52 | 53 | Outputs: 54 | AccountName: "mystorage4a3f2830" 55 | 56 | Resources: 57 | + 4 created 58 | 59 | Duration: 30s 60 | 61 | Permalink: https://app.pulumi.com/myuser/iac-workshop/prod/updates/1 62 | ``` 63 | 64 | A new set of resources has been created for the `prod` stack. 65 | 66 | ## Next Steps 67 | 68 | * [Destroying Your Infrastructure](./07-destroying-your-infrastructure.md) 69 | -------------------------------------------------------------------------------- /labs/azure/python/01-iac/code/03-provisioning-infrastructure/step1.py: -------------------------------------------------------------------------------- 1 | import pulumi 2 | from pulumi_azure import core, storage 3 | 4 | # Create an Azure Resource Group 5 | resource_group = core.ResourceGroup('my-group') -------------------------------------------------------------------------------- /labs/azure/python/01-iac/code/04-updating-your-infrastructure/step1.py: -------------------------------------------------------------------------------- 1 | import pulumi 2 | from pulumi_azure import core, storage 3 | 4 | # Create an Azure Resource Group 5 | resource_group = core.ResourceGroup('my-group') 6 | 7 | storage_account = storage.Account('mystorage', 8 | resource_group_name=resource_group.name, 9 | account_tier='Standard', 10 | account_replication_type='LRS') -------------------------------------------------------------------------------- /labs/azure/python/01-iac/code/04-updating-your-infrastructure/step2.py: -------------------------------------------------------------------------------- 1 | import pulumi 2 | from pulumi_azure import core, storage 3 | 4 | # Create an Azure Resource Group 5 | resource_group = core.ResourceGroup('resource_group') 6 | 7 | 8 | account = storage.Account('storage', 9 | resource_group_name=resource_group.name, 10 | account_tier='Standard', 11 | account_replication_type='LRS') 12 | 13 | 14 | pulumi.export('account_name', account.name) 15 | -------------------------------------------------------------------------------- /labs/azure/python/01-iac/code/04-updating-your-infrastructure/step4.py: -------------------------------------------------------------------------------- 1 | import pulumi 2 | from pulumi_azure import core, storage 3 | 4 | # Create an Azure Resource Group 5 | resource_group = core.ResourceGroup('resource_group') 6 | 7 | account = storage.Account('storage', 8 | resource_group_name=resource_group.name, 9 | account_tier='Standard', 10 | account_replication_type='LRS') 11 | 12 | container = storage.Container('mycontainer', 13 | name = 'files', 14 | storage_account_name = account.name) 15 | 16 | pulumi.export('account_name', account.name) -------------------------------------------------------------------------------- /labs/azure/python/01-iac/code/05-making-your-stack-configurable/step2.py: -------------------------------------------------------------------------------- 1 | import pulumi 2 | from pulumi_azure import core, storage 3 | 4 | config = pulumi.Config() 5 | 6 | # Create an Azure Resource Group 7 | resource_group = core.ResourceGroup('resource_group') 8 | 9 | account = storage.Account('storage', 10 | resource_group_name=resource_group.name, 11 | account_tier='Standard', 12 | account_replication_type='LRS') 13 | 14 | container = storage.Container('mycontainer', 15 | name = config.require('container'), 16 | storage_account_name = account.name) 17 | 18 | pulumi.export('account_name', account.name) -------------------------------------------------------------------------------- /labs/azure/python/Infrastructure as Code Workshop Azure.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulumi/infrastructure-as-code-workshop/a4d142c9237d8e9a4c1868f8b15e776a244c0e2f/labs/azure/python/Infrastructure as Code Workshop Azure.pdf -------------------------------------------------------------------------------- /labs/azure/python/README.md: -------------------------------------------------------------------------------- 1 | ### Prerequisites 2 | 3 | Please follow [this guide](./00-installing-prerequisites.md) to install all the binaries needed to complete the labs. 4 | 5 | ### Lab 1 — Modern Infrastructure as Code 6 | 7 | The first lab takes you on a tour of infrastructure as code concepts: 8 | 9 | 1. [Creating a New Project](./01-iac/01-creating-a-new-project.md) 10 | 2. [Configuring Azure](./01-iac/02-configuring-azure.md) 11 | 3. [Provisioning Infrastructure](./01-iac/03-provisioning-infrastructure.md) 12 | 4. [Updating your Infrastructure](./01-iac/04-updating-your-infrastructure.md) 13 | 5. [Making Your Stack Configurable](./01-iac/05-making-your-stack-configurable.md) 14 | 6. [Creating a Second Stack](./01-iac/06-creating-a-second-stack.md) 15 | 7. [Destroying Your Infrastructure](./01-iac/07-destroying-your-infrastructure.md) 16 | 17 | [Get Started with Lab 1](./01-iac/01-creating-a-new-project.md) -------------------------------------------------------------------------------- /labs/intro/python/README.md: -------------------------------------------------------------------------------- 1 | # Introduction to Pulumi 2 | 3 | This workshop introduces you to the basic Pulumi building blocks you need to be effective with Pulumi. 4 | 5 | ## Lab 1 - Create a Project 6 | 7 | In this [lab](./lab-01/README.md), you'll initialize a new project in Pulumi 8 | 9 | ## Lab 2 - Create Your First Resource 10 | 11 | In this [lab](./lab-02/README.md), you'll create your very first Pulumi resource, a Docker image. You'll learn how to define resource using Pulumi's TypeScript SDK 12 | and learn how to install a new Pulumi provider. 13 | 14 | ## Lab 3 - Configuration & Outputs 15 | 16 | In this [lab](./lab-03/README.md), you'll learn how to use Pulumi configuration in your project. You'll learn how to pass that configuration through to an application, 17 | and make it required. 18 | 19 | In addition to this, you'll create a second resource which will introduce you to the concept of Pulumi "inputs & outputs" 20 | 21 | ## Lab 4 - Exported Outputs, Stack & Stack References 22 | 23 | In this [lab](./lab-04/README.md), you'll learn how to export infrastructure values from your stack and pass them to other stacks. 24 | Finally, you'll learn about how configuration is related to Pulumi stacks and how your projects can be redeployed many times. 25 | 26 | 27 | -------------------------------------------------------------------------------- /labs/intro/python/lab-01/README.md: -------------------------------------------------------------------------------- 1 | # Creating a New Project 2 | 3 | Infrastructure in Pulumi is organized into projects. Each project is a single program that, when run, declares the desired infrastructure for Pulumi to manage. 4 | 5 | ## Step 1 — Create a Directory 6 | 7 | Each Pulumi project lives in its own directory. Create one now and change into it: 8 | 9 | ```bash 10 | mkdir my-first-app 11 | cd my-first-app 12 | ``` 13 | 14 | > Pulumi will use the directory name as your project name by default. To create an independent project, simply name the directory differently. 15 | 16 | ## Step 2 — Initialize Your Project 17 | 18 | A Pulumi project is just a directory with some files in it. It's possible for you to create a new one by hand. The `pulumi new` command, however, automates the process: 19 | 20 | ```bash 21 | pulumi new python -y 22 | ``` 23 | 24 | This will print output similar to the following with a bit more information and status as it goes: 25 | 26 | ``` 27 | Created stack 'dev' 28 | 29 | Creating virtual environment... 30 | 31 | Finished creating virtual environment 32 | 33 | Updating pip, setuptools, and wheel in virtual environment... 34 | ... 35 | Your new project is ready to go! ✨ 36 | 37 | To perform an initial deployment, run 'pulumi up' 38 | ``` 39 | 40 | This command has created all the files we need, initialized a new stack named `dev` (an instance of our project), and installed the needed package dependencies from PyPi. 41 | 42 | ## Step 3 — Inspect Your New Project 43 | 44 | Our project is comprised of multiple files: 45 | 46 | * **`__main__.py`**: your program's main entrypoint file 47 | * **`requirements.txt`**: your project's Python dependency information 48 | * **`Pulumi.yaml`**: your project's metadata, containing its name and language 49 | * **`venv`**: a [virtualenv](https://pypi.org/project/virtualenv/) for your project 50 | 51 | Run `cat __main__.py` to see the contents of your project's empty program: 52 | 53 | ```python 54 | """A Python Pulumi program""" 55 | 56 | import pulumi 57 | ``` 58 | 59 | Feel free to explore the other files, although we won't be editing any of them by hand. 60 | 61 | # Next Steps 62 | 63 | * [Create your first resource](../lab-02/README.md) 64 | -------------------------------------------------------------------------------- /labs/intro/python/lab-02/README.md: -------------------------------------------------------------------------------- 1 | # Lab 02 - Create & Run a Docker Container 2 | 3 | In this lab, we'll create our first Pulumi resource. We'll run a Docker container we build locally using infrastructure as code. 4 | 5 | ## Step 1 - Create your application 6 | 7 | Now, let's make a very simple HTTP application with Python. Inside your project directory, create an application directory: 8 | 9 | ```bash 10 | mkdir app 11 | ``` 12 | 13 | Inside this `app` directory should be two files. We need to bootstrap a webserver application. We can use native python for this: 14 | 15 | In a file called `app/__main__.py` add the following contents: 16 | 17 | ```python 18 | import http.server 19 | import socketserver 20 | from http import HTTPStatus 21 | 22 | 23 | class Handler(http.server.SimpleHTTPRequestHandler): 24 | def do_GET(self): 25 | self.send_response(HTTPStatus.OK) 26 | self.end_headers() 27 | self.wfile.write(b'Hello Lee') 28 | 29 | 30 | httpd = socketserver.TCPServer(('', 3000), Handler) 31 | httpd.serve_forever() 32 | ``` 33 | 34 | Next, create a `Dockerfile` which will be built and will include this webserver 35 | 36 | ``` 37 | FROM python:3.8.6-alpine 38 | 39 | WORKDIR /app 40 | 41 | COPY __main__.py /app 42 | 43 | CMD [ "python", "/app/__main__.py" ] 44 | ``` 45 | 46 | ## Step 2 - Build your Docker Image with Pulumi 47 | 48 | Back inside your pulumi program, let's build your Docker image. Inside your Pulumi program's `__main__.py` add the following: 49 | 50 | 51 | ```python 52 | import pulumi 53 | from pulumi_docker import Image, DockerBuild 54 | 55 | stack = pulumi.get_stack() 56 | image_name = "my-first-app" 57 | 58 | # build our image! 59 | image = Image(image_name, 60 | build=DockerBuild(context="app"), 61 | image_name=f"{image_name}:{stack}", 62 | skip_push=True) 63 | ``` 64 | 65 | Make sure you install the `pulumi_docker` provider from pip inside your virtualenv: 66 | 67 | ``` 68 | source venv/bin/activate 69 | pip3 install pulumi_docker 70 | ``` 71 | 72 | You should see some output showing the pip package and the provider being installed 73 | 74 | Run `pulumi up` and it should build your docker image 75 | 76 | If you run `docker images` you should see your built container. 77 | 78 | Now that we've provisioned our first piece of infrastructure, let's look at how we can use configuration in our Pulumi programs. 79 | 80 | # Next Steps 81 | 82 | * [Use configuration](../lab-03/README.md) 83 | -------------------------------------------------------------------------------- /labs/intro/typescript/README.md: -------------------------------------------------------------------------------- 1 | # Introduction to Pulumi 2 | 3 | This workshop introduces you to the basic Pulumi building blocks you need to be effective with Pulumi. 4 | 5 | ## Lab 1 - Create a Project 6 | 7 | In this [lab](./lab-01/README.md), you'll initialize a new project in Pulumi 8 | 9 | ## Lab 2 - Create Your First Resource 10 | 11 | In this [lab](./lab-02/README.md), you'll create your very first Pulumi resource, a Docker image. You'll learn how to define resource using Pulumi's TypeScript SDK 12 | and learn how to install a new Pulumi provider. 13 | 14 | ## Lab 3 - Configuration & Outputs 15 | 16 | In this [lab](./lab-03/README.md), you'll learn how to use Pulumi configuration in your project. You'll learn how to pass that configuration through to an application, 17 | and make it required. 18 | 19 | In addition to this, you'll create a second resource which will introduce you to the concept of Pulumi "inputs & outputs" 20 | 21 | ## Lab 4 - Exported Outputs, Stack & Stack References 22 | 23 | In this [lab](./lab-04/README.md), you'll learn how to export infrastructure values from your stack and pass them to other stacks. 24 | Finally, you'll learn about how configuration is related to Pulumi stacks and how your projects can be redeployed many times. 25 | 26 | 27 | -------------------------------------------------------------------------------- /labs/intro/typescript/lab-01/README.md: -------------------------------------------------------------------------------- 1 | # Creating a New Project 2 | 3 | Infrastructure in Pulumi is organized into projects. Each project is a single program that, when run, declares the desired infrastructure for Pulumi to manage. 4 | 5 | ## Step 1 — Create a Directory 6 | 7 | Each Pulumi project lives in its own directory. Create one now and change into it: 8 | 9 | ```bash 10 | mkdir my-first-app 11 | cd my-first-app 12 | ``` 13 | 14 | > Pulumi will use the directory name as your project name by default. To create an independent project, simply name the directory differently. 15 | 16 | ## Step 2 — Initialize Your Project 17 | 18 | A Pulumi project is just a directory with some files in it. It's possible for you to create a new one by hand. The `pulumi new` command, however, automates the process: 19 | 20 | ```bash 21 | pulumi new typescript 22 | ``` 23 | 24 | This will print output similar to the following with a bit more information and status as it goes: 25 | 26 | ``` 27 | Created project 'my-first-app' 28 | Created stack 'dev' 29 | Saved config 30 | Installing dependencies... 31 | Finished installing dependencies 32 | 33 | Your new project is ready to go! 34 | ``` 35 | 36 | This command has created all the files we need, initialized a new stack named `dev` (an instance of our project), and installed the needed package dependencies from NPM. 37 | 38 | ## Step 3 — Inspect Your New Project 39 | 40 | Our project is comprised of multiple files: 41 | 42 | * **`index.ts`**: your program's main entrypoint file 43 | * **`package.json`** and **`package-lock.json`**: your project's NPM dependency information 44 | * **`Pulumi.yaml`**: your project's metadata, containing its name and language 45 | * **`tsconfig.json`**: your project's TypeScript settings 46 | * **`node_modules/`**: a directory containing your project's installed NPM dependencies 47 | 48 | Run `cat index.ts` to see the contents of your project's empty program: 49 | 50 | ```typescript 51 | import * as pulumi from "@pulumi/pulumi"; 52 | ``` 53 | 54 | Feel free to explore the other files, although we won't be editing any of them by hand. 55 | 56 | # Next Steps 57 | 58 | * [Create your first resource](../lab-02/README.md) 59 | -------------------------------------------------------------------------------- /labs/intro/typescript/lab-02/code/.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /node_modules/ 3 | -------------------------------------------------------------------------------- /labs/intro/typescript/lab-02/code/Pulumi.dev.yaml: -------------------------------------------------------------------------------- 1 | config: 2 | docker.ts:image_name: jaxxstorm/intro-to-pulumi 3 | -------------------------------------------------------------------------------- /labs/intro/typescript/lab-02/code/Pulumi.yaml: -------------------------------------------------------------------------------- 1 | name: docker.ts 2 | runtime: nodejs 3 | description: A minimal TypeScript Pulumi program 4 | -------------------------------------------------------------------------------- /labs/intro/typescript/lab-02/code/app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12 2 | 3 | WORKDIR /app 4 | 5 | COPY *.json /app/ 6 | COPY index.ts /app/ 7 | 8 | RUN npm install && npm run env -- tsc 9 | # use dumb-init so docker containers respect signals 10 | RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_amd64 && chmod +x /usr/local/bin/dumb-init 11 | 12 | EXPOSE 3000 13 | 14 | ENTRYPOINT ["/usr/local/bin/dumb-init", "--"] 15 | CMD [ "node", "index.js" ] 16 | -------------------------------------------------------------------------------- /labs/intro/typescript/lab-02/code/app/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var express = require("express"); 4 | var app = express(); 5 | app.get('/', function (req, res) { 6 | res.send("Hello world!"); 7 | }); 8 | app.listen(3000, function () { 9 | console.log('Starting app on port 3000!'); 10 | }); 11 | -------------------------------------------------------------------------------- /labs/intro/typescript/lab-02/code/app/index.ts: -------------------------------------------------------------------------------- 1 | import express = require('express'); 2 | import morgan = require('morgan'); 3 | 4 | const app: express.Application = express(); 5 | 6 | app.use(morgan('combined')) 7 | 8 | app.get('/', function(req, res) { 9 | res.send("Hello world!"); 10 | }); 11 | 12 | app.listen(3000, function() { 13 | console.log('Starting app on port 3000!'); 14 | }) 15 | -------------------------------------------------------------------------------- /labs/intro/typescript/lab-02/code/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@types/express": "^4.17.9", 14 | "@types/morgan": "^1.9.2", 15 | "express": "^4.17.1", 16 | "morgan": "^1.10.0", 17 | "typescript": "^4.1.2" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /labs/intro/typescript/lab-02/code/index.ts: -------------------------------------------------------------------------------- 1 | import * as pulumi from "@pulumi/pulumi"; 2 | import * as docker from "@pulumi/docker"; 3 | 4 | const config = new pulumi.Config(); 5 | const stack = pulumi.getStack(); 6 | 7 | const imageName = config.require('image_name'); 8 | 9 | const image = new docker.Image('local-image', { 10 | build: './app', 11 | imageName: `${imageName}:${stack}`, 12 | skipPush: true, 13 | }) 14 | 15 | const container = new docker.Container('local-container', { 16 | image: image.baseImageName, 17 | ports: [{ 18 | internal: 3000, 19 | external: 3000, 20 | }] 21 | }) 22 | -------------------------------------------------------------------------------- /labs/intro/typescript/lab-02/code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docker.ts", 3 | "devDependencies": { 4 | "@types/node": "^10.0.0" 5 | }, 6 | "dependencies": { 7 | "@pulumi/docker": "^2.4.0", 8 | "@pulumi/pulumi": "^2.0.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /labs/intro/typescript/lab-02/code/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "outDir": "bin", 5 | "target": "es2016", 6 | "module": "commonjs", 7 | "moduleResolution": "node", 8 | "sourceMap": true, 9 | "experimentalDecorators": true, 10 | "pretty": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "noImplicitReturns": true, 13 | "forceConsistentCasingInFileNames": true 14 | }, 15 | "files": [ 16 | "index.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /slides/InfrastructureAsCodeWorkshop.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulumi/infrastructure-as-code-workshop/a4d142c9237d8e9a4c1868f8b15e776a244c0e2f/slides/InfrastructureAsCodeWorkshop.pdf --------------------------------------------------------------------------------