ProvisionIntuneDevicesV5.ps1

#Requires -Modules @{ModuleName="Microsoft.Graph.Intune";ModuleVersion = "6.1907.1.1"},@{ModuleName="AzureADPreview";ModuleVersion = "2.0.2.62"}

<#PSScriptInfo
 
.VERSION 1.7
 
.GUID e0a0afb3-a115-4254-93a0-e8d4c10cf34d
 
.AUTHOR Raúl Carboneras. racarb@microsoft.com
 
.COMPANYNAME Microsoft
 
.COPYRIGHT Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information.
 
.TAGS Intune RBAC Automation
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
    Microsoft.Graph.Intune 6.1907.1.0
    AzureADPreview 2.0.2.62
    Az.OperationalInsights 1.3.4
    AZ.Accounts 1.7.1
 
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
.PRIVATEDATA
 
#>


<#
.DESCRIPTION
    This script checks for the new devices registered in the last x hours and automatically creates objects in Intune for their management using Intune roles
     
    These are the objects created by this script:
     
     
    AdminRole-OB. Intune Role created for users OB, with specific permissions copied from a custom Role
     
    RoleAssigment-OB. Role assignment of the role AdminRole-OB.
     
         
    The assignment contains the following:
     
     
    AdminGroup-OB. Scope (Groups). Group that contains the users that has the assignment of that OB. This group will have permission to administer the devices
     
    ScopeGroup-OB. Members. Scope Dynamic group that contains all the users that has the ‘State’ AAD field filled with the OB’s name. This group is the scope of the assignment
     
    Tag-TG. Scope (Tags). Tag created for every OB. The assignment has this Tag as its scope.
     
     
     
    The script generates logs that are stored in an storage account in Azure as well as uploaded to a workspace in Log Analytics.
     
    This script in intended to be run as a runbook in Azure
     
    References
    https://github.com/Azure/azure-powershell/issues/8486 No way to read passord with the Get-AzAutomationCredential
    https://github.com/microsoft/Intune-PowerShell-SDK/blob/master/README.md
    https://github.com/microsoft/Intune-PowerShell-Management/tree/master/Samples
     
    OB Office Branch
    Required modules:
          Microsoft.Graph.Intune 6.1907.1.0
          AzureADPreview 2.0.2.62
          Az.OperationalInsights 1.3.4
          AZ.Accounts 1.7.1
 
.PARAMETER Hours
The time frame in hours used to search for registered devices
 
#>

Param (
    [int]$Hours = "6",
    [string]$ADField = 'State',
    [boolean]$LoginAzureMonitor=$true)

############################### INPUT SECTION ###############################

# Naming conventions - Prefixes and Sufixes for the OB Name . eg. : <TagPre><OB Name><TagSuf>
$TagPre = 'Tag-'
$TagSuf = $null

$AdminRolePre = 'AdminRole-'
$AdminRoleSuf = $null

$AdminGroupPre = 'AdminGroup-'
$AdminGroupSuf = $null

$ScopeGroupPre = 'ScopeGroup-'
$ScopeGroupSuf = $null

$RoleAssignmentPre = 'RoleAssignment-'
$RoleAssignmentSuf = $null

# Companies list. Array of companies from where devices are provisioned. Add the desired companies here.
# $Companies = @(
# "TG",
# "WH"
# )


#Values

$ResourceGroupName = "IntuneProvisioning"           # Name of the Resource Group
$LAworkspaceName = "IntuneProvisioningla"           # Log Analytics Workspace Name
$LAworkspaceKeyvariable = "ProvisioninglaKey"       # Name of the variable where the Log Analytics Primary Key is stored
$StorageAccountName = "intuneprovisioningsa"        # Name of the Storage Account
$ShareName = "provisioning"                         # Name of the Azure File Share
$StorageAccountKeyvariable = "ProvisioningsaKey"    # Name of the variable where the Storage Account Primary Key is stored
$InTuneCredname = "InTuneProvisioningCred"          # Name of the Credential Asset where the Intune Credential is stored
$MSGraphSchema = "beta"                             # Version of the Graph API

#Variables

$StorageAccountKey = Get-AutomationVariable -Name $StorageAccountKeyvariable
$LAworkspaceKey = Get-AutomationVariable -Name $LAworkspaceKeyvariable


############################### END INPUT SECTION ###############################



#region Function Definitions
Function Log-Event {
    Param (
        $Text
    )
    Write-Output $Text
    if (-not(Test-Path $logpath))
    { New-Item -ItemType File -Path $logpath }
    $text | Out-File -FilePath $logpath -Append
}

