Hey all. I thought I would (re)post a quick explanation as to how you can use the box windows sdk in powershell to automate box tasks. This method will require powershell 5 at least (or if you separately installed powershellget) to work. First, obtaining the assemblies. I created a custom little nuget package of my own which contains not only the box assembly needed to automate box tasks, but the dependencies that it requires as well. I did this due to, among other things, the errors encountered when using nuget to obtain the assemblies and their dependencies and trying to import them into powershell, and also because of the number of unnecessary assemblies that this method imports as well. I published the package on MyGet (which you can download here if you want to see the contents) to make it simple for a powershell script to download these assemblies to the local machine in the event that the powershell script could not find them. Below is the code powershell code that I use to accomplish this (using the packagemanagement module):
Get-PackageProvider -Name NuGet -ForceBootstrap
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
If (!(Test-Path "$env:ProgramData\boxlib"))
{
New-item -Name "boxlib" -Path $env:PROGRAMDATA -ItemType Directory -Force
}
If (!(Test-Path "$env:ProgramData\boxlib\Box.Dependencies\1.0.0\lib\net45\Box.V2.dll"))
{
Get-PackageProvider -Name NuGet -ForceBootstrap
Get-PackageProvider -Name Powershellget -ForceBootstrap
Register-PackageSource -Name "Box.Dependencies" -Location "https://www.myget.org/F/boxdependency/api/v2" -ProviderName "PowershellGet" -Trusted -Force
save-Package -Name Box.dependencies -path "$env:ProgramData\boxlib" -ProviderName "PowershellGet"
}
Get-ChildItem "$env:ProgramData\boxlib\*.dll" -Recurse | % { [reflection.assembly]::LoadFrom($_.FullName) }This will successfully import all of the box windows sdk assemblies you will need into the current powershell session.
While figuring out how to successfully import the box windows sdk was a challenge in of itself, figuring out how to use the box windows sdk was almost just as much a challenge. So lets talk about authenticating to box via a JWT assertion. This part was actually relatively simple, as this procedure is covered on the box windows sdk github page (the below code assumes that you generated your RSA keypair in the developement console, as it contains all of the information needed to authenticate using JWT):
$cli = Get-Content "Path\to\json\authentication\file\box-cli.json" | ConvertFrom-Json
$boxconfig = New-Object -TypeName Box.V2.Config.Boxconfig($cli.boxAppSettings.clientID, $cli.boxAppSettings.clientSecret, $cli.enterpriseID, $cli.boxAppSettings.appAuth.privateKey, $cli.boxAppSettings.appAuth.passphrase, $cli.boxAppSettings.appAuth.publicKeyID)
$boxJWT = New-Object -TypeName Box.V2.JWTAuth.BoxJWTAuth($boxconfig)
$boxjwt
$tokenreal = $boxJWT.AdminToken()
$adminclient = $boxJWT.AdminClient($tokenreal)
$adminclientNOTE: If you want to run the box commands under the context of a particular user (like if you wanted to search through a users files/folders, create share links to aforementioned files/folders, etc.), you can specify the user id of this user as the second argument in the creation of the $adminclient object (eg. $adminclient = $boxJWT.AdminClient($tokenreal, $userID)). This is essentially the same as using the "as-user" header. Another quick explanation. The "$adminclient" object is what you will be using to actually perform administrative tasks in box. Lets take a look at an example script that I wrote real quick, shall we? The below script is a script I was instructed to write in order to automate our "user termination process" as far as box is concerned:
<#
.SYNOPSIS
Obtain all Legal hold objects in the enterprise
.DESCRIPTION
Obtain a list of all legal hold objects in the enterprise.
.EXAMPLE
PS C:\> Get-LegalHold
.NOTES
Additional information about the function.
#>
function Get-LegalHold
{
$legalurl = $adminclient2.LegalHoldPoliciesManager.GetListLegalHoldPoliciesAsync()
$legalurl.Wait()
$legalOutput = $legalurl.Result
return $legalOutput
}
<#
.SYNOPSIS
Returns all legal hold assignment objects for all enterprise legal holds
.DESCRIPTION
This function will return all of the legal hold assignment objects, which are basically user objects associated with a particular legal hold, for all of the legal hold objects returned by Get-legalhold function
.EXAMPLE
PS C:\> Get-LegalHoldAssignments
.NOTES
Additional information about the function.
#>
function Get-LegalHoldAssignments
{
$tyy = Get-legalhold
$holds = $tyy.entries
$legids = New-Object System.Collections.ArrayList
foreach ($hold in $holds)
{
$newhold = $adminclient2.LegalHoldPoliciesManager.GetAssignmentsAsync($hold.Id)
foreach ($nope in $newhold.Result.Entries)
{
$legids.Add($nope.Id)
}
}
Return $legids
}
$user = Get-ADUser "ADuser" -Properties Mail, Manager
$email = $user.Mail
#retrieve information of the manager of the AD user
$manager = Get-aduser $user.Manager -Properties Mail, GivenName
$manmail = $manager.Mail
#Obtain a list of all enterprise box users, loop through the list, and if the user has a box account (identified using user's email), then save the user's and his/her manager's necessary information to variables.
$val = 0
$entries = New-Object System.Collections.ArrayList
$grog = $adminclient.UsersManager.GetEnterpriseUsersAsync($null, 0, 1000)
$grog.Wait()
$grog.Result.Entries | % { $entries.Add($_) }
$tot = $grog.Result.TotalCount
Do
{
$val = $val + 1000
$temp = $adminclient.UsersManager.GetEnterpriseUsersAsync($null, $val, 1000)
$temp.Wait()
$temp.Result.Entries | % { $entries.Add($_) }
}
while ($val + 1000 -lt $tot)
$id = $null
$name = $null
$login = $null
$manid = $null
$manlogin = $null
Foreach ($entry in $entries)
{
If ($entry.login -like $email)
{
$statusbar1.Text = "$($textbox1.Text) has a box account. Scan will continue. Please wait."
$id = $entry.Id
$login = $entry.login
$name = $entry.Name
}
If (($manager -ne $null) -and ($manager -ne ''))
{
If ($entry.login -like $manager.Mail)
{
$manid = $entry.Id
$manlogin = $entry.login
}
}
}
#The below code runs if the user has a box account
If ($id -ne $null)
{
#The below code will obtain a list of all legal hold assignments for all legal hold objects to determine if the user is on legal hold.
$leghols = Get-LegalHoldAssignments
$onlegalhold = $false
foreach ($leghol in $leghols)
{
$newoutput = $adminclient2.LegalHoldPoliciesManager.GetAssignmentAsync($leghol)
If ($newoutput.Result.Assignedto.Id -like $id)
{
$onlegalhold = $true
Break
}
}
$holdtext = ''
#Below code runs if the user is on legal hold.
If ($onlegalhold -eq $true)
{
#Moves all of the user's box content to the root of the Main "storage" account.
$foldproc = $adminclient.UsersManager.MoveUserFolderAsync("$id", "***number removed for privacy***", "0", $false)
$foldproc.Wait()
$foldid = $null
#Searches all of the folders in the Main "storage" account for the folder which was just created containing the user's files.
$folddd = $adminclient.FoldersManager.GetfolderItemsAsync("0", 1000, 0, $null)
$folddd.Wait()
$foldentries = $folddd.result.entries
foreach ($foldentry in $foldentries)
{
If ($foldentry.name -like "*$login*")
{
$foldid = $foldentry.id
Break
}
Else
{
Continue
}
}
#When the folder containing the user's files is located, the folder will be moved into the "Legal Holds" folder of the "storage" account.
$folderrequest = @{
id = "$foldid"
parent = @{
id = "2***phone number removed for privacy***"
}
}
$folderreqproc = $adminclient.FoldersManager.CopyAsync($folderrequest, $null)
$folderreqproc.Wait()
$folderdelete = $adminclient.FoldersManager.DeleteAsync("$foldid", $true, $null)
$folderdelete.Wait()
}
#Below Code runs if the user is not on legal hold
Else
{
#Below Code runs if the user did not have a manager configured in Active Directory or if the user's manager does not have a box account.
If ($manid -eq $null)
{
$foldproc = $adminclient.UsersManager.MoveUserFolderAsync("$id", "***number removed for privacy***", "0", $false)
$foldproc.Wait()
#Below code runs if user has a manager who does not have a box account. A shared link to user's box files will be created for manager.
If ($manmail -ne $null)
{
$foldid = $null
#Searches all of the folders in the Main "storage" account for the folder which was just created containing the user's files.
$folddd = $adminclient.FoldersManager.GetfolderItemsAsync("0", 1000, 0, $null)
$folddd.Wait()
$foldentries = $folddd.result.entries
foreach ($foldentry in $foldentries)
{
If ($foldentry.name -like "*$login*")
{
$foldid = $foldentry.id
Break
}
Else
{
Continue
}
}
#creates the shared link to the folder containing the user's box files.
$sharejson = @{
access = "open"
}
$sharelink = $adminclient.FoldersManager.CreateSharedLinkAsync("$foldid", "$sharejson", $null)
$sharelink.Wait()
$link = $sharelink.Result.SharedLink.Url
}
}
#Below Code runs if the user's manager does have a box account. User's files will be moved to manager's box account.
Else
{
$foldproc = $adminclient.UsersManager.MoveUserFolderAsync("$id", "$manid", "0", $false)
$foldproc.Wait()
}
}
#Deletes user's now empty box account and recovers license.
$deleteuser = $adminclient.UsersManager.DeleteEnterpriseUserAsync("$id", $false, $true)
$deleteuser.Wait()
}I did my best to annotate the script and be as clear as I could about each step in the process. As you can see (if you followed the script), the script takes an active directory user and that user's manager, loops through all enterprise users to see if they have box accounts (using their ad object's "mail" attribute). If the terminated user has a box account, the script will then determine if that user is on legal hold by cycling through all legal hold assignment of all of the legal hold object in the enterprise. If the user is on legal hold, the users files are stored in a secure folder. If they are not, then we look and see if the user had a configured manager. If they don't, the user's files are move to the same "secure" folder. If they do have a manager, but the manager does not have a box account, a shared link will be created for the manager to use to access that user's box files. If the manager does have a box account, then the files are simply moved to his/her account. There are a couple of things that I want to point out about the script that I feel deserve special mention:
1. You will notice that (almost) everytime I use $adminclient to execute an administrative action, I assign the process to a variable and on the very next line use that variable to call the "Wait()" method. This is because, as you can see from the names of the different box methods, that the box tasks run Asynchronously, and the "wait" method essentially performs the same task as the "await" keyword in C#, in that you wait for the previous command to finish executing and return the results before continuing to process the script. For example, say you have a box user you want to delete, but not force delete, and you use the "$adminclient.UsersManager.MoveUserFolderAsync" method to move this user's content to another box user. However, on the very next line, instead of using the "wait" method to wait for the file transfer process to finish, you use the "$adminclient.UsersManager.DeleteEnterpriseUserAsync" method (where the "force" parameter is set to $false) to attempt to delete the user. You will get an error because the previous action to move the user's files to another account is still processing and has not completed yet. I make it a point to use the "wait" method every time I perform a task using the $adminclient to make sure that all processes and results are returned before the script continues to execute.
2. When I used $adminclient.FoldersManager.CopyAsync($folderrequest, $null) to copy a folder in the script above, some of you may have been slightly confused as to why I assigned the value I did to "$folderrequest." This is because the syntax used is the powershell equivalent of using hashtables in a json object. This link is where I obtained the information I needed to create these variables. As for what information actually needs to be in the variables, look no further than the API documentation page. If you look at the Curl representation of the command, you will find the Json variables which are needed in order to construct the request you need to perform the operation.
I hope this helped some of you. I will be honest, I will probably never check this posting again just because of time constraints, but I hope this put you on the right path to getting the answers you needed to automate box in powershell. Peace.
