diff --git a/content/docs/iac/get-started/aws/begin.md b/content/docs/iac/get-started/aws/begin.md index 12bf1eec7ec9..bd81f19131f9 100644 --- a/content/docs/iac/get-started/aws/begin.md +++ b/content/docs/iac/get-started/aws/begin.md @@ -24,14 +24,6 @@ aliases: - /docs/clouds/aws/get-started/begin/ --- -## Install Pulumi - -Download and install Pulumi for your platform: - -{{< install-pulumi >}} -{{% notes info %}} -All Windows examples in this tutorial assume you are running in PowerShell. -{{% /notes %}} -{{< /install-pulumi >}} +{{< get-started-install-body >}} {{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/aws/create-component.md b/content/docs/iac/get-started/aws/create-component.md index 59bbb6958321..e6bb9c23096d 100644 --- a/content/docs/iac/get-started/aws/create-component.md +++ b/content/docs/iac/get-started/aws/create-component.md @@ -552,7 +552,7 @@ public class AwsS3Website : Pulumi.ComponentResource }); // Create an S3 Bucket object for each file; note the changes to name/source: - foreach (var file in args.Files) { + foreach (var file in args.Files ?? []) { new BucketObject(file, new() { Bucket = bucket.Id, @@ -753,7 +753,7 @@ using Pulumi; using Pulumi.Aws.S3; using System.Collections.Generic; -return await Deployment.RunAsync(() => +return await Pulumi.Deployment.RunAsync(() => { // Create an instance of our component with the same files as before: var website = new AwsS3Website("my-website", new AwsS3WebsiteArgs() diff --git a/content/docs/iac/get-started/aws/create-project.md b/content/docs/iac/get-started/aws/create-project.md index d3f692d7a19a..26a8e07c750d 100644 --- a/content/docs/iac/get-started/aws/create-project.md +++ b/content/docs/iac/get-started/aws/create-project.md @@ -296,7 +296,7 @@ using Pulumi; using Pulumi.Aws.S3; using System.Collections.Generic; -return await Deployment.RunAsync(() => +return await Pulumi.Deployment.RunAsync(() => { // Create an AWS resource (S3 Bucket) var bucket = new Bucket("my-bucket"); diff --git a/content/docs/iac/get-started/aws/deploy-stack.md b/content/docs/iac/get-started/aws/deploy-stack.md index 1d50e39ca9ab..d33dfb68d7b4 100644 --- a/content/docs/iac/get-started/aws/deploy-stack.md +++ b/content/docs/iac/get-started/aws/deploy-stack.md @@ -129,9 +129,7 @@ $ aws s3 ls ("s3://" + (pulumi stack output bucket_name)) ### View your update on Pulumi Cloud -If you are logged into [Pulumi Cloud](/docs/pulumi-cloud), you'll see "View Live" hyperlinks in the CLI output during your -update. These go to [a page](https://app.pulumi.com) with detailed information about your stack including resources, -configuration, a full history of updates, and more. Click on it to check it out: +If you are logged into [Pulumi Cloud](/docs/pulumi-cloud), you'll see "View Live" hyperlinks in the CLI output during your update. These go to [a page](https://app.pulumi.com) with detailed information about your stack including resources, configuration, a full history of updates, and more. Navigate to it to review the details of your update: A stack update with console output, as shown in the Pulumi Service diff --git a/content/docs/iac/get-started/azure/_index.md b/content/docs/iac/get-started/azure/_index.md index 6bacb90e76b3..e4083c44b469 100644 --- a/content/docs/iac/get-started/azure/_index.md +++ b/content/docs/iac/get-started/azure/_index.md @@ -1,8 +1,8 @@ --- -title_tag: Get Started with Azure -meta_desc: This page provides an overview and guide on how to get started with Azure. title: Azure -h1: Get started with Pulumi & Azure +title_tag: Get started with Pulumi and Azure +h1: Get started with Pulumi and Azure +meta_desc: This page provides an overview and guide on how to get started with Azure. menu: iac: name: Azure @@ -19,6 +19,51 @@ aliases: - /docs/clouds/azure/get-started/ --- -{{< cloud-intro "Microsoft Azure" >}} +**Infrastructure as code (IaC)** lets you deploy, change, and manage infrastructure safely, consistently, +and repeatably using code rather than a graphical user interface. + +Complete this step-by-step tutorial to deploy an Azure Blob Storage-based website using IaC. + +## Before you begin + +Make sure you have the Azure CLI installed and signed in to the Azure subscription you plan to use (for example, `az login`). Then choose your language and ensure you've performed any prerequisites: + +{{< chooser language "typescript,python,go,csharp,java,yaml" / >}} + +{{% choosable language "typescript" %}} + +* Node.js and npm installed locally + +{{% /choosable %}} + +{{% choosable language "python" %}} + +* Python and pip, Poetry or uv installed locally + +{{% /choosable %}} + +{{% choosable language "go" %}} + +* Go installed locally + +{{% /choosable %}} + +{{% choosable language "csharp" %}} + +* .NET installed locally + +{{% /choosable %}} + +{{% choosable language "java" %}} + +* Java 11+ and Maven 3.6.1+ installed locally + +{{% /choosable %}} + +{{% choosable language "yaml" %}} + +* A text editor + +{{% /choosable %}} {{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/azure/begin.md b/content/docs/iac/get-started/azure/begin.md index 2c502f059cef..a72a8c4cb63a 100644 --- a/content/docs/iac/get-started/azure/begin.md +++ b/content/docs/iac/get-started/azure/begin.md @@ -1,8 +1,9 @@ --- -title_tag: Before You Begin | Azure +title_tag: Install Pulumi | Azure meta_desc: This page provides an overview on how to get started with Pulumi when starting an Azure project. -title: Before you begin -h1: "Pulumi & Azure: Before you begin" +title: Install Pulumi +h1: "Get started with Pulumi and Azure" +stepper_link: "I'm ready to begin" weight: 2 menu: iac: @@ -18,74 +19,6 @@ aliases: - /docs/clouds/azure/get-started/begin/ --- -Before you get started using Pulumi, let's run through a few quick steps to ensure your environment is set up correctly. - -### Install Pulumi - -{{< install-pulumi >}} -{{% notes "info" %}} -All Windows examples in this tutorial assume you are running in PowerShell. -{{% /notes %}} -{{< /install-pulumi >}} - -Next, install the required language runtime, if you have not already. - -### Install Language Runtime - -#### Choose Your Language - -{{< chooser language "typescript,python,go,csharp,java,yaml" / >}} - -{{% choosable language "typescript" %}} -{{< install-node >}} -{{% /choosable %}} - -{{% choosable language python %}} -{{< install-python >}} -{{% /choosable %}} - -{{% choosable language go %}} -{{< install-go >}} -{{% /choosable %}} - -{{% choosable language "csharp,fsharp,visualbasic" %}} -{{< install-dotnet >}} -{{% /choosable %}} - -{{% choosable language java %}} -{{< install-java >}} -{{% /choosable %}} - -{{% choosable language yaml %}} -{{< install-yaml >}} -{{% /choosable %}} - -Finally, configure Pulumi with Microsoft Azure. - -### Configure Pulumi to access your Microsoft Azure account - -Pulumi requires cloud credentials to manage and provision resources. Pulumi can authenticate to Azure using a user account or service principal that has **Programmatic access** with rights to deploy and manage your Azure resources. - -{{% notes type="info" %}} -Pulumi relies on the Azure SDK to authenticate requests from your computer to Azure. Your credentials are never sent to pulumi.com. -{{% /notes %}} - -In this guide, you will need a user account with permissions to create and populate Blob storage containers and provide anonymous access to a Blob file. - -When developing locally, we recommend that you install the [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) and then authorize access with a user account. - -```bash -az login -``` - -After successfully logging in, you are ready to go. - -{{% notes type="info" %}} -The Azure CLI, and thus Pulumi, will use the default subscription for the account. You can change the active subscription with the [`az account set`](https://docs.microsoft.com/en-us/cli/azure/account?view=azure-cli-latest#az_account_set) command. -{{% /notes %}} - -For additional information on authenticating with Azure, or to login with a service principal, see [Azure Setup](/registry/packages/azure-native/installation-configuration/). - -Next, you'll create a new Pulumi project. +{{< get-started-install-body >}} {{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/azure/configure.md b/content/docs/iac/get-started/azure/configure.md new file mode 100644 index 000000000000..4232ad2065ba --- /dev/null +++ b/content/docs/iac/get-started/azure/configure.md @@ -0,0 +1,96 @@ +--- +title_tag: Configure access | Azure +title: Configure access +h1: "Get started with Pulumi and Azure" +meta_desc: This page provides an overview on how to get started with Pulumi when starting an Azure project. +weight: 3 +menu: + iac: + name: Configure access + parent: azure-get-started + weight: 3 + +aliases: + - /docs/quickstart/azure/configure/ + - /docs/clouds/azure/get-started/configure/ +--- + +## Configure access to Azure + +Pulumi's CLI needs access to your Azure account to manage cloud resources. + +If you've already installed and configured the Azure CLI, Pulumi will respect and use your configuration settings. + +You must use an Azure account that has rights to deploy and manage resources, such as storage accounts and blob containers. + +### Testing access + +To test that your Azure access is configured properly, run: + +{{% choosable os "linux,macos" %}} + +```bash +$ az account show +``` + +{{% /choosable %}} + +{{% choosable os "windows" %}} + +```powershell +> az account show +``` + +{{% /choosable %}} + +If your Azure subscription details are printed, you are good to go. If not, read on: + +```json +{ + "environmentName": "AzureCloud", + "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "isDefault": true, + "name": "My Subscription", + "state": "Enabled", + "tenantId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "user": { + "name": "user@example.com", + "type": "user" + } +} +``` + +### Alternative approaches + +If you don't have the Azure CLI installed, or you plan on using Pulumi in a CI/CD pipeline, you can create a service principal and set the following environment variables on your workstation: + +{{% choosable os "linux,macos" %}} + +```bash +$ export ARM_CLIENT_ID="" +$ export ARM_CLIENT_SECRET="" +$ export ARM_TENANT_ID="" +$ export ARM_SUBSCRIPTION_ID="" +``` + +{{% /choosable %}} + +{{% choosable os windows %}} + +```powershell +> $env:ARM_CLIENT_ID = "" +> $env:ARM_CLIENT_SECRET = "" +> $env:ARM_TENANT_ID = "" +> $env:ARM_SUBSCRIPTION_ID = "" +``` + +{{% /choosable %}} + +{{% notes type="info" %}} +Consider using [Pulumi ESC's Azure login support](/docs/esc/integrations/dynamic-login-credentials/azure-login) for dynamic, +short-lived Azure credentials via OpenID Connect (OIDC) instead of long-lived static credentials. This is a security best practice. +{{% /notes %}} + +For detailed information on Pulumi's use of Azure credentials, see [Azure Setup](/registry/packages/azure-native/installation-configuration/). + +{{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/azure/create-component.md b/content/docs/iac/get-started/azure/create-component.md new file mode 100644 index 000000000000..b07d32041518 --- /dev/null +++ b/content/docs/iac/get-started/azure/create-component.md @@ -0,0 +1,867 @@ +--- +title_tag: Create a component | Azure +title: Create a component +h1: "Get started with Pulumi and Azure" +meta_desc: This page provides an overview on how to create infrastructure abstractions with Pulumi. +weight: 7 +menu: + iac: + name: Create a component + identifier: azure-get-started.create-component + parent: azure-get-started + weight: 7 + +aliases: + - /docs/quickstart/azure/create-component/ + - /docs/clouds/azure/get-started/create-component/ +--- + +## Create a component + +[**Components**](/docs/iac/concepts/resources/components/) are infrastructure abstractions that encapsulate +complexity and enable sharing and reuse. Instead of copy-pasting common patterns, you can encode them as components. + +You will now create your first component that packages up your Azure static website so you can easily stamp out +entire websites in just a few lines of code: + +{{% choosable language typescript %}} + +```typescript +const website = new AzureStaticWebsite("my-website", { + files: [ "index.html" ], +}); +``` + +{{% /choosable %}} + +{{% choosable language python %}} + +```python +website = AzureStaticWebsite('my-website', files=['index.html']) +``` + +{{% /choosable %}} + +{{% choosable language go %}} + +```go +website, err := NewAzureStaticWebsite(ctx, "my-website", AzureStaticWebsiteArgs{ + Files: []string{"index.html"}, +}) +if err != nil { + return err +} +``` + +{{% /choosable %}} + +{{% choosable language csharp %}} + +```csharp +var website = new AzureStaticWebsite("my-website", new AzureStaticWebsiteArgs() +{ + Files = new[] { "index.html" } +}); +``` + +{{% /choosable %}} + +{{% choosable language java %}} + +```java +var website = new AzureStaticWebsite("my-website", + new AzureStaticWebsiteArgs(new String[] { "index.html" })); +``` + +{{% /choosable %}} + +{{% choosable language yaml %}} + +{{% notes type="warning" %}} + +Unfortunately, YAML lacks the language facilities to author components. Feel free to [skip ahead](/docs/iac/get-started/azure/destroy-stack/). + +{{% /notes %}} + +{{% /choosable %}} + +Using components here also has the benefit that, as the requirements for Azure static websites changes, you can +update the one component definition and have all uses of it benefit. + +### Define a new component + +To define a new component, create a class called `AzureStaticWebsite` that derives from `ComponentResource`. It'll have a mostly-empty +constructor to start with but you will add the Azure resources to it in the next step. You'll also define the inputs for the +component -- the `files` to add to the website -- and outputs -- a single property with the website `url`. + +To get going, create a new file {{< compfile >}} alongside {{< langfile >}} and add the following: + +{{% choosable language typescript %}} + +```typescript +import * as azure from "@pulumi/azure-native"; +import * as pulumi from "@pulumi/pulumi"; + +// Arguments for the Azure hosted static website component. +export interface AzureStaticWebsiteArgs { + files: string[]; // a list of files to serve. +} + +// A component that encapsulates creating an Azure hosted static website. +export class AzureStaticWebsite extends pulumi.ComponentResource { + public readonly url: pulumi.Output; // the website url. + + constructor(name: string, args: AzureStaticWebsiteArgs, opts?: pulumi.ComponentResourceOptions) { + super("quickstart:index:AzureStaticWebsite", name, args, opts); + + // Component initialization will go here next... + + this.registerOutputs({}); // Signal component completion. + } +} +``` + +{{% /choosable %}} + +{{% choosable language python %}} + +```python +import pulumi +from pulumi_azure_native import storage, resources +from typing import List + +# A component that encapsulates creating an Azure hosted static website. +class AzureStaticWebsite(pulumi.ComponentResource): + def __init__(self, name: str, files: List[str] = None, opts = None): + super().__init__('quickstart:index:AzureStaticWebsite', name, { 'files': files }, opts) + + # Component initialization will go here next... + + self.register_outputs({}) # Signal component completion. +``` + +{{% /choosable %}} + +{{% choosable language go %}} + +```go +package main + +import ( + "github.com/pulumi/pulumi-azure-native-sdk/resources/v2" + "github.com/pulumi/pulumi-azure-native-sdk/storage/v2" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +type AzureStaticWebsite struct { + pulumi.ResourceState + Url pulumi.StringOutput // the website url. +} + +type AzureStaticWebsiteArgs struct { + Files []string // a list of files to serve. +} + +func NewAzureStaticWebsite(ctx *pulumi.Context, name string, args AzureStaticWebsiteArgs, opts ...pulumi.ResourceOption) (*AzureStaticWebsite, error) { + self := &AzureStaticWebsite{} + err := ctx.RegisterComponentResource("quickstart:index:AzureStaticWebsite", name, self, opts...) + if err != nil { + return nil, err + } + + // Component initialization will go here next... + + ctx.RegisterResourceOutputs(self, pulumi.Map{}) // Signal component completion. + return self, nil +} +``` + +{{% /choosable %}} + +{{% choosable language csharp %}} + +```csharp +using Pulumi; +using Pulumi.AzureNative.Resources; +using Pulumi.AzureNative.Storage; +using Pulumi.AzureNative.Storage.Inputs; +using System.Collections.Generic; + +public class AzureStaticWebsiteArgs +{ + public string[]? Files { get; set; } +} + +public class AzureStaticWebsite : Pulumi.ComponentResource +{ + public Output Url { get; private set; } + + public AzureStaticWebsite(string name, AzureStaticWebsiteArgs args, ComponentResourceOptions? opts = null) + : base("quickstart:index:AzureStaticWebsite", name, opts) + { + // Component initialization will go here next... + + this.RegisterOutputs(new Dictionary{}); // Signal component completion. + } +} +``` + +{{% /choosable %}} + +{{% choosable language java %}} + +```java +package myproject; + +import com.pulumi.Pulumi; +import com.pulumi.azurenative.resources.ResourceGroup; +import com.pulumi.azurenative.storage.StorageAccount; +import com.pulumi.resources.ComponentResource; +import com.pulumi.resources.ComponentResourceOptions; + +public class AzureStaticWebsiteArgs { + public String[] files; + public AzureStaticWebsiteArgs(String[] files) { + this.files = files; + } +} + +public class AzureStaticWebsite extends ComponentResource { + public Output url; + + public AzureStaticWebsite(String name, AzureStaticWebsiteArgs args, ComponentResourceOptions opts) { + super("quickstart:index:AzureStaticWebsite", name, args, opts); + + // Component initialization will go here next... + + this.registerOutputs(Map.of()); + } +} +``` + +{{% /choosable %}} + +{{% choosable language yaml %}} + +{{% notes type="warning" %}} + +Unfortunately, YAML lacks the language facilities to author components. Feel free to [skip ahead](/docs/iac/get-started/azure/destroy-stack/). + +{{% /notes %}} + +{{% /choosable %}} + +This defines a component but it doesn't do much yet. + +### Refactor your code into the component + +Next, make four changes: + +1. Move all resources from {{< langfile >}} into the component's constructor +1. Change each resource to use the component [as the `parent`](/docs/iac/concepts/options/parent/) +1. Generalize the creation of blobs by looping over the list of `files` +1. Assign the resulting website URL to the `url` property of the component + +The resulting {{< compfile >}} file will look like this; feel free to make each edit one at a time if you'd like +to get a feel for things, or simply paste the contents of this into {{< compfile >}}: + +{{% choosable language typescript %}} + +```typescript +import * as azure from "@pulumi/azure-native"; +import * as pulumi from "@pulumi/pulumi"; + +// Arguments for the Azure hosted static website component. +export interface AzureStaticWebsiteArgs { + files: string[]; // a list of files to serve. +} + +// A component that encapsulates creating an Azure hosted static website. +export class AzureStaticWebsite extends pulumi.ComponentResource { + public readonly url: pulumi.Output; // the website url. + + constructor(name: string, args: AzureStaticWebsiteArgs, opts?: pulumi.ComponentResourceOptions) { + super("quickstart:index:AzureStaticWebsite", name, args, opts); + + // Create a resource group + const resourceGroup = new azure.resources.ResourceGroup("my-group", {}, { + // Set the parent to the component (step #2) above. + // Also, do the same for all other resources below. + parent: this, + }); + + // Create a storage account + const storageAccount = new azure.storage.StorageAccount("myaccount", { + resourceGroupName: resourceGroup.name, + kind: azure.storage.Kind.StorageV2, + sku: { + name: azure.storage.SkuName.Standard_LRS, + }, + }, { parent: this }); + + // Enable static website support + const staticWebsite = new azure.storage.StorageAccountStaticWebsite("static-website", { + accountName: storageAccount.name, + resourceGroupName: resourceGroup.name, + indexDocument: "index.html", + }, { parent: this }); + + // Upload each file as a blob: + for (const file of args.files) { + new azure.storage.Blob(file, { + accountName: storageAccount.name, + containerName: staticWebsite.containerName, + resourceGroupName: resourceGroup.name, + source: new pulumi.asset.FileAsset(file), + contentType: "text/html", + }, { parent: this }); + } + + // Capture the URL and make it available as a component property and output: + this.url = storageAccount.primaryEndpoints.apply(pe => pe.web); + this.registerOutputs({ url: this.url }); // Signal component completion. + } +} +``` + +{{% /choosable %}} + +{{% choosable language python %}} + +```python +import pulumi +from pulumi_azure_native import storage, resources +from typing import List + +# A component that encapsulates creating an Azure hosted static website. +class AzureStaticWebsite(pulumi.ComponentResource): + def __init__(self, name: str, files: List[str] = None, opts = None): + super().__init__('quickstart:index:AzureStaticWebsite', name, { 'files': files }, opts) + + # Create a resource group + resource_group = resources.ResourceGroup('my-group', + # Set the parent to the component (step #2) above. + # Also, do the same for all other resources below. + opts=pulumi.ResourceOptions(parent=self), + ) + + # Create a storage account + storage_account = storage.StorageAccount('myaccount', + resource_group_name=resource_group.name, + kind=storage.Kind.STORAGE_V2, + sku={ + 'name': storage.SkuName.STANDARD_LRS, + }, + opts=pulumi.ResourceOptions(parent=self), + ) + + # Enable static website support + static_website = storage.StorageAccountStaticWebsite('static-website', + account_name=storage_account.name, + resource_group_name=resource_group.name, + index_document='index.html', + opts=pulumi.ResourceOptions(parent=self), + ) + + # Upload each file as a blob: + for file in files: + storage.Blob( + file, + account_name=storage_account.name, + container_name=static_website.container_name, + resource_group_name=resource_group.name, + source=pulumi.FileAsset(file), + content_type='text/html', + opts=pulumi.ResourceOptions(parent=self), + ) + + # Capture the URL and make it available as a component property and output: + self.url = storage_account.primary_endpoints.apply(lambda pe: pe.web) + self.register_outputs({ 'url': self.url }) # Signal component completion. +``` + +{{% /choosable %}} + +{{% choosable language go %}} + +```go +package main + +import ( + "github.com/pulumi/pulumi-azure-native-sdk/resources/v2" + "github.com/pulumi/pulumi-azure-native-sdk/storage/v2" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +type AzureStaticWebsite struct { + pulumi.ResourceState + Url pulumi.StringOutput // the website url. +} + +type AzureStaticWebsiteArgs struct { + Files []string // a list of files to serve. +} + +func NewAzureStaticWebsite(ctx *pulumi.Context, name string, args AzureStaticWebsiteArgs, opts ...pulumi.ResourceOption) (*AzureStaticWebsite, error) { + self := &AzureStaticWebsite{} + err := ctx.RegisterComponentResource("quickstart:index:AzureStaticWebsite", name, self, opts...) + if err != nil { + return nil, err + } + + // Create a resource group + resourceGroup, err := resources.NewResourceGroup(ctx, "my-group", nil, + // Set the parent to the component (step #2) above. + // Also, do the same for all other resources below. + pulumi.Parent(self)) + if err != nil { + return nil, err + } + + // Create a storage account + storageAccount, err := storage.NewStorageAccount(ctx, "myaccount", &storage.StorageAccountArgs{ + ResourceGroupName: resourceGroup.Name, + Kind: pulumi.String("StorageV2"), + Sku: &storage.SkuArgs{ + Name: pulumi.String("Standard_LRS"), + }, + }, pulumi.Parent(self)) + if err != nil { + return nil, err + } + + // Enable static website support + staticWebsite, err := storage.NewStorageAccountStaticWebsite(ctx, "static-website", &storage.StorageAccountStaticWebsiteArgs{ + AccountName: storageAccount.Name, + ResourceGroupName: resourceGroup.Name, + IndexDocument: pulumi.String("index.html"), + }, pulumi.Parent(self)) + if err != nil { + return nil, err + } + + // Upload each file as a blob: + for _, file := range args.Files { + _, err = storage.NewBlob(ctx, file, &storage.BlobArgs{ + AccountName: storageAccount.Name, + ContainerName: staticWebsite.ContainerName, + ResourceGroupName: resourceGroup.Name, + Source: pulumi.NewFileAsset(file), + ContentType: pulumi.String("text/html"), + }, pulumi.Parent(self)) + if err != nil { + return nil, err + } + } + + // Capture the URL and make it available as a component property and output: + self.Url = storageAccount.PrimaryEndpoints.ApplyT(func(pe storage.EndpointsResponse) string { + return *pe.Web + }).(pulumi.StringOutput) + + ctx.RegisterResourceOutputs(self, pulumi.Map{"url": self.Url}) // Signal component completion. + return self, nil +} +``` + +{{% /choosable %}} + +{{% choosable language csharp %}} + +```csharp +using Pulumi; +using Pulumi.AzureNative.Resources; +using Pulumi.AzureNative.Storage; +using Pulumi.AzureNative.Storage.Inputs; +using System.Collections.Generic; + +public class AzureStaticWebsiteArgs +{ + public string[]? Files { get; set; } +} + +public class AzureStaticWebsite : Pulumi.ComponentResource +{ + public Output Url { get; private set; } + + public AzureStaticWebsite(string name, AzureStaticWebsiteArgs args, ComponentResourceOptions? opts = null) + : base("quickstart:index:AzureStaticWebsite", name, opts) + { + // Create a resource group + var resourceGroup = new ResourceGroup("my-group", new(), new CustomResourceOptions + { + // Set the parent to the component (step #2) above. + // Also, do the same for all other resources below. + Parent = this, + }); + + // Create a storage account + var storageAccount = new StorageAccount("myaccount", new() + { + ResourceGroupName = resourceGroup.Name, + Kind = Kind.StorageV2, + Sku = new SkuArgs + { + Name = SkuName.Standard_LRS, + }, + }, new CustomResourceOptions + { + Parent = this, + }); + + // Enable static website support + var staticWebsite = new StorageAccountStaticWebsite("static-website", new() + { + AccountName = storageAccount.Name, + ResourceGroupName = resourceGroup.Name, + IndexDocument = "index.html", + }, new CustomResourceOptions + { + Parent = this, + }); + + // Upload each file as a blob: + foreach (var file in args.Files ?? []) { + new Blob(file, new() + { + AccountName = storageAccount.Name, + ContainerName = staticWebsite.ContainerName, + ResourceGroupName = resourceGroup.Name, + Source = new FileAsset(file), + ContentType = "text/html", + }, new CustomResourceOptions + { + Parent = this, + }); + } + + // Capture the URL and make it available as a component property and output: + this.Url = storageAccount.PrimaryEndpoints.Apply(pe => pe.Web); + this.RegisterOutputs(new Dictionary{ + ["url"] = this.Url + }); + } +} +``` + +{{% /choosable %}} + +{{% choosable language java %}} + +```java +package myproject; + +import com.pulumi.*; +import com.pulumi.core.*; +import com.pulumi.asset.FileAsset; +import com.pulumi.resources.*; + +import com.pulumi.azurenative.resources.*; +import com.pulumi.azurenative.storage.*; +import com.pulumi.azurenative.storage.inputs.*; + +import java.util.Map; + +class AzureStaticWebsiteArgs extends ResourceArgs { + public String[] files; + public AzureStaticWebsiteArgs(String[] files) { + this.files = files; + } +} + +class AzureStaticWebsite extends ComponentResource { + public Output url; + + public AzureStaticWebsite(String name, AzureStaticWebsiteArgs args) { + this(name, args, null); + } + + public AzureStaticWebsite(String name, AzureStaticWebsiteArgs args, ComponentResourceOptions opts) { + super("quickstart:index:AzureStaticWebsite", name, args, opts); + + // Create a resource group + var resourceGroup = new ResourceGroup("my-group", null, + // Set the parent to the component (step #2) above. + // Also, do the same for all other resources below. + CustomResourceOptions.builder().parent(this).build()); + + // Create a storage account + var storageAccount = new StorageAccount("myaccount", StorageAccountArgs.builder() + .resourceGroupName(resourceGroup.name()) + .kind("StorageV2") + .sku(SkuArgs.builder() + .name("Standard_LRS") + .build()) + .build(), CustomResourceOptions.builder().parent(this).build()); + + // Enable static website support + var staticWebsite = new StorageAccountStaticWebsite("static-website", StorageAccountStaticWebsiteArgs.builder() + .accountName(storageAccount.name()) + .resourceGroupName(resourceGroup.name()) + .indexDocument("index.html") + .build(), CustomResourceOptions.builder().parent(this).build()); + + // Upload each file as a blob: + for (var file : args.files) { + new Blob(file, BlobArgs.builder() + .accountName(storageAccount.name()) + .containerName(staticWebsite.containerName()) + .resourceGroupName(resourceGroup.name()) + .source(new FileAsset(file)) + .contentType("text/html") + .build(), CustomResourceOptions.builder() + .parent(this) + .build()); + } + + // Capture the URL and make it available as a component property and output: + this.url = storageAccount.primaryEndpoints().applyValue(pe -> pe.web().get()); + this.registerOutputs(Map.of("url", this.url)); + } +} +``` + +{{% /choosable %}} + +{{% choosable language yaml %}} + +{{% notes type="warning" %}} + +Unfortunately, YAML lacks the language facilities to author components. Feel free to [skip ahead](/docs/iac/get-started/azure/destroy-stack/). + +{{% /notes %}} + +{{% /choosable %}} + +### Instantiate the component + +Now go back to your original file {{< langfile >}}. Now that you have moved all of the resources, you can start over with a clean slate. +Ensure the file is empty and we will build it back up by simply importing and instantiating our new component. + +Add this to your now-empty {{< langfile >}}: + +{{% choosable language typescript %}} + +```typescript +// Import from our new component module: +import { AzureStaticWebsite } from "./website"; + +// Create an instance of our component with the same files as before: +const website = new AzureStaticWebsite("my-website", { + files: [ "index.html" ], +}); + +// And export its autoassigned URL: +export const url = website.url; +``` + +{{% /choosable %}} + +{{% choosable language python %}} + +```python +import pulumi + +# Import from our new component module: +from website import AzureStaticWebsite + +# Create an instance of our component with the same files as before: +website = AzureStaticWebsite('my-website', files=['index.html']) + +# And export its autoassigned URL: +pulumi.export("url", website.url) +``` + +{{% /choosable %}} + +{{% choosable language go %}} + +```go +package main + +import ( + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +func main() { + pulumi.Run(func(ctx *pulumi.Context) error { + // Create an instance of our component with the same files as before: + website, err := NewAzureStaticWebsite(ctx, "my-website", AzureStaticWebsiteArgs{ + Files: []string{"index.html"}, + }) + if err != nil { + return err + } + + // And export its autoassigned URL: + ctx.Export("url", website.Url) + return nil + }) +} + +``` + +{{% /choosable %}} + +{{% choosable language csharp %}} + +```csharp +using Pulumi; +using Pulumi.AzureNative.Resources; +using Pulumi.AzureNative.Storage; +using System.Collections.Generic; + +return await Pulumi.Deployment.RunAsync(() => +{ + // Create an instance of our component with the same files as before: + var website = new AzureStaticWebsite("my-website", new AzureStaticWebsiteArgs() + { + Files = new[] { "index.html" } + }); + + // And export its autoassigned URL: + return new Dictionary + { + ["url"] = website.Url + }; +}); +``` + +{{% /choosable %}} + +{{% choosable language java %}} + +```java +package myproject; + +import com.pulumi.Pulumi; + +public class App { + public static void main(String[] args) { + Pulumi.run(ctx -> { + // Create an instance of our component with the same files as before: + var website = new AzureStaticWebsite("my-website", + new AzureStaticWebsiteArgs(new String[] { "index.html" })); + + // And export its autoassigned URL: + ctx.export("url", website.url); + }); + } +} +``` + +{{% /choosable %}} + +{{% choosable language yaml %}} + +{{% notes type="warning" %}} + +Unfortunately, YAML lacks the language facilities to author components. Feel free to [skip ahead](/docs/iac/get-started/azure/destroy-stack/). + +{{% /notes %}} + +{{% /choosable %}} + +### Deploy the component + +Now deploy the resulting component instantiation. To do so, run `pulumi up` as usual: + +``` +$ pulumi up +Previewing update (dev) + + Type Name Plan + pulumi:pulumi:Stack quickstart-dev + + ├─ quickstart:index:AzureStaticWebsite my-site create + + │ ├─ azure-native:resources:ResourceGroup my-group create + + │ ├─ azure-native:storage:StorageAccount myaccount create + + │ ├─ azure-native:storage:StorageAccountStaticWebsite static-website create + + │ └─ azure-native:storage:Blob index.html create + - ├─ azure-native:storage:Blob index.html delete + - ├─ azure-native:storage:StorageAccountStaticWebsite static-website delete + - ├─ azure-native:storage:StorageAccount myaccount delete + - └─ azure-native:resources:ResourceGroup my-group delete + +Resources: + + 5 to create + - 4 to delete + 9 changes. 1 unchanged + +Do you want to perform this update? [Use arrows to move, type to filter] + yes +> no + details +``` + +This preview shows you a few things. First, you'll see our `AzureStaticWebsite` component with all of its children +resources neatly parented underneath it. This helps to see what resources relate to which components. Next, +you'll see that your old resources are being destroyed. + +{{% notes type="info" %}} + +If you're wondering why Pulumi didn't simply update the resources in place, it's because certain changes -- like +refactoring resources into a component -- fundamentally change a resource's identity. Many changes like updating +properties or moving resources between files are not disruptive like this. In such cases, you can assign +[aliases](/docs/iac/concepts/options/aliases/) to prevent deletions from happening. + +{{% /notes %}} + +Accept the changes by selecting `yes` and the deployment will occur: + +``` +Updating (dev) + + Type Name Status + pulumi:pulumi:Stack pu-quickstart-dev + + ├─ quickstart:index:AzureStaticWebsite my-site created (0.16s) + + │ ├─ azure-native:resources:ResourceGroup my-group created (1s) + + │ ├─ azure-native:storage:StorageAccount myaccount created (2s) + + │ ├─ azure-native:storage:StorageAccountStaticWebsite static-website created (0.24s) + + │ └─ azure-native:storage:Blob index.html created (0.19s) + - ├─ azure-native:storage:Blob index.html deleted (0.18s) + - ├─ azure-native:storage:StorageAccountStaticWebsite static-website deleted (0.27s) + - ├─ azure-native:storage:StorageAccount myaccount deleted (0.51s) + - └─ azure-native:resources:ResourceGroup my-group deleted (0.58s) + +Outputs: + ~ url: "https://myaccountabc123.z13.web.core.windows.net/" => "https://myaccountxyz789.z13.web.core.windows.net/" + +Resources: + + 5 created + - 4 deleted + 9 changes. 1 unchanged + +Duration: 10s +``` + +Now test out your new website -- it works like before, just with a tidier codebase now! + +{{% choosable os "linux,macos" %}} + +```bash +$ curl $(pulumi stack output url) + + +