Function Log-ErrorEvent {
    Param (
        $Text
    )
    Write-Error $Text

}

<#
.DESCRIPTION
   This function creates and updates a hashtable using hashtable name, keys and values as parameters
.EXAMPLE
   Write-ResultstoHashTable -Hashtablename ResultsHashTable -Key users -Value $listofusers
 
#>

Function Write-ResultstoHashTable {
    Param(
        [string]$Hashtablename,
        [string]$Key,
        [string]$Value)
    
    $hashtableobj = (Get-Variable -Name $Hashtablename).Value
    
    if (($hashtableobj).ContainsKey($key)) {
        $tempvalue = $hashtableobj[$($key)]
        $tempvalue += $value
        $hashtableobj[$($key)] = $tempvalue
    }
    else {
        $hashtableobj[$($key)] = @($value)
    }
    
}

<#
.DESCRIPTION
   This function Checks if there is already a tag for the OB and returns $true or $false
.EXAMPLE
   Test-CompanyScopeTag -Company Companyname
 
#>

Function Test-CompanyScopeTag {

    [CmdletBinding()]
    Param ([string]$Company)
    $CompanyExists = $false
    Update-MSGraphEnvironment -SchemaVersion $MSGraphSchema -Quiet | Out-Null
    $uri = "deviceManagement/roleScopeTags"
    try {
        $ScopeTags = Invoke-MSGraphRequest -HttpMethod GET -Url $uri
        if ("$($TagPre)$($Company)$($TagSuf)" -in ($ScopeTags.value).displayname) { $CompanyExists = $true }
        else { $CompanyExists = $false }
    }
    catch { $($_.exception.message); Log-ErrorEvent "An error ocurred"; exit }
    return $CompanyExists

}

<#
.DESCRIPTION
    This function returns the Tag for a OB. If no OB is specified, it returns all the Tags
 
.EXAMPLE
    Get-CompanyScopeTag
 Returns all the tags
          
.EXAMPLE
    Get-CompanyScopeTag -Company OB
 Returns the tag for OB 'Companyname'
         
 
#>

Function Get-CompanyScopeTag {

    [CmdletBinding()]
    Param ([string]$Company)
    Update-MSGraphEnvironment -SchemaVersion $MSGraphSchema -Quiet | Out-Null
    $uri = "deviceManagement/roleScopeTags"
    try {
        $ScopeTags = Invoke-MSGraphRequest -HttpMethod GET -Url $uri
    }
    catch { Write-Error "$($_.exception.message)"; Write-Progress "An error ocurred"; exit }
    if ($PSBoundParameters.ContainsKey("Company")) { return $ScopeTags.value | where displayname -EQ "$($TagPre)$($Company)$($TagSuf)" }
    else { return $ScopeTags.value }

}

<#
.DESCRIPTION
   This function creates a new Tag for a OB
.EXAMPLE
   New-CompanyScopeTag -Company OB
 
#>

Function New-CompanyScopeTag {
    [CmdletBinding()]
    Param ([string]$Company)
    Update-MSGraphEnvironment -SchemaVersion $MSGraphSchema -Quiet | Out-Null

    $ScopeTagJSON = @"
        {
            "displayName": "$($TagPre)$($Company)$($TagSuf)",
            "description": "Tag for OB $Company",
            "isBuiltIn": false
        }
"@


    try { $NewTag = Invoke-MSGraphRequest -HttpMethod POST -Url "deviceManagement/roleScopeTags" -Content $ScopeTagJson }
    catch { Write-Error "$($_.exception.message)"; Write-Progress "An error ocurred"; exit }
    return $NewTag

}

<#
.DESCRIPTION
   This function Checks if there is already a Role Definition for the OB and returns $true or $false
.EXAMPLE
   Test-CompanyRBACRole -Company OB
 
#>

Function Test-CompanyRBACRole() {

    [cmdletbinding()]

    param
    (
        [string]$Company
    )


    Update-MSGraphEnvironment -SchemaVersion $MSGraphSchema -Quiet | Out-Null
    $uri = "deviceManagement/roleDefinitions"
    
    try {

        $CompanyRBACRoles = (Invoke-MSGraphRequest -Url $uri -HttpMethod GET).value
    }
    
    catch {

        $ex = $_.Exception
        $errorResponse = $ex.Response.GetResponseStream()
        $reader = New-Object System.IO.StreamReader($errorResponse)
        $reader.BaseStream.Position = 0
        $reader.DiscardBufferedData()
        $responseBody = $reader.ReadToEnd();
        Write-Host "Response content:`n$responseBody" -f Red
        Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
        write-host
        break
    }
    If ("$($AdminRolePre)$($Company)$($AdminRoleSuf)" -notin ($CompanyRBACRoles).Displayname) { return $false }
    else { return $true }

}

