Ablauf von Azure AD Application Secrets

Aus gegebenem Anlass habe ich mich gerade mit dem Ablaufdatum von Azure AD Applications beschäftigen müssen. Vor lauter Euphorie, dass ich meinen Blog mit dem Azure-AD verbinden konnte (um SSO zu ermöglichen) habe ich ganz übersehen, dass der von mir erstellte Key auch mit einem Ablaufdatum versehen ist.

Also: schön im Kalender das Ablaufdatum markieren und in einem angemessenen Zeitraum vorher einen neuen Key erzeugen und diesen in der Anwendung hinterlegen!

Aber was ist, wenn man sich die Ablaufdaten nicht notiert hat? Wenn man nur 1 oder 5 Anwendung hat, dann kann man die ja recht flott im Portal durchgucken, aber wie ist das bei einer größeren Menge an Apps? Immerhin gibt es in der Preview-Anzeige für Apps eine Spalte, in der der Status angezeigt wird – nur dummerweise kann man danach weder sortieren noch filtern.

image

PowerShell to the rescue!

Mit ein wenig PowerShell kann hier aber Abhilfe schaffen!

Voraussetzung ist, dass das Azure AD PowerShell-Modul installiert ist. Das kann ganz einfach per Install-Module -Name AzureAD installiert werden. Anschließend kann man mit

Get-AzureADApplication alle im Azure AD registrierten Anwendungen abrufen inklusive der jeweiligen Credentials. Mit ein wenig Formatierung kann man sich schließlich eine Liste sortiert nach Ablaufdatum anzeigen lassen.

image

Das komplette Skript sieht dann so aus:

Connect-AzureAD
$results = @()
Get-AzureADApplication -All $true | % {  
    $app = $_

    $owner = Get-AzureADApplicationOwner -ObjectId $_.ObjectID -Top 1

    $app.PasswordCredentials | % { 
        $results += [PSCustomObject] @{
            CredentialType = "PasswordCredentials"
            DisplayName    = $app.DisplayName; 
            ExpiryDate     = $_.EndDate;
            StartDate      = $_.StartDate;
            KeyID          = $_.KeyId;
            Type           = 'NA';
            Usage          = 'NA';
            Owners         = $owner.UserPrincipalName;
        }
    } 
                                  
    $app.KeyCredentials | % { 
        $results += [PSCustomObject] @{
            CredentialType = "KeyCredentials"                                        
            DisplayName    = $app.DisplayName; 
            ExpiryDate     = $_.EndDate;
            StartDate      = $_.StartDate;
            KeyID          = $_.KeyId;
            Type           = $_.Type;
            Usage          = $_.Usage;
            Owners         = $owner.UserPrincipalName;
        }
    }                            
}
$results | Sort ExpiryDate | FT -AutoSize 

17. Treffen der SharePoint UserGroup Köln – Nicki Borell – Azure Information Protection

Hallo liebe Interessenten,

ich möchte Sie heute herzlich zum 17. Treffen der SharePoint UserGroup Köln einladen. Wir treffen uns am 13. September im Mediapark in Köln.

Unser Programm dreht sich dieses Mal vollständig um das Thema Azure Information Protection, wie man E-Mails, Dokumente und vertrauliche Daten steuern und sichern kann, die außerhalb Ihres Unternehmens freigegeben sind. Von der einfachen Klassifizierung bis hin zu integrierten Bezeichnungen und Berechtigungen erweitert Azure Information Protection den Datenschutz zu jeder Zeit – unabhängig davon, wo die Daten gespeichert sind und für wen sie freigegeben werden.

Unser Sprecher ist diesmal:

Nicki Borell – Sprecher auf über 100 Konferenzen, Author, MVP, Microsoft Productivity Experte, Mitgründer von Experts Inside, Gründer des Labels Xperts at Work und Partner der atwork GmbH. Im Team setzt er erfolgreich IT- und Strategieprojekte im gehobenen Mittelstand und Großkundensegment um.

Über Ihre Teilnahme und interessante Diskussionen würde ich mich sehr freuen.

Hier noch einmal die Daten:

SharePoint UserGroup Köln

Termin: Donnerstag, 13.09.2018 – 18.00 Uhr

