Moka5
Moka5


Create Domain Join Packets using an API   « Go Back

Summary

This REST API provides an advanced method for administrators to implement the Domain Join Packet (also known as AD Packet) creation process, without the need to use Moka5 Creator. This can be used by enterprises to:
  • Automate the creation of packets at user-provisioning time, integrated with an external provisioning workflow
  • Manually recreate a packet for a specific user while preserving the same computer name

Applies To

Moka5 3.19

- Prerequisites
- Overview
- APIs
- AD Packet Upload API
- AD Packet Listing API
- AD Packet Delete API
- Examples
- Example - uploadpacket.ps1
- Example - gencert.ps1
- Example - listpackets.ps1
- Example - removepacket.ps1

Prerequisites

  • Moka5 3.19
  • Powershell 3.0 or higher, installed on the administrator’s workstation
  • Working knowledge of djoin.exe and applicable parameters (see the Microsoft Technet article)
  • Working knowledge of certreq.exe (see the Microsoft Technet article)
  • Windows Server 2008 R2
Important: Server-side domain join packet creation is only supported on Windows Server 2008 R2. These packets can only be consumed in Windows 7 LivePC images.

Overview

The generalized REST API can be used to upload, list, or delete AD packets on the Moka5 Management Server. These packets can be targeted for a specific user and/or LivePC. The API also allows an administrator to offload the creation of AD packets to an external script.

The external script would create AD packets using the Windows Offline Domain Join tool, djoin.exe, which allows you to create AD packets directly from the command line on Windows 7 and Windows 2008 R2. While MokaFive Creator has a special way of creating AD packets on Windows XP, it internally uses this same tool on Windows 7 and 2008 to create AD packets. Using this djoin.exe tool, an external script can use the same method Moka5 uses to provision AD packets.

AD Packet Upload API

Following is the specification of how to upload an AD packet via the new REST API, assuming it has already been generated using Windows' djoin.exe tool.

URL: http://<mconsole-url>/webapp/rest/api/adpackets

HTTP Method: POST

Content-Type: application/json

If the request parameters are invalid, then a HTTP 400 response will be returned, and the response body should contain an error message of the reason why it was invalid. A successful response will return HTTP 200 and information about the AD packet created.


Request parameters:

  • "computerName": The computer name the uploaded AD packet belongs to
  • "domainName": The name of the domain the uploaded AD packet belongs to
  • "livepcId": The id of the LivePC this packet should be used with (optional). This value corresponds to the "LivePC ID" on the LivePC's "View Details" screen. If this value is not specified, then this AD packet can be claimed by a user subscribing to any LivePC.
  • "batchName": The name of the AD packet set / batch that this packet should be stored in (optional). This value is ignored unless a "livepcId" is supplied.
  • "data": The packet data output from djoin.exe (which is Base64-encoded)
  • "certificate": A Base64-encoded PFX/PKCS12 file containing any certificates/keys to import into the trust store (optional). This file must be encoded with password "foo" by default.
  • "reservedUserName": The userid of the user that this AD packet should be reserved for (optional). It must be a valid user in AD. Only this user will be able to claim this AD packet.
  • "reservedUserDomain": The name of the domain to lookup the reservedUserName in (required if reservedUserName is specified). This should be the name of the domain as known by the management console, not the external actual name.
  • "expiry": The number of days after which this AD packet should be marked as expired and no longer used (optional).
  • "userName": The name of the user who uploaded the AD packet (optional).

Example request:

{
"computerName": "LPC-1Z97F",
"domainName": "company.com",
"reservedUserName": "jsmith",
"reservedUserDomain": "company",
"data": "<long base64 encoded string>"
}


Response parameters:

Most of the parameters are the same as the request parameters, though there are a couple of important additions:
  • "name": The unique identifier / name for this AD packet. If you choose later to use this same API to remove this packet, this is the name that must be used.
  • "state": The state of the packet. It should be either AVAILABLE or RESERVED for newly uploaded packets.

Example response:

{
"computerName": "LPC-1Z97F",
"domainName": "company.com",
"batchName": "Unspecified",
"reservedUserName": "jsmith",
"state": "RESERVED",
"name": "adp-f39701c5-94ac-47ee-a1ac-920c6c60445e"
}

AD Packet Listing API

Following is the specification of how to list known existing AD packets.

URL: http://<mconsole-url>/webapp/rest/api/adpackets

HTTP Method: GET

There are no request parameters presently. This simple GET request will return a list of known AD packets.


Example response:

{
"results":[
{
"computerName":"LPC-1Z97F",
"domainName":"company.com",
"batchName":"Unspecified",
"reservedUserName":"jsmith",
"state":"RESERVED",
"name":"adp-f39701c5-94ac-47ee-a1ac-920c6c60445e"
},
{
"computerName":"LPC-3G14Q",
"domainName":"company.com",
"livepcId": "win7-a",
"batchName":"set1",
"state":"AVAILABLE",
"name":"adp-311ceaed-a832-46c6-9fc8-b170912662a3"
}
],
"count":2
}


