Teams Devices Configuration Profiles Uploader

There’s a lot of long manual processes in the Teams Admin Center, like entering configuration profiles for devices like Phones, Panels, Displays, and Teams Rooms on Android. If you work as a consultant, you probably have to redo all this work on each tenant you work on. People maybe add 50-100 profiles for each tenant. This is really time-consuming.

My friend Jens Madsen asked me if it was possible to automate this? Yes, it is! Thanks for the idea Jens, and the extensive CSV file with predefined profiles for various countries/timezones.

You can find the CSV file here. (If it is downloaded as xls, open and save as CSV with comma as separator)

Now you can save hours of work and almost by one click upload this automatically. Only thing you need before running the script is a token from the Teams Admin Center.

You get this by opening the dev mode of your browser (F12 in Edge Chromium) and click network. Then go to the configuration profiles pages for one of the device types. In this case, Displays. In the Dev tab, look for the name ‘configProfiles’. Here you can find a token. Copy the token and paste it into the script when running it. That’s it!

### Teams Devices Configuration Profiles Uploader ###
### Version 1.0 ###
### Author: Alexander Holmeset ###
### Email: [email protected] ###
### Twitter: twitter.com/alexholmeset ###
### Blog: alexholmeset.blog ###
### Thanks to Jens Madsen for idea and extensive CSV ###
### with predefined configuration profiles. ###
### https://twitter.com/JensHMadsen ###
$PowerShellVersion = ((Get-Host).Version).Major
If($PowerShellVersion -eq 7){
Write-Host "PowerShell 7 does not support interatctive input longer than 1024 characters."
Write-Host "Open script in editor and paste token in token variable at line 25 instead of read host command."
Write-Host "Then remove line 11 to 20."
return
}
Write-Host "================ Teams Devices Configuration Profiles Uploader ================"
Write-Host ""
$Token = Read-Host "Enter Teams Admin Center token to authenticate"
#Checking if token is valid.
Try{
$URL = "https://admin.devicemgmt.teams.microsoft.com/api/v2/configProfiles"
$header = @{Authorization = "Bearer $token"}
$ConfigProfiles = (((Invoke-RestMethod -Uri $URL -Method GET -Headers $header -ContentType "application/json").paginatedconfigprofiles).baseinfo).id
}
Catch{Write-Host "";Write-Host "Authentication failed!" -ForegroundColor red;Return}
Write-Host ""
Write-Host "Authentication OK!" -ForegroundColor Green
Write-Host ""
function UploadConfigProfiles {
$URL = "https://admin.devicemgmt.teams.microsoft.com/api/v2/configProfiles"
$header = @{Authorization = "Bearer $token"}
$DeviceProfiles = Import-Csv (Read-Host "Enter full path of CSV file included filename")
foreach($DeviceProfile in $DeviceProfiles){
$body = @"
{
"identity":"$($DeviceProfile.identity)",
"deviceType":"$($DeviceProfile.deviceType)",
"description":"$($DeviceProfile.description)",
"configProfileCreationType":$($DeviceProfile.configProfileCreationType),
"deviceLock":"$($DeviceProfile.deviceLock)",
"deviceLockTimeout":"$($DeviceProfile.deviceLockTimeout)",
"deviceLockPin":"$($DeviceProfile.deviceLockPin)",
"language":"$($DeviceProfile.language)",
"timeZone":"$($DeviceProfile.timeZone)",
"timeZoneId":"$($DeviceProfile.timeZoneId)",
"dateFormat":"$($DeviceProfile.dateFormat)",
"timeFormat":"$($DeviceProfile.timeFormat)",
"displayScreenSaver":"$($DeviceProfile.displayScreenSaver)",
"screenSaverTimeout":"$($DeviceProfile.screenSaverTimeout)",
"displayBacklitBrightness":"$($DeviceProfile.displayBacklitBrightness)",
"displayBacklitTimeout":"$($DeviceProfile.displayBacklitTimeout)",
"displayHighContrast":"$($DeviceProfile.displayHighContrast)",
"silentMode":"$($DeviceProfile.silentMode)",
"officeStartHours":"$($DeviceProfile.officeStartHours)",
"officeEndHours":"$($DeviceProfile.officeEndHours)",
"powersaving":"$($DeviceProfile.powersaving)",
"loggingEnabled":"$($DeviceProfile.loggingEnabled)",
"loggingTypes":"$($DeviceProfile.loggingTypes)",
"networkDhcp":"$($DeviceProfile.networkDhcp)",
"networkWifi":"$($DeviceProfile.networkWifi)",
"networkPcPort":"$($DeviceProfile.networkPcPort)",
"deviceDefaultAdminPassword":"$($DeviceProfile.deviceDefaultAdminPassword)",
"screenCapture":"$($DeviceProfile.screenCapture)",
"dhcpEnabled":"$($DeviceProfile.dhcpEnabled)",
"hostName":"$($DeviceProfile.hostName)",
"domainName":"$($DeviceProfile.domainName)",
"ipAddress":"$($DeviceProfile.ipAddress)",
"subnetMask":"$($DeviceProfile.subnetMask)",
"defaultGateway":"$($DeviceProfile.defaultGateway)",
"primaryDNS":"$($DeviceProfile.primaryDNS)",
"secondaryDNS":"$($DeviceProfile.secondaryDNS)",
"theme":{"name":"$($DeviceProfile.theme)"},
"usageStateIndicatorBusyStateColor":{"name":"$($DeviceProfile.usageStateIndicatorBusyStateColor)"}
}
"@
Invoke-RestMethod -Uri $URL -Method POST -Headers $header -ContentType "application/json" -Body $body
}
Write-Host "Configuration profiles upload is completed!"
}
function DeleteConfigProfiles {
Write-Host ""
Write-Host "Are you sure you want to delete all configuration profiles?"
Write-Host ""
$input = Read-Host "Type Yes or No."
switch ($input)
{
'Yes' {
If($input -like "Yes"){
Write-Host ""
Write-Host "Deletion of configuration profiles initiated!"
Write-Host ""
}
} 'No' {
Write-Host ""
Write-Host 'Script aborted!' -ForegroundColor Red
return
}
}
$URL = "https://admin.devicemgmt.teams.microsoft.com/api/v2/configProfiles"
$header = @{Authorization = "Bearer $token"}
while ($ConfigProfilesCount -ne 0 ) {
$ConfigProfiles = (((Invoke-RestMethod -Uri $URL -Method GET -Headers $header -ContentType "application/json").paginatedconfigprofiles).baseinfo).id
$ConfigProfilesCount = $ConfigProfiles.Count
foreach($ConfigProfile in $ConfigProfiles){
$count2--
Write-Host ""
"Configuration profile deleted!"
$url2 = "https://admin.devicemgmt.teams.microsoft.com/api/v2/configProfiles/$ConfigProfile"
Invoke-RestMethod -Uri $URL2 -Method DELETE -Headers $header
}
}
If($input -like "Yes"){
write-Host ""
Write-Host "Configuration Profiles deleteion completed!"
}
}
Write-Host ""
Write-Host "1: Press '1' to uplload config prolfiles to Teams Admin Center."
Write-Host "2: Press '2' to delete all config profies from Teams Admin Center."
Write-Host "Q: Press 'Q' to quit."
Write-Host ""
$input = Read-Host "Please make a selection"
switch ($input)
{
'1' {
Write-Host ""
UploadConfigProfiles
} '2' {
Write-Host ""
DeleteConfigProfiles
}
'q' {
return
}
}