Ort: novaCapta Software & Consulting, Im Mediapark 5C, 50670 Köln

Parken: Parkplätze stehen in der Tiefgarage im Mediapark zur Verfügung, bitte in der Zentralgarage oder in der Garage Mediapark 5 parken.

Eine ausführliche Anfahrtsbeschreibung findet sich hier!

XING: https://www.xing.com/net/prib6b3dax/spugcgn

Ansprechpartner:
Andrej Doms
Tel. 0151 58 22 72 38

Neben den Vorträgen gibt es bei einem Snack hoffentlich reichlich Gelegenheit für Sie, mit den anwesenden Experten ins Gespräch zu kommen.
Ich freue mich, Sie alle am 12. November begrüßen zu dürfen.

Mit freundlichen Grüßen

Andrej Doms und das UserGroup Team

16. Treffen der UserGroup Köln

Hallo Liebe Teilnehmer,

ich möchte Sie heute herzlich zum 16. Treffen der SharePoint UserGroup Köln einladen. Nach einer projektbedingten Pause geht es endlich mit der SPUG Köln weiter. Wir treffen uns am 8. Mai im Mediapark in Köln.

Unser Programm dreht sich dieses Mal vollständig um das Thema Infrastructure as Code, wie man z.B. RollOuts oder Farmsetups effizient und flexibel gestalten kann und mittels Anlehnung an agile Methoden und Erfahrungen aus der Entwicklerwelt eine umfassende DevOps Kultur in der IT fördert. Unsere Sprecher sind diesmal:

Denis Buco, Microsoft Deutschland GmbH

Henning Eiben, busitec GmbH

Andrej Doms, novaCapta Software & Consulting

Über Ihre Teilnahme und interessante Diskussionen würde ich mich sehr freuen.

Hier noch einmal die Daten:

SharePoint UserGroup Köln

Termin: Dienstag, 08.05.2018 – 18.00 Uhr

Ort: novaCapta Software & Consulting, Im Mediapark 5C, 50670 Köln

Parken: Parkplätze stehen in der Tiefgarage im Mediapark zur Verfügung, bitte in der Zentralgarage oder in der Garage Mediapark 5 parken.

Eine ausführliche Anfahrtsbeschreibung findet sich hier!

XING: https://www.xing.com/net/prib6b3dax/spugcgn

Ansprechpartner:
Andrej Doms
Tel. 0151 58 22 72 38

Neben den Vorträgen gibt es bei einem Snack hoffentlich reichlich Gelegenheit für Sie, mit den anwesenden Experten ins Gespräch zu kommen.
Ich freue mich, Sie alle am 8. Mai begrüßen zu dürfen.

Mit freundlichen Grüßen

Andrej Doms und das UserGroup Team

Installation SharePoint 2016

So langsame wird es ja mal Zeit … SharePoint 2016 zu installieren.

Zunächst also mal die Vorbereitungen … ich habe hier schon ein Domäne und einen SQL-Server (SQL 2014), sowie einen neuen Windows Server 2012 R2.

Nun geht es also darum alles für die Installation von SharePoint 2016 vorzubereiten. Dabei müssen also folgende Dinge getan werden:

  1. Einrichten von neuen Dienstkonten
  2. Vorbereiten der Datenbank

Die Schritte sind ja nicht neu – waren sie ja bei SharePoint 2013 auch genauso schon notwendig. Aber dennoch wollte ich das noch einmal in Erinnerung rufen.

1. Dienstkonten

Diese lassen sich am besten per Skript anlegen. Also schnell eine kleine CSV-Datei mit allen benötigten Konten erstellen:

UserName,Password,Description
sp16Setup,super$ecret,Setup user Test Env SP2016
sp16Farm,super$ecret,server farm Test Env SP2016
sp16SearchService,super$ecret,SharePoint Server Search service Test Env SP2016
sp16SearchContent,super$ecret,default content access Test Env SP2016
sp16Services,super$ecret,SharePoint Services Test Env SP2016
sp16AppPool_Portal,super$ecret,Portal Application Pool Test Env SP2016
sp16AppPool_MySite,super$ecret,MySite Application Pool Test Env SP2016
sp16CacheUser,super$ecret,Cache Super User Test Env SP2016
sp16CacheReader,super$ecret,Cache Super User Reader Test Env SP2016
sp16ExcelUser,super$ecret,Excel Services Unattended Test Env SP2016
sp16VisioUser,super$ecret,Visio Services Unattended Test Env SP2016
sp16ProfileSync,super$ecret,SharePoint Profile Sync Test Env SP2016