AD Packet Delete API

Following is the specification of how to delete AD packets.

URL: http://<mconsole-url>/webapp/rest/api/adpackets/<name>

HTTP Method: DELETE

The "name" specified as part of the URL must be the AD Packet name as returned by the Upload or Listing APIs. As long as this Delete API returns a HTTP 200, then the deletion was successful.


Example - uploadpacket.ps1

  • Creates a packet with djoin.exe
  • Generates a machine certificate (using the gencert.ps1 script)
  • Uploads the packet to the Moka5 Management Server
NOTE: The following example is for illustrative purposes only and should not be deployed without customization and extensive testing in your environment.
param (
[string]$computerName = $(throw "-m5domainName is required."),
[string]$reservedName,
[string]$livepcid,
[string]$m5domainName = $(throw "-m5domainName is required."),
[boolean]$issueCert = $false
)
$ErrorActionPreference = "Stop"
# imports
. .\gencert.ps1
# Configuration
################
$m5host = "m5.mycompany.com" #DNS name of your Moka5 Management Server
$m5port = 443
$m5user = "username1" #account that has Author role permissions in Moka5 Management Console
$m5pass = "password1" #password for $m5user
$domain = "mycompany.com" #FQDN domain name
$domainUser = "username2" #AD account that has permissions to create computer objects
$domainPass = "password2" #password for $domainUser
$certTemplate = "WorkstationAuth2"
################
$PACKETS_PATH = "/webapp/rest/api/adpackets"
# I) Create packet with djoin.exe
$adPackFile = [io.path]::GetTempFileName()
# a) if running this script already logged in as a domain admin:
#$output = (djoin.exe /provision /reuse /domain "$domain" /machine "$computerName" /downlevel /savefile "$adPackFile")
# b) if not running script as a domain admin:
$output = (cmd /c m5runas.exe -u "$domainUser" -p "$domainPass" -d "$domain" -netonly -app djoin.exe -args /provision /reuse /domain "$domain" /machine "$computerName" /downlevel /savefile "$adPackFile")
$output = $output -join "n"
if (!$output.contains("")) {
Write-Host "Error creating domain join packet"
Write-Host $output
return
}
$packet = Get-Content $adPackFile | out-string
rm $adPackFile
if (!$packet) {
Write-Host "Error reading AD packet file"
return
}
Write-Host "Successfully generated AD packet"
# II) Generate certificate
if ($issueCert) {
$pfxBytes = GenerateMachineCert $computerName $domain $certTemplate $domainUser $domainPass
if (!$pfxBytes) {
Write-Host "Error generating machine certificate"
return
}
$pfx = [System.Convert]::ToBase64String($pfxBytes)
Write-Host "Successfully generated machine certificate"
}
# III) Upload packet to m5 console
$packetRequest = @{
"data" = $packet;
"certificate" = $pfx;
"livepcId" = $livepcid;
"computerName" = $computerName;
"domainName" = $m5domainName;
"reservedUserName" = $reservedName;
#"batchName" = "set2";
}
$basicAuth = [System.Convert]::ToBase64String(
[System.Text.Encoding]::UTF8.GetBytes("${m5user}:$m5pass")
)
$headers = @{
"Authorization" = "Basic $basicAuth";
}
$packetRequestJson = $packetRequest | ConvertTo-Json
Write-Host "https://${m5host}:$m5port$PACKETS_PATH" -Body $packetRequestJson -Method "Post" -Headers $headers -ContentType "application/json"
Invoke-RestMethod "https://${m5host}:$m5port$PACKETS_PATH" -Body $packetRequestJson -Method "Post" -Headers $headers -ContentType "application/json"

Example - gencert.ps1

  • Performs the same function as the “Include certificate auto-enrollment” function in Moka5 Creator when creating Domain Join Packets.
