diff --git a/CHANGELOG.md b/CHANGELOG.md index a1cb84a7..0eeadbb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog * [Changelog](#changelog) + * [2.22.4](#2224) * [2.22.3](#2223) * [2.22.2](#2222) * [2.22.1](#2221) @@ -72,6 +73,12 @@ *** +## 2.22.4 + +* [Issue #147](https://github.com/scrthq/PSGSuite/issues/147) + * Added: `Get-GSChromeOSDevice` - Handles Get or List requests, depending on if you specify a ResourceId or not. + * Added: `Update-GSChromeOSDevice` - Handles Action, Move and/or Patch requests depending on the parameters passed. + ## 2.22.3 * [Issue #144](https://github.com/scrthq/PSGSuite/issues/144) diff --git a/PSGSuite/PSGSuite.psd1 b/PSGSuite/PSGSuite.psd1 index 228bb69c..b185ad8d 100644 --- a/PSGSuite/PSGSuite.psd1 +++ b/PSGSuite/PSGSuite.psd1 @@ -12,7 +12,7 @@ RootModule = 'PSGSuite.psm1' # Version number of this module. - ModuleVersion = '2.22.3' + ModuleVersion = '2.22.4' # ID used to uniquely identify this module GUID = '9d751152-e83e-40bb-a6db-4c329092aaec' diff --git a/PSGSuite/Private/Move-GSChromeOSDevice.ps1 b/PSGSuite/Private/Move-GSChromeOSDevice.ps1 new file mode 100644 index 00000000..18935eea --- /dev/null +++ b/PSGSuite/Private/Move-GSChromeOSDevice.ps1 @@ -0,0 +1,70 @@ +function Move-GSChromeOSDevice { + <# + .SYNOPSIS + Moves a ChromeOS device to a new OrgUnit + + .DESCRIPTION + Moves a ChromeOS device to a new OrgUnit + + .PARAMETER ResourceId + The unique ID of the device you would like to move. + + .PARAMETER OrgUnitPath + Full path of the target organizational unit or its ID + + .EXAMPLE + Move-GSChromeOSDevice -ResourceId 'AFiQxQ8Qgd-rouSmcd2UnuvhYV__WXdacTgJhPEA1QoQJrK1hYbKJXm-8JFlhZOjBF4aVbhleS2FVQk5lI069K2GULpteTlLVpKLJFSLSL' -OrgUnitPath '/Corp' + + Moves the ChromeOS device specified to the /Corp OrgUnit + #> + [OutputType('Google.Apis.Admin.Directory.directory_v1.Data.ChromeOSDevice')] + [cmdletbinding()] + Param + ( + [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] + [Alias('Id','Device','DeviceId')] + [String[]] + $ResourceId, + [parameter(Mandatory = $true,Position = 1)] + [String] + $OrgUnitPath + ) + Begin { + $serviceParams = @{ + Scope = 'https://www.googleapis.com/auth/admin.directory.device.chromeos' + ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' + } + $service = New-GoogleService @serviceParams -Verbose:$false + $customerId = if ($Script:PSGSuite.CustomerID) { + $Script:PSGSuite.CustomerID + } + else { + "my_customer" + } + $toMove = New-Object 'System.Collections.Generic.List[string]' + } + Process { + $ResourceId | ForEach-Object { + $toMove.Add($_) + } + } + End { + try { + Write-Verbose "Moving the following Chrome OS Devices to OrgUnitPath '$OrgUnitPath':`n - $($toMove -join "`n - ")" + $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.ChromeOsMoveDevicesToOu' -Property @{ + DeviceIds = $toMove + } + $request = $service.Chromeosdevices.MoveDevicesToOu($body,$customerId,$OrgUnitPath) + $request.Execute() + Write-Verbose "The Chrome OS devices were successfully moved." + } + catch { + if ($ErrorActionPreference -eq 'Stop') { + $PSCmdlet.ThrowTerminatingError($_) + } + else { + Write-Error $_ + } + } + } +} diff --git a/PSGSuite/Public/Security/Get-GSChromeOSDevice.ps1 b/PSGSuite/Public/Security/Get-GSChromeOSDevice.ps1 new file mode 100644 index 00000000..7e33e419 --- /dev/null +++ b/PSGSuite/Public/Security/Get-GSChromeOSDevice.ps1 @@ -0,0 +1,166 @@ +function Get-GSChromeOSDevice { + <# + .SYNOPSIS + Gets the list of Chrome OS Devices registered for the user's account + + .DESCRIPTION + Gets the list of Chrome OS Devices registered for the user's account + + .PARAMETER ResourceId + Immutable ID of Chrome OS Device. Gets the list of Chrome OS devices if excluded. + + .PARAMETER OrgUnitPath + The full path of the organizational unit or its unique ID. + + .PARAMETER Filter + Search string in the format given at: http://support.google.com/chromeos/a/bin/answer.py?answer=1698333 + + .PARAMETER Projection + Restrict information returned to a set of selected fields. + + Acceptable values are: + * "BASIC": Includes only the basic metadata fields (e.g., deviceId, model, status, type, and status) + * "FULL": Includes all metadata fields + + Defauls to "FULL" + + .PARAMETER PageSize + Page size of the result set + + .PARAMETER OrderBy + Device property to use for sorting results. + + Acceptable values are: + * "annotatedLocation": Chrome device location as annotated by the administrator. + * "annotatedUser": Chrome device user as annotated by the administrator. + * "lastSync": The date and time the Chrome device was last synchronized with the policy settings in the Admin console. + * "notes": Chrome device notes as annotated by the administrator. + * "serialNumber": The Chrome device serial number entered when the device was enabled. + * "status": Chrome device status. For more information, see the chromeosdevices resource. + * "supportEndDate": Chrome device support end date. This is applicable only for devices purchased directly from Google. + + .PARAMETER SortOrder + Whether to return results in ascending or descending order. Must be used with the OrderBy parameter. + + Acceptable values are: + * "ASCENDING": Ascending order. + * "DESCENDING": Descending order. + + .EXAMPLE + Get-GSChromeOSDevice + + Gets the Chrome OS Device list for the customer + #> + [OutputType('Google.Apis.Admin.Directory.directory_v1.Data.ChromeOSDevice')] + [cmdletbinding(DefaultParameterSetName = "List")] + Param + ( + [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName = "Get")] + [Alias('Id','Device','DeviceId')] + [String[]] + $ResourceId, + [parameter(Mandatory = $false,ParameterSetName = "List")] + [Alias('Query')] + [String] + $Filter, + [parameter(Mandatory = $false,ParameterSetName = "List")] + [String] + $OrgUnitPath, + [parameter(Mandatory = $false)] + [ValidateSet("BASIC","FULL")] + [String] + $Projection = "FULL", + [parameter(Mandatory = $false,ParameterSetName = "List")] + [ValidateRange(1,100)] + [Alias('MaxResults')] + [Int] + $PageSize = "100", + [parameter(Mandatory = $false,ParameterSetName = "List")] + [ValidateSet("annotatedLocation","annotatedUser","lastSync","notes","serialNumber","status","supportEndDate")] + [String] + $OrderBy, + [parameter(Mandatory = $false,ParameterSetName = "List")] + [ValidateSet("ASCENDING","DESCENDING")] + [String] + $SortOrder + ) + Begin { + $serviceParams = @{ + Scope = 'https://www.googleapis.com/auth/admin.directory.device.chromeos' + ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' + } + $service = New-GoogleService @serviceParams + $customerId = if ($Script:PSGSuite.CustomerID) { + $Script:PSGSuite.CustomerID + } + else { + "my_customer" + } + + } + Process { + try { + switch ($PSCmdlet.ParameterSetName) { + Get { + foreach ($dev in $ResourceId) { + try { + Write-Verbose "Getting Chrome OS Device '$dev'" + $request = $service.Chromeosdevices.Get($customerId,$dev) + $request.Execute() + } + catch { + if ($ErrorActionPreference -eq 'Stop') { + $PSCmdlet.ThrowTerminatingError($_) + } + else { + Write-Error $_ + } + } + } + } + List { + $request = $service.Chromeosdevices.List($customerId) + if ($PSBoundParameters.Keys -contains 'Filter') { + Write-Verbose "Getting Chrome OS Device list for filter '$Filter'" + $request.Query = $PSBoundParameters['Filter'] + } + else { + Write-Verbose "Getting Chrome OS Device list" + } + foreach ($key in $PSBoundParameters.Keys | Where-Object {$_ -ne 'Filter'}) { + switch ($key) { + PageSize { + $request.MaxResults = $PSBoundParameters[$key] + } + default { + if ($request.PSObject.Properties.Name -contains $key) { + $request.$key = $PSBoundParameters[$key] + } + } + } + } + [int]$i = 1 + do { + $result = $request.Execute() + $result.Chromeosdevices + if ($result.NextPageToken) { + $request.PageToken = $result.NextPageToken + } + [int]$retrieved = ($i + $result.Chromeosdevices.Count) - 1 + Write-Verbose "Retrieved $retrieved Chrome OS Devices..." + [int]$i = $i + $result.Chromeosdevices.Count + } + until (!$result.NextPageToken) + } + } + } + catch { + if ($ErrorActionPreference -eq 'Stop') { + $PSCmdlet.ThrowTerminatingError($_) + } + else { + Write-Error $_ + } + } + } +} diff --git a/PSGSuite/Public/Security/Update-GSChromeOSDevice.ps1 b/PSGSuite/Public/Security/Update-GSChromeOSDevice.ps1 new file mode 100644 index 00000000..570302c1 --- /dev/null +++ b/PSGSuite/Public/Security/Update-GSChromeOSDevice.ps1 @@ -0,0 +1,142 @@ +function Update-GSChromeOSDevice { + <# + .SYNOPSIS + Updates a ChromeOS device + + .DESCRIPTION + Updates a ChromeOS device + + .PARAMETER ResourceId + The unique ID of the device you would like to update. + + .PARAMETER Action + Action to be taken on the Chrome OS device. + + Acceptable values are: + * "deprovision": Remove a device from management that is no longer active, being resold, or is being submitted for return / repair, use the deprovision action to dissociate it from management. + * "disable": If you believe a device in your organization has been lost or stolen, you can disable the device so that no one else can use it. When a device is disabled, all the user can see when turning on the Chrome device is a screen telling them that it’s been disabled, and your desired contact information of where to return the device. + Note: Configuration of the message to appear on a disabled device must be completed within the admin console. + "reenable": Re-enable a disabled device when a misplaced device is found or a lost device is returned. You can also use this feature if you accidentally mark a Chrome device as disabled. + Note: The re-enable action can only be performed on devices marked as disabled. + + .PARAMETER DeprovisionReason + Only used when the action is deprovision. With the deprovision action, this field is required. + + Note: The deprovision reason is audited because it might have implications on licenses for perpetual subscription customers. + + Acceptable values are: + * "different_model_replacement": Use if you're upgrading or replacing your device with a newer model of the same device. + * "retiring_device": Use if you're reselling, donating, or permanently removing the device from use. + * "same_model_replacement": Use if a hardware issue was encountered on a device and it is being replaced with the same model or a like-model replacement from a repair vendor / manufacturer. + + .PARAMETER OrgUnitPath + Full path of the target organizational unit or its ID that you would like to move the device to. + + .EXAMPLE + Update-GSChromeOSDevice -ResourceId 'AFiQxQ8Qgd-rouSmcd2UnuvhYV__WXdacTgJhPEA1QoQJrK1hYbKJXm-8JFlhZOjBF4aVbhleS2FVQk5lI069K2GULpteTlLVpKLJFSLSL' -Action deprovision -DeprovisionReason retiring_device + + Deprovisions the retired ChromeOS device + #> + [OutputType('Google.Apis.Admin.Directory.directory_v1.Data.ChromeOSDevice')] + [cmdletbinding()] + Param + ( + [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] + [Alias('Id','Device','DeviceId')] + [String[]] + $ResourceId, + [parameter(Mandatory = $false)] + [ValidateSet('deprovision','disable','reenable')] + [String] + $Action, + [parameter(Mandatory = $false)] + [ValidateSet('different_model_replacement','retiring_device','same_model_replacement')] + [String] + $DeprovisionReason, + [parameter(Mandatory = $false)] + [String] + $OrgUnitPath + + ) + Begin { + $serviceParams = @{ + Scope = 'https://www.googleapis.com/auth/admin.directory.device.chromeos' + ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' + } + $service = New-GoogleService @serviceParams + $customerId = if ($Script:PSGSuite.CustomerID) { + $Script:PSGSuite.CustomerID + } + else { + "my_customer" + } + $bodyUpdated = $false + } + Process { + $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.ChromeOsDevice' + foreach ($key in $PSBoundParameters.Keys | Where-Object {$_ -in @('Action','OrgUnitPath') -or $_ -in $body.PSObject.Properties.Name}) { + switch ($key) { + Action { + try { + $actionBody = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.ChromeOSDeviceAction' -Property @{ + Action = $Action + } + if ($PSBoundParameters.Keys -contains 'DeprovisionReason') { + $actionBody.DeprovisionReason = $PSBoundParameters['DeprovisionReason'] + } + foreach ($dev in $ResourceId) { + Write-Verbose "Updating Chrome OS Device '$dev' with Action '$Action'" + $request = $service.Chromeosdevices.Action($actionBody,$customerId,$dev) + $request.Execute() + Write-Verbose "Chrome OS Device was successfully updated" + } + } + catch { + if ($ErrorActionPreference -eq 'Stop') { + $PSCmdlet.ThrowTerminatingError($_) + } + else { + Write-Error $_ + } + } + } + OrgUnitPath { + try { + Move-GSChromeOSDevice -ResourceId $ResourceId -OrgUnitPath $OrgUnitPath + } + catch { + if ($ErrorActionPreference -eq 'Stop') { + $PSCmdlet.ThrowTerminatingError($_) + } + else { + Write-Error $_ + } + } + } + default { + if ($key -in $body.PSObject.Properties.Name) { + $bodyUpdated = $true + $body.$key = $PSBoundParameters[$key] + } + } + } + } + if ($bodyUpdated) { + foreach ($dev in $ResourceId) { + try { + Write-Verbose "Updating Chrome OS Device '$dev'" + $request = $service.Chromeosdevices.Patch($actionBody,$customerId,$dev) + $request.Execute() + } + catch { + if ($ErrorActionPreference -eq 'Stop') { + $PSCmdlet.ThrowTerminatingError($_) + } + else { + Write-Error $_ + } + } + } + } + } +} diff --git a/README.md b/README.md index 4957b088..6d561523 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,8 @@ Update-GSSheetValue Export-GSSheet [Full CHANGELOG here](https://github.com/scrthq/PSGSuite/blob/master/CHANGELOG.md) -#### 2.22.3 +#### 2.22.4 -* [Issue #144](https://github.com/scrthq/PSGSuite/issues/144) - * Updated: `Start-GSDriveFileUpload` to not call `[System.Console]::CursorVisible` when `$Host` is PowerShell ISE +* [Issue #147](https://github.com/scrthq/PSGSuite/issues/147) + * Added: `Get-GSChromeOSDevice` - Handles Get or List requests, depending on if you specify a ResourceId or not. + * Added: `Update-GSChromeOSDevice` - Handles Action, Move and/or Patch requests depending on the parameters passed. diff --git a/Tests/3. Integration Tests/PSGSuite.Integration.Tests.ps1 b/Tests/3. Integration Tests/PSGSuite.Integration.Tests.ps1 index ca1b4164..cdbeb3f0 100644 --- a/Tests/3. Integration Tests/PSGSuite.Integration.Tests.ps1 +++ b/Tests/3. Integration Tests/PSGSuite.Integration.Tests.ps1 @@ -1,21 +1,22 @@ -$projectRoot = Resolve-Path "$PSScriptRoot\..\.." -$ModulePath = Resolve-Path "$projectRoot\BuildOutput\$($env:BHProjectName)" -$decompiledModulePath = Resolve-Path "$projectRoot\$($env:BHProjectName)" +if ($ENV:BUILD_BUILDURI -like 'vstfs:*') { + $projectRoot = Resolve-Path "$PSScriptRoot\..\.." + $ModulePath = Resolve-Path "$projectRoot\BuildOutput\$($env:BHProjectName)" + $decompiledModulePath = Resolve-Path "$projectRoot\$($env:BHProjectName)" -$Verbose = @{} -if ($ENV:BHBranchName -eq "development" -or $env:BHCommitMessage -match "!verbose") { - $Verbose.add("Verbose",$True) -} -$moduleRoot = Split-Path (Resolve-Path "$ModulePath\*\*.psd1") - -Import-Module $ModulePath -Force -Verbose:$false -Import-PSGSuiteConfig -Json $env:PSGSuiteConfigJson -Temporary -Verbose + $Verbose = @{} + if ($ENV:BHBranchName -eq "development" -or $env:BHCommitMessage -match "!verbose") { + $Verbose.add("Verbose",$True) + } + $moduleRoot = Split-Path (Resolve-Path "$ModulePath\*\*.psd1") -$u = Get-GSUser -$u | Select-Object @{N="GivenName";E={$_.Name.GivenName}},OrgUnitPath,Kind + Import-Module $ModulePath -Force -Verbose:$false + Import-PSGSuiteConfig -Json $env:PSGSuiteConfigJson -Temporary -Verbose -Send-GmailMessage -From $u.PrimaryEmail -To $u.PrimaryEmail -Subject "Hello from Azure Pipelines + PS Version $($PSVersionTable.PSVersion.ToString())!" -Body "
`n$((Get-ChildItem Env: | Where-Object {$_.Name -match '^(BUILD_|BH).*$'} | Format-Table -AutoSize | Out-String).Trim())`n
" -BodyAsHtml -Verbose + $u = Get-GSUser + $u | Select-Object @{N="GivenName";E={$_.Name.GivenName}},OrgUnitPath,Kind + Send-GmailMessage -From $u.PrimaryEmail -To $u.PrimaryEmail -Subject "Hello from Azure Pipelines + PS Version $($PSVersionTable.PSVersion.ToString())!" -Body "
`n$((Get-ChildItem Env: | Where-Object {$_.Name -match '^(BUILD_|BH).*$'} | Format-Table -AutoSize | Out-String).Trim())`n
" -BodyAsHtml -Verbose +} <# Describe "Function contents" -Tag 'Module' { Context "Get-GSUser should return a user" { diff --git a/build.ps1 b/build.ps1 index 176ccd15..0372dfb5 100644 --- a/build.ps1 +++ b/build.ps1 @@ -160,7 +160,7 @@ else { $global:ForceDeploy = $false } Invoke-psake @psakeParams @verbose - if ($Task -contains 'Import' -and $psake.build_success) { + if (($Task -contains 'Import' -or $Task -contains 'Test') -and $psake.build_success) { Import-Module ([System.IO.Path]::Combine($env:BHBuildOutput,$env:BHProjectName)) -Verbose:$false } exit ( [int]( -not $psake.build_success ) )