5
votes

I'm deploying apps using Import-SPAppPackage and Install-SPApp. I'd like to be able to use Set-AppPrincipalPermission to set permissions but I can't get it working.

I'm uploading a SharePoint-hosted app to SharePoint using the PowerShell cmdlets Import-SPAppPackage and Install-SPApp. This is working fine for SharePoint-hosted apps that do not require additional permissions.

However, one app needs read access to the site, so this is declared in the manifest. And it works fine when run through Visual Studio - on first launch, it correctly asks to trust the app for read access to the site.

When I add this app via PowerShell, it has no opportunity to ask. The install continues without problems, but then the app doesn't work. (It fails with a permissions problem, which is absolutely the correct behavour since the permissions haven't yet been granted.)

I can fix the permissions by going to the Site Contents, clicking on the '...' for the problem app, choosing 'Permissions' and then clicking the link that says 'If there's something wrong with the app's permissions, click here to trust it again'.

But I really want to just be able to do the whole deployment via PowerShell.

The Set-AppPrincipalPermission cmdlet should allow me to set the permissions, but I can't get it to work. Specifically, I can't get a handle on the app principal that was automatically created when the app was deployed, so I can't pass this app principal to Set-AppPrincipalPermission.

The app principal has a name of the form 'i:0i.t|ms.sp.int|@' and it is listed on /_layouts/15/appprincipals.aspx. When I use Get-SPAppPrincipal with it, all I get is:

Get-SPAppPrincipal : The app principal could not be found.

I haven't seen any examples of using Get-SPAppPrincipal for any SharePoint-hosted apps - they all seem to be for provider-hosted apps. They also all seem to just use an app principal ID built from the client ID and the realm ID, but my SharePoint-hosted app doesn't have a client ID.

Is it possible to get the app principal of a SharePoint-hosted app and use it to set the permissions via PowerShell? Am I doing something wrong, or is there another approach?

3

3 Answers

3
votes

I struggled the same problem like you and finally found an answer in these two blogs:

Blog with a nice Install, Update and Delete Script

Here is a nice post about "pressing" the "Trust It" Button via PowerShell Link

And because I know how lazy programmers like me are, feel free to use this merged script to Install Apps:

    param
(
    [string]$Web = $(throw '- Need a SharePoint web site URL (e.g. "http://portal.contoso.com/")'),
[string]$Source = "ObjectModel"
)

Write-Host -ForegroundColor White "-------------------"
Write-Host -ForegroundColor White "| App Installer |"
Write-Host -ForegroundColor White "-------------------"
Write-Host -ForegroundColor White "- "

#Global vars
$AppPackageName = "App.app";

#Loads powershell settings
Write-Host -ForegroundColor White "- Load Powershell context.."
$0 = $myInvocation.MyCommand.Definition
$dp0 = [System.IO.Path]::GetDirectoryName($0)

#Loads the SharePoint snapin
Write-Host -ForegroundColor White "- Load SharePoint context.."
$ver = $host | select version
if ($ver.Version.Major -gt 1) {$host.Runspace.ThreadOptions = "ReuseThread"} 
if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null) {
    Add-PSSnapin "Microsoft.SharePoint.PowerShell";
}
[void][System.Reflection.Assembly]::Load("Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c") 

#Imports the App package
Write-Host -ForegroundColor White "- Import app package '$AppPackageName'..."
$appPath = "C:\Projects\App\App\bin\Debug\app.publish\1.0.0.0" + "\" + $AppPackageName;
if ($Source.Equals("ObjectModel", [System.StringComparison]::InvariantCultureIgnoreCase)) {
$sourceApp = ([microsoft.sharepoint.administration.spappsource]::ObjectModel);
}
elseif ($Source.Equals("Marketplace", [System.StringComparison]::InvariantCultureIgnoreCase)) {
$sourceApp = ([microsoft.sharepoint.administration.spappsource]::Marketplace);
}
elseif ($Source.Equals("CorporateCatalog", [System.StringComparison]::InvariantCultureIgnoreCase)) {
$sourceApp = ([microsoft.sharepoint.administration.spappsource]::CorporateCatalog);
}
elseif ($Source.Equals("DeveloperSite", [System.StringComparison]::InvariantCultureIgnoreCase)) {
$sourceApp = ([microsoft.sharepoint.administration.spappsource]::DeveloperSite);
}
elseif ($Source.Equals("RemoteObjectModel", [System.StringComparison]::InvariantCultureIgnoreCase)) {
$sourceApp = ([microsoft.sharepoint.administration.spappsource]::RemoteObjectModel);
}

$spapp = Import-SPAppPackage -Path "$appPath" -Site $Web -Source $sourceApp -Confirm:$false -ErrorAction SilentlyContinue -ErrorVariable err;
if ($err -or ($spapp -eq $null)) 
{
Write-Host -ForegroundColor Yellow "- An error occured during app import !"
throw $err;
}
Write-Host -ForegroundColor White "- Package imported with success."