Hello, Pulumi!

+ + +``` + +{{% /choosable %}} + +{{% choosable os "windows" %}} + +```powershell +> curl (pulumi stack output url) + + +

Hello, Pulumi!

+ + +``` + +{{% /choosable %}} + +Once you are ready to move on, let's destroy everything we've spun up in this tutorial. + +{{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/azure/create-project.md b/content/docs/iac/get-started/azure/create-project.md index 3b1371f76fd9..d6b6ceec2ac6 100644 --- a/content/docs/iac/get-started/azure/create-project.md +++ b/content/docs/iac/get-started/azure/create-project.md @@ -1,161 +1,592 @@ --- title_tag: Create a New Project | Azure -meta_desc: This page provides an overview of how to create a new Azure + Pulumi project. title: Create project -h1: "Pulumi & Azure: Create project" -weight: 3 +h1: "Get started with Pulumi and Azure" +meta_desc: This page provides an overview of how to create a new Azure + Pulumi project. +weight: 4 menu: iac: name: Create project identifier: azure-get-started.create-project parent: azure-get-started - weight: 3 + weight: 4 aliases: - /docs/quickstart/azure/create-project/ - /docs/clouds/azure/get-started/create-project/ + - /docs/quickstart/azure/review-project/ + - /docs/clouds/azure/get-started/review-project/ --- -Now that you have set up your environment by installing Pulumi, installing your preferred language runtime, -and configuring your Azure credentials, let's create your first Pulumi program. +## Create a new project + +A [**project**](/docs/iac/concepts/projects) is a program in your chosen language that defines a collection of related cloud resources. In this step, you will create a new project. + +### Initializing your project + +Each project lives in its own directory. Create a new one: + +{{% choosable os "linux,macos" %}} + +```bash +$ mkdir quickstart +``` + +{{% /choosable %}} +{{% choosable os "windows" %}} + +```powershell +> mkdir quickstart +``` + +{{% /choosable %}} + +Change into the new directory: + +{{% choosable os "linux,macos" %}} + +```bash +$ cd quickstart +``` + +{{% /choosable %}} +{{% choosable os "windows" %}} + +```powershell +> cd quickstart +``` -{{< chooser language "typescript,python,go,csharp,java,yaml" / >}} +{{% /choosable %}} + +Now initialize a new Pulumi project for Azure using the `pulumi new` command: {{% choosable language typescript %}} +{{% choosable os "linux,macos" %}} + ```bash -$ mkdir quickstart && cd quickstart $ pulumi new azure-typescript ``` +{{% /choosable %}} +{{% choosable os "windows" %}} + +```powershell +> pulumi new azure-typescript +``` + +{{% /choosable %}} + {{% /choosable %}} {{% choosable language python %}} +{{% choosable os "linux,macos" %}} + ```bash -$ mkdir quickstart && cd quickstart $ pulumi new azure-python ``` +{{% /choosable %}} +{{% choosable os "windows" %}} + +```powershell +> pulumi new azure-python +``` + +{{% /choosable %}} + {{% /choosable %}} {{% choosable language csharp %}} +{{% choosable os "linux,macos" %}} + ```bash -$ mkdir quickstart && cd quickstart $ pulumi new azure-csharp ``` +{{% /choosable %}} +{{% choosable os "windows" %}} + +```powershell +> pulumi new azure-csharp +``` + +{{% /choosable %}} + {{% /choosable %}} {{% choosable language go %}} +{{% choosable os "linux,macos" %}} + ```bash -$ mkdir quickstart && cd quickstart $ pulumi new azure-go ``` +{{% /choosable %}} +{{% choosable os "windows" %}} + +```powershell +> pulumi new azure-go +``` + +{{% /choosable %}} + {{% /choosable %}} {{% choosable language java %}} +{{% choosable os "linux,macos" %}} + ```bash -$ mkdir quickstart && cd quickstart $ pulumi new azure-java ``` +{{% /choosable %}} +{{% choosable os "windows" %}} + +```powershell +> pulumi new azure-java +``` + +{{% /choosable %}} + {{% /choosable %}} {{% choosable language yaml %}} +{{% choosable os "linux,macos" %}} + ```bash -$ mkdir quickstart && cd quickstart $ pulumi new azure-yaml ``` {{% /choosable %}} +{{% choosable os "windows" %}} -The [`pulumi new`](/docs/cli/commands/pulumi_new) command creates a new Pulumi project with some basic scaffolding based on the cloud and language specified. +```powershell +> pulumi new azure-yaml +``` -{{< cli-note >}} +{{% /choosable %}} -After logging in, the CLI will proceed with walking you through creating a new project. +{{% /choosable %}} -First, you will be asked for a **project name** and **project description**. Hit `ENTER` to accept the default values or specify new values. +The `pulumi new` command interactively walks through initializing a new project, as well as creating a +[**stack**](/docs/iac/concepts/stacks) and [**configuring**](/docs/iac/concepts/config) it. A stack is an instance of your +project and you may have many of them -- like `dev`, `staging`, and `prod` -- each with different configuration settings. +You will be prompted for configuration values such as an Azure location. You can hit ENTER to accept the default of `WestUS2`, +or can type in another value such as `eastus`: + +``` +azure-native:location: The Azure location to use: (WestUS2) eastus ``` -This command will walk you through creating a new Pulumi project. -Enter a value or leave blank to accept the (default), and press . -Press ^C at any time to quit. +{{< cli-note >}} -project name: (quickstart) -project description: (A minimal Azure Native Pulumi program) -Created project 'quickstart' -``` +{{% choosable language "typescript" %}} + +After some dependency installations from `npm`, the project and stack will be ready. -Next, you will be asked for a **stack name**. Hit `ENTER` to accept the default value of `dev`. +{{% /choosable %}} + +{{% choosable language python %}} + +After the command completes, the project and stack will be ready. +{{% /choosable %}} + +{{% choosable language go %}} + +After the command completes, the project and stack will be ready. + +{{% /choosable %}} + +{{% choosable language "csharp,fsharp,visualbasic" %}} + +After the command completes, the project and stack will be ready. + +{{% /choosable %}} + +{{% choosable language java %}} + +After the command completes, the project and stack will be ready. + +{{% /choosable %}} + +{{% choosable language yaml %}} + +After the command completes, the project and stack will be ready. + +{{% /choosable %}} + +### Review your new project's contents + +If you list the contents of your directory, you'll see some key files: + +{{% choosable language java %}} + +- `src/main/java/myproject` is the project's Java package root + +{{% /choosable %}} + +{{% choosable language "typescript,python,go,csharp,java" %}} + +- {{< langfile >}} contains your project's main code that declares an Azure resource group and storage account +- `Pulumi.yaml` is a [project file](/docs/iac/concepts/projects/project-file) containing metadata about your project like its name + +{{% /choosable %}} +{{% choosable language "yaml" %}} + +- `Pulumi.yaml` is a [project file](/docs/iac/concepts/projects/project-file) containing metadata about your project, like its name, as well as declaring your project's resources + +{{% /choosable %}} + +- `Pulumi.dev.yaml` contains configuration values for the stack you just initialized + +Now examine the code in {{< langfile >}}: + +{{% choosable language typescript %}} + +```typescript +import * as pulumi from "@pulumi/pulumi"; +import * as resources from "@pulumi/azure-native/resources"; +import * as storage from "@pulumi/azure-native/storage"; + +// Create an Azure Resource Group +const resourceGroup = new resources.ResourceGroup("resourceGroup"); + +// Create an Azure resource (Storage Account) +const storageAccount = new storage.StorageAccount("sa", { + resourceGroupName: resourceGroup.name, + sku: { + name: storage.SkuName.Standard_LRS, + }, + kind: storage.Kind.StorageV2, +}); + +const storageAccountKeys = storage.listStorageAccountKeysOutput({ + resourceGroupName: resourceGroup.name, + accountName: storageAccount.name +}); + +// Export the primary key of the Storage Account +export const primaryStorageKey = pulumi.secret(storageAccountKeys.keys[0].value); ``` -Please enter your desired stack name. -To create a stack in an organization, use the format / (e.g. `acmecorp/dev`). -stack name: (dev) -Created stack 'dev' + +{{% /choosable %}} +{{% choosable language python %}} + +```python +"""An Azure RM Python Pulumi program""" + +import pulumi +from pulumi_azure_native import storage +from pulumi_azure_native import resources + +# Create an Azure Resource Group +resource_group = resources.ResourceGroup("resource_group") + +# Create an Azure resource (Storage Account) +account = storage.StorageAccount( + "sa", + resource_group_name=resource_group.name, + sku={ + "name": storage.SkuName.STANDARD_LRS, + }, + kind=storage.Kind.STORAGE_V2, +) + +# Export the primary key of the Storage Account +primary_key = ( + pulumi.Output.all(resource_group.name, account.name) + .apply( + lambda args: storage.list_storage_account_keys( + resource_group_name=args[0], account_name=args[1] + ) + ) + .apply(lambda accountKeys: accountKeys.keys[0].value) +) + +pulumi.export("primary_storage_key", primary_key) ``` -For Azure projects, you will be prompted for the Azure location. You can accept the default value of `WestUS` or choose another location. +{{% /choosable %}} +{{% choosable language go %}} +```go +package main + +import ( + "github.com/pulumi/pulumi-azure-native-sdk/resources/v2" + "github.com/pulumi/pulumi-azure-native-sdk/storage/v2" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +func main() { + pulumi.Run(func(ctx *pulumi.Context) error { + // Create an Azure Resource Group + resourceGroup, err := resources.NewResourceGroup(ctx, "resourceGroup", nil) + if err != nil { + return err + } + + // Create an Azure resource (Storage Account) + account, err := storage.NewStorageAccount(ctx, "sa", &storage.StorageAccountArgs{ + ResourceGroupName: resourceGroup.Name, + Sku: &storage.SkuArgs{ + Name: storage.SkuName_Standard_LRS, + }, + Kind: storage.KindStorageV2, + }) + if err != nil { + return err + } + + // Export the primary key of the Storage Account + ctx.Export("primaryStorageKey", pulumi.All(resourceGroup.Name, account.Name).ApplyT( + func(args []interface{}) (string, error) { + resourceGroupName := args[0].(string) + accountName := args[1].(string) + accountKeys, err := storage.ListStorageAccountKeys(ctx, &storage.ListStorageAccountKeysArgs{ + ResourceGroupName: resourceGroupName, + AccountName: accountName, + }) + if err != nil { + return "", err + } + + return accountKeys.Keys[0].Value, nil + }, + )) + + return nil + }) +} ``` -azure-native:location: The Azure location to use: (WestUS2) -Saved config + +{{% /choosable %}} +{{% choosable language csharp %}} + +```csharp +using Pulumi; +using Pulumi.AzureNative.Resources; +using Pulumi.AzureNative.Storage; +using Pulumi.AzureNative.Storage.Inputs; +using System.Collections.Generic; + +return await Pulumi.Deployment.RunAsync(() => +{ + // Create an Azure Resource Group + var resourceGroup = new ResourceGroup("resourceGroup"); + + // Create an Azure resource (Storage Account) + var storageAccount = new StorageAccount("sa", new StorageAccountArgs + { + ResourceGroupName = resourceGroup.Name, + Sku = new SkuArgs + { + Name = SkuName.Standard_LRS + }, + Kind = Kind.StorageV2 + }); + + var storageAccountKeys = ListStorageAccountKeys.Invoke(new ListStorageAccountKeysInvokeArgs + { + ResourceGroupName = resourceGroup.Name, + AccountName = storageAccount.Name + }); + + var primaryStorageKey = storageAccountKeys.Apply(accountKeys => + { + var firstKey = accountKeys.Keys[0].Value; + return Output.CreateSecret(firstKey); + }); + + // Export the primary key of the Storage Account + return new Dictionary + { + ["primaryStorageKey"] = primaryStorageKey + }; +}); ``` -To list all available locations, use the `az account list-locations` command. +{{% /choosable %}} -```bash -$ az account list-locations --output table +{{% choosable language java %}} + +```java +package myproject; + +import com.pulumi.Pulumi; +import com.pulumi.azurenative.resources.ResourceGroup; +import com.pulumi.azurenative.storage.StorageAccount; +import com.pulumi.azurenative.storage.StorageAccountArgs; +import com.pulumi.azurenative.storage.StorageFunctions; +import com.pulumi.azurenative.storage.enums.Kind; +import com.pulumi.azurenative.storage.enums.SkuName; +import com.pulumi.azurenative.storage.inputs.ListStorageAccountKeysArgs; +import com.pulumi.azurenative.storage.inputs.SkuArgs; +import com.pulumi.azurenative.storage.outputs.EndpointsResponse; +import com.pulumi.core.Either; +import com.pulumi.core.Output; +import com.pulumi.deployment.InvokeOptions; + +public class App { + public static void main(String[] args) { + Pulumi.run(ctx -> { + var resourceGroup = new ResourceGroup("resourceGroup"); + var storageAccount = new StorageAccount("sa", StorageAccountArgs.builder() + .resourceGroupName(resourceGroup.name()) + .sku(SkuArgs.builder() + .name(SkuName.Standard_LRS) + .build()) + .kind(Kind.StorageV2) + .build()); + + var primaryStorageKey = getStorageAccountPrimaryKey( + resourceGroup.name(), + storageAccount.name()); + + ctx.export("primaryStorageKey", primaryStorageKey); + }); + } + + private static Output getStorageAccountPrimaryKey(Output resourceGroupName, + Output accountName) { + return Output.tuple(resourceGroupName, accountName).apply(tuple -> { + var actualResourceGroupName = tuple.t1; + var actualAccountName = tuple.t2; + var invokeResult = StorageFunctions.listStorageAccountKeys(ListStorageAccountKeysArgs.builder() + .resourceGroupName(actualResourceGroupName) + .accountName(actualAccountName) + .build(), InvokeOptions.Empty); + return Output.of(invokeResult) + .applyValue(r -> r.keys().get(0).value()) + .asSecret(); + }); + } +} ``` -You can then change the region for your stack by using the `pulumi config set` command as shown below: +{{% /choosable %}} -```bash -pulumi config set azure-native:location eastus +{{% choosable language yaml %}} + +```yaml +name: quickstart +runtime: yaml +description: A minimal Azure Native Pulumi YAML program + +resources: + # Create an Azure Resource Group + resourceGroup: + type: azure-native:resources:ResourceGroup + # Create an Azure resource (Storage Account) + sa: + type: azure-native:storage:StorageAccount + properties: + resourceGroupName: ${resourceGroup.name} + sku: + name: Standard_LRS + kind: StorageV2 + +variables: + storageAccountKeys: + fn::azure-native:storage:listStorageAccountKeys: + resourceGroupName: ${resourceGroup.name} + accountName: ${sa.name} + +outputs: + # Export the primary key of the Storage Account + primaryStorageKey: ${storageAccountKeys.keys[0].value} ``` -> What are [projects](/docs/concepts/projects/) and [stacks](/docs/concepts/stack/)? Pulumi projects and stacks let you organize Pulumi code. Consider a Pulumi _project_ to be analogous to a GitHub repo---a single place for code---and a _stack_ to be an instance of that code with a separate configuration. For instance, _Project Foo_ may have multiple stacks for different development environments (Dev, Test, or Prod), or perhaps for different cloud configurations (geographic region for example). See [Organizing Projects and Stacks](/docs/using-pulumi/organizing-projects-stacks/) for some best practices on organizing your Pulumi projects and stacks. +{{% /choosable %}} -{{% choosable language "typescript" %}} +The program declares an Azure Resource Group and Storage Account +[resources](/docs/iac/concepts/resources) and exports the storage account's primary key as a [stack output](/docs/iac/concepts/stacks/#outputs). +The primary key is marked as a secret to protect sensitive credential data. To demonstrate working with outputs, +you'll add a non-secret output that exports the storage account's name for convenient reference. -After some dependency installations from `npm`, the project and stack will be ready. +{{% choosable language typescript %}} + +After the line that exports the primary key, add the following export to also export the storage account name: + +```typescript +// Export the primary key of the Storage Account +export const primaryStorageKey = pulumi.secret(storageAccountKeys.keys[0].value); +export const storageAccountName = storageAccount.name; // Add this line +``` {{% /choosable %}} {{% choosable language python %}} -After the command completes, the project and stack will be ready. +After the line that exports the primary key, add the following export to also export the storage account name: + +```python +pulumi.export("primary_storage_key", primary_key) +pulumi.export("storage_account_name", account.name) # Add this line +``` {{% /choosable %}} {{% choosable language go %}} -After the command completes, the project and stack will be ready. +After the `ctx.Export("primaryStorageKey", ...)` statement, add the following export to also export the storage account name: + +```go +// Export the primary key of the Storage Account +ctx.Export("primaryStorageKey", pulumi.All(resourceGroup.Name, account.Name).ApplyT( + func(args []interface{}) (string, error) { + // ... existing code removed for brevity ... + }, +)) +ctx.Export("storageAccountName", account.Name) // Add this line + +return nil +``` {{% /choosable %}} -{{% choosable language "csharp,fsharp,visualbasic" %}} +{{% choosable language csharp %}} -After the command completes, the project and stack will be ready. +Update the returned Dictionary to also export the storage account name: + +```csharp +return new Dictionary +{ + ["primaryStorageKey"] = primaryStorageKey, + ["storageAccountName"] = storageAccount.Name // Add this line +}; +``` {{% /choosable %}} {{% choosable language java %}} -After the command completes, the project and stack will be ready. +After `ctx.export("primaryStorageKey", ...)`, add the following export to also export the storage account name: + +```java +ctx.export("primaryStorageKey", primaryStorageKey); +ctx.export("storageAccountName", storageAccount.name()); // Add this line +``` {{% /choosable %}} {{% choosable language yaml %}} -After the command completes, the project and stack will be ready. +In the `outputs` section, add the following line to also export the storage account name: + +```yaml +outputs: + # Export the primary key of the Storage Account + primaryStorageKey: ${storageAccountKeys.keys[0].value} + storageAccountName: ${sa.name} # Add this line +``` {{% /choosable %}} -Next, we'll review the generated project files. +Now you're ready for your first deployment! {{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/azure/deploy-changes.md b/content/docs/iac/get-started/azure/deploy-changes.md deleted file mode 100644 index ebcda6d5493f..000000000000 --- a/content/docs/iac/get-started/azure/deploy-changes.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -title_tag: Deploy the Changes | Azure -meta_desc: Learn how to deploy changes to an Azure project in this guide. -title: Deploy changes -h1: "Pulumi & Azure: Deploy changes" -weight: 7 -menu: - iac: - name: Deploy changes - identifier: azure-get-started.deploy-changes - parent: azure-get-started - weight: 7 -aliases: - - /docs/quickstart/azure/deploy-changes/ - - /docs/clouds/azure/get-started/deploy-changes/ ---- - -Deploy your changes by using `pulumi up` again. - -```bash -$ pulumi up -``` - -Pulumi will run the `preview` step of the update, which computes the minimally disruptive change to achieve the desired state described by the program. - -``` -Previewing update (dev): - - Type Name Plan - pulumi:pulumi:Stack quickstart-dev - + ├─ azure-native:storage:StorageAccountStaticWebsite staticWebsite create - + └─ azure-native:storage:Blob index.html create - -Outputs: - + staticEndpoint : "https://sa8dd8af62.z22.web.core.windows.net/" - -Resources: - + 2 to create - 3 unchanged - -Do you want to perform this update? -> yes - no - details -``` - -Choosing `yes` will proceed with the update by uploading the `index.html` file to a storage container in your account and enabling static website support on the container. - -``` -Do you want to perform this update? yes -Updating (dev): - - Type Name Status - pulumi:pulumi:Stack quickstart-dev - + ├─ azure-native:storage:StorageAccountStaticWebsite staticWebsite created - + └─ azure-native:storage:Blob index.html created - -Outputs: - primaryStorageKey: "" - + staticEndpoint : "https://sa8dd8af62.z22.web.core.windows.net/" - -Resources: - + 2 created - 3 unchanged - -Duration: 4s -``` - -You can check out your new static website at the URL in the `Outputs` section of your update or you can make a `curl` request and see the contents of your `index.html` object printed out in your terminal. - -{{% choosable language typescript %}} - -```bash -$ curl $(pulumi stack output staticEndpoint) -``` - -{{% /choosable %}} - -{{% choosable language python %}} - -```bash -$ curl $(pulumi stack output staticEndpoint) -``` - -{{% /choosable %}} - -{{% choosable language go %}} - -```bash -$ curl $(pulumi stack output staticEndpoint) -``` - -{{% /choosable %}} - -{{% choosable language csharp %}} - -```bash -$ curl $(pulumi stack output staticEndpoint) -``` - -{{% /choosable %}} - -{{% choosable language java %}} - -```bash -$ curl $(pulumi stack output staticEndpoint) -``` - -{{% /choosable %}} - -{{% choosable language yaml %}} - -```bash -$ curl $(pulumi stack output staticEndpoint) -``` - -{{% /choosable %}} - -And in a few moments, you should see: - -```bash - - -

