Skip to content

Commit

Permalink
Add *ready tag* check
Browse files Browse the repository at this point in the history
  • Loading branch information
np5 committed Feb 28, 2025
1 parent 81cdef1 commit f21748a
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 9 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Operations are passed as `operation` parameter in the URL query. The `serial_num

HTTP Method: `GET`

The lambda verifies in Zentral that the device has the correct DEP enrollment assigned in the Apple Business Manager.
The lambda verifies in Zentral that the device has the *ready tag* and the correct DEP enrollment assigned in the Apple Business Manager.

Example:

Expand All @@ -31,7 +31,11 @@ curl -s -H "Authorization: Bearer $THE_NEKOBUS_TOKEN" \

HTTP Method: `POST`

**IMPORTANT** The lambda does the same verification as during the `check` operation, and if successful, the device is Unenrolled in Jamf, and the *started tag* is set on it in Zentral.
**IMPORTANT** The lambda does the same verification as during the `check` operation, and if successful:

* the device is Unenrolled in Jamf
* the *ready tag* is removed in Zentral
* the *started tag* is set in Zentral.

```
curl -s -XPOST -H "Authorization: Bearer $THE_NEKOBUS_TOKEN" \
Expand All @@ -47,7 +51,7 @@ curl -s -XPOST -H "Authorization: Bearer $THE_NEKOBUS_TOKEN" \

HTTP Method: `POST`

The *finished tag* is set on the device in Zentral.
The *started tag* is removed and the *finished tag* is set on the device in Zentral.

```
curl -s -XPOST -H "Authorization: Bearer $THE_NEKOBUS_TOKEN" \
Expand Down
1 change: 1 addition & 0 deletions infrastructure/lambda.tf
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ resource "aws_lambda_function" "lambda" {
NEKOBUS_ZENTRAL_BASE_URL = var.zentral_base_url
NEKOBUS_PROFILE_UUID = var.profile_uuid
NEKOBUS_TAXONOMY = var.taxonomy
NEKOBUS_READY_TAG = var.ready_tag
NEKOBUS_STARTED_TAG = var.started_tag
NEKOBUS_FINISHED_TAG = var.finished_tag
}
Expand Down
4 changes: 4 additions & 0 deletions infrastructure/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ variable "taxonomy" {
type = string
}

variable "ready_tag" {
type = string
}

variable "started_tag" {
type = string
}
Expand Down
4 changes: 3 additions & 1 deletion lambda/lambda_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
# NEKOBUS_ZENTRAL_BASE_URL
# NEKOBUS_PROFILE_UUID
# NEKOBUS_TAXONOMY
# NEKOBUS_READY_TAG
# NEKOBUS_STARTED_TAG
# NEKOBUS_FINISHED_TAG

Expand Down Expand Up @@ -108,6 +109,7 @@ def initialize(self):
zentral_token,
os.environ["NEKOBUS_PROFILE_UUID"],
os.environ["NEKOBUS_TAXONOMY"],
os.environ["NEKOBUS_READY_TAG"],
os.environ["NEKOBUS_STARTED_TAG"],
os.environ["NEKOBUS_FINISHED_TAG"],
)
Expand Down Expand Up @@ -151,7 +153,7 @@ def execute_operation(self, op, serial_number):
result = getattr(self.mm, op)(serial_number)
except MigrationError as e:
logger.error("Operation %s device %s error: %s", op, serial_number, e)
raise LambdaError("Bad request", 400, body=body)
raise LambdaError("Not found" if e.status_code == 404 else "Bad request", e.status_code, body=body)
except Exception:
logger.exception("Operation %s device %s error", op, serial_number)
raise LambdaError("Internal server error", 500, body=body)
Expand Down
18 changes: 15 additions & 3 deletions nekobus/migration.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@


class MigrationError(Exception):
pass
def __init__(self, msg, status_code=400):
super().__init__(msg)
self.status_code = status_code


class MigrationManager:
Expand All @@ -20,23 +22,33 @@ def __init__(
zentral_token,
profile_uuid,
taxonomy,
ready_tag,
started_tag,
finished_tag,
):
self.jamf_client = JamfClient(jamf_base_url, jamf_client_id, jamf_client_secret)
self.zentral_client = ZentralClient(zentral_base_url, zentral_token)
self.profile_uuid = profile_uuid
self.taxonomy = taxonomy
self.ready_tag = ready_tag
self.started_tag = started_tag
self.finished_tag = finished_tag

def check(self, serial_number):
logger.info("Check device %s", serial_number)
result = self.zentral_client.check_tag(serial_number, self.ready_tag)
if result is None:
raise MigrationError("Device not found", 404)
elif result:
logger.info("Device %s has the %s tag", serial_number, self.ready_tag)
else:
logger.info("Device %s doesn't have the %s tag", serial_number, self.ready_tag)
return False
if self.zentral_client.check_dep_device_enrollment(serial_number, self.profile_uuid):
logger.info("Device %s OK", serial_number)
logger.info("Device %s DEP enrollment %s OK", serial_number, self.profile_uuid)
return True
else:
logger.info("Device %s not OK", serial_number)
logger.info("Device %s DEP enrollment %s OK", serial_number, self.profile_uuid)
return False

def start(self, serial_number):
Expand Down
31 changes: 29 additions & 2 deletions nekobus/zentral.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import base64
import logging
import requests
import urllib.parse
from .utils import CustomHTTPAdapter
from .version import __version__


logger = logging.getLogger(__name__)


def make_url_safe_serial_number(serial_number):
if serial_number.startswith(".") or \
urllib.parse.quote(serial_number, safe="") != serial_number:
return ".{}".format(
base64.urlsafe_b64encode(serial_number.encode("utf-8")).decode("utf-8").rstrip("=")
)
return serial_number


class ZentralClientError(Exception):
pass

Expand Down Expand Up @@ -43,6 +54,24 @@ def get_dep_device(self, serial_number):
logger.info("Unknown DEP device %s", serial_number)
return None

def get_tags(self, serial_number):
logger.info("Get device %s tags", serial_number)
url_safe_serial_number = make_url_safe_serial_number(serial_number)
try:
r = self.session.get(f"{self.api_base_url}/inventory/machines/{url_safe_serial_number}/meta/")
if r.status_code == 404:
return None
r.raise_for_status()
return r.json().get("tags", [])
except Exception:
raise ZentralClientError(f"Could not get device {serial_number} tags")

def check_tag(self, serial_number, tag):
tags = self.get_tags(serial_number)
if tags is None:
return
return any(t["name"] == tag for t in tags)

def check_dep_device_enrollment(self, serial_number, expected_profile_uuid):
logger.info("Check DEP device %s enrollment", serial_number)
dep_device = self.get_dep_device(serial_number)
Expand All @@ -57,8 +86,6 @@ def check_dep_device_enrollment(self, serial_number, expected_profile_uuid):
if profile_status != "pushed":
logger.warning("Wrong profile status %s for DEP device %s", profile_status or "-", serial_number)
ok = False
if ok:
logger.info("DEP device %s enrollment OK", serial_number)
return ok

def set_taxonomy_tags(self, serial_number, taxonomy, tags):
Expand Down

0 comments on commit f21748a

Please sign in to comment.