Dann PowerShell öffnen und die Konten aus der CSV-Datei anlegen lassen.

Import-Module ActiveDirectory

$FileName = "sp2016_ads_user.csv"
$spou = "ou=SPS2016,ou=Service-Accounts,dc=acme,dc=local"

Import-Csv $FileName | foreach-object { 
    Write-Host "Adding User $($_.UserName) ... " -NoNewline
    New-AdUser $_.UserName -Description $_.Description -CannotChangePassword $true -PasswordNeverExpires $true -Enabled $true -AccountPassword (ConvertTo-SecureString -AsPlainText $_.Password -Force) -Path $spou
    Write-Host "Done"
}


2. Datenbank

Ich habe hier schon einen SQL-Server mit einer neuen Instanz für meine SharePoint Datenbanken. Hier muss ja nun der Setup-User dbcreator und securityadmin sein. Zudem muss das MAXDOP auf “1” gesetzt werden.

Da es sich bei meiner Installation nicht um ein Produktiv-System handelt setze ich das Recovery-Model noch auf “simple” und ich limitieren den maximalen RAM für meine Instanz auf 512MB. Das Ganze geht auch am besten schnell mit einem Skript.

-- Create Login and assign permissions
USE [master]
GO
DECLARE @setupUser NVARCHAR(100) = N'acme\spSetup';
DECLARE @sqlStmt NVARCHAR(200);
SET @sqlStmt = 'CREATE LOGIN [' + @setupUser + '] FROM WINDOWS WITH DEFAULT_DATABASE=[master]'
EXEC (@sqlStmt)
EXEC master..sp_addsrvrolemember @loginame = @setupUser, @rolename = N'dbcreator'
EXEC master..sp_addsrvrolemember @loginame = @setupUser, @rolename = N'securityadmin'
GO

-- set MAXDOP to 1, as recommended
EXEC sys.sp_configure N'show advanced options', N'1'  RECONFIGURE WITH OVERRIDE
GO
EXEC sys.sp_configure N'max degree of parallelism', N'1'
GO
RECONFIGURE WITH OVERRIDE
GO
EXEC sys.sp_configure N'show advanced options', N'0'  RECONFIGURE WITH OVERRIDE
GO


-- for dev-env only!! Set recovery-model to simple!!
USE [master]
GO
ALTER DATABASE [model] SET RECOVERY SIMPLE WITH NO_WAIT
GO

-- for dev-env only!! Set max-ram to 768 MB!!
USE [master]
GO
EXEC sys.sp_configure N'show advanced options', N'1'  RECONFIGURE WITH OVERRIDE
GO
EXEC sys.sp_configure N'min server memory (MB)', N'768'
GO
EXEC sys.sp_configure N'max server memory (MB)', N'768'
GO
RECONFIGURE WITH OVERRIDE
GO
EXEC sys.sp_configure N'show advanced options', N'0'  RECONFIGURE WITH OVERRIDE
GO

3. Los!

Nachdem nun die Vorbereitungen soweit abgeschlossen sind, können wir uns unserem neuen SharePoint-Server widmen. Als erstes muss unser Installationsaccount sp16Setup noch in die lokale Admin-Gruppe aufgenommen werden.

net localgroup administrators /add acme\sp16Setup

Nun können wir (fast) mit der Installation beginnen. Was noch fehlt sind die Installationsdateien. Zunächst wird also SharePoint 2016 benötigt.

Ansonsten geht die Installation erst einmal wie bei SharePoint 2013 auch: Die Installationsdateien mit dem AutoSPSourceBuilder vorbereiten, sprich für den AutoSPInstaller in die notwendige Struktur bringen.