NOTE: The following example is for illustrative purposes only and should not be deployed without customization and extensive testing in your environment.
[Reflection.Assembly]::LoadWithPartialName("System.Security") > $null
function GenerateMachineCert {
param (
[string]$computerName = $(throw "-computerName is required."),
[string]$domainName = $(throw "-domainName is required."),
[string]$certTemplate = $(throw "-certTemplate is required."),
[string]$domainUser,
[string]$domainPass
)
# 0) Setup
$hostname = "${computerName}.${domainName}"
$certAuthorityName = $domainName -replace "\..*$", ""
$password = "foo"
$iniFile = [io.path]::GetTempFileName()
$csrFile = [io.path]::GetTempFileName()
$certFile = [io.path]::GetTempFileName()
$certBundleFile = [io.path]::GetTempFileName()
@"
[NewRequest]
Subject = "CN=$hostname"
HashAlgorithm = sha1
KeyLength = 2048
KeySpec = 1 # --> AT_KEYEXCHANGE
Exportable = true
MachineKeySet = true
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
Silent = true
FriendlyName = "Machine Cert for $hostname"
[Extensions]
2.5.29.17 = "{text}"
_continue_ = "DNS=$hostname"
"@ >> $iniFile
# 1) Generate CSR (and private key)
$out = (certreq -new -f -q -machine "$iniFile" "$csrFile")
$out = $out -join "`n"
if ($out.contains("Certificate Request Processor")) {
Write-Host "Error generating CSR"
Write-Host $out
return
}
rm $iniFile
#Write-Host "Successfully generated CSR"
# 2) Submit CSR to AD Certificate Services
# a) when not running as admin
$out = $(m5runas -netonly -d $domainName -u $domainUser -p $domainPass -app certreq.exe -args -Submit -f -q -attrib "CertificateTemplate: $certTemplate" -config "$domainName\$certAuthorityName" "$csrFile" "$certFile" "$certBundleFile")
# b) when script is already running as admin
# $out = $(certreq -Submit -f -q -attrib "CertificateTemplate: $certTemplate" -config "$domainName\$certAuthorityName" "$csrFile" "$certFile" "$certBundleFile")
$out = $out -join "`n"
if ($out.contains("Certificate Request Processor")) {
Write-Host "Error submitting certificate request"
Write-Host $out
return
}
rm $csrFile
#Write-Host "Successfully submitted certificate request"
# 3) Import received certificate into certificate store to connect with private key
$out = $(certreq -accept -q -machine "$certFile")
$out = $out -join "`n"
if ($out.contains("Certificate Request Processor")) {
Write-Host "Error importing certificate"
Write-Host $out
return
}
#Write-Host "Successfully imported certificate to cert store"
# 4) Package up received certificate (and chain) into a pkcs12 file
## Load certificate file to get thumbprint
$certExt = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certFile)
rm $certFile
## Load certificate object from cert store connected to private key
$certStorePath = "cert:\LocalMachine\My\" + $certExt.Thumbprint
$cert = Get-ChildItem $certStorePath
## create a cert collection and add machine cert
$pfx = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
$added = $pfx.Add($cert)
## load certificate bundle file to get cert chain
$p7File = Get-Content $certBundleFile
$p7File = $p7File -replace "-+(BEGIN|END)[^\-]+-+", ""
rm $certBundleFile
$p7 = New-Object System.Security.Cryptography.Pkcs.SignedCms
$p7.Decode([System.Convert]::FromBase64String($p7File))
## add each cert in the chain to the pkcs12
foreach ($p7Cert in $p7.Certificates) {
if (!$p7Cert.equals($certExt)) {
$added = $pfx.Add($p7Cert)
}
}
## export cert collection to pfx/pkcs12 format
$pfxBytes = $pfx.export("pfx", $password)
## remove certificate from cert store
rm $certStorePath
#Write-Host "Successfully export pkcs12 and removed certificate from certificate store"
return $pfxBytes
}

Example - listpackets.ps1

  • Lists packets from the Moka5 Management Server
NOTE: The following example is for illustrative purposes only and should not be deployed without customization and extensive testing in your environment.
# Configuration
################
$m5host = "<m5 console url>"
$m5port = 443
$m5user = "<m5 console user>" #account that has Author role permissions in Moka5 Management Console
$m5pass = "<m5 console pass>"
################
$packetsPath = "/webapp/rest/api/adpackets"
$basicAuth = [System.Convert]::ToBase64String(
[System.Text.Encoding]::UTF8.GetBytes("${m5user}:$m5pass")
)
$headers = @{
"Authorization" = "Basic $basicAuth";
}
$resp = Invoke-RestMethod "https://${m5host}:$m5port$packetsPath" -Method "Get" -Headers $headers
$resp.results | Format-Table -Property name, state, computerName, domainName, reservedUserName -Wrap -Autosize

Example - removepacket.ps1

  • Remove a packet from the Moka5 Management Server
NOTE: The following example is for illustrative purposes only and should not be deployed without customization and extensive testing in your environment.
param (
[string]$packetName = $(throw "-packetName is required.")
)
# Configuration
################
$m5host = "<m5 console hostname>"
$m5port = 443
$m5user = "<m5 console user>" #account that has Author role permissions in Moka5 Management Console
$m5pass = "<m5 console pass>"
################
$packetsPath = "/webapp/rest/api/adpackets"
$basicAuth = [System.Convert]::ToBase64String(
[System.Text.Encoding]::UTF8.GetBytes("${m5user}:$m5pass")
)
$headers = @{
"Authorization" = "Basic $basicAuth";
}
Invoke-RestMethod "https://${m5host}:$m5port$packetsPath/$packetName" -Method "Delete" -Headers $headers
echo "Removed packet $packetName"