From e9a63d865357d313c7535ccb3a00bbbec9dc1440 Mon Sep 17 00:00:00 2001 From: Carlos Nunez Date: Sat, 18 Nov 2017 15:27:54 -0600 Subject: [PATCH 1/2] Add instructions for the Python client. Azure uses the Python client in their Docker image. I've added additional documentation on how to "install" it as well as translations the node.js commands for it. --- .../source/docs/builders/azure-setup.html.md | 105 +++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) diff --git a/website/source/docs/builders/azure-setup.html.md b/website/source/docs/builders/azure-setup.html.md index 90c1a6e73..86522642d 100644 --- a/website/source/docs/builders/azure-setup.html.md +++ b/website/source/docs/builders/azure-setup.html.md @@ -63,6 +63,14 @@ If you already have node.js installed you can use `npm` to install `azure-cli`: $ npm install -g azure-cli --no-progress ``` +You can also use the Python-based Azure CLI in Docker. It also comes with `jq` pre-installed: + +```shell +$ docker run -it azuresdk/azure-cli-python +``` + +As there are differences between the node.js client and the Python client, we've included commands for the Python client underneath each node.js command. + ## Guided Setup The Packer project includes a [setup script](https://github.com/hashicorp/packer/blob/master/contrib/azure-setup.sh) that can help you setup your account. It uses an interactive bash script to log you into Azure, name your resources, and export your Packer configuration. @@ -80,6 +88,32 @@ $ azure config mode arm $ azure login -u USERNAME ``` +If you're using the Python client: + +```shell +$ az login +# To sign in, use a web browser to open the page https://aka.ms/devicelogin and enter the code CODE_PROVIDED to authenticate +``` + +Once you've completed logging in, you should get a JSON array like the one below: + +```shell +[ + { + "cloudName": "AzureCloud", + "id": "$uuid", + "isDefault": false, + "name": "Pay-As-You-Go", + "state": "Enabled", + "tenantId": "$tenant_uuid", + "user": { + "name": "my_email@anywhere.com", + "type": "user" + } + } +] + +``` Get your account information ``` shell @@ -88,6 +122,11 @@ $ azure account set ACCOUNTNAME $ azure account show --json | jq -r ".[] | .id" ``` +Python: +```shell +$ az account set "$(az account list | jq -r '.[].name')" +``` + -> Throughout this document when you see a command pipe to `jq` you may instead omit `--json` and everything after it, but the output will be more verbose. For example you can simply run `azure account list` instead. This will print out one line that look like this: @@ -107,6 +146,12 @@ $ azure location list $ azure group create -n GROUPNAME -l LOCATION ``` +Python: + +```shell +$ az group create -n GROUPNAME -l LOCATION +``` + Your storage account (below) will need to use the same `GROUPNAME` and `LOCATION`. ### Create a Storage Account @@ -121,9 +166,15 @@ $ azure storage account create \ --kind storage STORAGENAME ``` --> `LRS` is meant as a literal "LRS" and not as a variable. +Python: -Make sure that `GROUPNAME` and `LOCATION` are the same as above. +```shell +$ az storage account create -n STORAGENAME -g GROUPNAME -l LOCATION --sku Standard_LRS +``` + +-> `LRS` and `Standard_LRS` are meant as literal "LRS" or "Standard_LRS" and not as variables. + +Make sure that `GROUPNAME` and `LOCATION` are the same as above. Also, ensure that `GROUPNAME` is less than 24 characters long and contains only lowercase letters and numbers. ### Create an Application @@ -137,6 +188,12 @@ $ azure ad app create \ -p PASSWORD ``` +Python: + +```shell +az ad app create --display-name APPNAME --identifier-uris APPURL --homepage APPURL --password PASSWORD +``` + Password is your `client_secret` and can be anything you like. I recommend using `openssl rand -base64 24`. ### Create a Service Principal @@ -153,6 +210,13 @@ $ azure ad app list --json \ $ azure ad sp create --applicationId APPID ``` +Python: + +```shell +$ id=$(az ad app list | jq -r '.[] | select(.displayName == "Packer") | .appId') +$ az ad sp create --appid "$id" +``` + ### Grant Permissions to Your Application Finally, we will associate the proper permissions with our application's service principal. We're going to assign the `Owner` role to our Packer application and change the scope to manage our whole subscription. (The `Owner` role can be scoped to a specific resource group to further reduce the scope of the account.) This allows Packer to create temporary resource groups for each build. @@ -164,6 +228,13 @@ $ azure role assignment create \ -c /subscriptions/SUBSCRIPTIONID ``` +Python: + +```shell +# NOTE: Trying to assign the role to the service principal by name directly yields a HTTP 400 error. See: https://github.com/Azure/azure-cli/issues/4911 +$ az role assignment create --assignee "$(az ad sp list | jq -r '.[] | select(.displayName == "APPNAME") | .objectId')" --role Owner +``` + There are a lot of pre-defined roles and you can define your own with more granular permissions, though this is out of scope. You can see a list of pre-configured roles via: ``` shell @@ -182,6 +253,12 @@ $ azure account show --json \ | jq ".[] | .id" ``` +Python: + +```shell +$ az account show | jq -r '.id' +``` + Get `client_id` ``` shell @@ -189,6 +266,12 @@ $ azure ad app list --json \ | jq '.[] | select(.displayName | contains("APPNAME")) | .appId' ``` +Python + +```shell +$ az ad app list | jq '.[] | select(.displayName | contains("APPNAME")) | .appId' +``` + Get `client_secret` This cannot be retrieved. If you forgot this, you will have to delete and re-create your service principal and the associated permissions. @@ -199,14 +282,32 @@ Get `object_id` (OSTYpe=Windows only) azure ad sp show -n CLIENT_ID ``` +Python: + +```shell +$ az ad sp show -n CLIENT_ID +``` + Get `resource_group_name` ``` shell $ azure group list ``` +Python: + +```shell +$ az group list | jq '.[] | select(.name=="GROUPNAME") | .id' +``` + Get `storage_account` ``` shell $ azure storage account list ``` + +Python: + +```shell +$ az storage account list | jq '.[] | select(.name=="GROUPNAME") | .id' +``` From a8ff095059c1188e013be9af6a4e64c496e073bd Mon Sep 17 00:00:00 2001 From: Carlos Nunez Date: Sat, 18 Nov 2017 15:44:10 -0600 Subject: [PATCH 2/2] Generate a JSON object for Packer This changes adds code that will generate a JSON object containing the things you'll need to make Packer work with Azure. --- .../source/docs/builders/azure-setup.html.md | 46 +++++++------------ 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/website/source/docs/builders/azure-setup.html.md b/website/source/docs/builders/azure-setup.html.md index 86522642d..618e38543 100644 --- a/website/source/docs/builders/azure-setup.html.md +++ b/website/source/docs/builders/azure-setup.html.md @@ -246,6 +246,23 @@ $ azure role list --json \ Now (finally) everything has been setup in Azure. Let's get our configuration keys together: +Python: + +```shell +$ cat < { +> "subscription_id": $(az account show | jq '.id'), +> "client_id": $(az ad app list | jq '.[] | select(.displayName == "Packer") | .appId'), +> "client_secret": "$password", +> "location": "$location", +> "tenant_id": $(az account show | jq '.tenantId') +> "object_id": $(az ad app list | jq '.[] | select(.displayName == "Packer") | .objectId') +> } +> EOF +``` + +node.js: + Get `subscription_id`: ``` shell @@ -253,12 +270,6 @@ $ azure account show --json \ | jq ".[] | .id" ``` -Python: - -```shell -$ az account show | jq -r '.id' -``` - Get `client_id` ``` shell @@ -266,11 +277,6 @@ $ azure ad app list --json \ | jq '.[] | select(.displayName | contains("APPNAME")) | .appId' ``` -Python - -```shell -$ az ad app list | jq '.[] | select(.displayName | contains("APPNAME")) | .appId' -``` Get `client_secret` @@ -282,32 +288,14 @@ Get `object_id` (OSTYpe=Windows only) azure ad sp show -n CLIENT_ID ``` -Python: - -```shell -$ az ad sp show -n CLIENT_ID -``` - Get `resource_group_name` ``` shell $ azure group list ``` -Python: - -```shell -$ az group list | jq '.[] | select(.name=="GROUPNAME") | .id' -``` - Get `storage_account` ``` shell $ azure storage account list ``` - -Python: - -```shell -$ az storage account list | jq '.[] | select(.name=="GROUPNAME") | .id' -```