<#
.DESCRIPTION
The function returns the Role definition object for a given OB. If no OB is specified it returns all the Roles
.EXAMPLE
Get-CompanyRBACRole
This example returns all the Roles
 
.EXAMPLE
Get-CompanyRBACRole -Company OB
This example returns Role defined for OB 'OB'
 
#>

Function Get-CompanyRBACRole() {

    [cmdletbinding()]

    param
    (
        [string]$Company
    )

    Update-MSGraphEnvironment -SchemaVersion $MSGraphSchema -Quiet | Out-Null
    $uri = "deviceManagement/roleDefinitions"
    
    try {

        $CompanyRBACRoles = Invoke-MSGraphRequest -Url $uri -HttpMethod GET
    }
    
    catch {

        $ex = $_.Exception
        $errorResponse = $ex.Response.GetResponseStream()
        $reader = New-Object System.IO.StreamReader($errorResponse)
        $reader.BaseStream.Position = 0
        $reader.DiscardBufferedData()
        $responseBody = $reader.ReadToEnd();
        Write-Host "Response content:`n$responseBody" -f Red
        Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
        write-host
        break
    }
    
    if ($PSBoundParameters.ContainsKey("Company")) { return $CompanyRBACRoles.value | where displayname -EQ "$($AdminRolePre)$($Company)$($AdminRoleSuf)" }
    else { return $CompanyRBACRoles.value | where isBuiltIn -eq $False }

}

<#
.DESCRIPTION
   This function creates a new Role Definition for a given OB
.EXAMPLE
   New-CompanyRBACRole -Company OB
 
#>

Function New-CompanyRBACRole {
    
    [CmdletBinding()]
    Param ([string]$Company)

    Update-MSGraphEnvironment -SchemaVersion $MSGraphSchema -Quiet | Out-Null
    $uri = "deviceManagement/roleDefinitions"

    if (-not (Test-CompanyScopeTag -Company $Company)) { Log-ErrorEvent "Scope tag for OB $Company doesn't exists"; break }
    [string]$CompanyScopeTagid = (Get-CompanyScopeTag -Company $Company).id
    $RBACRoleJSON = @"
        {
            "@odata.type": "#microsoft.graph.deviceAndAppManagementRoleDefinition",
            "displayName": "$($AdminRolePre)$($Company)$($AdminRoleSuf)",
            "description": "Admin Role automatically created for OB $Company",
            "isBuiltIn": false,
            "roleScopeTagIds": [ "$($CompanyScopeTagid)" ],
            "rolePermissions": [
                {
                    "resourceActions": [
                        {
                            "allowedResourceActions": [
                                "Microsoft.Intune_AndroidSync_Read",
                                "Microsoft.Intune_CorporateDeviceIdentifiers_Read",
                                "Microsoft.Intune_CorporateDeviceIdentifiers_Create",
                                "Microsoft.Intune_DeviceCompliancePolices_Read",
                                "Microsoft.Intune_DeviceConfigurations_Read",
                                "Microsoft.Intune_DeviceEnrollmentManagers_Read",
                                "Microsoft.Intune_EndpointProtection_Read",
                                "Microsoft.Intune_AppleDeviceSerialNumbers_Read",
                                "Microsoft.Intune_AppleEnrollmentProfiles_Read",
                                "Microsoft.Intune_EnrollmentProgramToken_Read",
                                "Microsoft.Intune_ManagedApps_Read",
                                "Microsoft.Intune_ManagedApps_Wipe",
                                "Microsoft.Intune_ManagedDevices_Delete",
                                "Microsoft.Intune_ManagedDevices_Read",
                                "Microsoft.Intune_ManagedDevices_Update",
                                "Microsoft.Intune_MobileApps_Read",
                                "Microsoft.Intune_Organization_Read",
                                "Microsoft.Intune_RemoteAssistance_Read",
                                "Microsoft.Intune_RemoteTasks_CleanPC",
                                "Microsoft.Intune_RemoteTasks_DisableLostMode",
                                "Microsoft.Intune_RemoteTasks_EnableLostMode",
                                "Microsoft.Intune_RemoteTasks_EnableWindowsIntuneAgent",
                                "Microsoft.Intune_RemoteTasks_GetFileVaultKey",
                                "Microsoft.Intune_RemoteTasks_LocateDevice",
                                "Microsoft.Intune_RemoteTasks_ManageSharedDeviceUsers",
                                "Microsoft.Intune_RemoteTasks_PlayLostModeSound",
                                "Microsoft.Intune_RemoteTasks_RebootNow",
                                "Microsoft.Intune_RemoteTasks_RemoteLock",
                                "Microsoft.Intune_RemoteTasks_RequestRemoteAssistance",
                                "Microsoft.Intune_RemoteTasks_ResetPasscode",
                                "Microsoft.Intune_RemoteTasks_Retire",
                                "Microsoft.Intune_RemoteTasks_RevokeAppleVppLicenses",
                                "Microsoft.Intune_RemoteTasks_RotateBitLockerKeys",
                                "Microsoft.Intune_RemoteTasks_RotateFileVaultKey",
                                "Microsoft.Intune_RemoteTasks_SetDeviceName",
                                "Microsoft.Intune_RemoteTasks_ShutDown",
                                "Microsoft.Intune_RemoteTasks_SyncDevice",
                                "Microsoft.Intune_RemoteTasks_UpdateDeviceAccount",
                                "Microsoft.Intune_RemoteTasks_WindowsDefender",
                                "Microsoft.Intune_RemoteTasks_Wipe",
                                "Microsoft.Intune_Roles_Read",
                                "Microsoft.Intune_SecurityBaselines_Read",
                                "Microsoft.Intune_TelecomExpenses_Read",
                                "Microsoft.Intune_TermsAndConditions_Read"
                            ],
                            "notAllowedResourceActions": []
                        }
                    ]
                }
            ]
        }
"@


    try { $CompanyRBACRole = Invoke-MSGraphRequest -HttpMethod POST -Url $uri -Content $RBACRoleJSON }
    catch { $($_.exception.message); Log-ErrorEvent "An error ocurred"; break }
    return $CompanyRBACRole
}