Achtung: in dem XML-File für die Pre-Requisits ist (Stand Mai 2016) noch ein Fehler. Die URL für den ODBC Treiber 11 lautet nicht http://download.microsoft.com/download/5/7/2/57249A3A-19D6-4901-ACCE-80924ABEB267/1033/x64/msodbcsql.msi sondern http://download.microsoft.com/download/5/7/2/57249A3A-19D6-4901-ACCE-80924ABEB267/1033/amd64/msodbcsql.msi. Alos entweder die URL in dem XML anpassen, oder den Treiber einfach vorher schon laden und in dem Install-Verzeichnis bei den Pre-Requisits ablegen. Dann wird das von dem AutoSPSourceBuilder übersprungen werden.

.\AutoSPSourceBuilder.ps1 -SourceLocation "\\acme\dfs\SharePoint\2016\Server\" -Destination "c:\Install\" -GetPrerequisites $true

Nachdem das entpakte ISO kopiert und die Pre-Requisits heruntergeladen wurden kann man die XML Datei für den AutoSPInstaller anpassen. Entweder von Hand – oder über AutoSPInstaller Online.

Nach getaner Konfiguration geht es dann auch direkt los – fast. In meinem Fall habe ich einen frischen Windows Server 2012 R2 als OS – dummerweise fehlt hier aber noch das .Net Framework 3.x, und die Installation über den AutoSPInstaller mit Add-WindowsFeature -Name NET-Framework will nicht so richtig, weil für das .Net Framework die Installationsquelle mit angegeben werden muss. Also kurz vorher an der Kommandozeile mit

dism /online /enable-feature /featurename:NetFX3 /all /Source:d:\sources\sxs /LimitAccess

Das .Net Framework installieren. In meinem Fall habe ich die Windows Server Installationsquelle als Laufwerk D: eingebunden. Nun kann aber wirklich die Installation gestartet werden.

Zunächst also auf AutoSPInstaller Online eine neue Farm Konfiguration erstellen und als XML-Datei speichern.

Achtung: durch den AutoSPSourceBuilder wurde bei den Pre-Requisits eine Datei WcfDataServices56.exe bereitgestellt, der AutoSPInstaller erwartet hier aber WcfDataServices.exe. Damit die Offline-Installation auch ohne Probleme durchläuft sollte die Datei also umbenannt werden (oder das Skript anpassen).

Danach kann es dann aber losgehen mit der eigentlichen Installation.

image

Und nach kurzer Zeit (ein paar Reboots und gut drei Duzend Windows Updates später) steht die neue Farm – voila!

Happy #SharePointing

SharePoint PeoplePicker mit mehreren Domänen

Hier ein Update zu meinem Post, in dem ich schon mal beschrieben habe, wie man von einem SharePoint System eine andere Domäne mit durchsucht. Ich kannte bis dato nur den Weg über das gute alte STSADM; aber natürlich geht es auch mit PowerShell!

Und sogar noch viel besser. Mit der PowerShell kann ich in SharePoint mehr als eine Domäne durchsuchen. Und so geht es:

$wa = Get-SPWebApplication http://sp2013.dev-acme.local 

$adsearchobj = New-Object Microsoft.SharePoint.Administration.SPPeoplePickerSearchActiveDirectoryDomain
$userpassword = ConvertTo-SecureString "MeinPasswort" -AsPlainText – -Force
$adsearchobj.DomainName = "acme.local"
$adsearchobj.ShortDomainName = "ACME Corporation"
$adsearchobj.IsForest = $false #$true for Forest, $false for Domain
$adsearchobj.LoginName = "devlookup"
$adsearchobj.SetPassword($userpassword)

$wa.PeoplePickerSettings.SearchActiveDirectoryDomains.Add($adsearchobj)
$wa.Update()

Das ist zwar insgesamt etwas länger, aber deutlich verständlicher als die Zeile

STSADM.exe -o setproperty -pn peoplepicker-searchadforests -pv "domain:acme.local,acme\devlookup,MeinPasswort" -url http://sp2013.dev-acme.local

Schnell mal ActiveDirectory

Gerade in Verbindung mit SharePoint braucht man ja auch häufig ein Active Directory. Also gehört es zur Pflicht-Übung für ein Demo-System auch schnell mal ein AD aufzusetzen.

Das ist im Kern nicht so wirklich schwer – besonders wenn es ja nur zu Demo-Zwecken ist. Aber irgendwie lästig ist es ja doch.

