diff --git a/azure-py-webserver/Pulumi.yaml b/azure-py-webserver/Pulumi.yaml new file mode 100644 index 000000000..ddd3ac21b --- /dev/null +++ b/azure-py-webserver/Pulumi.yaml @@ -0,0 +1,3 @@ +name: azure-py-webserver +runtime: python +description: An Azure example using Python to provision a webserver diff --git a/azure-py-webserver/README.md b/azure-py-webserver/README.md new file mode 100644 index 000000000..bf2aaefdd --- /dev/null +++ b/azure-py-webserver/README.md @@ -0,0 +1,138 @@ +[![Deploy](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new) + +# Azure Web Server example in Python + +This example deploys an Azure Virtual Machine and starts a HTTP server on it. + +## Prerequisites + +1. [Install Pulumi](https://pulumi.io/install/) +1. [Configure Pulumi for Azure](https://pulumi.io/quickstart/azure/setup.html) +1. [Configure Pulumi for Python](https://pulumi.io/reference/python.html) + +## Deploying and running the program + +1. Set up a virtual Python environment and install dependencies + + ``` + $ virtualenv -p python3 venv + $ source venv/bin/activate + $ pip install -r requirements.txt + ``` + +1. Create a new stack: + + ``` + $ pulumi stack init azure-py-webserver + ``` + +1. Set the Azure environment: + + ``` + $ pulumi config set azure:environment public + ``` + +1. Set the required configuration for this example. This example requires you to supply a username and password to +the virtual machine that we are going to create. + + ``` + $ pulumi config set azure-web:username myusername + ``` + + The password is a secret, so we can ask Pulumi to encrypt the configuration: + + ``` + $ pulumi config set --secret azure-web:password Hunter2hunter2 + ``` + +1. Run `pulumi up` to preview and deploy the changes: + + ``` + $ pulumi update + Previewing update (azuredev): + + Type Name Plan + + pulumi:pulumi:Stack azure-py-webserver-azuredev create + + ├─ azure:core:ResourceGroup server create + + ├─ azure:network:VirtualNetwork server-network create + + ├─ azure:network:PublicIp server-ip create + + ├─ azure:network:Subnet server-subnet create + + ├─ azure:network:NetworkInterface server-nic create + + └─ azure:compute:VirtualMachine server-vm create + + Resources: + + 7 to create + + Do you want to perform this update? yes + Updating (azuredev): + + Type Name Status + + pulumi:pulumi:Stack azure-py-webserver-azuredev created + + ├─ azure:core:ResourceGroup server created + + ├─ azure:network:VirtualNetwork server-network created + + ├─ azure:network:PublicIp server-ip created + + ├─ azure:network:Subnet server-subnet created + + ├─ azure:network:NetworkInterface server-nic created + + └─ azure:compute:VirtualMachine server-vm created + + Outputs: + public_ip: "137.117.15.111" + + Resources: + + 7 created + + Duration: 2m55s + + Permalink: https://app.pulumi.com/swgillespie/azure-py-webserver/azuredev/updates/3 + ``` + +1. Get the IP address of the newly-created instance from the stack's outputs: + + ``` + $ pulumi stack output public_ip + 137.117.15.111 + ``` + +1. Check to see that your server is now running: + + ``` + $ curl http://$(pulumi stack output public_ip) + Hello, World! + ``` + +1. Destroy the stack: + + ``` + ▶ pulumi destroy --yes + Previewing destroy (azuredev): + + Type Name Plan + - pulumi:pulumi:Stack azure-py-webserver-azuredev delete + - ├─ azure:compute:VirtualMachine server-vm delete + - ├─ azure:network:NetworkInterface server-nic delete + - ├─ azure:network:Subnet server-subnet delete + - ├─ azure:network:PublicIp server-ip delete + - ├─ azure:network:VirtualNetwork server-network delete + - └─ azure:core:ResourceGroup server delete + + Resources: + - 7 to delete + + Destroying (azuredev): + + Type Name Status + - pulumi:pulumi:Stack azure-py-webserver-azuredev deleted + - ├─ azure:compute:VirtualMachine server-vm deleted + - ├─ azure:network:NetworkInterface server-nic deleted + - ├─ azure:network:Subnet server-subnet deleted + - ├─ azure:network:VirtualNetwork server-network deleted + - ├─ azure:network:PublicIp server-ip deleted + - └─ azure:core:ResourceGroup server deleted + + Resources: + - 7 deleted + + Duration: 3m49s + + Permalink: https://app.pulumi.com/swgillespie/azure-py-webserver/azuredev/updates/4 + ``` diff --git a/azure-py-webserver/__main__.py b/azure-py-webserver/__main__.py new file mode 100644 index 000000000..f5c1dd711 --- /dev/null +++ b/azure-py-webserver/__main__.py @@ -0,0 +1,79 @@ +import pulumi +from pulumi import Output +from pulumi_azure import core, compute, network + +config = pulumi.Config("azure-web") +username = config.require("username") +password = config.require("password") + +resource_group = core.ResourceGroup("server", location="West US") +net = network.VirtualNetwork( + "server-network", + resource_group_name=resource_group.name, + location=resource_group.location, + address_spaces=["10.0.0.0/16"], + subnets=[{ + "name": "default", + "address_prefix": "10.0.1.0/24", + }]) + +subnet = network.Subnet( + "server-subnet", + resource_group_name=resource_group.name, + virtual_network_name=net.name, + address_prefix="10.0.2.0/24") +public_ip = network.PublicIp( + "server-ip", + resource_group_name=resource_group.name, + location=resource_group.location, + allocation_method="Dynamic") + +network_iface = network.NetworkInterface( + "server-nic", + resource_group_name=resource_group.name, + location=resource_group.location, + ip_configurations=[{ + "name": "webserveripcfg", + "subnet_id": subnet.id, + "private_ip_address_allocation": "Dynamic", + "public_ip_address_id": public_ip.id, + }]) + +userdata = """#!/bin/bash + +echo "Hello, World!" > index.html +nohup python -m SimpleHTTPServer 80 &""" + +vm = compute.VirtualMachine( + "server-vm", + resource_group_name=resource_group.name, + location=resource_group.location, + network_interface_ids=[network_iface.id], + vm_size="Standard_A0", + delete_data_disks_on_termination=True, + delete_os_disk_on_termination=True, + os_profile={ + "computer_name": "hostname", + "admin_username": username, + "admin_password": password, + "custom_data": userdata, + }, + os_profile_linux_config={ + "disable_password_authentication": False, + }, + storage_os_disk={ + "create_option": "FromImage", + "name": "myosdisk1", + }, + storage_image_reference={ + "publisher": "canonical", + "offer": "UbuntuServer", + "sku": "16.04-LTS", + "version": "latest", + }) + +combined_output = Output.all(vm.id, public_ip.name, + public_ip.resource_group_name) +public_ip_addr = combined_output.apply( + lambda lst: network.get_public_ip(name=lst[1], resource_group_name=lst[2])) +pulumi.export("public_ip", public_ip_addr.ip_address) diff --git a/azure-py-webserver/requirements.txt b/azure-py-webserver/requirements.txt new file mode 100644 index 000000000..f90ac13b3 --- /dev/null +++ b/azure-py-webserver/requirements.txt @@ -0,0 +1,2 @@ +pulumi>=0.17.2 +pulumi_azure>=0.17.3 diff --git a/misc/test/examples_test.go b/misc/test/examples_test.go index cb5842525..f8b8d4d23 100644 --- a/misc/test/examples_test.go +++ b/misc/test/examples_test.go @@ -319,6 +319,17 @@ func TestExamples(t *testing.T) { // }) // }, }), + base.With(integration.ProgramTestOptions{ + Dir: path.Join(cwd, "..", "..", "azure-py-webserver"), + Config: map[string]string{ + "azure:environment": azureEnviron, + "azure-web:username": "myusername", + "azure-web:password": "Hunter2hunter2", + }, + ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { + assertHTTPHelloWorld(t, stack.Outputs["public_ip"]) + }, + }), // TODO: This test fails due to a bug in the Terraform Azure provider in which the // service principal is not available when attempting to create the K8s cluster. // See the azure-ts-aks-example readme for more detail.