<#
.DESCRIPTION
   This function Checks if there is already a Role Assignment for the OB and returns $true or $false
.EXAMPLE
   Test-RoleAssignment -Company OB
 
#>

Function Test-RoleAssignment {

    [CmdletBinding()]
    Param (
        [string]$Company
    )

    $RoleAssignments = Get-RoleAssignment -Company $Company
    if ("$($RoleAssignmentPre)$($Company)$($RoleAssignmentSuf)" -notin @($RoleAssignments.displayname)) { return $false }
    else { return $true }
}

<#
.DESCRIPTION
The function returns the Assignments defined for a given OB
.EXAMPLE
Get-RoleAssignment -Company OB
This example returns the Assignments defined for OB 'OB'
 
#>

Function Get-RoleAssignment {
    [CmdletBinding()]

    Param(
        [Parameter(Mandatory = $true)]
        $Company)

    Update-MSGraphEnvironment -SchemaVersion $MSGraphSchema -Quiet | Out-Null
    $resource = "deviceManagement/roleDefinitions"
    
    try {
        $CompanyRBACRoleid = (Get-CompanyRBACRole -Company $company).id

        Update-MSGraphEnvironment -SchemaVersion $MSGraphSchema -Quiet | Out-Null

    
        $RoleAssignments = (Invoke-MSGraphRequest -HttpMethod GET -Url "$resource('$($CompanyRBACRoleid)')?`$expand=roleassignments").roleAssignments
   
    }
    catch {
    
        $ex = $_.Exception
        $errorResponse = $ex.Response.GetResponseStream()
        $reader = New-Object System.IO.StreamReader($errorResponse)
        $reader.BaseStream.Position = 0
        $reader.DiscardBufferedData()
        $responseBody = $reader.ReadToEnd();
        Write-Host "Response content:`n$responseBody" -f Red
        Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
        write-host
        break
    
    }

    return $RoleAssignments
    
}

<#
.DESCRIPTION
   This function creates a new Role Assignment for a given OB
.EXAMPLE
   New-CompanyScRoleAssignment -Company OB
 
#>

