UGDSB.PS.psm1
#Region '.\Public\Add-GraphGroupMember.ps1' -1 function Add-GraphGroupMember{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$groupId, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string[]]$ids ) # Confirm we have a valid graph token if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # Get Graph Headers for Call $headers = Get-GraphHeader try{ $batchObj = [System.Collections.Generic.List[PSCustomObject]]@() $batches = [System.Collections.Generic.List[PSCustomObject]]@() for($i = 1; $i -le $ids.Count; ++$i){ $obj = [PSCustomObject]@{ "id" = $i "headers" = @{ "Content-type" = "application/json" } "method" = "POST" "url" = "/groups/$($groupId)/members/`$ref" "body" = @{ "@odata.id" = "https://graph.microsoft.com/beta/directoryObjects/$($ids[$i-1])" } } $batchObj.Add($obj) | Out-Null if($($i % 20) -eq 0){ $batches.Add($batchObj) | Out-Null $batchObj = $null $batchObj = [System.Collections.Generic.List[PSCustomObject]]@() } } $batches.Add($batchObj) | Out-Null foreach($batch in $batches){ $json = [PSCustomObject]@{ "requests" = $batch } | ConvertTo-JSON -Depth 5 $results = Invoke-RestMethod -Method "POST" -Uri "https://graph.microsoft.com/beta/`$batch" -Headers $headers -Body $json } } catch{ throw "Unable to add members. $($_.Exception.Message)" } } #EndRegion '.\Public\Add-GraphGroupMember.ps1' 48 #Region '.\Public\Add-GraphIntuneAppAddToESP.ps1' -1 function Add-GraphIntuneAppAddToESP{ [CmdletBinding()] param( [Parameter(Mandatory = $true,ParameterSetName = 'id')][ValidateNotNullOrEmpty()][string]$id, [Parameter(Mandatory = $true, ParameterSetName = 'displayName')][ValidateNotNullOrEmpty()][string]$displayName, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$applicationid ) # Body for JSON if($id){ $currentData = Get-GraphIntuneEnrollmentStatusPage -id $id } elseif($displayName){ $currentData = Get-GraphIntuneEnrollmentStatusPage -displayName $displayName } # Endpoint $endpoint = "deviceManagement/deviceEnrollmentConfigurations/$($currentData.id)" # Add Application to the ESP $currentData.selectedMobileAppIds = @("$($currentData.selectedMobileAppIds -join ','),$($applicationid)") -split "," $params = @{ "@odata.type" = "#microsoft.graph.windows10EnrollmentCompletionPageConfiguration" id = $currentData.id displayName = $currentData.displayName description = $currentData.description showInstallationProgress = $currentData.showInstallationProgress blockDeviceSetupRetryByUser = $currentData.blockDeviceSetupRetryByUser allowDeviceResetOnInstallFailure = $currentData.allowDeviceResetOnInstallFailure allowLogCollectionOnInstallFailure = $currentData.allowLogCollectionOnInstallFailure customErrorMessage = $currentData.customErrorMessage installProgressTimeoutInMinutes = $currentData.installProgressTimeoutInMinutes allowDeviceUseOnInstallFailure = $currentData.allowDeviceUseOnInstallFailure selectedMobileAppIds = $currentData.selectedMobileAppIds trackInstallProgressForAutopilotOnly = $currentData.trackInstallProgressForAutopilotOnly disableUserStatusTrackingAfterFirstUser = $currentData.disableUserStatusTrackingAfterFirstUser roleScopeTagIds = $currentData.roleScopeTagIds allowNonBlockingAppInstallation = $currentData.allowNonBlockingAppInstallation installQualityUpdates = $currentData.installQualityUpdates } try{ $uri = "https://graph.microsoft.com/beta/$($endpoint)" $headers = Get-GraphHeader Invoke-RestMethod -Method Patch -Uri $uri -Headers $headers -Body $($params | ConvertTo-Json -Depth 10) -StatusCodeVariable statusCode } catch{ $_ } } #EndRegion '.\Public\Add-GraphIntuneAppAddToESP.ps1' 47 #Region '.\Public\Add-GraphIntuneAppAssignment.ps1' -1 function Add-GraphIntuneAppAssignment{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$applicationid, [Parameter(Mandatory = $true)][ValidateSet("available","required","uninstall")][string]$intent, [Parameter(Mandatory = $false)][string[]]$groups, [Parameter(Mandatory = $false)][Object[]]$filters = $null, [Parameter(Mandatory = $false)][switch]$exclude, [Parameter(Mandatory = $false)][bool]$foreground = $false, [Parameter(Mandatory = $false)][ValidateSet("Output","Verbose")][string]$LogLevel = "Verbose" ) # Default format for assignment body $assignment = [PSCustomObject]@{ "@odata.type" = "#microsoft.graph.mobileAppAssignment" "intent" = $intent "target" = $null "settings" = $null } foreach($group in $groups){ # Set Filter details $filterId = $null $filterType = "none" if($null -ne $filters.$group){ $assignmentFilters = Get-GraphIntuneFilters $filterId = ($assignmentFilters | Where-Object {$_.DisplayName -eq $filters.$group.filterName}).id $filterType = $filters.$group.filterType } $target = [PSCustomObject]@{ "deviceAndAppManagementAssignmentFilterId" = $filterId "deviceAndAppManagementAssignmentFilterType" = $filterType } # Targeting Values switch($group.ToLower()){ "all users" { $target | Add-Member -MemberType "NoteProperty" -Name "@odata.type" -Value "#microsoft.graph.allLicensedUsersAssignmentTarget" } "all devices" { $target | Add-Member -MemberType "NoteProperty" -Name "@odata.type" -Value "#microsoft.graph.allDevicesAssignmentTarget" } default { if($exclude){ $target | Add-Member -MemberType "NoteProperty" -Name "@odata.type" -Value "#microsoft.graph.exclusionGroupAssignmentTarget" } else{ $target | Add-Member -MemberType "NoteProperty" -Name "@odata.type" -Value "#microsoft.graph.groupAssignmentTarget" } try{ $groupdetails = Get-GraphGroup -groupName $group $target | Add-Member -MemberType "NoteProperty" -Name "groupId" -Value $groupdetails.id } catch{ throw "Unable to get ID of the group selected. $($_.Exception.Message)" } } } $assignment.target = $target # Settings Values if(!$exclude){ $settings = [PSCustomObject]@{ "@odata.type" = "#microsoft.graph.win32LobAppAssignmentSettings" } if($foreground){ $settings | Add-Member -MemberType "NoteProperty" -Name "deliveryOptimizationPriority" -Value "foreground" } $assignment.settings = $settings } try{ $endpoint = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$($applicationid)/assignments" $headers = Get-GraphHeader Invoke-RestMethod -Method post -Uri $endpoint -Headers $headers -Body $($assignment | ConvertTo-Json -Depth 10) -StatusCodeVariable statusCode | Out-Null } catch{ if(([REGEX]::Match($((($_ | ConvertFrom-Json).error.message | ConvertFrom-JSON).Message),"The MobileApp Assignment already exists")).Success){ continue } else{ throw $($_.Exception.Message) } } } } #EndRegion '.\Public\Add-GraphIntuneAppAssignment.ps1' 82 #Region '.\Public\Add-PInvokeType.ps1' -1 function Add-PInvokeType { [cmdletbinding()] param() #region LSAUtil # C# Code to P-invoke LSA functions. # This code is copied from PInvoke.net # http://www.pinvoke.net/default.aspx/advapi32.lsaretrieveprivatedata Add-Type @" using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; namespace PInvoke.LSAUtil { public class LSAutil { [StructLayout (LayoutKind.Sequential)] private struct LSA_UNICODE_STRING { public UInt16 Length; public UInt16 MaximumLength; public IntPtr Buffer; } [StructLayout (LayoutKind.Sequential)] private struct LSA_OBJECT_ATTRIBUTES { public int Length; public IntPtr RootDirectory; public LSA_UNICODE_STRING ObjectName; public uint Attributes; public IntPtr SecurityDescriptor; public IntPtr SecurityQualityOfService; } private enum LSA_AccessPolicy : long { POLICY_VIEW_LOCAL_INFORMATION = 0x00000001L, POLICY_VIEW_AUDIT_INFORMATION = 0x00000002L, POLICY_GET_PRIVATE_INFORMATION = 0x00000004L, POLICY_TRUST_ADMIN = 0x00000008L, POLICY_CREATE_ACCOUNT = 0x00000010L, POLICY_CREATE_SECRET = 0x00000020L, POLICY_CREATE_PRIVILEGE = 0x00000040L, POLICY_SET_DEFAULT_QUOTA_LIMITS = 0x00000080L, POLICY_SET_AUDIT_REQUIREMENTS = 0x00000100L, POLICY_AUDIT_LOG_ADMIN = 0x00000200L, POLICY_SERVER_ADMIN = 0x00000400L, POLICY_LOOKUP_NAMES = 0x00000800L, POLICY_NOTIFICATION = 0x00001000L } [DllImport ("advapi32.dll", SetLastError = true, PreserveSig = true)] private static extern uint LsaRetrievePrivateData ( IntPtr PolicyHandle, ref LSA_UNICODE_STRING KeyName, out IntPtr PrivateData ); [DllImport ("advapi32.dll", SetLastError = true, PreserveSig = true)] private static extern uint LsaStorePrivateData ( IntPtr policyHandle, ref LSA_UNICODE_STRING KeyName, ref LSA_UNICODE_STRING PrivateData ); [DllImport ("advapi32.dll", SetLastError = true, PreserveSig = true)] private static extern uint LsaOpenPolicy ( ref LSA_UNICODE_STRING SystemName, ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, uint DesiredAccess, out IntPtr PolicyHandle ); [DllImport ("advapi32.dll", SetLastError = true, PreserveSig = true)] private static extern uint LsaNtStatusToWinError ( uint status ); [DllImport ("advapi32.dll", SetLastError = true, PreserveSig = true)] private static extern uint LsaClose ( IntPtr policyHandle ); [DllImport ("advapi32.dll", SetLastError = true, PreserveSig = true)] private static extern uint LsaFreeMemory ( IntPtr buffer ); private LSA_OBJECT_ATTRIBUTES objectAttributes; private LSA_UNICODE_STRING localsystem; private LSA_UNICODE_STRING secretName; public LSAutil (string key) { if (key.Length == 0) { throw new Exception ("Key lenght zero"); } objectAttributes = new LSA_OBJECT_ATTRIBUTES (); objectAttributes.Length = 0; objectAttributes.RootDirectory = IntPtr.Zero; objectAttributes.Attributes = 0; objectAttributes.SecurityDescriptor = IntPtr.Zero; objectAttributes.SecurityQualityOfService = IntPtr.Zero; localsystem = new LSA_UNICODE_STRING (); localsystem.Buffer = IntPtr.Zero; localsystem.Length = 0; localsystem.MaximumLength = 0; secretName = new LSA_UNICODE_STRING (); secretName.Buffer = Marshal.StringToHGlobalUni (key); secretName.Length = (UInt16) (key.Length * UnicodeEncoding.CharSize); secretName.MaximumLength = (UInt16) ((key.Length + 1) * UnicodeEncoding.CharSize); } private IntPtr GetLsaPolicy (LSA_AccessPolicy access) { IntPtr LsaPolicyHandle; uint ntsResult = LsaOpenPolicy (ref this.localsystem, ref this.objectAttributes, (uint) access, out LsaPolicyHandle); uint winErrorCode = LsaNtStatusToWinError (ntsResult); if (winErrorCode != 0) { throw new Exception ("LsaOpenPolicy failed: " + winErrorCode); } return LsaPolicyHandle; } private static void ReleaseLsaPolicy (IntPtr LsaPolicyHandle) { uint ntsResult = LsaClose (LsaPolicyHandle); uint winErrorCode = LsaNtStatusToWinError (ntsResult); if (winErrorCode != 0) { throw new Exception ("LsaClose failed: " + winErrorCode); } } private static void FreeMemory (IntPtr Buffer) { uint ntsResult = LsaFreeMemory (Buffer); uint winErrorCode = LsaNtStatusToWinError (ntsResult); if (winErrorCode != 0) { throw new Exception ("LsaFreeMemory failed: " + winErrorCode); } } public void SetSecret (string value) { LSA_UNICODE_STRING lusSecretData = new LSA_UNICODE_STRING (); if (value.Length > 0) { //Create data and key lusSecretData.Buffer = Marshal.StringToHGlobalUni (value); lusSecretData.Length = (UInt16) (value.Length * UnicodeEncoding.CharSize); lusSecretData.MaximumLength = (UInt16) ((value.Length + 1) * UnicodeEncoding.CharSize); } else { //Delete data and key lusSecretData.Buffer = IntPtr.Zero; lusSecretData.Length = 0; lusSecretData.MaximumLength = 0; } IntPtr LsaPolicyHandle = GetLsaPolicy (LSA_AccessPolicy.POLICY_CREATE_SECRET); uint result = LsaStorePrivateData (LsaPolicyHandle, ref secretName, ref lusSecretData); ReleaseLsaPolicy (LsaPolicyHandle); uint winErrorCode = LsaNtStatusToWinError (result); if (winErrorCode != 0) { throw new Exception ("StorePrivateData failed: " + winErrorCode); } } public string GetSecret () { IntPtr PrivateData = IntPtr.Zero; IntPtr LsaPolicyHandle = GetLsaPolicy (LSA_AccessPolicy.POLICY_GET_PRIVATE_INFORMATION); uint ntsResult = LsaRetrievePrivateData (LsaPolicyHandle, ref secretName, out PrivateData); ReleaseLsaPolicy (LsaPolicyHandle); uint winErrorCode = LsaNtStatusToWinError (ntsResult); if (winErrorCode != 0) { throw new Exception ("RetreivePrivateData failed: " + winErrorCode); } LSA_UNICODE_STRING lusSecretData = (LSA_UNICODE_STRING) Marshal.PtrToStructure (PrivateData, typeof (LSA_UNICODE_STRING)); string value = Marshal.PtrToStringAuto (lusSecretData.Buffer).Substring (0, lusSecretData.Length / 2); FreeMemory (PrivateData); return value; } } } "@ #endregion } #EndRegion '.\Public\Add-PInvokeType.ps1' 190 #Region '.\Public\Add-TopdeskAssetAssignment.ps1' -1 <# .DESCRIPTION This cmdlet is designed to add an location assignment to an asset .PARAMETER assetID The unid of the asset we want to update .PARAMETER locationID The location id that we want to add to asset .EXAMPLE #> function Add-TopdeskAssetAssignment{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][string]$assetID, [Parameter(Mandatory = $true)][string]$locationID ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } $body = @{ "assetIds" = @( $assetID ) "branchId" = $locationID "linkToId" = $locationID "linkType" = "branch" } $uri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/assetmgmt/assets/assignments" try{ $results = Invoke-RestMethod -Method PUT -Uri $uri -Headers $global:topdeskAccessToken -body ($body | ConvertTo-Json) -StatusCodeVariable statusCode } catch{ throw "Error Accessing API at $($uri). $($Error[0])" } return $results } #EndRegion '.\Public\Add-TopdeskAssetAssignment.ps1' 38 #Region '.\Public\Add-TopdeskIncidentAttachment.ps1' -1 function Add-TopdeskIncidentAttachment{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$id, [Parameter()][ValidateNotNullOrEmpty()][string]$filepath, [Parameter()][ValidateNotNullOrEmpty()][string]$filename, [Parameter()][ValidateNotNullOrEmpty()][string]$base64, [Parameter()][ValidateNotNullOrEmpty()][string]$contenttype = "application/plain;charset=utf-8", [Parameter()][bool]$invisibleForCaller = $false ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } $uri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/incidents/id/$($id)/attachments" if($filepath){ # Information on file $item = Get-Item -Path $filepath # Get content of file $FileContent = Get-Content $filepath # Get bytes of file $fileContentInBytes = [System.Text.Encoding]::UTF8.GetBytes($FileContent) # Get base 64 of file $fileContentEncoded = [System.Convert]::ToBase64String($fileContentInBytes) # Build body for upload $body = "--BOUNDARY Content-Disposition: form-data; name=`"file`"; filename=`"$($filename)`" Content-Type: $($contenttype) Content-Transfer-Encoding: base64 $($fileContentEncoded) --BOUNDARY--" } else{ $body = "--BOUNDARY Content-Disposition: form-data; name=`"file`"; filename=`"$($filename)`" Content-Type: application/pdf Content-Transfer-Encoding: base64 $($base64) --BOUNDARY--" } try{ $header = $global:topdeskAccessToken.Clone() $header."Content-Type" = "multipart/form-data; boundary=BOUNDARY" Invoke-RestMethod -Method POST -Uri $uri -Headers $header -body $body -StatusCodeVariable statusCode } catch{ throw "Error Accessing API at $($uri). $($Error[0])" } } #EndRegion '.\Public\Add-TopdeskIncidentAttachment.ps1' 52 #Region '.\Public\Connect-Meraki.ps1' -1 <# .DESCRIPTION This cmdlet is designed to convert and store header information for meraki api .PARAMETER credential The credential to connect to the API endpoints .PARAMETER organizationId Your meraki organization ID #> function Connect-Meraki{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][SecureString]$credential, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$organizationId, [Parameter()][ValidateNotNullOrEmpty()][string]$URI = "https://api.meraki.com/api/v1/" ) # This sets the global variables that are used to connect to the topdesk API $global:merakiHeader = @{ "X-Cisco-Meraki-API-Key" = ConvertFrom-SecureString $credential -AsPlainTex "Content-Type" = "application/json" } # This sets a variable for the orgId $global:merakiOrgId = $organizationId # This sets a variable for the orgId $global:merakiApiURI = $URI } #EndRegion '.\Public\Connect-Meraki.ps1' 26 #Region '.\Public\Connect-Topdesk.ps1' -1 <# .DESCRIPTION This cmdlet is designed to convert and store header information for topdesk api .PARAMETER credential The credential to connect to the API endpoints .PARAMETER environment The topdesk enviroment URI. For examble https://test.topdesk.net then this value would be test #> function Connect-Topdesk{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][pscredential]$credential, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$environment ) # This sets the global variables that are used to connect to the topdesk API $global:topdeskAccessToken = @{ "Authorization" = "Basic $([System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("$($credential.username):$($credential.GetNetworkCredential().password)")))" "Content-Type" = "application/json" } # This sets a variable for the enviorment $global:topdeskEnvironment = $environment } #EndRegion '.\Public\Connect-Topdesk.ps1' 23 #Region '.\Public\Convert-EntraObjIDtoSid.ps1' -1 <# .DESCRIPTION This cmdlet will convert a Azure AD Object ID TO Sid .PARAMETER ObjectId Azure AD Object ID .EXAMPLE Convert AzADObject to Sid Convert-EntraObjIDtoSid -objectId <ID> #> function Convert-EntraObjIDtoSid{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][String]$ObjectId ) $bytes = [Guid]::Parse($ObjectId).ToByteArray() $array = New-Object 'UInt32[]' 4 [Buffer]::BlockCopy($bytes, 0, $array, 0, 16) $sid = "S-1-12-1-$array".Replace(' ', '-') return $sid } #EndRegion '.\Public\Convert-EntraObjIDtoSid.ps1' 21 #Region '.\Public\Convert-SidtoEntraObjID.ps1' -1 <# .DESCRIPTION This cmdlet will convert a SID to an Azure AD Object ID .PARAMETER sid SID of object .EXAMPLE Convert Sid to Object ID Convert-SidtoEntraObjID -sid <SID> #> function Convert-SidtoEntraObjID{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][String]$sid ) $text = $sid.Replace('S-1-12-1-', '') $array = [UInt32[]]$text.Split('-') $bytes = New-Object 'Byte[]' 16 [Buffer]::BlockCopy($array, 0, $bytes, 0, 16) [Guid]$guid = $bytes return $guid } #EndRegion '.\Public\Convert-SidtoEntraObjID.ps1' 22 #Region '.\Public\Copy-GraphIntuneAppAssignments.ps1' -1 function Copy-GraphIntuneAppAssignments{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$applicationid, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$copyapplicationid ) # Get the Assignments that we will be copying $assignments = Get-GraphIntuneAppAssignment -applicationid $copyapplicationid # Loop through the assignments foreach($assignment in $assignments){ $assignment = [PSCustomObject]@{ "@odata.type" = "#microsoft.graph.mobileAppAssignment" "intent" = $assignment.intent "target" = $assignment.target "settings" = $assignment.settings } try{ $endpoint = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$($applicationid)/assignments" $headers = Get-GraphHeader Invoke-RestMethod -Method post -Uri $endpoint -Headers $headers -Body $($assignment | ConvertTo-Json -Depth 10) -StatusCodeVariable statusCode | Out-Null } catch{ if(([REGEX]::Match($((($_ | ConvertFrom-Json).error.message | ConvertFrom-JSON).Message),"The MobileApp Assignment already exists")).Success){ continue } else{ throw $($_.Exception.Message) } } } } #EndRegion '.\Public\Copy-GraphIntuneAppAssignments.ps1' 32 #Region '.\Public\Disable-Chromebook.ps1' -1 function Disable-Chromebook{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][string]$deviceID ) # Ensure that they have a access token if(-not $global:googleAccessToken){ throw "Please ensure that you have called Get-GoogleAccessToken cmdlet" } # Confirm we have a valid access token if(-not $(Test-GoogleAccessToken)){ Get-GoogleAccessToken -json $global:googleJSON -customerId $global:googleCustomerId } # Generate URI for REST API call $uri = "https://admin.googleapis.com/admin/directory/v1/customer/$($global:googleCustomerId)/devices/chromeos/$($deviceID)/action" # REST API Call to Get Orginzation Lists $headers = @{ authorization = "Bearer $($Global:googleAccessToken)" "content-type" = "application/json" } $body = @{ "action" = "disable" } $result = Invoke-RestMethod -Method "POST" -URI $uri -Headers $headers -Body ($body | ConvertTo-Json) -StatusCodeVariable statusCode Write-Verbose "Status Result: $($statusCode)" } #EndRegion '.\Public\Disable-Chromebook.ps1' 28 #Region '.\Public\Enable-Chromebook.ps1' -1 function Enable-Chromebook{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][string]$deviceID ) # Ensure that they have a access token if(-not $global:googleAccessToken){ throw "Please ensure that you have called Get-GoogleAccessToken cmdlet" } # Confirm we have a valid access token if(-not $(Test-GoogleAccessToken)){ Get-GoogleAccessToken -json $global:googleJSON -customerId $global:googleCustomerId } # Generate URI for REST API call $uri = "https://admin.googleapis.com/admin/directory/v1/customer/$($global:googleCustomerId)/devices/chromeos/$($deviceID)/action" # REST API Call to Get Orginzation Lists $headers = @{ authorization = "Bearer $($Global:googleAccessToken)" "content-type" = "application/json" } $body = @{ "action" = "reenable" } $result = Invoke-RestMethod -Method "POST" -URI $uri -Headers $headers -Body ($body | ConvertTo-Json) -StatusCodeVariable statusCode Write-Verbose "Status Result: $($statusCode)" } #EndRegion '.\Public\Enable-Chromebook.ps1' 28 #Region '.\Public\Get-AlertFactoryTicketDetails.ps1' -1 <# .DESCRIPTION This cmdlet is designed to parse out data from emails for Alert Factory #> function Get-AlertFactoryTicketDetails{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$details, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$emails ) # Generate blank hashtable for ticket $ticketDetails = @{} foreach($obj in $details.PSObject.Properties){ if($null -ne $obj.value.type){ if($null -ne $obj.value.attribute){ switch($obj.value.attribute){ "createdDateTime" { $content = $emails[0].createdDateTime } "receivedDateTime" { $content = $emails[0].receivedDateTime } "sentDateTime" { $content = $emails[0].sentDateTime } "subject" { $content = $emails[0].subject } "bodyPreview"{ $content = $emails[0].bodyPreview } "body" { $content = $emails[0].body.content } "sender" { $content = $emails[0].sender.emailAddress.address } "from" { $content = $emails[0].from.emailAddress.address } } } else{ $content = $obj.value.value } switch($obj.value.type){ "source" { $ticketDetails.Add($obj.Name,$content) | Out-Null } "string" { $ticketDetails.Add($obj.Name,$obj.value.value) | Out-Null } "regex" { $match = [Regex]::Match($content,$obj.value.value) $val = $null if($match.Success){ $val = $match.Value } $ticketDetails.Add($obj.Name,$val) | Out-Null } } } } return $ticketDetails } #EndRegion '.\Public\Get-AlertFactoryTicketDetails.ps1' 66 #Region '.\Public\Get-AutorunRegKeys.ps1' -1 function Get-AutorunRegKeys { [cmdletbinding()] param( [parameter(Mandatory = $true)][string]$Name, [parameter()][string]$UserName = $null, [parameter()][switch]$runOnce ) $forceload = $false # Get a list of all the user profiles on the machine $ProfileList = Get-ChildItem Registry::"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" | Where-Object { $_.Name -notlike "*_Classes" -and $_.PSChildName -notin ("S-1-5-18", "S-1-5-19", "S-1-5-20") } $UserList = foreach ($UserKey in $ProfileList) { @{ ProfileKey = $UserKey | Where-Object { $_.name -like "*" + $UserKey.PSChildName + "*" } UserName = try { ((([system.security.principal.securityidentIfier]$UserKey.PSChildName).Translate([System.Security.Principal.NTAccount])).ToString()).substring(3) } catch { continue }; SID = $UserKey.PSChildName ProfilePath = Get-ItemProperty $UserKey.PSPath | Select-Object -ExpandProperty ProfileImagePath } } if($null -ne $Username -and $UserName -ne ""){ $SID = ($UserList | Where-Object {$_.UserName -like "*$($UserName)*"}).SID $baseprofile = ($UserList | Where-Object {$_.UserName -like "*$($UserName)*"}).ProfilePath if($runOnce.IsPresent){ $registryPath = "REGISTRY::HKEY_USERS\$($SID)\Software\Microsoft\Windows\CurrentVersion\RunOnce" } else{ $registryPath = "REGISTRY::HKEY_USERS\$($SID)\Software\Microsoft\Windows\CurrentVersion\Run" } if(-not (Test-Path -Path $registryPath)){ $hivepath = Join-Path -Path $baseprofile -ChildPath "NTUSER.DAT" reg Load "HKU\$($SID)" "$($hivepath)" | Out-Null $forceload = $true if(-not (Test-Path -Path $registryPath)){ throw "Unable to load hive for user: $($UserName)" } } } else{ if($runOnce.IsPresent){ $registryPath = "REGISTRY::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce" } else{ $registryPath = "REGISTRY::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run" } } $Keys = (Get-ItemProperty -Path $registryPath).psobject.properties | Where-Object { $_.Name -eq $Name } if($forceload){ [gc]::Collect() reg unload "HKU\$($SID)" | Out-Null } return $Keys } #EndRegion '.\Public\Get-AutorunRegKeys.ps1' 52 #Region '.\Public\Get-ChromeDevices.ps1' -1 <# .DESCRIPTION This cmdlet will retrive chrome OS devices https://developers.google.com/admin-sdk/directory/reference/rest/v1/chromeosdevices/list https://developers.google.com/admin-sdk/directory/v1/list-query-operators .PARAMETER maxResults How many results to return per page, maximum per page is 300 .PARAMETER orderBy How the results should be sorted .PARAMETER orgUnitPath Restrict to a specific organization unit .PARAMETER projection If we want basic or full data. Default is full. .PARAMETER query The query to use against the data https://developers.google.com/admin-sdk/directory/v1/list-query-operators .PARAMETER sortOrder Ascending or Descending sort order .PARAMETER includeChildOrgunits If we should include child organization in use with orgUnitPath .PARAMETER all If we should return all results and not just a single page #> function Get-ChromeDevices{ [CmdletBinding()] [OutputType([System.Collections.Generic.List[PSCustomObject]])] param( [Parameter()][int]$maxResults, [Parameter()][ValidateSet('ANNOTATED_LOCATION','ANNOTATED_USER','LAST_SYNC','NOTES','SERIAL_NUMBER','STATUS')][string]$orderBy, [Parameter()][string]$orgUnitPath, [Parameter()][ValidateSet('BASIC','FULL')][string]$projection, [Parameter()][string]$query, [Parameter()][ValidateSet('ASCENDING','DESCENDING')][string]$sortOrder, [Parameter()][switch]$includeChildOrgunits, [Parameter()][switch]$all ) # Ensure that they have a access token if(-not $global:googleAccessToken){ throw "Please ensure that you have called Get-GoogleAccessToken cmdlet" } # Confirm we have a valid access token if(-not $(Test-GoogleAccessToken)){ Get-GoogleAccessToken -json $global:googleJSON -customerId $global:googleCustomerId } # Generate URI for REST API call $uri = "https://admin.googleapis.com/admin/directory/v1/customer/$($global:googleCustomerId)/devices/chromeos" $options = [System.Collections.Generic.List[String]]@() if($maxResults){ $options.Add("maxResults=$($maxResults)") | Out-Null } if($orderBy){ $options.Add("orderBy=$($orderBy)") | Out-Null } if($orgUnitPath){ $options.Add("orgUnitPath=$($orgUnitPath)") | Out-Null } if($projection){ $options.Add("projection=$($projection)") | Out-Null } if($sortOrder){ $options.Add("sortOrder=$($sortOrder)") | Out-Null } if($includeChildOrgunits){ $options.Add("includeChildOrgunits=$($includeChildOrgunits)") | Out-Null } if($query){ $options.Add("query=$($query)") | Out-Null } if($options.count -gt 0){ $uri = "$($uri)?$($options -join "&")" } # REST API Call to Get Orginzation Lists $splat = @{ Method = "GET" Headers = @{authorization = "Bearer $($Global:googleAccessToken)"} } $baseURI = $uri $deviceList = [System.Collections.Generic.List[PSCustomObject]]@() do{ Write-Verbose "$($baseURI)" Write-Verbose "Count: $($deviceList.count)" $result = Invoke-RestMethod @splat -uri $baseURI foreach($item in $result.chromeosdevices){ $deviceList.add($item) | Out-Null } if($options.count -eq 0){$baseURI = "$($uri)?"} else{$baseURI = "$($uri)&"} $baseURI = "$($baseURI)pageToken=$($result.nextPageToken)" }while($null -ne $result.nextPageToken -and $all) return $deviceList } #EndRegion '.\Public\Get-ChromeDevices.ps1' 93 #Region '.\Public\Get-DSREGCMDStatus.ps1' -1 <# .DESCRIPTION This is designed to parse the dsregcmd command to usable data. Originally from https://github.com/AdamGrossTX/ManagedUserManagement/blob/main/ClientScripts/Set-AutoLogon.ps1 by Adam Gross #> function Get-DSREGCMDStatus { [cmdletbinding()] param( [parameter(HelpMessage = "Use to add /DEBUG to DSREGCMD")][switch]$bDebug ) try { Write-Output "Calling DSREGCMDSTATUS" $cmdArgs = if ($bDebug) { "/STATUS", "/DEBUG" } else { "/STATUS" } $DSREGCMDStatus = & DSREGCMD $cmdArgs $DSREGCMDEntries = [PSCustomObject]@{} if ($DSREGCMDStatus) { for ($i = 0; $i -le $DSREGCMDStatus.Count ; $i++) { if ($DSREGCMDStatus[$i] -like "| *") { $GroupName = $DSREGCMDStatus[$i].Replace("|", "").Trim().Replace(" ", "") $Member = @{ MemberType = "NoteProperty" Name = $GroupName Value = $null } $DSREGCMDEntries | Add-Member @Member $i++ #Increment to skip next line with +---- $GroupEntries = [PSCustomObject]@{} do { $i++ if ($DSREGCMDStatus[$i] -like "*::*") { $DiagnosticEntries = $DSREGCMDStatus[$i] -split "(^DsrCmd.+(?=DsrCmd)|DsrCmd.+(?=\n))" | Where-Object { $_ -ne '' } foreach ($Entry in $DiagnosticEntries) { $EntryParts = $Entry -split "(^.+?::.+?: )" | Where-Object { $_ -ne '' } $EntryParts[0] = $EntryParts[0].Replace("::", "").Replace(": ", "") if ($EntryParts) { $Member = @{ MemberType = "NoteProperty" Name = $EntryParts[0].Trim().Replace(" ", "") Value = $EntryParts[1].Trim() } $GroupEntries | Add-Member @Member $Member = $null } } } elseif ($DSREGCMDStatus[$i] -like "* : *") { $EntryParts = $DSREGCMDStatus[$i] -split ':' if ($EntryParts) { $Member = @{ MemberType = "NoteProperty" Name = $EntryParts[0].Trim().Replace(" ", "") Value = if ($EntryParts.Count -gt 2) { ( $EntryParts[1..(($EntryParts.Count) - 1)] -join ":").Split("--").Replace("[ ", "").Replace(" ]", "").Trim() } else { $EntryParts[1].Trim() } } $GroupEntries | Add-Member @Member $Member = $null } } } until($DSREGCMDStatus[$i] -like "+-*" -or $i -eq $DSREGCMDStatus.Count) $DSREGCMDEntries.$GroupName = $GroupEntries } } return $DSREGCMDEntries } else { return "No Status Found" } } catch { throw $_ } } #EndRegion '.\Public\Get-DSREGCMDStatus.ps1' 77 #Region '.\Public\Get-EntraDeviceCertificate.ps1' -1 <# .DESCRIPTION This is designed to get the device certificate for Entra that is enrolled. Originally from https://github.com/AdamGrossTX/ManagedUserManagement/blob/main/ClientScripts/Set-AutoLogon.ps1 by Adam Gross #> function Get-EntraDeviceCertificate { [CmdletBinding()] [OutputType([X509Certificate])] param ( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][Object]$dsregcmdStatus ) try { Write-Host "Getting Azure AD Device Certificate" #Get best cert from DSRegCmd $Thumbprint = $dsregcmdstatus.DeviceDetails.Thumbprint #Get the local cert that matches the DSRegCMD Cert $Certs = Get-ChildItem -Path Cert:\LocalMachine\My $Cert = $Certs | Where-Object { $_.Thumbprint -eq $dsregcmdstatus.DeviceDetails.Thumbprint } if ($Cert.Thumbprint -eq $Thumbprint) { return $Cert } else { Write-Output "No valid Entra Device Cert Found." } } catch { throw $_ } } #EndRegion '.\Public\Get-EntraDeviceCertificate.ps1' 29 #Region '.\Public\Get-EntraIDDeviceID.ps1' -1 <# .DESCRIPTION This is designed to get the entra id device id. Originally from https://azuretothemax.net/log-analytics-index/ #> function Get-EntraIDDeviceID { [CmdletBinding()] param() # Define Cloud Domain Join information registry path $EntraIDJoinInfoRegistryKeyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\CloudDomainJoin\JoinInfo" # Retrieve the child key name that is the thumbprint of the machine certificate containing the device identifier guid $EntraIDJoinInfoThumbprint = Get-ChildItem -Path $EntraIDJoinInfoRegistryKeyPath | Select-Object -ExpandProperty "PSChildName" if ($EntraIDJoinInfoThumbprint -ne $null) { # Retrieve the machine certificate based on thumbprint from registry key $EntraIDJoinCertificate = Get-ChildItem -Path "Cert:\LocalMachine\My" -Recurse | Where-Object { $PSItem.Thumbprint -eq $EntraIDJoinInfoThumbprint } if ($EntraIDJoinCertificate -ne $null) { # Determine the device identifier from the subject name $EntraIDDeviceID = ($EntraIDJoinCertificate | Select-Object -ExpandProperty "Subject") -replace "CN=", "" # Convert upper to lowercase. $EntraIDDeviceID = "$($EntraIDDeviceID)".ToLower() # Handle return value return $EntraIDDeviceID } else { #If no certificate was found, locate it by Common Name instead of Thumbprint. This is likely a CPC or similar. $EntraIDJoinCertificate = Get-ChildItem -Path "Cert:\LocalMachine\My" -Recurse | Where-Object { $PSItem.Subject -like "CN=($EntraIDJoinInfoThumbprint)" } if ($EntraIDJoinCertificate -ne $null) { # Cert is now found, extract Device ID from Common Name $EntraIDDeviceID = ($EntraIDJoinCertificate | Select-Object -ExpandProperty "Subject") -replace "CN=", "" # Convert upper to lowercase. $EntraIDDeviceID = "$($EntraIDDeviceID)".ToLower() # Handle return value return $EntraIDDeviceID } else { # Last ditch effort, try and use the ThumbPrint (reg key) itself. $EntraIDDeviceID = $EntraIDJoinInfoThumbprint # Convert upper to lowercase. $EntraIDDeviceID = "$($EntraIDDeviceID)".ToLower() return $EntraIDDeviceID } } } } #EndRegion '.\Public\Get-EntraIDDeviceID.ps1' 45 #Region '.\Public\Get-EntraIDJoinDate.ps1' -1 <# .DESCRIPTION This is designed to get the entra id join date. Originally from https://azuretothemax.net/log-analytics-index/ #> function Get-EntraIDJoinDate { [CmdletBinding()] param() # Define Cloud Domain Join information registry path $EntraIDJoinInfoRegistryKeyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\CloudDomainJoin\JoinInfo" # Retrieve the child key name that is the thumbprint of the machine certificate containing the device identifier guid $EntraIDJoinInfoThumbprint = Get-ChildItem -Path $EntraIDJoinInfoRegistryKeyPath | Select-Object -ExpandProperty "PSChildName" if ($EntraIDJoinInfoThumbprint -ne $null) { # Retrieve the machine certificate based on thumbprint from registry key $EntraIDJoinCertificate = Get-ChildItem -Path "Cert:\LocalMachine\My" -Recurse | Where-Object { $PSItem.Thumbprint -eq $EntraIDJoinInfoThumbprint } if ($EntraIDJoinCertificate -ne $null) { # Determine the device identifier from the subject name $EntraIDJoinDate = ($EntraIDJoinCertificate | Select-Object -ExpandProperty "NotBefore") # Handle return value return $EntraIDJoinDate } if ($EntraIDJoinCertificate -eq $null) { $EntraIDJoinCertificate = Get-ChildItem -Path "Cert:\LocalMachine\My" -Recurse | Where-Object { $PSItem.Subject -eq "CN=$($EntraIDJoinInfoThumbprint)" } $EntraIDJoinDate = ($EntraIDJoinCertificate | Select-Object -ExpandProperty "NotBefore") return $EntraIDJoinDate } } } #EndRegion '.\Public\Get-EntraIDJoinDate.ps1' 28 #Region '.\Public\Get-EntraIDRegistrationCertificateThumbprint.ps1' -1 <# .SYNOPSIS Get the thumbprint of the certificate used for Azure AD device registration. .DESCRIPTION Get the thumbprint of the certificate used for Azure AD device registration. .NOTES Author: Nickolaj Andersen Contact: @NickolajA Created: 2021-06-03 Updated: 2021-06-03 Version history: 1.0.0 - (2021-06-03) Function created 1.0.1 - (2023-05-10) Max Updated for Cloud PCs which don't have their thumbprint as their JoinInfo key name. #> function Get-EntraIDRegistrationCertificateThumbprint { [CmdletBinding()] param() # Define Cloud Domain Join information registry path $EntraIDJoinInfoRegistryKeyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\CloudDomainJoin\JoinInfo" # Retrieve the child key name that is the thumbprint of the machine certificate containing the device identifier guid $EntraIDJoinInfoThumbprint = Get-ChildItem -Path $EntraIDJoinInfoRegistryKeyPath | Select-Object -ExpandProperty "PSChildName" # Check for a cert matching that thumbprint $EntraIDJoinCertificate = Get-ChildItem -Path "Cert:\LocalMachine\My" -Recurse | Where-Object { $PSItem.Thumbprint -eq $EntraIDJoinInfoThumbprint } if ($EntraIDJoinCertificate -ne $null) { # if a matching cert was found tied to that reg key (thumbprint) value, then that is the thumbprint and it can be returned. $EntraIDThumbprint = $EntraIDJoinInfoThumbprint # Handle return value return $EntraIDThumbprint } else { # If a cert was not found, that reg key was not the thumbprint but can be used to locate the cert as it is likely the Azure ID which is in the certs common name. $EntraIDJoinCertificate = Get-ChildItem -Path "Cert:\LocalMachine\My" -Recurse | Where-Object { $PSItem.Subject -like "CN=$($EntraIDJoinInfoThumbprint)" } #Pull thumbprint from cert $EntraIDThumbprint = $EntraIDJoinCertificate.Thumbprint # Handle return value return $EntraIDThumbprint } } #EndRegion '.\Public\Get-EntraIDRegistrationCertificateThumbprint.ps1' 42 #Region '.\Public\Get-EntraIDTenantID.ps1' -1 <# .DESCRIPTION This is designed to get the entra tenant id that device belogs too. Originally from https://azuretothemax.net/log-analytics-index/ #> function Get-EntraIDTenantID{ [CmdletBinding()] param() # Cloud Join information registry path $EntraIDTenantInfoRegistryKeyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\CloudDomainJoin\TenantInfo" # Retrieve the child key name that is the tenant id for EntraID $EntraIDTenantID = Get-ChildItem -Path $EntraIDTenantInfoRegistryKeyPath | Select-Object -ExpandProperty "PSChildName" return $EntraIDTenantID } #EndRegion '.\Public\Get-EntraIDTenantID.ps1' 14 #Region '.\Public\Get-GoogleAccessToken.ps1' -1 function Get-GoogleAccessToken{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][PSCustomObject]$json, [Parameter(Mandatory = $true)][string]$customerId ) if($Global:googleAccessToken){ if(Test-GoogleAccessToken){return} } # Convert JSOn to object $json = $json | ConvertFrom-Json # COvert Private Key to Byte Stream $rsaPrivateKey = [System.Text.Encoding]::UTF8.GetBytes($json.private_key) # List of Scopes for the token $scopes = @( "https://www.googleapis.com/auth/admin.directory.device.chromeos", "https://www.googleapis.com/auth/admin.directory.group.member", "https://www.googleapis.com/auth/admin.directory.orgunit", "https://www.googleapis.com/auth/admin.directory.user", "https://www.googleapis.com/auth/admin.directory.user.security", "https://www.googleapis.com/auth/admin.directory.rolemanagement", "https://www.googleapis.com/auth/admin.directory.userschema", "https://www.googleapis.com/auth/admin.directory.customer", "https://www.googleapis.com/auth/admin.directory.domain", "https://www.googleapis.com/auth/admin.directory.resource.calendar", "https://mail.google.com/", "http://sites.google.com/feeds", "https://www.googleapis.com/auth/apps.alerts", "https://www.googleapis.com/auth/calendar", "https://www.googleapis.com/auth/classroom.announcements", "https://www.googleapis.com/auth/classroom.coursework.students", "https://www.googleapis.com/auth/classroom.courseworkmaterials", "https://www.googleapis.com/auth/classroom.profile.emails", "https://www.googleapis.com/auth/classroom.rosters", "https://www.googleapis.com/auth/classroom.topics", "https://www.googleapis.com/auth/cloud-identity", "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/contacts", "https://www.googleapis.com/auth/datastudio", "https://www.googleapis.com/auth/documents", "https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/drive.activity", "https://www.googleapis.com/auth/drive.admin.labels", "https://www.googleapis.com/auth/drive.labels", "https://www.googleapis.com/auth/gmail.modify", "https://www.googleapis.com/auth/gmail.settings.basic", "https://www.googleapis.com/auth/gmail.settings.sharing", "https://www.googleapis.com/auth/keep", "https://www.googleapis.com/auth/spreadsheets", "https://www.googleapis.com/auth/tasks", "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/admin.directory.device.mobile" ) # Get Current Time $now = (Get-Date).ToUniversalTime() # Expiry Time $expiry = (Get-Date).ToUniversalTime().AddHours(1) # Convert to Format for JWT $createDate = [Math]::Floor([decimal](Get-Date($now) -UFormat "%s")) $expiryDate = [Math]::Floor([decimal](Get-Date($expiry) -UFormat "%s")) # Create JWT Payload $jwtPayload = @{ sub = $json.client_email scope = $($scopes -join " ") aud = "https://oauth2.googleapis.com/token" iat = $createDate } $jwt = New-JWT -Algorithm 'RS256' -Issuer $json.client_email -SecretKey $rsaPrivateKey -ExpiryTimestamp $expiryDate -PayloadClaims $jwtPayload # Request Google API Token $tokenVars = @{ Method = "POST" Uri = "https://oauth2.googleapis.com/token" ContentType = "application/x-www-form-urlencoded" Body = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=$jwt" } $token = Invoke-WebRequest @tokenVars $Global:googleAccessToken = ($token.content | ConvertFrom-JSON).access_token $global:googleJSON = $json $global:googleCustomerId = $customerId } #EndRegion '.\Public\Get-GoogleAccessToken.ps1' 82 #Region '.\Public\Get-GoogleOU.ps1' -1 <# .DESCRIPTION This cmdlet will use GAM installed on your system to query Google for the list of your OUs. https://developers.google.com/admin-sdk/directory/reference/rest/v1/orgunits/list .PARAMETER Type The type of organization units to return. .PARAMETER orgUnitPath The starting point for the OU table #> function Get-GoogleOU{ [CmdletBinding()] [OutputType([System.Collections.Generic.List[PSCustomObject]])] param( [Parameter()][ValidateSet('All','Children','All_Including_Parent')][string]$Type, [Parameter()][string]$orgUnitPath ) # Ensure that they have a access token if(-not $global:googleAccessToken){ throw "Please ensure that you have called Get-GoogleAccessToken cmdlet" } # Confirm we have a valid access token if(-not $(Test-GoogleAccessToken)){ Get-GoogleAccessToken -json $global:googleJSON -customerId $global:googleCustomerId } # Generate URI for REST API call $uri = "https://admin.googleapis.com/admin/directory/v1/customer/$($global:googleCustomerId)/orgunits" $options = [System.Collections.Generic.List[String]]@() if($Type){ $options.Add("type=$($type)") | Out-Null } if($orgUnitPath){ $options.Add("orgUnitPath=$($orgUnitPath)") | Out-Null } if($options.count -gt 0){ $uri = "$($uri)?$($options -join "&")" } # REST API Call to Get Orginzation Lists $splat = @{ Method = "GET" Uri = $uri Headers = @{authorization = "Bearer $($Global:googleAccessToken)"} } $orglist = Invoke-RestMethod @splat # Return the list return $orglist.organizationUnits } #EndRegion '.\Public\Get-GoogleOU.ps1' 47 #Region '.\Public\Get-GraphAccessPackageAssignments.ps1' -1 function Get-GraphAccessPackageAssignments{ [cmdletBinding()] param( [Parameter()][ValidateNotNullOrEmpty()][string]$id, [Parameter()][ValidateNotNullOrEmpty()][string]$displayName, [Parameter()][ValidateNotNullOrEmpty()][string]$accessPackageId, [Parameter()][ValidateNotNullOrEmpty()][string]$groupid, [Parameter()][ValidateNotNullOrEmpty()][string]$groupname ) $endpoint = "identityGovernance/entitlementManagement/accessPackageAssignmentPolicies" if($id){ $endpoint = "$($endpoint)?`$filter=id eq '$id'" } elseif($displayName){ $endpoint = "$($endpoint)?`$filter=displayName eq '$displayName'" } elseif($accessPackageId){ $endpoint = "$($endpoint)?`$filter=accessPackageId eq '$accessPackageId'" } # Create empty list $List = [System.Collections.Generic.List[PSCustomObject]]@() # Get Graph Headers for Call $headers = Get-GraphHeader try { $uri = "https://graph.microsoft.com/beta/$($endpoint)" do { $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode if ($results.value) { foreach ($item in $results.value) { if($groupid -or $groupName){ $skipItem = $true if($item.requestorSettings.allowedRequestors.id -contains $groupID){ $skipItem = $false } if($item.requestorSettings.allowedRequestors.description -contains $groupname){ $skipItem = $false } if($item.requestApprovalSettings.approvalStages.primaryApprovers.id -contains $groupID){ $skipItem = $false } if($item.requestApprovalSettings.approvalStages.primaryApprovers.description -contains $groupname){ $skipItem = $false } } if($skipItem){continue} $List.add($item) } } $uri = $results."@odata.nextLink" }while ($null -ne $results."@odata.nextLink") } catch { throw "Unable to get Access Packages. $($_.Exception.Message)" } return $List } #EndRegion '.\Public\Get-GraphAccessPackageAssignments.ps1' 57 #Region '.\Public\Get-GraphAccessPackageCatalog.ps1' -1 function Get-GraphAccessPackageCatalog{ [cmdletBinding()] param( [Parameter()][ValidateNotNullOrEmpty()][string]$id, [Parameter()][ValidateNotNullOrEmpty()][string]$displayName ) $endpoint = "identityGovernance/entitlementManagement/accessPackageCatalogs" if($id){ $endpoint = "$($endpoint)?`$filter=id eq '$id'" } elseif($displayName){ $endpoint = "$($endpoint)?`$filter=displayName eq '$displayName'" } # Create empty list $List = [System.Collections.Generic.List[PSCustomObject]]@() # Get Graph Headers for Call $headers = Get-GraphHeader try { $uri = "https://graph.microsoft.com/beta/$($endpoint)" do { $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode if ($results.value) { foreach ($item in $results.value) { $List.add($item) } } $uri = $results."@odata.nextLink" }while ($null -ne $results."@odata.nextLink") } catch { throw "Unable to get Access Packages. $($_.Exception.Message)" } return $List } #EndRegion '.\Public\Get-GraphAccessPackageCatalog.ps1' 35 #Region '.\Public\Get-GraphAccessPackages.ps1' -1 function Get-GraphAccessPackages { [cmdletBinding()] param( [Parameter()][ValidateNotNullOrEmpty()][string]$displayName, [Parameter()][ValidateNotNullOrEmpty()][string]$id ) if (!$(Test-GraphAcessToken $global:graphAccessToken)) { throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # URI Endpoint $endpoint = "identityGovernance/entitlementManagement/accessPackages" if ($id) { $endpoint = "$($endpoint)?`$filter=id eq '$($id)'" } elseif ($displayName) { $endpoint = "$($endpoint)?`$filter=displayName eq '$($displayName)'" } # Create empty list $List = [System.Collections.Generic.List[PSCustomObject]]@() # Get Graph Headers for Call $headers = Get-GraphHeader try { $uri = "https://graph.microsoft.com/beta/$($endpoint)" do { $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode if ($results.value) { foreach ($item in $results.value) { $List.add($item) } } $uri = $results."@odata.nextLink" }while ($null -ne $results."@odata.nextLink") } catch { throw "Unable to get Access Packages. $($_.Exception.Message)" } return $List } #EndRegion '.\Public\Get-GraphAccessPackages.ps1' 39 #Region '.\Public\Get-GraphAccessToken.ps1' -1 <# .DESCRIPTION This cmdlet is designed to use MSAL.PS to get an access token through an app registration, and then store in a global access token variable .PARAMETER clientID The client ID for the app registration used .PARAMETER clientSecret The secret used for the app registration .PARAMETER tenantID The tenant id for the app registration #> function Get-GraphAccessToken{ [CmdletBinding()] param( [Parameter()][ValidateNotNullOrEmpty()][string]$clientID, [Parameter()][ValidateNotNullOrEmpty()][string]$tenantID, [Parameter()][ValidateNotNullOrEmpty()][string]$clientSecret, [Parameter()][switch]$interactive, [Parameter()][ValidateNotNullOrEmpty()][PSCustomObject]$azToken ) if($(Test-GraphAcessToken $global:graphAccessToken)){ return $global:graphAccessToken } Add-Type -Path "$($PSScriptRoot)\Microsoft.Identity.Client.dll" -ErrorAction SilentlyContinue | Out-Null [string[]]$scopes = @("https://graph.microsoft.com/.default") try{ if($interactive.IsPresent){ $clientApp = [Microsoft.Identity.Client.PublicClientApplicationBuilder]::Create($clientId).WithAuthority("https://login.microsoftonline.com/$tenantId").WithDefaultRedirectUri().Build() $authenticationResult = $clientApp.AcquireTokenInteractive($scopes).ExecuteAsync().GetAwaiter().GetResult() } elseif($clientSecret){ $clientApp = [Microsoft.Identity.Client.ConfidentialClientApplicationBuilder]::Create($clientId).WithClientSecret($clientSecret).WithAuthority("https://login.microsoftonline.com/$tenantId").Build() $authenticationResult = $clientApp.AcquireTokenForClient($scopes).ExecuteAsync().GetAwaiter().GetResult() } elseif($azToken){ $authenticationResult = $azToken.PSObject.Copy() $authenticationResult | Add-Member -MemberType AliasProperty -Name "AccessToken" -Value "Token" -Force } $global:graphAccessToken = $authenticationResult return $global:graphAccessToken } catch{ throw "Unable to generate access token. Error message: $($_)" } } #EndRegion '.\Public\Get-GraphAccessToken.ps1' 47 #Region '.\Public\Get-GraphAutopilotInformation.ps1' -1 function Get-GraphAutopilotInformation { [CmdletBinding()] param( [parameter()][ValidateNotNullOrEmpty()][string]$SerialNumber, [parameter()][ValidateNotNullOrEmpty()][string]$groupTag, [parameter()][ValidateNotNullOrEmpty()][string]$manufacturer, [parameter()][ValidateNotNullOrEmpty()][string]$model, [parameter()][ValidateNotNullOrEmpty()][string]$azureAdDeviceId ) if (!$(Test-GraphAcessToken $global:graphAccessToken)) { throw "Please Call Get-GraphAccessToken before calling this cmdlet" } $endpoint = "deviceManagement/windowsAutopilotDeviceIdentities" # Create empty list $filters = [System.Collections.Generic.List[PSCustomObject]]@() if($groupTag){ $filters.Add("contains(groupTag,'$($groupTag)')") | Out-Null } if($SerialNumber){ $filters.Add("contains(SerialNumber,'$($SerialNumber)')") | Out-Null } if($manufacturer){ $filters.Add("contains(manufacturer,'$($manufacturer)')") | Out-Null } if($model){ $filters.Add("contains(model,'$($model)')") | Out-Null } if($userPrincipalName){ $filters.Add("contains(userPrincipalName,'$($userPrincipalName)')") | Out-Null } if($azureAdDeviceId){ $filters.Add("contains(azureAdDeviceId,'$($azureAdDeviceId)')") | Out-Null } # Create query string for the filter $filterList = $filters -join " and " if($filterList){ $endpoint = "$($endpoint)?`$filter=$($filterList)" } # Create empty list $deviceList = [System.Collections.Generic.List[PSCustomObject]]@() # Get Graph Headers for Call $headers = Get-GraphHeader # Get Autopilot Devices $uri = "https://graph.microsoft.com/beta/$($endpoint)" do { # Execute call against graph $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode foreach($item in $results.value){ $deviceList.Add($item) | Out-Null } # Set the URI to the nextlink if it exists $uri = $results."@odata.nextLink" }while ($null -ne $results."@odata.nextLink") return $deviceList } #EndRegion '.\Public\Get-GraphAutopilotInformation.ps1' 56 #Region '.\Public\Get-GraphDevice.ps1' -1 <# .DESCRIPTION This cmdlet is designed to get devices from Azure ID #> function Get-GraphDevice{ [CmdletBinding()] param( [Parameter()][ValidateNotNullOrEmpty()][string[]]$id, [Parameter()][ValidateNotNullOrEmpty()][string[]]$deviceId, [Parameter()][ValidateNotNullOrEmpty()][bool]$accountEnabled, [Parameter()][ValidateNotNullOrEmpty()][string]$displayName, [Parameter()][ValidateNotNullOrEmpty()][string]$fields ) if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # Create empty list $filters = [System.Collections.Generic.List[PSCustomObject]]@() if($displayName){ $filters.Add("displayName eq '$($displayName)'") | Out-Null } if($accountEnabled){ $filters.Add("accountEnabled eq '$($accountEnabled)'") | Out-Null } if($deviceId.count -eq 1){ $filters.Add("deviceId eq '$($deviceId)'") | Out-Null } $batch = $false # General endpoint $endpoint = "devices" if($id.count -eq 1){ $endpoint = "$($endpoint)/$($id)" } elseif($id.count -gt 1 -or $deviceId.count -gt 1){ $batch = $true } # Create query string for the filter $filterList = $filters -join " and " # Create empty list $deviceList = [System.Collections.Generic.List[PSCustomObject]]@() # Get Graph Headers for Call $headers = Get-GraphHeader # If ID and deviceID are not an array if(-not $batch){ # Setup endpoint based on if filter or fields are passed if($filterList){ $endpoint = "$($endpoint)?`$filter=$($filterList)" } if($filterList -and $fields){ $endpoint = "$($endpoint)&`$select=$($fields)" } elseif($fields){ $endpoint = "$($endpoint)?`$select=$($fields)" } # Try to call graph API and get results back try{ $uri = "https://graph.microsoft.com/beta/$($endpoint)" do{ $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode if($results.value){ foreach($item in $results.value){ $deviceList.Add($item) | Out-Null } } else{ $deviceList.Add($results) | Out-Null } $uri = $results."@odata.nextLink" }while($null -ne $results."@odata.nextLink") } catch{ throw "Unable to get devices. $($_.Exception.Message)" } return $deviceList } # If a batch job because id or device id is an array else{ $objid = 1 $batchObj = [System.Collections.Generic.List[PSCustomObject]]@() $batches = [System.Collections.Generic.List[PSCustomObject]]@() # if trying to return multiple ids if($id.count -gt 1){ foreach($device in $id){ if($objid -lt 21){ if($fields){ $uri = "$($endpoint)/$($device)?`$select=$($fields)" } else{ $uri = "$($endpoint)/$($device)" } $obj = [PSCustomObject]@{ "id" = $objid "method" = "GET" "url" = $uri } $batchObj.Add($obj) | Out-Null $objid++ } if($objId -eq 21){ $batches.Add($batchObj) | Out-Null $batchObj = $null $batchObj = [System.Collections.Generic.List[PSCustomObject]]@() $objid = 1 } } $batches.Add($batchObj) | Out-Null } # if trying to return multiple deviceids elseif($deviceId.Count -gt 1){ foreach($device in $deviceId){ if($objid -lt 21){ if($fields){ $uri = "$($endpoint)?`$filter=deviceId eq '$($device)'&`$select=$($fields)" } else{ $uri = "$($endpoint)?`$filter=deviceId eq '$($device)'" } $obj = [PSCustomObject]@{ "id" = $objid "method" = "GET" "url" = $uri } $batchObj.Add($obj) | Out-Null $objid++ } if($objId -eq 21){ $batches.Add($batchObj) | Out-Null $batchObj = $null $batchObj = [System.Collections.Generic.List[PSCustomObject]]@() $objid = 1 } } $batches.Add($batchObj) | Out-Null } for($x = 0; $x -lt $batches.count; $x++){ if($batches[$x].count -gt 0){ $json = [PSCustomObject]@{ "requests" = $batches[$x] } | ConvertTo-JSON $results = Invoke-RestMethod -Method "POST" -Uri "https://graph.microsoft.com/beta/`$batch" -Headers $headers -Body $json foreach($item in $results.responses.body){ if($item.value){ $deviceList.Add($item.value) | Out-Null } else{ $deviceList.Add($item) | Out-Null } } } } return $deviceList } } #EndRegion '.\Public\Get-GraphDevice.ps1' 155 #Region '.\Public\Get-GraphGroup.ps1' -1 <# .DESCRIPTION This cmdlet is designed to query graph for Entra ID groups .PARAMETER groupName If want to find based on group name .PARAMETER groupId If want to lookup by group id .PARAMETER All If want to return all groups #> function Get-GraphGroup{ [CmdletBinding()] param( [Parameter(Mandatory = $true,ParameterSetName = 'groupName')][ValidateNotNullOrEmpty()][string]$groupName, [Parameter(Mandatory = $true, ParameterSetName = 'groupId')][ValidateNotNullOrEmpty()][string]$groupId, [Parameter(Mandatory = $true, ParameterSetName = 'All')][switch]$All ) # Confirm we have a valid graph token if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # Create empty list $groupList = [System.Collections.Generic.List[PSCustomObject]]@() # Build the headers we will use to get groups $headers = Get-GraphHeader # Base URI for resource call $uri = "https://graph.microsoft.com/beta/groups" if($groupName){ # Filter based on group name is required $uri = "$($uri)?`$filter=displayName eq '$($groupName)'" } elseif($groupId){ # Filter based on group ID $uri = "$($uri)/$($groupId)" } try{ # Loop until nextlink is null do{ # Execute call against graph $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode # Add results to a list variable foreach($item in $results.value){ $groupList.Add($item) | Out-Null } # Set the URI to the nextlink if it exists $uri = $results."@odata.nextLink" }while($null -ne $results."@odata.nextLink") # If there is only one result, return that if($groupList.count -eq 0){ return $results } else{ # Return the group list if it exists return $groupList } } catch{ throw "Unable to get groups. $($_.Exception.Message)" } } #EndRegion '.\Public\Get-GraphGroup.ps1' 61 #Region '.\Public\Get-GraphGroupMembers.ps1' -1 #https://learn.microsoft.com/en-us/graph/api/group-list-members?view=graph-rest-beta&tabs=http function Get-GraphGroupMembers{ [CmdletBinding()] param( [Parameter(Mandatory = $true,ParameterSetName = 'groupName')][ValidateNotNullOrEmpty()][string]$groupName, [Parameter(Mandatory = $true, ParameterSetName = 'groupId')][ValidateNotNullOrEmpty()][string]$groupId, [Parameter()][switch]$Recurse ) # Confirm we have a valid graph token if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # Get the Group ID if Group Name was Sent if($groupName){ $group = Get-GraphGroup -groupName $groupName $groupId = $group.id } # Create empty list $groupMemberList = [System.Collections.Generic.List[PSCustomObject]]@() # Create List for recurse if needed $groupList = [System.Collections.Generic.List[PSCustomObject]]@() # Build the headers we will use to get groups $headers = Get-GraphHeader # Deterime if we just want members of transitive members if($Recurse){ $uri = "https://graph.microsoft.com/beta/groups/$($groupId)/transitiveMembers" } else{ $uri = "https://graph.microsoft.com/beta/groups/$($groupId)/members" } try{ # Loop until nextlink is null do{ # Execute call against graph $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode # Add results to a list variable foreach($item in $results.value){ $groupMemberList.Add($item) } # Set the URI to the nextlink if it exists $uri = $results."@odata.nextLink" }while($null -ne $results."@odata.nextLink") return $groupMemberList } catch{ throw "Unable to get group members. $($_.Exception.Message)" } } #EndRegion '.\Public\Get-GraphGroupMembers.ps1' 49 #Region '.\Public\Get-GraphHeader.ps1' -1 <# .DESCRIPTION This cmdlet is designed to format the graph header for the REST api calls .PARAMETER ConsistencyLevel This field will add the ConsistencyLevel variable to eventual #> function Get-GraphHeader{ [CmdletBinding()] param( [Parameter()][switch]$ConsistencyLevel ) if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # Base header variables $headerVars = @{ Authorization = "Bearer $($global:graphAccessToken.AccessToken)" "Content-Type" = "application/json" } # If flagged to include the Consitency Level header if($ConsistencyLevel.IsPresent){ $headerVars.Add("ConsistencyLevel","eventual") } return $headerVars } #EndRegion '.\Public\Get-GraphHeader.ps1' 26 #Region '.\Public\Get-GraphIntuneAPNCertificate.ps1' -1 function Get-GraphIntuneAPNCertificate{ [CmdletBinding()] param() if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # URI Endpoint $endpoint = "deviceManagement/applePushNotificationCertificate" # Get Graph Headers for Call $headers = Get-GraphHeader # Invoke Rest API $uri = "https://graph.microsoft.com/beta/$($endpoint)" $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode # return results return $results } #EndRegion '.\Public\Get-GraphIntuneAPNCertificate.ps1' 17 #Region '.\Public\Get-GraphIntuneApp.ps1' -1 function Get-GraphIntuneApp{ [CmdletBinding(DefaultParameterSetName = 'All')] param( [Parameter(Mandatory = $true,ParameterSetName = 'id')][ValidateNotNullOrEmpty()][string]$id, [Parameter(Mandatory = $true, ParameterSetName = 'displayName')][ValidateNotNullOrEmpty()][string]$displayName, [Parameter()][ValidateNotNullOrEmpty()][ValidateSet("microsoft.graph.androidManagedStoreApp","microsoft.graph.iosStoreApp","microsoft.graph.iosVppApp","microsoft.graph.macOSLobApp","microsoft.graph.macOSMicrosoftEdgeApp","microsoft.graph.macOSOfficeSuiteApp","microsoft.graph.macOSPkgApp","microsoft.graph.macOsVppApp","microsoft.graph.managedAndroidStoreApp","microsoft.graph.managedIOSStoreApp","microsoft.graph.officeSuiteApp","microsoft.graph.webApp","microsoft.graph.win32LobApp","microsoft.graph.winGetApp")] [string]$type, [Parameter()][ValidateNotNullOrEmpty()][string]$fields ) if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # URI Endpoint $endpoint = "deviceAppManagement/mobileApps" # Build filters for URI $filters = [System.Collections.Generic.List[PSCustomObject]]@() if($id){ $filters.Add("id eq '$($id)'") | Out-Null } if($displayName){ $filters.Add("displayName eq '$($displayName)'") | Out-Null } if($type){ $filters.Add("(isof('$($type)'))") | Out-Null } # Create query string for the filter $filterList = $filters -join " and " if($filterList){ $endpoint = "$($endpoint)?`$filter=$($filterList)" } if($filterList -and $fields){ $endpoint = "$($endpoint)&`$select=$($fields)" } elseif($fields){ $endpoint = "$($endpoint)?`$select=$($fields)" } # Create empty list $applicationList = [System.Collections.Generic.List[PSCustomObject]]@() # Get Graph Headers for Call $headers = Get-GraphHeader try{ $uri = "https://graph.microsoft.com/beta/$($endpoint)" do{ $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode if($results.value){ foreach($item in $results.value){ $applicationList.add($item) } } $uri = $results."@odata.nextLink" }while($null -ne $results."@odata.nextLink") } catch{ throw "Unable to get devices. $($_.Exception.Message)" } return $applicationList } #EndRegion '.\Public\Get-GraphIntuneApp.ps1' 60 #Region '.\Public\Get-GraphIntuneAppAssignment.ps1' -1 function Get-GraphIntuneAppAssignment{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$applicationid ) $endpoint = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$($applicationid)/assignments" $headers = Get-GraphHeader $results = Invoke-RestMethod -Method "GET" -Uri $endpoint -Headers $headers return $results.value } #EndRegion '.\Public\Get-GraphIntuneAppAssignment.ps1' 11 #Region '.\Public\Get-GraphIntuneDEPCertificate.ps1' -1 function Get-GraphIntuneDEPCertificate{ [CmdletBinding()] param() if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # URI Endpoint $endpoint = "deviceManagement/depOnboardingSettings" # Get Graph Headers for Call $headers = Get-GraphHeader # Invoke Rest API $uri = "https://graph.microsoft.com/beta/$($endpoint)" $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode # return results return $results.Value } #EndRegion '.\Public\Get-GraphIntuneDEPCertificate.ps1' 17 #Region '.\Public\Get-GraphIntuneEnrollmentStatusPage.ps1' -1 function Get-GraphIntuneEnrollmentStatusPage{ [CmdletBinding()] param( [Parameter()][ValidateNotNullOrEmpty()][string]$id, [Parameter()][ValidateNotNullOrEmpty()][string]$displayName ) if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # URI Endpoint $endpoint = "deviceManagement/deviceEnrollmentConfigurations" if($id){ $endpoint = "$($endpoint)/$($id)" } if($displayName){ $endpoint = "$($endpoint)?`$filter=displayName eq '$($displayName)'" } # Create empty list $esplist = [System.Collections.Generic.List[PSCustomObject]]@() # Get Graph Headers for Call $headers = Get-GraphHeader try{ $uri = "https://graph.microsoft.com/beta/$($endpoint)" do{ $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode if($results.value){ foreach($item in $results.value){ $esplist.add($item) } } else{ $esplist.add($results) } $uri = $results."@odata.nextLink" }while($null -ne $results."@odata.nextLink") } catch{ throw "Unable to get devices. $($_.Exception.Message)" } return $esplist } #EndRegion '.\Public\Get-GraphIntuneEnrollmentStatusPage.ps1' 42 #Region '.\Public\Get-GraphIntuneFilters.ps1' -1 function Get-GraphIntuneFilters{ [CmdletBinding()] param( [Parameter()][ValidateNotNullOrEmpty()][string]$id, [Parameter()][ValidateNotNullOrEmpty()][string]$displayName, [Parameter()][ValidateNotNullOrEmpty()][string]$platform ) # Construct array list to build the dynamic filter list $FilterList = [System.Collections.Generic.List[PSCustomObject]]@() if($id){ $FilterList.Add("`$PSItem.id -eq '$($id)'") | Out-Null } if($displayName){ $FilterList.Add("`$PSItem.displayName -eq '$($displayName)'") | Out-Null } if($platform){ $FilterList.Add("`$PSItem.platform -eq '$($platform)'") | Out-Null } # Construct script block from filter list array $FilterExpression = [scriptblock]::Create(($FilterList -join " -and ")) # Create empty list $filters = [System.Collections.Generic.List[PSCustomObject]]@() # Build the headers we will use to get groups $headers = Get-GraphHeader # Endpoint for the API $uri = "https://graph.microsoft.com/beta/deviceManagement/assignmentFilters" try{ # Loop until nextlink is null do{ # Execute call against graph $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode # Add results to a list variable foreach($item in $results.value){ $filters.Add($item) } # Set the URI to the nextlink if it exists $uri = $results."@odata.nextLink" }while($null -ne $results."@odata.nextLink") if($FilterList.Count -gt 0){ return $filters | Where-Object -FilterScript $FilterExpression } return $filters } catch{ throw "Unable to get group members. $($_.Exception.Message)" } } #EndRegion '.\Public\Get-GraphIntuneFilters.ps1' 48 #Region '.\Public\Get-GraphIntuneVPPCertificate.ps1' -1 function Get-GraphIntuneVPPCertificate{ [CmdletBinding()] param() if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # URI Endpoint $endpoint = "deviceAppManagement/vppTokens" # Get Graph Headers for Call $headers = Get-GraphHeader # Invoke Rest API $uri = "https://graph.microsoft.com/beta/$($endpoint)" $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode # return results return $results.Value } #EndRegion '.\Public\Get-GraphIntuneVPPCertificate.ps1' 17 #Region '.\Public\Get-GraphMail.ps1' -1 <# .DESCRIPTION This cmdlet is designed to read email from a specific mailbox .PARAMETER mailbox The email address of the account that we are reading from .PARAMETER folder The ID of the folder that we want to read from, if it is not the whole mailbox .PARAMETER unread If we want to return only unread email #> function Get-GraphMail{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$mailbox, [Parameter()][ValidateNotNullOrEmpty()][string]$folderid, [Parameter()][switch]$unread ) if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } $headers = Get-GraphHeader $uri = "https://graph.microsoft.com/beta/users/$($mailbox)" if ($folderid) { $uri = "$($uri)/mailFolders/$($folderid)" } $uri = "$($uri)/messages" $FilterList = [System.Collections.Generic.List[PSCustomObject]]@() if ($unread) { $FilterList.Add("isRead eq false") | Out-Null } if($FilterList -ne "") { $FilterExpression = [scriptblock]::Create(($FilterList -join " and ")) $uri = "$($uri)?`$filter=$($FilterExpression)" } try{ $emailList = [System.Collections.Generic.List[PSCustomObject]]@() do{ $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode foreach($item in $results.value){ $emailList.Add($item) | Out-Null } $uri = $results."@odata.nextLink" }while($null -ne $results."@odata.nextLink") # If there is only one result, return that if($emailList.count -eq 0){ return $results } else{ # Return the group list if it exists return $emailList } } catch{ throw "Unable to get emails from mailbox." } } #EndRegion '.\Public\Get-GraphMail.ps1' 58 #Region '.\Public\Get-GraphMailAttachment.ps1' -1 function Get-GraphMailAttachment{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$mailbox, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$messageid ) if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } $headers = Get-GraphHeader $uri = "https://graph.microsoft.com/beta/users/$($mailbox)/messages/$($messageid)/attachments" $results = Invoke-RestMethod -Method "GET" -headers $headers -StatusCodeVariable statuscode -uri $uri return $results.value } #EndRegion '.\Public\Get-GraphMailAttachment.ps1' 15 #Region '.\Public\Get-GraphMailAttachmentContent.ps1' -1 function Get-GraphMailAttachmentContent{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$mailbox, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$messageid, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$attachmentid, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$filename ) if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } $headers = Get-GraphHeader Invoke-RestMethod -method "GET" -uri "https://graph.microsoft.com/beta/users/$($mailbox)/messages/$($messageid)/attachments/$($attachmentid)/`$value" -Headers $headers -OutFile $filename } #EndRegion '.\Public\Get-GraphMailAttachmentContent.ps1' 15 #Region '.\Public\Get-GraphMailFolder.ps1' -1 function Get-GraphMailFolder{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$mailbox, [Parameter()][ValidateNotNullOrEmpty()][string]$foldername ) # Confirm we have a valid graph token if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # Get Graph Header $headers = Get-GraphHeader # Endpoint for the folders $uri = "https://graph.microsoft.com/beta/users/$($mailbox)/mailFolders" if($foldername){ $uri = "$($uri)?`$filter=displayName eq '$($foldername)'" } $results = Invoke-RestMethod -Method "GET" -Uri $uri -Headers $headers return $results.value } #EndRegion '.\Public\Get-GraphMailFolder.ps1' 21 #Region '.\Public\Get-GraphManagedDevice.ps1' -1 <# .DESCRIPTION This cmdlet is designed to get managed devices (intune) from the graph endpoints #> function Get-GraphManagedDevice{ [CmdletBinding()] param( [Parameter()][ValidateNotNullOrEmpty()][datetime]$lastSyncBefore, [Parameter()][ValidateNotNullOrEmpty()][datetime]$lastSyncAfter, [Parameter()][ValidateNotNullOrEmpty()][ValidateSet("Windows","Android","macOS","iOS")][string]$operatingSystem, [Parameter()][ValidateNotNullOrEmpty()][ValidateSet("compliant","noncompliant","unknown")][string]$complianceState, [Parameter()][ValidateNotNullOrEmpty()][string]$OSVersion, [Parameter()][ValidateNotNullOrEmpty()][string]$OSVersionStartsWith, [Parameter()][ValidateNotNullOrEmpty()][string]$id, [Parameter()][ValidateNotNullOrEmpty()][string]$azureADDeviceId, [Parameter()][ValidateNotNullOrEmpty()][string]$userPrincipalName, [Parameter()][ValidateNotNullOrEmpty()][string]$model, [Parameter()][ValidateNotNullOrEmpty()][string]$manufacturer, [Parameter()][ValidateNotNullOrEmpty()][string]$serialNumber, [Parameter()][ValidateNotNullOrEmpty()][ValidateSet("disabled","enabled")][string]$lostModeState, [Parameter()][ValidateNotNullOrEmpty()][string]$minimumOSVersion, [Parameter()][ValidateNotNullOrEmpty()][string]$maximumOSVersion, [Parameter()][ValidateNotNullOrEmpty()][bool]$isEncrypted, [Parameter()][ValidateNotNullOrEmpty()][string]$fields, [Parameter()][switch]$batch ) if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # Create Filters for the URI # Create empty list $filters = [System.Collections.Generic.List[PSCustomObject]]@() # Build Filters if($lastSyncBefore){ $filters.Add("lastSyncDateTime le $($lastSyncBefore.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))") | Out-Null } if($lastSyncAfter){ $filters.Add("lastSyncDateTime ge $($lastSyncAfter.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))") | Out-Null } if($operatingSystem){ $filters.Add("operatingSystem eq '$($operatingSystem)'") | Out-Null } if($complianceState){ $filters.Add("complianceState eq '$($complianceState)'") | Out-Null } if($OSVersion){ $filters.Add("OSVersion eq '$($OSVersion)'") | Out-Null } if($OSVersionStartsWith){ $filters.Add("startsWith(OSVersion,'$($OSVersionStartsWith)')") | Out-Null } if($azureADDeviceId){ $filters.Add("azureADDeviceId eq '$($azureADDeviceId)'") | Out-Null } if($userPrincipalName){ $filters.Add("userPrincipalName eq '$($userPrincipalName)'") | Out-Null } if($model){ $filters.Add("model eq '$($model)'") | Out-Null } if($manufacturer){ $filters.Add("manufacturer eq '$($manufacturer)'") | Out-Null } if($serialNumber){ $filters.Add("serialNumber eq '$($serialNumber)'") | Out-Null } # Create query string for the filter $filterList = $filters -join " and " # URI Endpoint $endpoint = "deviceManagement/managedDevices" if($id){ $uri = "$($endpoint)/$($id)" } else{ $uri = "$($endpoint)" } if($filterList){ $uri = "$($uri)?`$filter=$($filterList)" } if(!$batch){ if($fields -ne ""){ if($filters.count -gt 0){ $uri = "$($uri)&`$select=$($fields)" } else{ $uri = "$($uri)?`$select=$($fields)" } } } else{ if($fields -ne ""){ if($filters.count -gt 0){ $uri = "$($uri)&`$select=id" } else{ $uri = "$($uri)?`$select=id" } } } # Create empty list $deviceList = [System.Collections.Generic.List[PSCustomObject]]@() # Create empty list $idlist = [System.Collections.Generic.List[PSCustomObject]]@() # Get Graph Headers for Call $headers = Get-GraphHeader try{ $uri = "https://graph.microsoft.com/beta/$($uri)" do{ $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode if($results.value){ foreach($item in $results.value){ if(!$batch){$deviceList.Add($item) | Out-Null} else{$idlist.Add($item) | Out-Null} } } else{ if(!$batch){$deviceList.Add($item) | Out-Null} else{$idlist.Add($item) | Out-Nul} } $uri = $results."@odata.nextLink" }while($null -ne $results."@odata.nextLink") } catch{ throw "Unable to get devices. $($_.Exception.Message)" } if(!$batch) { return $deviceList } $objid = 1 $batchObj = [System.Collections.Generic.List[PSCustomObject]]@() $batches = [System.Collections.Generic.List[PSCustomObject]]@() foreach($device in $idlist){ if($objid -lt 21){ $url = "deviceManagement/managedDevices/$($device.id)" if($fields -ne ""){ $url = "$($url)?`$select=$($fields)" } $obj = [PSCustomObject]@{ "id" = $objid "method" = "GET" "url" = $url } $batchObj.Add($obj) | Out-Null $objid++ } if($objId -eq 21){ $batches.Add($batchObj) | Out-Null $batchObj = $null $batchObj = [System.Collections.Generic.List[PSCustomObject]]@() $objid = 1 } } $batches.Add($batchObj) | Out-Null for($x = 0; $x -lt $batches.count; $x++){ if($batches[$x].count -gt 0){ $json = [PSCustomObject]@{ "requests" = $batches[$x] } | ConvertTo-JSON $results = Invoke-RestMethod -Method "POST" -Uri "https://graph.microsoft.com/beta/`$batch" -Headers $headers -Body $json foreach($item in $results.responses.body){ $deviceList.Add($item) | Out-Null } } } return $deviceList } #EndRegion '.\Public\Get-GraphManagedDevice.ps1' 168 #Region '.\Public\Get-GraphSignInAuditLogs.ps1' -1 <# .DESCRIPTION This cmdlet is designed to query the sign in logs for the users in the entra id tenant .PARAMETER userDisplayName The list of user display names that we should be looking for .PARAMETER userPrincipalName The list of user principal names that we should be looking for .PARAMETER userId The lsit of user ids that we should be looking for .PARAMETER appDisplayName The name of the application that we attempted to sign into .PARAMETER ipAddress The list of ipaddresses that we should be looking for .PARAMETER afterDateTime Sign ins after this date #> function Get-GraphSignInAuditLogs{ [CmdletBinding()] param( [Parameter()][string[]]$userDisplayName, [Parameter()][string[]]$userPrincipalName, [Parameter()][string[]]$userId, [Parameter()][string[]]$appDisplayName, [Parameter()][string[]]$ipAddress, [Parameter()][datetime]$afterDateTime ) # Confirm we have a valid graph token if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # Create Filters for the URI # Create empty list $filters = [System.Collections.Generic.List[PSCustomObject]]@() # URI Endpoint $endpoint = "auditLogs/signIns" # Create empty list $signinList = [System.Collections.Generic.List[PSCustomObject]]@() # Build Filters # User Display Name Filter if($userDisplayName){ $list = [System.Collections.Generic.List[String]]@() foreach($item in $userDisplayName){ $list.Add("userDisplayName eq '$($item)'") | Out-Null } $filters.Add("($($list -join " or "))") | Out-Null } # User Principal Name Filter if($userPrincipalName){ $list = [System.Collections.Generic.List[String]]@() foreach($item in $userPrincipalName){ $list.Add("userPrincipalName eq '$($item)'") | Out-Null } $filters.Add("($($list -join " or "))") | Out-Null } # User ID Filter if($userId){ $list = [System.Collections.Generic.List[String]]@() foreach($item in $userId){ $list.Add("userId eq '$($item)'") | Out-Null } $filters.Add("($($list -join " or "))") | Out-Null } # App Display Name Filter if($appDisplayName){ $list = [System.Collections.Generic.List[String]]@() foreach($item in $appDisplayName){ $list.Add("appDisplayName eq '$($item)'") | Out-Null } $filters.Add("($($list -join " or "))") | Out-Null } # IP Address Filter if($ipAddress){ $list = [System.Collections.Generic.List[String]]@() foreach($item in $ipAddress){ $list.Add("ipAddress eq '$($item)'") | Out-Null } $filters.Add("($($list -join " or "))") | Out-Null } # Date Filter if($afterDateTime){ $filters.Add("createdDateTime ge $($afterDateTime.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))") | Out-Null } # Create query string for the filter $filterList = $filters -join " and " if($filterList){ $endpoint = "$($endpoint)?`$filter=$($filterList)" } # Get Graph Headers for Call $headers = Get-GraphHeader try{ $uri = "https://graph.microsoft.com/beta/$($endpoint)" do{ $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode if($results.value){ foreach($item in $results.value){ $signinList.Add($item) | Out-Null } } $uri = $results."@odata.nextLink" }while($null -ne $results."@odata.nextLink") return $signinList } catch{ throw "Unable to get devices. $($_.Exception.Message)" } } #EndRegion '.\Public\Get-GraphSignInAuditLogs.ps1' 107 #Region '.\Public\Get-GraphUser.ps1' -1 function Get-GraphUser{ [CmdletBinding(DefaultParameterSetName="All")] param( [Parameter(Mandatory = $true,ParameterSetName = 'userPrincipalName')][ValidateNotNullOrEmpty()][string]$userPrincipalName, [Parameter(Mandatory = $true, ParameterSetName = 'userid')][ValidateNotNullOrEmpty()][string]$userid, [Parameter()][switch]$All, [Parameter()][switch]$ConsistencyLevel, [Parameter()][switch]$Count, [Parameter()][ValidateNotNullOrEmpty()][string]$filter, [Parameter()][ValidateNotNullOrEmpty()][string]$select ) # Confirm we have a valid graph token if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # Create empty list $userList = [System.Collections.Generic.List[PSCustomObject]]@() # Build the headers we will use to get groups $ConsistencyLevelHeader = @{} if($ConsistencyLevel.IsPresent){ $ConsistencyLevelHeader.Add("ConsistencyLevel",$true) | Out-Null } $headers = Get-GraphHeader @ConsistencyLevelHeader # Base URI for resource call $uri = "https://graph.microsoft.com/beta/users" if($userPrincipalName){ # Filter based on group name is required $uri = "$($uri)/$($userPrincipalName)" } elseif($userid){ # Filter based on group ID $uri = "$($uri)/$($userid)" } if($filter){ $uri = "$($uri)?`$filter=$($filter)" if($select){ $uri = "$($uri)&`$select=$($select)" } } if($count.IsPresent){ $uri = "$($uri)&`$count=true" } try{ # Loop until nextlink is null do{ # Execute call against graph $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -StatusCodeVariable statusCode # Add results to a list variable foreach($item in $results.value){ $userList.Add($item) | Out-Null } # Set the URI to the nextlink if it exists $uri = $results."@odata.nextLink" }while($null -ne $results."@odata.nextLink") # If there is only one result, return that if($userList.count -eq 0){ return $results } else{ # Return the group list if it exists return $userList } } catch{ throw "Unable to get users. $($_.Exception.Message)" } } #https://graph.microsoft.com/beta/users?$filter=startsWith(jobTitle,'Occasional') #"16e6375b-4c11-4306-be0f-b6ad3984fe89" <# [Parameter()][switch]$signInActivity, [Parameter()][switch]$accountEnabled, [Parameter()][string]$assignedLicenses, [Parameter()][string]$displayName, [Parameter()][string]$employeeId, [Parameter()][string]$officeLocation, [Parameter()][string]$onPremisesExtensionAttributes, [Parameter()][string]$onPremisesSamAccountName, [Parameter()][string]$onPremisesUserPrincipalName, [Parameter()][string]$userType, [Parameter()][string]$department #> #EndRegion '.\Public\Get-GraphUser.ps1' 86 #Region '.\Public\Get-InstalledApplications.ps1' -1 <# .DESCRIPTION This is designed to get the get the list of applications on the system. Originally from https://azuretothemax.net/log-analytics-index/ #> function Get-InstalledApplications { [CmdletBinding()] param( [Parameter()][ValidateNotNullOrEmpty()][string]$UserSid ) New-PSDrive -PSProvider Registry -Name "HKU" -Root HKEY_USERS | Out-Null $regpath = @("HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*") $regpath += "HKU:\$UserSid\Software\Microsoft\Windows\CurrentVersion\Uninstall\*" if (-not ([IntPtr]::Size -eq 4)) { $regpath += "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" $regpath += "HKU:\$UserSid\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" } $propertyNames = 'DisplayName', 'DisplayVersion', 'Publisher', 'UninstallString', 'InstallDate' $Apps = Get-ItemProperty $regpath -Name $propertyNames -ErrorAction SilentlyContinue | . { process { if ($_.DisplayName) { $_ } } } | Select-Object DisplayName, DisplayVersion, Publisher, UninstallString, InstallDate, PSPath | Sort-Object DisplayName Remove-PSDrive -Name "HKU" | Out-Null Return $Apps } #EndRegion '.\Public\Get-InstalledApplications.ps1' 22 #Region '.\Public\Get-IntuneDeviceCertificate.ps1' -1 <# .DESCRIPTION This is designed to get the device certificate for Intune that is enrolled. Originally from https://github.com/AdamGrossTX/ManagedUserManagement/blob/main/ClientScripts/Set-AutoLogon.ps1 by Adam Gross #> function Get-IntuneDeviceCertificate { [CmdletBinding()] [OutputType([X509Certificate])] param ( ) try { $CertIssuer = "CN=Microsoft Intune MDM Device CA" $ProviderRegistryPath = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Enrollments" $ProviderPropertyName = "ProviderID" $ProviderPropertyValue = "MS DM Server" $ProviderGUID = (Get-ChildItem -Path Registry::$ProviderRegistryPath -Recurse | ForEach-Object { if ((Get-ItemProperty -Name $ProviderPropertyName -Path $_.PSPath -ErrorAction SilentlyContinue | Get-ItemPropertyValue -Name $ProviderPropertyName -ErrorAction SilentlyContinue) -match $ProviderPropertyValue) { $_ } }).PSChildName $DMClientPath = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Enrollments\$($ProviderGUID)\DMClient\MS DM Server" $IntuneDeviceId = (Get-ItemPropertyValue -Path Registry::$DMClientPath -Name "EntDMID") $Cert = (Get-ChildItem cert:\LocalMachine\my | where-object { $_.Issuer -in $CertIssuer -and $_.Subject -like "*$IntuneDeviceId*" }) if ($cert) { return $Cert } } catch { throw $_ } } #EndRegion '.\Public\Get-IntuneDeviceCertificate.ps1' 28 #Region '.\Public\Get-KeyVaultSecret.ps1' -1 <# .DESCRIPTION This cmdlet is used to connect to a keyvault as the machine identity of the Azure Machine it is running under. .PARAMETER vaultName The name of the vault that we want to check .PARAMETER secretName The name of the secret we want to recover .EXAMPLE Check a secret out of a specific vault Get-KeyVaultSecret -vaultName <VAULT> -secretName <SECRET> #> function Get-KeyVaultSecret{ [CmdletBinding()] [OutputType([System.String])] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$vaultName, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$secretName ) # The URI for the vault that we want to access $keyVaultURI = "https://$($vaultName).vault.azure.net/secrets/$($secretName)?api-version=2016-10-01" # Using the identity of the virtual machine account running the script $response = Invoke-RestMethod -Uri 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fvault.azure.net' -Method GET -Headers @{Metadata="true"} # What the vault token is $keyVaultToken = $response.access_token try{ # Get the relevant secret and return it $secret = Invoke-RestMethod -Uri $keyVaultURI -Method GET -Headers @{Authorization="Bearer $KeyVaultToken"} return $secret.Value | ConvertFrom-Json } # Error handling possible expected errors catch{ if(($Error[0] -match "The remote name could not be resolved")){ $message = "Error: Attempting to connect to Azure Key vault URI $($keyVaultURI)`n$($_)" } elseif(($Error[0] -match "Unauthorized")){ $message = "Error: No authorization to Azure Key Vault URI $($keyVaultURI)`n$($_)" } elseif(($Error[0] -match "SecretNotFound")){ $message = "Error: The secret $($secretName) is not found in Azure Key Vault URI $($keyVaultURI)`n$($_)" } else{ $message = "Error: Unknown error connection to Azure Key vault URI $($keyVaultURI)`n$($_)" } Write-EventLog -LogName "Application" -Source "PowerShell Universal Scripts" -EntryType "Warning" -EventId 1001 -Message $message return $message } } #EndRegion '.\Public\Get-KeyVaultSecret.ps1' 48 #Region '.\Public\Get-MerakiNetwork.ps1' -1 <# .DESCRIPTION This cmdlet is designed to convert get the meraki networks .PARAMETER type The type of network that we should filter to #> function Get-MerakiNetwork{ [CmdletBinding()] param( [Parameter()][ValidateSet("wireless","systemsmanager")][string]$type ) # Confirm we have a valid meraki header if(!$global:merakiHeader){ throw "Please Call Connect-Meraki before calling this cmdlet" } # Generate URI for call $uri = "$($global:merakiApiURI)/organizations/$($global:merakiOrgId)/networks" # Execute API Call $networks = Invoke-RestMethod -Method Get -Uri $uri -Headers $global:merakiHeader -StatusCodeVariable statusCode -FollowRelLink # Filter for productTypes $networkList = [System.Collections.Generic.List[PSCustomObject]]@() foreach($network in $networks){ foreach($item in $network){ if($type -and $type -notin $item.productTypes){continue} $networkList.Add($item) | Out-Null } } return $networkList } #EndRegion '.\Public\Get-MerakiNetwork.ps1' 30 #Region '.\Public\Get-MerakiSystemsManagerDevice.ps1' -1 <# .DESCRIPTION This cmdlet is designed to get system managed devices in a network .PARAMETER networkId The network that we want to get the devices from .PARAMETER fields Additional fields that we want to return #> function Get-MerakiSystemsManagerDevice{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$networkId, [Parameter()][ValidateNotNullOrEmpty()][string]$fields ) # Confirm we have a valid meraki header if(!$global:merakiHeader){ throw "Please Call Connect-Meraki before calling this cmdlet" } # Generate URI for call $uri = "$($global:merakiApiURI)/networks/$($networkId)/sm/devices" if($fields){ $uri = "$($uri)?fields[]=$($fields)" } # Execute API Call $devices = Invoke-RestMethod -Method Get -Uri $uri -Headers $global:merakiHeader -StatusCodeVariable statusCode -FollowRelLink # Filter for productTypes $deviceList = [System.Collections.Generic.List[PSCustomObject]]@() foreach($device in $devices){ foreach($item in $device){ $deviceList.Add($item) | Out-Null } } return $deviceList } #EndRegion '.\Public\Get-MerakiSystemsManagerDevice.ps1' 35 #Region '.\Public\Get-PublicKeyBytesEncodedString.ps1' -1 <# .SYNOPSIS Returns the public key byte array encoded as a Base64 string, of the certificate where the thumbprint passed as parameter input is a match. .DESCRIPTION Returns the public key byte array encoded as a Base64 string, of the certificate where the thumbprint passed as parameter input is a match. The certificate used must be available in the LocalMachine\My certificate store. .PARAMETER Thumbprint Specify the thumbprint of the certificate. .NOTES Author: Nickolaj Andersen / Thomas Kurth Contact: @NickolajA Created: 2021-06-07 Updated: 2023-05-10 Version history: 1.0.0 - (2021-06-07) Function created 1.0.1 - (2023-05-10) Max - Updated to use X509 for the full public key with extended properties in the PEM format Credits to Thomas Kurth for sharing his original C# code. #> function Get-PublicKeyBytesEncodedString { [CmdletBinding()] param( [parameter(Mandatory = $true, HelpMessage = "Specify the thumbprint of the certificate.")] [ValidateNotNullOrEmpty()] [string]$Thumbprint ) Process { # Determine the certificate based on thumbprint input $Certificate = Get-ChildItem -Path "Cert:\LocalMachine\My" -Recurse | Where-Object { $PSItem.Thumbprint -eq $Thumbprint } if ($Certificate -ne $null) { # Bring the cert into a X509 object $X509 = [System.Security.Cryptography.X509Certificates.X509Certificate2]::New($Certificate) #Set the type of export to perform $type = [System.Security.Cryptography.X509Certificates.X509ContentType]::Cert #Export the public cert $PublicKeyBytes = $X509.Export($type, "") # Handle return value - convert to Base64 return [System.Convert]::ToBase64String($PublicKeyBytes) } } } #EndRegion '.\Public\Get-PublicKeyBytesEncodedString.ps1' 47 #Region '.\Public\Get-ScriptVariables.ps1' -1 function Get-ScriptVariables{ [CmdLetBinding()] param( [Parameter(Mandatory = $true,ParameterSetName = 'JSON')][ValidateNotNullOrEmpty()][String]$JSON, [Parameter(Mandatory = $true,ParameterSetName = 'URI')][ValidateNotNullOrEmpty()][String]$URI, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][String]$Environment, [Parameter()][ValidateNotNullOrEmpty()][String]$Script ) # If path to JSON file is selected if($JSON){ $vars = Get-Content -Path $JSON | ConvertFrom-JSON -Depth 10 } # If a URI to a JSON is provided else{ $vars = (Invoke-WebRequest -Uri $URI -Method "GET" -UseBasicParsing).Content | ConvertFrom-JSON -Depth 10 } foreach ($var in $vars.PSObject.Properties) { if($var.Name -eq "Environment"){ foreach($item in $var.Value.PSObject.Properties){ if($item.Name -eq $Environment){ foreach($obj in $item.Value.PSObject.Properties){ Set-Variable -Name $obj.Name -Value $obj.Value -Scope Global } break } } } elseif($var.Name -eq "ScriptSpecific"){ foreach($item in $var.Value.PSObject.Properties){ if($item.Name -eq $Script){ foreach($obj in $item.Value.PSObject.Properties){ Set-Variable -Name $obj.Name -Value $obj.Value -Scope Global } break } } } else{ Set-Variable -Name $var.Name -Value $var.Value -Scope Global } } } #EndRegion '.\Public\Get-ScriptVariables.ps1' 43 #Region '.\Public\Get-Shortcut.ps1' -1 function Get-Shortcut{ [CmdletBinding()] param( [parameter()][ValidateNotNullOrEmpty()][string]$Name, [parameter()][string]$UserName = $null, [parameter()][string]$OneDriveOrgName = $null, [parameter()][switch]$StartMenu, [parameter()][string]$folder ) # Get a list of all the user profiles on the machine $ProfileList = Get-ChildItem Registry::"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" | Where-Object { $_.Name -notlike "*_Classes" -and $_.PSChildName -notin ("S-1-5-18", "S-1-5-19", "S-1-5-20") } $UserList = foreach ($UserKey in $ProfileList) { @{ ProfileKey = $UserKey | Where-Object { $_.name -like "*" + $UserKey.PSChildName + "*" } UserName = try { ((([system.security.principal.securityidentIfier]$UserKey.PSChildName).Translate([System.Security.Principal.NTAccount])).ToString()).substring(3) } catch { continue }; SID = $UserKey.PSChildName ProfilePath = Get-ItemProperty $UserKey.PSPath | Select-Object -ExpandProperty ProfileImagePath } } # Determine if we should be using paths from the user's profile or the public profile if($null -ne $Username -and $UserName -ne ""){ $baseprofile = ($UserList | Where-Object {$_.UserName -like "*$($UserName)*"}).ProfilePath if(-not $baseprofile){ throw "Unable to find profile for username: $($UserName)" } $desktopPath = Join-Path -Path $baseprofile -ChildPath "Desktop\$($folder)" $onedrivePath = Join-Path -Path $baseprofile -ChildPath "OneDrive - $($OneDriveOrgName)\Desktop\$($folder)" if($null -ne $OneDriveOrgName -and (Test-Path $onedrivePath)){ $desktopPath = $onedrivePath } $startMenuPath = Join-Path -Path $baseprofile -ChildPath "AppData\Roaming\Microsoft\Windows\Start Menu\Programs\$($folder)" } else{ $desktopPath = Join-Path -Path $ENV:PUBLIC -ChildPath "Desktop\$($folder)" $startMenuPath = Join-Path -path $ENV:ALLUSERSPROFILE -ChildPath "Microsoft\Windows\Start Menu\Programs\$($folder)" } # Set the path based on if we are doing start menu or desktop if($startMenu.IsPresent){ $path = $startMenuPath } else{ $path = $desktopPath } $shortcut = Join-Path -Path $path -ChildPath "$($Name).lnk" if(Test-Path $shortcut){ $obj = New-Object -ComObject WScript.Shell $link = $obj.CreateShortcut($shortcut) return $link } else{ throw "Shortcut not found: $($shortcut)" } } #EndRegion '.\Public\Get-Shortcut.ps1' 54 #Region '.\Public\Get-TopdeskAsset.ps1' -1 <# .DESCRIPTION This cmdlet is designed to query the assets in your enviroment .PARAMETER credential The credential to connect to the API endpoints .PARAMETER environment The topdesk enviroment URI. For examble https://test.topdesk.net then this value would be test .PARAMETER nameFragment Query a specific name fragment from the name field .PARAMETER searchTerm Add an additional search term to your filter .PARAMETER templateId Filter for specific template id for assets .PARAMETER templateName Filter for specific template name for assets .PARAMETER archived If you want to only include non-archived, or archived assets .PARAMETER includeArchived If you haven't specified archive behavior, default is false. This includes all archived as well .PARAMETER fields What fields you want to return by default .PARAMETER showAssignments If you want to show any asset assingments to locations .PARAMETER linkedTo .PARAMETER filter What to add a custom filter .RETURNS Returns a list of assets that fall into the parameters .EXAMPLE Return all assets Get-TopdeskAsset Return all assets for a specific asset template id Get-TopdeskAsset -templateid <TEMPLATEID> Return all assets for a specific asset template name Get-TopdeskAsset-templateName <TEMPLATENAME> Return assets with their assignments Get-TopdeskAsset -showAssignments $true Return assets starting with a specific name fragment Get-TopdeskAsset -nameFragment <FRAGEMENT> #> function Get-TopdeskAsset{ [CmdletBinding()] [OutputType([System.Collections.Generic.List[PSObject]])] param( [Parameter()][string]$nameFragment, [Parameter()][string]$searchTerm, [Parameter()][string]$templateId, [Parameter()][string]$templateName, [Parameter()][bool]$archived = $false, [Parameter()][bool]$includeArchived = $false, [Parameter()][string]$fields, [Parameter()][switch]$showAssignments, [Parameter()][string]$linkedTo, [Parameter()][string]$filter ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } # Base URI for retrieval $baseuri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/assetmgmt/assets?`$orderby=name asc&fields=text,archived,id,name,$($fields)" # Filter base on either template ID or template Name if($templateId){ $baseuri = "$($baseuri)&templateId=$($templateId)" } elseif($templateName){ $baseuri = "$($baseuri)&templateName=$($templateName)" } # Fragment of the name if($nameFragment){ $baseuri = "$($baseuri)&nameFragment=$($nameFragment)" } # Search term if($searchTerm){ $baseuri = "$($baseuri)&searchTerm=$($searchTerm)" } # If the devices are archived or not if($archived){ $baseuri = "$($baseuri)&archived='true'" } elseif($archived -eq $false -and -not $includeArchived){ $baseuri = "$($baseuri)&archived='false'" } # If we want to show the assignments for the asset if($showAssignments){ $baseuri = "$($baseuri)&showAssignments=$($showAssignments)" } # Custom filter if($filter){ $baseuri = "$($baseuri)&`$filter=$($filter)" } # If it is linked to specific id if($linkedTo){ $baseuri = "$($baseuri)&linkedTo=$($linkedTo)" } # Set the first URI to query $uri = $baseuri try{ # Empty List for Results $data = [System.Collections.Generic.List[PSObject]]@() do{ $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $global:topdeskAccessToken -StatusCodeVariable statusCode foreach($item in $results.dataSet){ $data.Add($item) | Out-Null } # Set next URI if we are going to need to loop again. $uri = "$($baseuri)&lastSeenName='$($data[$data.count - 1].Name)'&lastSeenOrderbyValue='$($data[$data.count - 1].Name)'" Write-Verbose "Next Query: $($uri)" Write-Verbose "Found $($data.count) assets." } while($statusCode -eq 206) } catch{ throw "Error Accessing API at $($uri). $($Error[0])" } # Return results return $data } #EndRegion '.\Public\Get-TopdeskAsset.ps1' 119 #Region '.\Public\Get-TopdeskAssetDropdown.ps1' -1 <# .DESCRIPTION This cmdlet is designed to get the values of a asset dropdown field .PARAMETER credential The credential to connect to the API endpoints .PARAMETER environment The topdesk enviroment URI. For examble https://test.topdesk.net then this value would be test .PARAMETER dropdownId The id of the dropdown field that trying to query .PARAMETER dropdownName The name of the dropdown field trying to query .PARAMETER includeArchived Should we include archived items .RETURNS Returns a list of dropdown values that fall into the parameters .EXAMPLE Return values for a specific dropdown by name Get-TopdeskAssetDropdown -dropdownName <DROPDOWNNAME> Return values for a specific dropdown by id Get-TopdeskAssetDropdown dropdownId <DROPDOWNNAME> #> function Get-TopdeskAssetDropdown{ [CmdletBinding()] param( [Parameter(Mandatory = $true,ParameterSetName = 'dropdownId')][string]$dropdownId, [Parameter(Mandatory = $true,ParameterSetName = 'dropdownName')][string]$dropdownName, [Parameter()][bool]$includeArchived = $false ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } # We require either the ID or the Name of the dropdown that we are looking for if($dropdownId){ $dropdown = $dropdownId } elseif($dropdownName){ $dropdown = $dropdownName } # Base URI $uri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/assetmgmt/dropdowns/$($dropdown)?field=name" # Include archived if requested if($includeArchived){ $uri = "$($uri)&includeArchived=$($includeArchived)" } try{ # Get data from the API $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $global:topdeskAccessToken -StatusCodeVariable statusCode } catch{ throw "Error Accessing API at $($uri). $($Error[0])" } # Return results return $results.results } #EndRegion '.\Public\Get-TopdeskAssetDropdown.ps1' 56 #Region '.\Public\Get-TopdeskAssetTemplates.ps1' -1 <# .DESCRIPTION This cmdlet will return a list of asset templates that are in your topdesk enviroment .PARAMETER credential The credential to connect to the API endpoints .PARAMETER environment The topdesk enviroment URI. For examble https://test.topdesk.net then this value would be test .PARAMETER searchTerm If you are looking for a specific template you can search for it. .RETURNS Returns a list of asset templates .EXAMPLE Return all asset templates Get-TopdeskAssetTemplates Return only asset templates that start with the word Apple Get-TopdeskAssetTemplates -searchTerm "Apple" #> function Get-TopdeskAssetTemplates{ [CmdletBinding()] param( [Parameter()][string]$searchTerm ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } # Base URI for the list of asset management templates $uri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/assetmgmt/templates" # If search term is passed, add that to URI to filter if($searchTerm){ $uri = "$($uri)?searchTerm=$($searchTerm)" } try{ # Retrieve data from API $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $global:topdeskAccessToken -StatusCodeVariable statusCode } catch{ throw "Error Accessing API at $($uri). $($Error[0])" } # Return the data retrieved return $results.dataSet } #EndRegion '.\Public\Get-TopdeskAssetTemplates.ps1' 43 #Region '.\Public\Get-TopdeskBranches.ps1' -1 <# .DESCRIPTION This cmdlet will return the branches in your enviroment .PARAMETER credential The credential to connect to the API endpoints .PARAMETER environment The topdesk enviroment URI. For examble https://test.topdesk.net then this value would be test .PARAMETER fields The fields that return from the data. Does not work when using the ID filter .PARAMETER id The id of the subcategory you are trying to retrieve .PARAMETER startsWith The start of the name of the branches .PARAMETER clientReferenceNumber The start of the client reference number .RETURNS Returns a list of branches .EXAMPLE Return all branches Get-TopdeskBranches Return specific branch by ID Get-TopdeskBranches -id <ID> Return specific branch where the name starts with Get-TopdeskBranches -startsWith <NAME> Return specific fields Get-TopdeskBranches -fields <FIELDS> #> function Get-TopdeskBranches{ [CmdletBinding()] param( [Parameter()][string]$fields, [Parameter()][string]$id, [Parameter()][string]$startsWith, [Parameter()][string]$clientReferenceNumber ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } # The base URI for the branches endpoint $uri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/branches?" # If ID, get details of specific branch. This is more detailed, and does not allow additional filtering if($id){ $uri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/branches/id/$($id)?" } else{ # By default the query is null $query = $null # If only want specific fields, set what fields if($fields){ $uri = "$($uri)`$fields=$($fields)" } # If Name is populated, filter based on the name, using starts with if($startsWith){ $query = "&query=name=sw=$($startsWith)" } # If Client Reference Number is populated, filter using starts with if($clientReferenceNumber){ if($null -eq $query){$query = "&query="} else{$query = "$($query);"} $query = "$($query)clientReferenceNumber=sw=$($clientReferenceNumber)" } # If query is not blank, add it to the URI if($null -ne $query){ $uri = "$($uri)$($query)" } } try{ # Get base data from Topdesk $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $global:topdeskAccessToken -StatusCodeVariable statusCode } catch{ throw "Error Accessing API at $($uri). $($Error[0])" } # Return results $results } #EndRegion '.\Public\Get-TopdeskBranches.ps1' 78 #Region '.\Public\Get-TopdeskChangeProgress.ps1' -1 <# .DESCRIPTION This cmdlet will return the change progress text for specific change .PARAMETER credential The credential to connect to the API endpoints .PARAMETER environment The topdesk enviroment URI. For examble https://test.topdesk.net then this value would be test .PARAMETER changeID The id of the change that you want the original request text from. .EXAMPLE Return change request progress Get-TopdeskChangeProgress -changeID <CHANGEID> #> function Get-TopdeskChangeProgress{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$changeID ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } $uri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/operatorChanges/$($changeID)/progresstrail" try{ # Invoke API to get the details $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $global:topdeskAccessToken -StatusCodeVariable statusCode } catch{ throw "Error Accessing API at $($uri). $($Error[0])" } # Return the details of the ticket return $results.results } #EndRegion '.\Public\Get-TopdeskChangeProgress.ps1' 34 #Region '.\Public\Get-TopDeskChangeRequest.ps1' -1 <# .DESCRIPTION This cmdlet is designed to query operator changes in Topdesk and return their details .PARAMETER credential The credential to connect to the API endpoints .PARAMETER environment The topdesk enviroment URI. For examble https://test.topdesk.net then this value would be test .PARAMETER query If you want to add a filtered query to the details .PARAMETER sort If there is a parameter you want to sort by .PARAMETER direction The direction of the sort only applicable if sort is set .PARAMETER pageSize The number of results you want to return .PARAMETER pageStart If you want to skip a certain number of results and start your retrieval there .PARAMETER fields What fields you want to retrieve from the API .PARAMETER all If you want to retrieve all relevant results, or just the first page .RETURNS Returns a list of changes that fall into the parameters .EXAMPLE Return the first page based on default sort Get-TopDeskChangeRequest Return all pages based on default sort Get-TopDeskChangeRequest -all Return a specific change based on the query function Get-TopDeskChangeRequest -query "number=='<CHANGENUMBER>'" #> function Get-TopDeskChangeRequest{ [CmdletBinding()] [OutputType([System.Collections.Generic.List[PSObject]])] param( [Parameter()][string]$query, [Parameter()][ValidateSet("id","creationDate","simple.closedDate","simple.plannedImplementationDate","simple.plannedStartDate","phases.rfc.plannedEndDate","phases.progress.plannedEndDate","phases.evaluation.plannedEndDate")] [string]$sort, [Parameter()][ValidateSet("asc","desc")][string]$direction = "asc", [Parameter()][string]$pageSize, [Parameter()][int]$pageStart = 0, [Parameter()][string]$fields, [Parameter()][switch]$all ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } try{ # Base URI for Operator Changes. $base = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/operatorChanges?pageStart=$($pageStart)" # If we are expecting a different page size from default if($pageSize -ne ""){ $base = "$($base)&pageSize=$($pageSize)" } # If we want to sort based on specific criteria, and the direction of that sort if($sort -ne ""){ $base = "$($base)&sort=$($sort):$($direction)" } # If we have added a query to our API call if($query -ne ""){ $base = "$($base)&query=$($query)" } # What fields to return if we want something other than the default if($fields -ne ""){ $base = "$($base)&fields=$($fields)" } # Set the initial URI to what we determined for the URI $uri = $base # Put inside a loop in case we want to get all. Only loop if result code is 206 and set to all $data = [System.Collections.Generic.List[PSObject]]@() do{ # Query API based on URI $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $global:topdeskAccessToken -StatusCodeVariable statusCode # Populate the URI to the next link returned by API $uri = $results.next # Loop through results and add to list foreach($item in $results.results){ $data.Add($item) | Out-Null } Write-Verbose "Next Query: $($uri)" Write-Verbose "Found $($data.count) tickets." }while($statusCode -eq 206 -and $all.IsPresent -ne $false) } catch{ throw "Error Accessing API at $($uri). $($Error[0])" } return $data } #EndRegion '.\Public\Get-TopDeskChangeRequest.ps1' 90 #Region '.\Public\Get-TopdeskChangeRequestText.ps1' -1 <# .DESCRIPTION This cmdlet will return the change request text for specific change .PARAMETER credential The credential to connect to the API endpoints .PARAMETER environment The topdesk enviroment URI. For examble https://test.topdesk.net then this value would be test .PARAMETER changeID The id of the change that you want the original request text from. .EXAMPLE Return change request text Get-TopdeskChangeRequestText -changeID <CHANGEID> #> function Get-TopdeskChangeRequestText{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$changeID ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } # Base URI for change request data $uri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/operatorChanges/$($changeID)/requests" try{ # Invoke API to get the details $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $global:topdeskAccessToken -StatusCodeVariable statusCode } catch{ throw "Error Accessing API at $($uri). $($Error[0])" } # Return the details of the ticket return $results.results } #EndRegion '.\Public\Get-TopdeskChangeRequestText.ps1' 35 #Region '.\Public\Get-TopdeskIncidentCategory.ps1' -1 <# .DESCRIPTION This cmdlet will return the categories you have configured for your incidents .PARAMETER credential The credential to connect to the API endpoints .PARAMETER environment The topdesk enviroment URI. For examble https://test.topdesk.net then this value would be test .PARAMETER id The id of the category you are trying to retrieve .PARAMETER name The name of the category you are trying to retrieve .RETURNS Returns a list of categories .EXAMPLE Return all categories Get-TopdeskIncidentCategory Return specific category by ID Get-TopdeskIncidentCategory -id <ID> Return specific category by Name Get-TopdeskIncidentCategory -name <NAME> #> function Get-TopdeskIncidentCategory{ [CmdletBinding()] param( [Parameter()][string]$id, [Parameter()][string]$name ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } # Topdesk URI for incident categories $uri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/incidents/categories" try{ # Retrieve data from API $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $global:topdeskAccessToken -StatusCodeVariable statusCode } catch{ throw "Error Accessing API at $($uri). $($Error[0])" } # Filter for either Id or Name, or return all if neither specificed if($id){ return $results | Where-Object {$_.id -eq $id} } elseif($name){ return $results | Where-Object {$_.name -eq $name} } else{ return $results } } #EndRegion '.\Public\Get-TopdeskIncidentCategory.ps1' 52 #Region '.\Public\Get-TopdeskIncidentSearchList.ps1' -1 <# .DESCRIPTION This cmdlet will recover the details of the optional fields search lists. It will .PARAMETER credential The credential to connect to the API endpoints .PARAMETER environment The topdesk enviroment URI. For examble https://test.topdesk.net then this value would be test .PARAMETER tab What tabs to get data from. This is an array .PARAMETER searchList What search lists to check for data. This is an array .PARAMETER includeEmpty Return empty data .RETURNS This returns a hashtable with the tabs and values. .EXAMPLE Return all search lists data Get-TopdeskIncidentSearchList Return all search lists data that are not empty Get-TopdeskIncidentSearchList -includeEmpty $false #> function Get-TopdeskIncidentSearchList{ [CmdletBinding()] [OutputType([hashtable])] param( [Parameter()][int[]]$tab = @(1,2), [Parameter()][int[]]$searchList = @(1,2,3,4,5), [Parameter()][bool]$includeEmpty = $true ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } # Iniatilize a empty list. $list = @{} # Loop through the tabs that hold the search lists foreach($t in $tab){ # Iniatilize a empty sublist $sublist = @{} # Loop through the items in the tab foreach($l in $searchList){ # Set the URI for the search list to get details of $uri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/incidents/free_fields/$($t)/searchlists/$($l)" try{ # Query API for data $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $global:topdeskAccessToken -StatusCodeVariable statusCode # If Include Empty is false then skip anything that is blank if($null -eq $results.id -and $includeEmpty -eq $false){continue} # Add Results to Sublist $sublist.Add($l,$results) | Out-Null } catch{ throw "Error Accessing API at $($uri). $($Error[0])" } } # If include empty is false, and the sublist is empty, skip. if($sublist.Count -eq 0 -and $includeEmpty -eq $false){continue} # Add sublist to the over all list $list.Add($t,$sublist) | Out-Null } # Return data return $list } #EndRegion '.\Public\Get-TopdeskIncidentSearchList.ps1' 64 #Region '.\Public\Get-TopdeskIncidentSubcategory.ps1' -1 <# .DESCRIPTION This cmdlet will return the subcategories that you have in your incident module .PARAMETER credential The credential to connect to the API endpoints .PARAMETER environment The topdesk enviroment URI. For examble https://test.topdesk.net then this value would be test .PARAMETER id The id of the subcategory you are trying to retrieve .PARAMETER name The name of the subcategory you are trying to retrieve .PARAMETER category_id The id of the category you are trying to retrieve .PARAMETER category_name The name of the category you are trying to retrieve .RETURNS Returns a list of subcategories .EXAMPLE Return all subcategories Get-TopdeskIncidentSubcategory Return specific subcategory by ID Get-TopdeskIncidentSubcategory -id <ID> Return specific subcategory by Name Get-TopdeskIncidentSubcategory <NAME> Return specific subcategory by what category id it is in Get-TopdeskIncidentSubcategory -category_id <CATEGORYID> Return specific subcategory by what category name it is in Get-TopdeskIncidentSubcategory -category_name <NAME> #> function Get-TopdeskIncidentSubcategory{ [CmdletBinding()] param( [Parameter()][string]$id, [Parameter()][string]$name, [Parameter()][string]$category_id, [Parameter()][string]$category_name ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } # Base URI for subcategory data $uri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/incidents/subcategories" try{ # Get base data from Topdesk $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $global:topdeskAccessToken -StatusCodeVariable statusCode } catch{ throw "Error Accessing API at $($uri). $($Error[0])" } # If looking for specific id, just return it if($id){ return $results | Where-Object {$_.id -eq $id} } else{ # If filtering by name, narrow results to match name if($name){ $results = $results | Where-Object {$_.name -eq $name} } # If filtering by category id, narrow results to match if($category_id){ $results = $results | Where-Object {$_.category.id -eq $category_id} } # If filtering by category name, narrow results to match if($category_name){ $results = $results | Where-Object {$_.category.name -eq $category_name} } # Return data return $results } } #EndRegion '.\Public\Get-TopdeskIncidentSubcategory.ps1' 72 #Region '.\Public\Get-TopdeskOperator.ps1' -1 function Get-TopdeskOperator{ [CmdletBinding()] param( [Parameter()][ValidateNotNullOrEmpty()][string]$query, [Parameter()][ValidateNotNullOrEmpty()][string]$fields ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } # Default Page Size. Between 1 and 100 $pageSize = 10 # The base URI for the branches endpoint $uri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/operators?start=0&page_size=$($pageSize)" # If query set add it to the uri if($query){ $uri = "$($uri)&query=$($query)" } # If fields set filter to those fields if($fields){ $uri = "$($uri)&fields=$($fields)" } try{ $data = [System.Collections.Generic.List[PSObject]]@() do{ $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $global:topdeskAccessToken -StatusCodeVariable statusCode foreach($item in $results){ $data.Add($item) | Out-Null } $currentStart = [Regex]::Match($uri,"\d+") $uri = $uri -replace "start=$($currentStart.Value)","start=$($currentStart.Value/1 + $pageSize)" } while($statusCode -eq 206) return $data } catch{ throw "Error Accessing API at $($uri). $($Error[0])" } } #EndRegion '.\Public\Get-TopdeskOperator.ps1' 39 #Region '.\Public\Get-TopdeskOperatorGroup.ps1' -1 function Get-TopdeskOperatorGroup{ [CmdletBinding()] param( [Parameter()][ValidateNotNullOrEmpty()][string]$query, [Parameter()][ValidateNotNullOrEmpty()][string]$fields ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } # Default Page Size. Between 1 and 100 $pageSize = 10 # The base URI for the branches endpoint $uri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/operatorgroups?start=0&page_size=$($pageSize)" # If query set add it to the uri if($query){ $uri = "$($uri)&query=$($query)" } # If fields set filter to those fields if($fields){ $uri = "$($uri)&fields=$($fields)" } try{ $data = [System.Collections.Generic.List[PSObject]]@() do{ $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $global:topdeskAccessToken -StatusCodeVariable statusCode foreach($item in $results){ $data.Add($item) | Out-Null } $currentStart = [Regex]::Match($uri,"\d+") $uri = $uri -replace "start=$($currentStart.Value)","start=$($currentStart.Value/1 + $pageSize)" } while($statusCode -eq 206) return $data } catch{ throw "Error Accessing API at $($uri). $($Error[0])" } } #EndRegion '.\Public\Get-TopdeskOperatorGroup.ps1' 39 #Region '.\Public\Get-TopdeskOrderedItems.ps1' -1 <# .DESCRIPTION This cmdlet will return any ordered items that are attached to the specific change id .PARAMETER credential The credential to connect to the API endpoints .PARAMETER environment The topdesk enviroment URI. For examble https://test.topdesk.net then this value would be test .PARAMETER changeID The id of the change that you want to look for items on .RETURNS Returns a list of ordered items .EXAMPLE Return the ordered items of a change Get-TopdeskOrderedItems-changeID <CHANGEID> #> function Get-TopdeskOrderedItems{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$changeID ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } # URI to get ordered items of a change $uri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/operatorChanges/$($changeID)/orderedItems" try{ # Query the API for the relevant data $results = Invoke-RestMethod -Method Get -Uri $uri -Headers $global:topdeskAccessToken -StatusCodeVariable statusCode } catch{ throw $Error[0] } # Return results return $results.results } #EndRegion '.\Public\Get-TopdeskOrderedItems.ps1' 37 #Region '.\Public\Get-Values.ps1' -1 <# .DESCRIPTION This cmdlet is designed to help PSU scripts for display of values .PARAMETER fields What fields to display #> function Get-Values(){ param( [Parameter(Mandatory)][string[]]$fields, [Parameter(Mandatory)][PSCustomObject]$item ) $value = "" # Loop through the array to take not of what values were selected, and then return that value. foreach($i in $fields){ if($item.$i){ if($i.Contains('-1')){$value += $i.Substring(0,$i.Length-2) + ","} else{$value += $i + ","} } } return $value.Replace("-"," ") } #EndRegion '.\Public\Get-Values.ps1' 22 #Region '.\Public\Get-YesNo.ps1' -1 <# .DESCRIPTION This cmdlet is designed to help PSU scripts for display of yes/no .PARAMETER fields What fields to display #> function Get-YesNo(){ [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory = $true)][string[]]$fields, [Parameter(Mandatory)][PSCustomObject]$item ) # Loop through values that are passed, and if exist, return yes, otherwise return No foreach($i in $fields){ if($item.$i){return "Yes"} } return "No" } #EndRegion '.\Public\Get-YesNo.ps1' 20 #Region '.\Public\Move-GraphMail.ps1' -1 <# .DESCRIPTION This cmdlet is designed to move emails between folders in a mailbox .PARAMETER id The id of the mail message we are acting on .PARAMETER emailAddress The email address of the account that we are reading from .PARAMETER folder The id of the folder that we are moving the message to #> function Move-GraphMail{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$id, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$emailAddress, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$folder ) if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } $headers = Get-GraphHeader # Body Content $body = @{ "destinationId" = $folder } | ConvertTo-Json $uri = "https://graph.microsoft.com/beta/users/$($emailAddress)/messages/$($id)/move" $results = Invoke-RestMethod -Method Post -Uri $uri -Headers $headers -Body $body -StatusCodeVariable statusCode # Return Results if($statusCode -in (200,201)){ return $results } else{ throw "Unable to move email." } } #EndRegion '.\Public\Move-GraphMail.ps1' 36 #Region '.\Public\Move-MerakiSystemsManagerDevice.ps1' -1 <# .DESCRIPTION This cmdlet is designed to move devices from one network to another .PARAMETER networkId The network that we want to get the devices from .PARAMETER newNetworkId The network that we want to get the devices to .PARAMETER ids If we want to move devices based on their id .PARAMETER serialNumbers If we want to move devices based on their serial number #> function Move-MerakiSystemsManagerDevice{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$networkId, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$newNetworkId, [Parameter()][ValidateNotNullOrEmpty()][System.Object]$ids, [Parameter()][ValidateNotNullOrEmpty()][System.Object]$serialNumbers ) # Confirm we have a valid meraki header if(!$global:merakiHeader){ throw "Please Call Connect-Meraki before calling this cmdlet" } # Confirm we have items to move if(-not $ids -and -not $serialNumbers){ throw "You need to specify at least one id or one serial number" } # Generate URI for call $uri = "$($global:merakiApiURI)/networks/$($networkId)/sm/devices/move" # Create Body $body = @{ "newNetwork" = $newNetworkId } if($ids){ $body.Add("ids",$ids) } if($serialNumbers){ $body.Add("serials",$serialNumbers) } # Try to move devices try{ $results = Invoke-RestMethod -Method "Post" -Uri $uri -Headers $global:merakiHeader -Body ($body | ConvertTo-Json) -StatusCodeVariable statusCode } catch{ throw "Unable to move devices. $($_.Exception.Message)" } } #EndRegion '.\Public\Move-MerakiSystemsManagerDevice.ps1' 49 #Region '.\Public\New-AlertFactoryIncident.ps1' -1 <# .DESCRIPTION This cmdlet is designed to create a new Topdesk Support Ticket .PARAMETER details The details that we will be using for the ticket .PARAMETER emails The emails that will be part of this incident .PARAMETER sourceDir The directory that the process is running from, so it can save the emails to html files #> function New-AlertFactoryIncident{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][hashtable]$details, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$emails, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$sourceDir, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$mailbox, [Parameter()][ValidateNotNullOrEmpty()][int]$count = 1 ) $body = @{} foreach($item in $details.GetEnumerator()){ if($null -ne $item.Value){ switch($item.key){ "email"{ $body.Add("caller_dynamicName",$item.Value) $body.Add("caller_email",$item.Value) } "location" { $body.Add("caller_branch_id",(Get-TopdeskBranches -clientReferenceNumber $item.Value | Select-Object -first 1).id) } "briefDescription" { $description = $item.Value if($count -gt 1){ $description = "$($description) (Has Occured $($count) times)" } $body.Add("briefDescription",$description) } "callType" { $body.Add("callType_name",$item.Value) } "category" { $body.Add("category_name",$item.Value) } "subcategory" { $body.Add("subcategory_name",$item.Value) } "impact" { $body.Add("impact_name",$item.Value) } "urgency" { $body.Add("urgency_name",$item.Value) } "priorty" { $body.Add("priority_name",$item.Value) } "duration" { $body.Add("duration_name",$item.Value) } "operatorGroup" { $body.Add("operatorGroup_id",(Get-TopdeskOperatorGroup -query "groupName=='$($item.value)'" -fields id | Select-Object -first 1).id) } "operator" { $body.Add("operator_id",(Get-TopdeskOperator -query "dynamicName=='$($item.value)'" -fields id | Select-Object -first 1).id) } "status" { $body.Add("processingStatus_name",$item.Value) } "request" { $body.Add("request",$item.value) } } } } try{ $incident = New-TopdeskIncident @body foreach($email in $emails) { $tempfolder = Join-Path -Path $sourceDir -ChildPath "Temp" if(-not (Test-Path $tempfolder)){ New-Item -Path $tempfolder -Force -ItemType Directory } $filename = "$(Get-Random).html" $tempfile = Join-Path -Path $tempfolder -ChildPath $filename $email.body.content | Out-File $tempFile Add-TopdeskIncidentAttachment -id $incident.id -filepath $tempFile -filename $filename Remove-Item $tempfile -Force if($email.hasAttachments -and $rule.saveattachments){ $attachments = Get-GraphMailAttachment -mailbox $mailbox -messageid $email.id foreach($attachment in $attachments){ Add-TopdeskIncidentAttachment -id $incident.id -base64 $attachment.contentBytes -contenttype $attachment.contentType -filename $attachment.name } } } return $incident } catch{ throw "Unable to create incident. $($Error[0])" } } #EndRegion '.\Public\New-AlertFactoryIncident.ps1' 101 #Region '.\Public\New-EntraIDDeviceTrustBody.ps1' -1 <# .SYNOPSIS Construct the body with the elements for a sucessful device trust validation required by a Function App that's leveraging the AADDeviceTrust.FunctionApp module. .DESCRIPTION Construct the body with the elements for a sucessful device trust validation required by a Function App that's leveraging the AADDeviceTrust.FunctionApp module. .EXAMPLE .\New-AADDeviceTrustBody.ps1 .NOTES Author: Nickolaj Andersen Contact: @NickolajA Created: 2022-03-14 Updated: 2023-05-14 Version history: 1.0.0 - (2022-03-14) Script created 1.0.1 - (2023-05-10) Max - Updated to no longer use Thumbprint field, no redundant. 1.0.2 - (2023-05-14) Max - Updating to pull the Azure AD Device ID from the certificate itself. #> function New-EntraIDDeviceTrustBody { [CmdletBinding()] param() # Retrieve required data for building the request body $EntraIDDeviceID = Get-EntraIDDeviceID # Still needed to form the signature. $CertificateThumbprint = Get-EntraIDRegistrationCertificateThumbprint $Signature = New-RSACertificateSignature -Content $EntraIDDeviceID -Thumbprint $CertificateThumbprint $PublicKeyBytesEncoded = Get-PublicKeyBytesEncodedString -Thumbprint $CertificateThumbprint # Construct client-side request header $BodyTable = [ordered]@{ DeviceName = $env:COMPUTERNAME #DeviceID = $EntraIDDeviceID - Will be pulled from the key. Signature = $Signature #Thumbprint = $CertificateThumbprint - Will be pulled from the key. PublicKey = $PublicKeyBytesEncoded } # Handle return value return $BodyTable } #EndRegion '.\Public\New-EntraIDDeviceTrustBody.ps1' 43 #Region '.\Public\New-GraphGroup.ps1' -1 <# .DESCRIPTION This cmdlet is designed to create a new Entra ID group via graph .PARAMETER displayName The display name of the group .PARAMETER mailEnabled If the group should be mail enabled, default false .PARAMETER mailNickname What the mailnickname will be, required even if mailenabled is false .PARAMETER description The description for the group .PARAMETER securityEnabled If the group should be security enabled, default true #> function New-GraphGroup{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][string]$displayName, [Parameter()][bool]$mailEnabled = $false, [Parameter(Mandatory = $true)][string]$mailNickname, [Parameter()][string]$description, [Parameter()][bool]$securityEnabled = $true ) # Confirm we have a valid graph token if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # Build the headers we will use to get groups $headers = Get-GraphHeader # Variables $body = $PsBoundParameters | ConvertTo-Json # Base URI for resource call $uri = "https://graph.microsoft.com/beta/groups" try{ # Execute call against graph $results = Invoke-RestMethod -Method Post -Uri $uri -Headers $headers -Body $body -StatusCodeVariable statusCode return $results } catch{ throw "Unable to create group. $($_.Exception.Message)" } } #EndRegion '.\Public\New-GraphGroup.ps1' 44 #Region '.\Public\New-RandomString.ps1' -1 <# .DESCRIPTION This cmdet will generate a random character string based on inputs passed to it. .PARAMETER length The number of characters you want the random string to contain. .PARAMETER characters The list of characters that you want it to use to generate the random string .EXAMPLE New-RandomString -length 10 -characters 'abcdefghiklmnoprstuvwxyzABCDEFGHKLMNOPRSTUVWXYZ1234567890!@#$%^&*' Will Generate a random string of 10 characters in length with the characters in abcdefghiklmnoprstuvwxyzABCDEFGHKLMNOPRSTUVWXYZ1234567890!@#$%^&* #> function New-RandomString{ [CmdletBinding()] [OutputType([System.String])] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][int]$length, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$characters ) # Generate a random string based on the length and characters passed $randomString = 1..$length | ForEach-Object { Get-Random -Maximum $characters.length} $private:ofs = "" return [string]$characters[$randomString] } #EndRegion '.\Public\New-RandomString.ps1' 24 #Region '.\Public\New-RSACertificateSignature.ps1' -1 <# .SYNOPSIS Creates a new signature based on content passed as parameter input using the private key of a certificate determined by it's thumbprint, to sign the computed hash of the content. .DESCRIPTION Creates a new signature based on content passed as parameter input using the private key of a certificate determined by it's thumbprint, to sign the computed hash of the content. The certificate used must be available in the LocalMachine\My certificate store, and must also contain a private key. .PARAMETER Content Specify the content string to be signed. .PARAMETER Thumbprint Specify the thumbprint of the certificate. .NOTES Author: Nickolaj Andersen / Thomas Kurth Contact: @NickolajA Created: 2021-06-03 Updated: 2021-06-03 Version history: 1.0.0 - (2021-06-03) Function created Credits to Thomas Kurth for sharing his original C# code. #> function New-RSACertificateSignature { param( [parameter(Mandatory = $true, HelpMessage = "Specify the content string to be signed.")] [ValidateNotNullOrEmpty()] [string]$Content, [parameter(Mandatory = $true, HelpMessage = "Specify the thumbprint of the certificate.")] [ValidateNotNullOrEmpty()] [string]$Thumbprint ) Process { # Determine the certificate based on thumbprint input $Certificate = Get-ChildItem -Path "Cert:\LocalMachine\My" -Recurse | Where-Object { $PSItem.Thumbprint -eq $CertificateThumbprint } if ($Certificate -ne $null) { if ($Certificate.HasPrivateKey -eq $true) { # Read the RSA private key $RSAPrivateKey = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($Certificate) if ($RSAPrivateKey -ne $null) { if ($RSAPrivateKey -is [System.Security.Cryptography.RSACng]) { # Construct a new SHA256Managed object to be used when computing the hash $SHA256Managed = New-Object -TypeName "System.Security.Cryptography.SHA256Managed" # Construct new UTF8 unicode encoding object $UnicodeEncoding = [System.Text.UnicodeEncoding]::UTF8 # Convert content to byte array [byte[]]$EncodedContentData = $UnicodeEncoding.GetBytes($Content) # Compute the hash [byte[]]$ComputedHash = $SHA256Managed.ComputeHash($EncodedContentData) # Create signed signature with computed hash [byte[]]$SignatureSigned = $RSAPrivateKey.SignHash($ComputedHash, [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1) # Convert signature to Base64 string $SignatureString = [System.Convert]::ToBase64String($SignatureSigned) # Handle return value return $SignatureString } } } } } } #EndRegion '.\Public\New-RSACertificateSignature.ps1' 65 #Region '.\Public\New-Shortcut.ps1' -1 <# .DESCRIPTION This is designed to create a shortcut on the desktop. Originally from https://github.com/AdamGrossTX/ManagedUserManagement/blob/main/ClientScripts/Set-AutoLogon.ps1 by Adam Gross #> function New-Shortcut { [CmdletBinding()] param( [parameter()][ValidateNotNullOrEmpty()][string]$Name, [parameter()][ValidateNotNullOrEmpty()][string]$CommandLine, [parameter()][ValidateNotNullOrEmpty()][string]$Arguments, [parameter()][string]$UserName = $null, [parameter()][string]$OneDriveOrgName = $null, [parameter()][switch]$StartMenu, [parameter()][string]$folder ) # Get a list of all the user profiles on the machine $ProfileList = Get-ChildItem Registry::"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" | Where-Object { $_.Name -notlike "*_Classes" -and $_.PSChildName -notin ("S-1-5-18", "S-1-5-19", "S-1-5-20") } $UserList = foreach ($UserKey in $ProfileList) { @{ ProfileKey = $UserKey | Where-Object { $_.name -like "*" + $UserKey.PSChildName + "*" } UserName = try { ((([system.security.principal.securityidentIfier]$UserKey.PSChildName).Translate([System.Security.Principal.NTAccount])).ToString()).substring(3) } catch { continue }; SID = $UserKey.PSChildName ProfilePath = Get-ItemProperty $UserKey.PSPath | Select-Object -ExpandProperty ProfileImagePath } } # Determine if we should be using paths from the user's profile or the public profile if($null -ne $Username -and $UserName -ne ""){ $baseprofile = ($UserList | Where-Object {$_.UserName -like "*$($UserName)*"}).ProfilePath if(-not $baseprofile){ throw "Unable to find profile for username: $($UserName)" } $desktopPath = Join-Path -Path $baseprofile -ChildPath "Desktop\$($folder)" $onedrivePath = Join-Path -Path $baseprofile -ChildPath "OneDrive - $($OneDriveOrgName)\Desktop\$($folder)" if($null -ne $OneDriveOrgName -and (Test-Path $onedrivePath)){ $desktopPath = $onedrivePath } $startMenuPath = Join-Path -Path $baseprofile -ChildPath "AppData\Roaming\Microsoft\Windows\Start Menu\Programs\$($folder)" } else{ $desktopPath = Join-Path -Path $ENV:PUBLIC -ChildPath "Desktop\$($folder)" $startMenuPath = Join-Path -path $ENV:ALLUSERSPROFILE -ChildPath "Microsoft\Windows\Start Menu\Programs\$($folder)" } # Set the path based on if we are doing start menu or desktop if($startMenu.IsPresent){ $path = $startMenuPath } else{ $path = $desktopPath } try{ # Create folder if it does not exist, and is set to need one if($folder -and -not (Test-Path -Path $path)){ New-Item -Path $path -ItemType Directory | Out-Null } $path = Join-Path -Path $path -ChildPath "$($Name).lnk" $WshShell = New-Object -comObject WScript.Shell $Shortcut = $WshShell.CreateShortcut($Path) $Shortcut.TargetPath = $CommandLine $Shortcut.Arguments = $Arguments Write-host "Shortcut-Path = $Path" $Shortcut.Save() } catch { throw $_ } } #EndRegion '.\Public\New-Shortcut.ps1' 67 #Region '.\Public\New-TopdeskAssetUpload.ps1' -1 function New-TopdeskAssetUpload{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$filePath, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$fileName, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$TopdeskTenant ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } $global:topdeskAccessToken."Content-Type" = "application/octet-stream" try{ $uri = "https://$($TopdeskTenant).topdesk.net/services/import-to-api-v1/api/sourceFiles?filename=$($fileName)" Invoke-RestMethod -Uri $uri -Method PUT -headers $global:topdeskAccessToken -InFile $filePath } catch{ throw "Error Accessing API at $($uri). $($Error[1])" } } #EndRegion '.\Public\New-TopdeskAssetUpload.ps1' 21 #Region '.\Public\New-TopdeskIncident.ps1' -1 function New-TopdeskIncident{ [CmdletBinding()] param( [Parameter(Mandatory = $true,ParameterSetName = 'registeredCaller')][ValidateNotNullOrEmpty()][string]$caller_id, [Parameter()][ValidateNotNullOrEmpty()][string]$caller_branch_id, [Parameter(Mandatory = $true,ParameterSetName = 'nonregisteredCaller')][ValidateNotNullOrEmpty()][string]$caller_dynamicName, [Parameter()][ValidateNotNullOrEmpty()][string]$caller_phoneNumber, [Parameter()][ValidateNotNullOrEmpty()][string]$caller_mobileNumber, [Parameter()][ValidateNotNullOrEmpty()][string]$caller_email, [Parameter()][ValidateNotNullOrEmpty()][string]$caller_department_id, [Parameter()][ValidateNotNullOrEmpty()][string]$caller_department_name, [Parameter()][ValidateNotNullOrEmpty()][string]$caller_location_id, [Parameter()][ValidateNotNullOrEmpty()][string]$caller_budgetHolder_id, [Parameter()][ValidateNotNullOrEmpty()][string]$caller_budgetHolder_name, [Parameter()][ValidateNotNullOrEmpty()][string]$caller_personExtraFieldA_id, [Parameter()][ValidateNotNullOrEmpty()][string]$caller_personExtraFieldA_name, [Parameter()][ValidateNotNullOrEmpty()][string]$caller_personExtraFieldB_id, [Parameter()][ValidateNotNullOrEmpty()][string]$caller_personExtraFieldB_name, [Parameter()][ValidateNotNullOrEmpty()][string]$caller_callerLookup_id, [Parameter()][ValidateNotNullOrEmpty()][string]$status, [Parameter()][ValidateNotNullOrEmpty()][string]$briefDescription, [Parameter()][ValidateNotNullOrEmpty()][string]$request, [Parameter()][ValidateNotNullOrEmpty()][string]$action, [Parameter()][ValidateNotNullOrEmpty()][bool]$actionInvisibleForCaller, [Parameter()][ValidateNotNullOrEmpty()][string]$entryType_id, [Parameter()][ValidateNotNullOrEmpty()][string]$entryType_name, [Parameter()][ValidateNotNullOrEmpty()][string]$callType_id, [Parameter()][ValidateNotNullOrEmpty()][string]$callType_name, [Parameter()][ValidateNotNullOrEmpty()][string]$category_id, [Parameter()][ValidateNotNullOrEmpty()][string]$category_name, [Parameter()][ValidateNotNullOrEmpty()][string]$subcategory_id, [Parameter()][ValidateNotNullOrEmpty()][string]$subcategory_name, [Parameter()][ValidateNotNullOrEmpty()][string]$externalNumber, [Parameter()][ValidateNotNullOrEmpty()][string]$object_id, [Parameter()][ValidateNotNullOrEmpty()][string]$object_name, [Parameter()][ValidateNotNullOrEmpty()][string]$location_id, [Parameter()][ValidateNotNullOrEmpty()][string]$branch_id, [Parameter()][ValidateNotNullOrEmpty()][string]$mainIncident_id, [Parameter()][ValidateNotNullOrEmpty()][string]$mainIncident_number, [Parameter()][ValidateNotNullOrEmpty()][string]$impact_id, [Parameter()][ValidateNotNullOrEmpty()][string]$impact_name, [Parameter()][ValidateNotNullOrEmpty()][string]$urgency_id, [Parameter()][ValidateNotNullOrEmpty()][string]$urgency_name, [Parameter()][ValidateNotNullOrEmpty()][string]$priority_id, [Parameter()][ValidateNotNullOrEmpty()][string]$priority_name, [Parameter()][ValidateNotNullOrEmpty()][string]$duration_id, [Parameter()][ValidateNotNullOrEmpty()][string]$duration_name, [Parameter()][ValidateNotNullOrEmpty()][datetime]$targetDate, [Parameter()][ValidateNotNullOrEmpty()][string]$sla_id, [Parameter()][ValidateNotNullOrEmpty()][bool]$onHold, [Parameter()][ValidateNotNullOrEmpty()][string]$operator_id, [Parameter()][ValidateNotNullOrEmpty()][string]$operatorGroup_id, [Parameter()][ValidateNotNullOrEmpty()][string]$supplier_id, [Parameter()][ValidateNotNullOrEmpty()][string]$processingStatus_id, [Parameter()][ValidateNotNullOrEmpty()][string]$processingStatus_name, [Parameter()][ValidateNotNullOrEmpty()][bool]$responded, [Parameter()][ValidateNotNullOrEmpty()][datetime]$responseDate, [Parameter()][ValidateNotNullOrEmpty()][bool]$completed, [Parameter()][ValidateNotNullOrEmpty()][datetime]$completedDate, [Parameter()][ValidateNotNullOrEmpty()][bool]$closed, [Parameter()][ValidateNotNullOrEmpty()][datetime]$closedDate, [Parameter()][ValidateNotNullOrEmpty()][string]$closureCode_id, [Parameter()][ValidateNotNullOrEmpty()][string]$closureCode_name, [Parameter()][ValidateNotNullOrEmpty()][decimal]$costs, [Parameter()][ValidateNotNullOrEmpty()][int]$feedbackRating, [Parameter()][ValidateNotNullOrEmpty()][string]$feedbackMessage, [Parameter()][ValidateNotNullOrEmpty()][bool]$majorCall, [Parameter()][ValidateNotNullOrEmpty()][string]$majorCallObject_id, [Parameter()][ValidateNotNullOrEmpty()][string]$majorCallObject_number, [Parameter()][ValidateNotNullOrEmpty()][bool]$publishToSsd, [Parameter()][ValidateNotNullOrEmpty()][bool]$optionalFields1_boolean1, [Parameter()][ValidateNotNullOrEmpty()][bool]$optionalFields1_boolean2, [Parameter()][ValidateNotNullOrEmpty()][bool]$optionalFields1_boolean3, [Parameter()][ValidateNotNullOrEmpty()][bool]$optionalFields1_boolean4, [Parameter()][ValidateNotNullOrEmpty()][bool]$optionalFields1_boolean5, [Parameter()][ValidateNotNullOrEmpty()][decimal]$optionalFields1_number1, [Parameter()][ValidateNotNullOrEmpty()][decimal]$optionalFields1_number2, [Parameter()][ValidateNotNullOrEmpty()][decimal]$optionalFields1_number3, [Parameter()][ValidateNotNullOrEmpty()][decimal]$optionalFields1_number4, [Parameter()][ValidateNotNullOrEmpty()][decimal]$optionalFields1_number5, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_text1, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_text2, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_text3, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_text4, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_text5, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_memo1, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_memo2, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_memo3, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_memo4, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_memo5, [Parameter()][ValidateNotNullOrEmpty()][datetime]$optionalFields1_date1, [Parameter()][ValidateNotNullOrEmpty()][datetime]$optionalFields1_date2, [Parameter()][ValidateNotNullOrEmpty()][datetime]$optionalFields1_date3, [Parameter()][ValidateNotNullOrEmpty()][datetime]$optionalFields1_date4, [Parameter()][ValidateNotNullOrEmpty()][datetime]$optionalFields1_date5, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_searchlist1_id, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_searchlist1_name, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_searchlist2_id, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_searchlist2_name, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_searchlist3_id, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_searchlist3_name, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_searchlist4_id, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_searchlist4_name, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_searchlist5_id, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields1_searchlist5_name, [Parameter()][ValidateNotNullOrEmpty()][bool]$optionalFields2_boolean1, [Parameter()][ValidateNotNullOrEmpty()][bool]$optionalFields2_boolean2, [Parameter()][ValidateNotNullOrEmpty()][bool]$optionalFields2_boolean3, [Parameter()][ValidateNotNullOrEmpty()][bool]$optionalFields2_boolean4, [Parameter()][ValidateNotNullOrEmpty()][bool]$optionalFields2_boolean5, [Parameter()][ValidateNotNullOrEmpty()][decimal]$optionalFields2_number1, [Parameter()][ValidateNotNullOrEmpty()][decimal]$optionalFields2_number2, [Parameter()][ValidateNotNullOrEmpty()][decimal]$optionalFields2_number3, [Parameter()][ValidateNotNullOrEmpty()][decimal]$optionalFields2_number4, [Parameter()][ValidateNotNullOrEmpty()][decimal]$optionalFields2_number5, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_text1, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_text2, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_text3, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_text4, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_text5, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_memo1, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_memo2, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_memo3, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_memo4, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_memo5, [Parameter()][ValidateNotNullOrEmpty()][datetime]$optionalFields2_date1, [Parameter()][ValidateNotNullOrEmpty()][datetime]$optionalFields2_date2, [Parameter()][ValidateNotNullOrEmpty()][datetime]$optionalFields2_date3, [Parameter()][ValidateNotNullOrEmpty()][datetime]$optionalFields2_date4, [Parameter()][ValidateNotNullOrEmpty()][datetime]$optionalFields2_date5, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_searchlist1_id, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_searchlist1_name, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_searchlist2_id, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_searchlist2_name, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_searchlist3_id, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_searchlist3_name, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_searchlist4_id, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_searchlist4_name, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_searchlist5_id, [Parameter()][ValidateNotNullOrEmpty()][string]$optionalFields2_searchlist5_name, [Parameter()][ValidateNotNullOrEmpty()][string]$externalLink_id, [Parameter()][ValidateNotNullOrEmpty()][string]$externalLink_type, [Parameter()][ValidateNotNullOrEmpty()][string]$externalLink_date ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } $body = @{} foreach($item in $PsBoundParameters.GetEnumerator()){ $key = $item.Key.split("_") if($key.count -gt 1){ $parent = "" for($i = 0; $i -lt $key.count - 1; $i++){ if($i -eq 0){ if(!$body.ContainsKey($key[$i])){ $body.Add($key[$i],@{}) | Out-Null } $parent = $key[$i] } else{ $scriptBlock = " if(!`$body.$($parent).ContainsKey(`"$($key[$i])`")){ `$body.$($parent).Add(`"$($key[$i])`",@{}) | Out-Null } `$parent = `"$($parent).$($key[$i])`" " Invoke-Expression $scriptBlock } } $scriptBlock = " `$body.$($parent).Add(`"$($key[$i])`",`$item.value) " Invoke-Expression $scriptBlock } else{ if(!$body.ContainsKey($item.Key)){ $body.Add($item.Key,$item.Value) | Out-Null } } } $uri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/incidents" try{ $results = Invoke-RestMethod -Method POST -Uri $uri -Headers $global:topdeskAccessToken -body ($body | ConvertTo-Json) -StatusCodeVariable statusCode } catch{ throw "Error Accessing API at $($uri). $($Error[0])" } return $results } #EndRegion '.\Public\New-TopdeskIncident.ps1' 191 #Region '.\Public\Remove-AutorunRegKeys.ps1' -1 <# .DESCRIPTION This is designed to remove autorun keys for the machine. Originally from https://github.com/AdamGrossTX/ManagedUserManagement/blob/main/ClientScripts/Set-AutoLogon.ps1 by Adam Gross #> function Remove-AutorunRegKeys { [cmdletbinding()] param ( [parameter(Mandatory = $true)][string]$Name, [parameter()][string]$UserName = $null, [parameter()][switch]$runOnce, [parameter()][switch]$wildcard ) $forceload = $false # Get a list of all the user profiles on the machine $ProfileList = Get-ChildItem Registry::"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" | Where-Object { $_.Name -notlike "*_Classes" -and $_.PSChildName -notin ("S-1-5-18", "S-1-5-19", "S-1-5-20") } $UserList = foreach ($UserKey in $ProfileList) { @{ ProfileKey = $UserKey | Where-Object { $_.name -like "*" + $UserKey.PSChildName + "*" } UserName = try { ((([system.security.principal.securityidentIfier]$UserKey.PSChildName).Translate([System.Security.Principal.NTAccount])).ToString()).substring(3) } catch { continue }; SID = $UserKey.PSChildName ProfilePath = Get-ItemProperty $UserKey.PSPath | Select-Object -ExpandProperty ProfileImagePath } } if($null -ne $Username -and $UserName -ne ""){ $SID = ($UserList | Where-Object {$_.UserName -like "*$($UserName)*"}).SID $baseprofile = ($UserList | Where-Object {$_.UserName -like "*$($UserName)*"}).ProfilePath if($runOnce.IsPresent){ $registryPath = "REGISTRY::HKEY_USERS\$($SID)\Software\Microsoft\Windows\CurrentVersion\RunOnce" } else{ $registryPath = "REGISTRY::HKEY_USERS\$($SID)\Software\Microsoft\Windows\CurrentVersion\Run" } if(-not (Test-Path -Path $registryPath)){ $hivepath = Join-Path -Path $baseprofile -ChildPath "NTUSER.DAT" reg Load "HKU\$($SID)" "$($hivepath)" | Out-Null $forceload = $true if(-not (Test-Path -Path $registryPath)){ throw "Unable to load hive for user: $($UserName)" } } } else{ if($runOnce.IsPresent){ $registryPath = "REGISTRY::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce" } else{ $registryPath = "REGISTRY::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run" } } if($wildcard.IsPresent){ $Keys = (Get-ItemProperty -Path $registryPath).psobject.properties | Where-Object { $_.Name -like "$($Name)*" } } else{ $Keys = (Get-ItemProperty -Path $registryPath).psobject.properties | Where-Object { $_.Name -eq $Name } } try{ foreach ($entry in $Keys) { Write-Host "Removing existing items $($entry.Name)" Remove-ItemProperty -Path $registryPath -Name $Entry.Name } if($forceload){ [gc]::Collect() reg unload "HKU\$($SID)" | Out-Null } } catch{ throw $_ } } #EndRegion '.\Public\Remove-AutorunRegKeys.ps1' 70 #Region '.\Public\Remove-GraphGroupMember.ps1' -1 function Remove-GraphGroupMember{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$groupId, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string[]]$ids ) # Confirm we have a valid graph token if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # Get Graph Headers for Call $headers = Get-GraphHeader try{ $batchObj = [System.Collections.Generic.List[PSCustomObject]]@() $batches = [System.Collections.Generic.List[PSCustomObject]]@() for($i = 1; $i -le $ids.Count; ++$i){ $obj = [PSCustomObject]@{ "id" = $i "method" = "DELETE" "url" = "/groups/$($groupId)/members/$($ids[$i-1])/`$ref" } $batchObj.Add($obj) | Out-Null if($($i % 20) -eq 0){ $batches.Add($batchObj) | Out-Null $batchObj = $null $batchObj = [System.Collections.Generic.List[PSCustomObject]]@() } } $batches.Add($batchObj) | Out-Null foreach($batch in $batches){ $json = [PSCustomObject]@{ "requests" = $batch } | ConvertTo-JSON -Depth 5 $results = Invoke-RestMethod -Method "POST" -Uri "https://graph.microsoft.com/beta/`$batch" -Headers $headers -Body $json } } catch{ throw "Unable to remove members. $($_.Exception.Message)" } } #EndRegion '.\Public\Remove-GraphGroupMember.ps1' 41 #Region '.\Public\Remove-GraphIntuneApp.ps1' -1 function Remove-GraphIntuneApp{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$applicationid ) # Invoke graph API to remove the application $endpoint = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$($applicationid)" $headers = Get-GraphHeader Invoke-RestMethod -Method Delete -Uri $endpoint -Headers $headers -StatusCodeVariable statusCode | Out-Null } #EndRegion '.\Public\Remove-GraphIntuneApp.ps1' 11 #Region '.\Public\Remove-Shortcut.ps1' -1 function Remove-Shortcut { [CmdletBinding()] param( [parameter(Mandatory = $true)][string]$Name, [parameter()][string]$UserName = $null, [parameter()][string]$OneDriveOrgName = $null, [parameter()][switch]$StartMenu, [parameter()][string]$folder, [parameter()][switch]$wildcard ) # Get a list of all the user profiles on the machine $ProfileList = Get-ChildItem Registry::"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" | Where-Object { $_.Name -notlike "*_Classes" -and $_.PSChildName -notin ("S-1-5-18", "S-1-5-19", "S-1-5-20") } $UserList = foreach ($UserKey in $ProfileList) { @{ ProfileKey = $UserKey | Where-Object { $_.name -like "*" + $UserKey.PSChildName + "*" } UserName = try { ((([system.security.principal.securityidentIfier]$UserKey.PSChildName).Translate([System.Security.Principal.NTAccount])).ToString()).substring(3) } catch { continue }; SID = $UserKey.PSChildName ProfilePath = Get-ItemProperty $UserKey.PSPath | Select-Object -ExpandProperty ProfileImagePath } } # Determine if we should be using paths from the user's profile or the public profile if($null -ne $Username -and $UserName -ne ""){ $baseprofile = ($UserList | Where-Object {$_.UserName -like "*$($UserName)*"}).ProfilePath if(-not $baseprofile){ throw "Unable to find profile for username: $($UserName)" } $desktopPath = Join-Path -Path $baseprofile -ChildPath "Desktop\$($folder)" $onedrivePath = Join-Path -Path $baseprofile -ChildPath "OneDrive - $($OneDriveOrgName)\Desktop\$($folder)" if(Test-Path $onedrivePath){ $desktopPath = $onedrivePath } $startMenuPath = Join-Path -Path $baseprofile -ChildPath "AppData\Roaming\Microsoft\Windows\Start Menu\Programs\$($folder)" } else{ $desktopPath = Join-Path -Path $ENV:PUBLIC -ChildPath "Desktop\$($folder)" $startMenuPath = Join-Path -path $ENV:ALLUSERSPROFILE -ChildPath "Microsoft\Windows\Start Menu\Programs\$($folder)" } # Set the path based on if we are doing start menu or desktop if($startMenu.IsPresent){ $path = $startMenuPath } else{ $path = $desktopPath } if($wildcard.IsPresent){ Get-ChildItem -Path $path -Filter "$($Name)*.lnk" | Remove-Item -Force -Confirm:$false } else{ Get-ChildItem -Path $path -Filter "$($Name).lnk" | Remove-Item -Force -Confirm:$false } } #EndRegion '.\Public\Remove-Shortcut.ps1' 52 #Region '.\Public\Remove-TopdeskAssetAssignment.ps1' -1 <# .DESCRIPTION This cmdlet will remove a location assignment for an asset .PARAMETER asset The asset that we want to update with the assignments listed .PARAMETER locationID The location ID that we want to remove .EXAMPLE #> function Remove-TopdeskAssetAssignment{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][PSCustomObject]$asset, [Parameter(Mandatory = $true)][string]$locationID ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } if($null -eq $asset.'@assignments'.locations){ throw "Asset has no location assignments" } foreach($link in $asset.'@assignments'.locations){ if($link.branch.id -eq $locationID){ $uri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/assetmgmt/assets/$($asset.unid)/assignments/$($link.linkid)" Invoke-RestMethod -Method DELETE -Uri $uri -Headers $global:topdeskAccessToken -StatusCodeVariable statusCode } } } #EndRegion '.\Public\Remove-TopdeskAssetAssignment.ps1' 30 #Region '.\Public\Remove-WindowsAutoLogon.ps1' -1 <# .DESCRIPTION This is designed to disable windows autologon functionality. Originally from https://github.com/AdamGrossTX/ManagedUserManagement/blob/main/ClientScripts/Set-AutoLogon.ps1 by Adam Gross and https://github.com/mkht/DSCR_AutoLogon #> function Remove-WindowsAutoLogon { [cmdletbinding()] param () try { if (-not (Test-LocalAdmin)) { Write-Error ('Administrator privilege is required to execute this command') return } Add-PInvokeType Write-Output "Disabling AutoLogon" $WinLogonKey = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" Set-ItemProperty -Path registry::$WinLogonKey -Name "AutoAdminLogon" -Value 0 -ErrorAction SilentlyContinue Remove-ItemProperty -Path registry::$WinLogonKey -Name "DefaultPassword" -ErrorAction SilentlyContinue Remove-ItemProperty -Path registry::$WinLogonKey -Name "DefaultUserName" -ErrorAction SilentlyContinue $private:LsaUtil = New-Object PInvoke.LSAUtil.LSAutil -ArgumentList "DefaultPassword" if ($LsaUtil.GetSecret()) { $LsaUtil.SetSecret($null) #Clear existing password } Write-Verbose ('Auto logon has been disabled') } catch { throw $_ } } #EndRegion '.\Public\Remove-WindowsAutoLogon.ps1' 29 #Region '.\Public\Send-GraphMailMessage.ps1' -1 function Send-GraphMailMessage{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$from, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$subject, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$message, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string[]]$to, [Parameter()][ValidateNotNullOrEmpty()][string[]]$cc, [Parameter()][ValidateNotNullOrEmpty()][string[]]$bcc, [Parameter()][ValidateSet("html","text")][string]$contenttype = "html", [Parameter(Mandatory = $false)][switch]$savetosentitems ) # Confirm we have a valid graph token if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } $mailbody = @{ "message" = @{ "subject" = $subject "body" = @{ "contentType" = $contenttype "content" = $message } } "saveToSentItems" = $savetosentitems.IsPresent } # Loop through recipients and add them as appropriate $mailto = [System.Collections.Generic.List[Hashtable]]@() $mailcc = [System.Collections.Generic.List[Hashtable]]@() $mailbcc = [System.Collections.Generic.List[Hashtable]]@() foreach($item in $to){ $obj = @{ "emailAddress" = @{ "address" = $item } } $mailto.add($obj) | Out-Null } foreach($item in $cc){ $obj = @{ "emailAddress" = @{ "address" = $item } } $mailcc.add($obj) | Out-Null } foreach($item in $bcc){ $obj = @{ "emailAddress" = @{ "address" = $item } } $mailbcc.add($obj) | Out-Null } $mailbody.message.add("toRecipients",$mailto) if($mailcc){ $mailbody.message.add("ccRecipients",$mailcc) } if($mailbcc){ $mailbody.message.add("bccRecipients",$mailbcc) } # Mail endpoint $uri = "https://graph.microsoft.com/beta/users/$($from)/sendMail" # Get Graph Header $headers = Get-GraphHeader # Send Email Invoke-RestMethod -Method POST -Uri $uri -Headers $headers -Body ($mailbody | ConvertTo-Json -Depth 5) -StatusCodeVariable statuscode return $statuscode } #EndRegion '.\Public\Send-GraphMailMessage.ps1' 70 #Region '.\Public\Send-TeamsMessage.ps1' -1 function Send-TeamsMessage{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$webhook, [Parameter()][ValidateNotNullOrEmpty()][string]$text, [Parameter()][ValidateNotNullOrEmpty()][string]$summary, [Parameter()][ValidateNotNullOrEmpty()][string]$themeColor, [Parameter()][ValidateNotNullOrEmpty()][string]$title, [Parameter()][ValidateNotNullOrEmpty()][string]$activitytitle, [Parameter()][ValidateNotNullOrEmpty()][string]$activitysubtitle, [Parameter()][ValidateNotNullOrEmpty()][string]$activityimageuri, [Parameter()][ValidateNotNullOrEmpty()][string]$activitytext, [Parameter()][ValidateNotNullOrEmpty()][hashtable]$facts ) $card = @{ "@type" = "MessageCard" "@context" = "https://schema.org/extensions" } if($summary){ $card.Add("summary",$summary) | Out-Null } else{ $card.Add("text",$text) | Out-Null } if($themeColor){$card.Add("themeColor",$themeColor) | Out-Null} if($title){$card.Add("title",$title) | Out-Null} if($activitytitle -or $activitysubtitle -or $activityimageuri -or $activitytext -or $facts){ $section = @{} if($activitytitle){ $section.Add("activitytitle",$activitytitle) | Out-Null } if($activitysubtitle){ $section.Add("activitysubtitle",$activitysubtitle) | Out-Null } if($activityimageuri){ $section.Add("activityImage",$activityimageuri) | Out-Null } if($activitytext){ $section.Add("text",$activitytext) | Out-Null } if($facts){ $messageFacts = [System.Collections.Generic.List[Hashtable]]@() foreach($item in $facts.GetEnumerator()){ $obj = @{ "name" = $item.key "value" = $item.value } $messageFacts.Add($obj) | Out-Null } $section.Add("facts",$messageFacts) | Out-Null } $card.Add("sections",@($section)) | Out-Null } Invoke-RestMethod -uri $webhook -Method Post -body ($card | ConvertTo-Json -depth 5) -ContentType 'application/json' | Out-Null } #EndRegion '.\Public\Send-TeamsMessage.ps1' 57 #Region '.\Public\Set-AutorunRegKeys.ps1' -1 <# .DESCRIPTION This is designed to add autorun keys for the machine. Originally from https://github.com/AdamGrossTX/ManagedUserManagement/blob/main/ClientScripts/Set-AutoLogon.ps1 by Adam Gross #> function Set-AutorunRegKeys { [cmdletbinding()] param( [parameter(Mandatory = $true)][string]$Name, [parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$CommandLine, [parameter()][string]$UserName = $null, [parameter()][switch]$runOnce ) $forceload = $false # Get a list of all the user profiles on the machine $ProfileList = Get-ChildItem Registry::"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" | Where-Object { $_.Name -notlike "*_Classes" -and $_.PSChildName -notin ("S-1-5-18", "S-1-5-19", "S-1-5-20") } $UserList = foreach ($UserKey in $ProfileList) { @{ ProfileKey = $UserKey | Where-Object { $_.name -like "*" + $UserKey.PSChildName + "*" } UserName = try { ((([system.security.principal.securityidentIfier]$UserKey.PSChildName).Translate([System.Security.Principal.NTAccount])).ToString()).substring(3) } catch { continue }; SID = $UserKey.PSChildName ProfilePath = Get-ItemProperty $UserKey.PSPath | Select-Object -ExpandProperty ProfileImagePath } } if($null -ne $Username -and $UserName -ne ""){ $SID = ($UserList | Where-Object {$_.UserName -like "*$($UserName)*"}).SID $baseprofile = ($UserList | Where-Object {$_.UserName -like "*$($UserName)*"}).ProfilePath if($runOnce.IsPresent){ $registryPath = "REGISTRY::HKEY_USERS\$($SID)\Software\Microsoft\Windows\CurrentVersion\RunOnce" } else{ $registryPath = "REGISTRY::HKEY_USERS\$($SID)\Software\Microsoft\Windows\CurrentVersion\Run" } if(-not (Test-Path -Path $registryPath)){ $hivepath = Join-Path -Path $baseprofile -ChildPath "NTUSER.DAT" reg Load "HKU\$($SID)" "$($hivepath)" | Out-Null $forceload = $true if(-not (Test-Path -Path $registryPath)){ throw "Unable to load hive for user: $($UserName)" } } } else{ if($runOnce.IsPresent){ $registryPath = "REGISTRY::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce" } else{ $registryPath = "REGISTRY::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run" } } New-ItemProperty -Path $registryPath -Name $Name -Value $CommandLine if($forceload){ [gc]::Collect() reg unload "HKU\$($SID)" | Out-Null } } #EndRegion '.\Public\Set-AutorunRegKeys.ps1' 56 #Region '.\Public\Set-GraphAutopilotDeviceAssignment.ps1' -1 function Set-GraphAutopilotInformation { param ( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$deviceId, [Parameter()][ValidateNotNullOrEmpty()][string]$userPrincipalName, [Parameter()][ValidateNotNullOrEmpty()][string]$groupTag, [Parameter()][ValidateNotNullOrEmpty()][string]$deviceName ) if (!$(Test-GraphAcessToken $global:graphAccessToken)) { throw "Please Call Get-GraphAccessToken before calling this cmdlet" } $endpoint = "deviceManagement/windowsAutopilotDeviceIdentities/$($deviceId)/UpdateDeviceProperties" $headers = Get-GraphHeader $body = @{ } if($userPrincipalName){ $body.Add("userPrincipalName",$userPrincipalName) | Out-Null } if($groupTag){ $body.Add("groupTag",$groupTag) | Out-Null } if($deviceName){ $body.Add("displayName",$deviceName) | Out-Null } $uri = "https://graph.microsoft.com/beta/$($endpoint)" if($body.count -gt 0){ Invoke-RestMethod -Method "POST" -URI $uri -Headers $headers -Body ($body | ConvertTo-Json) -StatusCodeVariable "statusCode" } } #EndRegion '.\Public\Set-GraphAutopilotDeviceAssignment.ps1' 29 #Region '.\Public\Set-GraphIntuneDevicePrimaryUser.ps1' -1 function Set-GraphIntuneDevicePrimaryUser{ param( [parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$deviceId, [parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$userId ) if (!$(Test-GraphAcessToken $global:graphAccessToken)) { throw "Please Call Get-GraphAccessToken before calling this cmdlet" } $endpoint = "deviceManagement/managedDevices/$($deviceId)/users/`$ref" $headers = Get-GraphHeader $Body = @{ "@odata.id" = "https://graph.microsoft.com/beta/users/$($userId)" } $uri = "https://graph.microsoft.com/beta/$($endpoint)" Invoke-RestMethod -method "POST" -Uri $uri -Headers $headers -body ($body | ConvertTo-JSON) -StatusCodeVariable "statusCode" } #EndRegion '.\Public\Set-GraphIntuneDevicePrimaryUser.ps1' 17 #Region '.\Public\Set-GraphMailRead.ps1' -1 <# .DESCRIPTION This cmdlet is designed to mark a specific email as read .PARAMETER id The id of the mail message we are acting on .PARAMETER emailAddress The email address of the account that we are reading from #> function Set-GraphMailRead{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$id, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$emailAddress ) if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } $headers = Get-GraphHeader # Body Content $body = @{ "isRead" = $true } | ConvertTo-Json # Execute Graph Call $uri = "https://graph.microsoft.com/beta/users/$($emailAddress)/messages/$($id)" $results = Invoke-RestMethod -Method Patch -Uri $uri -Headers $headers -Body $body -StatusCodeVariable statusCode # Return Results if($statusCode -in (200,201)){ return $results } else{ throw "Unable to mark email as read." } } #EndRegion '.\Public\Set-GraphMailRead.ps1' 34 #Region '.\Public\Set-WindowsAutoLogon.ps1' -1 <# .DESCRIPTION This is designed to enable windows autologon functionality. Originally from https://github.com/AdamGrossTX/ManagedUserManagement/blob/main/ClientScripts/Set-AutoLogon.ps1 by Adam Gross and https://github.com/mkht/DSCR_AutoLogon #> function Set-WindowsAutoLogon { [cmdletBinding()] param( [parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCredential]$Credential ) try { if (-not (Test-LocalAdmin)) { Write-Error ('Administrator privilege is required to execute this command') return } Add-PInvokeType Write-Output "Enabling Autologon" $WinLogonKey = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" Set-ItemProperty -Path registry::$WinLogonKey -Name "AutoAdminLogon" -Value 1 -Force Set-ItemProperty -Path registry::$WinLogonKey -Name "DefaultUserName" -Value $Credential.UserName -Force Remove-ItemProperty -Path registry::$WinLogonKey -Name "AutoLogonCount" -ErrorAction SilentlyContinue Write-Verbose ('Password will be encrypted') Remove-ItemProperty -Path registry::$WinLogonKey -Name "DefaultPassword" -ErrorAction SilentlyContinue $private:LsaUtil = New-Object PInvoke.LSAUtil.LSAutil -ArgumentList "DefaultPassword" $LsaUtil.SetSecret($Credential.GetNetworkCredential().Password) Write-Verbose ('Auto logon has been enabled') } catch { throw $_ } } #EndRegion '.\Public\Set-WindowsAutoLogon.ps1' 31 #Region '.\Public\Test-AlertFactoryRule.ps1' -1 <# .DESCRIPTION This cmdlet is designed to test rules against emails that are in the mailbox for alert factory .PARAMETER rule The rule we are testing against .PARAMETER emails The emails that will be part of this incident #> function Test-AlertFactoryRule{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$rule, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][PSCustomObject]$emails ) foreach($obj in $rule.PSObject.Properties){ if($null -ne $obj.value.type){ switch($obj.name){ "sender" { $lookupField = "from.emailAddress.Address" } "body" { $lookupField = "body.content" } "subject" { $lookupField = "subject" } } switch($obj.value.type){ "equals" { $command = "[System.Collections.Generic.List[PSObject]]`$emails = `$emails | Where-Object {`$_.$($lookupField) -eq `"$($obj.value.expression)`"}" } "contains" { $command = "[System.Collections.Generic.List[PSObject]]`$emails = `$emails | Where-Object {`$_.$($lookupField) -match `"$($obj.value.expression)`"}" } "regex" { $command = "[System.Collections.Generic.List[PSObject]]`$emails = `$emails | Where-Object {`$_.$($lookupField) -match `"$($obj.value.expression)`"}" } } Invoke-Expression -Command $command } } return $emails } #EndRegion '.\Public\Test-AlertFactoryRule.ps1' 45 #Region '.\Public\Test-AllowedGroupMember.ps1' -1 # TEST (maybe split) <# .DESCRIPTION This cmdlet is used to check to see if a specific user belongs to a group that is passed .PARAMETER groupList Array of the groups to check .PARAMETER domain If active directory, what domain to check. If you use this, it ignores any of the Az parameters .PARAMETER AzAppRegistration The client id of the azure app registration working under .PARAMETER AzTenant The directory id for the Azure AD tenant .PARAMETER AzSecret The client secret used to connect to MS Graph .EXAMPLE Check for a specific user in active directory Test-AllowedGroupMember -userPrincipalName <UPN> -groupList @("GROUPNAME") -domain <DOMAIN> Check for a specific user in Azure AD group Test-AllowedGroupMember -userPrincipalName <UPN> -groupList @("GROUPNAME") -AzTenant $AzTenant -AzAppRegistration $AzAppRegistration -AzSecret $Secret #> function Test-AllowedGroupMember{ [CmdletBinding()] [OutputType([System.Boolean])] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$userPrincipalName, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][Object[]]$groupList, [Parameter()][string]$domain, [Parameter()][string]$AzAppRegistration, [Parameter()][string]$AzTenant, [Parameter()][pscredential]$AzSecret ) # Nested function to be able to recurse through groups in Azure AD since Get-MGGroupMembers does not have this function currently function getNestedMembers{ param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$groupId, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$userPrincipalName ) # Set found to false $results = $false # Get memberes of the group that was passed $members = Get-MgGroupMember -All -GroupId $groupId # If the username is found return true if($userPrincipalName -in $members.AdditionalProperties.userPrincipalName){ return $true } # If not found, check the list for other nested groups else{ $groups = $members | where-object {$_.AdditionalProperties.'@odata.type' -eq "#microsoft.graph.group"} # Loop through those groups those nested function foreach($group in $groups){ $results = getNestedMembers -groupId $groupId -userPrincipalName $userPrincipalName if($results -eq $true){ # if the results returned are true, return true. return $true } } } } # If set to query Azure AD Groups connect to MS Graph if($AzAppRegistration){ # Connect to MS Graph $msalToken = Get-MsalToken -clientID $AzAppRegistration -clientSecret $AzSecret.Password -tenantID $AzTenant Connect-MgGraph -AccessToken $msalToken.AccessToken | Out-Null } foreach($group in $groupList){ try{ if($domain){ # Get all the members and nested members of the group in Active Directory $members = Get-ADGroupMember -Recursive -Server $domain -Identity $group -ErrorAction SilentlyContinue | where-object {$_.objectClass -eq 'User'} | Get-ADUser | select-object UserPrincipalName # Check to see if the list contains the expected UPN and if return true if($members.UserPrincipalName -contains $userPrincipalName){ return $true } } else{ # Get the group from Azure AD $groups = Get-MGgroup -Filter "DisplayName eq '$($group)'" # Loop through if there are multiple groups with the same name foreach($group in $groups){ # Get the results of the function to recurse through the groups $results = getNestedMembers -groupId $group.id -userPrincipalName $userPrincipalName # Return true if correct if($results -eq $true){ return $true } } } } catch{ throw "An error occured while processing group $($group) : $($Error[0])" } } return $false } #EndRegion '.\Public\Test-AllowedGroupMember.ps1' 96 #Region '.\Public\Test-EntraIDDeviceRegistration.ps1' -1 <# .SYNOPSIS Determine if the device conforms to the requirement of being either Azure AD joined or Hybrid Azure AD joined. .DESCRIPTION Determine if the device conforms to the requirement of being either Azure AD joined or Hybrid Azure AD joined. .NOTES Author: Nickolaj Andersen Contact: @NickolajA Created: 2022-01-27 Updated: 2022-01-27 Version history: 1.0.0 - (2022-01-27) Function created #> function Test-EntraIDDeviceRegistration { [CmdletBinding()] param() $EntraIDJoinInfoRegistryKeyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\CloudDomainJoin\JoinInfo" if (Test-Path -Path $EntraIDJoinInfoRegistryKeyPath) { return $true } else { return $false } } #EndRegion '.\Public\Test-EntraIDDeviceRegistration.ps1' 28 #Region '.\Public\Test-GoogleAccessToken.ps1' -1 function Test-GoogleAccessToken{ [CmdletBinding()] param() if(-not $global:googleAccessToken){ throw "Please ensure that you have called Get-GoogleAccessToken cmdlet" } try{ $uri = "https://oauth2.googleapis.com/tokeninfo?access_token=$($Global:googleAccessToken)" $tokenDetails = Invoke-RestMethod -Method "GET" -URI $uri -StatusCodeVariable statusCode if([int]$tokenDetails.expires_in -gt 900){ Write-Verbose "Token is valid for more than 15 minutes, not getting new token." return $true } else{ Write-Verbose "Token is valid for less than 15 minutes, getting new token." return $false } } catch{ Write-Verbose "Unable to check token. Marking as needing refresh." return $false } "hello" } #EndRegion '.\Public\Test-GoogleAccessToken.ps1' 26 #Region '.\Public\Test-GraphAcessToken.ps1' -1 <# .DESCRIPTION This cmdlet is tests to see if the passed variable is not null, and expires in less than 10 minutes .PARAMETER token The current access tokenb variable #> function Test-GraphAcessToken{ [CmdletBinding()] param( [Parameter()][System.Object]$token ) if(!$token){ return $false } $expiryTime = $token.ExpiresOn - (Get-Date) if($expiryTime.Minutes -lt 10){ return $false } else{ return $true } } #EndRegion '.\Public\Test-GraphAcessToken.ps1' 23 #Region '.\Public\Test-GraphIntuneAPNCertificate.ps1' -1 function Test-GraphIntuneAPNCertificate{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$days, [Parameter()][ValidateNotNullOrEmpty()][string]$emailfrom, [Parameter()][ValidateNotNullOrEmpty()][string]$emailto, [Parameter()][ValidateNotNullOrEmpty()][string]$teamswebhook ) if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # Get the APN Certificate $APNCertificate = Get-GraphIntuneAPNCertificate # Times for Certificate $APNExpDate = $APNCertificate.expirationDateTime $APNIdentifier = $APNCertificate.appleIdentifier $APNExpShortDate = $APNExpDate.ToShortDateString() # Set Status as null $APNExpirationStatus = $null # If the certificate has already expired if ($APNExpDate -lt (Get-Date)) { $APNExpirationStatus = "Apple MDM Push certificate has already expired" } else{ $APNDaysLeft = ($APNExpDate - (Get-Date)) if ($APNDaysLeft.Days -le $APNCertificateDays) { $APNExpirationStatus = "Apple MDM Push certificate expires in $($APNDaysLeft.Days) days" } } if($APNExpirationStatus -and $teamswebhook){ $message = @{ "webhook" = $teamswebhook "summary" = "Intune Apple Notification" "activityimageuri" = "https://dev.azure.com/jeremyputman/71648e28-a38f-4acc-bafa-aa569ba7b3f8/_apis/git/repositories/d80cf88f-316e-4d8d-b8f5-d40311acc791/items?path=/Resources/Images/warning.png&%24format=octetStream" "title" = $APNExpirationStatus "activitytitle" = "Action Required!" "activitytext" = "Must be renewed by IT Admin before the expiry date." "facts" = [ordered]@{ "Connector:" = "Apple Push Notification Certificate" "Status:" = $APNExpirationStatus "AppleID:" = $APNIdentifier "Expiry Date:" = $APNExpShortDate } } Send-TeamsMessage @message | Out-Null } if($APNExpirationStatus -and $emailfrom -and $emailto){ $message = @{ "from" = $emailfrom "to" = $emailto "subject" = $APNExpirationStatus "savetosentitems" = $true "message" = " <strong style='font-size:14px;'>Action Required!</strong><br/><br/> <strong>Connector:</strong> Apple Push Notification Certificate<br/> <strong>Status:</strong> $($APNExpirationStatus)<br/> <strong>AppleID:</strong> $($APNIdentifier)<br/> <strong>Expiry Date:</strong> $($APNExpShortDate)<br/> " } Send-GraphMailMessage @message | Out-Null } return $APNExpirationStatus } #EndRegion '.\Public\Test-GraphIntuneAPNCertificate.ps1' 65 #Region '.\Public\Test-GraphIntuneDEPCertificate.ps1' -1 function Test-GraphIntuneDEPCertificate{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$days, [Parameter()][ValidateNotNullOrEmpty()][string]$emailfrom, [Parameter()][ValidateNotNullOrEmpty()][string]$emailto, [Parameter()][ValidateNotNullOrEmpty()][string]$teamswebhook ) if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # List of Alerts $alerts = [System.Collections.Generic.List[Hashtable]]@() # Get DEP Tokens $DEPTokens = Get-GraphIntuneDEPCertificate foreach($DEP in $DEPTokens){ $DEPTokenDaysLeft = ($DEP.tokenExpirationDateTime - (Get-Date)) if ($DEPTokenDaysLeft.Days -le $days) { $obj = [ordered]@{ "Status" = "Apple DEP Token expires in $($DEPTokenDaysLeft.Days) days" "appleid" = $DEP.appleIdentifier "expiry" = $DEP.tokenExpirationDateTime.ToShortDateString() "displayname" = $DEP.tokenName } $alerts.add($obj) | Out-Null } } if($alerts -and $teamswebhook){ foreach($item in $alerts){ $message = @{ "webhook" = $teamswebhook "summary" = "Intune Apple Notification" "activityimageuri" = "https://dev.azure.com/jeremyputman/71648e28-a38f-4acc-bafa-aa569ba7b3f8/_apis/git/repositories/d80cf88f-316e-4d8d-b8f5-d40311acc791/items?path=/Resources/Images/warning.png&%24format=octetStream" "title" = $item.Status "activitytitle" = "Action Required!" "activitytext" = "Must be renewed by IT Admin before the expiry date." "facts" = [ordered]@{ "Connector:" = "Apple DEP Token" "Status:" = $item.Status "Display Name:" = $item.displayname "AppleID:" = $item.appleid "Expiry Date:" = $item.expiry } } Send-TeamsMessage @message | Out-Null } } if($alerts -and $emailfrom -and $emailto){ foreach($item in $alerts){ $message = @{ "from" = $emailfrom "to" = $emailto "subject" = $item.Status "savetosentitems" = $true "message" = " <h2>Action Required!</h2> <strong>Connector:</strong> Apple DEP Token<br/> <strong>Status:</strong> $($item.Status)<br/> <strong>Display Name:</strong> $($item.displayname)<br/> <strong>AppleID:</strong> $($item.appleid)<br/> <strong>Expiry Date:</strong> $($item.expiry)<br/> " } Send-GraphMailMessage @message | Out-Null } } return $alerts } #EndRegion '.\Public\Test-GraphIntuneDEPCertificate.ps1' 69 #Region '.\Public\Test-GraphIntuneLicense.ps1' -1 function Test-GraphIntuneLicense{ [CmdletBinding()] param( [parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$userId ) if (!$(Test-GraphAcessToken $global:graphAccessToken)) { throw "Please Call Get-GraphAccessToken before calling this cmdlet" } $endpoint = "deviceAppManagement/managedAppStatuses('userstatus')?userId=$($userId)" $headers = Get-GraphHeader $uri = "https://graph.microsoft.com/beta/$($endpoint)" $result = Invoke-RestMethod -Method "GET" -URI $uri -Headers $headers -StatusCodeVariable "statusCode" $license = $result.content.validationStatuses | Where-Object { $_.validationName -eq 'Intune License' } if($license.State -eq 'Pass'){ return $true } else{ return $false } } #EndRegion '.\Public\Test-GraphIntuneLicense.ps1' 21 #Region '.\Public\Test-GraphIntuneVPPCertificate.ps1' -1 function Test-GraphIntuneVPPCertificate{ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$days, [Parameter()][ValidateNotNullOrEmpty()][string]$emailfrom, [Parameter()][ValidateNotNullOrEmpty()][string]$emailto, [Parameter()][ValidateNotNullOrEmpty()][string]$teamswebhook ) if(!$(Test-GraphAcessToken $global:graphAccessToken)){ throw "Please Call Get-GraphAccessToken before calling this cmdlet" } # List of Alerts $alerts = [System.Collections.Generic.List[Hashtable]]@() # Get VPP Tokens $VPPTokens = Get-GraphIntuneVPPCertificate foreach($VPP in $VPPTokens){ # Check if the token is current valid if ($VPP.state -ne 'valid') { $obj = [ordered]@{ "Status" = "Apple VPP Token is not valid, new token required" "appleid" = $VPP.appleId "expiry" = $VPP.expirationDateTime.ToShortDateString() "displayname" = $VPP.displayName } $alerts.add($obj) | Out-Null } else{ $VPPTokenDaysLeft = ($VPP.expirationDateTime - (Get-Date)) if ($VPPTokenDaysLeft.Days -le $days) { $obj = [ordered]@{ "Status" = "Apple VPP Token expires in $($VPPTokenDaysLeft.Days) days" "appleid" = $VPP.appleId "expiry" = $VPP.expirationDateTime.ToShortDateString() "displayname" = $VPP.displayName } $alerts.add($obj) | Out-Null } } } if($alerts -and $teamswebhook){ foreach($item in $alerts){ $message = @{ "webhook" = $teamswebhook "summary" = "Intune Apple Notification" "activityimageuri" = "https://dev.azure.com/jeremyputman/71648e28-a38f-4acc-bafa-aa569ba7b3f8/_apis/git/repositories/d80cf88f-316e-4d8d-b8f5-d40311acc791/items?path=/Resources/Images/warning.png&%24format=octetStream" "title" = $item.Status "activitytitle" = "Action Required!" "activitytext" = "Must be renewed by IT Admin before the expiry date." "facts" = [ordered]@{ "Connector:" = "Apple VPP Token" "Status:" = $item.Status "Display Name:" = $item.displayname "AppleID:" = $item.appleid "Expiry Date:" = $item.expiry } } Send-TeamsMessage @message | Out-Null } } if($alerts -and $emailfrom -and $emailto){ foreach($item in $alerts){ $message = @{ "from" = $emailfrom "to" = $emailto "subject" = $item.Status "savetosentitems" = $true "message" = " <h2>Action Required!</h2> <strong>Connector:</strong> Apple VPP Token<br/> <strong>Status:</strong> $($item.Status)<br/> <strong>Display Name:</strong> $($item.displayname)<br/> <strong>AppleID:</strong> $($item.appleid)<br/> <strong>Expiry Date:</strong> $($item.expiry)<br/> " } Send-GraphMailMessage @message | Out-Null } } return $alerts } #EndRegion '.\Public\Test-GraphIntuneVPPCertificate.ps1' 81 #Region '.\Public\Test-LocalAdmin.ps1' -1 function Test-LocalAdmin{ [CmdletBinding()] param () try{ return ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator') } catch { throw $_ } } #EndRegion '.\Public\Test-LocalAdmin.ps1' 11 #Region '.\Public\Test-SamAccountName.ps1' -1 <# .DESCRIPTION This cmdlet is designed to check Active Directory for a valid samAccountName when creating/changing user. .PARAMETER samAccountName The account name we want to test for .PARAMETER server The server that we want to test against .EXAMPLE Check for a specific samAccountName Test-SamAccountName -samAccountName <NAME> -server <SERVER> #> function Test-SamAccountName{ [CmdletBinding()] [OutputType([System.String],[System.Boolean])] param( [Parameter(Mandatory = $true)]$samAccountName, [Parameter(Mandatory = $true)]$server ) # Default Addition at the end of the name if it exists. $postFix = 2 # Loop through to try to find a valid samAccountName or fail if loops too many times do{ try{ # Check to see if the user already exists. Get-ADUser -Identity $samAccountName -Server $server | Out-Null # If it does exist, then add the postfix if($postFix -eq 2){ $samAccountName = "$($samAccountName)$($postFix)" } # If postfix is greater than default, then remove it (as we max at 9) to add the new postfix else{ $samAccountName = "$($samAccountName.substring(0,$samAccountName.length -1))$($postFix)" } } # If the account doesn't exist, return the samAccountName as good catch [Microsoft.ActiveDirectory.Management.ADIdentityResolutionException] { return $samAccountName } catch { throw $Error[0] } $postFix++ }while($postFix -lt 10) # Return false if we couldn't find a valid samAccountName we could use return $false } #EndRegion '.\Public\Test-SamAccountName.ps1' 47 #Region '.\Public\Update-TopdeskAsset.ps1' -1 <# .DESCRIPTION This cmdlet will update an asset .PARAMETER unid The unid of the asset that we want to update .PARAMETER update A hashtabe of the values for the change .EXAMPLE #> function Update-TopdeskAsset(){ [CmdletBinding()] param( [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$unid, [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][hashtable]$update ) # Confirm we have a valid graph token if(!$global:topdeskAccessToken){ throw "Please Call Connect-Topdesk before calling this cmdlet" } # Base URI for subcategory data $uri = "https://$($global:topdeskEnvironment).topdesk.net/tas/api/assetmgmt/assets/$($unid)" try{ $results = Invoke-RestMethod -Method Post -Uri $uri -Headers $global:topdeskAccessToken -body ($update | ConvertTo-Json) -StatusCodeVariable statusCode } catch{ throw $Error[0] } return $results } #EndRegion '.\Public\Update-TopdeskAsset.ps1' 30 |