Zuerst muss man das entsprechende Feature in Windows hinzufügen:

Import-Module servermanager
Add-WindowsFeature adds-domain-controller

Anschließend muss dann noch die Domäne angelegt werden. Also hier mal eben die Anlage eines neue AD per Kommandozeile. Einfach auf einem Windows 2008 Server (oder neuer) ausführen:

# Windows Server 2008
 $unattend = "c:\unattend.txt"
if(!(test-path $unattend)) { new-item $unattend -type file }
$content = @(
    "[DCInstall]"
    "ReplicaOrNewDomain=Domain"
    "NewDomain=Forest"
    "NewDomainDNSName=acme.local"
    "ForestLevel=4"
    "DomainNetbiosName=ACME"
    "DomainLevel=4"
    "InstallDNS=Yes"
    "ConfirmGc=Yes"
    "CreateDNSDelegation=No"
    'DatabasePath="C:\Windows\NTDS"'
    'LogPath="C:\Windows\NTDS"'
    'SYSVOLPath="C:\Windows\SYSVOL"'
    'SafeModeAdminPassword="P@ssw0rd1"'
    "RebootOnCompletion=Yes"
    )
$content | out-file $unattend
$dcpromo = "dcpromo /unattend:$unattend"
invoke-expression -command $dcpromo

ggf. müssen die Angaben zur Domäne noch angepasst werden.

Unter Windows Server 2012 geht das etwas kürzer, hier gibt es ein entsprechendes PowerShell cmdlet.

Install-ADDSDomainController -CreateDnsDelegation:$false -DatabasePath 'C:\Windows\NTDS' -DomainName 'acme.local' -InstallDns:$true -LogPath 'C:\Windows\NTDS' -NoGlobalCatalog:$false -SiteName 'Default-First-Site-Name' -SysvolPath 'C:\Windows\SYSVOL' -NoRebootOnCompletion:$true -Force:$true

Wie groß ist mein SharePoint?

Jetzt hatte ich doch gerade die Frage: wie groß ist eigentlich meine ContentDatenbank meiner aktuellen Site-Collection? Und weil ich Herausforderungen mag: SharePoint 2007 bzw. Windows Services 3.0.

Natürlich gibt es nur einen Weg um dies herauszufinden:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$site = New-Object Microsoft.SharePoint.SPSite("http://portal.acme.local")
$diskMethod = [Microsoft.Sharepoint.Administration.SPContentDatabase].getMethod("get_DiskSizeRequired")
$diskMethod.Invoke($site.ContentDatabase, "instance,public", $null, $null, $null)

Das Ergebnis zeigt die Größe der Datenbank in Bytes an.

Geht doch!

Es geht nicht ohne: PowerShell und SharePoint

Es geht doch nichts über Rituale. Ich bin also gerade auf einem SharePoint-Server als Farm-Admin eingeloggt und wollte ein wenig powershellen. Und da kommt die Meldung:

Auf die lokale Farm kann nicht zugegriffen werden. Cmdlets mit `FeatureDependencyId`sind nicht registriet.

Wer kennt die Meldung nicht. Und warum ist die da: Sicherheit. Nur Farm-Admin reicht halt für die PowerShell nicht (Ach so: natürlich habe ich meine PowerShell schon mit erhöhten Rechten gestartet, das ist doch klar).

Was ist nun also das Problem? Der Benutzer ist zwar Farm-Admin, kann aber von der Shell nicht direkt auf die Datenbanken von SharePoint zugreifen. Somit kann ich die meisten PowerShell Befehle (für SharePoint) nicht durchführen. Wenn ich nun also doch mal ein Get-SPWebApplication “foo” mache, dann bekomme ich die Meldung

Auf die lokale Farm kann nicht zugegriffen werden. Vergewissern Sie sich,. dass die lokale Farm ordnungsgemäß konfiguriert sowie aktuell verfügbar ist und Sie über ausreichende Berechtigungen verfügen, um auf die Datenbank zugreifen zu können, bevor Sie es erneut versuchen.

