1

I have some trouble with the new Microsoft API Graph. I have a default folder of contacts in a generic Outlook profil. I've already push it to all my users. Everyone one have a generic folder with 530 contacts inside.

The problem now is to update it without delete and start again, or it's 14 hours long of script execution !

I've seen the API Graph Delta, but i don't know how to use it, im "new" to powershell so URI request and all of that typicals things are kind of difficult, can someone give me an example about how to update a folder with my default folder ? I'll do the rest.

I've made this powershell code, took a part of the first script on the Internet, credit to Sean McAvinue

<#
Details: Graph / PowerShell Script t populate user contacts based on CSV input, 
        Please fully read and test any scripts before running in your production environment!
        .SYNOPSIS
        Populates mail contacts into user mailboxes from a CSV

        .DESCRIPTION
        Creates a new mail contact for each entry in the input CSV in the target mailbox.

        .PARAMETER Mailbox
        User Principal Name of target mailbox

        .PARAMETER CSVPath
        Full path to the input CSV

        .PARAMETER ClientID
        Application (Client) ID of the App Registration

        .PARAMETER ClientSecret
        Client Secret from the App Registration

        .PARAMETER TenantID
        Directory (Tenant) ID of the Azure AD Tenant

        .EXAMPLE
        .\graph-PopulateContactsFromCSV.ps1 -Mailbox  $mailbox -ClientSecret $clientSecret -ClientID $clientID -TenantID $tenantID -CSVPath $csv
        
        .Notes
        For similar scripts check out the links below
        
            Blog: https://seanmcavinue.net
            GitHub: https://github.com/smcavinue
            Twitter: @Sean_McAvinue
            Linkedin: https://www.linkedin.com/in/sean-mcavinue-4a058874/


#>
#>
#################################################
####################FONCTIONS####################
#################################################
function GetGraphToken {
    # Azure AD OAuth Application Token for Graph API
    # Get OAuth token for a AAD Application (returned as $token)
<#
        .SYNOPSIS
        This function gets and returns a Graph Token using the provided details
    
        .PARAMETER clientSecret
        -is the app registration client secret
    
        .PARAMETER clientID
        -is the app clientID
    
        .PARAMETER tenantID
        -is the directory ID of the tenancy
#>
    Param(
        [parameter(Mandatory = $true)]
        [String]
        $ClientSecret,
        [parameter(Mandatory = $true)]
        [String]
        $ClientID,
        [parameter(Mandatory = $true)]
        [String]
        $TenantID
    
    )
    
    # Construct URI
    $uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
         
    # Construct Body
    $body = @{
        client_id     = $clientId
        scope         = "https://graph.microsoft.com/.default"
        client_secret = $clientSecret
        grant_type    = "client_credentials"
    }
         
    # Get OAuth 2.0 Token
    $tokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing
         
    # Access Token
    $token = ($tokenRequest.Content | ConvertFrom-Json).access_token
    return $token
}

function ImportContact {
    <#
.SYNOPSIS
Imports contact into specified user mailbox

.DESCRIPTION
This function accepts an AAD token, user account and contact object and imports the contact into the users mailbox

.PARAMETER Mailbox
User Principal Name of target mailbox

.PARAMETER Contact
Contact object for processing

.PARAMETER Token
Access Token      
#>
    Param(
        [parameter(Mandatory = $true)]
        [String]
        $Token,
        [parameter(Mandatory = $true)]
        [String]
        $Mailbox,
         [parameter(Mandatory = $true)]
        [String]
        $folder,
        [parameter(Mandatory = $true)]
        [PSCustomObject]
        $contact
    
    )
    write-host "contactcompanyname $($contact.companyname)"
    write-host $contact
    #Creation de l'objet "contact"
    $ContactObject = @"
    {
        "assistantName": "$($contact.assistantName)",
        "businessHomePage": "$($contact.businessHomePage)",
        "businessPhones": [
            "$($contact.businessPhones)"
          ],
        "displayName": "$($contact.displayName)",
        "emailAddresses": [
            {
                "address": "$($contact.emailaddress)",
                "name": "$($contact.displayname)"
            }
        ],
        "givenName": "$($contact.givenname)",
        "middleName": "$($contact.middleName)",
        "nickName": "$($contact.nickName)",
        "surname": "$($contact.surname)",
        "title": "$($contact.title)"
    }
"@
    write-host "contact object: $contactobject"

#Creation du token d'autentification pour chaque boite mail contenue dans cible.txt
foreach($mail in $CSVPath){
    $apiUri = "https://graph.microsoft.com/v1.0/users/$person/contactFolders/$folderid/contacts"
    write-host $apiuri
  
        $NewContact = (Invoke-RestMethod -Headers @{Authorization = "Bearer $($token)" } -ContentType 'application/json' -Body $contactobject -Uri $apiUri -Method Post)
        return $NewContact
         }
    catch {
        throw "Error creating contact $($contact.emailaddress) for $person $($_.Exception.Message)"
        continue
        }
}
                    
                    

