#!/usr/bin/env python # coding: utf-8 # # Pulumi Automation API # # Pulumi's Automation API is the programmatic interface for driving pulumi programs from within your code. # The package can be used for a number of use cases: # # * Driving pulumi deployments within CI/CD workflows # * Integration testing # * Multi-stage deployments such as blue-green deployment patterns # * Deployments involving application code like database migrations # * Building higher level tools, custom CLIs over pulumi, etc # * Using pulumi behind a REST or GRPC API # * Debugging Pulumi programs (by using a single main entrypoint with "inline" programs) # # This jupyter notebook explores various facets of automation API itself and explores how to deploy infrastructure without ever leaving the notebook. # # To run this example you'll need a few pre-reqs: # # 1. A Pulumi CLI installation ([v3.0.0](https://www.pulumi.com/docs/get-started/install/versions/) or later) # 2. The AWS CLI, with appropriate credentials. # # Alright, let's get started. # # ### Automation API 101 # # In addition to fine-grained building blocks, Automation API provides two out-of-the-box ways to work with Stacks: # # 1. Programs locally available on-disk and addressed via a filepath (local source): # # ```python # stack = create_stack("myOrg/myProj/myStack", work_dir=os.path.join("..", "path", "to", "project")) # ``` # # 2. Programs defined as a function alongside your Automation API code (inline source): # # ```python # def pulumi_program(): # bucket = s3.Bucket("bucket") # pulumi.export("bucket_name", bucket.Bucket) # # stack = create_stack("myOrg/myProj/myStack", program=pulumi_program) # ``` # # Each of these creates a stack with access to the full range of Pulumi lifecycle methods # (up/preview/refresh/destroy), as well as methods for managing config, stack, and project settings: # # ```python # stack.set_config("key", ConfigValue(value="value", secret=True)) # preview_response = stack.preview() # ``` # # # ### Pulumi programs as functions # # An inline program allows you to define your infrastructure within a function alongside your other code. Consider the following function called `s3_static_site`. It creates an s3 bucket, sets it up as a basic static website and exports the URL. # # In[ ]: import pulumi from pulumi_aws import s3 def s3_static_site(): # Create a bucket and expose a website index document site_bucket = s3.Bucket("s3-website-bucket", website=s3.BucketWebsiteArgs(index_document="index.html")) index_content = """
Hello, world!
Made with ❤️ with Pulumi
""" # Write our index.html into the site bucket s3.BucketObject("index", bucket=site_bucket.id, # reference to the s3.Bucket object content=index_content, key="index.html", # set the key of the object content_type="text/html; charset=utf-8") # set the MIME type of the file # Set the access policy for the bucket so all objects are readable s3.BucketPolicy("bucket-policy", bucket=site_bucket.id, policy={ "Version": "2012-10-17", "Statement": { "Effect": "Allow", "Principal": "*", "Action": ["s3:GetObject"], # Policy refers to bucket explicitly "Resource": [pulumi.Output.concat("arn:aws:s3:::", site_bucket.id, "/*")] }, }) # Export the website URL pulumi.export("website_url", site_bucket.website_endpoint) # ### Automating your deployment # # Now, let's define some functions to deploy and destroy our stacks. # In[ ]: from typing import List, Tuple, Optional, Dict from pulumi import automation as auto stack_name = "dev" def noop(): pass def deploy_project(project_name: str, program: callable, plugins: Optional[List[Tuple]] = None, config: Optional[Dict[str, auto.ConfigValue]] = None): # create (or select if one already exists) a stack that uses our inline program stack = auto.create_or_select_stack(stack_name=stack_name, project_name=project_name, program=program) if plugins: for plugin in plugins: stack.workspace.install_plugin(plugin[0], plugin[1]) print("plugins installed") if config: stack.set_all_config(config) print("config set") stack.refresh(on_output=print) stack.up(on_output=print) return stack def destroy_project(project_name: str): stack = auto.create_or_select_stack(stack_name=stack_name, project_name=project_name, program=noop) stack.destroy(on_output=print) stack.workspace.remove_stack(stack_name) print(f"stack {stack_name} in project {project_name} removed") # ### Deploy all the things! # # Alright, we're ready to deploy our first project. Execute the code below and watch the output as your program progresses. # In[ ]: s3_site = deploy_project("my_first_project", s3_static_site, plugins=[("aws", "v4.0.0")], config={"aws:region": auto.ConfigValue(value="us-west-2")}) # ### Using stack outputs # # Now that our stack is deployed, let's make sure everything was deployed correctly by making a request to the URL we exported. # In[ ]: outputs = s3_site.outputs() url = f"http://{outputs['website_url'].value}" # In[ ]: import requests site_content = requests.get(url).text site_content # Cool! Looks like we got some HTML back. Let's display it in our notebook using IPython. # In[ ]: from IPython.core.display import HTML HTML(site_content) # Alright, that looks much better. We can even open our website in a new browser tab. # In[ ]: import webbrowser outputs = s3_site.outputs() webbrowser.open(url) # ### Clean up # # Now that we're done testing everything out, we can destroy our stack. # In[ ]: destroy_project("my_first_project")