Function New-RoleAssignment {
     
    [Cmdletbinding()]

    Param (
        [string]$Company,
        [string]$DisplayName = "$($RoleAssignmentPre)$($Company)$($RoleAssignmentSuf)",
        [string]$Description = "Assignment automaticaly created for OB $($Company)",
        [string]$AdminGroupname = "$($AdminGroupPre)$($Company)$($AdminGroupSuf)", #Members
        [string]$ScopeGroupname = "$($ScopeGroupPre)$($Company)$($ScopeGroupSuf)", #Scope (Groups)
        [string]$RoledefinitionName = "$($AdminRolePre)$($Company)$($AdminRoleSuf)"
    )
    $uri = "deviceManagement/roleAssignments"

    $AdminGroupid = (Get-AzureADGroup -Filter "DisplayName eq '$($AdminGroupname)'" ).Objectid
    if ($null -eq $AdminGroupid) { Log-ErrorEvent "No Admin Group exists for OB $company"; break }

    $UsersGroupid = (Get-AzureADGroup -Filter "DisplayName eq '$($ScopeGroupname)'" ).Objectid
    if ($null -eq $UsersGroupid) { Log-ErrorEvent "No Scope Group exists for OB $company"; break }

    $Roledefinitionid = (Get-CompanyRBACRole -Company $Company).id
    $ScopeTagid = (Get-CompanyScopeTag -Company $Company).id
    if ($Roledefinitionid -eq $false) { Log-Event -Text "Role Definition $($AdminRolePre)$($Company)$($AdminRoleSuf) not found"; break }
    $JSONRoleAssignment = @"
        {
            "description": "$($Description)",
            "displayName": "$($DisplayName)",
            "members": ["$($AdminGroupid)"],
            "scopeMembers": ["$($UsersGroupid)"],
            "roleDefinition@odata.bind":"https://graph.microsoft.com/beta/deviceManagement/roleDefinitions/$($Roledefinitionid)"
        }
"@

    $JSONRoleAssignment 

    Update-MSGraphEnvironment -SchemaVersion $MSGraphSchema -Quiet | Out-Null
    Connect-MSGraph -AdminConsent | Out-Null
    try {
        $RoleAssignment = Invoke-MSGraphRequest -HttpMethod POST -Url $uri -Content $JSONRoleAssignment

        #Update the scope Tag

        Update-RoleAssignmentScopeTag -Company $Company -roleScopeTagids $ScopeTagid

    }
    catch {
        $ex = $_.Exception
        $ex
        $errorResponse = $ex.Response.GetResponseStream()
        $reader = New-Object System.IO.StreamReader($errorResponse)
        $reader.BaseStream.Position = 0
        $reader.DiscardBufferedData()
        $responseBody = $reader.ReadToEnd();
        Write-Host "Response content:`n$responseBody" -f Red
        Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
        write-host
        break
    }

    return $RoleAssignment
}

<#
.DESCRIPTION
   This function updates the scope tag for an assignment
.PARAMETER ids
   Array of ids to set
.EXAMPLE
   Update-RoleAssignmentScopeTag -Company OB -RoleScopeTagids ids
 
   This example updates the scope tags ids for the Assignment for OB 'OB'
 
#>

Function Update-RoleAssignmentScopeTag {
    [CmdletBinding()]
    Param (
        [string]$Company,
        [int[]]$RoleScopeTagids)


    $RoleAssignmentid = (Get-RoleAssignment -Company $company).id
    $seturl = "https://graph.microsoft.com/beta/deviceManagement/roleAssignments/$RoleAssignmentid/roleScopeTags/`$ref"
    foreach ($roleScopeTagid in $roleScopeTagids) {

        $putjson = @"
{
  "@odata.id": "https://graph.microsoft.com/beta/deviceManagement/roleScopeTags/$roleScopeTagid"
}
"@

        try { Invoke-MSGraphRequest -HttpMethod POST -Url $seturl -Content $putjson -Verbose }
        catch { }
    }

}

<#
.DESCRIPTION
   This function updates the scope tag for a device
.PARAMETER Deviceid
   Id of the Device
.PARAMETER ScopeTagids
   Array of ids to set
.EXAMPLE
   Update-DeviceScopeTag -Deviceid $deviceid -ScopeTagids ids
 
   This example updates the scope tags ids for the Device specified with its id.
 
#>

Function Update-DeviceScopeTag {
    [CmdletBinding()]
    Param (
        [string]$Deviceid,
        [int[]]$ScopeTagids)

    Update-MSGraphEnvironment -SchemaVersion $MSGraphSchema -Quiet | Out-Null
    $seturl = "deviceManagement/managedDevices('$($Deviceid)')"

    [string]$ScopeTagidsstring = '"' + ($ScopeTagids -join '","') + '"'

    $putjson = @"
    {
        "roleScopeTagIds": [ $($ScopeTagidsstring) ]
    }
"@


    try { Invoke-MSGraphRequest -HttpMethod PATCH -Url $seturl -Content $putjson -Verbose }
    catch { $_.Exception }
    

}



#endregion


#region authentication

