{"id":8708,"date":"2025-04-05T03:38:50","date_gmt":"2025-04-05T03:38:50","guid":{"rendered":"https:\/\/pariswells.com\/blog\/?p=8708"},"modified":"2025-04-05T03:38:52","modified_gmt":"2025-04-05T03:38:52","slug":"how-to-setup-new-app-in-entra-with-cloudapp-api-permissions","status":"publish","type":"post","link":"https:\/\/pariswells.com\/blog\/research\/how-to-setup-new-app-in-entra-with-cloudapp-api-permissions","title":{"rendered":"How to setup new App in Entra with CloudApp API Permissions"},"content":{"rendered":"\n<pre class=\"wp-block-code\"><code class=\"\">\n\n# Connect to Microsoft Graph with necessary scopes\nConnect-MgGraph -Scopes \"Application.ReadWrite.All\", \"Directory.ReadWrite.All\", \"DelegatedPermissionGrant.ReadWrite.All\" -ErrorAction Stop\n\n# Get Tenant ID\n$tenantId = (Get-MgOrganization).Id\nWrite-Host \"Tenant ID: $tenantId\"\n\n# Find and delete all existing CloudAppUsageExporter apps\n$appName = \"CloudAppUsageExporter\"\n$existingApps = Get-MgApplication -Filter \"DisplayName eq '$appName'\" -ErrorAction Stop\nif ($existingApps) {\n    Write-Host \"Found $($existingApps.Count) app(s) named '$appName'. Deleting them...\"\n    foreach ($app in $existingApps) {\n        try {\n            Remove-MgApplication -ApplicationId $app.Id -ErrorAction Stop\n            Write-Host \"Deleted app with ID: $($app.AppId)\"\n        } catch {\n            Write-Warning \"Failed to delete app with ID: $($app.AppId). Error: $_\"\n        }\n    }\n    Start-Sleep -Seconds 5  # Wait for deletion to propagate\n} else {\n    Write-Host \"No existing apps named '$appName' found.\"\n}\n\n# Create a new Azure AD App Registration\n$app = New-MgApplication -DisplayName $appName -SignInAudience \"AzureADMyOrg\" -ErrorAction Stop\n$clientId = $app.AppId\nWrite-Host \"Created new app with Client ID: $clientId\"\n\n# Define Microsoft Graph API permission: CloudApp-Discovery.Read.All (Application Permission)\n$graphServicePrincipal = Get-MgServicePrincipal -Filter \"AppId eq '00000003-0000-0000-c000-000000000000'\" -ErrorAction Stop  # Microsoft Graph App ID\n$permission = $graphServicePrincipal.AppRoles | Where-Object { $_.Value -eq \"CloudApp-Discovery.Read.All\" }\n\nif ($permission) {\n    # Structure RequiredResourceAccess\n    $requiredResourceAccess = @(\n        @{\n            ResourceAppId  = $graphServicePrincipal.AppId\n            ResourceAccess = @(\n                @{\n                    Id   = $permission.Id\n                    Type = \"Role\"\n                }\n            )\n        }\n    )\n    Update-MgApplication -ApplicationId $app.Id -RequiredResourceAccess $requiredResourceAccess -ErrorAction Stop\n    Write-Host \"Added CloudApp-Discovery.Read.All permission to the app.\"\n} else {\n    Write-Error \"Could not find CloudApp-Discovery.Read.All permission.\"\n    Disconnect-MgGraph\n    exit\n}\n\n# Create a Service Principal for the app\n$sp = Get-MgServicePrincipal -Filter \"AppId eq '$clientId'\" -ErrorAction SilentlyContinue\nif (-not $sp) {\n    $sp = New-MgServicePrincipal -AppId $clientId -ErrorAction Stop\n    Start-Sleep -Seconds 10  # Wait for propagation\n    Write-Host \"Created new service principal for app.\"\n}\n$servicePrincipalId = $sp.Id\n\n# Grant admin consent for the permission\ntry {\n    New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $servicePrincipalId `\n        -PrincipalId $servicePrincipalId `\n        -ResourceId $graphServicePrincipal.Id `\n        -AppRoleId $permission.Id `\n        -ErrorAction Stop\n    Write-Host \"Admin consent granted for the permission.\"\n} catch {\n    Write-Warning \"Failed to grant admin consent programmatically: $_\"\n    Write-Host \"Please grant consent manually in the Azure Portal after running this script.\"\n}\n\n# Create a client secret\n$secret = Add-MgApplicationPassword -ApplicationId $app.Id -PasswordCredential @{ DisplayName = \"CloudAppUsageSecret\"; EndDateTime = (Get-Date).AddYears(1) } -ErrorAction Stop\n$clientSecret = $secret.SecretText\nWrite-Host \"Client Secret: $clientSecret\"\n\n# Output the values for your records\nWrite-Host \"Save these values:\"\nWrite-Host \"Tenant ID: $tenantId\"\nWrite-Host \"Client ID: $clientId\"\nWrite-Host \"Client Secret: $clientSecret\"\n\n# Disconnect from Microsoft Graph\nDisconnect-MgGraph<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-8708","post","type-post","status-publish","format-standard","hentry","category-research"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/posts\/8708","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/comments?post=8708"}],"version-history":[{"count":1,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/posts\/8708\/revisions"}],"predecessor-version":[{"id":8709,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/posts\/8708\/revisions\/8709"}],"wp:attachment":[{"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/media?parent=8708"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/categories?post=8708"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/tags?post=8708"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}