16 thoughts on “Teams Devices Configuration Profiles Uploader

  1. thank your for this great script. Not too sure I will have a super-global customer like the CSV files implies, but if I do, I already know how to handle it. 😉
    best regards from Vienna, Austria

    HST

    Like

    • Thanks 🙂 yeah, I guess its not often you need that an extensive list, but at least you have a list to choose from now, hehe

      Like

  2. Hello Alexander,

    First of all, thanks for you Script.

    I have an issue with it. It’s run with this error but the configuration profiles added successfully.
    Error: Invoke-RestMethod : The remote server returned an error: (400) Bad Request.
    At C:\Users\XXXXXXXX\TeamsDevicesConfigurationProfiles.ps1:98 char:1
    + Invoke-RestMethod -Uri $URL -Method POST -Headers $header -ContentTy …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

    Could you please help meon this ?

    Many thanks in advance.

    Like

      • It looks like its working. Is the profiles file in .csv format? Theres this stupid thing when you upload to onedrive, and it opens in Excel web, when you save, it saves as XLSX format.

        Like

      • Thanks for your reply.
        Yes, profiles file is in csv format (with comma delimited).
        Profiles are created but after creating all profiles i have this error.

        Like

      • I found the issue.
        I left only one line in the csv file and deleted all the others but the file kept the lines empty.
        Thanks for your help again.

        Like

      • Ah, i didnt let it finish. So the error is at the end after all profiles is uploaded to the admin portal?

        Like

  3. @Alexander. Is there a way to modify the script to allow assignment of the Config Profiles to specific devices? I am thinking assigning a unique unlock PIN to each phone.

    Like

    • Ah, havent looked at it, but guess it would be possible.
      So getting all phones thats in your tenant, then random generate a pincode for each by a creating a profile for each and assign?

      Like

      • That would work. I was thinking just associating a profile with a device and manually setting the PIN in each profile. But your idea would remove the manual step. Even better. I guess we could always manually update afterwards if a user wanted a specific PIN.

        Like

  4. Hi there, brilliant script, works a treat, how do you go about getting the updated fields within the configuration profile pages?

    Like

    • thanks.
      Havent looked at this in ages, trying to understand your question. Is there new fields? or could you rephrase that? 🙂

      Like

  5. Thank you for this great work! Unfortunately, I am unable to download the CSV anymore. I am interested in the format of the CSV. Could someone provide it to me? Thank you, Chris.

    Like

    • I was without a license for a while in my test tenant where the csv was stored. Remind me if it is still not working on monday 🙂 just need to find a copy and upload again

      Like

Leave a comment