Hello, Pulumi!

- - -``` - -Now that you have deployed your site, you will destroy the resources. - -{{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/azure/deploy-stack.md b/content/docs/iac/get-started/azure/deploy-stack.md index 01192b1579df..36bd9c48500f 100644 --- a/content/docs/iac/get-started/azure/deploy-stack.md +++ b/content/docs/iac/get-started/azure/deploy-stack.md @@ -1,12 +1,12 @@ --- title_tag: Deploy the Stack | Azure +title: Deploy to Azure +h1: "Get started with Pulumi and Azure" meta_desc: Learn how to deploy your stack to an Azure project in this guide. -title: Deploy stack -h1: "Pulumi & Azure: Deploy stack" weight: 5 menu: iac: - name: Deploy stack + name: Deploy identifier: azure-get-started.deploy-stack parent: azure-get-started weight: 5 @@ -15,13 +15,15 @@ aliases: - /docs/clouds/azure/get-started/deploy-stack/ --- -Let's go ahead and deploy your stack: +## Deploy to Azure + +Now run `pulumi up` to start deploying your new storage account: ```bash $ pulumi up ``` -This command evaluates your program and determines the resource updates to make. First, a preview is shown that outlines the changes that will be made when you run the update: +This command first shows you a **preview** of the changes that will be made: ``` Previewing update (dev): @@ -31,6 +33,10 @@ Previewing update (dev): + ├─ azure-native:resources:ResourceGroup resourceGroup create + └─ azure-native:storage:StorageAccount sa create +Outputs: + primaryStorageKey : [unknown] + storageAccountName: [unknown] + Resources: + 3 to create @@ -40,82 +46,87 @@ Do you want to perform this update? details ``` -Once the preview has finished, you are given three options to choose from. Choosing `details` will show you a rich diff of the changes to be made. Choosing `yes` will create your new storage account in Azure. Choosing `no` will return you to the user prompt without performing the update operation. +No changes have been made yet. You may decline to proceed by selecting `no` or choose `details` to +see more information about the proposed update like your storage account's properties. + +### Performing the update + +To proceed and deploy your new storage account, select `yes`. This begins an **update**: ``` Do you want to perform this update? yes -Updating (dev): +Updating (dev) - Type Name Status - + pulumi:pulumi:Stack quickstart-dev created - + ├─ azure-native:resources:ResourceGroup resourceGroup created - + └─ azure-native:storage:StorageAccount sa created +View in Browser (Ctrl+O): https://app.pulumi.com/your-org-name/quickstart/dev/updates/1 + + Type Name Status + + pulumi:pulumi:Stack quickstart-dev created (25s) + + ├─ azure-native:resources:ResourceGroup resourceGroup created (2s) + + └─ azure-native:storage:StorageAccount sa created (20s) Outputs: - primaryStorageKey: "" + primaryStorageKey : [secret] + storageAccountName: "sa8deefa78" Resources: + 3 created -Duration: 26s +Duration: 27s ``` -Remember the output you defined in the previous step? That [stack output](/docs/concepts/stack#outputs) can be seen in the `Outputs:` section of your update. You can access your outputs from the CLI by running the `pulumi stack output [property-name]` command. For example you can print the primary key of your bucket with the following command: - -{{< chooser language "typescript,python,go,csharp,java,yaml" / >}} +Updates can take some time since they wait for the cloud resources to finish being created. Storage accounts +may take a bit longer, so the update could finish in 20-30 seconds. -{{% choosable language typescript %}} +{{< auto-naming-note resource="storage account" suffix="a1b2c3d" >}} -```bash -$ pulumi stack output primaryStorageKey -``` +### Using stack outputs -{{% /choosable %}} +Both the storage account name and primary key are available as stack outputs. To view the storage account name: -{{% choosable language python %}} +{{% choosable language "typescript,go,csharp,java,yaml" %}} ```bash -$ pulumi stack output primary_storage_key +$ pulumi stack output storageAccountName ``` {{% /choosable %}} -{{% choosable language go %}} +{{% choosable language python %}} ```bash -$ pulumi stack output primaryStorageKey +$ pulumi stack output storage_account_name ``` {{% /choosable %}} -{{% choosable language csharp %}} +Running that command will print out the storage account's name. -```bash -$ pulumi stack output primaryStorageKey -``` +The primary key is marked as a secret. To view it, use the `--show-secrets` flag: -{{% /choosable %}} - -{{% choosable language java %}} +{{% choosable language "typescript,go,csharp,java,yaml" %}} ```bash -$ pulumi stack output primaryStorageKey +$ pulumi stack output primaryStorageKey --show-secrets ``` {{% /choosable %}} -{{% choosable language yaml %}} +{{% choosable language python %}} ```bash -$ pulumi stack output primaryStorageKey +$ pulumi stack output primary_storage_key --show-secrets ``` {{% /choosable %}} -Running that command will print out the storage account's primary key. +### View your update on Pulumi Cloud + +If you are logged into [Pulumi Cloud](/docs/pulumi-cloud), you'll see "View Live" hyperlinks in the CLI output during your update. These go to [a page](https://app.pulumi.com) with detailed information about your stack including resources, configuration, a full history of updates, and more. Navigate to it to review the details of your update: -{{< console-note >}} + + A stack update with console output, as shown in the Pulumi Service + -Now that your storage account has been provisioned, let's modify it to host a static website. +Now that the storage account has been provisioned, you'll update it to host a static website. {{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/azure/destroy-stack.md b/content/docs/iac/get-started/azure/destroy-stack.md index 96b30537d228..fbbbd54d4a6f 100644 --- a/content/docs/iac/get-started/azure/destroy-stack.md +++ b/content/docs/iac/get-started/azure/destroy-stack.md @@ -1,12 +1,12 @@ --- title_tag: Destroy the Stack | Azure -meta_desc: This page provides an overview of how to destroy a Pulumi stack of an Azure project. title: Destroy stack -h1: "Pulumi & Azure: Destroy stack" +h1: "Get started with Pulumi and Azure" +meta_desc: This page provides an overview of how to destroy a Pulumi stack of an Azure project. weight: 8 menu: iac: - name: Destroy stack + name: Cleanup & destroy identifier: azure-get-started.destroy-stack parent: azure-get-started weight: 8 @@ -15,15 +15,27 @@ aliases: - /docs/clouds/azure/get-started/destroy-stack/ --- -Now that you've seen how to deploy changes to our program, let's clean up and tear down the resources that are part of your stack. +## Cleanup & destroy the stack + +Our final step is to clean up all of the resources we've provisioned. This is as simple as running `pulumi destroy`: -To destroy resources, run the following: +{{% choosable os "linux,macos" %}} ```bash -pulumi destroy +$ pulumi destroy +``` + +{{% /choosable %}} + +{{% choosable os "windows" %}} + +```powershell +> pulumi destroy ``` -You'll be prompted to make sure you really want to delete these resources. This can take a minute or two; Pulumi waits until all resources are shut down and deleted before it considers the destroy operation to be complete. +{{% /choosable %}} + +Just like `pulumi up`, `pulumi destroy` shows you a preview before performing any changes: ``` Previewing destroy (dev): @@ -36,13 +48,20 @@ Previewing destroy (dev): - └─ azure-native:resources:ResourceGroup resourceGroup delete Outputs: - - primaryStorageKey: "" - - staticEndpoint : "https://sa8dd8af62.z22.web.core.windows.net/" + - url: "https://sa8dd8af62.z22.web.core.windows.net/" Resources: - 5 to delete -Do you want to perform this destroy? yes +Do you want to perform this destroy? +> yes + no + details +``` + +As with an update, we can choose `no` or `details`; select `yes` to proceed: + +``` Destroying (dev): Type Name Status @@ -53,8 +72,7 @@ Destroying (dev): - └─ azure-native:resources:ResourceGroup resourceGroup deleted Outputs: - - primaryStorageKey: "" - - staticEndpoint : "https://sa8dd8af62.z22.web.core.windows.net/" + - url: "https://sa8dd8af62.z22.web.core.windows.net/" Resources: - 5 deleted @@ -62,17 +80,29 @@ Resources: Duration: 53s ``` -To delete the stack itself, run [`pulumi stack rm`](/docs/cli/commands/pulumi_stack_rm). Note that this removes the stack -entirely from Pulumi Cloud, along with all of its update history. +At this stage, your stack still exists, but all cloud resources have been deleted from it. + +## Remove the stack + +The final step is to remove the stack itself. Destroy keeps the stack around so that you still have the full +history of what happened to the stack. Running [`pulumi stack rm`](/docs/cli/commands/pulumi_stack_rm) will +delete it entirely, including all history and state snapshots. Be careful, this step cannot be undone! + +{{% choosable "os" "macos,linux" %}} + +```bash +$ pulumi stack rm +``` + +{{% /choosable %}} +{{% choosable "os" "windows" %}} -Congratulations! You've successfully provisioned some cloud resources using Pulumi. By completing this guide you have successfully: +```powershell +> pulumi stack rm +``` -- Created a Pulumi new project. -- Provisioned a new Azure storage account and container. -- Added an `index.html` file to your container. -- Served the `index.html` as a static website. -- Destroyed the resources you've provisioned. +{{% /choosable %}} -On the next page, we have a collection of examples and tutorials that you can deploy as they are or use them as a foundation for your own applications and infrastructure projects. +You'll be prompted to confirm the removal. Confirm it to successfully complete this tutorial. {{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/azure/modify-program.md b/content/docs/iac/get-started/azure/modify-program.md index 9d951c669d08..8b0fb0605599 100644 --- a/content/docs/iac/get-started/azure/modify-program.md +++ b/content/docs/iac/get-started/azure/modify-program.md @@ -1,21 +1,40 @@ --- -title_tag: Modify the Program | Azure +title_tag: Make an Update | Azure +title: Make an update +h1: "Get started with Pulumi and Azure" meta_desc: This page provides an overview on how to update an Azure project from a Pulumi program. -title: Modify program -h1: "Pulumi & Azure: Modify program" weight: 6 menu: iac: - name: Modify program + name: Make an update identifier: azure-get-started.modify-program parent: azure-get-started weight: 6 aliases: - /docs/quickstart/azure/modify-program/ - /docs/clouds/azure/get-started/modify-program/ + - /docs/quickstart/azure/deploy-changes/ + - /docs/clouds/azure/get-started/deploy-changes/ --- -Now that your storage account is provisioned, let's add an object to it. First, from within your project directory, create a new `index.html` file with some content in it. +## Make an update + +Now you will update your project to serve a static website out of your Azure storage account. You will change your code and then re-run `pulumi up` which will update your infrastructure. + +### Add new resources + +Pulumi knows how to evolve your current infrastructure to your project's new desired state, both for the first deployment as well as subsequent updates. + +To turn your storage account into a static website, you will add two new Azure resources: + +1. [`StorageAccountStaticWebsite`](/registry/packages/azure-native/api-docs/storage/storageaccountstaticwebsite/): + enables static website support on your storage account +2. [`Blob`](/registry/packages/azure-native/api-docs/storage/blob/): + uploads your website content to the storage container + +### Add an index.html + +First, from within your project directory, create a new `index.html` file with some content in it. {{< chooser os "macos,linux,windows" / >}} @@ -61,16 +80,17 @@ EOT {{% /choosable %}} -Now that you have your new `index.html` with some content, you can enable static website support, upload `index.html` to a storage container, and retrieve a public URL through the use of resource properties. These properties can be used to define dependencies between related resources or to retrieve property values for further processing. - -{{< chooser language "typescript,python,go,csharp,java,yaml" / >}} +Now open {{< langfile >}} in your editor and enable static website support by adding a [`StorageAccountStaticWebsite`](/registry/packages/azure-native/api-docs/storage/storageaccountstaticwebsite/) resource right after the storage account is created: {{% choosable language typescript %}} -To start, open `index.ts` and add the following right after the storage account creation: - ```typescript -// Enable static website support +// Create an Azure resource (Storage Account) +const storageAccount = new storage.StorageAccount("sa", { + /* existing storage account configuration */ +}); + +// Enable static website support - add this code const staticWebsite = new storage.StorageAccountStaticWebsite("staticWebsite", { accountName: storageAccount.name, resourceGroupName: resourceGroup.name, @@ -81,10 +101,14 @@ const staticWebsite = new storage.StorageAccountStaticWebsite("staticWebsite", { {{% /choosable %}} {{% choosable language python %}} -To start, open `__main__.py` and add the following right after the storage account creation: - ```python -# Enable static website support +# Create an Azure resource (Storage Account) +account = storage.StorageAccount( + "sa", + # existing storage account configuration +) + +# Enable static website support - add this code static_website = storage.StorageAccountStaticWebsite( "staticWebsite", account_name=account.name, @@ -96,10 +120,16 @@ static_website = storage.StorageAccountStaticWebsite( {{% /choosable %}} {{% choosable language go %}} -To start, open `main.go` and add the following right after the storage account creation: - ```go -// Enable static website support +// Create an Azure resource (Storage Account) +account, err := storage.NewStorageAccount(ctx, "sa", &storage.StorageAccountArgs{ + // existing storage account configuration +}) +if err != nil { + return err +} + +// Enable static website support - add this code staticWebsite, err := storage.NewStorageAccountStaticWebsite(ctx, "staticWebsite", &storage.StorageAccountStaticWebsiteArgs{ AccountName: account.Name, ResourceGroupName: resourceGroup.Name, @@ -113,10 +143,14 @@ if err != nil { {{% /choosable %}} {{% choosable language csharp %}} -To start, open `Program.cs` and add the following right after the storage account creation: - ```csharp -// Enable static website support +// Create an Azure resource (Storage Account) +var storageAccount = new StorageAccount("sa", new StorageAccountArgs +{ + /* existing storage account configuration */ +}); + +// Enable static website support - add this code var staticWebsite = new StorageAccountStaticWebsite("staticWebsite", new StorageAccountStaticWebsiteArgs { AccountName = storageAccount.Name, @@ -129,7 +163,7 @@ var staticWebsite = new StorageAccountStaticWebsite("staticWebsite", new Storage {{% choosable language java %}} -To start, open `App.java` and add the following imports: +First, add the following imports at the top of `App.java`: ```java import com.pulumi.azurenative.storage.StorageAccountStaticWebsite; @@ -140,9 +174,15 @@ import com.pulumi.azurenative.storage.outputs.EndpointsResponse; import com.pulumi.asset.FileAsset; ``` -Next, add the following right after the storage account creation: +Then add the following right after the storage account creation: ```java +// Create an Azure resource (Storage Account) +var storageAccount = new StorageAccount("sa", StorageAccountArgs.builder() + // existing storage account configuration + .build()); + +// Enable static website support - add this code var staticWebsite = new StorageAccountStaticWebsite("staticWebsite", StorageAccountStaticWebsiteArgs.builder() .accountName(storageAccount.name()) @@ -155,11 +195,14 @@ var staticWebsite = new StorageAccountStaticWebsite("staticWebsite", {{% choosable language yaml %}} -To start, open `Pulumi.yaml` and add the following right after the storage account creation: - ```yaml resources: - # ... + # Create an Azure resource (Storage Account) + sa: + type: azure-native:storage:StorageAccount + # existing storage account configuration + + # Enable static website support - add this code staticWebsite: type: azure-native:storage:StorageAccountStaticWebsite properties: @@ -170,9 +213,10 @@ resources: {{% /choosable %}} -The static website resource leverages the storage account and resource group names defined previously in your program. +Notice that resources can reference each other, which forms automatic dependencies between them. +Pulumi uses this information to parallelize deployments safely. -Now use all of these cloud resources and a local `FileAsset` resource to upload `index.html` into your storage container by adding the following at the end of the file (after enabling the static website support): +Now use all of these cloud resources and a local `FileAsset` resource to upload `index.html` into your storage container by adding a [`Blob`](/registry/packages/azure-native/api-docs/storage/blob/) at the end of the file (after enabling the static website support): {{% choosable language typescript %}} ```typescript @@ -272,11 +316,19 @@ resources: {{% /choosable %}} -{{% choosable language typescript %}} +This uploads the `index.html` file to your storage container using a Pulumi concept called an [asset](/docs/iac/concepts/assets-archives/#assets). + +### Export the website URL + +Now to export the website's URL for easy access, add the `staticEndpoint` export to your return statement as shown in this example: -Finally, at the end of `index.ts`, export the resulting storage container's endpoint URL to stdout for easy access: +{{% choosable language typescript %}} ```typescript +// Export the primary key of the Storage Account +export const primaryStorageKey = pulumi.secret(storageAccountKeys.keys[0].value); +export const storageAccountName = storageAccount.name; + // Web endpoint to the website export const staticEndpoint = storageAccount.primaryEndpoints.web; ``` @@ -285,9 +337,11 @@ export const staticEndpoint = storageAccount.primaryEndpoints.web; {{% choosable language python %}} -Finally, at the end of `__main__.py`, export the resulting storage container's endpoint URL to stdout for easy access: - ```python +# Export the primary key of the Storage Account +pulumi.export("primary_storage_key", primary_key) +pulumi.export("storage_account_name", account.name) + # Web endpoint to the website pulumi.export("staticEndpoint", account.primary_endpoints.web) ``` @@ -296,9 +350,25 @@ pulumi.export("staticEndpoint", account.primary_endpoints.web) {{% choosable language go %}} -Finally, at the end of `main.go`, export the resulting storage container's endpoint URL to stdout for easy access: - ```go +// Export the primary key of the Storage Account +ctx.Export("primaryStorageKey", pulumi.All(resourceGroup.Name, account.Name).ApplyT( + func(args []interface{}) (string, error) { + resourceGroupName := args[0].(string) + accountName := args[1].(string) + accountKeys, err := storage.ListStorageAccountKeys(ctx, &storage.ListStorageAccountKeysArgs{ + ResourceGroupName: resourceGroupName, + AccountName: accountName, + }) + if err != nil { + return "", err + } + + return accountKeys.Keys[0].Value, nil + }, +)) +ctx.Export("storageAccountName", account.Name) + // Web endpoint to the website ctx.Export("staticEndpoint", account.PrimaryEndpoints.Web()) ``` @@ -307,13 +377,12 @@ ctx.Export("staticEndpoint", account.PrimaryEndpoints.Web()) {{% choosable language csharp %}} -Finally, at the end of `Program.cs`, export the resulting storage container's endpoint URL to stdout for easy access: - ```csharp -// Web endpoint to the website +// Export outputs return new Dictionary { ["primaryStorageKey"] = primaryStorageKey, + ["storageAccountName"] = storageAccount.Name, ["staticEndpoint"] = storageAccount.PrimaryEndpoints.Apply(primaryEndpoints => primaryEndpoints.Web) }; ``` @@ -322,9 +391,12 @@ return new Dictionary {{% choosable language java %}} -Finally, at the end of `App.java`, export the resulting storage container's endpoint URL to stdout for easy access: - ```java +// Export the primary key of the Storage Account +ctx.export("primaryStorageKey", primaryStorageKey); +ctx.export("storageAccountName", storageAccount.name()); + +// Web endpoint to the website ctx.export("staticEndpoint", storageAccount.primaryEndpoints() .applyValue(EndpointsResponse::web)); ``` @@ -333,16 +405,115 @@ ctx.export("staticEndpoint", storageAccount.primaryEndpoints() {{% choosable language yaml %}} -Finally, at the end of `Pulumi.yaml` in the `outputs`, export the resulting storage container's endpoint URL to stdout for easy access: - ```yaml outputs: - # ... + # Export the primary key of the Storage Account + primaryStorageKey: ${storageAccountKeys.keys[0].value} + storageAccountName: ${sa.name} + + # Web endpoint to the website staticEndpoint: ${sa.primaryEndpoints.web} ``` {{% /choosable %}} -Now that you have declared how you want your resources to be provisioned, it is time to deploy these remaining changes. +The storage account's endpoint is [an output property](/docs/iac/concepts/inputs-outputs/#outputs) +that Azure assigns at deployment time, not a raw string, meaning its value is not known in advance. + +### Deploy the changes + +To deploy the changes, run `pulumi up` again and it will figure out the deltas: + +{{% choosable "os" "macos,linux" %}} + +```bash +$ pulumi up +``` + +{{% /choosable %}} +{{% choosable "os" "windows" %}} + +```powershell +> pulumi up +``` + +{{% /choosable %}} + +Just like the first time you will see a preview of the changes before they happen: + +``` +Previewing update (dev): + + Type Name Plan + pulumi:pulumi:Stack quickstart-dev + + ├─ azure-native:storage:StorageAccountStaticWebsite staticWebsite create + + └─ azure-native:storage:Blob index.html create + +Outputs: + + staticEndpoint : "https://sa8dd8af62.z22.web.core.windows.net/" + +Resources: + + 2 to create + 3 unchanged + +Do you want to perform this update? +> yes + no + details +``` + +Choose `yes` to perform the deployment: + +``` +Do you want to perform this update? yes +Updating (dev): + + Type Name Status + pulumi:pulumi:Stack quickstart-dev + + ├─ azure-native:storage:StorageAccountStaticWebsite staticWebsite created + + └─ azure-native:storage:Blob index.html created + +Outputs: + primaryStorageKey: "" + + staticEndpoint : "https://sa8dd8af62.z22.web.core.windows.net/" + +Resources: + + 2 created + 3 unchanged + +Duration: 4s +``` + +In just a few seconds, your new website will be ready. Curl the endpoint to see it live: + +{{% choosable os "linux,macos" %}} + +```bash +$ curl $(pulumi stack output staticEndpoint) +``` + +{{% /choosable %}} + +{{% choosable os "windows" %}} + +```powershell +> curl (pulumi stack output staticEndpoint) +``` + +{{% /choosable %}} + +This will reveal your new website! + +``` + + +