#Connection to Azure
$connectionName = "AzureRunAsConnection"
$servicePrincipalConnection = Get-AutomationConnection -Name $connectionName         
Add-AzAccount `
    -ServicePrincipal `
    -TenantId $servicePrincipalConnection.TenantId `
    -ApplicationId $servicePrincipalConnection.ApplicationId `
    -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint 

#Intune Validation

$IntuneCred = Get-AutomationPSCredential -Name $InTuneCredname
Connect-MSGraph -Credential $IntuneCred -Verbose
Update-MSGraphEnvironment -SchemaVersion $MSGraphSchema -Quiet | Out-Null
Connect-MSGraph -AdminConsent

#View MSGraph environment
Get-MSGraphEnvironment
#Change to beta API: Update-MSGraphEnvironment -SchemaVersion "beta"

#Connect to Azure AD

Connect-AzureAD -Credential $IntuneCred



#endregion


#TODO: Time out control

#DONE: Beter Management of the API Graph versions
#DONE: Clean older logs in the storage account (last year?)
#DONE: Clean up Code
#DONE: Assign Role for the execution account to allow creation on AD groups: Groups administrator
#DONE: Filter Devices. Exclude Windows and MAC and (linux?)
#DONE: Copy permission from custom role
#DONE: Document functions
#DONE: update the catch blocks in every call to the API

#Begin of Script racarb

$Begindate = Get-Date
$Logdate = Get-Date
$logpath = "$("{0:yyyy}-{0:MM}-{0:dd}-{0:HH}{0:mm}" -f ($Logdate))-ProvisioningResults.log"
$ProvisioningResults = @{ }
Log-Event -Text "$($Begindate)"


#Get Mobile Devices enrolled in the last xhours
$EnrollmentDateTime = (Get-Date).AddHours(-$Hours)
Log-Event "Getting devices enrolled since $($EnrollmentDateTime)..."
$EnrollmentDateTimefilter = "{0:yyyy}-{0:MM}-{0:dd}T{0:hh}:{0:mm}:{0:ss}Z" -f $EnrollmentDateTime

