Skip to content

Commit

Permalink
docs: How-to create WSL instances on multiple Windows machines via La…
Browse files Browse the repository at this point in the history
…ndscape API (#928)

This PR makes heavy use of tabbed code blocks to show alternative ways
to do things with Bash and PowerShell.


![image](https://github.com/user-attachments/assets/a35f3c82-2094-404d-8a68-f13a3c2ccf6c)

Both alternatives were tested. The bash one comes from the [Landscape
documentation](https://ubuntu.com/landscape/docs/use-a-specific-ubuntu-image-source-for-wsl#installing-with-a-custom-image),
I essentially extracted the piece I was more concerned about (custom
rootfs with cloud-init data) and made a self-contained guide out of it
(plus translating to PowerShell).

---

UDENG-4694
  • Loading branch information
CarlosNihelton authored Oct 4, 2024
2 parents 68db565 + 3fb6531 commit e82d9cb
Show file tree
Hide file tree
Showing 7 changed files with 330 additions and 8 deletions.
2 changes: 2 additions & 0 deletions docs/.custom_wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@ fpath
FQDN
HIPAA
Hostagent
JWT
LTS
os
pem
repo
rootfs
SLA
SRU
SSL
Expand Down
5 changes: 3 additions & 2 deletions docs/custom_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,13 @@
# Links to ignore when checking links
linkcheck_ignore = [
'http://127.0.0.1:8000',

# Linkcheck does not have access to the repo
'https://github.com/canonical/ubuntu-pro-for-wsl/*',
# This page redirects to SSO login:
'https://ubuntu.com/pro/dashboard',
]
# Only users logged in to MS Store with their account registered for beta can access this link
'https://apps.microsoft.com/detail/9PD1WZNBDXKZ',
]

# Pages on which to ignore anchors
# (This list will be appended to linkcheck_anchors_ignore_for_url)
Expand Down
318 changes: 318 additions & 0 deletions docs/howto/custom-rootfs-multiple-targets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
# How-to deploy a custom rootfs across multiple Windows machines with the Landscape API

This guide shows how to use the Landscape API to automate the deployment of a custom rootfs across multiple Windows machines.
Scaled deployment is enabled by Ubuntu Pro for WSL, which ensures that Ubuntu WSL instances on Windows machines are automatically registered with Landscape.
Cloud-init is used for initialisation and final configuration of the instances.
To follow the steps outlined in this guide you can use either:
- Bash scripting on Linux, or
- PowerShell scripting on Windows

## Prerequisites

- A running self-hosted Landscape server version `24.10~beta.5` or later.
- Multiple Windows machines [already registered with Landscape](howto::config-landscape) via Ubuntu Pro for WSL.
- Make sure you have installed `curl` and `jq`, if you're following this guide using Bash.
- Familiarity with Bash and/or PowerShell.

## Prepare the environment

For convenience when writing subsequent commands, first export the following environment variables, modifying the values that are assigned as needed:

`````{tabs}
````{group-tab} Bash
```bash
# Credentials to authenticate the API requests
export [email protected]
export LANDSCAPE_USER_PASSWORD=mib
export LANDSCAPE_URL=https://landscape.mib.com
# The URL of the custom rootfs to be deployed
export ROOTFS_URL="http://landscape.mib.com:9009/ubuntu-24.04-custom.tar.gz"
# The list of IDs of the different Windows machines on which we are going to deploy WSL instances
export PARENT_COMPUTER_IDS=(26 30 31)
# The name of the WSL instance to be created
export COMPUTER_NAME=Carbonizer
# Path to the cloud-config file whose contents will be used to initialize the WSL instances
export CLOUD_INIT_FILE="~/Downloads/init.yaml"
```
````
````{group-tab} PowerShell
```powershell
# Credentials to authenticate the API requests
$LANDSCAPE_USER_EMAIL="[email protected]"
$LANDSCAPE_USER_PASSWORD="mib"
$LANDSCAPE_URL="https://landscape.mib.com"
# The URL of the custom rootfs to be deployed
$ROOTFS_URL="http://landscape.mib.com:9009/ubuntu-24.04-custom.tar.gz"
# The list of IDs of the different Windows machines on which we are going to deploy WSL instances
$PARENT_COMPUTER_IDS=@(26, 30, 31)
# The name of the WSL instance to be created
$COMPUTER_NAME="Carbonizer"
# Path to the cloud-config file whose contents will be used to initialize the WSL instances
$CLOUD_INIT_FILE="~\Downloads\init.yaml"
```
````
`````


Generate a Base64-encoded string with the cloud-config data:

`````{tabs}
````{group-tab} Bash
```bash
BASE64_ENCODED_CLOUD_INIT=$(cat $CLOUD_INIT_FILE | base64 --wrap=0)
```
````
````{group-tab} PowerShell
```powershell
$content = Get-Content -Path $CLOUD_INIT_FILE -Raw
$bytes = [System.Text.Encoding]::UTF8.GetBytes($content)
$BASE64_ENCODED_CLOUD_INIT = [System.Convert]::ToBase64String($bytes)
```
````
`````

## Authenticate against the Landscape API

Build the authentication payload of the form: `{"email": "[email protected]", "password": "mib"}` using the values exported in prior steps:

`````{tabs}
````{group-tab} Bash
```bash
LOGIN_JSON=$( jq -n \
--arg em "$LANDSCAPE_USER_EMAIL" \
--arg pwd "$LANDSCAPE_USER_PASSWORD" \
'{email: $em, password: $pwd}' )
```
````
````{group-tab} PowerShell
```powershell
$LOGIN_JSON = @{
email = "$LANDSCAPE_USER_EMAIL"
password = "$LANDSCAPE_USER_PASSWORD"
} | ConvertTo-Json
```
````
`````

Issue an authenticate request and retrieve the JSON web token (JWT) to be used in the subsequent API requests.

`````{tabs}
````{group-tab} Bash
```bash
LOGIN_RESPONSE=$( curl -s -X POST "$LANDSCAPE_URL/api/v2/login" \
--data "$LOGIN_JSON" \
--header "Content-Type: application/json" \
--header "Accept: application/json" )
JWT=$( echo $LOGIN_RESPONSE | jq .token | tr -d '"')
```
````
````{group-tab} PowerShell
```powershell
$LOGIN_RESPONSE = Invoke-WebRequest -Method POST `
-URI "$LANDSCAPE_URL/api/v2/login" `
-Body "$LOGIN_JSON" -ContentType "application/json"
$JWT = ConvertTo-SecureString -AsPlainText -Force $( $LOGIN_RESPONSE.Content | ConvertFrom-Json).token
```
````
`````

## Send the Install request

Build the payload with information about the WSL instance to be deployed. In this case it would look like:

```json
{"rootfs_url": "http://landscape.mib.com:9009/ubuntu-24.04-custom.tar.gz", "computer_name": "Carbonizer", "cloud_init": "<base64 encoded material>"}
```

`````{tabs}
````{group-tab} Bash
```bash
WSL_JSON=$( jq -n \
--arg rf "$ROOTFS_URL" \
--arg cn "$COMPUTER_NAME" \
--arg b64 "$BASE64_ENCODED_CLOUD_INIT" \
'{rootfs_url: $rf, computer_name: $cn, cloud_init: $b64}' )
```
````
````{group-tab} PowerShell
```powershell
$WSL_JSON = @{
rootfs_url = "$ROOTFS_URL"
computer_name = "$COMPUTER_NAME"
cloud_init = "$BASE64_ENCODED_CLOUD_INIT"
} | ConvertTo-Json
```
````
`````

At the moment of this writing there is no specific API endpoint to trigger
installation of WSL instances on multiple Windows machines at once.
Instead we send one request per target machine.

`````{tabs}
````{group-tab} Bash
```bash
for COMPUTER_ID in "${PARENT_COMPUTER_IDS[@]}"; do
API_RESPONSE=$( curl -s -X POST \
"$LANDSCAPE_URL/api/v2/computers/$COMPUTER_ID/children" \
--data "$WSL_JSON" \
--header "Authorization:Bearer $JWT" \
--header "Content-Type: application/json" \
--header "Accept: application/json" )
# show the response
echo $API_RESPONSE
echo
done
```
````
````{group-tab} PowerShell
```powershell
foreach ($COMPUTER_ID in $PARENT_COMPUTER_IDS) {
$API_RESPONSE = Invoke-WebRequest -Method POST -Body "$WSL_JSON" `
-Uri "$LANDSCAPE_URL/api/v2/computers/$COMPUTER_ID/children" `
-Authentication Bearer -Token $JWT -ContentType "application/json"
# show the response
Write-Output $API_RESPONSE
}
```
````
`````

When that completes, you'll be able to find activities in the Landscape
dashboard about the installation of a new WSL instance for each of the Windows
machines listed.

## Summarising the steps in a single script

The steps above can be made into a single script:

`````{tabs}
````{group-tab} Bash
```bash
#!/usr/bin/env bash
# Base64-encoding the cloud-config file contents
BASE64_ENCODED_CLOUD_INIT=$(cat $CLOUD_INIT_FILE | base64 --wrap=0)
# Build the auth payload
LOGIN_JSON=$( jq -n \
--arg em "$LANDSCAPE_USER_EMAIL" \
--arg pwd "$LANDSCAPE_USER_PASSWORD" \
'{email: $em, password: $pwd}' )
# Issue an auth request and retrieve the JWT
LOGIN_RESPONSE=$( curl -s -X POST "$LANDSCAPE_URL/api/v2/login" \
--data "$LOGIN_JSON" \
--header "Content-Type: application/json" \
--header "Accept: application/json" )
JWT=$( echo $LOGIN_RESPONSE | jq .token | tr -d '"')
# Build the installation payload
WSL_JSON=$( jq -n \
--arg rf "$ROOTFS_URL" \
--arg cn "$COMPUTER_NAME" \
--arg b64 "$BASE64_ENCODED_CLOUD_INIT" \
'{rootfs_url: $rf, computer_name: $cn, cloud_init: $b64}' )
# Issue the command for each Windows machine
for COMPUTER_ID in "${PARENT_COMPUTER_IDS[@]}"; do
API_RESPONSE=$( curl -s -X POST \
"$LANDSCAPE_URL/api/v2/computers/$COMPUTER_ID/children" \
--data "$WSL_JSON" \
--header "Authorization:Bearer $JWT" \
--header "Content-Type: application/json" \
--header "Accept: application/json" )
# show the response
echo $API_RESPONSE
echo
done
```
````
````{group-tab} PowerShell
```powershell
# Base64-encoding the cloud-config file contents
$content = Get-Content -Path $CLOUD_INIT_FILE -Raw
$bytes = [System.Text.Encoding]::UTF8.GetBytes($content)
$BASE64_ENCODED_CLOUD_INIT = [System.Convert]::ToBase64String($bytes)
# Build the auth payload
$LOGIN_JSON = @{
email = "$LANDSCAPE_USER_EMAIL"
password = "$LANDSCAPE_USER_PASSWORD"
} | ConvertTo-Json
# Issue an auth request and retrieve the JWT
$LOGIN_RESPONSE = Invoke-WebRequest -Method POST `
-URI "$LANDSCAPE_URL/api/v2/login" `
-Body "$LOGIN_JSON" -ContentType "application/json"
$JWT = ConvertTo-SecureString -AsPlainText -Force $( $LOGIN_RESPONSE.Content | ConvertFrom-Json).token
# Build the installation payload
$WSL_JSON = @{
rootfs_url = "$ROOTFS_URL"
computer_name = "$COMPUTER_NAME"
cloud_init = "$BASE64_ENCODED_CLOUD_INIT"
} | ConvertTo-Json
# Issue the command for each Windows machine
foreach ($COMPUTER_ID in $PARENT_COMPUTER_IDS) {
$API_RESPONSE = Invoke-WebRequest -Method POST -Body "$WSL_JSON" `
-Uri "$LANDSCAPE_URL/api/v2/computers/$COMPUTER_ID/children" `
-Authentication Bearer -Token $JWT -ContentType "application/json"
# show the response
Write-Output $API_RESPONSE
}
```
````
`````

## Further reading

- Visit [the Landscape API documentation](https://ubuntu.com/landscape/docs/make-rest-api-requests) to learn more about it.
- [Landscape documentation about WSL integration](https://ubuntu.com/landscape/docs/use-a-specific-ubuntu-image-source-for-wsl)
contains more information about this and other methods of creating WSL
instances on Windows machines registered with Landscape via its REST API.
1 change: 1 addition & 0 deletions docs/howto/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ Back up and restore Ubuntu WSL instances <backup-and-restore>
Uninstalling UP4W, Ubuntu WSL apps and WSL <uninstalling>
Configure the Landscape client with UP4W <set-up-landscape-client>
Set up a Landscape server within WSL <set-up-landscape-server-in-wsl>
Create WSL instances on multiple Windows machines with the Landscape API <custom-rootfs-multiple-targets>
```
2 changes: 1 addition & 1 deletion docs/howto/set-up-up4w.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ You should also verify that the [firewall rules are correctly set up](../referen
The install link below will work only if you're logged in to the Microsoft Store with an account for which access to the app has been enabled.
```

You can install UP4W [on this page of the Microsoft Store](https://www.microsoft.com/store/productId/9PD1WZNBDXKZ):
You can install UP4W [on this page of the Microsoft Store](https://apps.microsoft.com/detail/9PD1WZNBDXKZ):

![Install Ubuntu Pro for WSL from the Store](./assets/store.png)

Expand Down
4 changes: 2 additions & 2 deletions docs/reference/firewall_requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The following table lists the default ports and protocols used by Ubuntu Pro for

| Description | Client System | Server System | Protocol | Default Port | Target address |
|-------------|---------------|---------------|----------|--------------|----------------|
| Required for online installation of WSL instances[^1].|Windows Host / Pro Agent |MS Store | tcp | https (443) | See [Microsoft documentation](https://learn.microsoft.com/en-us/microsoft-store/prerequisites-microsoft-store-for-business) for a list of addresses to allow. |
| Required for online installation of WSL instances[^1].|Windows Host / Pro Agent |MS Store | tcp | https (443) | See [Microsoft documentation](https://learn.microsoft.com/en-us/windows/privacy/manage-windows-11-endpoints) for a list of addresses to allow. |
| Ubuntu Pro enablement[^2] | Windows Host / Pro Agent |Canonical Contract Server |tcp | https (443) | `contracts.canonical.com` |
| Landscape management[^2] | Windows Host / Pro Agent | Landscape Server | tcp | grpc (6554) | On-premise Landscape address |
| WSL instance management on the Windows host. Firewall rules set up at installation time of the WSL Pro agent. | WSL Instance / wsl-pro-service | Windows Host / Pro Agent | tcp | grpc (dynamic:49152-65535) | Hyper-V Virtual Ethernet Adapter IP |
Expand All @@ -19,7 +19,7 @@ The following table lists the default ports and protocols used by Ubuntu Pro for

If the client system is behind a proxy, ensure that the proxy is configured to allow the required connections.

[^1]: [Access to the Microsoft Store](https://learn.microsoft.com/en-us/microsoft-store/prerequisites-microsoft-store-for-business) is required for the online installation of WSL instances. Without it Ubuntu Pro for WSL will still be functional but it will not be possible to install WSL instances centrally from Landscape. In this case WSL instances have to be installed manually on the Windows hosts.
[^1]: [Access to the Microsoft Store](https://learn.microsoft.com/en-us/windows/privacy/manage-windows-11-endpoints) is required for the online installation of WSL instances. Without it Ubuntu Pro for WSL will still be functional but it will only be possible to install WSL instances centrally from Landscape from custom tarballs, not using the official Ubuntu releases.

[^2]: Access to the contract server and Landscape server is required for proper operation of Ubuntu Pro for WSL.

Expand Down
Loading

0 comments on commit e82d9cb

Please sign in to comment.