Hello, Pulumi!

+ + +``` + +Feel free to experiment, such as changing the contents of `index.html` and redeploying. + +Next, let's wrap this website up into an infrastructure abstraction. {{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/azure/next-steps.md b/content/docs/iac/get-started/azure/next-steps.md index f7f6284b2b42..df25b94f43da 100644 --- a/content/docs/iac/get-started/azure/next-steps.md +++ b/content/docs/iac/get-started/azure/next-steps.md @@ -1,9 +1,10 @@ --- title_tag: Next Steps | Azure +title: Next steps +h1: Next Steps with Pulumi & Azure +stepper_link: "Congratulations!" meta_desc: This page provides a list of tutorials that take a deeper dive into Azure cloud resources. -title: Next steps -h1: "Pulumi & Azure: Next steps" weight: 9 menu: iac: @@ -16,7 +17,15 @@ aliases: - /docs/clouds/azure/get-started/next-steps/ --- -Congrats! You've deployed your first project on Microsoft Azure with Pulumi. Here are some next steps, depending on your learning style. +Congratulations! You've successfully provisioned some cloud resources using Pulumi. By completing this guide you have successfully: + +- Created a Pulumi new project. +- Provisioned a new Azure Storage account. +- Enabled the static website feature on the storage account. +- Created a website component for easy reuse. +- Destroyed all of the resources you've provisioned. + +Below are some recommended next steps, including examples and tutorials that you can explore or use them as a foundation for your own applications and infrastructure projects. Also be sure to [join the Community Slack](https://slack.pulumi.com/) to meet fellow IaC practitioners. ## Try Pulumi ESC (Environments, Secrets, and Configuration) diff --git a/content/docs/iac/get-started/azure/review-project.md b/content/docs/iac/get-started/azure/review-project.md deleted file mode 100644 index e950259f148a..000000000000 --- a/content/docs/iac/get-started/azure/review-project.md +++ /dev/null @@ -1,317 +0,0 @@ ---- -title_tag: Review the New Project | Azure -meta_desc: This page provides an overview on how to a review a new Azure project. -title: Review project -h1: "Pulumi & Azure: Review project" -weight: 4 -menu: - iac: - name: Review project - identifier: azure-get-started.review-project - parent: azure-get-started - weight: 4 - -aliases: - - /docs/quickstart/azure/review-project/ - - /docs/clouds/azure/get-started/review-project/ ---- - -Let's review some of the generated project files: - -{{% choosable language "typescript,python,go,csharp,java" %}} - -- `Pulumi.yaml` defines the [project](/docs/concepts/projects/). - -{{% /choosable %}} - -{{% choosable language yaml %}} - -- `Pulumi.yaml` defines both the [project](/docs/concepts/projects/) and the program that manages your stack resources. - -{{% /choosable %}} - -- `Pulumi.dev.yaml` contains [configuration](/docs/concepts/config/) values for the [stack](/docs/concepts/stack/) you initialized. - -{{% choosable language java %}} - -- `src/main/java/myproject` defines the project's Java package root. - -{{% /choosable %}} - -{{% choosable language python %}} - -- `__main__.py` is the Pulumi program that defines your stack resources. - -{{% /choosable %}} - -{{% choosable language "typescript,go,csharp,java" %}} - -- {{< langfile >}} is the Pulumi program that defines your stack resources. - -{{% /choosable %}} - -Let's examine {{< langfile >}}. - -{{< chooser language "typescript,python,go,csharp,java,yaml" / >}} - -{{% choosable language typescript %}} - -```typescript -import * as pulumi from "@pulumi/pulumi"; -import * as resources from "@pulumi/azure-native/resources"; -import * as storage from "@pulumi/azure-native/storage"; - -// Create an Azure Resource Group -const resourceGroup = new resources.ResourceGroup("resourceGroup"); - -// Create an Azure resource (Storage Account) -const storageAccount = new storage.StorageAccount("sa", { - resourceGroupName: resourceGroup.name, - sku: { - name: storage.SkuName.Standard_LRS, - }, - kind: storage.Kind.StorageV2, -}); - -// Export the primary key of the Storage Account -const storageAccountKeys = storage.listStorageAccountKeysOutput({ - resourceGroupName: resourceGroup.name, - accountName: storageAccount.name -}); - -export const primaryStorageKey = storageAccountKeys.keys[0].value; -``` - -{{% /choosable %}} -{{% choosable language python %}} - -```python -"""An Azure RM Python Pulumi program""" - -import pulumi -from pulumi_azure_native import storage -from pulumi_azure_native import resources - -# Create an Azure Resource Group -resource_group = resources.ResourceGroup("resource_group") - -# Create an Azure resource (Storage Account) -account = storage.StorageAccount( - "sa", - resource_group_name=resource_group.name, - sku={ - "name": storage.SkuName.STANDARD_LRS, - }, - kind=storage.Kind.STORAGE_V2, -) - -# Export the primary key of the Storage Account -primary_key = ( - pulumi.Output.all(resource_group.name, account.name) - .apply( - lambda args: storage.list_storage_account_keys( - resource_group_name=args[0], account_name=args[1] - ) - ) - .apply(lambda accountKeys: accountKeys.keys[0].value) -) - -pulumi.export("primary_storage_key", primary_key) -``` - -{{% /choosable %}} -{{% choosable language go %}} - -```go -package main - -import ( - "github.com/pulumi/pulumi-azure-native-sdk/resources/v2" - "github.com/pulumi/pulumi-azure-native-sdk/storage/v2" - "github.com/pulumi/pulumi/sdk/v3/go/pulumi" -) - -func main() { - pulumi.Run(func(ctx *pulumi.Context) error { - // Create an Azure Resource Group - resourceGroup, err := resources.NewResourceGroup(ctx, "resourceGroup", nil) - if err != nil { - return err - } - - // Create an Azure resource (Storage Account) - account, err := storage.NewStorageAccount(ctx, "sa", &storage.StorageAccountArgs{ - ResourceGroupName: resourceGroup.Name, - AccessTier: storage.AccessTierHot, - Sku: &storage.SkuArgs{ - Name: storage.SkuName_Standard_LRS, - }, - Kind: storage.KindStorageV2, - }) - if err != nil { - return err - } - - // Export the primary key of the Storage Account - ctx.Export("primaryStorageKey", pulumi.All(resourceGroup.Name, account.Name).ApplyT( - func(args []interface{}) (string, error) { - resourceGroupName := args[0].(string) - accountName := args[1].(string) - accountKeys, err := storage.ListStorageAccountKeys(ctx, &storage.ListStorageAccountKeysArgs{ - ResourceGroupName: resourceGroupName, - AccountName: accountName, - }) - if err != nil { - return "", err - } - - return accountKeys.Keys[0].Value, nil - }, - )) - - return nil - }) -} -``` - -{{% /choosable %}} -{{% choosable language csharp %}} - -```csharp -using Pulumi; -using Pulumi.AzureNative.Resources; -using Pulumi.AzureNative.Storage; -using Pulumi.AzureNative.Storage.Inputs; -using System.Collections.Generic; - -return await Pulumi.Deployment.RunAsync(() => -{ - // Create an Azure Resource Group - var resourceGroup = new ResourceGroup("resourceGroup"); - - // Create an Azure resource (Storage Account) - var storageAccount = new StorageAccount("sa", new StorageAccountArgs - { - ResourceGroupName = resourceGroup.Name, - Sku = new SkuArgs - { - Name = SkuName.Standard_LRS - }, - Kind = Kind.StorageV2 - }); - - var storageAccountKeys = ListStorageAccountKeys.Invoke(new ListStorageAccountKeysInvokeArgs - { - ResourceGroupName = resourceGroup.Name, - AccountName = storageAccount.Name - }); - - var primaryStorageKey = storageAccountKeys.Apply(accountKeys => - { - var firstKey = accountKeys.Keys[0].Value; - return Output.CreateSecret(firstKey); - }); - - // Export the primary key of the Storage Account - return new Dictionary - { - ["primaryStorageKey"] = primaryStorageKey - }; -}); -``` - -{{% /choosable %}} - -{{% choosable language java %}} - -```java -package myproject; - -import com.pulumi.Pulumi; -import com.pulumi.azurenative.resources.ResourceGroup; -import com.pulumi.azurenative.storage.StorageAccount; -import com.pulumi.azurenative.storage.StorageAccountArgs; -import com.pulumi.azurenative.storage.StorageFunctions; -import com.pulumi.azurenative.storage.enums.Kind; -import com.pulumi.azurenative.storage.enums.SkuName; -import com.pulumi.azurenative.storage.inputs.ListStorageAccountKeysArgs; -import com.pulumi.azurenative.storage.inputs.SkuArgs; -import com.pulumi.core.Either; -import com.pulumi.core.Output; -import com.pulumi.deployment.InvokeOptions; - -public class App { - public static void main(String[] args) { - Pulumi.run(ctx -> { - var resourceGroup = new ResourceGroup("resourceGroup"); - var storageAccount = new StorageAccount("sa", StorageAccountArgs.builder() - .resourceGroupName(resourceGroup.name()) - .sku(SkuArgs.builder() - .name(SkuName.Standard_LRS) - .build()) - .kind(Kind.StorageV2) - .build()); - - var primaryStorageKey = getStorageAccountPrimaryKey( - resourceGroup.name(), - storageAccount.name()); - - ctx.export("primaryStorageKey", primaryStorageKey); - }); - } - - private static Output getStorageAccountPrimaryKey(Output resourceGroupName, - Output accountName) { - return Output.tuple(resourceGroupName, accountName).apply(tuple -> { - var actualResourceGroupName = tuple.t1; - var actualAccountName = tuple.t2; - var invokeResult = StorageFunctions.listStorageAccountKeys(ListStorageAccountKeysArgs.builder() - .resourceGroupName(actualResourceGroupName) - .accountName(actualAccountName) - .build(), InvokeOptions.Empty); - return Output.of(invokeResult) - .applyValue(r -> r.keys().get(0).value()) - .asSecret(); - }); - } -} -``` - -{{% /choosable %}} - -{{% choosable language yaml %}} - -```yaml -name: quickstart -runtime: yaml -description: A minimal Azure Native Pulumi YAML program -resources: - resourceGroup: - type: azure-native:resources:ResourceGroup - sa: - type: azure-native:storage:StorageAccount - properties: - resourceGroupName: ${resourceGroup.name} - sku: - name: Standard_LRS - kind: StorageV2 -variables: - storageAccountKeys: - fn::azure-native:storage:listStorageAccountKeys: - resourceGroupName: ${resourceGroup.name} - accountName: ${sa.name} -outputs: - primaryStorageKey: ${storageAccountKeys.keys[0].value} -``` - -{{% /choosable %}} - -This Pulumi program creates an Azure resource group and storage account and then exports the storage account's primary key. - -{{% notes %}} -In this program, the location of the resource group is set in the configuration setting `azure-native:location` (check the `Pulumi.dev.yaml` file). This is an easy way to set a global location for your program so you don't have to specify the location for each resource manually. The location for the storage account is automatically derived from the location of the resource group. To override the location for a resource, set the location property to one of Azure's [supported locations](https://azure.microsoft.com/en-us/global-infrastructure/locations/). -{{% /notes %}} - -Next, you'll deploy your stack, which will provision a resource group and your storage account. - -{{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/gcp/_index.md b/content/docs/iac/get-started/gcp/_index.md index 8c8b05a52f5a..30b2245e3d2e 100644 --- a/content/docs/iac/get-started/gcp/_index.md +++ b/content/docs/iac/get-started/gcp/_index.md @@ -1,25 +1,65 @@ --- -title_tag: Get Started with Google Cloud -meta_desc: This page provides an overview and guide on how to get started with Google Cloud. title: Google Cloud -h1: Get started with Pulumi & Google Cloud +title_tag: Get started with Pulumi and Google Cloud +h1: Get started with Pulumi and Google Cloud +meta_desc: This page provides an overview and guide on how to get started with Google Cloud. menu: iac: name: Google Cloud identifier: gcp-get-started parent: iac-get-started weight: 3 - clouds: - parent: google-cloud - identifier: google-cloud-get-started - weight: 3 + aliases: - - /docs/get-started/gcp/ - /docs/quickstart/gcp/ - - /docs/clouds/get-started/gcp - /docs/clouds/gcp/get-started/ --- -{{< cloud-intro "Google Cloud" >}} +**Infrastructure as code (IaC)** lets you deploy, change, and manage infrastructure safely, consistently, +and repeatably using code rather than a graphical user interface. + +Complete this step-by-step tutorial to deploy a Google Cloud Storage-based website using IaC. + +## Before you begin + +Make sure you have the gcloud CLI installed and authenticated. Then choose your language and ensure you've performed any prerequisites: + +{{< chooser language "typescript,python,go,csharp,java,yaml" / >}} + +{{% choosable language "typescript" %}} + +* Node.js and npm installed locally + +{{% /choosable %}} + +{{% choosable language "python" %}} + +* Python and pip, Poetry or uv installed locally + +{{% /choosable %}} + +{{% choosable language "go" %}} + +* Go installed locally + +{{% /choosable %}} + +{{% choosable language "csharp" %}} + +* .NET installed locally + +{{% /choosable %}} + +{{% choosable language "java" %}} + +* Java 11+ and Maven 3.6.1+ installed locally + +{{% /choosable %}} + +{{% choosable language "yaml" %}} + +* A text editor + +{{% /choosable %}} {{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/gcp/begin.md b/content/docs/iac/get-started/gcp/begin.md index 07046fab58df..e8e0cd60ae9a 100644 --- a/content/docs/iac/get-started/gcp/begin.md +++ b/content/docs/iac/get-started/gcp/begin.md @@ -1,8 +1,9 @@ --- -title_tag: Before You Begin | Google Cloud -meta_desc: This page provides an overview on how to get started with Pulumi and Google Cloud. -title: Before you begin -h1: "Pulumi & Google Cloud: Before you begin" +title_tag: Install Pulumi | Google Cloud +title: Install Pulumi +h1: "Get started with Pulumi and Google Cloud" +stepper_link: "I'm ready to begin" +meta_desc: This page provides an overview on how to get started with Pulumi when starting a Google Cloud project. weight: 2 menu: iac: @@ -10,80 +11,19 @@ menu: identifier: gcp-get-started.begin parent: gcp-get-started weight: 2 + aliases: - - /docs/quickstart/gcp/begin/ - - /docs/quickstart/gcp/install-pulumi/ - - /docs/quickstart/gcp/install-language-runtime/ - - /docs/quickstart/gcp/configure/ - - /docs/clouds/gcp/get-started/begin/ +- /docs/quickstart/gcp/begin/ +- /docs/quickstart/gcp/install-pulumi/ +- /docs/quickstart/gcp/install-language-runtime/ +- /docs/quickstart/gcp/configure/ +- /docs/get-started/gcp/install-pulumi/ +- /docs/get-started/gcp/install-language-runtime/ +- /docs/get-started/gcp/configure/ +- /docs/get-started/gcp/begin/ +- /docs/clouds/gcp/get-started/begin/ --- -Before you get started using Pulumi, let's run through a few quick steps to ensure your environment is set up correctly. - -### Install Pulumi - -{{< install-pulumi >}} -{{% notes "info" %}} -All Windows examples in this tutorial assume you are running in PowerShell. -{{% /notes %}} -{{< /install-pulumi >}} - -Next, install the required language runtime, if you have not already. - -### Install Language Runtime - -#### Choose Your Language - -{{< chooser language "typescript,python,go,csharp,java,yaml" / >}} - -{{% choosable language "typescript" %}} -{{< install-node >}} -{{% /choosable %}} - -{{% choosable language python %}} -{{< install-python >}} -{{% /choosable %}} - -{{% choosable language go %}} -{{< install-go >}} -{{% /choosable %}} - -{{% choosable language "csharp,fsharp,visualbasic" %}} -{{< install-dotnet >}} -{{% /choosable %}} - -{{% choosable language java %}} -{{< install-java >}} -{{% /choosable %}} - -{{% choosable language yaml %}} -{{< install-yaml >}} -{{% /choosable %}} - -Finally, configure Pulumi with Google Cloud. - -### Configure Pulumi to access your Google Cloud account - -Pulumi requires cloud credentials to manage and provision resources. You must use an IAM user or service account that has **Programmatic access** with rights to deploy and manage your Google Cloud resources. - -In this guide, you will need an IAM user account with permissions that can create and populate a Cloud Storage bucket, such as those in the predefined Storage Admin (`roles/storage.admin`) or the Storage Legacy Bucket Owner (`roles/storage.legacyBucketOwner`) roles. - -When developing locally, we recommend that you install the [Google Cloud SDK](https://cloud.google.com/sdk/install) and then [authorize access with a user account](https://cloud.google.com/sdk/docs/authorizing#authorizing_with_a_user_account). - -If `gcloud` is not configured to interact with your Google Cloud project, set it with the `config` command using the project's ID: - -```bash -gcloud config set project -``` - -Next, Pulumi requires default application credentials to interact with your Google Cloud resources, so run `auth application-default login` command to obtain those credentials. - -```bash -gcloud auth application-default login -``` - -For additional information on setting and using Google Cloud credentials, see [Google Cloud Setup](/registry/packages/gcp/installation-configuration/). - -Next, you'll create a new Pulumi project. +{{< get-started-install-body >}} {{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/gcp/create-component.md b/content/docs/iac/get-started/gcp/create-component.md new file mode 100644 index 000000000000..72e49a454db5 --- /dev/null +++ b/content/docs/iac/get-started/gcp/create-component.md @@ -0,0 +1,716 @@ +--- +title_tag: Create a component | Google Cloud +title: Create a component +h1: "Get started with Pulumi and Google Cloud" +meta_desc: This page provides an overview on how to create infrastructure abstractions with Pulumi. +weight: 7 +menu: + iac: + name: Create a component + identifier: gcp-get-started.create-component + parent: gcp-get-started + weight: 7 + +aliases: + - /docs/quickstart/gcp/create-component/ + - /docs/clouds/gcp/get-started/create-component/ +--- + +## Create a component + +[**Components**](/docs/iac/concepts/resources/components/) are infrastructure abstractions that encapsulate +complexity and enable sharing and reuse. Instead of copy-pasting common patterns, you can encode them as components. + +You will now create your first component that packages up your Google Cloud Storage website so you can easily stamp out +entire websites in just a few lines of code: + +{{% choosable language typescript %}} + +```typescript +const website = new GcpStorageWebsite("my-website", { + files: [ "index.html" ], +}); +``` + +{{% /choosable %}} + +{{% choosable language python %}} + +```python +website = GcpStorageWebsite('my-website', files=['index.html']) +``` + +{{% /choosable %}} + +{{% choosable language go %}} + +```go +website, err := NewGcpStorageWebsite(ctx, "my-website", GcpStorageWebsiteArgs{ + Files: []string{"index.html"}, +}) +if err != nil { + return err +} +``` + +{{% /choosable %}} + +{{% choosable language csharp %}} + +```csharp +var website = new GcpStorageWebsite("my-website", new GcpStorageWebsiteArgs() +{ + Files = new[] { "index.html" } +}); +``` + +{{% /choosable %}} + +{{% choosable language java %}} + +```java +var website = new GcpStorageWebsite("my-website", + new GcpStorageWebsiteArgs(new String[] { "index.html" })); +``` + +{{% /choosable %}} + +{{% choosable language yaml %}} + +{{% notes type="warning" %}} + +Unfortunately, YAML lacks the language facilities to author components. Feel free to [skip ahead](/docs/iac/get-started/gcp/destroy-stack/). + +{{% /notes %}} + +{{% /choosable %}} + +Using components here also has the benefit that, as the requirements for your static website change, you can +update the one component definition and have all uses of it benefit. + +### Define a new component + +To define a new component, create a class called `GcpStorageWebsite` that derives from `ComponentResource`. It'll have a mostly-empty +constructor to start with but you will add the Google Cloud Storage resources to it in the next step. You'll also define the inputs for the +component -- the `files` to add to the website -- and outputs -- a single property with the website `url`. + +To get going, create a new file {{< compfile >}} alongside {{< langfile >}} and add the following: + +{{% choosable language typescript %}} + +```typescript +import * as gcp from "@pulumi/gcp"; +import * as pulumi from "@pulumi/pulumi"; + +// Arguments for the Google Cloud Storage static website component. +export interface GcpStorageWebsiteArgs { + files: string[]; // a list of files to serve. +} + +// A component that encapsulates creating a Google Cloud Storage hosted static website. +export class GcpStorageWebsite extends pulumi.ComponentResource { + public readonly url: pulumi.Output; // the website url. + + constructor(name: string, args: GcpStorageWebsiteArgs, opts?: pulumi.ComponentResourceOptions) { + super("quickstart:index:GcpStorageWebsite", name, args, opts); + + // Component initialization will go here next... + + this.registerOutputs({}); // Signal component completion. + } +} +``` + +{{% /choosable %}} + +{{% choosable language python %}} + +```python +import pulumi +from pulumi_gcp import storage +from typing import List + +# A component that encapsulates creating a Google Cloud Storage hosted static website. +class GcpStorageWebsite(pulumi.ComponentResource): + def __init__(self, name: str, files: List[str] = None, opts = None): + super().__init__('quickstart:index:GcpStorageWebsite', name, { 'files': files }, opts) + + # Component initialization will go here next... + + self.register_outputs({}) # Signal component completion. +``` + +{{% /choosable %}} + +{{% choosable language go %}} + +```go +package main + +import ( + "github.com/pulumi/pulumi-gcp/sdk/v7/go/gcp/storage" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +type GcpStorageWebsite struct { + pulumi.ResourceState + Url pulumi.StringOutput // the website url. +} + +type GcpStorageWebsiteArgs struct { + Files []string // a list of files to serve. +} + +func NewGcpStorageWebsite(ctx *pulumi.Context, name string, args GcpStorageWebsiteArgs, opts ...pulumi.ResourceOption) (*GcpStorageWebsite, error) { + self := &GcpStorageWebsite{} + err := ctx.RegisterComponentResource("quickstart:index:GcpStorageWebsite", name, self, opts...) + if err != nil { + return nil, err + } + + // Component initialization will go here next... + + ctx.RegisterResourceOutputs(self, pulumi.Map{}) // Signal component completion. + return self, nil +} +``` + +{{% /choosable %}} + +{{% choosable language csharp %}} + +```csharp +using Pulumi; +using Pulumi.Gcp.Storage; +using Pulumi.Gcp.Storage.Inputs; +using System.Collections.Generic; + +public class GcpStorageWebsiteArgs +{ + public string[]? Files { get; set; } +} + +public class GcpStorageWebsite : Pulumi.ComponentResource +{ + public Output Url { get; private set; } + + public GcpStorageWebsite(string name, GcpStorageWebsiteArgs args, ComponentResourceOptions? opts = null) + : base("quickstart:index:GcpStorageWebsite", name, opts) + { + // Component initialization will go here next... + + this.RegisterOutputs(new Dictionary{}); // Signal component completion. + } +} +``` + +{{% /choosable %}} + +{{% choosable language java %}} + +```java +package myproject; + +import com.pulumi.resources.ComponentResource; +import com.pulumi.resources.ComponentResourceOptions; +import com.pulumi.core.Output; + +public class GcpStorageWebsiteArgs { + public String[] files; + public GcpStorageWebsiteArgs(String[] files) { + this.files = files; + } +} + +public class GcpStorageWebsite extends ComponentResource { + public Output url; + + public GcpStorageWebsite(String name, GcpStorageWebsiteArgs args, ComponentResourceOptions opts) { + super("quickstart:index:GcpStorageWebsite", name, args, opts); + + // Component initialization will go here next... + + this.registerOutputs(java.util.Map.of()); + } +} +``` + +{{% /choosable %}} + +{{% choosable language yaml %}} + +{{% notes type="warning" %}} + +Unfortunately, YAML lacks the language facilities to author components. Feel free to [skip ahead](/docs/iac/get-started/gcp/destroy-stack/). + +{{% /notes %}} + +{{% /choosable %}} + +This defines a component but it doesn't do much yet. + +### Refactor your code into the component + +Next, make four changes: + +1. Move all resources from {{< langfile >}} into the component's constructor +2. Change each resource to use the component [as the `parent`](/docs/iac/concepts/options/parent/) +3. Generalize the creation of bucket objects by looping over the list of `files` +4. Assign the resulting website URL to the `url` property of the component + +The resulting {{< compfile >}} file will look like this; feel free to make each edit one at a time if you'd like +to get a feel for things, or simply paste the contents of this into {{< compfile >}}: + +{{% choosable language typescript %}} + +```typescript +import * as gcp from "@pulumi/gcp"; +import * as pulumi from "@pulumi/pulumi"; + +// Arguments for the Google Cloud Storage static website component. +export interface GcpStorageWebsiteArgs { + files: string[]; // a list of files to serve. +} + +// A component that encapsulates creating a Google Cloud Storage hosted static website. +export class GcpStorageWebsite extends pulumi.ComponentResource { + public readonly url: pulumi.Output; // the website url. + + constructor(name: string, args: GcpStorageWebsiteArgs, opts?: pulumi.ComponentResourceOptions) { + super("quickstart:index:GcpStorageWebsite", name, args, opts); + + // Create a Google Cloud resource (Storage Bucket) configured for website hosting + const bucket = new gcp.storage.Bucket("my-bucket", { + location: "US", + website: { + mainPageSuffix: "index.html", + }, + uniformBucketLevelAccess: true, + }, { + parent: this, + }); + + // Create a Bucket object for each file + for (const file of args.files) { + new gcp.storage.BucketObject(file, { + bucket: bucket.name, + source: new pulumi.asset.FileAsset(file), + }, { parent: this }); + } + + // Allow public access to the objects + new gcp.storage.BucketIAMBinding("my-bucket-binding", { + bucket: bucket.name, + role: "roles/storage.objectViewer", + members: ["allUsers"], + }, { parent: this }); + + // Capture the URL and make it available as a component property and output: + this.url = pulumi.concat("http://storage.googleapis.com/", bucket.name, "/", args.files[0]); + this.registerOutputs({ url: this.url }); // Signal component completion. + } +} +``` + +{{% /choosable %}} + +{{% choosable language python %}} + +```python +import pulumi +from pulumi_gcp import storage +from typing import List + +# A component that encapsulates creating a Google Cloud Storage hosted static website. +class GcpStorageWebsite(pulumi.ComponentResource): + def __init__(self, name: str, files: List[str] = None, opts = None): + super().__init__('quickstart:index:GcpStorageWebsite', name, { 'files': files }, opts) + + # Create a Google Cloud resource (Storage Bucket) configured for website hosting + bucket = storage.Bucket( + 'my-bucket', + location="US", + website=\{ + "main_page_suffix": "index.html" + \}, + uniform_bucket_level_access=True, + opts=pulumi.ResourceOptions(parent=self), + ) + + # Create a Bucket object for each file + for file in files: + storage.BucketObject( + file, + bucket=bucket.name, + source=pulumi.FileAsset(file), + opts=pulumi.ResourceOptions(parent=self), + ) + + # Allow public access to the objects + storage.BucketIAMBinding( + "my-bucket-binding", + bucket=bucket.name, + role="roles/storage.objectViewer", + members=["allUsers"], + opts=pulumi.ResourceOptions(parent=self), + ) + + # Capture the URL and make it available as a component property and output + self.url = pulumi.Output.concat( + "http://storage.googleapis.com/", bucket.name, "/", files[0] + ) + self.register_outputs({ 'url': self.url }) # Signal component completion. +``` + +{{% /choosable %}} + +{{% choosable language go %}} + +```go +package main + +import ( + "github.com/pulumi/pulumi-gcp/sdk/v7/go/gcp/storage" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +type GcpStorageWebsite struct { + pulumi.ResourceState + Url pulumi.StringOutput // the website url. +} + +type GcpStorageWebsiteArgs struct { + Files []string // a list of files to serve. +} + +func NewGcpStorageWebsite(ctx *pulumi.Context, name string, args GcpStorageWebsiteArgs, opts ...pulumi.ResourceOption) (*GcpStorageWebsite, error) { + self := &GcpStorageWebsite{} + err := ctx.RegisterComponentResource("quickstart:index:GcpStorageWebsite", name, self, opts...) + if err != nil { + return nil, err + } + + // Create a Google Cloud resource (Storage Bucket) configured for website hosting + bucket, err := storage.NewBucket(ctx, "my-bucket", &storage.BucketArgs{ + Location: pulumi.String("US"), + Website: storage.BucketWebsiteArgs{ + MainPageSuffix: pulumi.String("index.html"), + }, + UniformBucketLevelAccess: pulumi.Bool(true), + }, pulumi.Parent(self)) + if err != nil { + return nil, err + } + + // Create a Bucket object for each file + for _, file := range args.Files { + _, err := storage.NewBucketObject(ctx, file, &storage.BucketObjectArgs{ + Bucket: bucket.Name, + Source: pulumi.NewFileAsset(file), + }, pulumi.Parent(self)) + if err != nil { + return nil, err + } + } + + // Allow public access to the objects + _, err = storage.NewBucketIAMBinding(ctx, "my-bucket-binding", &storage.BucketIAMBindingArgs{ + Bucket: bucket.Name, + Role: pulumi.String("roles/storage.objectViewer"), + Members: pulumi.StringArray{ + pulumi.String("allUsers"), + }, + }, pulumi.Parent(self)) + if err != nil { + return nil, err + } + + // Export the website URL + self.Url = pulumi.Sprintf("http://storage.googleapis.com/%s/%s", bucket.Name, args.Files[0]) + ctx.RegisterResourceOutputs(self, pulumi.Map{"url": self.Url}) // Signal component completion. + return self, nil +} +``` + +{{% /choosable %}} + +{{% choosable language csharp %}} + +```csharp +using Pulumi; +using Pulumi.Gcp.Storage; +using Pulumi.Gcp.Storage.Inputs; +using System.Collections.Generic; + +public class GcpStorageWebsiteArgs +{ + public string[]? Files { get; set; } +} + +public class GcpStorageWebsite : Pulumi.ComponentResource +{ + public Output Url { get; private set; } + + public GcpStorageWebsite(string name, GcpStorageWebsiteArgs args, ComponentResourceOptions? opts = null) + : base("quickstart:index:GcpStorageWebsite", name, opts) + { + // Create a Google Cloud resource (Storage Bucket) configured for website hosting + var bucket = new Bucket("my-bucket", new BucketArgs + { + Location = "US", + Website = new BucketWebsiteArgs + { + MainPageSuffix = "index.html" + }, + UniformBucketLevelAccess = true, + }, new CustomResourceOptions + { + Parent = this, + }); + + // Create a Bucket object for each file + foreach (var file in args.Files ?? []) { + new BucketObject(file, new BucketObjectArgs + { + Bucket = bucket.Name, + Source = new FileAsset(file), + }, new CustomResourceOptions + { + Parent = this, + }); + } + + // Allow public access to the objects + var bucketBinding = new BucketIAMBinding("my-bucket-binding", new BucketIAMBindingArgs + { + Bucket = bucket.Name, + Role = "roles/storage.objectViewer", + Members = new[] + { + "allUsers", + }, + }, new CustomResourceOptions + { + Parent = this, + }); + + // Capture the URL and make it available as a component property and output + this.Url = Output.Format($"http://storage.googleapis.com/{bucket.Name}/{args.Files?[0]}"); + this.RegisterOutputs(new Dictionary{ + ["url"] = this.Url + }); + } +} +``` + +{{% /choosable %}} + +{{% choosable language java %}} + +```java +package myproject; + +import com.pulumi.*; +import com.pulumi.core.*; +import com.pulumi.asset.FileAsset; +import com.pulumi.resources.*; + +import com.pulumi.gcp.storage.*; +import com.pulumi.gcp.storage.inputs.*; + +import java.util.Map; + +class GcpStorageWebsiteArgs extends ResourceArgs { + public String[] files; + public GcpStorageWebsiteArgs(String[] files) { + this.files = files; + } +} + +class GcpStorageWebsite extends ComponentResource { + public Output url; + + public GcpStorageWebsite(String name, GcpStorageWebsiteArgs args) { + this(name, args, null); + } + + public GcpStorageWebsite(String name, GcpStorageWebsiteArgs args, ComponentResourceOptions opts) { + super("quickstart:index:GcpStorageWebsite", name, args, opts); + + // Create a Google Cloud resource (Storage Bucket) configured for website hosting + var bucket = new Bucket("my-bucket", BucketArgs.builder() + .location("US") + .website(BucketWebsiteArgs.builder() + .mainPageSuffix("index.html") + .build()) + .uniformBucketLevelAccess(true) + .build(), CustomResourceOptions.builder().parent(this).build()); + + // Create a Bucket object for each file + for (var file : args.files) { + new BucketObject(file, BucketObjectArgs.builder() + .bucket(bucket.name()) + .source(new FileAsset(file)) + .build(), CustomResourceOptions.builder().parent(this).build()); + } + + // Allow public access to the objects + var bucketBinding = new BucketIAMBinding("my-bucket-binding", BucketIAMBindingArgs.builder() + .bucket(bucket.name()) + .role("roles/storage.objectViewer") + .members("allUsers") + .build(), CustomResourceOptions.builder().parent(this).build()); + + // Capture the URL and make it available as a component property and output + this.url = Output.format("http://storage.googleapis.com/%s/%s", bucket.name(), args.files[0]); + this.registerOutputs(Map.of("url", this.url)); + } +} +``` + +{{% /choosable %}} + +{{% choosable language yaml %}} + +{{% notes type="warning" %}} + +Unfortunately, YAML lacks the language facilities to author components. Feel free to [skip ahead](/docs/iac/get-started/gcp/destroy-stack/). + +{{% /notes %}} + +{{% /choosable %}} + +### Instantiate the component + +Now go back to your original file {{< langfile >}}. Now that you have moved all of the resources, you can start over with a clean slate. +Ensure the file is empty and we will build it back up by simply importing and instantiating our new component. + +Add this to your now-empty {{< langfile >}}: + +{{% choosable language typescript %}} + +```typescript +// Import from our new component module: +import { GcpStorageWebsite } from "./website"; + +// Create an instance of our component with the same files as before: +const website = new GcpStorageWebsite("my-website", { + files: [ "index.html" ], +}); + +// And export its autoassigned URL: +export const url = website.url; +``` + +{{% /choosable %}} + +{{% choosable language python %}} + +```python +import pulumi + +# Import from our new component module: +from website import GcpStorageWebsite + +# Create an instance of our component with the same files as before: +website = GcpStorageWebsite('my-website', files=['index.html']) + +# And export its autoassigned URL: +pulumi.export("url", website.url) +``` + +{{% /choosable %}} + +{{% choosable language go %}} + +```go +package main + +import ( + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +func main() { + pulumi.Run(func(ctx *pulumi.Context) error { + // Create an instance of our component with the same files as before: + website, err := NewGcpStorageWebsite(ctx, "my-website", GcpStorageWebsiteArgs{ + Files: []string{"index.html"}, + }) + if err != nil { + return err + } + + // And export its autoassigned URL: + ctx.Export("url", website.Url) + return nil + }) +} + +``` + +{{% /choosable %}} + +{{% choosable language csharp %}} + +```csharp +using Pulumi; +using Pulumi.Gcp.Storage; +using System.Collections.Generic; + +return await Pulumi.Deployment.RunAsync(() => +{ + // Create an instance of our component with the same files as before: + var website = new GcpStorageWebsite("my-website", new GcpStorageWebsiteArgs() + { + Files = new[] { "index.html" } + }); + + // And export its autoassigned URL: + return new Dictionary + { + ["url"] = website.Url + }; +}); +``` + +{{% /choosable %}} + +{{% choosable language java %}} + +```java +package myproject; + +import com.pulumi.Pulumi; + +public class App { + public static void main(String[] args) { + Pulumi.run(ctx -> { + // Create an instance of our component with the same files as before: + var website = new GcpStorageWebsite("my-website", + new GcpStorageWebsiteArgs(new String[] { "index.html" })); + + // And export its autoassigned URL: + ctx.export("url", website.url); + }); + } +} +``` + +{{% /choosable %}} + +{{% choosable language yaml %}} + +{{% notes type="warning" %}} + +Unfortunately, YAML lacks the language facilities to author components. Feel free to [skip ahead](/docs/iac/get-started/gcp/destroy-stack/). + +{{% /notes %}} + +{{% /choosable %}} + +### Deploy the component + +To deploy the componentized version, run `pulumi up` again. Just like before, you'll see a preview before any changes +are applied. Choosing `yes` will deploy the component's resources and export the website URL, which you can then access. + +{{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/gcp/create-project.md b/content/docs/iac/get-started/gcp/create-project.md index fcc2e9de46ba..6ba754e18038 100644 --- a/content/docs/iac/get-started/gcp/create-project.md +++ b/content/docs/iac/get-started/gcp/create-project.md @@ -1,8 +1,8 @@ --- title_tag: Create a New Project | Google Cloud -meta_desc: This page provides an overview of how to create a new Google Cloud + Pulumi project. title: Create project -h1: "Pulumi & Google Cloud: Create project" +h1: "Get started with Pulumi and Google Cloud" +meta_desc: This page provides an overview of how to create a new Google Cloud + Pulumi project. weight: 3 menu: iac: @@ -10,68 +10,181 @@ menu: identifier: gcp-get-started.create-project parent: gcp-get-started weight: 3 + aliases: - /docs/quickstart/gcp/create-project/ - /docs/clouds/gcp/get-started/create-project/ --- -Now that you have set up your environment by installing Pulumi, installing your preferred language runtime, -and configuring your Google Cloud credentials, let's create your first Pulumi program. +## Create a new project + +A [**project**](/docs/iac/concepts/projects) is a program in your chosen language that defines a collection of related +cloud resources. In this step, you will create a new project. + +### Initializing your project + +Each project lives in its own directory. Create a new one: + +{{% choosable os "linux,macos" %}} + +```bash +$ mkdir pulumi-start-gcp +``` + +{{% /choosable %}} +{{% choosable os "windows" %}} + +```powershell +> mkdir pulumi-start-gcp +``` + +{{% /choosable %}} + +Change into the new directory: + +{{% choosable os "linux,macos" %}} + +```bash +$ cd pulumi-start-gcp +``` + +{{% /choosable %}} +{{% choosable os "windows" %}} + +```powershell +> cd pulumi-start-gcp +``` + +{{% /choosable %}} + +Now initialize a new Pulumi project for Google Cloud using the `pulumi new` command: {{< chooser language "typescript,python,go,csharp,java,yaml" / >}} {{% choosable language typescript %}} +{{% choosable os "linux,macos" %}} + ```bash -$ mkdir quickstart && cd quickstart $ pulumi new gcp-typescript ``` +{{% /choosable %}} +{{% choosable os "windows" %}} + +```powershell +> pulumi new gcp-typescript +``` + +{{% /choosable %}} + {{% /choosable %}} {{% choosable language python %}} +{{% choosable os "linux,macos" %}} + ```bash -$ mkdir quickstart && cd quickstart $ pulumi new gcp-python ``` +{{% /choosable %}} +{{% choosable os "windows" %}} + +```powershell +> pulumi new gcp-python +``` + +{{% /choosable %}} + {{% /choosable %}} {{% choosable language go %}} +{{% choosable os "linux,macos" %}} + ```bash -$ mkdir quickstart && cd quickstart $ pulumi new gcp-go ``` +{{% /choosable %}} +{{% choosable os "windows" %}} + +```powershell +> pulumi new gcp-go +``` + +{{% /choosable %}} + {{% /choosable %}} {{% choosable language csharp %}} +{{% choosable os "linux,macos" %}} + ```bash -$ mkdir quickstart && cd quickstart $ pulumi new gcp-csharp ``` +{{% /choosable %}} +{{% choosable os "windows" %}} + +```powershell +> pulumi new gcp-csharp +``` + +{{% /choosable %}} + {{% /choosable %}} {{% choosable language java %}} +{{% choosable os "linux,macos" %}} + ```bash -$ mkdir quickstart && cd quickstart $ pulumi new gcp-java ``` +{{% /choosable %}} +{{% choosable os "windows" %}} + +```powershell +> pulumi new gcp-java +``` + +{{% /choosable %}} + {{% /choosable %}} {{% choosable language yaml %}} +{{% choosable os "linux,macos" %}} + ```bash -$ mkdir quickstart && cd quickstart $ pulumi new gcp-yaml ``` {{% /choosable %}} +{{% choosable os "windows" %}} -The [`pulumi new`](/docs/cli/commands/pulumi_new) command creates a new Pulumi project with some basic scaffolding based on the cloud and language specified. +```powershell +> pulumi new gcp-yaml +``` + +{{% /choosable %}} + +{{% /choosable %}} + +The [`pulumi new`](/docs/cli/commands/pulumi_new) command interactively walks through initializing a new project, as well as creating a +[**stack**](/docs/iac/concepts/stacks) and [**configuring**](/docs/iac/concepts/config) it. A stack is an instance of your +project and you may have many of them -- like `dev`, `staging`, and `prod` -- each with different configuration settings. + +You will be prompted for configuration values such as the Google Cloud project. Enter the project ID for the project you +set with the gcloud CLI or hit ENTER to accept any defaults: + +``` +gcp:project: The Google Cloud project to deploy into: my-project +Saved config +``` + +{{< cli-note >}} {{< cli-note >}} diff --git a/content/docs/iac/get-started/gcp/deploy-changes.md b/content/docs/iac/get-started/gcp/deploy-changes.md deleted file mode 100644 index 220b5425a445..000000000000 --- a/content/docs/iac/get-started/gcp/deploy-changes.md +++ /dev/null @@ -1,419 +0,0 @@ ---- -title_tag: Deploy the Changes | Google Cloud -meta_desc: This page provides an overview of how deploy changes to a Google Cloud project. -title: Deploy changes -h1: "Pulumi & Google Cloud: Deploy changes" -weight: 7 -menu: - iac: - name: Deploy changes - identifier: gcp-get-started.deploy-changes - parent: gcp-get-started - weight: 7 - -aliases: - - /docs/quickstart/gcp/deploy-changes/ - - /docs/clouds/gcp/get-started/deploy-changes/ ---- - -Now let's deploy your changes. - -```bash -$ pulumi up -``` - -Pulumi will run the `preview` step of the update, which computes the minimally disruptive change to achieve the desired state described by the program: - -``` -Previewing update (dev) - - Type Name Plan - pulumi:pulumi:Stack quickstart-dev - + ├─ gcp:storage:BucketIAMBinding my-bucket-binding create - + └─ gcp:storage:BucketObject index.html create - -Resources: - + 2 to create - 2 unchanged - -Do you want to perform this update? -> yes - no - details -``` - -Choosing `yes` will proceed with the update and write the `index.html` file to the bucket: - -``` -Updating (dev) - - Type Name Status - pulumi:pulumi:Stack quickstart-dev - + ├─ gcp:storage:BucketIAMBinding my-bucket-binding created (5s) - + └─ gcp:storage:BucketObject index.html created (0.76s) - -Outputs: - bucketName: "gs://my-bucket-daa12be" - -Resources: - + 2 created - 2 unchanged - -Duration: 8s -``` - -Once the update has completed, you can verify the object was created by checking the Google Cloud Console or running the following `gsutil` command: - -{{< chooser language "typescript,python,go,csharp,java,yaml" >}} - -{{% choosable language typescript %}} - -```bash -$ gsutil ls $(pulumi stack output bucketName) -``` - -{{% /choosable %}} - -{{% choosable language python %}} - -```bash -$ gsutil ls $(pulumi stack output bucket_name) -``` - -{{% /choosable %}} - -{{% choosable language go %}} - -```bash -$ gsutil ls $(pulumi stack output bucketName) -``` - -{{% /choosable %}} - -{{% choosable language csharp %}} - -```bash -$ gsutil ls $(pulumi stack output bucketName) -``` - -{{% /choosable %}} - -{{% choosable language java %}} - -```bash -$ gsutil ls $(pulumi stack output bucketName) -``` - -{{% /choosable %}} - -{{% choosable language yaml %}} - -```bash -$ gsutil ls $(pulumi stack output bucketName) -``` - -{{% /choosable %}} - -{{< /chooser >}} - -Notice that your `index.html` file has been added to the bucket: - -```bash -gs://my-bucket-daa12be/index.html-a52debd -``` - -{{% choosable language typescript %}} - -Now that `index.html` exists in the bucket, modify the program to have the bucket serve the file as a static website. - -To do that, update the bucket definition to configure its `website` property. Then, to align with Google Cloud Storage recommendations, set its uniform bucket-level access property to `true`: - -```typescript -const bucket = new gcp.storage.Bucket("my-bucket", { - location: "US", - website: { - mainPageSuffix: "index.html" - }, - uniformBucketLevelAccess: true -}); -``` - -Finally, at the end of the file, export the website's public URL to make it easy to access: - -```typescript -export const bucketEndpoint = pulumi.concat("http://storage.googleapis.com/", bucket.name, "/", bucketObject.name); -``` - -{{% /choosable %}} - -{{% choosable language python %}} - -Now that `index.html` exists in the bucket, modify the program to have the bucket serve the file as a static website. - -To do that, update the bucket definition to configure its `website` property. Then, to align with Google Cloud Storage recommendations, set its uniform bucket-level access property to `True`: - -```python -bucket = storage.Bucket( - "my-bucket", - location="US", - website={ - "main_page_suffix": "index.html" - }, - uniform_bucket_level_access=True, -) -``` - -Finally, at the end of the file, export the website's public URL to make it easy to access: - -```python -pulumi.export( - "bucket_endpoint", - pulumi.Output.concat( - "http://storage.googleapis.com/", bucket.id, "/", bucket_object.name - ), -) -``` - -{{% /choosable %}} - -{{% choosable language go %}} - -Now that `index.html` exists in the bucket, modify the program to have the bucket serve the file as a static website. - -To do that, update the bucket definition to configure its `Website` property. Then, to align with Google Cloud Storage recommendations, set its uniform bucket-level access property to `true`: - -```go -bucket, err := storage.NewBucket(ctx, "my-bucket", &storage.BucketArgs{ - Location: pulumi.String("US"), - Website: storage.BucketWebsiteArgs{ - MainPageSuffix: pulumi.String("index.html"), - }, - UniformBucketLevelAccess: pulumi.Bool(true), -}) -if err != nil { - return err -} -``` - -Finally, at the end of the file, export the website's public URL to make it easy to access. - -```go -ctx.Export("bucketEndpoint", pulumi.Sprintf("http://storage.googleapis.com/%s/%s", bucket.Name, bucketObject.Name)) -``` - -Be sure to change the variable name of the `BucketObject` from `_` to `bucketObject` in this step, or Go may fail to compile the program: - -```go -bucketObject, err := storage.NewBucketObject(ctx, "index.html", &storage.BucketObjectArgs{ - Bucket: bucket.Name, - Source: pulumi.NewFileAsset("index.html"), -}) -``` - -{{% /choosable %}} - -{{% choosable language csharp %}} - -Now that `index.html` exists in the bucket, modify the program to have the bucket serve the file as a static website. - -To do that, update the bucket definition to configure its `Website` property. Then, to align with Google Cloud Storage recommendations, set its uniform bucket-level access property to `true`: - -```csharp -// Add this using statement -using Pulumi.Gcp.Storage.Inputs; -``` - -```csharp -var bucket = new Bucket("my-bucket", new BucketArgs -{ - Location = "US", - Website = new BucketWebsiteArgs - { - MainPageSuffix = "index.html" - }, - UniformBucketLevelAccess = true -}); -``` - -Finally, at the end of the file, export the website's public URL to make it easy to access: - -```csharp -return new Dictionary -{ - ["bucketName"] = bucket.Url, - ["bucketEndpoint"] = Output.Format($"http://storage.googleapis.com/{bucket.Name}/{bucketObject.Name}") -}; -``` - -{{% /choosable %}} - -{{% choosable language java %}} - -Now that `index.html` exists in the bucket, modify the program to have the bucket serve the file as a static website. - -To do that, add the `BucketWebsiteArgs` class to the list of imports, then update the bucket definition to configure its `website` property. To align with Google Cloud Storage recommendations, also set its uniform bucket-level access property to `true`: - -```java -// ... -import com.pulumi.gcp.storage.inputs.BucketWebsiteArgs; - -public class App { - public static void main(String[] args) { - Pulumi.run(ctx -> { - // Create an AWS resource (S3 Bucket) - var bucket = new Bucket("my-bucket", BucketArgs.builder() - .location("US") - .website(BucketWebsiteArgs.builder() - .mainPageSuffix("index.html") - .build()) - .build()); - //... -``` - -Finally, at the end of the file, export the website's public URL to make it easy to access: - -```java -ctx.export("bucketEndpoint", Output.format("http://storage.googleapis.com/%s/%s", bucket.name(), bucketObject.name())); -``` - -{{% /choosable %}} - -{{% choosable language yaml %}} - -Now that `index.html` exists in the bucket, modify the program to have the bucket serve the file as a static website. - -To do that, update the bucket definition to configure its `Website` property. Then, to align with Google Cloud Storage recommendations, set its uniform bucket-level access setting to `true`: - -```yaml -resources: - my-bucket: - type: gcp:storage:Bucket - properties: - location: US - website: - mainPageSuffix: index.html - uniformBucketLevelAccess: true -``` - -Finally, at the end of the file, export the website's public URL to make it easy to access: - -```yaml -# ... -outputs: - # ... - bucketEndpoint: http://storage.googleapis.com/${my-bucket.name}/${index-html.name} - ``` - -{{% /choosable %}} - -Give the stack one final update to apply these changes: - -```bash -$ pulumi up -``` - -Again, you'll see a preview of the changes to be made: - -``` -Previewing update (dev) - - Type Name Plan Info - pulumi:pulumi:Stack quickstart-dev - ~ └─ gcp:storage:Bucket my-bucket update [diff: +website~uniformBucketLevelAccess] - -Outputs: - + bucketEndpoint: "http://storage.googleapis.com/my-bucket-daa12be/index.html-a52debd" - -Resources: - ~ 1 to update - 3 unchanged - -Do you want to perform this update? -> yes - no - details -``` - -Choose `yes` to deploy them: - -``` -Updating (dev) - - Type Name Status Info - pulumi:pulumi:Stack quickstart-dev - ~ └─ gcp:storage:Bucket my-bucket updated (1s) [diff: +website~uniformBucketLevelAccess] - -Outputs: - + bucketEndpoint: "http://storage.googleapis.com/my-bucket-daa12be/index.html-a52debd" - bucketName : "gs://my-bucket-daa12be" - -Resources: - ~ 1 updated - 3 unchanged - -Duration: 4s -``` - -When the deployment completes, you can check out your new static website at the URL under `Outputs`, or make a `curl` request and see the contents of `index.html` printed to the terminal: - -{{% choosable language typescript %}} - -```bash -$ curl $(pulumi stack output bucketEndpoint) -``` - -{{% /choosable %}} - -{{% choosable language python %}} - -```bash -$ curl $(pulumi stack output bucket_endpoint) -``` - -{{% /choosable %}} - -{{% choosable language go %}} - -```bash -$ curl $(pulumi stack output bucketEndpoint) -``` - -{{% /choosable %}} - -{{% choosable language csharp %}} - -```bash -$ curl $(pulumi stack output bucketEndpoint) -``` - -{{% /choosable %}} - -{{% choosable language java %}} - -```bash -$ curl $(pulumi stack output bucketEndpoint) -``` - -{{% /choosable %}} - -{{% choosable language yaml %}} - -```bash -$ curl $(pulumi stack output bucketEndpoint) -``` - -{{% /choosable %}} - -And you should see: - -```bash - - -