#Mobiles
$ManagedDevices = Get-IntuneManagedDevice -Filter `
"(enrolledDateTime ge $($EnrollmentDateTimefilter)) and `
((deviceType eq 'android') or `
(deviceType eq 'androidEnterprise') or `
(deviceType eq 'androidForWork') or `
(deviceType eq 'iPhone') or `
(deviceType eq 'iPod') or `
(deviceType eq 'iPad') or `
(deviceType eq 'windowsPhone') or `
(deviceType eq 'macMDM')) and `
((managementAgent eq 'mdm') or `
(managementAgent eq 'easMdm') or `
(managementAgent eq 'configurationManagerClientMdm'))"
 `
| Get-MSGraphAllPages | Where-Object { ($null -ne $_.userPrincipalName) -and ($_.userPrincipalName) -ne "" }


#region getting info
if ($ManagedDevices) {

    Log-Event "Got info from $($ManagedDevices.count) devices enrolled in the last $Hours hours"
    $ManagedDeviceshash = $ManagedDevices | Group-Object -Property userPrincipalName -AsHashTable -AsString

    
Log-Event "Begin provisioning..."

foreach ($userupnhash in $ManagedDeviceshash.GetEnumerator()) {
    
    if (($userupnhash.key -eq "") -or ($null -eq $userupnhash.key )) { continue } #Checks if upn of the user is missing and bypasses the user
    
    try { $Company = (Get-AzureADUser -ObjectId $userupnhash.Key -ErrorAction Stop).$ADfield }
    catch { $_.Exception; continue } #Error when retrieving the OB

    Log-Event -Text "Provisioning user $($userupnhash.Key)"
    Log-Event -Text "Checking Tags,Roles and Groups for user's OB: $($Company)"

# if ($Company -notin $Companies) {
# Log-Event -Text "User's OB not in the list";
# Write-ResultstoHashTable -Hashtablename ProvisioningResults -key Skippedusers -value $($userupnhash.key); continue
# } #Checks if the OB is included in the list and if not bypasses the user

    else { Write-ResultstoHashTable -Hashtablename ProvisioningResults -key Newusers -value $($userupnhash.key) }
           
    #Test if there's a Tag for that OB and create it if necesary
    if (-not (Test-CompanyScopeTag -Company $Company)) {
        Log-Event -Text "Scope Tag for OB $Company not found"
        try {
            $CompanyScopeTag = New-CompanyScopeTag -Company $Company
            Log-Event -Text "Scope Tag for OB $Company Created"
            Write-ResultstoHashTable -Hashtablename ProvisioningResults -key NewTags -value $CompanyScopeTag.displayName
        }
        catch { $_.Exception; Log-ErrorEvent -Text "Scope Tag for OB $Company could not be created"; break }
    }
    
    #Test if there's a RBAC Role for that OB and create it if necesary
    if (-not (Test-CompanyRBACRole -Company $Company)) {
        Log-Event "RBAC Role for OB $Company not found"
        try {
            $CompanyRBACRole = New-CompanyRBACRole -Company $Company
            Log-Event "RBAC Role for OB $Company Created"
            Write-ResultstoHashTable -Hashtablename ProvisioningResults -key NewRBACRoles -value $CompanyRBACRole.displayName
        }
        catch { $_.Exception; Log-ErrorEvent -Text "RBAC Role for OB $Company could not be created"; break }
    }
    else {
        Log-Event "RBAC Role for OB $Company already exists"
        if (((Get-CompanyScopeTag -Company $Company).id) -notin (@((Get-CompanyRBACRole -Company $Company).roleScopeTagIds)))
        { Log-Event "Tags doesn't match, IMPLEMENT FUNCTION" }
    }
        
    #Test if there's an Admin Group for that OB and create it if necesary
    if ($null -eq (Get-AzureADGroup -Filter "DisplayName eq '$($AdminGroupPre)$($Company)$($AdminGroupSuf)'")) {
        Log-Event "Admin Group for OB $Company not found"
        try {
            $AdminGroup = New-AzureADGroup -DisplayName "$($AdminGroupPre)$($Company)$($AdminGroupSuf)" -SecurityEnabled $true -MailEnabled $false -MailNickName "$($AdminGroupPre)$($Company -replace " ")$($AdminGroupSuf)" -Description "Admin Group for $OB $company Automatically Created"
            Log-Event "Admin Group for OB $Company Created"
            Write-ResultstoHashTable -Hashtablename ProvisioningResults -key NewAdminGroups -value $AdminGroup.displayName
        }
        catch { $_.Exception; Log-ErrorEvent -Text "Admin Group for OB $Company could not be created"; break }
    }
    else { Log-Event "Admin Group for OB $Company already exists" }

    #Test if there's a Scope Dynamic Group for that OB and create it if necesary
    if ($null -eq (Get-AzureADMSGroup -Filter "DisplayName eq '$($ScopeGroupPre)$($Company)$($ScopeGroupSuf)'")) {
        Log-Event "Scope Dynamic Group for OB $Company not found"
        try {
            $ScopeGroup = New-AzureADMSGroup -DisplayName "$($ScopeGroupPre)$($Company)$($ScopeGroupSuf)" -MailEnabled $False -MailNickName "$($AdminGroupPre)$($Company -replace " ")$($AdminGroupSuf)" -GroupTypes "DynamicMembership" -MembershipRule "(user.state -eq `"$Company`")" -MembershipRuleProcessingState On -SecurityEnabled $true -Description "Scope Group for $OB $company Automatically Created"
            Log-Event "Scope Dynamic Group for OB $Company Created"
            Write-ResultstoHashTable -Hashtablename ProvisioningResults -key NewScopeGroups -value $ScopeGroup.displayName
        }
        catch { $_.Exception; Log-ErrorEvent -Text "Scope Group for OB $Company could not be created"; break }
    }
    else { Log-Event "Scope Dynamic Group for OB $Company already exists" }

    #Test if there's a Role Assignment for that OB and create it if necesary
    if (Test-RoleAssignment -Company $Company) { "Role Assignment for OB $Company already exists" }
    else {
        Log-Event "Role Assignment for OB $Company not found"
        try {
            $RoleAssignment = New-RoleAssignment -Company $Company
            Log-Event "Role Assignment for OB $Company Created"
            Write-ResultstoHashTable -Hashtablename ProvisioningResults -key NewRoleAssignments -value $RoleAssignment.displayName
        }
        catch { $_.Exception; Log-ErrorEvent -Text "Role Assignment for OB $Company could not be created"; break }
    }

    #Enroll the devices for the user
    Log-Event -Text "Provisioning $(($userupnhash.Value).count) Device(s) for user $($userupnhash.Key):"
    foreach ($userdevice in $userupnhash.Value) {
        Log-Event -Text "Provisioning Device $($userdevice.deviceName):"

        try {
            Update-DeviceScopeTag -Deviceid $userdevice.id -ScopeTagids (Get-CompanyScopeTag -Company $company).id
            Write-ResultstoHashTable -Hashtablename ProvisioningResults -key NewUserDevices -value $userdevice.managedDeviceName
        }
        catch { $_.Exception }
            
    }
        
    
}



}
else {

    Write-Output ""
    Log-Event "No device was enrolled in the last $Hours Hour(s)"
    Write-Output ""

}