Aha – das steht’s ja schon. Ich kann nicht auf die Datenbank zugreifen. Um das Zu ändern gibt es den Befehl Add-SPShellAdmin. Nur dumm, dass ich den als jemand ausführen muss, der über entsprechende Berechtigungen verfügt (also der schon Shell-Admin ist).

Na gut, dann muss dafür mal eben kurz der spFarm herhalten. Also eben eine CMD als spFarm gestartet mit

runas /user:spFarm cmd

und dann eine PowerShell starten. Hierbei muss diese aber elevated gestartet werden, und dass vom Prompt aus. Das geht eigentlich auch ganz einfach

@powershell Start-Process -Verb "runas" powershell

So, nun habe ich also eine PowerShell als spFarm mit erhöhten Rechten. Der Rest ist einfach

Add-PSSnapin Microsoft.SharePoint.PowerShell
Add-SPShellAdmin busitec\eiben

Wenn ich nun wieder hingehe und eine SharePoint Verwaltungsshell (mit erhöhten Rechten) starte, dann kann ich auch per PowerShell auf die Farm zugreifen.

Voila!

Ein paar Fragen an das Active Directory

Beim Betrieb einer SharePoint-Farm ist man auf die korrekte Pflege von Accounts im Active Directory angewiesen. Gerade im Zusammenhang mit Kerberos ist es wichtig, dass alle Einstellungen im Active Directory richtig eingetragen werden, damit die Authentifizierung auch ohne Probleme funktioniert.

Als SharePoint-Farm Administrator hat man dazu allerdings nicht immer ohne weiteres Zugriff auf das Active Directory, zumindest nicht auf die Management-Tools. Nun stellt sich also die Frage, wie kann ich feststellen, welche Service-Principles alle für einen Account im Active Directory registriert wurden. Wenn ich Zugriff auf einen Domain-Controller hätte, dann würde ich ganz einfach SETSPN verwenden.

C:\>SETSPN spIntranetWA
Registered ServicePrincipalNames for CN=spIntranetWA,OU=Dienstkonten,OU=HeadQuarter,DC=acme,DC=local:
    HTTP/sp01.acme.local
    HTTP/intranet.acme.local

Aber SETSPN kann ich nur direkt auf einem Domain Controller ausführen, da komme ich als SharePoint Farm-Admin also nicht drauf. Was nun?

Wie bei allen Administrativen gibt es eine Ultimative Antwort: PowerShell!

Die Lösung ist so einfach, ich traue mich die ja schon kaum zu posten. Mit folgendem Befehl kann ich alle Einträge im Active Directory suchen, deren Account-Name “spIntranetWA” ist:

C:\> ([adsisearcher]"samaccountname=spIntranetWA").FindAll()

Path                                                        Properties
----                                                        ----------
LDAP://CN=spIntranetWA,OU=Dienstkonten,OU=HeadQuarter,... {logoncount, codepage, objectcategory, usnchanged...}

Damit habe ich schon mal ein paar Informationen.

Das funktioniert natürlich von jedem Arbeitsplatz mit PowerShell. Im Gegensatz zu den Active-Directory cmdlets verwende ich hier den ADSISearcher (dabei handelt es sich um ein Alias für die DirectorySearcher-Klasse aus dem .Net Framework). Das ist eine der genialen Dinge der PowerShell – ich kann mal eben so was aus dem .Net Framework verwenden. Um nun Objekte im Active-Directory zu suchen kann man dem ADSISearcher LDAP-Abfragen übergeben.

Insbesondere die Properties sind hierbei interessant. Die kann man sich auch etwas genauer ansehen.

C:\> $accounts = ([adsisearcher]"samaccountname=spIntranetWA").FindAll()
C:\> $accounts.properties