Hello, Pulumi!

- - -``` - -Next you will destroy the resources. - -{{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/gcp/deploy-stack.md b/content/docs/iac/get-started/gcp/deploy-stack.md index f9279a9f6446..ff22fae316b4 100644 --- a/content/docs/iac/get-started/gcp/deploy-stack.md +++ b/content/docs/iac/get-started/gcp/deploy-stack.md @@ -1,12 +1,12 @@ --- title_tag: Deploy the Stack | Google Cloud -meta_desc: This page provides an overview of how to deploy changes to a Google Cloud project. -title: Deploy stack -h1: "Pulumi & Google Cloud: Deploy stack" +title: Deploy to Google Cloud +h1: "Get started with Pulumi and Google Cloud" +meta_desc: Learn how to deploy your stack to a Google Cloud project in this guide. weight: 5 menu: iac: - name: Deploy stack + name: Deploy identifier: gcp-get-started.deploy-stack parent: gcp-get-started weight: 5 @@ -16,13 +16,15 @@ aliases: - /docs/clouds/gcp/get-started/deploy-stack/ --- -Let's go ahead and deploy your stack: +## Deploy to Google Cloud + +Now run `pulumi up` to start deploying your new storage bucket: ```bash $ pulumi up ``` -This command evaluates your program and determines the resource updates to make. First, a preview is shown that outlines the changes that will be made when you run the update: +This command first shows you a **preview** of the changes that will be made: ``` Previewing update (dev) @@ -37,16 +39,22 @@ Outputs: Resources: + 2 to create -Do you want to perform this update? [Use arrows to move, enter to select, type to filter] +Do you want to perform this update? > yes no details ``` -Once the preview has finished, you are given three options to choose from. Choosing `details` will show you a rich diff of the changes to be made. Choosing `yes` will create your new storage bucket in Google Cloud. Choosing `no` will return you to the user prompt without performing the update operation. +No changes have been made yet. You may decline to proceed by selecting `no` or choose `details` to +see more information about the proposed update like your bucket's properties. + +### Performing the update + +To proceed and deploy your new storage bucket, select `yes`. This begins an **update**: ``` -Updating (dev) +Do you want to perform this update? yes +Updating (dev): Type Name Status + pulumi:pulumi:Stack quickstart-dev created (3s) @@ -61,7 +69,14 @@ Resources: Duration: 4s ``` -Remember the output you defined in the previous step? That [stack output](/docs/concepts/stack#outputs) can be seen in the `Outputs:` section of your update. You can access your outputs from the CLI by running the `pulumi stack output [property-name]` command. For example you can print the name of your bucket with the following command: +Updates can take some time since they wait for the cloud resources to finish being created. Storage buckets +are quick, however, so the update will finish in just a few seconds. + +{{< auto-naming-note resource="bucket" suffix="daa12be" >}} + +### Using stack outputs + +The bucket name is available as a stack output. To view it: {{< chooser language "typescript,python,go,csharp,java,yaml" / >}} @@ -115,10 +130,14 @@ $ pulumi stack output bucketName Running that command will print out the name of your bucket. -{{< auto-naming-note resource="bucket" suffix="daa12be" >}} +### View your update on Pulumi Cloud + +If you are logged into [Pulumi Cloud](/docs/pulumi-cloud), you'll see "View Live" hyperlinks in the CLI output during your update. These go to [a page](https://app.pulumi.com) with detailed information about your stack including resources, configuration, a full history of updates, and more. Navigate to it to review the details of your update: -{{< console-note >}} + + A stack update with console output, as shown in the Pulumi Service + -Now that your bucket has been provisioned, let's modify the bucket to host a static website. +Now that the storage bucket has been provisioned, you'll update it to host a static website. {{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/gcp/destroy-stack.md b/content/docs/iac/get-started/gcp/destroy-stack.md index 86fd709b9680..af9b009a9cb5 100644 --- a/content/docs/iac/get-started/gcp/destroy-stack.md +++ b/content/docs/iac/get-started/gcp/destroy-stack.md @@ -1,33 +1,47 @@ --- title_tag: Destroy the Stack | Google Cloud -meta_desc: This page provides an overview of how to destroy a Pulumi stack of a Google Cloud (GCP) project. title: Destroy stack -h1: "Pulumi & Google Cloud: Destroy stack" +h1: "Get started with Pulumi and Google Cloud" +meta_desc: This page provides an overview of how to destroy a Pulumi stack of a Google Cloud project. weight: 8 menu: - iac: - name: Destroy stack - identifier: gcp-get-started.destroy-stack - parent: gcp-get-started - weight: 8 + iac: + name: Cleanup & destroy + identifier: gcp-get-started.destroy-stack + parent: gcp-get-started + weight: 8 aliases: - /docs/quickstart/gcp/destroy-stack/ - /docs/clouds/gcp/get-started/destroy-stack/ --- -Now that you've seen how to deploy and manage cloud resources with Pulumi, let's clean up by tearing down all of the resources that belong to this stack. +## Cleanup & destroy the stack -To do so, run the following: +Our final step is to clean up all of the resources we've allocated in this tutorial. + +Run the `pulumi destroy` command to delete all cloud resources in this project/stack: + +{{% choosable os "linux,macos" %}} ```bash $ pulumi destroy ``` -Again you'll be presented with a preview of the changes to be made. Choose `yes`. The destroy operation may take few moments, as Pulumi waits for all resources are to be deleted before considering the operation complete: +{{% /choosable %}} + +{{% choosable os "windows" %}} + +```powershell +> pulumi destroy +``` + +{{% /choosable %}} + +Just like `pulumi up`, you'll be shown a preview to ensure that you want to proceed: ``` -Previewing destroy (dev) +Previewing destroy (dev): Type Name Plan - pulumi:pulumi:Stack quickstart-dev delete @@ -36,24 +50,32 @@ Previewing destroy (dev) - └─ gcp:storage:Bucket my-bucket delete Outputs: - - bucketEndpoint: "http://storage.googleapis.com/my-bucket-daa12be/index.html-a52debd" - - bucketName : "gs://my-bucket-daa12be" + - bucketName: "gs://my-bucket-daa12be" + - url : "http://storage.googleapis.com/my-bucket-daa12be/index.html" Resources: - 4 to delete -Do you want to perform this destroy? yes -Destroying (dev) +Do you want to perform this destroy? +> yes + no + details +``` + +As with an update, we can choose `no` or `details`; select `yes` to proceed: + +``` +Destroying (dev): Type Name Status - - pulumi:pulumi:Stack quickstart-dev deleted + - pulumi:pulumi:Stack quickstart-dev deleted (0.31s) - ├─ gcp:storage:BucketIAMBinding my-bucket-binding deleted (6s) - ├─ gcp:storage:BucketObject index.html deleted (0.78s) - └─ gcp:storage:Bucket my-bucket deleted (1s) Outputs: - - bucketEndpoint: "http://storage.googleapis.com/my-bucket-daa12be/index.html-a52debd" - - bucketName : "gs://my-bucket-daa12be" + - bucketName: "gs://my-bucket-daa12be" + - url : "http://storage.googleapis.com/my-bucket-daa12be/index.html" Resources: - 4 deleted @@ -61,16 +83,29 @@ Resources: Duration: 9s ``` -Optionally, to delete the stack itself, you can also run [`pulumi stack rm`](/docs/cli/commands/pulumi_stack_rm). Doing so removes the stack entirely from the Pulumi Cloud, along with all of its update history. +At this stage, your stack still exists, but all cloud resources have been deleted from it. + +## Remove the stack + +The final step is to remove the stack itself. Destroy keeps the stack around so that you still have the full +history of what happened to the stack. Running [`pulumi stack rm`](/docs/cli/commands/pulumi_stack_rm) will +delete it entirely, including all history and state snapshots. Be careful, this step cannot be undone! -Congratulations! You've successfully provisioned some cloud resources using Pulumi. By completing this guide you have successfully: +{{% choosable "os" "macos,linux" %}} + +```bash +$ pulumi stack rm +``` + +{{% /choosable %}} +{{% choosable "os" "windows" %}} + +```powershell +> pulumi stack rm +``` -- Created a Pulumi new project. -- Provisioned a new storage bucket. -- Added an `index.html` file to your bucket. -- Served the file as a static website. -- Destroyed the resources you've provisioned. +{{% /choosable %}} -On the next page, we have a collection of examples and tutorials that you can deploy as they are or use them as a foundation for your own applications and infrastructure projects. +You'll be prompted to confirm the removal. Confirm it to successfully complete this tutorial. {{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/gcp/modify-program.md b/content/docs/iac/get-started/gcp/modify-program.md index d4df8165d842..9b00e026d535 100644 --- a/content/docs/iac/get-started/gcp/modify-program.md +++ b/content/docs/iac/get-started/gcp/modify-program.md @@ -1,12 +1,12 @@ --- -title_tag: Modify the Program | Google Cloud -meta_desc: This page provides an overview on how to update Google Cloud (GCP) project from a Pulumi program. -title: Modify program -h1: "Pulumi & Google Cloud: Modify program" +title_tag: Make an Update | Google Cloud +title: Make an update +h1: "Get started with Pulumi and Google Cloud" +meta_desc: This page provides an overview on how to update a Google Cloud project from a Pulumi program. weight: 6 menu: iac: - name: Modify program + name: Make an update identifier: gcp-get-started.modify-program parent: gcp-get-started weight: 6 @@ -14,9 +14,30 @@ menu: aliases: - /docs/quickstart/gcp/modify-program/ - /docs/clouds/gcp/get-started/modify-program/ + - /docs/quickstart/gcp/deploy-changes/ + - /docs/clouds/gcp/get-started/deploy-changes/ --- -Now that your storage bucket is provisioned, let's add an object to it. First, from within your project directory, create a new `index.html` file with some content in it. +## Make an update + +Now you will update your project to serve a static website out of your Google Cloud Storage bucket. You will change +your code and then re-run `pulumi up` which will update your infrastructure. + +### Add new resources + +Pulumi knows how to evolve your current infrastructure to your project's new desired state, both for +the first deployment as well as subsequent updates. + +To turn your bucket into a static website, you will add two new Google Cloud Storage resources: + +1. [`BucketObject`](/registry/packages/gcp/api-docs/storage/bucketobject/): + uploads your website content to the bucket +2. [`BucketIAMBinding`](/registry/packages/gcp/api-docs/storage/bucketiambinding/): + makes the bucket publicly accessible + +### Add an index.html + +First, from within your project directory, create a new `index.html` file with some content in it. {{< chooser os "macos,linux,windows" / >}} @@ -73,6 +94,12 @@ For this, you'll use Pulumi's `FileAsset` class to assign the content of the fil In `index.ts`, create the `BucketObject` right after creating the bucket itself: ```typescript +// Create a Google Cloud resource (Storage Bucket) +const bucket = new gcp.storage.Bucket("my-bucket", { + /* existing bucket configuration */ +}); + +// Upload the file const bucketObject = new gcp.storage.BucketObject("index.html", { bucket: bucket.name, source: new pulumi.asset.FileAsset("index.html") @@ -86,6 +113,13 @@ const bucketObject = new gcp.storage.BucketObject("index.html", { In `__main__.py`, create a new bucket object by adding the following right after creating the bucket itself: ```python +# Create a Google Cloud resource (Storage Bucket) +bucket = storage.Bucket( + "my-bucket", + # existing bucket configuration +) + +# Upload the file bucket_object = storage.BucketObject( "index.html", bucket=bucket.name, source=pulumi.FileAsset("index.html") ) @@ -98,6 +132,15 @@ bucket_object = storage.BucketObject( In `main.go`, create the `BucketObject` right after creating the bucket itself: ```go +// Create a Google Cloud resource (Storage Bucket) +bucket, err := storage.NewBucket(ctx, "my-bucket", &storage.BucketArgs{ + // existing bucket configuration +}) +if err != nil { + return err +} + +// Upload the file _, err = storage.NewBucketObject(ctx, "index.html", &storage.BucketObjectArgs{ Bucket: bucket.Name, Source: pulumi.NewFileAsset("index.html"), @@ -114,6 +157,13 @@ if err != nil { In `Program.cs`, create the `BucketObject` right after creating the bucket itself: ```csharp +// Create a Google Cloud resource (Storage Bucket) +var bucket = new Bucket("my-bucket", new BucketArgs +{ + /* existing bucket configuration */ +}); + +// Upload the file var bucketObject = new BucketObject("index.html", new BucketObjectArgs { Bucket = bucket.Name, @@ -138,9 +188,12 @@ import com.pulumi.gcp.storage.BucketObjectArgs; public class App { public static void main(String[] args) { Pulumi.run(ctx -> { - // ... + // Create a Google Cloud resource (Storage Bucket) + var bucket = new Bucket("my-bucket", BucketArgs.builder() + // existing bucket configuration + .build()); - // Create a Bucket object + // Upload the file var bucketObject = new BucketObject("index.html", BucketObjectArgs.builder() .bucket(bucket.name()) .source(new FileAsset("index.html")) @@ -161,7 +214,12 @@ In {{< langfile >}}, create the `BucketObject` right below the bucket itself. ```yaml resources: - # ... + # Create a Google Cloud resource (Storage Bucket) + my-bucket: + type: gcp:storage:Bucket + # existing bucket configuration + + # Upload the file index-html: type: gcp:storage:BucketObject properties: @@ -260,6 +318,235 @@ my-bucket-binding: {{% /choosable %}} -Next, you'll deploy your changes. +Now that `index.html` exists in the bucket, modify the bucket to serve the file as a static website. + +To do that, update the bucket definition to configure its website property. Then, to align with Google Cloud Storage recommendations, set its uniform bucket-level access property to `true`: + +{{% choosable language typescript %}} + +```typescript +const bucket = new gcp.storage.Bucket("my-bucket", { + location: "US", + website: { + mainPageSuffix: "index.html" + }, + uniformBucketLevelAccess: true +}); +``` + +### Export the website URL + +Now to export the website's public URL for easy access, add the `url` export to your return statement as shown in this example: + +```typescript +// Export the DNS name of the bucket +export const bucketName = bucket.url; + +// Export the bucket's public URL +export const url = pulumi.concat("http://storage.googleapis.com/", bucket.name, "/", bucketObject.name); +``` + +{{% /choosable %}} + +{{% choosable language python %}} + +```python +bucket = storage.Bucket( + "my-bucket", + location="US", + website={ + "main_page_suffix": "index.html" + }, + uniform_bucket_level_access=True, +) + +# Export the DNS name of the bucket +pulumi.export("bucket_name", bucket.url) + +# Export the bucket's public URL +pulumi.export( + "url", + pulumi.Output.concat( + "http://storage.googleapis.com/", bucket.id, "/", bucket_object.name + ), +) +``` + +{{% /choosable %}} + +{{% choosable language go %}} + +```go +bucket, err := storage.NewBucket(ctx, "my-bucket", &storage.BucketArgs{ + Location: pulumi.String("US"), + Website: storage.BucketWebsiteArgs{ + MainPageSuffix: pulumi.String("index.html"), + }, + UniformBucketLevelAccess: pulumi.Bool(true), +}) +if err != nil { + return err +} + +// Export the DNS name of the bucket +ctx.Export("bucketName", bucket.Url) + +// Export the bucket's public URL +ctx.Export("url", pulumi.Sprintf("http://storage.googleapis.com/%s/%s", bucket.Name, bucketObject.Name)) +``` + +{{% /choosable %}} + +{{% choosable language csharp %}} + +```csharp +var bucket = new Bucket("my-bucket", new BucketArgs +{ + Location = "US", + Website = new BucketWebsiteArgs + { + MainPageSuffix = "index.html" + }, + UniformBucketLevelAccess = true +}); + +return new Dictionary +{ + ["bucketName"] = bucket.Url, + ["url"] = Output.Format($"http://storage.googleapis.com/{bucket.Name}/{bucketObject.Name}") +}; +``` + +{{% /choosable %}} + +{{% choosable language java %}} + +```java +var bucket = new Bucket("my-bucket", BucketArgs.builder() + .location("US") + .website(BucketWebsiteArgs.builder() + .mainPageSuffix("index.html") + .build()) + .uniformBucketLevelAccess(true) + .build()); + +// Export the DNS name of the bucket +ctx.export("bucketName", bucket.url()); + +// Export the bucket's public URL +ctx.export("url", Output.format("http://storage.googleapis.com/%s/%s", bucket.name(), bucketObject.name())); +``` + +{{% /choosable %}} + +{{% choosable language yaml %}} + +```yaml +resources: + my-bucket: + type: gcp:storage:Bucket + properties: + location: US + website: + mainPageSuffix: index.html + uniformBucketLevelAccess: true + +outputs: + bucketName: ${my-bucket.url} + url: http://storage.googleapis.com/${my-bucket.name}/${index-html.name} +``` + +{{% /choosable %}} + +We prepend `http://` using a helper because the bucket's URL is [an output property](/docs/iac/concepts/inputs-outputs/#outputs) +that Google Cloud assigns at deployment time, not a raw string, meaning its value is not known in advance. + +### Deploy the changes + +To deploy the changes, run `pulumi up` again and it will figure out the deltas: + +{{% choosable os "linux,macos" %}} + +```bash +$ pulumi up +``` + +{{% /choosable %}} +{{% choosable os "windows" %}} + +```powershell +> pulumi up +``` + +{{% /choosable %}} + +Just like the first time you will see a preview of the changes before they happen: + +``` +Previewing update (dev): + + Type Name Plan + pulumi:pulumi:Stack quickstart-dev + + ├─ gcp:storage:BucketObject index.html create + + └─ gcp:storage:BucketIAMBinding my-bucket-binding create + ~ └─ gcp:storage:Bucket my-bucket update + +Outputs: + + url: "http://storage.googleapis.com/my-bucket-a2b3c4d/index.html" + +Resources: + + 2 to create + ~ 1 to update + 2 unchanged + +Do you want to perform this update? +> yes + no + details +``` + +Choose `yes` to perform the deployment: + +``` +Do you want to perform this update? yes +Updating (dev): + + Type Name Status + pulumi:pulumi:Stack quickstart-dev + + ├─ gcp:storage:BucketObject index.html created + + └─ gcp:storage:BucketIAMBinding my-bucket-binding created + ~ └─ gcp:storage:Bucket my-bucket updated + +Outputs: + bucketName: "gs://my-bucket-a2b3c4d" + + url : "http://storage.googleapis.com/my-bucket-a2b3c4d/index.html" + +Resources: + + 2 created + ~ 1 updated + 2 unchanged + +Duration: 8s +``` + +In just a few seconds, your new website will be ready. Curl the endpoint to see it live: + +```bash +$ curl $(pulumi stack output url) +``` + +This will reveal your new website! + +``` + + +