#Installs the App
Write-Host -ForegroundColor White "- Install the APP in web site..."
$app = Install-SPApp -Web $Web -Identity $spapp -Confirm:$false -ErrorAction SilentlyContinue -ErrorVariable err;
if ($err -or ($app -eq $null)) {
Write-Host -ForegroundColor Yellow "- An error occured during app installation !"
throw $err;
}
$AppName = $app.Title;
Write-Host -ForegroundColor White "- App '$AppName' registered, please wait during installation..."
$appInstance = Get-SPAppInstance -Web $Web | where-object {$_.Title -eq $AppName};
$counter = 1;
$maximum = 150;
$sleeptime = 2;

Write-Host -ForegroundColor White "- Please wait..." -NoNewline;

$url = "$($Web)_layouts/15/appinv.aspx?AppInstanceId={$($appInstance.Id)}"
$ie = New-Object -com internetexplorer.application
try
{
    $ie.visible=$true
    $ie.navigate2($url)
    while ($ie.busy)
    {
        sleep -milliseconds 60
    }
    $trustButton = $ie.Document.getElementById("ctl00_PlaceHolderMain_BtnAllow")         
    $trustButton.click() 
    sleep -Seconds 1
    Write-Host "App was trusted successfully!"
}
catch
{
    throw ("Error Trusting App");
}

while (($appInstance.Status -eq ([Microsoft.SharePoint.Administration.SPAppInstanceStatus]::Installing)) -and ($counter -lt $maximum))
{
Write-Host -ForegroundColor White "." -NoNewline;
sleep $sleeptime;
$counter++;
$appInstance = Get-SPAppInstance -Web $Web | where-object {$_.Title -eq $AppName} 
}

Write-Host -ForegroundColor White ".";

if ($appInstance.Status -eq [Microsoft.SharePoint.Administration.SPAppInstanceStatus]::Installed) {
Write-Host -ForegroundColor White "- The App was successfully installed.";
$appUrl = $appInstance.AppWebFullUrl;
Write-Host -ForegroundColor White "- The App is now available at '$appUrl'.";
Write-Host -ForegroundColor White  "- (Don't forget to add app host name in your host file if necessary...).";
Write-Host -ForegroundColor White "- "
}
else {
Write-Host -ForegroundColor Yellow "- An unknown error has occured during app installation. Read SharePoint log for more information.";
}
1
votes

Figured out a way other than using IE.

Basically just using powershell to call SPAppPrincipalPermissionsManager.AddAppPrincipalToWeb

$rootUrl = "https://ur-sp.com"
$urlSiteName = "ur-site"
$web = Get-SPWeb "$rootUrl/$urlSiteName"
$appPrincipalManager = [Microsoft.SharePoint.SPAppPrincipalManager]::GetManager($web)
$applicationEndPointAuthorities = new-object System.Collections.Generic.List[string]
$applicationEndPointAuthorities.Add("$rootUrl/$urlSiteName");
$symmetricKey = New-Object System.Security.SecureString;
$datetimeNow = [System.DateTime]::Now
$credential = [Microsoft.SharePoint.SPAppPrincipalCredential]::CreateFromSymmetricKey($symmetricKey,$datetimeNow,$datetimeNow)
$creationParameters =New-Object Microsoft.SharePoint.SPExternalAppPrincipalCreationParameters($appid,$appFriendlyName,$applicationEndPointAuthorities,$credential)

$appPrincipal = $appPrincipalManager.CreateAppPrincipal($creationParameters)

$appPrincipalPermissionsManager = New-Object -TypeName 
Microsoft.SharePoint.SPAppPrincipalPermissionsManager -ArgumentList $web

$r = $appPrincipalPermissionsManager.AddAppPrincipalToWeb($appPrincipal, 3)

3 is of SPAppPrincipalPermissionKind enum, and I don't think its value really matters.

0
votes

This will do the full trust part via powershell:

$targetWeb = Get-SPSite "http://dev.my.com"
$clientID = "82ea34fc-31ba-4e93-b89a-aa41b023fa7e"

$authRealm = Get-SPAuthenticationRealm -ServiceContext $targetWeb 
$AppIdentifier = $clientID + "@" + $authRealm 
$appPrincipal = Get-SPAppPrincipal -Site $targetWeb.RootWeb -NameIdentifier $AppIdentifier 

Set-SPAppPrincipalPermission -Site $targetWeb.RootWeb -AppPrincipal $appPrincipal -Scope SiteCollection -Right FullControl

More info here: http://lixuan0125.wordpress.com/2013/11/18/register-and-install-app-through-powershell/