Name                           Value
----                           -----
logoncount                     {323}
codepage                       {0}
objectcategory                 {CN=Person,CN=Schema,CN=Configuration,DC=acme,DC=local}
usnchanged                     {15699245}
instancetype                   {4}
name                           {spIntranetWA}
pwdlastset                     {128843629222870406}
serviceprincipalname           {HTTP/sp01.acme.local, HTTP/intranet.acme.local}
objectclass                    {top, person, organizationalPerson, user}
samaccounttype                 {805306368}
lastlogontimestamp             {130265658248173942}
usncreated                     {19523}
dscorepropagationdata          {21.06.2013 13:56:40, 21.06.2013 12:28:23, 20.07.2012 09:54:16, 01.01.1601 18:16:33}
whencreated                    {16.04.2009 13:42:02}
adspath                        {LDAP://CN=spIntranetWA,OU=Dienstkonten,OU=HeadQuarter,DC=acme,DC=local}
useraccountcontrol             {590336}
cn                             {spIntranetWA}
countrycode                    {0}
primarygroupid                 {513}
whenchanged                    {18.10.2013 10:30:24}
lastlogon                      {130270479005600192}
distinguishedname              {CN=spIntranetWA,OU=Dienstkonten,OU=HeadQuarter,DC=acme,DC=local}
samaccountname                 {spIntranetWA}
objectsid                      {1 5 0 0 0 0 0 5 21 0 0 0 130 139 166 40 17 195 95 115 138 167 50 63 245 22 0 0}
displayname                    {spIntranetWA}
objectguid                     {217 8 220 57 70 168 118 76 178 20 180 160 198 219 146 67}
accountexpires                 {9223372036854775807}
userprincipalname              {spIntranetWA@acme.local}

Hier kann man auch den ServicePrincipleName ablesen. Der Rest ist dann nur noch ein wenig  PowerShell gefummel, um das ganze dann auch tabellarisch angezeigt zu bekommen.

C:\> ([adsisearcher]"samaccountname=sp*").FindAll()|select @{name='cn'; expression = {$_.properties['cn']}}, @{name="spn"; expression={$_.properties["ServicePrincipalName"]}}

cn                      spn
--                      ---
spCrawl
spDBAccount
spExtranetWA
spFarm
spInternetWA
spIntranetWA            {HTTP/sp01.acme.local, HTTP/intranet.acme.local}
spMySites
spSearch
spSPService

Ändern des Edit-Formulars für eine SharePoint Liste

Vor kurzem kam ein Kunde mit einem Problem auf mich zu. Er hatte mit dem SharePoint-Designer ein eigenes Edit-Form für eine Liste erstellt und dieses als Standard-Editor-Formular der Liste zugeordnet. Mit der Zeit wurde die Liste angepasst und es sind neue Feld in die Liste dazugekommen. Leider werden diese neuen Felder von dem angepassten Formular nicht angezeigt.

Nun wollte der Kunde als das Standard-Formular wieder zurücksetzen. Leider wurde in der Zwischenzeit die Verwendung des SharePoint Designers in der Farm deaktiviert. Nun war also die Frage: wie kann ich das Standard Edit-Formular wieder zurücksetzen?

Klare Antwort: PowerShell!

$url = Read-Host "Enter URL" 
$listname = Read-Host "Enter List Name"
$editformurl = Read-Host "Enter Edit Form Url"

$web = Get-SPWeb $url 
$list = $web.Lists[$listname]

$list.DefaultEditFormUrl = $editformurl
$list.update()

So weit, so gut. Aber leider hatte der Kunde keinen Zugriff auf den Server. Somit hilft PowerShell an dieser Stelle leider einmal nicht weiter.

Was bleibt dann noch? JavaScript! Mit dem Client-Object-Model von SharePoint 2010 geht halt doch schon einiges.

<script type="text/javascript">
(function() {
    "use strict";

    var context;
    var list;

    SP.SOD.executeOrDelayUntilScriptLoaded(function () {
        context = new SP.ClientContext.get_current();
        list = context.get_web().get_lists().getByTitle("Test");
        context.load(list);
        context.executeQueryAsync(onSuccessLoadList, onFailure);
    }, "sp.js");

    function onSuccessLoadList() {
        list.set_defaultEditFormUrl("/users/eiben/Lists/Test/NewEditForm.aspx");
        list.update();
        context.executeQueryAsync(onSuccessUpdateList, onFailure);
    }

    function onSuccessUpdateList() {
        alert("done");
    }

    function onFailure(sender, args) {
        SP.UI.Notify.addNotification("Es ist ein Fehler aufgetreten ... " + args.get_message(), true, "", null);
        console.debug(args.get_stackTrace());
    }

})();
</script>

Das mal eben auf der Site z.B. in einem Content-Editor-WebPart einbinden – und voila!