Hello, Pulumi!

+ + +``` + +Feel free to experiment, such as changing the contents of `index.html` and redeploying. + +Next, let's wrap this website up into an infrastructure abstraction. {{< get-started-stepper >}} diff --git a/content/docs/iac/get-started/gcp/next-steps.md b/content/docs/iac/get-started/gcp/next-steps.md index a8511b016b86..38eef7c44e98 100644 --- a/content/docs/iac/get-started/gcp/next-steps.md +++ b/content/docs/iac/get-started/gcp/next-steps.md @@ -1,23 +1,32 @@ --- title_tag: Next Steps | Google Cloud +title: Next steps +h1: Next Steps with Pulumi & Google Cloud +stepper_link: "Congratulations!" meta_desc: This page provides a list of tutorials that take a deeper dive into Google Cloud cloud resources. -title: Next steps -h1: "Pulumi & Google Cloud: Next steps" weight: 9 menu: - iac: - name: Next steps - identifier: gcp-get-started.next-steps - parent: gcp-get-started - weight: 9 + iac: + name: Next steps + identifier: gcp-get-started.next-steps + parent: gcp-get-started + weight: 9 aliases: - /docs/quickstart/gcp/next-steps/ - /docs/clouds/gcp/get-started/next-steps/ --- -Congrats! You've deployed your first project on Google Cloud with Pulumi. Here are some next steps, depending on your learning style. +Congratulations! You've successfully provisioned some cloud resources using Pulumi. By completing this guide you have successfully: + +- Created a Pulumi new project. +- Provisioned a new storage bucket. +- Turned it into a static website. +- Created a website component for easy reuse. +- Destroyed all of the resources you've provisioned. + +Below are some recommended next steps, including examples and tutorials that you can explore or use them as a foundation for your own applications and infrastructure projects. Also be sure to [join the Community Slack](https://slack.pulumi.com/) to meet fellow IaC practitioners. ## Try Pulumi ESC (Environments, Secrets, and Configuration) @@ -31,11 +40,11 @@ With Pulumi ESC you can: {{< get-started-next-step path="/docs/esc/get-started" label="Learn more about Pulumi ESC" ref="gs-gcp-esc" >}} -## Learn Pulumi +## Try a tutorial -Dive into Learn Pulumi for a comprehensive walkthrough of key Pulumi concepts in the context of a real-life application. +Let our Google Cloud tutorials guide you through key Pulumi concepts. -{{< get-started-next-step path="/learn/pulumi-fundamentals" label="Learn Pulumi Fundamentals" ref="gs-gcp-learn" >}} +{{< get-started-next-step path="/docs/using-pulumi/" label="Browse tutorials" ref="gs-gcp-tutorials" >}} ## Launch a new project with a template diff --git a/content/docs/iac/get-started/gcp/review-project.md b/content/docs/iac/get-started/gcp/review-project.md index 49792433dd3a..75b99770a908 100644 --- a/content/docs/iac/get-started/gcp/review-project.md +++ b/content/docs/iac/get-started/gcp/review-project.md @@ -2,7 +2,7 @@ title_tag: Review the New Project | Google Cloud meta_desc: This page provides an overview on how to review a new Google Cloud project. title: Review project -h1: "Pulumi & Google Cloud: Review project" +h1: "Get started with Pulumi and Google Cloud" weight: 4 menu: iac: @@ -13,6 +13,7 @@ menu: aliases: - /docs/quickstart/gcp/review-project/ + - /docs/get-started/gcp/review-project/ - /docs/clouds/gcp/get-started/review-project/ --- @@ -20,17 +21,17 @@ Let's review some of the generated project files: {{% choosable language "typescript,python,go,csharp,java" %}} -- `Pulumi.yaml` defines the [project](/docs/concepts/projects/). +- `Pulumi.yaml` defines the [project](/docs/iac/concepts/projects/). {{% /choosable %}} {{% choosable language yaml %}} -- `Pulumi.yaml` defines both the [project](/docs/concepts/projects/) and the program that manages your stack resources. +- `Pulumi.yaml` defines both the [project](/docs/iac/concepts/projects/) and the program that manages your stack resources. {{% /choosable %}} -- `Pulumi.dev.yaml` contains [configuration](/docs/concepts/config/) values for the [stack](/docs/concepts/stack/) you initialized. +- `Pulumi.dev.yaml` contains [configuration](/docs/iac/concepts/config/) values for the [stack](/docs/iac/concepts/stack/) you initialized. {{% choosable language java %}} @@ -122,7 +123,7 @@ using Pulumi; using Pulumi.Gcp.Storage; using System.Collections.Generic; -return await Deployment.RunAsync(() => +return await Pulumi.Deployment.RunAsync(() => { // Create a Google Cloud resource (Storage Bucket). var bucket = new Bucket("my-bucket", new BucketArgs diff --git a/content/docs/iac/get-started/kubernetes/begin.md b/content/docs/iac/get-started/kubernetes/begin.md index 4bb642b595f7..fc4eea99b278 100644 --- a/content/docs/iac/get-started/kubernetes/begin.md +++ b/content/docs/iac/get-started/kubernetes/begin.md @@ -1,8 +1,9 @@ --- -title_tag: Before You Begin | Kubernetes -meta_desc: This page provides an overview on how to get started with Pulumi when starting an Kubernetes project. -title: Before you begin -h1: "Pulumi & Kubernetes: Before you begin" +title_tag: Install Pulumi | Kubernetes +meta_desc: This page provides an overview of how to get started with Pulumi when starting a Kubernetes project. +title: Install Pulumi +h1: "Get started with Pulumi and Kubernetes" +stepper_link: "I'm ready to begin" weight: 2 menu: iac: @@ -19,54 +20,6 @@ aliases: - /get-started/kubernetes/begin/ --- -Before we get started using Pulumi, let's run through a few quick steps to ensure our environment is setup correctly. - -### Install Pulumi - -{{< install-pulumi >}} -{{% notes "info" %}} -All Windows examples in this tutorial assume you are running in PowerShell. -{{% /notes %}} -{{< /install-pulumi >}} - -Next, we'll install the required language runtime. - -### Install Language Runtime - -#### Choose Your Language - -{{< chooser language "typescript,python,go,csharp,java,yaml" / >}} - -{{% choosable language "typescript" %}} -{{< install-node >}} -{{% /choosable %}} - -{{% choosable language python %}} -{{< install-python >}} -{{% /choosable %}} - -{{% choosable language go %}} -{{< install-go >}} -{{% /choosable %}} - -{{% choosable language "csharp,fsharp,visualbasic" %}} -{{< install-dotnet >}} -{{% /choosable %}} - -{{% choosable language java %}} -{{< install-java >}} -{{% /choosable %}} - -{{% choosable language yaml %}} -{{< install-yaml >}} -{{% /choosable %}} - -Next, we'll configure Kubernetes. - -### Configure Kubernetes - -Configure Kubernetes so the Pulumi CLI can connect to a Kubernetes cluster. If you have previously configured the kubectl CLI, `kubectl`, Pulumi will respect and use your configuration settings. Depending on the approach you choose, you may need to apply some of the configuration after creating your project and stack in the next step. - -Next, we'll create a new Pulumi project. +{{< get-started-install-body >}} {{< get-started-stepper >}} diff --git a/layouts/shortcodes/get-started-install-body.html b/layouts/shortcodes/get-started-install-body.html new file mode 100644 index 000000000000..11bff9d7ac37 --- /dev/null +++ b/layouts/shortcodes/get-started-install-body.html @@ -0,0 +1,5 @@ +

Install Pulumi

+ +

Download and install Pulumi for your platform:

+ +{{ .Page.RenderString "{{< install-pulumi >}}\n{{% notes info %}}\nAll Windows examples in this tutorial assume you are running in PowerShell.\n{{% /notes %}}\n{{< /install-pulumi >}}" }}