##Import CSV
try {
    $Contacts = import-csv $CSVPath -ErrorAction stop
}
catch {
    throw "Erreur d'import CSV: $($_.Exception.Message)"
    break
}

##Graph Token
Try {
    $Token = GetGraphToken -ClientSecret $ClientSecret -ClientID $ClientID -TenantID $TenantID
}
catch {
    throw "Erreur d'obtention de token"
    break
}

##ProcessImport
foreach ($contact in $contacts) {
    $NewContact = ImportContact -Mailbox $person -token $token -contact $contact -folder "Sync-Opac"
}

Then my script, need a lot of "if" to locate errors and demonstrate it to collegues. It was usefull for the first "big" deployement.


<#
"----Installation --ET-- importation du module Pwsh Microsoft Graph pour executer le script---" -ForegroundColor red
"----------Processus LONG, patientez 10/15min------"      
Install-module -Name Microsoft.Graph -verbose
Import-Module -Name Microsoft.Graph -verbose
#>

##Connexion###
$uri = "https://login.microsoftonline.com/4f232a97-8219-4450-9bec-410e6d0472d0/oauth2/v2.0/token"
#
    $body = @{
        client_id     = $clientId
        scope         = "https://graph.microsoft.com/.default"
        client_secret = $clientSecret
        grant_type    = "client_credentials"
    }  
#
$tokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing -verbose  
#
write-host "-------------Obtention & Conversion du token en cours...-------------" -ForegroundColor green
$token = ($tokenRequest.Content | ConvertFrom-Json).access_token
#
    if ($? -eq $true){
        write-host "-------------Token genere >> securisation en cours-------------" -ForegroundColor green
                    }
else{
    write-host "-------------Erreur sur la recuperation du token-------------" -ForegroundColor red
    break
    }
$tokensecure = ConvertTo-SecureString -string $token -AsPlainText -Force -verbose
#
    if ($? -eq $true){
        write-host "-------------Token sécurisé obtenu, connexion...-------------" -ForegroundColor green
                try{
                    Connect-MgGraph -NoWelcome -AccessToken $tokensecure
                    "---------------------------------------"
                    "------------#CONNEXION OK!#------------"
                    "---------------------------------------"
                }catch{
                    write-host "-------------Connexion impossible avec le token specifie-------------" -ForegroundColor red
                    break
                      }
}

