With the new Store Integration now speedily being added to tenants and my previous post on adding Company Portal proving so popular, I thought I’d have a look at what else is sitting in Graph
This post from Sander Rozemuller gave me an excellent starting point and the script dropped nicely into a function:
The full script can be found here on GitHub
Function Add-MSStoreApp(){
<#
.SYNOPSIS
This function adds Microsoft Store Apps using Winget
.DESCRIPTION
The function connects to the Graph API Interface and creates a Microsoft Store App using the new experience
.EXAMPLE
Add-MSStoreApp -name "WhatsApp"
.NOTES
NAME: Add-MSStoreApp
#>
[cmdletbinding()]
param
(
$name
)
$appName = $name
$storeSearchUrl = "https://storeedgefd.dsx.mp.microsoft.com/v9.0/manifestSearch"
$body = @{
Query = @{
KeyWord = $appName
MatchType = "Substring"
}
} | ConvertTo-Json
$appSearch = Invoke-RestMethod -Uri $storeSearchUrl -Method POST -ContentType 'application/json' -body $body
$exactApp = $appSearch.Data | Where-Object { $_.PackageName -eq $appName }
$appUrl = "https://storeedgefd.dsx.mp.microsoft.com/v9.0/packageManifests/{0}" -f $exactApp.PackageIdentifier
$app = Invoke-RestMethod -Uri $appUrl -Method GET
$appId = $app.Data.PackageIdentifier
$appInfo = $app.Data.Versions[-1].DefaultLocale
$appInstaller = $app.Data.Versions[-1].Installers
$appdescription = ($appInfo.Shortdescription).ToString()
$appdescription2 = $appdescription.replace("`n"," ").replace("`r"," ").replace("\n"," ").replace("\\n"," ")
$appdeveloper = $appInfo.Publisher
$appdisplayName = $appInfo.packageName
$appinformationUrl = $appInfo.PublisherSupportUrl
$apprunAsAccount = ($appInstaller.scope | select-object -First 1)
$appisFeatured = $false
$apppackageIdentifier = $appId
$appprivacyInformationUrl = $appInfo.PrivacyUrl
$apppublisher = $appInfo.publisher
$deployUrl = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps"
$json = @"
{
"@odata.type": "#microsoft.graph.winGetApp",
"categories": [],
"description": "$appdescription2",
"developer": "$appdeveloper",
"displayName": "$appdisplayName",
"informationUrl": "$appinformationUrl",
"installExperience": {
"runAsAccount": "$apprunAsAccount"
},
"isFeatured": false,
"notes": "",
"owner": "",
"packageIdentifier": "$apppackageIdentifier",
"privacyInformationUrl": "$appprivacyInformationUrl",
"publisher": "$apppublisher",
"repositoryType": "microsoftStore",
"roleScopeTagIds": []
}
"@
$appDeploy = Invoke-mggraphrequest -uri $deployUrl -Method POST -Body $json -ContentType "application/JSON"
return $appDeploy
}
I also separated out the assignment and switched it to Group ID rather than everyone (NOTE: the group needs to be a user group):
Function Add-StoreAppAssignment(){
<#
.SYNOPSIS
This function is used to add a store app assignment using the Graph API REST interface
.DESCRIPTION
The function connects to the Graph API Interface and adds a store app assignment
.EXAMPLE
Add-StoreAppAssignment -StoreAppID $StoreAppIdId -TargetGroupId $TargetGroupId
Adds a Store app assignment in Intune
.NOTES
NAME: Add-SStoreAppAssignment
#>
[cmdletbinding()]
param
(
$StoreAppID,
$TargetGroupId
)
$graphApiVersion = "Beta"
$Resource = "deviceManagement/deviceAppManagement/mobileApps/$storeAppID/assign"
try {
if(!$StoreAppID){
write-host "No Compliance Policy Id specified, specify a valid Compliance Policy Id" -f Red
break
}
if(!$TargetGroupId){
write-host "No Target Group Id specified, specify a valid Target Group Id" -f Red
break
}
$JSON = @"
{
"mobileAppAssignments": [
{
"settings": {
"installTimeSettings": null,
"notifications": "showAll",
"restartSettings": null,
"@odata.type": "#microsoft.graph.winGetAppAssignmentSettings"
},
"intent": "Required",
"target": {
"groupId": "$TargetGroupId"
},
"@odata.type": "#microsoft.graph.mobileAppAssignment"
}
]
}
"@
$uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
Invoke-MgGraphRequest -Uri $uri -Method Post -Body $JSON -ContentType "application/json"
}
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
}
}
Then a function to display all Store Apps:
Function Get-MSStoreApps(){
<#
.SYNOPSIS
This function is used to get MS Store Apps from the Graph API REST interface
.DESCRIPTION
The function connects to the Graph API Interface and gets any MS Store Apps
.EXAMPLE
Get-MSStoreApps
Returns any MS Store Apps configured in Intune
Get-MSStoreApps -id $id
Returns specific app
.NOTES
NAME: Get-MSStoreApps
#>
[cmdletbinding()]
param
(
$id
)
$graphApiVersion = "beta"
$DCP_resource = "deviceAppManagement/MobileApps"
try {
if($Name){
$uri = "https://graph.microsoft.com/$graphApiVersion/$($DCP_resource)/$id"
((Invoke-MgGraphRequest -Uri $uri -Method Get -OutputType PSObject).value)
}
else {
$uri = "https://graph.microsoft.com/$graphApiVersion/$($DCP_resource)?`$filter=(isof('microsoft.graph.winGetApp'))"
(Invoke-MgGraphRequest -Uri $uri -Method Get -OutputType PSObject).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
}
}
Putting these together you could:
Assign all Store apps:
##Assign Store Apps
$sapps = Get-MSStoreApps
foreach ($sapp in $sapps) {
Add-StoreAppAssignment -Id $sapp.id -TargetGroupId "$intunegrp.id"GROUPIDHERE"
}
Or add multiple apps:
##List of apps to install
$storeapps = @("Company Portal", "Microsoft To Do: Lists, Tasks & Reminders", "Windows Terminal")
##Add each
foreach ($app in $storeapps) {
Add-MSStoreApp -Name $app
}
The possibilities are endless!!
I actually found that I could install Adobe Creative Cloud if I changed the scope to user from machine. I didn’t realize that the following was also in the error.
{“error”:{“code”:”ModelValidationFailure”,”message”:”Requested value ‘machine’ was not found.”
The icon generation fails for every app I tested with. The API also doesn’t seem to accept machine scope so unsure how to requested accepted values.
Thank you, will do some testing and get it updated
It looks like the API for images doesn’t exist any more so I will remove that from the next update.
I can’t programatically detect if it’s a user or machine group, but will add a note in the post
It’s also failing for me. Trying to get Adobe Create Cloud deployed. I also had to remove the icon capture piece as that was failing completely.
Invoke-mggraphrequest : POST https://graph.microsoft.com/beta/deviceAppManagement/mobileApps
HTTP/1.1 400 Bad Request
{“error”:{“code”:”BadRequest”,”message”:”Unable to read JSON request payload. Please ensure Content-Type header is set and payload is of valid JSON
format.”
Doesn’t work.
What doesn’t work? Are you getting any error messages?