Version 2
The Qwiklabs Bundle Spec provides an interchange format for qwiklabs resources (entities) across our ecosystem. The spec is used extensively to support our content distribution and processes across many customer deployments and has allowed us a great deal of systemization and automation.
While not explicitly referenced in the spec, perhaps the most important
contribution is the notion of "universal" content identifiers (content_id
)
that let us easily reference and report on particular pieces of content across
multiple systems and databases. These identifiers are always included as a
required parameter for requests made to our PublisherApi to create, update or
delete content. Though the content_id
is required, the bundle spec does not
actually specify how they should be constructed, rather that is up to a
particular integrator to determine for themselves.
At Qwiklabs we view content management for labs, quizzes, quests and courses much like software source control management and so we have decided to use GitHub as our CMS/persistence layer on top of the Qwiklabs Bundle Spec.
GitWhisperer is our integration layer for content creators and helps with content management in many critical ways by
- generating content_ids and inferring base fields based on the Git repo layout
- using QL_OWNER files to assign lab ownership on Qwiklabs staging deployments
- simplifying the translation pipeline
- compiling instructions from Markdown to HTML so production applications can be unified for rendering
- handling common instruction fragments
- compiling Activity Tracking assessments from individual base "step" files
For GitHub integration, the base repository (e.g. gcp-spl-content) has
directories for each of the supported entity types, (e.g. labs, learning_paths,
quizzes) and additionally a "/fragments" directory that contains common
instruction sections that are included by reference, such as "copyright". The
content_id
from GitWhisperer concatenates the repo name, which we commonly
call "library" and the directory name for a particular lab which we call the lab
"slug". In this way, GitWhisperer assigns both a content_id
and the base
entity
type through examining the directory structure of the repository.
The other special file is QL_OWNER which contains the email address of the user on the target staging deployment to assign ownership and edit privileges.
For a particular entity type, such as V2 Labs, the GitHub bundle files will differ in common ways from those specified in the Qwiklabs Bundle Spec interchange format. The reason for this is to simplify localization and content management for the authoring team and is illustrated below.
Sometimes it is useful to reference values that are unknown at authoring time. For example, an author may specify that a GCP Project username should be displayed to the student, but this username is not known until the lab is started. To address this, we have the concept of resource references.
A resource reference has the form [RESOURCE_ID].[RESOURCE_ATTRIBUTE]
. Each
environment resource type has an allowed set of attributes that can be
referenced, defined in its "Valid resource references" section. When an
attribute has type "resource reference", the value should be of this form.
Currently, this can only be used for student_visible_outputs
,
custom_properties
(of startup/cleanup scripts), and activity tracking
resources
objects. In the future, we hope to allow interpolating these
resource references into the lab instructions.
Here's a sample qwiklabs.yaml
file, with all nested details removed to make it
easier to see the general file structure.
entity_type: Lab
schema_version: 2
default_locale: en
title: Best Lab Ever
description: "No, seriously. It's the best lab ever. You're going to love it!"
duration: 60
max_duration: 90
level: introductory
tags: [sample, life-changing, gcp]
# Other resources the learner may access while taking this lab
resources: ...
environment:
# Lab resources that are provisioned by Qwiklabs
# e.g. cloud account, databases, etc.
resources: ...
# Properties of the lab environment that are displayed to the user
# e.g. gcp project, username, password. etc.
student_visible_outputs: ...
# Checkpoint evaluation and quiz data
assessment: ...
Two properties are critical for specifying your lab bundle:
-
entity_type
This should be set to
Lab
for lab bundles. For other entity types such asCourseTemplate
orQuiz
please see their own bundle spec. -
schema_version
This notes which version of this spec the bundle is using. The valid values are
1
(deprecated) and2
(current).
The lab bundle MUST specify a default_locale
. It corresponds to the locale
that the lab is originally authored in. Authoring tools can use this as a hint
to notify localizers when content in the default locale is updated. Also, it
provides a hint to the learner interface about which locale to display if an
instruction/resource is not localized for the learner's current locale.
Add an additional locale specific file of the form "qwiklabs.xx.yaml" for each
locale to be included. For example, Japanese entries would be in a file named
"qwiklabs.ja.yaml". Within a single piece of content, be sure to use consistent
ids
across all the "qwiklabs.xx.yaml" files for data that contains lists of
objects.
attribute | required | type | notes |
---|---|---|---|
title | ✓ | string | Unique Lab title that a student sees |
description | ✓ | string | |
duration | ✓ | integer | Amount of time it should take an average learner to complete the lab (in minutes) |
max_duration | integer | Maximum duration time of a lab, displayed in student timer (in minutes) | |
credits | integer | ||
level | enum | One of ['introductory', 'intermediate', 'advanced'] | |
logo | file path | ||
tags | array | ||
legacy_display_options | array | Elements to hide/show in ql-lab-control-panel widget |
Place your instruction files under "instructions" folder. Name of the file MUST be same as the locale key.
Your folder structure may look like:
my-lab/instructions/en.md
my-lab/instructions/es.html
You can only have one file per locale.
attribute | required | type | notes |
---|---|---|---|
type | ✓ | enum | [See list of valid types below] |
uri | ✓ | string | Relative path to a file |
instruction:
type: html
uri: instructions/en.html
html
pdf
md
Markdown (MD) or HTML are the preferred formats for stored instructions. PDFs will be displayed embedded in the learner interface, but will lack any navigation or interactive functionality.
There are benefits to formatting lab instructions as HTML.
- Instruction styling will be updated automatically as the Qwiklabs interface evolves.
- Qwiklabs will help users navigate within your instruction document with a table of contents or direct links. It will also remember the learner's location in the document if they leave the page.
- Authors can specify interactive elements that will be displayed inline with your instructions in the learner's interface (quizzes, checkpoints, etc).
However, we will not accept arbitrary HTML. Your input will be heavily scrubbed.
- Only a standard subset of HTML elements will be supported (
<h1>
,<p>
,<strong>
, etc). All other tags will be stripped out of displayed content. - All styling will be removed.
- All scripting will be removed.
See the Instruction part of the HTML spec for details.
- In HTML/MD instruction files you can optionally add fragments.
- Fragments are reusable pieces of instructions that you would use in multiple lab instructions files.
The syntax is:
![[/<folder>/<file_name>]]
where is the relative folder path where the fragment is and <file_name> is the sub folder where the fragment file resides. The naming convention for the fragment file is .md or .html where locale is the two-letter locale for the fragment.
For example:
![[/fragment/gcpconsole]]
will include a fragment that resides in /fragment/gcpconsole/en.md for the English locale and /fragment/gcpconsole/es.md for the Spanish locale.
Resources are additional materials that learners may refer to while taking this lab.
See Resource Spec for details.
The sandbox learning environment is a key feature of Qwiklabs. As the author of a lab, you need to tell us which cloud accounts to provision for a learner, and what resources we should create in that account before handing it over to the learner.
The properties of each environment resource will depend on their type, e.g. AWS Accounts and Google Workspace admin users require different configuration data. However, there are three properties that all resources have regardless of type:
attribute | required | type | notes |
---|---|---|---|
type | ✓ | enum | [See list of valid types] |
id | string | Identifier that can be used throughout project bundle. | |
variant | string | The subtype resource being requested. Each type below lists its valid variants and specifies which is the default. |
environment:
resources:
- type: gcp_project
id: my_primary_project
- type: gcp_project
id: secondary_project
variant: gcpfree
- type: gcp_folder
id: my_primary_folder
- type: gcp_user
id: primary_user
variant: gcp_only
- type: gcp_user
id: secondary_user
attribute | required | type | notes |
---|---|---|---|
parent | string | If this project should reside inside a specific folder, the id of that folder. | |
startup_script | GCP Startup Script | GCP startup script object. GCP Projects support startup scripts of type deployment_manager and qwiklabs . |
|
ssh_key_user | string | If this project should use a user's SSH key, the id of that user. | |
allowed_locations | array | List of GCP regions or zones to set a default zone from. |
- type: gcp_project
id: my_primary_project
parent: my_primary_folder
variant: gcpfree
ssh_key_user: primary_user
allowed_locations:
- us-central1
- europe-west1-b
startup_script:
type: deployment_manager
path: dm_startup_dir
custom_properties:
- key: userNameWindows
value: student
- key: userName
reference: primary_user.local_username
- key: userPassword
reference: primary_user.password
- key: sshPubKey
reference: primary_user.public_key
NOTE: The existing concept of Qwiklabs' Fleets does not have a single analog in content bundles.
Some fleet types map to resource types (e.g.
gsuite_multi_tenant
fleet is now thegoogle_workspace_domain
resource type), while other fleets are allowed as "variants" ofgcp_project
(see below).
NOTE: Cleanup scripts are supported by invitation only. If you think you have a reason to use cleanup scripts, please get in touch with Qwiklabs engineering and we can discuss the specifics of cleanup scripts.
NOTE: To be able to use
userName
,userPassword
, orsshPubKey
within a startup script, the variable must be passed in as custom property
Every GCP project has a default zone set. When no zone is specified (either in a
gcloud command or API call), GCP will try to use this default zone in that call.
For example, attempting to create a GCE VM without specifying a location will
create a VM in this location. You can set this default zone by specifying values
in allowed_locations
. When a student launches a lab, Qwiklabs will set their
default zone to a random value from this list. If a region is specified,
Qwiklabs will randomly pick a zone in that region.
If a lab does not have particular region/zone requirements, we strongly
recommend specifying at least two whole regions in this list. Unless you have a
reason not to, we recommend ['us-west1', 'us-central1', 'us-east1']
as a good
starting point.
For backward compatibility, if allowed_locations
is not specified, we always
set the default zone to us-central1-a
. This is usually not an ideal value so
we recommend always setting allowed_locations
(even in the unlikely event that
us-central1-a
is the one and only zone you want this resource to default to).
The allowed variants are:
- gcpd [default]
- gcpfree
- gcpondemand
- gcp_very_low_base
- gcp_low_extra
- gcp_medium_extra
- gcp_high_extra
The valid reference
s for the gcp_project
resource are:
reference | displayed as |
---|---|
[PROJECT].project_id | copyable text |
[PROJECT].default_zone | copyable text |
[PROJECT].default_region | copyable text |
[PROJECT].console_url | button |
attribute | required | type | notes |
---|---|---|---|
startup_script | GCP Startup Script | GCP startup script object. GCP Users only support startup scripts of type qwiklabs . |
|
permissions | array | Array of project/roles(array) pairs and/or folder/roles(array) pairs |
- type: gcp_user
id: primary_user
permissions:
- project: my_primary_project
roles:
- roles/editor
- roles/bigquery.admin
- folder: my_primary_folder
roles:
- roles/compute.xpnAdmin
startup_script:
type: qwiklabs
path: ql_script_dir
custom_properties:
- key: userNameWindows
value: student
- key: userName
reference: primary_user.local_username
- key: userPassword
reference: primary_user.password
- key: sshPubKey
reference: primary_user.public_key
The gcp_user
type could more properly be called gaia_user
, since that's what
it provisions. However, the term gaia
is less well-known, so we stick with
gcp
.
The allowed variants are:
default
[default] - Has access to GCP and Workspace services (like Drive)gcp_only
- Only has access to GCP (Cloud Console, Cloud Shell, and Data Studio)extra
- Superset ofdefault
with access to some additional services. Usage of this variant is restricted.
The valid reference
s for the gcp_user
resource are:
reference | displayed as |
---|---|
[USER].username | copyable text |
[USER].local_username | NA |
[USER].password | copyable text |
[USER].ssh_key | PEM and PPK download buttons |
[USER].public_key | NA |
[USER].docs_url | button |
[USER].sheets_url | button |
[USER].slides_url | button |
[USER].gmail_url | button |
[USER].drive_url | button |
[USER].calendar_url | button |
[USER].app_sheet_url | button |
[USER].access_token | copyable text |
Note:
[USER].local_username
and[USER].public_key
are only meant to be used as custom properties within a startup script.[USER].access_token
requires special permissions to use for now.
All of the URLs will take the student to a new page where they can sign in with their temporary credentials and then get redirected to the requested Workspace service. When signing in, the temporary email address will be pre-filled but the student will still need to enter a password. If there are multiple temporary students in a lab (rare), we recommend creating different buttons for each one so each sign-in experience has the correct pre-filled email address.
For example, when creating a multi-user lab where two different users should do
different things in Google Docs, the environment
section in qwiklabs.yaml
should look something like:
environment:
resources:
- type: gcp_user
id: user_1
variant: default
- type: gcp_user
id: user_2
variant: default
student_visible_outputs:
- label: "Open Docs as First User"
reference: user_1.docs_url
- label: "Open Docs as Second User"
reference: user_2.docs_url
- label: "First User Email"
reference: user_1.username
- label: "Second User Email"
reference: user_2.username
- label: "Password (for both users)"
reference: user_1.password
No additional attributes for this resource
- type: gcp_folder
id: my_primary_folder
When working with folders, the users could be assigned the required roles on the folders similar to projects. Below is an example for assigning a user a specific role (roles/compute.xpnAdmin) on the folder resource 'my_primary_folder'.
- type: gcp_user
id: primary_user
permissions:
- folder: my_primary_folder
roles:
- roles/compute.xpnAdmin
The projects resources could also be associated with a specific folder by
specifying the parent
attribute in the gcp_project
resource. For example, if
you want to create a project 'my_primary_project' under folder
'my_primary_folder', then you can specify the parent attribute in the project
resource definition as following:
- type: gcp_project
id: my_primary_project
parent: my_primary_folder
The valid reference
s for the gcp_folder
resource are:
reference | displayed as |
---|---|
[FOLDER].folder_name | copyable text |
[FOLDER].display_name | copyable text |
attribute | required | type | notes |
---|---|---|---|
type | ✓ | string | The type of startup script. Only deployment_manager and qwiklabs are supported. |
path | ✓ | path | Relative path to a directory tree with the script contents. |
custom_properties | Custom Script Properties array | Array of custom script property objects. |
Any custom outputs being generated with a startup_script
can be reference as
[PROJECT/USER].startup_script.[FILL-IN-OUTPUT-NAME]. The output will be
displayed as a copyable text if provided within the student_visible_outputs.
See GCP Project or GCP User for examples.
Every GCP project or GCP user may have one startup script associated with it.
attribute | required | type | notes |
---|---|---|---|
key | ✓ | string | How the property will be referenced within the script. |
value | string | A static value to be passed into the script. | |
reference | resource reference | A resource reference object to be passed into the script. |
Custom script properties are passed into the script as input. A custom script property must have a key and either a value or a reference associated with it. For examples please see the GCP project or GCP user resource.
attribute | required | type | notes |
---|
No additional attributes
- type: google_workspace_domain
id: primary_domain
The valid reference
s for the google_workspace_domain
resource are:
reference | displayed as |
---|---|
[DOMAIN].console_url | button |
[DOMAIN].admin_username | copyable text |
[DOMAIN].admin_password | copyable text |
attribute | required | type | notes |
---|---|---|---|
permissions | ✓ | array | Array of project/roles(array) pairs |
startup_script.path | path | Relative path to a file with the script contents. |
- type: cloud_terminal
id: shell
permissions:
- project: my_primary_project
roles:
- roles/editor
startup_script:
path: startup.sh
Note: Due to a limitation in Qwiklabs, you must specify roles/editor
on
exactly one project when creating a cloud_terminal
resource. However, this
role is ignored and students will always get roles/owner
,
roles/storage.admin
, and roles/bigquery.admin
. The spec will be updated when
these limitations are fixed.
Student progress on cloud_terminal
instances can be inspected by either:
- Regular activity tracking on the student's project
- Directly running commands on the student's
cloud_terminal
instance with theRunRemoteCommand
handle
Assuming the resources in your lab looked like:
- type: gcp_project
id: project
- type: cloud_terminal
id: terminal
permissions:
- project: project
roles:
- roles/editor
You could check that a student created a VM in the project with either of these activity tracking snippets:
assessment:
passing_percentage: 100
steps:
- title: Create a VM
maximum_score: 100
student_messages:
success: Great job! You created a VM in GCP!
make_a_vm: Please make a VM by running gcloud compute instances create
services:
- project.ComputeV1
code: |-
def check(handles:, maximum_score:, resources:)
compute = handles['project.ComputeV1']
instances = compute.list_aggregated_instances&.items
if instances.count > 0
{ score: maximum_score, student_message: 'success' }
else
{ score: 0, student_message: 'make_a_vm' }
end
end
assessment:
passing_percentage: 100
steps:
- title: Create a VM
maximum_score: 100
student_messages:
success: Great job! You created a VM in GCP!
make_a_vm: Please make a VM by running gcloud compute instances create
services:
- terminal.RunRemoteCommand
code: |-
def check(handles:, maximum_score:, resources:)
terminal = handles['terminal.RunRemoteCommand']
response = terminal.run_remote_command 'gcloud compute instances list --format json'
instances = JSON.parse(response.stdout)
if instances.count > 0
{ score: maximum_score, student_message: 'success' }
else
{ score: 0, student_message: 'make_a_vm' }
end
end
For more information on using gcloud
, please consult the
official documentation on the Google Cloud SDK.
Startup scripts are executed from /home/student
as the student
user (the
same user the student uses cloud_terminal
as). Startup scripts can do
everything students can do including modifying the terminal (create files or
running git clone
) or modify the associated GCP project by running gcloud
,
gsutil
, and kubectl
commands.
attribute | required | type | notes |
---|---|---|---|
startup_script.path | path | Relative path to a file with the script contents. |
- type: linux_terminal
id: terminal
startup_script:
path: startup.sh
The allowed linux_terminal
variants are:
- it_cert [default] (can initiate internet connection via shared Cloud NAT IP address, no unsolicited inbound requests)
- it_cert_extra (gets an external IP address for labs using a web server on port 80; and two hard disks)
The valid reference
s for a linux_terminal
resource are:
- [LINUX_TERMINAL].external_ip
attribute | required | type | notes |
---|---|---|---|
permissions | ✓ | array | Array of project/roles(array) pairs |
startup_script.path | path | Relative path to a file with the script contents. |
- type: looker_instance
id: looker
permissions:
- project: my_primary_project
roles:
- roles/editor
startup_script:
path: startup.sh
Note: Even though the spec supports any number of projects with any number
roles, Qwiklabs only supports a Looker instance having access to a single
project and it must have the roles/editor
role in that project. This note will
be removed when Qwiklabs supports multiple projects and different roles for
looker_instance
.
The valid reference
s for a looker_instance
resource are:
- [LOOKER_INSTANCE].developer_username (The username of a user on the Looker instance with the Developer role)
- [LOOKER_INSTANCE].developer_password (The password of a user on the Looker instance with the Developer role)
- [LOOKER_INSTANCE].student_url (URL a student can open in a separate tab to see the Looker interface)
Student progress on Looker instances can be inspected with the
RunRemoteCommand
handle. Assuming your resource ID is looker
, the following
activity tracking section would check if a student has created at least one
Look.
assessment:
passing_percentage: 100
steps:
- title: Create a Look
maximum_score: 100
student_messages:
success: Great job! You created a Look in Looker!
make_a_look: Please make a Look from any Explore.
services:
- looker.RunRemoteCommand
code: |-
def check(handles:, maximum_score:, resources:)
looker = handles['looker.RunRemoteCommand']
response = looker.run_remote_command 'lcurl GET /api/3.1/looks'
looks = JSON.parse(response.stdout)
if looks.count > 0
{ score: maximum_score, student_message: 'success' }
else
{ score: 0, student_message: 'make_a_look' }
end
end
All commands specified in run_remote_command
get executed from the home
directory where Looker is running.
All looker_instance
resources come with an extra lcurl
command to make it
easier to use the Looker API. lcurl
is a light wrapper around curl
that
generates a Looker access token from pre-specified API credentials and adds them
to a curl
request. lcurl
's syntax is:
lcurl <HTTP_VERB> <API_PATH> [EXTRA_CURL_OPTIONS]
Activity tracking should not mutate the state of a resource so the GET
verb is
the only verb that should be used in activity tracking.
For more info on the Looker API, check out the Looker API 3.1 Reference.
Startup scripts are executed from /home/looker
which is the directory where
Looker is installed. In addition to a standard, self-hosted Looker instance,
there is also a folder called all-lookml/
with three folder in it:
- qwiklabs-flights - A clone of https://github.com/llooker/qwiklabs-flights.git
- qwiklabs-ecommerce - A clone of https://github.com/llooker/qwiklabs-ecommerce.git
- qwiklabs-ecommerce-adv - A clone of https://github.com/llooker/qwiklabs-ecommerce-adv.git
If you'd like to use one or more of these projects in your lab, copy them to the
models/
folder with commands like:
mv all-lookml/qwiklabs-flights models/
mv all-lookml/qwiklabs-ecommerce models/
mv all-lookml/qwiklabs-ecommerce-adv models/
The LookML projects are already configured so no additional API calls are
needed. However, you can use lcurl
in startup scripts the same way you use it
in activity tracking. Notably, startup scripts should modify the state of a
looker_instance
so they will generally use POST
and other HTTP verbs.
lcurl POST /api/3.1/folders --data '{"name": "Sample top-level folder", "parent_id": 1}'
jq
is also installed on all
looker_instance
s. This is useful for parsing the output of one lcurl
command
to feed it into another one.
# Create a new user and assign it the auto-generated Developer role.
DEVELOPER_USER_ID=$(lcurl POST /api/3.1/users --data '{"first_name": "Developer", "last_name": "Student", "email": "[email protected]"}' | jq -r '.id')
DEVELOPER_ROLE_ID=$(lcurl GET /api/3.1/roles | jq -r '.[] | select (.name == "Developer") | .id')
lcurl PUT /api/3.1/roles/${DEVELOPER_ROLE_ID}/users --data "[\"${DEVELOPER_USER_ID}\"]"
attribute | required | type | notes |
---|---|---|---|
startup_script.path | path | Relative path to a file with the script contents. | |
student_files | path | Relative path to a directory student file contents. |
- type: ide
id: ide
startup_script:
path: startup/startup.sh
student_files:
- path: student_files
Student files specified in the qwiklabs.yaml will be added the student's home
directory /home/project
.
Startup scripts are executed from /home/theia
which is the directory where IDE
is installed.
If there is a main.py
student file, it will be visible to the student at lab
startup.
attribute | required | type | notes |
---|---|---|---|
startup_script.path | path | Relative path to a file with the script contents. | |
student_files | path | Relative path to a directory student file contents. |
- type: jupyter_notebook
id: notebook
startup_script:
path: startup/startup.sh
student_files:
- path: student_files
Student files specified in the qwiklabs.yaml will be added to the directory
/home/jovyan
. This folder is viewable in the Jupyter Notebook.
For notebooks that require activity tracking, all files accessed by the notebook
must be placed in the /home/jovyan
directory with no subdirectories.
Startup scripts are executed from /home/jovyan
which is the directory where
Jupyter Notebook is installed.
Startup scripts are run as root, but the student accesses the notebook as the
unprivileged Jovyan user. Thus, if your startup script creates any files or
directories you want students to have access to, you must adjust the permissions
of those files/directories. To ensure students have full access to all of the
contents of /home/jovyan
, add the following command at the end of your script:
chmod -R 777 ~jovyan
If there is a main.ipynb
student file, it will be opened in the Jupyter
Notebook at lab startup.
attribute | required | type | notes |
---|---|---|---|
startup_script.path | path | Relative path to a file with the script contents. |
- type: windows_vm
variant: it_cert
id: vm
startup_script:
path: startup.ps1
The allowed windows_vm
variants are:
- it_cert [default] (can initiate internet connection via shared Cloud NAT IP address, no unsolicited inbound requests)
- it_cert_extra (gets an external IP address for labs using a web server on port 80; and two hard disks)
The valid reference
s for a windows_vm
resource are:
- [WINDOWS_VM].external_ip
- [WINDOWS_VM].student_url (URL a student can open in a separate tab to see the Windows desktop experience)
attribute | required | type | notes |
---|---|---|---|
account_restrictions.allow_dedicated_instances | boolean | Should the account allow users to create Dedicated Instances. Default false. | |
account_restrictions.allow_spot_instances | boolean | Should the account allow users to create Spot Instances functionality. Default false. | |
account_restrictions.allow_subnet_deletion | boolean | Default false. | |
account_restrictions.allow_vpc_deletion | boolean | Default false. | |
account_restrictions.allowed_ec2_instances | array | Array of EC2 instance types that are valid for any EC2 product (e.g. dedicated, spot, on-demand). Default none. | |
account_restrictions.allowed_rds_instances | array | Array of EC2 instance types that are valid for RDS usage. Default none. | |
startup_script.type | string | The type of startup script. Only cloud_formation is supported. |
|
startup_script.path | path | Relative path to a file with the script contents. | |
user_policy | path | Relative path to a JSON policy document. | |
allowed_locations | array | List of AWS regions to set as the default for this account. |
- type: aws_account
id: the_account
variant: aws_vpc
startup_script:
type: cloud_formation
path: ./lab.template
user_policy: ./student.policy
account_restrictions:
allow_dedicated_instances: false
allow_spot_instances: false
allow_subnet_deletion: false
allow_vpc_deletion: false
allowed_ec2_instances: []
allowed_rds_instances: ['db.t2.micro']
allowed_locations: ['us-east-1', 'us-central-1']
allowed_locations
lets you specify the default AWS region for this AWS
account. If multiple regions are specified, Qwiklabs will randomly pick one
every time a student launches a lab. If no locations are specified, Qwiklabs
will select a default region based on that customer's static default region.
The allowed aws_account
variants are:
- aws_vpc [default]
- aws_vpc_ml
- aws_rt53labs_ilt
- aws_vpc_sts
Note that a lab will only launch on a given deployment if the deployment has an AWS fleet for the designated variant.
The valid reference
s for an aws_account
resource are:
reference | displayed as |
---|---|
[AWS_ACCOUNT].account_number | copyable text |
[AWS_ACCOUNT].username | copyable text |
[AWS_ACCOUNT].password | copyable text |
[AWS_ACCOUNT].access_key_id | copyable text |
[AWS_ACCOUNT].secret_access_key | copyable text |
[AWS_ACCOUNT].rdp_credentials | copyable text |
[AWS_ACCOUNT].ssh_key | copyable text |
[AWS_ACCOUNT].console_url | button |
[AWS_ACCOUNT].sts_link | button |
[AWS_ACCOUNT].vnc_link | button |
Any custom outputs being generated with a startup_script
can be reference as
[AWS_ACCOUNT].startup_script.[FILL-IN-OUTPUT-KEY]. The output will be displayed
as a copyable text if provided within the student_visible_outputs.
Qwiklabs regularly syncronizes it's list of EC2 instance types with the AWS platform. We purposefully do not provide a full list of EC2 instance types in this document, because AWS adds and deprecates instance types regularly.
For a complete and up-to-date list of EC2 instance types, see AWS official documentation
attribute | required | type | notes |
---|---|---|---|
startup_script | Azure Startup Script | Azure startup script object. Azure resource groups support startup scripts of type qwiklabs . |
- type: azure_resource_group
id: rg_1
variant: default
startup_script:
type: qwiklabs
path: startup_dir
custom_properties:
- key: name
value: student
- key: userPassword
reference: primary_user.password
The allowed variants are:
default
- Currently the only variant that enforce a set number of virtual machine types.
The valid reference
s for the azure_resource_group
resource are:
reference | displayed as |
---|---|
[USER].console_url | button |
attribute | required | type | notes |
---|---|---|---|
permissions | array | Array of resource group/roles(array) pairs |
- type: azure_user
id: primary_azure_user
permissions:
- resource_group: my_primary_rg
roles:
- virtual_machine_contributor_custom
The allowed variants are:
default
- Currently the only variant.
The allowed permission roles:
virtual_machine_contributor_custom
- Similar to the built-in role Virtual Machine Contributor with additional permissions forMicrosoft.Network
andMicrosoft.Compute
.
The valid reference
s for the azure_user
resource are:
reference | displayed as |
---|---|
[USER].username | copyable text |
[USER].password | copyable text |
attribute | required | type | notes |
---|---|---|---|
type | ✓ | string | The type of startup script. Only qwiklabs is supported. |
path | ✓ | path | Relative path to a directory tree with the script contents. |
custom_properties | Custom Script Properties array | Array of custom script property objects. |
Any custom outputs being generated with a startup_script
can be reference as
[RESOURCE_GROUP].startup_script.[FILL-IN-OUTPUT-NAME]. The output will be
displayed as a copyable text if provided within the student_visible_outputs.
Every Azure resource group may have one startup script associated with it.
attribute | required | type | notes |
---|---|---|---|
key | ✓ | string | How the property will be referenced within the script. |
value | string | A static value to be passed into the script. | |
reference | resource reference | A resource reference object to be passed into the script. |
Custom script properties are passed into the script as input. A custom script property must have a key and either a value or a reference associated with it. For examples please see the Azure resource group resource.
Specify which resource properties are given to the lab taker.
For the lab taker to get access to a gcp_project
, a console_url
resource
reference must be specified. For the lab taker to get access to an
aws_account
, one of the following resource references must be specified:
console_url
sts_link
vnc_link
All console_url
, sts_link
, vnc_link
, and student_url
resource references
are presented as a button to the student. The label provided with the resource
reference will be displayed on the button. These labels should be no longer than
20 characters. For windows_vm
resources, it is strongly recommended that the
author specifies a student_url
reference to improve the user experience.
Not all details of the lab environment should be exposed to the lab taker. For example, a lab may involve a GCP project and two GCP users. The lab taker is expected to log into GCP as one user, and manipulate the IAM privileges of the other. Since the lab taker is not expected to log in as the second user, there is no reason to display the second user's password and doing so may be distracting.
attribute | required | type | notes |
---|---|---|---|
label | ✓ | string | A label identifying to the student what the displayed reference is. |
reference | ✓ | resource reference | A resource reference for a value to be displayed to the student. |
Note the order of which the labels are placed within the student visible outputs is the order of which the details will appear within the lab control panel. For example, the following student visible outputs:
environment:
student_visible_outputs:
- label: "AWS Console"
reference: aws_account.console_url
- label: "AWS Username"
reference: aws_account.username
- label: "AWS Password"
reference: aws_account.password
- label: "AWS Account Number"
reference: aws_account.account_number
- label: "GCP Console"
reference: my_primary_project.console_url
- label: "GCP Username"
reference: primary_user.username
- label: "GCP Password"
reference: primary_user.password
will be presented as follows:
Activity tracking is a feature for evaluating a student's performance in a lab
by running a script at "checkpoints". These scripts can call APIs relevant to
any environment resource to query their current state. For example, the script
may inspect and validate the configuration of GCE instances running in
my-project
, to ensure the user is following the instructions properly.
A lab has an Assessment, which in turn contains the Steps (checkpoints).
There are two options to configure Activity Tracking, our preference is 2) where separate '.rb' files are used for each assessment method, but will retain backwards compatibility with 1):
- All inline in the qwiklabs.yaml file as below: qwiklabs.yaml:
assessment:
passing_percentage: 75
steps:
- title: Create a Cloud Storage bucket
maximum_score: 5
student_messages:
- success: Great job! You created the bucket!
- bucket_missing: Oops! No bucket found.
- bucket_misconfigured: Hmm. The bucket is there, but it is misconfigured.
services:
- target_project.StorageV1
code: |-
def check(handles:, resources:, maximum_score:)
storage_handle = handles['target_project.StorageV1']
# Check for bucket
found_bucket = ...
unless found_bucket
return { score: 0, message: 'bucket is missing', student_message: 'bucket_missing' }
end
# Check bucket configuration
bucket_configured_correctly = ...
unless bucket_configured_correctly
return { score: 2, message: 'bucket is misconfigured', student_message: 'bucket_misconfigured' }
end
{ score: maximum_score, message: 'step completed', student_message: 'success' }
end
- Or putting all the code segments as separate files into a folder (assessments/) and then referencing the method name as below: qwiklabs.yaml:
passing_percentage: 75
steps:
- title: Create a Cloud Storage bucket
maximum_score: 5
student_messages:
- success: Great job! You created the bucket!
- bucket_missing: Oops! No bucket found.
- bucket_misconfigured: Hmm. The bucket is there, but it is misconfigured.
services:
- target_project.StorageV1
method_name: check1
assessments/check1.rb
def check1(handles:, resources:, maximum_score:)
storage_handle = handles['target_project.StorageV1']
# Check for bucket
found_bucket = ...
unless found_bucket
return { score: 0, message: 'bucket is missing', student_message: 'bucket_missing' }
end
# Check bucket configuration
bucket_configured_correctly = ...
unless bucket_configured_correctly
return { score: 2, message: 'bucket is misconfigured', student_message: 'bucket_misconfigured' }
end
{ score: maximum_score, message: 'step completed', student_message: 'success' }
end
attribute | required | type | notes |
---|---|---|---|
passing_percentage | ✓ | integer | The percentage of total points the student must achieve to "pass" the lab. |
steps | ✓ | array | An array of Steps |
assessment:
passing_percentage: 75
steps: ...
attribute | required | type | notes |
---|---|---|---|
title | ✓ | string | |
maximum_score | ✓ | integer | The maximum number of points this step can award. |
student_messages | ✓ | dictionary | The keys are how the messages will be referenced in the code, and the values are strings to be displayed. |
services | ✓ | array of resource services | An array of services that will be used in the code block. Each resource type specifies a set of allowed services. |
code | ✓ | string | Code to be executed. See below for more information. |
assessment:
passing_percentage: 75
steps:
- title: Create a Cloud Storage bucket
maximum_score: 5
student_messages:
- success: Great job! You created the bucket!
- bucket_missing: Oops! No bucket found.
- bucket_misconfigured: Hmm. The bucket is there, but it is misconfigured.
services:
- target_project.StorageV1
code: |-
def check(handles:, resources:, maximum_score:)
storage_handle = handles['target_project.StorageV1']
# Check for bucket
found_bucket = ...
unless found_bucket
return { score: 0, message: 'bucket is missing', student_message: 'bucket_missing' }
end
# Check bucket configuration
bucket_configured_correctly = ...
unless bucket_configured_correctly
return { score: 2, message: 'bucket is misconfigured', student_message: 'bucket_misconfigured' }
end
{ score: maximum_score, message: 'step completed', student_message: 'success' }
end
- title: Copy a file to the bucket
maximum_score: 5
student_messages:
- success: Great job! You copied the file!
- file_missing: Oops! No file found.
- file_mismatch: Hmm. There's a file here, but it doesn't match the source contents.
services:
- source_project.StorageV1
- target_project.StorageV1
code: |-
def check(handles:, resources:, maximum_score:)
target_storage_handle = handles['target_project.StorageV1']
source_storage_handle = handles['source_project.StorageV1']
# Check for file
found_file = ...
unless found_file
return { score: 0, message: 'file is missing', student_message: 'file_missing' }
end
# Check file contents
source_contents = ...
target_contents = ...
unless source_contents == target_contents
return { score: 2, message: 'file mismatch', student_message: 'file_mismatch' }
end
{ score: maximum_score, message: 'step completed', student_message: 'success' }
end
The code block must be valid Ruby code. It must have a method called check
,
and may optionally contain helper methods as well.
The method check
will be called with three keyword arguments:
-
handles
: A map from[RESOURCE].[SERVICE]
to a handle to that resource's service. It will only contain handles to services specified in the step'sservices
array. -
resources
: A map from[RESOURCE]
(ids) to maps of that resource's references. For example:{ my_primary_project: { project_id: 'some-gcp-project-id', default_zone: 'us-central1-a' }, a_cool_user: { username: 'some-username', password: 'hunter2' } }
-
maximum_score
: The maximum score for the step.
The method check
should return a single hash with:
:score
: the number of points the student earned.:message
: a message for only the lab creator to see when debugging each assessment step.:student_message
: a key from the step'sstudent_messages
array, which will be presented to the student in the appropriate locale.