#Summary
Log-Event "Results:"

$ProvisioningResults


$Enddate = Get-Date
Log-Event "Script execution time: $(($Enddate - $Begindate).Minutes) minutes, $(($Enddate - $Begindate).Seconds) seconds"
Log-Event "End provisioning..."

#Uploading results to an Storage Account

$xmlpath = "$("{0:yyyy}-{0:MM}-{0:dd}-{0:HH}{0:mm}" -f ($Logdate))-ProvisioningResults.xml"
$ProvisioningResults | Export-Clixml -Path $xmlpath


$sacontext = New-AzStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $StorageAccountKey
$shareobj = Get-AzStorageShare -Context $sacontext -Name $ShareName
Set-AzStorageFileContent -Share $shareobj -Source $xmlpath -Path $xmlpath -Force
Set-AzStorageFileContent -Share $shareobj -Source $logpath -Path $logpath -Force

Get-ChildItem $logpath
Get-Content $logpath
Log-Event "Results have been uploaded to Storage account $($StorageAccountName)"



#Remove older logs ( More than a Year)
$Filestodelete = Get-AzStorageFile -Share $shareobj | Where-Object { ($_.Name).Substring(0, 10) -as [datetime] -lt ($Logdate.AddYears(-1)) } | Remove-AzStorageFile


#Forward Logs to Azure Monitor if specified


if ($LoginAzureMonitor) {


#function to create the authorization signature
Function Build-Signature ($customerId, $sharedKey, $date, $contentLength, $method, $contentType, $resource)
{
    $xHeaders = "x-ms-date:" + $date
    $stringToHash = $method + "`n" + $contentLength + "`n" + $contentType + "`n" + $xHeaders + "`n" + $resource

    $bytesToHash = [Text.Encoding]::UTF8.GetBytes($stringToHash)
    $keyBytes = [Convert]::FromBase64String($sharedKey)

    $sha256 = New-Object System.Security.Cryptography.HMACSHA256
    $sha256.Key = $keyBytes
    $calculatedHash = $sha256.ComputeHash($bytesToHash)
    $encodedHash = [Convert]::ToBase64String($calculatedHash)
    $authorization = 'SharedKey {0}:{1}' -f $customerId,$encodedHash
    return $authorization
}

#Function to create and post the request
Function Post-LogAnalyticsData($customerId, $sharedKey, $body, $logType)
{
    $method = "POST"
    $contentType = "application/json"
    $resource = "/api/logs"
    $rfc1123date = [DateTime]::UtcNow.ToString("r")
    $contentLength = $body.Length
    $signature = Build-Signature `
        -customerId $customerId `
        -sharedKey $sharedKey `
        -date $rfc1123date `
        -contentLength $contentLength `
        -method $method `
        -contentType $contentType `
        -resource $resource
    "Signature:"
    $Signature | fl *
    $uri = "https://" + $customerId + ".ods.opinsights.azure.com" + $resource + "?api-version=2016-04-01"

    $headers = @{
        "Authorization" = $signature;
        "Log-Type" = $logType;
        "x-ms-date" = $rfc1123date;
        "time-generated-field" = $TimeStampField;
    }

    $response = Invoke-WebRequest -Uri $uri -Method $method -ContentType $contentType -Headers $headers -Body $body -UseBasicParsing
    return $response.StatusCode

}

$logType = "IntuneProvisioning"

$xmlfile = Get-Item $xmlpath
$hashobject = Import-Clixml $xmlfile.FullName
$TimeStampField = $xmlfile.CreationTimeUtc
$hashobject["CreatedTime"] = $xmlfile.CreationTimeUtc

$json = ConvertTo-Json -InputObject $hashobject

# Submit the data to the API endpoint
$CustomerId = (Get-AzOperationalInsightsWorkspace -Name $LAworkspaceName -ResourceGroupName $ResourceGroupName).CustomerId.guid
try {Post-LogAnalyticsData -customerId $customerId -sharedKey $LAworkspaceKey -body ([System.Text.Encoding]::UTF8.GetBytes($json)) -logType $logType
Log-Event "Results have been uploaded to Log Analytics Workspace $($LAworkspaceName)"}
catch {Log-Event "Error. Results couldn't been uploaded to Log Analytics Workspace $($LAworkspaceName)"}

}




#End of Script racarb