foreach ($person in $mailbox ){
    Clear-Host
    $folder = Get-MgUserContactFolder -UserID $person | ? {$_.DisplayName -eq"Sync-Opac"} -errorAction ignore
    if($folder -eq $null){

                                                ##Connexion###
                                               $uri = "https://login.microsoftonline.com/4f232a97-8219-4450-9bec-410e6d0472d0/oauth2/v2.0/token"
    
                                               $body = @{
                                               client_id     = $clientId
                                               scope         = "https://graph.microsoft.com/.default"
                                               client_secret = $clientSecret
                                               grant_type    = "client_credentials"
                                              }  

                                            $tokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing -verbose  
                                            #
                                            write-host "-------------Obtention & Conversion du token en cours...-------------" -ForegroundColor green
                                            $token = ($tokenRequest.Content | ConvertFrom-Json).access_token
                                            #
                                                if ($? -eq $true){
                                                    write-host "-------------Token genere >> securisation en cours-------------" -ForegroundColor green
                                                                }
                                            else{
                                                write-host "-------------Erreur sur la recuperation du token-------------" -ForegroundColor red
                                                break
                                                }
                                            $tokensecure = ConvertTo-SecureString -string $token -AsPlainText -Force -verbose
                                            #
                                            if ($? -eq $true){
                                                write-host "-------------Token sécurisé obtenu, connexion...-------------" -ForegroundColor green
                                                        try{
                                                            Connect-MgGraph -NoWelcome -AccessToken $tokensecure
                                                            "---------------------------------------"
                                                            "------------#CONNEXION OK!#------------"
                                                            "---------------------------------------"
                                                            Clear-Host
                                                        }catch{
                                                            write-host "-------------Connexion impossible avec le token specifie-------------" -ForegroundColor red
                                                            break
                                                              }
                                        }
                                                #Fin de connexion
                              ########################################Creation contact##################
New-MgUserContactFolder -UserId $person -DisplayName Sync-Opac
start-sleep 5
$folder = Get-MgUserContactFolder -UserID $person | ? {$_.DisplayName -eq"Sync-Opac"}
$folderid = $folder.Id
./pwsh_graph_contacts.ps1
write-host "----------Contact importé && Dossier Contacts créé pour $person------------" -ForegroundColor Green
Clear-Host
continue
#si compte deja peuplé alors connexion et suppression
                                }else{
                                            ##Connexion###
                                            $uri = "https://login.microsoftonline.com/4f232a97-8219-4450-9bec-410e6d0472d0/oauth2/v2.0/token"
                                            #
                                                $body = @{
                                                    client_id     = $clientId
                                                    scope         = "https://graph.microsoft.com/.default"
                                                    client_secret = $clientSecret
                                                    grant_type    = "client_credentials"
                                                }  
                                            #
                                            $tokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing -verbose  
                                            #
                                            write-host "-------------Obtention & Conversion du token en cours...-------------" -ForegroundColor green
                                            $token = ($tokenRequest.Content | ConvertFrom-Json).access_token
                                            #
                                                if ($? -eq $true){
                                                    write-host "-------------Token genere >> securisation en cours-------------" -ForegroundColor green
                                                                }
                                            else{
                                                write-host "-------------Erreur sur la recuperation du token-------------" -ForegroundColor red
                                                break
                                                }
                                            $tokensecure = ConvertTo-SecureString -string $token -AsPlainText -Force -verbose
                                            #
                                                if ($? -eq $true){
                                                    write-host "-------------Token sécurisé obtenu, connexion...-------------" -ForegroundColor green
                                                    
                                                            try{
                                                                Connect-MgGraph -NoWelcome -AccessToken $tokensecure
                                                                "---------------------------------------"
                                                                "------------#CONNEXION OK!#------------"
                                                                "---------------------------------------"
                                                                Clear-Host
                                                            }catch{
                                                                write-host "-------------Connexion impossible avec le token specifie-------------" -ForegroundColor red
                                                                break
                                                                  }
                                            }
                                            #Fin de connexion
                                            ########################################Creation contact##################
                                       
.\suppression_graph_contacts.ps1
Clear-Host
start-sleep 10
New-MgUserContactFolder -UserId $person -DisplayName Sync-Opac
$folder = Get-MgUserContactFolder -UserID $person | ? {$_.DisplayName -eq"Sync-Opac"}
$folderid = $folder.Id
./pwsh_graph_contacts.ps1
write-host "----------Contacts importés pour $person-------------------" -ForegroundColor Green
continue
   }
}

I clearly understand my part, but only 50% of the first one i'm using actually.

2 Answers 2

2

You can use HTTP request, which is a direct way to call Microsoft Graph API and gives you more flexibility to control the request and response. You can use the GET method to get the incremental changes of the contact folders, and use the PATCH method to update the contact folders’ properties, for example:

 // Get the incremental changes of the contact folders
GET /me/contactFolders/delta
// Iterate through the delta collection
for each deltaFolder in response
{
    // Check if it is your default folder
    if (deltaFolder.displayName == "Default Folder")
    {
        // Modify the folder's properties
        PATCH /me/contactFolders/{id}
        Content-type: application/json
        {
            "displayName": "Updated Folder"
        }
    }
}
1
  • Thanks for this answer, i'll give a try and come back later !
    – FuxT
    Commented Nov 22, 2023 at 9:34
0

Remove the New-MgUserContactFolder commands from your script. Otherwise it should run fine as-is to import your CSV to everyone's personal contacts. It will not overwrite the whole folder or delete contacts that aren't in the CSV.

Test it out on a single user.

If you want to reduce the time it takes, you could filter contacts you've already added out of the CSV, or use Get-MgUserContact to query the existing contacts and only update as-needed.


Generally, you should only do this for important contacts that won't update very often if possible (like the IT Service Desk, HR, and other internal services) due to how slow it is. If the list gets too large, consider using a central shared contacts folder.

1
  • Thanks for this answer, i'll give a try and come back later !
    – FuxT
    Commented Nov 22, 2023 at 9:34

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .